پسوند فروشنده

افزونه‌های فروشنده API شبکه‌های عصبی (NNAPI) که در اندروید 10 معرفی شده‌اند، مجموعه‌ای از عملیات‌ها و انواع داده‌های تعریف‌شده توسط فروشنده هستند. در دستگاه‌هایی که NN HAL 1.2 یا بالاتر دارند، درایورها می‌توانند با پشتیبانی از برنامه‌های افزودنی فروشنده مربوطه، عملیات تسریع‌شده سخت‌افزاری سفارشی را ارائه دهند. برنامه های افزودنی فروشنده رفتار عملیات موجود را تغییر نمی دهند.

برنامه‌های افزودنی فروشنده جایگزین ساختارمندتری برای عملیات OEM و انواع داده‌ها، که در Android 10 منسوخ شده بودند، ارائه می‌کنند. برای اطلاعات بیشتر، عملیات OEM و انواع داده را ببینید.

لیست مجاز استفاده از برنامه های افزودنی

برنامه‌های افزودنی فروشنده فقط می‌توانند توسط برنامه‌های Android مشخص شده و باینری‌های بومی در پارتیشن‌های /product ، /vendor ، /odm و /data استفاده شوند. برنامه‌ها و باینری‌های بومی واقع در پارتیشن /system نمی‌توانند از پسوندهای فروشنده استفاده کنند.

فهرستی از برنامه‌های Android و باینری‌های مجاز برای استفاده از پسوندهای فروشنده NNAPI در /vendor/etc/nnapi_extensions_app_allowlist ذخیره می‌شود. هر خط از فایل حاوی یک ورودی جدید است. یک ورودی می‌تواند یک مسیر باینری بومی باشد که با یک اسلش (/)، به عنوان مثال، /data/foo ، یا نام یک بسته برنامه Android، به عنوان مثال، com.foo.bar پیشوند باشد.

لیست مجاز از کتابخانه مشترک زمان اجرا NNAPI اعمال می شود. این کتابخانه در برابر استفاده تصادفی محافظت می کند، اما نه در برابر دور زدن عمدی توسط یک برنامه که مستقیماً از رابط HAL درایور NNAPI استفاده می کند.

تعریف پسوند فروشنده

فروشنده یک فایل هدر با تعریف پسوند ایجاد و نگهداری می کند. یک مثال کامل از یک تعریف پسوند را می توان در example/fibonacci/FibonacciExtension.h یافت.

هر برنامه افزودنی باید یک نام منحصر به فرد داشته باشد که با نام دامنه معکوس فروشنده شروع می شود.

const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";

نام به عنوان فضای نام برای عملیات و انواع داده عمل می کند. NNAPI از این نام برای تمایز بین پسوندهای فروشنده استفاده می کند.

عملیات ها و انواع داده ها به روشی مشابه موارد در runtime/include/NeuralNetworks.h اعلان می شوند.

enum {
    /**
     * A custom scalar type.
     */
    EXAMPLE_SCALAR = 0,

    /**
     * A custom tensor type.
     *
     * Attached to this tensor is {@link ExampleTensorParams}.
     */
    EXAMPLE_TENSOR = 1,
};

enum {
    /**
     * Computes example function.
     *
     * Inputs:
     * * 0: A scalar of {@link EXAMPLE_SCALAR}.
     *
     * Outputs:
     * * 0: A tensor of {@link EXAMPLE_TENSOR}.
     */
    EXAMPLE_FUNCTION = 0,
};

یک عملیات پسوندی می تواند از هر نوع عملوند، از جمله انواع عملوند غیرپسوندی و انواع عملوند از پسوندهای دیگر استفاده کند. هنگام استفاده از یک نوع عملوند از یک برنامه افزودنی دیگر، درایور باید از برنامه افزودنی دیگر پشتیبانی کند.

افزونه‌ها همچنین می‌توانند ساختارهای سفارشی را برای همراهی با عملوندهای افزونه اعلام کنند.

/**
 * Quantization parameters for {@link EXAMPLE_TENSOR}.
 */
typedef struct ExampleTensorParams {
    double scale;
    int64_t zeroPoint;
} ExampleTensorParams;

از برنامه های افزودنی در کلاینت های NNAPI استفاده کنید

فایل runtime/include/NeuralNetworksExtensions.h (C API) پشتیبانی برنامه افزودنی زمان اجرا را فراهم می کند. این بخش یک نمای کلی از C API را ارائه می دهد.

برای بررسی اینکه آیا یک دستگاه از یک برنامه افزودنی پشتیبانی می کند یا خیر، از ANeuralNetworksDevice_getExtensionSupport استفاده کنید.

bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
                                                   &isExtensionSupported),
         ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
    // The device supports the extension.
    ...
}

برای ساخت یک مدل با عملوند پسوند، از ANeuralNetworksModel_getExtensionOperandType برای بدست آوردن نوع عملوند استفاده کنید و ANeuralNetworksModel_addOperand فراخوانی کنید.

int32_t type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperandType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_TENSOR, &type),
         ANEURALNETWORKS_NO_ERROR);
ANeuralNetworksOperandType operandType{
        .type = type,
        .dimensionCount = dimensionCount,
        .dimensions = dimensions,
};
CHECK_EQ(ANeuralNetworksModel_addOperand(model, &operandType), ANEURALNETWORKS_NO_ERROR);

به صورت اختیاری، از ANeuralNetworksModel_setOperandExtensionData برای مرتبط کردن داده های اضافی با یک عملوند افزونه استفاده کنید.

ExampleTensorParams params{
        .scale = 0.5,
        .zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, &params, sizeof(params)),
         ANEURALNETWORKS_NO_ERROR);

برای ساخت یک مدل با عملیات افزونه، از ANeuralNetworksModel_getExtensionOperationType برای بدست آوردن نوع عملیات استفاده کنید و ANeuralNetworksModel_addOperation فراخوانی کنید.

ANeuralNetworksOperationType type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperationType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_FUNCTION,
                                                        &type),
         ANEURALNETWORKS_NO_ERROR);
CHECK_EQ(ANeuralNetworksModel_addOperation(model, type, inputCount, inputs, outputCount, outputs),
         ANEURALNETWORKS_NO_ERROR);

پشتیبانی برنامه افزودنی را به درایور NNAPI اضافه کنید

درایورها برنامه های افزودنی پشتیبانی شده را از طریق روش IDevice::getSupportedExtensions گزارش می دهند. لیست برگشتی باید حاوی ورودی باشد که هر برنامه افزودنی پشتیبانی شده را توصیف کند.

Extension {
    .name = EXAMPLE_EXTENSION_NAME,
    .operandTypes = {
        {
            .type = EXAMPLE_SCALAR,
            .isTensor = false,
            .byteSize = 8,
        },
        {
            .type = EXAMPLE_TENSOR,
            .isTensor = true,
            .byteSize = 8,
        },
    },
}

از 32 بیتی که برای شناسایی انواع و عملیات استفاده می شود، بیت های High Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX پیشوند پسوند و بیت های Low Model::ExtensionTypeEncoding::LOW_BITS_TYPE نشان دهنده نوع یا عملکرد پسوند است.

هنگام مدیریت یک عملیات یا نوع عملوند، درایور باید پیشوند پسوند را بررسی کند. اگر پیشوند پسوند دارای مقدار غیر صفر باشد، نوع عملیات یا عملوند یک نوع پسوند است. اگر مقدار 0 باشد، نوع عملیات یا عملوند یک نوع پسوند نیست.

برای نگاشت پیشوند به نام برنامه افزودنی، آن را در model.extensionNameToPrefix جستجو کنید. نگاشت از پیشوند به نام پسوند یک مطابقت یک به یک (bijection) برای یک مدل معین است. مقادیر مختلف پیشوند ممکن است با یک نام پسوند در مدل های مختلف مطابقت داشته باشد.

درایور باید عملیات برنامه افزودنی و انواع داده را تأیید کند زیرا زمان اجرا NNAPI نمی تواند عملیات برنامه افزودنی و انواع داده را تأیید کند.

عملوندهای برنامه افزودنی می‌توانند داده‌های مرتبطی در operand.extraParams.extension داشته باشند، که زمان اجرا به عنوان یک حباب داده خام با اندازه دلخواه در نظر می‌گیرد.

عملیات OEM و انواع داده

NNAPI دارای یک عملیات OEM و انواع داده های OEM است تا به سازندگان دستگاه امکان ارائه عملکردهای سفارشی و خاص درایور را بدهد. این نوع عملیات و داده فقط توسط برنامه های OEM استفاده می شود. معنای عملیات OEM و انواع داده ها مختص OEM است و می تواند در هر زمان تغییر کند. عملیات OEM و انواع داده ها با استفاده از OperationType::OEM_OPERATION ، OperandType::OEM و OperandType::TENSOR_OEM_BYTE کدگذاری می شوند.

،

افزونه‌های فروشنده API شبکه‌های عصبی (NNAPI) که در اندروید 10 معرفی شده‌اند، مجموعه‌ای از عملیات‌ها و انواع داده‌های تعریف‌شده توسط فروشنده هستند. در دستگاه‌هایی که NN HAL 1.2 یا بالاتر دارند، درایورها می‌توانند با پشتیبانی از برنامه‌های افزودنی فروشنده مربوطه، عملیات تسریع‌شده سخت‌افزاری سفارشی را ارائه دهند. برنامه های افزودنی فروشنده رفتار عملیات موجود را تغییر نمی دهند.

برنامه‌های افزودنی فروشنده جایگزین ساختارمندتری برای عملیات OEM و انواع داده‌ها، که در Android 10 منسوخ شده بودند، ارائه می‌کنند. برای اطلاعات بیشتر، عملیات OEM و انواع داده را ببینید.

لیست مجاز استفاده از برنامه های افزودنی

برنامه‌های افزودنی فروشنده فقط می‌توانند توسط برنامه‌های Android مشخص شده و باینری‌های بومی در پارتیشن‌های /product ، /vendor ، /odm و /data استفاده شوند. برنامه‌ها و باینری‌های بومی واقع در پارتیشن /system نمی‌توانند از پسوندهای فروشنده استفاده کنند.

فهرستی از برنامه‌های Android و باینری‌های مجاز برای استفاده از پسوندهای فروشنده NNAPI در /vendor/etc/nnapi_extensions_app_allowlist ذخیره می‌شود. هر خط از فایل حاوی یک ورودی جدید است. یک ورودی می‌تواند یک مسیر باینری بومی باشد که با یک اسلش (/)، به عنوان مثال، /data/foo ، یا نام یک بسته برنامه Android، به عنوان مثال، com.foo.bar پیشوند باشد.

لیست مجاز از کتابخانه مشترک زمان اجرا NNAPI اعمال می شود. این کتابخانه در برابر استفاده تصادفی محافظت می کند، اما نه در برابر دور زدن عمدی توسط یک برنامه که مستقیماً از رابط HAL درایور NNAPI استفاده می کند.

تعریف پسوند فروشنده

فروشنده یک فایل هدر با تعریف پسوند ایجاد و نگهداری می کند. یک مثال کامل از یک تعریف پسوند را می توان در example/fibonacci/FibonacciExtension.h یافت.

هر برنامه افزودنی باید یک نام منحصر به فرد داشته باشد که با نام دامنه معکوس فروشنده شروع می شود.

const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";

نام به عنوان فضای نام برای عملیات و انواع داده عمل می کند. NNAPI از این نام برای تمایز بین پسوندهای فروشنده استفاده می کند.

عملیات ها و انواع داده ها به روشی مشابه موارد در runtime/include/NeuralNetworks.h اعلان می شوند.

enum {
    /**
     * A custom scalar type.
     */
    EXAMPLE_SCALAR = 0,

    /**
     * A custom tensor type.
     *
     * Attached to this tensor is {@link ExampleTensorParams}.
     */
    EXAMPLE_TENSOR = 1,
};

enum {
    /**
     * Computes example function.
     *
     * Inputs:
     * * 0: A scalar of {@link EXAMPLE_SCALAR}.
     *
     * Outputs:
     * * 0: A tensor of {@link EXAMPLE_TENSOR}.
     */
    EXAMPLE_FUNCTION = 0,
};

یک عملیات پسوندی می تواند از هر نوع عملوند، از جمله انواع عملوند غیرپسوندی و انواع عملوند از پسوندهای دیگر استفاده کند. هنگام استفاده از یک نوع عملوند از یک برنامه افزودنی دیگر، درایور باید از برنامه افزودنی دیگر پشتیبانی کند.

افزونه‌ها همچنین می‌توانند ساختارهای سفارشی را برای همراهی با عملوندهای افزونه اعلام کنند.

/**
 * Quantization parameters for {@link EXAMPLE_TENSOR}.
 */
typedef struct ExampleTensorParams {
    double scale;
    int64_t zeroPoint;
} ExampleTensorParams;

از برنامه های افزودنی در کلاینت های NNAPI استفاده کنید

فایل runtime/include/NeuralNetworksExtensions.h (C API) پشتیبانی برنامه افزودنی زمان اجرا را فراهم می کند. این بخش یک نمای کلی از C API را ارائه می دهد.

برای بررسی اینکه آیا یک دستگاه از یک برنامه افزودنی پشتیبانی می کند یا خیر، از ANeuralNetworksDevice_getExtensionSupport استفاده کنید.

bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
                                                   &isExtensionSupported),
         ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
    // The device supports the extension.
    ...
}

برای ساخت یک مدل با عملوند پسوند، از ANeuralNetworksModel_getExtensionOperandType برای بدست آوردن نوع عملوند استفاده کنید و ANeuralNetworksModel_addOperand فراخوانی کنید.

int32_t type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperandType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_TENSOR, &type),
         ANEURALNETWORKS_NO_ERROR);
ANeuralNetworksOperandType operandType{
        .type = type,
        .dimensionCount = dimensionCount,
        .dimensions = dimensions,
};
CHECK_EQ(ANeuralNetworksModel_addOperand(model, &operandType), ANEURALNETWORKS_NO_ERROR);

به صورت اختیاری، از ANeuralNetworksModel_setOperandExtensionData برای مرتبط کردن داده های اضافی با یک عملوند افزونه استفاده کنید.

ExampleTensorParams params{
        .scale = 0.5,
        .zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, &params, sizeof(params)),
         ANEURALNETWORKS_NO_ERROR);

برای ساخت یک مدل با عملیات افزونه، از ANeuralNetworksModel_getExtensionOperationType برای بدست آوردن نوع عملیات استفاده کنید و ANeuralNetworksModel_addOperation فراخوانی کنید.

ANeuralNetworksOperationType type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperationType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_FUNCTION,
                                                        &type),
         ANEURALNETWORKS_NO_ERROR);
CHECK_EQ(ANeuralNetworksModel_addOperation(model, type, inputCount, inputs, outputCount, outputs),
         ANEURALNETWORKS_NO_ERROR);

پشتیبانی برنامه افزودنی را به درایور NNAPI اضافه کنید

درایورها برنامه های افزودنی پشتیبانی شده را از طریق روش IDevice::getSupportedExtensions گزارش می دهند. لیست برگشتی باید حاوی ورودی باشد که هر برنامه افزودنی پشتیبانی شده را توصیف کند.

Extension {
    .name = EXAMPLE_EXTENSION_NAME,
    .operandTypes = {
        {
            .type = EXAMPLE_SCALAR,
            .isTensor = false,
            .byteSize = 8,
        },
        {
            .type = EXAMPLE_TENSOR,
            .isTensor = true,
            .byteSize = 8,
        },
    },
}

از 32 بیتی که برای شناسایی انواع و عملیات استفاده می شود، بیت های High Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX پیشوند پسوند و بیت های Low Model::ExtensionTypeEncoding::LOW_BITS_TYPE نشان دهنده نوع یا عملکرد پسوند است.

هنگام مدیریت یک عملیات یا نوع عملوند، درایور باید پیشوند پسوند را بررسی کند. اگر پیشوند پسوند دارای مقدار غیر صفر باشد، نوع عملیات یا عملوند یک نوع پسوند است. اگر مقدار 0 باشد، نوع عملیات یا عملوند یک نوع پسوند نیست.

برای نگاشت پیشوند به نام برنامه افزودنی، آن را در model.extensionNameToPrefix جستجو کنید. نگاشت از پیشوند به نام پسوند یک مطابقت یک به یک (bijection) برای یک مدل معین است. مقادیر مختلف پیشوند ممکن است با یک نام پسوند در مدل های مختلف مطابقت داشته باشد.

درایور باید عملیات برنامه افزودنی و انواع داده را تأیید کند زیرا زمان اجرا NNAPI نمی تواند عملیات برنامه افزودنی و انواع داده را تأیید کند.

عملوندهای برنامه افزودنی می‌توانند داده‌های مرتبطی در operand.extraParams.extension داشته باشند، که زمان اجرا به عنوان یک حباب داده خام با اندازه دلخواه در نظر می‌گیرد.

عملیات OEM و انواع داده

NNAPI دارای یک عملیات OEM و انواع داده های OEM است تا به سازندگان دستگاه امکان ارائه عملکردهای سفارشی و خاص درایور را بدهد. این نوع عملیات و داده فقط توسط برنامه های OEM استفاده می شود. معنای عملیات OEM و انواع داده ها مختص OEM است و می تواند در هر زمان تغییر کند. عملیات OEM و انواع داده ها با استفاده از OperationType::OEM_OPERATION ، OperandType::OEM و OperandType::TENSOR_OEM_BYTE کدگذاری می شوند.