Le estensioni del fornitore dell'API Neural Networks (NNAPI), introdotte in Android 10, sono raccolte di operazioni e tipi di dati definiti dal fornitore. Sui dispositivi che eseguono NN HAL 1.2 o versioni successive, i driver possono fornire operazioni personalizzate con accelerazione hardware supportando le estensioni del fornitore corrispondenti. Le estensioni del fornitore non modificano il comportamento delle operazioni esistenti.
Le estensioni del fornitore forniscono un'alternativa più strutturata ai tipi di dati e alle operazioni OEM, che sono stati ritirati in Android 10. Per saperne di più, vedi Operazioni e tipi di dati OEM.
Lista consentita per l'utilizzo delle estensioni
Le estensioni del fornitore possono essere utilizzate solo da app Android e
binari nativi specificati in modo esplicito nelle partizioni /product
, /vendor
, /odm
e /data
.
Le app e i binari nativi che si trovano nella partizione /system
non possono utilizzare le estensioni del fornitore.
Un elenco di app e binari Android autorizzati a utilizzare le estensioni del fornitore NNAPI è
memorizzato in /vendor/etc/nnapi_extensions_app_allowlist
. Ogni riga del file
contiene una nuova voce. Una voce può essere un percorso binario nativo con il prefisso
di una barra (/), ad esempio /data/foo
, o un nome di un pacchetto di app per Android, ad
esempio com.foo.bar
.
La lista consentita viene applicata dalla libreria condivisa di runtime NNAPI. Questa libreria protegge dall'utilizzo accidentale, ma non dall'elusione intenzionale da parte di un'app che utilizza direttamente l'interfaccia HAL del driver NNAPI.
Definizione dell'estensione del fornitore
Il fornitore crea e gestisce un file di intestazione con la definizione dell'estensione. Un
esempio completo di definizione di un'estensione è disponibile in
example/fibonacci/FibonacciExtension.h
.
Ogni estensione deve avere un nome univoco che inizi con il nome di dominio inverso del fornitore.
const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";
Il nome funge da spazio dei nomi per operazioni e tipi di dati. La NNAPI utilizza questo nome per distinguere le estensioni del fornitore.
Le operazioni e i tipi di dati vengono dichiarati in modo simile a quelli di
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,
};
Un'operazione di estensione può utilizzare qualsiasi tipo di operando, inclusi i tipi di operando non di estensione e i tipi di operando di altre estensioni. Quando utilizzi un tipo di operando di un'altra estensione, il driver deve supportare l'altra estensione.
Le estensioni possono anche dichiarare strutture personalizzate per accompagnare gli operandi delle estensioni.
/**
* Quantization parameters for {@link EXAMPLE_TENSOR}.
*/
typedef struct ExampleTensorParams {
double scale;
int64_t zeroPoint;
} ExampleTensorParams;
Utilizzare le estensioni nei client NNAPI
Il file
runtime/include/NeuralNetworksExtensions.h
(API C) fornisce il supporto per l'estensione di runtime. Questa sezione fornisce una
panoramica dell'API C.
Per verificare se un dispositivo supporta un'estensione, utilizza
ANeuralNetworksDevice_getExtensionSupport
.
bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
&isExtensionSupported),
ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
// The device supports the extension.
...
}
Per creare un modello con un operando di estensione, utilizza
ANeuralNetworksModel_getExtensionOperandType
per ottenere il tipo di operando e chiama
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);
Se vuoi, utilizza
ANeuralNetworksModel_setOperandExtensionData
per associare dati aggiuntivi a un operando di estensione.
ExampleTensorParams params{
.scale = 0.5,
.zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, ¶ms, sizeof(params)),
ANEURALNETWORKS_NO_ERROR);
Per creare un modello con un'operazione di estensione, utilizza
ANeuralNetworksModel_getExtensionOperationType
per ottenere il tipo di operazione e chiama
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);
Aggiungere il supporto delle estensioni a un driver NNAPI
I conducenti segnalano le estensioni supportate tramite il metodo
IDevice::getSupportedExtensions
. L'elenco restituito deve contenere una voce che descriva ogni estensione supportata.
Extension {
.name = EXAMPLE_EXTENSION_NAME,
.operandTypes = {
{
.type = EXAMPLE_SCALAR,
.isTensor = false,
.byteSize = 8,
},
{
.type = EXAMPLE_TENSOR,
.isTensor = true,
.byteSize = 8,
},
},
}
Dei 32 bit utilizzati per identificare tipi e operazioni, i bit
Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX
più significativi sono il
prefisso dell'estensione e i bit
Model::ExtensionTypeEncoding::LOW_BITS_TYPE
meno significativi rappresentano il tipo o l'operazione
dell'estensione.
Quando gestisce un tipo di operazione o operando, il driver deve controllare il prefisso
dell'estensione. Se il prefisso dell'estensione ha un valore diverso da zero, il tipo di operazione o operando
è un tipo di estensione. Se il valore è 0
, il tipo di operazione o operando
non è un tipo di estensione.
Per mappare il prefisso a un nome di estensione, cercalo in
model.extensionNameToPrefix
.
La mappatura dal prefisso al nome dell'estensione è una corrispondenza biunivoca
(bigezione) per un determinato modello. Valori di prefisso diversi potrebbero corrispondere
allo stesso nome di estensione in modelli diversi.
Il driver deve convalidare le operazioni di estensione e i tipi di dati perché il runtime NNAPI non può convalidare particolari operazioni di estensione e tipi di dati.
Gli operandi dell'estensione possono avere dati associati in
operand.extraParams.extension
,
che il runtime considera come un blob di dati non elaborati di dimensioni arbitrarie.
Operazione OEM e tipi di dati
NNAPI dispone di un'operazione OEM e di tipi di dati OEM per consentire
ai produttori di dispositivi di fornire funzionalità personalizzate e specifiche per i driver. Questi
tipi di operazioni e dati vengono utilizzati solo dalle applicazioni OEM. La semantica dell'operazione e dei tipi di dati OEM è specifica per l'OEM e può cambiare in qualsiasi momento. I tipi di dati e le operazioni OEM
sono codificati utilizzando OperationType::OEM_OPERATION
,
OperandType::OEM
e OperandType::TENSOR_OEM_BYTE
.