Расширения поставщика

Расширения API нейронных сетей (NNAPI), представленные в Android 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 бит, используемых для идентификации типов и операций, старшие Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX обозначают префикс расширения, а младшие биты Model::ExtensionTypeEncoding::LOW_BITS_TYPE представляют тип или операцию расширения.

При обработке типа операции или операнда драйвер должен проверить префикс расширения. Если префикс расширения имеет ненулевое значение, операция или тип операнда является типом расширения. Если значение равно 0 , операция или тип операнда не является типом расширения.

Чтобы сопоставить префикс с именем расширения, найдите его в model.extensionNameToPrefix . Сопоставление префикса с именем расширения представляет собой однозначное соответствие (биекцию) для данной модели. Разные значения префикса могут соответствовать одному и тому же имени расширения в разных моделях.

Драйвер должен проверять операции расширения и типы данных, поскольку среда выполнения NNAPI не может проверять конкретные операции расширения и типы данных.

Операнды расширения могут иметь связанные данные в operand.extraParams.extension , которые среда выполнения обрабатывает как необработанный блок данных произвольного размера.

Типы данных и режимы работы OEM-производителя

NNAPI имеет операции OEM и типы данных OEM, позволяющие производителям устройств предоставлять пользовательскую функциональность, специфичную для драйверов. Эти операции и типы данных используются только в приложениях OEM. Семантика операций и типов данных OEM является специфичной для OEM и может изменяться в любое время. Операции и типы данных OEM кодируются с помощью OperationType::OEM_OPERATION , OperandType::OEM и OperandType::TENSOR_OEM_BYTE .