Android 10 推出的 Neural Networks API (NNAPI) 供應商擴充功能,是供應商定義的作業和資料型別集合。在搭載 NN HAL 1.2 以上版本的裝置上,驅動程式可以支援相應的供應商擴充功能,提供自訂的硬體加速作業。供應商擴充功能不會修改現有作業的行為。
相較於 Android 10 中已淘汰的 OEM 作業和資料型別,供應商擴充功能提供更結構化的替代方案。詳情請參閱「OEM 作業和資料類型」。
擴充功能使用許可清單
只有明確指定的 Android 應用程式和 /product
、/vendor
、/odm
和 /data
分割區上的原生二進位檔,才能使用供應商擴充功能。位於 /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
編碼。