Anbietererweiterungen der Neural Networks API (NNAPI), die in Android 10 eingeführt wurden, sind Sammlungen von vom Anbieter definierten Vorgängen und Datentypen. Auf Geräten mit NN HAL 1.2 oder höher können Treiber benutzerdefinierte hardwarebeschleunigte Vorgänge bereitstellen, indem sie entsprechende Anbietererweiterungen unterstützen. Anbietererweiterungen ändern das Verhalten vorhandener Vorgänge nicht.
Anbietererweiterungen sind eine strukturiertere Alternative zum OEM-Betrieb und Datentypen, die in Android 10 eingestellt wurden. Weitere Informationen finden Sie unter OEM-Betrieb und -Datentypen:
Zulassungsliste für die Nutzung von Erweiterungen
Anbietererweiterungen können nur von explizit angegebenen Android-Apps und
native Binärdateien in den Partitionen /product
, /vendor
, /odm
und /data
.
Für Apps und native Binärdateien auf der Partition /system
können keine Anbietererweiterungen verwendet werden.
Eine Liste der Android-Apps und -Binärprogramme, die NNAPI-Anbietererweiterungen verwenden dürfen, ist
gespeichert in /vendor/etc/nnapi_extensions_app_allowlist
. Jede Zeile der Datei
enthält einen neuen Eintrag. Ein Eintrag kann ein nativer Binärpfad mit dem Präfix
einen Schrägstrich (/), z. B. /data/foo
oder der Name eines Android-App-Pakets für
Beispiel: com.foo.bar
.
Die Zulassungsliste wird von der gemeinsam genutzten NNAPI-Laufzeitbibliothek erzwungen. Diese Bibliothek vor versehentlicher Nutzung, aber nicht vor absichtlicher Umgehung durch eine Anwendung direkt über die HAL-Schnittstelle des NNAPI-Treibers.
Definition der Anbietererweiterung
Der Anbieter erstellt und verwaltet eine Headerdatei mit der Erweiterungsdefinition. A
Ein vollständiges Beispiel für eine Erweiterungsdefinition finden Sie unter
example/fibonacci/FibonacciExtension.h
Jede Erweiterung muss einen eindeutigen Namen haben, der mit dem umgekehrten Domainnamen des Anbieters beginnt.
const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";
Der Name dient als Namespace für Vorgänge und Datentypen. Die NNAPI verwendet diese Namen, um zwischen den Anbietererweiterungen zu unterscheiden.
Vorgänge und Datentypen werden ähnlich wie die in
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,
};
Für einen Erweiterungsvorgang kann jeder Operandentyp verwendet werden, einschließlich Operandentypen, die nicht zu Erweiterungen gehören, und Operandentypen aus anderen Erweiterungen. Wenn Sie einen Operandentyp aus einer anderen Erweiterung verwenden, muss der Treiber die andere Erweiterung unterstützen.
Erweiterungen können auch benutzerdefinierte Strukturen für Erweiterungsoperanden deklarieren.
/**
* Quantization parameters for {@link EXAMPLE_TENSOR}.
*/
typedef struct ExampleTensorParams {
double scale;
int64_t zeroPoint;
} ExampleTensorParams;
Erweiterungen in NNAPI-Clients verwenden
Die
runtime/include/NeuralNetworksExtensions.h
(C API) unterstützt Laufzeiterweiterung. Dieser Abschnitt bietet einen Überblick über die C API.
Wenn Sie prüfen möchten, ob ein Gerät eine Erweiterung unterstützt, verwenden Sie
ANeuralNetworksDevice_getExtensionSupport
bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
&isExtensionSupported),
ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
// The device supports the extension.
...
}
Wenn Sie ein Modell mit einem Erweiterungsoperanden erstellen möchten, verwenden Sie ANeuralNetworksModel_getExtensionOperandType
, um den Operandentyp abzurufen, und rufen Sie ANeuralNetworksModel_addOperand
auf.
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);
Optional:
ANeuralNetworksModel_setOperandExtensionData
, um zusätzliche Daten mit einem Erweiterungsoperanden zu verknüpfen.
ExampleTensorParams params{
.scale = 0.5,
.zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, ¶ms, sizeof(params)),
ANEURALNETWORKS_NO_ERROR);
Wenn Sie ein Modell mit einem Erweiterungsvorgang erstellen möchten, verwenden Sie ANeuralNetworksModel_getExtensionOperationType
, um den Vorgangstyp abzurufen, und rufen Sie ANeuralNetworksModel_addOperation
auf.
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);
Erweiterungsunterstützung für einen NNAPI-Treiber hinzufügen
Treiber melden unterstützte Erweiterungen über das
IDevice::getSupportedExtensions
. Die zurückgegebene Liste muss einen Eintrag für jede unterstützte Erweiterung enthalten.
Extension {
.name = EXAMPLE_EXTENSION_NAME,
.operandTypes = {
{
.type = EXAMPLE_SCALAR,
.isTensor = false,
.byteSize = 8,
},
{
.type = EXAMPLE_TENSOR,
.isTensor = true,
.byteSize = 8,
},
},
}
Von den 32 Bits, die zum Identifizieren von Typen und Vorgängen verwendet werden, sind die hohen Bits Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX
das Prefix der Erweiterung und die niedrigen Bits Model::ExtensionTypeEncoding::LOW_BITS_TYPE
der Typ oder Vorgang der Erweiterung.
Beim Umgang mit einem Vorgang oder Operandentyp muss der Treiber das Erweiterungspräfix prüfen. Wenn das Erweiterungspräfix einen Wert ungleich Null hat, ist der Vorgangs- oder Operandentyp ein Erweiterungstyp. Wenn der Wert 0
ist, ist der Vorgang oder Operand kein Erweiterungstyp.
Wenn Sie das Präfix einem Erweiterungsnamen zuordnen möchten, suchen Sie in model.extensionNameToPrefix
danach.
Die Zuordnung vom Präfix zum Erweiterungsnamen ist für ein bestimmtes Modell eine Eins-zu-eins-Beziehung (Bijektion). Unterschiedliche Präfixwerte können demselben Erweiterungsnamen in verschiedenen Modellen entsprechen.
Der Treiber muss Erweiterungsvorgänge und Datentypen validieren, da die NNAPI Laufzeit bestimmte Erweiterungsvorgänge und Datentypen nicht validieren kann.
Erweiterungsoperanden können Daten in operand.extraParams.extension
haben, die von der Laufzeit als Rohdaten-Blob beliebiger Größe behandelt werden.
OEM-Betrieb und Datentypen
NNAPI bietet einen OEM-Betrieb und OEM-Datentypen,
Geräteherstellern zur Bereitstellung benutzerdefinierter, treiberspezifischer Funktionen. Diese
Vorgang und Datentypen werden nur von OEM-Anwendungen verwendet. Die Semantik der OEM-Funktionen und -Datentypen ist OEM-spezifisch und kann sich jederzeit ändern. Der OEM
Vorgang und Datentypen werden mit OperationType::OEM_OPERATION
,
OperandType::OEM
und OperandType::TENSOR_OEM_BYTE
.