Extensões de fornecedor da API Neural Networks (NNAPI), introduzidas no no Android 10, são coleções de operações definidas pelo fornecedor e tipos de dados. Em dispositivos que executam NN HAL 1.2 ou superior, os drivers podem fornecer operações personalizadas aceleradas por hardware e suporte às extensões de fornecedor correspondentes. As extensões do fornecedor não modificam o comportamento das operações atuais.
As extensões de fornecedores oferecem uma alternativa mais estruturada à operação de OEM e tipos de dados, que foram descontinuados no Android 10. Para mais informações, consulte OEMs e tipos de dados.
Lista de permissões de uso de extensões
As extensões de fornecedor só podem ser usadas por apps Android explicitamente especificados e
binários nativos nas partições /product
, /vendor
, /odm
e /data
.
Os apps e binários nativos localizados na partição /system
não podem usar o fornecedor.
extensões.
Uma lista de apps Android e binários permitidos para usar extensões do fornecedor NNAPI está
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 com o prefixo
uma barra (/), por exemplo, /data/foo
ou o nome de um pacote de app Android, para
exemplo: com.foo.bar
.
A lista de permissões é aplicada a partir da biblioteca compartilhada do ambiente de execução da NNAPI. Esta biblioteca protege contra o uso acidental, mas não contra evasão deliberada usando um app usando diretamente a interface HAL do driver da NNAPI.
Definição da extensão do fornecedor
O fornecedor cria e mantém um arquivo principal com a definição da extensão. Um
um exemplo completo de uma 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 inverso 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 isso para distinguir as extensões do fornecedor.
Operações e tipos de dados são declarados de forma semelhante aos
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 um que não seja de extensão de outras extensões e de operandos. 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 os operandos de extensão.
/**
* Quantization parameters for {@link EXAMPLE_TENSOR}.
*/
typedef struct ExampleTensorParams {
double scale;
int64_t zeroPoint;
} ExampleTensorParams;
Usar extensões em clientes NNAPI
A
runtime/include/NeuralNetworksExtensions.h
(API C) fornece suporte à extensão de ambiente de execução. Nesta seção,
da API C.
Para verificar se um dispositivo é compatível com 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 obter o tipo de operando e chamar
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 saber o tipo de operação e chamar
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 de extensão a um driver de NNAPI
Os motoristas relataram as extensões compatíveis com o
IDevice::getSupportedExtensions
. A lista retornada deve conter uma entrada que descreva cada 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, o alto
Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX
bits é a extensão
prefix e o valor
Model::ExtensionTypeEncoding::LOW_BITS_TYPE
bits representa o tipo ou a operação
da extensão.
Ao processar um tipo de operação ou operando, o motorista precisa verificar a extensão
. Se o prefixo de extensão tiver um valor diferente de zero, a operação ou o operando
é um tipo de extensão. Se o valor for 0
, o tipo de operação ou de operando
não é 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. Diferentes valores de prefixo podem corresponder ao
mesmo nome de extensão em modelos diferentes.
O driver precisa validar operações de extensão e tipos de dados porque a NNAPI O ambiente de execução não valida determinadas operações de extensão e tipos de dados.
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ção de OEM e tipos de dados
A NNAPI tem uma operação e tipos de dados OEM para permitir
os fabricantes de dispositivos forneçam funcionalidades personalizadas e específicas para os drivers. Esses
operação e tipos de dados são usados apenas por aplicativos OEM. A semântica do OEM
operação e dados são específicos do OEM e podem mudar a qualquer momento. O OEM
operação e os tipos de dados são codificados usando OperationType::OEM_OPERATION
,
OperandType::OEM
e OperandType::TENSOR_OEM_BYTE
.