Расширения поставщика для 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) обеспечивает поддержку расширений во время выполнения. В этом разделе приведена общая информация об API на языке C.
Чтобы проверить, поддерживает ли устройство расширение, используйте 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, ¶ms, 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 кодируются с помощью OperationType::OEM_OPERATION, OperandType::OEM и OperandType::TENSOR_OEM_BYTE.