Die Android Neural Networks API (NNAPI) ist eine Android C API, die zum Ausführen rechenintensive Vorgänge für maschinelles Lernen auf Android-Geräten. NNAPI bietet eine Basisfunktionalität für allgemeine Frameworks für maschinelles Lernen wie TensorFlow Lite und Caffe2, die neuronale Netzwerke erstellen und trainieren. Die API ist verfügbar auf allen Android-Geräten mit Android 8.1 (API-Level 27) oder höher.
NNAPI unterstützt die Ableitung durch die Anwendung von Daten von Android-Geräten auf frühere für die Entwicklung trainierter, von Entwickler*innen definierter Modelle. Beispiele für Ableitungen sind die Klassifizierung Bilder vorhersagen, das Nutzerverhalten vorhersagen und passende Antworten Suchanfrage.
Die Inferenz auf dem Gerät hat viele Vorteile:
- Latenz: Sie müssen keine Anfrage über eine Netzwerkverbindung senden und auf eine Antwort warten. Das kann z. B. für Videoanwendungen von entscheidender Bedeutung sein, die aufeinanderfolgende Bilder einer Kamera verarbeitet.
- Verfügbarkeit: Die Anwendung wird auch außerhalb der Netzwerkabdeckung ausgeführt.
- Geschwindigkeit: Neue Hardware speziell für die Verarbeitung neuronaler Netzwerke ist wesentlich schneller als eine Allzweck-CPU allein.
- Datenschutz: Die Daten werden ausschließlich auf dem Android-Gerät gespeichert.
- Kosten: Wenn alle Berechnungen in einem Gerät ausgeführt werden, ist keine Serverfarm erforderlich. auf dem Android-Gerät.
Entwickler sollten außerdem einige Vor- und Nachteile beachten:
- Systemauslastung: Die Bewertung neuronaler Netzwerke beinhaltet viele und dadurch den Akkuverbrauch erhöhen. Sie sollten überlegen, Überwachen Sie den Akkuzustand, wenn dies ein Problem für Ihre App ist, insbesondere für Ihre App. für Berechnungen mit langer Ausführungszeit.
- Anwendungsgröße: Achten Sie auf die Größe Ihrer Modelle. Modelle können mehrere Megabyte Speicherplatz einnehmen. Wenn Sie große Modelle in Ihrem APK bündeln zu stark auf Ihre Nutzer auswirken würde, sollten Sie erwägen, die nach der Installation der App, bei Verwendung kleinerer Modelle oder beim Ausführen für Berechnungen in der Cloud. NNAPI bietet keine Funktion zum Ausführen in der Cloud.
Weitere Informationen finden Sie in der Beispiel für die Android Neural Networks API ein Beispiel für die Verwendung von NNAPI.
Informationen zur Laufzeit der Neural Networks API
NNAPI soll von Bibliotheken, Frameworks und Tools für maschinelles Lernen aufgerufen werden mit denen Entwickler ihre Modelle extern trainieren und auf Android-Geräten bereitstellen können Geräte. Apps verwenden NNAPI normalerweise nicht direkt, sondern übergeordnete Frameworks für maschinelles Lernen. Diese Frameworks wiederum könnten NNAPI zur Ausführung hardwarebeschleunigter Inferenzvorgänge auf unterstützten Geräten.
Basierend auf den Anforderungen der App und den Hardwarefunktionen eines Android-Geräts kann die neuronale Netzwerklaufzeit von Android die Rechenlast auf den verfügbaren On-Device-Prozessoren, einschließlich dedizierter neuronale Netzwerkhardware, Grafikprozessoren (GPUs) und digitale Signale Prozessoren (DSP).
Für Android-Geräte ohne speziellen Anbietertreiber: NNAPI-Laufzeit führt die Anfragen auf der CPU aus.
Abbildung 1 zeigt die allgemeine Systemarchitektur für NNAPI.
<ph type="x-smartling-placeholder">![](https://developer.android.google.cn/static/ndk/images/nnapi/nnapi_architecture.png?authuser=7&hl=de)
Neural Networks API-Programmiermodell
Um Berechnungen mit NNAPI durchzuführen, müssen Sie zunächst eine gerichtete Grafik, die die durchzuführenden Berechnungen definiert. Diese Berechnungsgrafik, kombiniert mit Ihren Eingabedaten (z. B. Gewichtungen und Verzerrungen, die von einem ML-Framework), bildet das Modell für die NNAPI-Laufzeitbewertung.
NNAPI verwendet vier Hauptabstraktionen:
- Modell: Ein Berechnungsdiagramm mathematischer Operationen und der Konstante
Werte, die durch einen Trainingsprozess erlernt wurden. Diese Vorgänge sind spezifisch für
neuronalen Netzen. Sie umfassen zweidimensionale
Faltung,
Logistik
(Sigmoid)
Aktivierung,
rektifizierte Lineareinheit
(ReLU)-Aktivierung und mehr. Das Erstellen eines Modells ist ein synchroner Vorgang.
Nach der Erstellung kann sie über Threads und Kompilierungen hinweg wiederverwendet werden.
In NNAPI wird ein Modell als
ANeuralNetworksModel
Instanz. - Kompilierung: Stellt eine Konfiguration zum Kompilieren eines NNAPI-Modells in
Low-Level-Code. Das Erstellen einer Kompilierung ist ein synchroner Vorgang. Einmal
erstellt wurde, kann er in Threads und Ausführungen wiederverwendet werden. In
NNAPI wird jede Kompilierung als
ANeuralNetworksCompilation
Instanz. - Arbeitsspeicher: Steht für gemeinsamen Arbeitsspeicher, Dateien mit Zuordnung im Arbeitsspeicher und ähnlichen Arbeitsspeicher
Puffer. Mit einem Arbeitsspeicherpuffer kann die NNAPI-Laufzeit Daten an Treiber übertragen
effizienter gestalten. Eine App erstellt in der Regel
einen Zwischenspeicher für gemeinsamen Arbeitsspeicher,
enthält jeden Tensor, der zum Definieren eines Modells erforderlich ist. Sie können auch Speicher
Puffer zum Speichern der Ein- und Ausgaben für eine Ausführungsinstanz. In NNAPI
wird jeder Zwischenspeicher als
ANeuralNetworksMemory
Instanz. Ausführung: Schnittstelle zum Anwenden eines NNAPI-Modells auf eine Reihe von Eingaben und für die Ergebnisse zu sammeln. Die Ausführung kann synchron oder asynchron durchgeführt werden.
Bei asynchroner Ausführung mehrere Threads auf dieselbe Ausführung warten kann. Nach Abschluss dieser Ausführung sind alle Threads veröffentlicht.
In NNAPI wird jede Ausführung als
ANeuralNetworksExecution
Instanz.
Abbildung 2 zeigt den grundlegenden Programmierablauf.
<ph type="x-smartling-placeholder">![](https://developer.android.google.cn/static/ndk/images/nnapi/nnapi_flow.png?authuser=7&hl=de)
Der Rest dieses Abschnitts beschreibt die Schritte zur Einrichtung Ihres NNAPI-Modells, um Berechnungen durchführen, das Modell kompilieren und das kompilierte Modell ausführen.
Zugriff auf Trainingsdaten gewähren
Ihre trainierten Daten zu Gewichtungen und Verzerrungen sind wahrscheinlich in einer Datei gespeichert. Um die
NNAPI-Laufzeit mit effizientem Zugriff auf diese Daten, erstellen Sie eine
ANeuralNetworksMemory
indem Sie die Methode
ANeuralNetworksMemory_createFromFd()
und übergeben den Dateideskriptor der geöffneten Datendatei. Außerdem
Speicherschutz-Flags und einen Offset für die Region des gemeinsamen Arbeitsspeichers
beginnt in der Datei.
// Create a memory buffer from the file that contains the trained data
ANeuralNetworksMemory* mem1 = NULL;
int fd = open("training_data", O_RDONLY);
ANeuralNetworksMemory_createFromFd(file_size, PROT_READ, fd, 0, &mem1);
Obwohl wir in diesem Beispiel nur eine
ANeuralNetworksMemory
ist es möglich, mehr als eine Methode
ANeuralNetworksMemory
-Instanz für mehrere Dateien.
Native Hardwarepuffer verwenden
Sie können native Hardwarepuffer verwenden.
für Modelleingaben, -ausgaben und konstante Operandenwerte. In bestimmten Fällen
NNAPI-Beschleuniger kann auf
AHardwareBuffer
ohne dass der Treiber die Daten kopieren muss. AHardwareBuffer
hat viele
Konfigurationen und nicht jeder NNAPI-Beschleuniger unterstützt möglicherweise alle
für diese Konfigurationen. Lesen Sie aufgrund dieser Einschränkung die Einschränkungen
aufgeführt in
Referenzdokumentation zu ANeuralNetworksMemory_createFromAHardwareBuffer
und im Voraus auf Zielgeräten zu testen, um Kompilierungen und Ausführungen sicherzustellen
die AHardwareBuffer
verwenden, wie erwartet verhalten,
Gerätezuweisung, um den Beschleuniger anzugeben.
Damit die NNAPI-Laufzeit auf ein AHardwareBuffer
-Objekt zugreifen kann, erstellen Sie ein
ANeuralNetworksMemory
indem Sie die Methode
ANeuralNetworksMemory_createFromAHardwareBuffer
und übergeben den Parameter
AHardwareBuffer
-Objekt wie im folgenden Codebeispiel gezeigt:
// Configure and create AHardwareBuffer object AHardwareBuffer_Desc desc = ... AHardwareBuffer* ahwb = nullptr; AHardwareBuffer_allocate(&desc, &ahwb); // Create ANeuralNetworksMemory from AHardwareBuffer ANeuralNetworksMemory* mem2 = NULL; ANeuralNetworksMemory_createFromAHardwareBuffer(ahwb, &mem2);
Wenn NNAPI nicht mehr auf das AHardwareBuffer
-Objekt zugreifen muss, geben Sie den Parameter
entsprechende ANeuralNetworksMemory
-Instanz:
ANeuralNetworksMemory_free(mem2);
Hinweis:
- Sie können
AHardwareBuffer
nur für den gesamten Zwischenspeicher; können Sie es nicht mit einenARect
-Parameter. - Die NNAPI-Laufzeit wird nicht geleert Zwischenspeichern. Sie müssen sicherstellen, dass die Ein- und Ausgabezwischenspeicher bevor Sie die Ausführung planen.
- Es wird keine Unterstützung für Fence-Dateideskriptoren synchronisieren.
- Für
AHardwareBuffer
mit anbieterspezifischen Formaten und Nutzungsbits verwendet werden, um zu ermitteln, ob der Client oder der Fahrer Cache gespeichert werden.
Modell
Ein Modell ist die grundlegende Berechnungseinheit in NNAPI. Jedes Modell ist definiert durch einen oder mehrere Operanden und Operationen.
Operanden
Operanden sind Datenobjekte, die zum Definieren des Graphen verwendet werden. Dazu gehören die Eingaben und Ausgaben des Modells, die Zwischenknoten, die die Daten enthalten, von einer Operation zur nächsten fließt, und die Konstanten, für diese Vorgänge.
Es gibt zwei Arten von Operanden, die NNAPI-Modellen hinzugefügt werden können: Skalare und tensors.
Ein Skalar stellt einen einzelnen Wert dar. NNAPI unterstützt skalare Werte in booleschen, 16-Bit-Gleitkomma, 32-Bit-Gleitkomma, 32-Bit-Ganzzahl und ohne Vorzeichen 32-Bit-Ganzzahlformate.
Die meisten Vorgänge in NNAPI beinhalten Tensoren. Tensoren sind n-dimensionale Arrays. NNAPI unterstützt Tensoren mit 16-Bit-Gleitkomma, 32-Bit-Gleitkomma, 8-Bit quantisiert, 16 Bit quantisiert, 32 Bit-Ganzzahl und 8 Bit boolesche Werte.
Abbildung 3 zeigt beispielsweise ein Modell mit zwei Operationen: einer Addition. gefolgt von einer Multiplikation. Das Modell verwendet einen Eingabetensor und erzeugt einen Ausgabetensor
<ph type="x-smartling-placeholder">![](https://developer.android.google.cn/static/ndk/images/nnapi/nnapi_operands.png?authuser=7&hl=de)
Das obige Modell hat sieben Operanden. Diese Operanden werden implizit identifiziert durch Index der Reihenfolge, in der sie dem Modell hinzugefügt werden. Der erste Operand der hinzugefügte Index den Index 0 hat, der zweite Index 1 usw. Operanden 1, 2, 3, und 5 sind konstante Operanden.
Die Reihenfolge, in der Sie die Operanden hinzufügen, spielt keine Rolle. Zum Beispiel hat das Modell Ausgabeoperanden könnten der erste hinzugefügte Operand sein. Wichtig ist, dass Sie die korrekter Indexwert beim Verweisen auf einen Operanden.
Operanden haben Typen. Diese werden angegeben, wenn sie dem Modell hinzugefügt werden.
Ein Operand kann nicht sowohl als Eingabe als auch als Ausgabe eines Modells verwendet werden.
Jeder Operand muss entweder eine Modelleingabe, eine Konstante oder der Ausgabeoperanden von genau einen Vorgang ausführen.
Weitere Informationen zur Verwendung von Operanden finden Sie unter Weitere Informationen zu Operanden
Aufgaben und Ablauf
Ein Vorgang gibt die auszuführenden Berechnungen an. Jeder Vorgang besteht aus dieser Elemente:
- einen Vorgangstyp (z. B. Addition, Multiplikation, Faltung)
- eine Liste der Indexe der Operanden, die die Operation für die Eingabe verwendet, und
- Eine Liste der Indexe der Operanden, die der Vorgang für die Ausgabe verwendet.
Die Reihenfolge in diesen Listen ist wichtig: sieh dir die NNAPI API-Referenz für die erwarteten Eingaben und die Ausgaben für jeden Vorgangstyp.
Sie müssen dem Modell die Operanden hinzufügen, die eine Operation verbraucht oder erzeugt bevor Sie den Vorgang hinzufügen.
Die Reihenfolge, in der Sie Vorgänge hinzufügen, spielt keine Rolle. NNAPI basiert auf dem die durch den Berechnungsgraphen von Operanden und Operationen geschaffen wurden, Reihenfolge festlegen, in der Vorgänge ausgeführt werden.
Die von NNAPI unterstützte Vorgänge sind in der folgenden Tabelle zusammengefasst:
Bekanntes Problem in API-Level 28:Beim Übergeben
ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
Tensoren zu den
ANEURALNETWORKS_PAD
der unter Android 9 (API-Level 28) und höher verfügbar ist,
Die NNAPI-Ausgabe stimmt möglicherweise nicht mit der Ausgabe des übergeordneten maschinellen Lernens überein
Frameworks wie
TensorFlow Lite Ich
sollte stattdessen nur
ANEURALNETWORKS_TENSOR_FLOAT32
Das Problem wurde unter Android 10 (API-Level 29) und höher behoben.
Modelle erstellen
Im folgenden Beispiel erstellen wir das Modell mit zwei Vorgängen, das sich in Abbildung 3.
So erstellen Sie das Modell:
Rufen Sie die Methode
ANeuralNetworksModel_create()
um ein leeres Modell zu definieren.ANeuralNetworksModel* model = NULL; ANeuralNetworksModel_create(&model);
Fügen Sie Ihrem Modell die Operanden hinzu, indem Sie
ANeuralNetworks_addOperand()
Ihre Datentypen werden mithilfe derANeuralNetworksOperandType
Datenstruktur.// In our example, all our tensors are matrices of dimension [3][4] ANeuralNetworksOperandType tensor3x4Type; tensor3x4Type.type = ANEURALNETWORKS_TENSOR_FLOAT32; tensor3x4Type.scale = 0.f; // These fields are used for quantized tensors tensor3x4Type.zeroPoint = 0; // These fields are used for quantized tensors tensor3x4Type.dimensionCount = 2; uint32_t dims[2] = {3, 4}; tensor3x4Type.dimensions = dims;
// We also specify operands that are activation function specifiers ANeuralNetworksOperandType activationType; activationType.type = ANEURALNETWORKS_INT32; activationType.scale = 0.f; activationType.zeroPoint = 0; activationType.dimensionCount = 0; activationType.dimensions = NULL;
// Now we add the seven operands, in the same order defined in the diagram ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 0 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 1 ANeuralNetworksModel_addOperand(model, &activationType); // operand 2 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 3 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 4 ANeuralNetworksModel_addOperand(model, &activationType); // operand 5 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 6Für Operanden mit konstanten Werten, z. B. Gewichtungen und Verzerrungen, App aus einem Trainingsprozess bezieht, verwenden Sie
ANeuralNetworksModel_setOperandValue()
undANeuralNetworksModel_setOperandValueFromMemory()
Funktionen.Im folgenden Beispiel legen wir konstante Werte aus der Trainingsdatendatei fest. entsprechend dem Zwischenspeicher, den wir in Zugriff auf Trainingsdaten
// In our example, operands 1 and 3 are constant tensors whose values were // established during the training process const int sizeOfTensor = 3 * 4 * 4; // The formula for size calculation is dim0 * dim1 * elementSize ANeuralNetworksModel_setOperandValueFromMemory(model, 1, mem1, 0, sizeOfTensor); ANeuralNetworksModel_setOperandValueFromMemory(model, 3, mem1, sizeOfTensor, sizeOfTensor);
// We set the values of the activation operands, in our example operands 2 and 5 int32_t noneValue = ANEURALNETWORKS_FUSED_NONE; ANeuralNetworksModel_setOperandValue(model, 2, &noneValue, sizeof(noneValue)); ANeuralNetworksModel_setOperandValue(model, 5, &noneValue, sizeof(noneValue));Fügen Sie für jeden Vorgang im gerichteten Graphen, den Sie berechnen möchten, den Wert auf Ihr Modell anwenden, indem Sie die Methode
ANeuralNetworksModel_addOperation()
.Als Parameter für diesen Aufruf muss Ihre App Folgendes bereitstellen:
- Vorgangstyp
- die Anzahl der Eingabewerte
- das Array der Indexe für Eingabeoperanden
- die Anzahl der Ausgabewerte
- das Array der Indexe für Ausgabeoperanden
Beachten Sie, dass ein Operand nicht für die Ein- und Ausgabe desselben .
// We have two operations in our example // The first consumes operands 1, 0, 2, and produces operand 4 uint32_t addInputIndexes[3] = {1, 0, 2}; uint32_t addOutputIndexes[1] = {4}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD, 3, addInputIndexes, 1, addOutputIndexes);
// The second consumes operands 3, 4, 5, and produces operand 6 uint32_t multInputIndexes[3] = {3, 4, 5}; uint32_t multOutputIndexes[1] = {6}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_MUL, 3, multInputIndexes, 1, multOutputIndexes);Festlegen, welche Operanden das Modell als Ein- und Ausgaben behandeln soll das Aufrufen der
ANeuralNetworksModel_identifyInputsAndOutputs()
.// Our model has one input (0) and one output (6) uint32_t modelInputIndexes[1] = {0}; uint32_t modelOutputIndexes[1] = {6}; ANeuralNetworksModel_identifyInputsAndOutputs(model, 1, modelInputIndexes, 1 modelOutputIndexes);
Geben Sie optional an, ob
ANEURALNETWORKS_TENSOR_FLOAT32
mit einem Bereich oder einer Genauigkeit berechnet werden, die so niedrig ist wie der IEEE 754 16-Bit-Gleitkommaformat durch Aufrufen vonANeuralNetworksModel_relaxComputationFloat32toFloat16()
ANeuralNetworksModel_finish()
anrufen um die Definition Ihres Modells abzuschließen. Wenn keine Fehler auftreten, gibt den Ergebniscode zurück:ANEURALNETWORKS_NO_ERROR
ANeuralNetworksModel_finish(model);
Nachdem Sie ein Modell erstellt haben, können Sie es beliebig oft kompilieren und jedes beliebig oft kompiliert werden.
Ablauf steuern
So binden Sie den Ablauf steuern in ein NNAPI-Modell ein:
Die entsprechenden Teilgraphen für die Ausführung (
then
- undelse
-Teilgraphen) erstellen für eineIF
-Anweisung,condition
- undbody
-Teildiagramme für eineWHILE
-Schleife) als eigenständigeANeuralNetworksModel*
-Modelle:ANeuralNetworksModel* thenModel = makeThenModel(); ANeuralNetworksModel* elseModel = makeElseModel();
Erstellen Sie Operanden, die auf diese Modelle innerhalb des Modells verweisen, das die Ablauf steuern:
ANeuralNetworksOperandType modelType = { .type = ANEURALNETWORKS_MODEL, }; ANeuralNetworksModel_addOperand(model, &modelType); // kThenOperandIndex ANeuralNetworksModel_addOperand(model, &modelType); // kElseOperandIndex ANeuralNetworksModel_setOperandValueFromModel(model, kThenOperandIndex, &thenModel); ANeuralNetworksModel_setOperandValueFromModel(model, kElseOperandIndex, &elseModel);
Fügen Sie den Ablauf für die Ablaufsteuerung hinzu:
uint32_t inputs[] = {kConditionOperandIndex, kThenOperandIndex, kElseOperandIndex, kInput1, kInput2, kInput3}; uint32_t outputs[] = {kOutput1, kOutput2}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_IF, std::size(inputs), inputs, std::size(output), outputs);
Compilation
Der Kompilierungsschritt bestimmt, auf welchen Prozessoren Ihr Modell ausgeführt wird und bittet die entsprechenden Treiber, sich auf die Ausführung vorzubereiten. Dies könnte Erzeugen von Maschinencode, der für die Prozessoren Ihres Modells spezifisch ist ausgeführt werden soll.
So kompilieren Sie ein Modell:
Rufen Sie die Methode
ANeuralNetworksCompilation_create()
zum Erstellen einer neuen Kompilierungsinstanz.// Compile the model ANeuralNetworksCompilation* compilation; ANeuralNetworksCompilation_create(model, &compilation);
Optional können Sie die Gerätezuweisung nutzen, um explizit auf welchen Geräten ausgeführt werden soll.
Sie können optional beeinflussen, wie die Laufzeit zwischen Akkuleistung wechselt. Nutzungs- und Ausführungsgeschwindigkeit. Rufen Sie hierzu einfach
ANeuralNetworksCompilation_setPreference()
// Ask to optimize for low power consumption ANeuralNetworksCompilation_setPreference(compilation, ANEURALNETWORKS_PREFER_LOW_POWER);
Sie können folgende Einstellungen festlegen:
ANEURALNETWORKS_PREFER_LOW_POWER
: Die Ausführung sollte so erfolgen, dass die Akkuentladung minimiert wird. Das ist wünschenswert, für häufig ausgeführte Kompilierungen.ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER
: Eine einzelne Antwort sollte so schnell wie möglich zurückgegeben werden, auch wenn dadurch erhöht den Stromverbrauch. Das ist die Standardeinstellung.ANEURALNETWORKS_PREFER_SUSTAINED_SPEED
: Sie bevorzugen das Maximieren des Durchsatzes aufeinanderfolgender Frames, z. B. wenn aufeinanderfolgende Bilder der Kamera verarbeitet.
Optional können Sie das Kompilierungs-Caching einrichten, indem Sie folgenden Befehl aufrufen:
ANeuralNetworksCompilation_setCaching
// Set up compilation caching ANeuralNetworksCompilation_setCaching(compilation, cacheDir, token);
getCodeCacheDir()
verwenden fürcacheDir
. Die angegebenetoken
muss für jedes Modell innerhalb der Anwendung.Schließen Sie die Kompilierungsdefinition ab, indem Sie
ANeuralNetworksCompilation_finish()
Wenn keine Fehler vorliegen, gibt diese Funktion den Ergebniscode zurück:ANEURALNETWORKS_NO_ERROR
ANeuralNetworksCompilation_finish(compilation);
Geräteerkennung und -zuweisung
Auf Android-Geräten mit Android 10 (API-Level 29) und höher bietet NNAPI Funktionen, mit denen ML-Framework-Bibliotheken und -Apps Informationen zu den verfügbaren Geräten und die Geräte, die verwendet werden sollen, Ausführung. Wenn Sie Informationen über die verfügbaren Geräte angeben, können Apps die genaue Version der Treiber auf einem Gerät, um bekannte Inkompatibilitäten. Apps können festlegen, auf welchen Geräten um verschiedene Abschnitte eines Modells auszuführen, auf dem sie bereitgestellt werden.
Geräteerkennung
Verwenden Sie
ANeuralNetworks_getDeviceCount
um die Anzahl der verfügbaren Geräte zu ermitteln. Verwenden Sie für jedes Gerät
ANeuralNetworks_getDevice
um eine ANeuralNetworksDevice
-Instanz auf einen Verweis auf dieses Gerät festzulegen.
Sobald du eine Gerätereferenz hast, kannst du zusätzliche Informationen zu mit den folgenden Funktionen:
ANeuralNetworksDevice_getFeatureLevel
ANeuralNetworksDevice_getName
ANeuralNetworksDevice_getType
ANeuralNetworksDevice_getVersion
Gerätezuweisung
Verwenden Sie
ANeuralNetworksModel_getSupportedOperationsForDevices
um zu ermitteln, welche Vorgänge eines Modells auf bestimmten Geräten ausgeführt werden können.
Um zu steuern, welche Beschleuniger für die Ausführung verwendet werden sollen, rufen Sie
ANeuralNetworksCompilation_createForDevices
anstelle von ANeuralNetworksCompilation_create
.
Verwende wie gewohnt das resultierende ANeuralNetworksCompilation
-Objekt.
Die Funktion gibt einen Fehler zurück, wenn das bereitgestellte Modell Vorgänge enthält, die
wird von den ausgewählten Geräten nicht unterstützt.
Wenn mehrere Geräte angegeben sind, sorgt die Laufzeit für die Verteilung geräteübergreifend arbeiten.
Ähnlich wie bei anderen Geräten wird die NNAPI-CPU-Implementierung durch ein
ANeuralNetworksDevice
mit dem Namen nnapi-reference
und dem Typ
ANEURALNETWORKS_DEVICE_TYPE_CPU
Beim Anrufen
ANeuralNetworksCompilation_createForDevices
, die CPU-Implementierung ist nicht
zur Bewältigung von Fehlern bei der Modellkompilierung und -ausführung verwendet wird.
Es liegt in der Verantwortung einer Anwendung, ein Modell in Untermodelle zu unterteilen,
auf den angegebenen Geräten ausgeführt werden kann. Anwendungen, die keine manuellen Schritte ausführen müssen
sollte bei der Partitionierung weiterhin die einfachere
ANeuralNetworksCompilation_create
damit alle verfügbaren Geräte (einschließlich der CPU) genutzt werden,
Modell. Wenn das Modell von den angegebenen Geräten nicht vollständig unterstützt werden konnte
mit ANeuralNetworksCompilation_createForDevices
,
ANEURALNETWORKS_BAD_DATA
zurückgegeben.
Modellpartitionierung
Wenn für das Modell mehrere Geräte verfügbar sind, wird die NNAPI-Laufzeit
und verteilt die Arbeit
auf alle Geräte. Wenn beispielsweise mehr als ein Gerät
bereitgestellt ANeuralNetworksCompilation_createForDevices
, alle angegebenen
werden bei der Zuweisung der Arbeit berücksichtigt. Wenn das CPU-Gerät
nicht in der Liste enthalten ist, wird die CPU-Ausführung deaktiviert. Bei Verwendung von ANeuralNetworksCompilation_create
werden alle verfügbaren Geräte berücksichtigt, einschließlich der CPU.
Die Verteilung erfolgt, indem für jedes Gerät ein Gerät aus der Liste der verfügbaren Geräte ausgewählt wird.
die Vorgänge im Modell, das Gerät, das den Betrieb und
die beste Leistung, d.h. die schnellste Ausführungszeit,
den niedrigsten Energieverbrauch, abhängig von der Ausführungspräferenz, die durch
Kundschaft. Dieser Partitionierungsalgorithmus berücksichtigt mögliche
Ineffizienzen aufgrund der E/A zwischen den verschiedenen Prozessoren.
mehrere Prozessoren angeben (entweder explizit bei
ANeuralNetworksCompilation_createForDevices
oder implizit durch Verwendung
ANeuralNetworksCompilation_create
) ist es wichtig, ein Profil für die
.
Informationen dazu, wie Ihr Modell durch NNAPI partitioniert wurde, finden Sie in der
Android-Protokolle für eine Nachricht (auf INFO-Ebene mit dem Tag ExecutionPlan
):
ModelBuilder::findBestDeviceForEachOperation(op-name): device-index
op-name
ist der beschreibende Name des Vorgangs in der Grafik.
device-index
ist der Index des Kandidatengeräts in der Geräteliste.
Diese Liste wird ANeuralNetworksCompilation_createForDevices
zur Verfügung gestellt
oder, wenn Sie ANeuralNetworksCompilation_createForDevices
verwenden, die Liste der Geräte
zurückgegeben, wenn Iteration über alle Geräte mit ANeuralNetworks_getDeviceCount
und
ANeuralNetworks_getDevice
Die Nachricht (auf INFO-Ebene mit dem Tag ExecutionPlan
):
ModelBuilder::partitionTheWork: only one best device: device-name
Diese Meldung gibt an, dass das gesamte Diagramm auf dem Gerät beschleunigt wurde.
device-name
Umsetzung
Beim Ausführungsschritt wird das Modell auf eine Reihe von Eingaben angewendet Die Berechnungen werden in einem oder mehreren Nutzerzwischenspeichern oder Arbeitsspeicherbereichen ausgegeben, die Ihre App zugewiesen sind.
So führen Sie ein kompiliertes Modell aus:
Rufen Sie die Methode
ANeuralNetworksExecution_create()
zum Erstellen einer neuen Ausführungsinstanz.// Run the compiled model against a set of inputs ANeuralNetworksExecution* run1 = NULL; ANeuralNetworksExecution_create(compilation, &run1);
Geben Sie an, wo die App die Eingabewerte für die Berechnung liest. Ihre App kann Eingabewerte entweder aus einem Nutzerzwischenspeicher oder einem zugewiesenen Speicherplatz lesen durch einen Anruf
ANeuralNetworksExecution_setInput()
oderANeuralNetworksExecution_setInputFromMemory()
.// Set the single input to our sample model. Since it is small, we won't use a memory buffer float32 myInput[3][4] = { ...the data... }; ANeuralNetworksExecution_setInput(run1, 0, NULL, myInput, sizeof(myInput));
Geben Sie an, wo die Anwendung die Ausgabewerte schreibt. Ihre Anwendung kann Ausgabewerte entweder in eine oder einen zugewiesenen Speicherplatz haben, indem Sie
ANeuralNetworksExecution_setOutput()
oderANeuralNetworksExecution_setOutputFromMemory()
.// Set the output float32 myOutput[3][4]; ANeuralNetworksExecution_setOutput(run1, 0, NULL, myOutput, sizeof(myOutput));
Planen Sie den Start der Ausführung, indem Sie die Methode
ANeuralNetworksExecution_startCompute()
. Wenn keine Fehler vorliegen, gibt diese Funktion den Ergebniscode zurück:ANEURALNETWORKS_NO_ERROR
// Starts the work. The work proceeds asynchronously ANeuralNetworksEvent* run1_end = NULL; ANeuralNetworksExecution_startCompute(run1, &run1_end);
Rufen Sie die
ANeuralNetworksEvent_wait()
auf. um auf den Abschluss der Ausführung zu warten. Wenn die Ausführung erfolgreich ist, gibt diese Funktion den ErgebniscodeANEURALNETWORKS_NO_ERROR
Das Warten kann in einem anderen Thread ausgeführt werden als dem, der die Ausführung gestartet hat.// For our example, we have no other work to do and will just wait for the completion ANeuralNetworksEvent_wait(run1_end); ANeuralNetworksEvent_free(run1_end); ANeuralNetworksExecution_free(run1);
Optional können Sie einen anderen Satz von Eingaben auf das kompilierte Modell anwenden, indem Sie mit derselben Kompilierungsinstanz eine neue
ANeuralNetworksExecution
Instanz.// Apply the compiled model to a different set of inputs ANeuralNetworksExecution* run2; ANeuralNetworksExecution_create(compilation, &run2); ANeuralNetworksExecution_setInput(run2, ...); ANeuralNetworksExecution_setOutput(run2, ...); ANeuralNetworksEvent* run2_end = NULL; ANeuralNetworksExecution_startCompute(run2, &run2_end); ANeuralNetworksEvent_wait(run2_end); ANeuralNetworksEvent_free(run2_end); ANeuralNetworksExecution_free(run2);
Synchrone Ausführung
Bei der asynchronen Ausführung wird Zeit benötigt, um Threads zu erstellen und zu synchronisieren. Darüber hinaus kann die Latenz stark variieren, Verzögerungen von bis zu 500 Mikrosekunden zwischen der Benachrichtigung über einen Thread oder und der Zeitpunkt, an dem sie schließlich an einen CPU-Kern gebunden ist.
Um die Latenz zu verbessern, können Sie eine Anwendung stattdessen anweisen, eine synchrone
Inferenzaufruf an die Laufzeit. Dieser Aufruf wird erst zurückgegeben, wenn eine Inferenz
abgeschlossen sein, anstatt nach dem Start einer Inferenz zurückzukehren. Stattdessen
des Anrufs
ANeuralNetworksExecution_startCompute
Für einen asynchronen Inferenzaufruf an die Laufzeit ruft die Anwendung
ANeuralNetworksExecution_compute
um einen synchronen Aufruf an die Laufzeit zu tätigen. Ein Aufruf an
ANeuralNetworksExecution_compute
nimmt kein ANeuralNetworksEvent
und
ist nicht mit einem Aufruf von ANeuralNetworksEvent_wait
gekoppelt.
Serienausführungen
Auf Android-Geräten mit Android 10 (API-Level 29) und höher unterstützt die NNAPI Burst
Ausführungen über die
ANeuralNetworksBurst
-Objekt enthält. Burst-Ausführungen sind eine Abfolge von Ausführungen derselben Kompilierung
die sehr schnell nacheinander auftreten, z. B. wenn sie auf den Einzelbild einer Kamera
Audio-Samples nacheinander aufnehmen. Die Verwendung von ANeuralNetworksBurst
-Objekten kann
die Ausführung beschleunigen, da sie für Beschleuniger darauf hinweisen, dass Ressourcen
zwischen Ausführungen wiederverwendet werden und dass Beschleuniger in einem
für die Dauer des Bursts hoch.
ANeuralNetworksBurst
führt nur zu einer kleinen Änderung bei der normalen Ausführung
Pfad. Sie erstellen ein Burst-Objekt mit
ANeuralNetworksBurst_create
,
Dies wird im folgenden Code-Snippet gezeigt:
// Create burst object to be reused across a sequence of executions ANeuralNetworksBurst* burst = NULL; ANeuralNetworksBurst_create(compilation, &burst);
Burst-Ausführungen erfolgen synchron. Statt jedoch die
ANeuralNetworksExecution_compute
um jede Ableitung durchzuführen, kombinieren Sie die verschiedenen
ANeuralNetworksExecution
Objekte mit demselben ANeuralNetworksBurst
in Aufrufen der Funktion
ANeuralNetworksExecution_burstCompute
// Create and configure first execution object // ... // Execute using the burst object ANeuralNetworksExecution_burstCompute(execution1, burst); // Use results of first execution and free the execution object // ... // Create and configure second execution object // ... // Execute using the same burst object ANeuralNetworksExecution_burstCompute(execution2, burst); // Use results of second execution and free the execution object // ...
Geben Sie das Objekt ANeuralNetworksBurst
kostenlos mit
ANeuralNetworksBurst_free
wenn sie nicht mehr benötigt werden.
// Cleanup ANeuralNetworksBurst_free(burst);
Asynchrone Befehlswarteschlangen und Fenced-Ausführung
Ab Android 11 unterstützt NNAPI eine zusätzliche Möglichkeit,
asynchrone Ausführung über die
ANeuralNetworksExecution_startComputeWithDependencies()
. Bei dieser Methode wartet die Ausführung auf alle
zu signalisieren,
bevor die Auswertung gestartet wird. Sobald die Ausführung
abgeschlossen ist und die Ausgaben bereit sind, wird das zurückgegebene Ereignis
signalisiert.
Je nachdem, auf welchen Geräten die Ausführung erfolgt, wird das Ereignis möglicherweise durch eine
Sync Fence. Ich
muss anrufen
ANeuralNetworksEvent_wait()
um auf das Ereignis zu warten und die
bei der Ausführung verwendeten Ressourcen wiederherzustellen. Ich
können Synchronisierungszäune mithilfe von
ANeuralNetworksEvent_createFromSyncFenceFd()
,
und Sie können Synchronisierungszäune aus einem Ereignisobjekt exportieren, indem Sie
ANeuralNetworksEvent_getSyncFenceFd()
Ausgaben mit dynamischer Größe
Um Modelle zu unterstützen, bei denen die Größe der Ausgabe von der Eingabe abhängt
Daten, d. h., die Größe kann bei der Modellausführung nicht bestimmt werden
Uhrzeit – verwenden Sie
ANeuralNetworksExecution_getOutputOperandRank
und
ANeuralNetworksExecution_getOutputOperandDimensions
Das folgende Codebeispiel zeigt, wie dies funktioniert:
// Get the rank of the output uint32_t myOutputRank = 0; ANeuralNetworksExecution_getOutputOperandRank(run1, 0, &myOutputRank); // Get the dimensions of the output std::vector<uint32_t> myOutputDimensions(myOutputRank); ANeuralNetworksExecution_getOutputOperandDimensions(run1, 0, myOutputDimensions.data());
Bereinigung
Im Bereinigungsschritt werden interne Ressourcen freigegeben, Berechnung.
// Cleanup ANeuralNetworksCompilation_free(compilation); ANeuralNetworksModel_free(model); ANeuralNetworksMemory_free(mem1);
Fehlerverwaltung und CPU-Fallback
Tritt während der Partitionierung ein Fehler auf und ein Treiber kann ein oder wenn ein Treiber ein kompiliertes Modell nicht ausführen kann, NNAPI greift möglicherweise auf seine eigene CPU-Implementierung der einen oder mehrerer Geschäftsabläufe.
Wenn der NNAPI-Client optimierte Versionen des Vorgangs enthält (z. B. z. B. TFLite), kann es vorteilhaft sein, das CPU-Fallback zu deaktivieren und Fehler mit der optimierten Vorgangsimplementierung des Clients beheben.
Wenn in Android 10 die Kompilierung mit
ANeuralNetworksCompilation_createForDevices
, dann wird das CPU-Fallback deaktiviert.
In Android P greift die NNAPI-Ausführung auf die CPU zurück, wenn die Ausführung auf dem Treiber fehlschlägt.
Dies gilt auch für Android 10, wenn ANeuralNetworksCompilation_create
statt
ANeuralNetworksCompilation_createForDevices
verwendet wird.
Bei der ersten Ausführung wird für diese eine Partition schlägt das gesamte Modell auf der CPU fehl.
Wenn die Partitionierung oder Kompilierung fehlschlägt, wird das gesamte Modell auf der CPU ausprobiert.
Es gibt Fälle, in denen einige Vorgänge auf der CPU nicht unterstützt werden. schlägt die Kompilierung oder Ausführung fehl.
Auch nach dem Deaktivieren des CPU-Fallbacks kann es noch Vorgänge im Modell geben
die auf der CPU geplant sind. Wenn die CPU in der Liste der bereitgestellten Prozessoren enthalten ist
an ANeuralNetworksCompilation_createForDevices
. Dies ist entweder die einzige
der Auftragsverarbeiter ist, der diese Vorgänge unterstützt, oder der Auftragsverarbeiter ist, der behauptet, am besten,
Leistung für diese Vorgänge hat, wird er als primärer (kein Fallback) ausgewählt.
Executor.
Um sicherzustellen, dass keine CPU ausgeführt wird, verwenden Sie ANeuralNetworksCompilation_createForDevices
während nnapi-reference
aus der Geräteliste ausgeschlossen wird.
Ab Android P ist es möglich, das Fallback bei der Ausführung auf
Die FEHLERBEHEBUNG wird erstellt, indem die Eigenschaft debug.nn.partition
auf 2 gesetzt wird.
Arbeitsspeicherdomains
In Android 11 und höher unterstützt NNAPI Speicherdomains, die eine Zuordnung bereitstellen für undurchsichtige Erinnerungen. Dadurch können Anwendungen gerätenative über mehrere Ausführungen hinweg, sodass NNAPI keine Daten unnötig, wenn aufeinanderfolgende Ausführungen für denselben Treiber erfolgen.
Das Memory Domain-Feature ist für Tensoren gedacht, die hauptsächlich intern und nicht häufig auf Clientseite zugreifen müssen. Beispiele für Zu diesen Tensoren gehören die Zustandstensoren in Sequenzmodellen. Für Tensoren, die häufigen CPU-Zugriff auf der Clientseite haben, verwenden Sie stattdessen Pools mit gemeinsam genutztem Arbeitsspeicher.
Führen Sie die folgenden Schritte aus, um einen intransparenten Speicher zuzuweisen:
Rufen Sie die Methode
ANeuralNetworksMemoryDesc_create()
-Funktion zum Erstellen eines neuen Speicherdeskriptors:// Create a memory descriptor ANeuralNetworksMemoryDesc* desc; ANeuralNetworksMemoryDesc_create(&desc);
Geben Sie alle gewünschten Eingabe- und Ausgaberollen an, indem Sie den Aufruf
ANeuralNetworksMemoryDesc_addInputRole()
undANeuralNetworksMemoryDesc_addOutputRole()
// Specify that the memory may be used as the first input and the first output // of the compilation ANeuralNetworksMemoryDesc_addInputRole(desc, compilation, 0, 1.0f); ANeuralNetworksMemoryDesc_addOutputRole(desc, compilation, 0, 1.0f);
Geben Sie optional die Speicherabmessungen an, indem Sie den Aufruf
ANeuralNetworksMemoryDesc_setDimensions()
// Specify the memory dimensions uint32_t dims[] = {3, 4}; ANeuralNetworksMemoryDesc_setDimensions(desc, 2, dims);
Schließen Sie die Deskriptordefinition ab, indem Sie
ANeuralNetworksMemoryDesc_finish()
ANeuralNetworksMemoryDesc_finish(desc);
Weisen Sie so viele Erinnerungen wie nötig zu, indem Sie den Deskriptor an
ANeuralNetworksMemory_createFromDesc()
// Allocate two opaque memories with the descriptor ANeuralNetworksMemory* opaqueMem; ANeuralNetworksMemory_createFromDesc(desc, &opaqueMem);
Geben Sie den Arbeitsspeicherdeskriptor kostenlos, wenn Sie ihn nicht mehr benötigen.
ANeuralNetworksMemoryDesc_free(desc);
Der Client darf das erstellte ANeuralNetworksMemory
-Objekt nur mit
ANeuralNetworksExecution_setInputFromMemory()
oder
ANeuralNetworksExecution_setOutputFromMemory()
entsprechend den Rollen
die im ANeuralNetworksMemoryDesc
-Objekt angegeben sind. Der Versatz und die Länge
Argumente müssen auf 0 gesetzt werden, was bedeutet, dass der gesamte Arbeitsspeicher verwendet wird. Der Kunde
kann den Speicherinhalt auch explizit festlegen oder extrahieren, indem
ANeuralNetworksMemory_copy()
Sie können intransparente Erinnerungen mit Rollen mit nicht angegebenen Dimensionen oder Rängen erstellen.
In diesem Fall schlägt die Speichererstellung möglicherweise mit dem Fehler
ANEURALNETWORKS_OP_FAILED
-Status, wenn er vom zugrunde liegenden Dienst nicht unterstützt wird.
. Dem Client wird empfohlen, eine Fallback-Logik zu implementieren, indem eine
Der Zwischenspeicher ist groß genug und wird von Ashmem- oder BLOB-Modus AHardwareBuffer
unterstützt.
Wenn NNAPI nicht mehr auf das undurchsichtige Speicherobjekt zugreifen muss, geben Sie das Objekt
entsprechende ANeuralNetworksMemory
-Instanz:
ANeuralNetworksMemory_free(opaqueMem);
Leistungsmessung
Sie können die Leistung Ihrer App bewerten, indem Sie die Ausführungszeit messen oder Profilerstellung.
Ausführungszeit
Wenn Sie die Gesamtausführungszeit über die Laufzeit ermitteln möchten, können Sie
Synchronous Execution API und messen die vom Aufruf benötigte Zeit. Wenn Sie
Sie möchten die Gesamtausführungszeit über eine niedrigere Softwareebene ermitteln.
können Sie
ANeuralNetworksExecution_setMeasureTiming
und
ANeuralNetworksExecution_getDuration
Sie erhalten:
- Ausführungszeit auf einem Beschleuniger (nicht im Treiber, der auf dem Host ausgeführt wird) des Prozessors.
- Ausführungszeit im Treiber, einschließlich der Zeit auf dem Beschleuniger.
Die Ausführungszeit im Treiber schließt Overhead wie den der Laufzeit aus und der IPC, die für die Kommunikation zwischen Laufzeit und Treiber erforderlich sind.
Diese APIs messen die Dauer zwischen der eingereichten und der abgeschlossenen Arbeit. Ereignisse und nicht die Zeit, die ein Fahrer oder Beschleuniger Inferenz, möglicherweise durch Kontextwechsel unterbrochen.
Wenn beispielsweise Inferenz 1 beginnt, stoppt der Fahrer die Arbeit. Inferenz 2, dann wird fortgesetzt und Inferenz 1 beendet, die Ausführungszeit Inferenz 1 enthält die Zeit, zu der die Arbeit angehalten wurde, um Inferenz 2 auszuführen.
Diese Zeitangaben können bei der Produktionsbereitstellung eines zum Erfassen von Telemetriedaten zur Offlinenutzung. Anhand der Zeitdaten können Sie die App modifizieren, um die Leistung zu steigern.
Beachten Sie bei der Verwendung dieser Funktion Folgendes:
- Das Erfassen von Zeitinformationen kann Leistungskosten verursachen.
- Die für die Fahrt aufgewendete Zeit kann nur vom Fahrer berechnet werden. Beschleuniger, ohne die in der NNAPI-Laufzeit und im IPC verbrachte Zeit.
- Sie können diese APIs nur mit einem
ANeuralNetworksExecution
verwenden, der erstellt mitANeuralNetworksCompilation_createForDevices
mitnumDevices = 1
. - Es ist kein Fahrer erforderlich, um Zeitinformationen zu melden.
Profil für Ihre Anwendung mit Android Systrace erstellen
Ab Android 10 generiert NNAPI automatisch systrace-Ereignissen, die mit denen Sie ein Profil für Ihre Anwendung erstellen können.
Die NNAPI Source wird mit einem parse_systrace
-Dienstprogramm geliefert, um
systrace-Ereignisse, die von Ihrer Anwendung generiert wurden, und generieren eine Tabellenansicht, in der
die in den verschiedenen Phasen des Modelllebenszyklus verbrachte Zeit (Instanziierung,
Vorbereitung, Durchführung und Beendigung der Zusammenstellung) und verschiedener Ebenen des
Anwendungen. Ihre Anwendung ist in folgende Ebenen aufgeteilt:
Application
: der HauptanwendungscodeRuntime
: NNAPI-LaufzeitIPC
: Die Kommunikation zwischen Prozessen zwischen der NNAPI-Laufzeit und dem Treiber CodeDriver
: der Beschleunigertreiberprozess.
Analysedaten zur Profilerstellung generieren
Angenommen, Sie haben sich den AOSP-Quellbaum unter $ANDROID_BUILD_TOP angesehen und mit dem Beispiel zur TFLite-Bildklassifizierung als Zielanwendung haben, können Sie die NNAPI-Profildaten mit der Methode folgenden Schritten:
- Starten Sie das Android-Systrace mit dem folgenden Befehl:
$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py -o trace.html -a org.tensorflow.lite.examples.classification nnapi hal freq sched idle load binder_driver
Der Parameter -o trace.html
gibt an, dass die Traces
geschrieben in den trace.html
. Wenn Sie für eine eigene Anwendung ein Profil erstellen,
Ersetzen Sie org.tensorflow.lite.examples.classification
durch den Prozessnamen.
die in deinem App-Manifest angegeben sind.
Dadurch wird eine Ihrer Shell-Konsolen ausgelastet. Führen Sie den Befehl nicht in
Hintergrund, da sie interaktiv auf das Beenden eines enter
wartet.
- Starten Sie nach dem Start des Systrace-Collectors Ihre App und führen Sie den Benchmark-Test zu testen.
In unserem Fall können Sie die App Image Classification (Bildklassifizierung) in Android Studio starten. oder direkt über die Benutzeroberfläche des Testtelefons, falls die App bereits installiert ist. Zum Generieren einiger NNAPI-Daten müssen Sie die App für die Verwendung von NNAPI konfigurieren, indem Sie Auswahl von NNAPI als Zielgerät im Dialogfeld für die App-Konfiguration.
Beenden Sie nach Abschluss des Tests Systrace, indem Sie
enter
auf das Konsolenterminal seit Schritt 1 aktiv ist.Führen Sie das Dienstprogramm
systrace_parser
aus, um kumulative Statistiken zu generieren:
$ANDROID_BUILD_TOP/frameworks/ml/nn/tools/systrace_parser/parse_systrace.py --total-times trace.html
Der Parser akzeptiert die folgenden Parameter:
– --total-times
: zeigt die Gesamtzeit an, die in einer Ebene verbracht wurde, einschließlich der Zeit
Wartezeit für die Ausführung eines Aufrufs einer zugrunde liegenden Ebene
– --print-detail
: gibt alle von Systrace erfassten Ereignisse aus
- --per-execution
: gibt nur die Ausführung und ihre Unterphasen aus
(anhand der Ausführungszeit) anstelle der Statistiken für alle Phasen
– --json
: erzeugt die Ausgabe im JSON-Format
Hier ein Beispiel für die Ausgabe:
===========================================================================================================================================
NNAPI timing summary (total time, ms wall-clock) Execution
----------------------------------------------------
Initialization Preparation Compilation I/O Compute Results Ex. total Termination Total
-------------- ----------- ----------- ----------- ------------ ----------- ----------- ----------- ----------
Application n/a 19.06 1789.25 n/a n/a 6.70 21.37 n/a 1831.17*
Runtime - 18.60 1787.48 2.93 11.37 0.12 14.42 1.32 1821.81
IPC 1.77 - 1781.36 0.02 8.86 - 8.88 - 1792.01
Driver 1.04 - 1779.21 n/a n/a n/a 7.70 - 1787.95
Total 1.77* 19.06* 1789.25* 2.93* 11.74* 6.70* 21.37* 1.32* 1831.17*
===========================================================================================================================================
* This total ignores missing (n/a) values and thus is not necessarily consistent with the rest of the numbers
Der Parser kann fehlschlagen, wenn die erfassten Ereignisse keinen vollständigen Anwendungs-Trace. Insbesondere kann es fehlschlagen, wenn Systrace-Ereignisse generiert wurden. zum Markieren des Endes eines Abschnitts sind im Trace ohne zugehörige Startereignis des Abschnitts. Das passiert in der Regel, wenn Ereignisse aus früheren Profilerstellungssitzung generiert, wenn Sie den Systrace-Collector starten. In diesem Fall müssen Sie die Profilerstellung noch einmal ausführen.
Statistiken für Ihren Anwendungscode zur systrace_parser-Ausgabe hinzufügen
Die Anwendung „parse_systrace“ basiert auf dem integrierten Android-Systrace Funktionalität. Sie können Traces für bestimmte Vorgänge in Ihrer Anwendung mithilfe der Systrace-API (für Java , für native Anwendungen ) mit benutzerdefinierten Ereignisnamen.
So verknüpfen Sie Ihre benutzerdefinierten Ereignisse mit Phasen des Anwendungslebenszyklus: Stellen Sie Ihrem Ereignisnamen einen der folgenden Strings voran:
[NN_LA_PI]
: Ereignis auf Anwendungsebene zur Initialisierung[NN_LA_PP]
: Ereignis auf Anwendungsebene für die Vorbereitung[NN_LA_PC]
: Ereignis auf Anwendungsebene für die Kompilierung[NN_LA_PE]
: Ereignis auf Anwendungsebene für die Ausführung
Hier ist ein Beispiel dafür, wie Sie das Beispiel für die TFLite-Bildklassifizierung ändern können
indem Sie einen runInferenceModel
-Abschnitt für die Execution
-Phase und den
Application
Ebene mit weiteren Abschnitten preprocessBitmap
, die
nicht in NNAPI-Traces berücksichtigt. Der Bereich runInferenceModel
wird
Teil der systrace-Ereignisse, die vom nnapi-Systrace-Parser verarbeitet wurden:
Kotlin
/** Runs inference and returns the classification results. */ fun recognizeImage(bitmap: Bitmap): List{ // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap") convertBitmapToByteBuffer(bitmap) Trace.endSection() // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel") long startTime = SystemClock.uptimeMillis() runInference() long endTime = SystemClock.uptimeMillis() Trace.endSection() ... return recognitions }
Java
/** Runs inference and returns the classification results. */ public ListrecognizeImage(final Bitmap bitmap) { // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap"); convertBitmapToByteBuffer(bitmap); Trace.endSection(); // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel"); long startTime = SystemClock.uptimeMillis(); runInference(); long endTime = SystemClock.uptimeMillis(); Trace.endSection(); ... Trace.endSection(); return recognitions; }
Servicequalität
Ab Android 11 ermöglicht NNAPI eine bessere Dienstqualität, So kann eine Anwendung die relativen Prioritäten ihrer Modelle angeben, die maximale Zeit, die für die Vorbereitung eines bestimmten Modells erwartet wird, sowie für die Durchführung einer bestimmten Berechnung benötigt. Außerdem werden mit Android 11 zusätzliche NNAPI-Ergebniscodes mit denen Anwendungen Fehler wie verpasste Ausführungen verstehen können Fristen einzuhalten.
Priorität einer Arbeitslast festlegen
Rufen Sie zum Festlegen der Priorität einer NNAPI-Arbeitslast den
ANeuralNetworksCompilation_setPriority()
bevor Sie ANeuralNetworksCompilation_finish()
angerufen haben.
Fristen festlegen
Anwendungen können Fristen sowohl für die Modellkompilierung als auch für die Inferenz festlegen.
- Rufen Sie zum Festlegen des Zeitlimits für die Kompilierung
ANeuralNetworksCompilation_setTimeout()
bevor SieANeuralNetworksCompilation_finish()
angerufen haben. - Rufen Sie zum Festlegen der Inferenzzeitüberschreitung Folgendes auf:
ANeuralNetworksExecution_setTimeout()
bevor du mit der Kompilierung beginnst.
Weitere Informationen zu Operanden
Im folgenden Abschnitt werden weiterführende Themen zur Verwendung von Operanden behandelt.
Quantisierte Tensoren
Ein quantisierter Tensor ist eine kompakte Möglichkeit, ein n-dimensionales Array von Gleitkommawerte.
NNAPI unterstützt asymmetrische quantisierte 8-Bit-Tensoren. Für diese Tensoren jeder Zelle wird durch eine 8-Bit-Ganzzahl dargestellt. Verknüpft mit Tensor eine Skala und ein Nullpunktwert. Diese werden verwendet, um die 8-Bit- in die dargestellten Gleitkommawerte umgewandelt werden.
Die Formel lautet:
(cellValue - zeroPoint) * scale
wobei der Wert "nullPoint" eine 32-Bit-Ganzzahl und die Skalierung eine 32-Bit-Gleitkommazahl Punktzahl.
Im Vergleich zu Tensoren von 32-Bit-Gleitkommawerten, quantisierten 8-Bit-Tensoren haben zwei Vorteile:
- Ihre Anwendung ist kleiner, da die trainierten Gewichtungen ein Viertel der Größe einnehmen von 32-Bit-Tensoren.
- Berechnungen können oft schneller ausgeführt werden. Grund dafür ist der geringere Betrag der Daten, die aus dem Speicher abgerufen werden müssen, und die Effizienz von Prozessoren, wie z. B. DSPs bei der Berechnung ganzer Zahlen.
Es ist zwar möglich, ein Gleitkommamodell in ein quantisiertes Modell zu konvertieren, Erfahrung hat gezeigt, dass bessere Ergebnisse erzielt werden, wenn man ein quantisiertes modellieren können. Tatsächlich lernt das neuronale Netzwerk, den Detaillierungsgrad jedes Werts. Für jeden quantisierten Tensor werden die Skala und ZeroPoint-Werte werden während des Trainingsprozesses bestimmt.
In NNAPI definieren Sie quantisierte Tensortypen, indem Sie das Typfeld des
ANeuralNetworksOperandType
Datenstruktur zu verstehen,
ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
Sie geben auch den „scale“- und „nullPoint“-Wert des Tensors in diesen Daten an
Struktur.
Neben den asymmetrischen quantisierten 8-Bit-Tensoren unterstützt NNAPI Folgendes:
ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
die Sie verwenden können, um GewichtungenCONV/DEPTHWISE_CONV/TRANSPOSED_CONV
Vorgänge.ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
die Sie für den internen StatusQUANTIZED_16BIT_LSTM
ANEURALNETWORKS_TENSOR_QUANT8_SYMM
die eine Eingabe fürANEURALNETWORKS_DEQUANTIZE
Optionale Operanden
Einige Vorgänge wie
ANEURALNETWORKS_LSH_PROJECTION
,
optionale Operanden. Um im Modell anzugeben, dass der optionale Operand ist
wird die Funktion
ANeuralNetworksModel_setOperandValue()
und übergeben Sie NULL
für den Puffer und 0 für die Länge.
Ob die Entscheidung, ob der Operand vorhanden ist oder nicht, bei jedem
-Ausführung angeben, geben Sie an, dass der Operand weggelassen wird, indem
ANeuralNetworksExecution_setInput()
oder
ANeuralNetworksExecution_setOutput()
und übergeben NULL
für den Puffer und 0 für die Länge.
Tensoren mit unbekanntem Rang
Mit Android 9 (API-Level 28) wurden Modelloperanden mit unbekannten Dimensionen eingeführt, aber bekannten Rang (die Anzahl der Dimensionen). Android 10 (API-Level 29) eingeführt Tensoren unbekannten Rangs, wie in ANeuralNetworksOperandType.
NNAPI-Benchmark
Der NNAPI-Benchmark ist auf AOSP in platform/test/mlts/benchmark
verfügbar
(Benchmark-Anwendung) und platform/test/mlts/models
(Modelle und Datasets).
Die Benchmark bewertet Latenz und Genauigkeit und vergleicht Fahrer mit denselben mit Tensorflow Lite auf der CPU für dieselben Modelle und Datasets.
So verwenden Sie die Benchmark:
Schließen Sie ein Android-Zielgerät an Ihren Computer an, öffnen Sie ein Terminalfenster und Achte darauf, dass das Gerät über ADB erreichbar ist.
Wenn mehrere Android-Geräte verbunden sind, exportieren Sie das Zielgerät
ANDROID_SERIAL
.Rufen Sie das Android-Quellverzeichnis der obersten Ebene auf.
Führen Sie folgende Befehle aus:
lunch aosp_arm-userdebug # Or aosp_arm64-userdebug if available ./test/mlts/benchmark/build_and_run_benchmark.sh
Am Ende einer Benchmarkausführung werden die Ergebnisse als HTML-Seite dargestellt. an
xdg-open
übergeben.
NNAPI-Logs
NNAPI generiert nützliche Diagnoseinformationen in den Systemprotokollen. Verwenden Sie zum Analysieren der Logs den logcat. Dienstprogramm.
Aktivieren Sie die ausführliche NNAPI-Protokollierung für bestimmte Phasen oder Komponenten, indem Sie die
Attribut debug.nn.vlog
(mit adb shell
) der folgenden Liste von Werten hinzufügen,
durch Leerzeichen, Doppelpunkt oder Komma getrennt:
model
: Modellerstellungcompilation
: Generierung des Modellausführungsplans und Kompilierungexecution
: Modellausführungcpuexe
: Ausführung von Vorgängen mit der NNAPI-CPU-Implementierungmanager
: NNAPI-Erweiterungen sowie Informationen zu verfügbaren Schnittstellen und Funktionenall
oder1
: alle Elemente oben
Um beispielsweise ein vollständiges ausführliches Logging zu aktivieren, verwenden Sie den Befehl
adb shell setprop debug.nn.vlog all
Deaktivieren Sie die ausführliche Protokollierung mit dem Befehl
adb shell setprop debug.nn.vlog '""'
Nach der Aktivierung generiert die ausführliche Protokollierung Logeinträge auf INFO-Ebene mit einem Tag, das auf den Phasen- oder Komponentennamen eingestellt ist.
Neben den von debug.nn.vlog
gesteuerten Nachrichten bieten NNAPI-Komponenten
weitere Logeinträge auf verschiedenen Ebenen, die jeweils ein bestimmtes Log-Tag verwenden.
Um eine Liste der Komponenten zu erhalten, durchsuchen Sie die Quellstruktur mithilfe der folgenden Ausdruck:
grep -R 'define LOG_TAG' | awk -F '"' '{print $2}' | sort -u | egrep -v "Sample|FileTag|test"
Dieser Ausdruck gibt derzeit die folgenden Tags zurück:
- BurstBuilder
- Rückrufe
- CompilationBuilder
- CPUExecutor
- AusführungsBuilder
- ExecutionBurstController
- ExecutionBurstServer
- Ausführungsplan
- Fibonacci-Treiber
- GraphDump
- IndexedShapeWrapper
- IonWatcher
- Manager
- Arbeitsspeicher
- MemoryUtils
- MetaModel
- Modellargumentinformationen
- Modell-Builder
- Neuronale Netzwerke
- OperationResolver
- Aufgaben und Ablauf
- Betriebsprogramme
- Paketinformationen
- TokenHasher
- Typmanager
- Dienstprogramme
- ValidHal
- Versionierte Schnittstellen
Um die Ebene der Logeinträge zu steuern, die von logcat
angezeigt werden, verwenden Sie
die Umgebungsvariable ANDROID_LOG_TAGS
.
Um alle NNAPI-Logmeldungen anzuzeigen und alle anderen zu deaktivieren, legen Sie ANDROID_LOG_TAGS
auf
Folgendes:
BurstBuilder:V Callbacks:V CompilationBuilder:V CpuExecutor:V ExecutionBuilder:V ExecutionBurstController:V ExecutionBurstServer:V ExecutionPlan:V FibonacciDriver:V GraphDump:V IndexedShapeWrapper:V IonWatcher:V Manager:V MemoryUtils:V Memory:V MetaModel:V ModelArgumentInfo:V ModelBuilder:V NeuralNetworks:V OperationResolver:V OperationsUtils:V Operations:V PackageInfo:V TokenHasher:V TypeManager:V Utils:V ValidateHal:V VersionedInterfaces:V *:S.
Sie können ANDROID_LOG_TAGS
mit dem folgenden Befehl festlegen:
export ANDROID_LOG_TAGS=$(grep -R 'define LOG_TAG' | awk -F '"' '{ print $2 ":V" }' | sort -u | egrep -v "Sample|FileTag|test" | xargs echo -n; echo ' *:S')
Beachten Sie, dass dies nur ein Filter ist, der für logcat
gilt. Sie müssen noch
Legen Sie das Attribut debug.nn.vlog
auf all
fest, um ausführliche Loginformationen zu generieren.