Die in Android 10 eingeführten Anbietererweiterungen für die Neural Networks API (NNAPI) sind Sammlungen von anbieterdefinierten 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 bieten eine strukturiertere Alternative zu OEM-Vorgangs- und Datentypen, die in Android 10 eingestellt wurden. Weitere Informationen finden Sie unter OEM-Betrieb und Datentypen.
Zulassungsliste für die Verwendung von Erweiterungen
Herstellererweiterungen können nur von explizit angegebenen Android-Apps und nativen Binärdateien auf den Partitionen /product
, /vendor
, /odm
und /data
verwendet werden.
Apps und native Binärdateien auf der Partition /system
können keine Anbietererweiterungen verwenden.
Eine Liste der Android-Apps und Binärdateien, die NNAPI-Anbietererweiterungen verwenden dürfen, wird in /vendor/etc/nnapi_extensions_app_allowlist
gespeichert. Jede Zeile der Datei enthält einen neuen Eintrag. Ein Eintrag kann ein nativer Binärpfad sein, dem ein Schrägstrich (/) vorangestellt ist, z. B. /data/foo
, oder der Name eines Android-App-Pakets, z. B. com.foo.bar
.
Die Zulassungsliste wird von der gemeinsam genutzten NNAPI-Laufzeitbibliothek erzwungen. Diese Bibliothek schützt vor versehentlicher Verwendung, aber nicht vor absichtlicher Umgehung durch eine App, die die NNAPI-Treiber-HAL-Schnittstelle direkt verwendet.
Definition von Anbietererweiterungen
Der Anbieter erstellt und verwaltet eine Headerdatei mit der Erweiterungsdefinition. 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 diesen Namen, um zwischen Anbietererweiterungen zu unterscheiden.
Vorgänge und Datentypen werden ähnlich wie in runtime/include/NeuralNetworks.h
deklariert.
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 zur Erweiterung 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 deklarieren, die mit Erweiterungsoperanden einhergehen.
/**
* Quantization parameters for {@link EXAMPLE_TENSOR}.
*/
typedef struct ExampleTensorParams {
double scale;
int64_t zeroPoint;
} ExampleTensorParams;
Erweiterungen in NNAPI-Clients verwenden
Die Datei runtime/include/NeuralNetworksExtensions.h
(C API) bietet Unterstützung für Laufzeit-Erweiterungen. Dieser Abschnitt bietet einen Überblick über die C API.
Mit ANeuralNetworksDevice_getExtensionSupport
können Sie prüfen, ob ein Gerät eine Erweiterung unterstützt.
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 können Sie ANeuralNetworksModel_setOperandExtensionData
verwenden, um einem Erweiterungsoperanden zusätzliche Daten zuzuordnen.
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);
Unterstützung für Erweiterungen in einem NNAPI-Treiber hinzufügen
Fahrer melden unterstützte Erweiterungen über die Methode IDevice::getSupportedExtensions
. Die zurückgegebene Liste muss einen Eintrag enthalten, der jede unterstützte Erweiterung beschreibt.
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 oberen Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX
Bits das Präfix der Erweiterung und die unteren Model::ExtensionTypeEncoding::LOW_BITS_TYPE
Bits der Typ oder Vorgang der Erweiterung.
Beim Verarbeiten eines Vorgangs oder Operandentyps muss der Treiber das Erweiterungspräfix prüfen. Wenn das Erweiterungspräfix einen Wert ungleich null hat, ist der Vorgang oder Operandentyp ein Erweiterungstyp. Wenn der Wert 0
ist, ist der Vorgang oder Operandentyp kein Erweiterungstyp.
Wenn Sie das Präfix einem Erweiterungsnamen zuordnen möchten, suchen Sie es in model.extensionNameToPrefix
.
Die Zuordnung vom Präfix zum Erweiterungsnamen ist für ein bestimmtes Modell eine Eins-zu-eins-Entsprechung (Bijektion). Unterschiedliche Präfixwerte können in verschiedenen Modellen demselben Erweiterungsnamen entsprechen.
Der Treiber muss Erweiterungsvorgänge und Datentypen validieren, da die NNAPI-Laufzeit bestimmte Erweiterungsvorgänge und Datentypen nicht validieren kann.
Erweiterungsoperanden können zugeordnete Daten in operand.extraParams.extension
haben, die von der Laufzeit als Rohdaten-Blob beliebiger Größe behandelt werden.
OEM-Vorgang und Datentypen
NNAPI bietet einen OEM-Vorgang und OEM-Datentypen, damit Gerätehersteller benutzerdefinierte, treiberspezifische Funktionen bereitstellen können. Diese Vorgangs- und Datentypen werden nur von OEM-Anwendungen verwendet. Die Semantik von OEM-Vorgängen und Datentypen ist OEM-spezifisch und kann sich jederzeit ändern. Die OEM-Vorgangs- und Datentypen werden mit OperationType::OEM_OPERATION
, OperandType::OEM
und OperandType::TENSOR_OEM_BYTE
codiert.