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

Расширения поставщиков Neural Networks 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 .