As extensões de fornecedor da API Neural Networks (NNAPI), introduzidas no Android 10, são coleções de operações e tipos de dados definidos pelo fornecedor. Em dispositivos com o NN HAL 1.2 ou mais recente, os drivers podem fornecer operações personalizadas aceleradas por hardware com suporte a extensões de fornecedor correspondentes. As extensões de fornecedor não modificam o comportamento das operações atuais.
As extensões de fornecedor oferecem uma alternativa mais estruturada para operações e tipos de dados OEM, que foram descontinuados no Android 10. Para mais informações, consulte Operações e tipos de dados OEM.
Lista de permissões de uso de extensões
As extensões de fornecedor só podem ser usadas por apps Android e binários nativos especificados explicitamente nas partições /product, /vendor, /odm e /data.
Os apps e binários nativos localizados na partição /system não podem usar extensões de fornecedor.
Uma lista de apps e binários Android permitidos para usar extensões de fornecedor da NNAPI é armazenada em /vendor/etc/nnapi_extensions_app_allowlist. Cada linha do arquivo contém uma nova entrada. Uma entrada pode ser um caminho binário nativo prefixado com uma barra (/), por exemplo, /data/foo, ou um nome de pacote do app Android, por exemplo, com.foo.bar.
A lista de permissões é aplicada pela biblioteca compartilhada de execução da NNAPI. Essa biblioteca protege contra o uso acidental, mas não contra a burla deliberada de um app que usa diretamente a interface HAL do driver da NNAPI.
Definição de extensão do fornecedor
O fornecedor cria e mantém um arquivo principal com a definição de extensão. Um
exemplo completo de definição de extensão pode ser encontrado em
example/fibonacci/FibonacciExtension.h.
Cada extensão precisa ter um nome exclusivo que comece com o nome de domínio reverso do fornecedor.
const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";
O nome atua como um namespace para operações e tipos de dados. A NNAPI usa esse nome para distinguir entre extensões de fornecedor.
As operações e os tipos de dados são declarados de maneira semelhante aos de
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,
};
Uma operação de extensão pode usar qualquer tipo de operando, incluindo tipos de operandos não extensíveis e tipos de operandos de outras extensões. Ao usar um tipo de operando de outra extensão, o driver precisa oferecer suporte à outra extensão.
As extensões também podem declarar estruturas personalizadas para acompanhar operandos de extensão.
/**
* Quantization parameters for {@link EXAMPLE_TENSOR}.
*/
typedef struct ExampleTensorParams {
double scale;
int64_t zeroPoint;
} ExampleTensorParams;
Usar extensões em clientes da NNAPI
O arquivo
runtime/include/NeuralNetworksExtensions.h
(API C) oferece suporte a extensões de execução. Esta seção oferece uma visão geral da API C.
Para verificar se um dispositivo oferece suporte a uma extensão, use
ANeuralNetworksDevice_getExtensionSupport.
bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
&isExtensionSupported),
ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
// The device supports the extension.
...
}
Para criar um modelo com um operando de extensão, use
ANeuralNetworksModel_getExtensionOperandType
para receber o tipo de operando e chame
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);
Opcionalmente, use
ANeuralNetworksModel_setOperandExtensionData
para associar dados adicionais a um operando de extensão.
ExampleTensorParams params{
.scale = 0.5,
.zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, ¶ms, sizeof(params)),
ANEURALNETWORKS_NO_ERROR);
Para criar um modelo com uma operação de extensão, use
ANeuralNetworksModel_getExtensionOperationType
para receber o tipo de operação e chame
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);
Adicionar suporte a extensões a um driver da NNAPI
Os drivers informam as extensões com suporte pelo
IDevice::getSupportedExtensions
método. A lista retornada precisa conter uma entrada que descreva cada extensão com suporte.
Extension {
.name = EXAMPLE_EXTENSION_NAME,
.operandTypes = {
{
.type = EXAMPLE_SCALAR,
.isTensor = false,
.byteSize = 8,
},
{
.type = EXAMPLE_TENSOR,
.isTensor = true,
.byteSize = 8,
},
},
}
Dos 32 bits usados para identificar tipos e operações, os
Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX
bits altos são o
prefixo da extensão, e os
Model::ExtensionTypeEncoding::LOW_BITS_TYPE
bits baixos representam o tipo ou a operação
da extensão.
Ao processar um tipo de operação ou operando, o driver precisa verificar o prefixo da extensão. Se o prefixo da extensão tiver um valor diferente de zero, a operação ou o tipo de operando será um tipo de extensão. Se o valor for 0, a operação ou o tipo de operando não será um tipo de extensão.
Para mapear o prefixo para um nome de extensão, procure-o em
model.extensionNameToPrefix.
O mapeamento do prefixo para o nome da extensão é uma correspondência um para um (bijeção) para um determinado modelo. Valores de prefixo diferentes podem corresponder ao mesmo nome de extensão em modelos diferentes.
O driver precisa validar operações e tipos de dados de extensão porque o ambiente de execução da NNAPI não pode validar operações e tipos de dados de extensão específicos.
Os operandos de extensão podem ter dados associados em
operand.extraParams.extension,
que o ambiente de execução trata como um blob de dados brutos de tamanho arbitrário.
Operações e tipos de dados OEM
A NNAPI tem uma operação OEM e tipos de dados OEM para permitir que os fabricantes de dispositivos ofereçam funcionalidades personalizadas e específicas do driver. Esses tipos de operação e dados são usados apenas por aplicativos OEM. A semântica das operações e dos tipos de dados OEM é específica do OEM e pode mudar a qualquer momento. As operações e os tipos de dados OEM são codificados usando OperationType::OEM_OPERATION, OperandType::OEM e OperandType::TENSOR_OEM_BYTE.