Android 10 中引入的神经⽹络 API (NNAPI) 供应商扩展是供应商定义的操作和数据类型的集合。在运⾏ NN HAL 1.2 或更⾼版本的设备上,驱动程序可以通过⽀持相应的供应商扩展来提供⾃定义硬件加速操作。供应商扩展不会修改现有操作的⾏为。
供应商扩展提供了比 OEM 操作和数据类型更结构化的替代方案,后者已在 Android 10 中被弃用。如需了解详情,请参阅 OEM 操作和数据类型。
扩展程序使用许可名单
供应商扩展只能供 /product
、/vendor
、/odm
和 /data
分区上明确指定的 Android 应用和原生⼆进制⽂件使用。/system
分区上的应用和原生⼆进制⽂件⽆法使用供应商扩展。
允许使用 NNAPI 供应商扩展程序的 Android 应用和⼆进制⽂件的列表存储在 /vendor/etc/nnapi_extensions_app_allowlist
中。⽂件的每⼀⾏都包含⼀个新条目。条目可以是带有斜杠 (/) 前缀的原生⼆进制⽂件路径(例如,/data/foo
),也可以是 Android 应用软件包的名称(例如,com.foo.bar
)。
许可名单由 NNAPI 运⾏时共享库强制执⾏。此库可防⽌意外使用,但不能防⽌应用通过直接使用 NNAPI 驱动程序 HAL 接口进⾏蓄意规避。
供应商扩展定义
供应商创建并维护带有扩展定义的头⽂件。扩展定义的完整示例可以在 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, ¶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
中具有关联数据,运⾏时将其视为任意⼤⼩的原始数据 blob。
OEM 操作和数据类型
NNAPI 具有 OEM 操作和 OEM 数据类型,允许设备制造商提供⾃定义的、特定于驱动程序的功能。这些操作和数据类型仅供 OEM 应用使用。OEM 操作和数据类型的语义是 OEM 特定的,并且可能随时更改。OEM 操作和数据类型使用 OperationType::OEM_OPERATION
、OperandType::OEM
和 OperandType::TENSOR_OEM_BYTE
进⾏编码。