Extensions de fournisseur de l'API Neural Networks (NNAPI), introduites dans Android 10, sont d'opérations et de types de données définis par le fournisseur. Sur les appareils exécutant NN HAL 1.2 ou version ultérieure, les pilotes peuvent fournir des opérations personnalisées avec accélération matérielle en prenant en charge les extensions de fournisseurs correspondantes. Les extensions de fournisseurs ne modifient pas le comportement des opérations existantes.
Les extensions de fournisseur offrent une alternative plus structurée aux opérations OEM et de données, qui ont été abandonnés dans Android 10. Pour en savoir plus, consultez la section Fonctionnement de l'OEM et types de données.
Liste d'autorisation d'utilisation des extensions
Les extensions du fournisseur ne peuvent être utilisées que par des applications Android et des binaires natifs spécifiés explicitement sur les partitions /product
, /vendor
, /odm
et /data
.
Les applications et les binaires natifs situés sur la partition /system
ne peuvent pas utiliser le fournisseur
.
La liste des applications et binaires Android autorisés à utiliser les extensions de fournisseur NNAPI est
stocké dans /vendor/etc/nnapi_extensions_app_allowlist
. Chaque ligne du fichier contient une nouvelle entrée. Une entrée peut être un chemin binaire natif précédé d'une barre oblique (/), par exemple /data/foo
, ou le nom d'un package d'application Android, par exemple com.foo.bar
.
La liste d'autorisation est appliquée à partir de la bibliothèque partagée de l'environnement d'exécution NNAPI. Cette bibliothèque protège contre l'utilisation accidentelle, mais pas contre le contournement délibéré une application directement à l'aide de l'interface HAL du pilote NNAPI.
Définition de l'extension du fournisseur
Le fournisseur crée et gère un fichier d'en-tête avec la définition de l'extension. Vous trouverez un exemple complet de définition d'extension dans example/fibonacci/FibonacciExtension.h
.
Chaque extension doit avoir un nom unique qui commence par le nom de domaine inversé du fournisseur.
const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";
Le nom sert d'espace de noms pour les opérations et les types de données. NNAPI utilise ce nom pour distinguer les extensions du fournisseur.
Les opérations et les types de données sont déclarés de la même manière que dans
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,
};
Une opération d'extension peut utiliser n'importe quel type d'opérande, y compris les opérandes sans extension et les types d'opérandes d'autres extensions. Lorsque vous utilisez un type d'opérande d'une autre extension, le pilote doit être compatible avec l'autre extension.
Les extensions peuvent également déclarer des structures personnalisées pour accompagner les opérandes d'extension.
/**
* Quantization parameters for {@link EXAMPLE_TENSOR}.
*/
typedef struct ExampleTensorParams {
double scale;
int64_t zeroPoint;
} ExampleTensorParams;
Utiliser des extensions dans les clients NNAPI
La
runtime/include/NeuralNetworksExtensions.h
(API C) prend en charge l'extension d'exécution. Cette section présente l'API C.
Pour vérifier si un appareil est compatible avec une extension, utilisez
ANeuralNetworksDevice_getExtensionSupport
bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
&isExtensionSupported),
ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
// The device supports the extension.
...
}
Pour créer un modèle avec une opérande d'extension, utilisez ANeuralNetworksModel_getExtensionOperandType
pour obtenir le type d'opérande et appelez 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);
Vous pouvez également utiliser ANeuralNetworksModel_setOperandExtensionData
pour associer des données supplémentaires à un opérande d'extension.
ExampleTensorParams params{
.scale = 0.5,
.zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, ¶ms, sizeof(params)),
ANEURALNETWORKS_NO_ERROR);
Pour créer un modèle avec une opération d'extension, utilisez ANeuralNetworksModel_getExtensionOperationType
pour obtenir le type d'opération et appelez 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);
Ajouter la prise en charge des extensions à un pilote NNAPI
Les pilotes signalent les extensions compatibles via la méthode IDevice::getSupportedExtensions
. La liste renvoyée doit contenir une entrée décrivant chaque élément compatible
.
Extension {
.name = EXAMPLE_EXTENSION_NAME,
.operandTypes = {
{
.type = EXAMPLE_SCALAR,
.isTensor = false,
.byteSize = 8,
},
{
.type = EXAMPLE_TENSOR,
.isTensor = true,
.byteSize = 8,
},
},
}
Sur les 32 bits utilisés pour identifier les types et les opérations, les bits supérieurs Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX
correspondent au préfixe de l'extension, et les bits inférieurs Model::ExtensionTypeEncoding::LOW_BITS_TYPE
représentent le type ou l'opération de l'extension.
Lors du traitement d'une opération ou d'un type d'opérande, le pilote doit vérifier l'extension
. Si le préfixe d'extension a une valeur non nulle, l'opération ou l'opérande
type est un type d'extension. Si la valeur est 0
, le type d'opération ou d'opérande n'est pas un type d'extension.
Pour mapper le préfixe à un nom d'extension, recherchez-le dans
model.extensionNameToPrefix
Le mappage entre le préfixe et le nom de l'extension est une correspondance
(bijection) pour un modèle donné. Différentes valeurs de préfixe peuvent correspondre au
le même nom d'extension dans différents modèles.
Le pilote doit valider les opérations et les types de données d'extension, car l'environnement d'exécution NNAPI ne peut pas valider certaines opérations et types de données d'extension.
Les opérandes d'extension peuvent
avoir des données associées dans
operand.extraParams.extension
,
que l'environnement d'exécution traite comme un blob de données brutes de taille arbitraire.
Opération et types de données OEM
NNAPI dispose d'une opération OEM et de types de données OEM
aux fabricants de périphériques de fournir
des fonctionnalités personnalisées et spécifiques au pilote. Ces types d'opérations et de données ne sont utilisés que par les applications OEM. La sémantique des produits OEM
et les types de données sont spécifiques aux OEM et peuvent changer à tout moment. L'OEM
et les types de données sont encodés à l'aide de OperationType::OEM_OPERATION
,
OperandType::OEM
et OperandType::TENSOR_OEM_BYTE
.