Auf dieser Seite erhalten Sie einen Überblick darüber, wie Sie einen NNAPI-Treiber (Neural Networks API) implementieren. Weitere Informationen finden Sie in der Dokumentation in den HAL-Definitionsdateien in hardware/interfaces/neuralnetworks
.
Eine Beispielimplementierung des Treibers finden Sie unter frameworks/ml/nn/driver/sample
.
Weitere Informationen zur Neural Networks API finden Sie unter Neural Networks API.
Neuronale Netzwerke HAL
HAL (neuronale Netzwerke) definiert eine Abstraktion der verschiedenen Geräte wie Grafikprozessoren (GPUs) und digitale Signalprozessoren (DSP), die sich in einem Produkt (z. B. einem Smartphone oder Tablet) befinden. Die Treiber für diese Geräte müssen der NN HAL entsprechen. Die Schnittstelle wird in den HAL-Definitionsdateien in hardware/interfaces/neuralnetworks
angegeben.
Der allgemeine Ablauf der Schnittstelle zwischen dem Framework und einem Treiber ist in Abbildung 1 dargestellt.
Abbildung 1. Fluss neuronaler Netzwerke
Initialisierung
Bei der Initialisierung fragt das Framework mit IDevice::getCapabilities_1_3
seine Funktionen vom Treiber ab.
Die @1.3::Capabilities
-Struktur enthält alle Datentypen und stellt die unveränderliche Leistung mithilfe eines Vektors dar.
Um zu bestimmen, wie Berechnungen den verfügbaren Geräten zugewiesen werden, verwendet das Framework die Funktionen, um zu ermitteln, wie schnell und energieeffizient jeder Treiber eine Ausführung ausführen kann. Dazu muss der Treiber standardisierte Leistungszahlen angeben, die auf der Ausführung von Referenzarbeitslasten basieren.
Um die Werte zu ermitteln, die der Treiber als Antwort auf IDevice::getCapabilities_1_3
zurückgibt, können Sie die NNAPI-Benchmark-App verwenden, um die Leistung für die entsprechenden Datentypen zu messen. Die MobileNet v1- und v2-, asr_float
- und tts_float
-Modelle werden für die Leistungsmessung bei 32-Bit-Gleitkommawerten empfohlen. Die MobileNet v1- und v2-quantisierten Modelle werden für 8-Bit-quantisierte Werte empfohlen. Weitere Informationen finden Sie unter Android Machine Learning Test Suite.
Unter Android 9 und niedriger enthält die Capabilities
-Struktur nur Informationen zur Treiberleistung für Gleitkomma- und quantisierte Tensoren, aber keine skalaren Datentypen.
Im Rahmen der Initialisierung kann das Framework weitere Informationen abfragen, indem es IDevice::getType
, IDevice::getVersionString
, IDevice:getSupportedExtensions
und IDevice::getNumberOfCacheFilesNeeded
verwendet.
Zwischen Produktneustarts erwartet das Framework, dass bei allen in diesem Abschnitt beschriebenen Abfragen immer die gleichen Werte für einen bestimmten Treiber gemeldet werden. Andernfalls kann die Leistung einer App, die diesen Treiber verwendet, beeinträchtigt sein oder es kann zu einem falschen Verhalten kommen.
Compilation
Das Framework bestimmt, welche Geräte verwendet werden, wenn es eine Anfrage von einer App empfängt. Unter Android 10 können Apps die Geräte erkennen und angeben, von denen das Framework eine Auswahl trifft. Weitere Informationen finden Sie unter Geräteerkennung und -zuweisung.
Zum Zeitpunkt der Modellkompilierung sendet das Framework das Modell durch Aufrufen von IDevice::getSupportedOperations_1_3
an jeden Kandidatentreiber.
Jeder Treiber gibt ein Array von Booleschen Werten zurück, die angeben, welche Vorgänge des Modells unterstützt werden. Ein Treiber kann aus verschiedenen Gründen feststellen, dass er einen bestimmten Vorgang nicht unterstützt. Beispiel:
- Der Datentyp wird vom Treiber nicht unterstützt.
- Der Treiber unterstützt nur Vorgänge mit bestimmten Eingabeparametern. Ein Treiber unterstützt beispielsweise 3x3- und 5x5-, aber keine 7x7-Faltungsvorgänge.
- Aufgrund von Speichereinschränkungen des Treibers kann er keine großen Grafiken oder Eingaben verarbeiten.
Während der Kompilierung können die Eingabe-, Ausgabe- und internen Operanden des Modells, wie in OperandLifeTime
beschrieben, unbekannte Dimensionen oder einen unbekannten Rang haben. Weitere Informationen finden Sie unter Ausgabeform.
Das Framework weist jeden ausgewählten Treiber an, sich durch Aufrufen von IDevice::prepareModel_1_3
auf die Ausführung eines Teils des Modells vorzubereiten.
Jeder Treiber kompiliert dann seine Teilmenge. Beispielsweise kann ein Treiber Code generieren oder eine neu angeordnete Kopie der Gewichtungen erstellen. Da zwischen der Kompilierung des Modells und der Ausführung der Anfragen viel Zeit vergehen kann, sollten Ressourcen wie große Gerätespeicherblöcke während der Kompilierung nicht zugewiesen werden.
Bei Erfolg gibt der Treiber einen @1.3::IPreparedModel
-Handle zurück. Wenn der Treiber bei der Vorbereitung seiner Teilmenge des Modells einen Fehlercode zurückgibt, führt das Framework das gesamte Modell auf der CPU aus.
Ein Treiber kann Kompilierungsartefakte im Cache speichern, um den Zeitaufwand für die Kompilierung beim Start einer Anwendung zu reduzieren. Weitere Informationen finden Sie unter Kompilierungs-Caching.
Umsetzung
Wenn eine Anwendung das Framework anfordert, eine Anfrage auszuführen, ruft das Framework standardmäßig die HAL-Methode IPreparedModel::executeSynchronously_1_3
auf, um eine synchrone Ausführung für ein vorbereitetes Modell durchzuführen.
Eine Anfrage kann auch asynchron mit der Methode execute_1_3
, der Methode executeFenced
(siehe Fenced-Ausführung) oder mit einer Burst-Ausführung ausgeführt werden.
Synchrone Ausführungsanfragen verbessern die Leistung und reduzieren den Thread-Overhead im Vergleich zu asynchronen Aufrufen, da die Steuerung erst nach Abschluss der Ausführung an den App-Prozess zurückgegeben wird. Das bedeutet, dass der Treiber keinen separaten Mechanismus benötigt, um den Anwendungsprozess über den Abschluss einer Ausführung zu informieren.
Bei der asynchronen execute_1_3
-Methode kehrt die Steuerung nach Beginn der Ausführung zum App-Prozess zurück. Der Treiber muss das Framework über die @1.3::IExecutionCallback
benachrichtigen, wenn die Ausführung abgeschlossen ist.
Der Parameter Request
, der an die Ausführungsmethode übergeben wird, enthält die für die Ausführung verwendeten Eingabe- und Ausgabeoperanden. Der Speicher, in dem die Operandendaten gespeichert werden, muss eine nach Zeilen geordnete Reihenfolge haben, wobei die erste Dimension die langsamste iteriert. Am Ende einer Zeile darf kein Padding erfolgen. Weitere Informationen zu den Arten von Operanden finden Sie unter Operanden.
Für NN HAL 1.2-Treiber oder höher werden beim Abschluss einer Anfrage der Fehlerstatus, die Ausgabeform und die Zeitinformationen an das Framework zurückgegeben. Während der Ausführung können Ausgabe- oder interne Operanden des Modells eine oder mehrere unbekannte Dimensionen oder einen unbekannten Rang haben. Wenn mindestens ein Ausgabe-Operand eine unbekannte Dimension oder einen unbekannten Rang hat, muss der Treiber Ausgabeinformationen in dynamischer Größe zurückgeben.
Bei Treibern mit NN HAL 1.1 oder niedriger wird nur der Fehlerstatus zurückgegeben, wenn eine Anfrage abgeschlossen ist. Die Dimensionen für Eingabe- und Ausgabeoperanden müssen vollständig angegeben sein, damit die Ausführung erfolgreich abgeschlossen werden kann. Interne Operanden können eine oder mehrere unbekannte Dimensionen haben, es muss jedoch ein Rang angegeben sein.
Bei Nutzeranfragen, die mehrere Treiber umfassen, ist das Framework für die Reservierung von Zwischenspeicher und die Sequenzierung der Aufrufe an jeden Treiber zuständig.
Für dieselbe @1.3::IPreparedModel
können mehrere Anfragen parallel initiiert werden.
Der Treiber kann Anfragen parallel ausführen oder die Ausführungen serialisieren.
Das Framework kann einen Fahrer auffordern, mehr als ein vorbereitetes Modell zu behalten. Beispiel: Modell m1
vorbereiten, m2
vorbereiten, Anfrage r1
auf m1
ausführen, r2
auf m2
ausführen, r3
auf m1
ausführen, r4
auf m2
ausführen, m1
(siehe Bereinigung) und m2
freigeben.
Der Treiber sollte die meisten Initialisierungen in der Kompilierungsphase ausführen, um eine langsame erste Ausführung zu vermeiden, die zu einer schlechten Nutzererfahrung führen könnte (z. B. Ruckeln des ersten Frames). Die Initialisierung bei der ersten Ausführung sollte auf Aktionen beschränkt sein, die sich bei vorzeitiger Ausführung negativ auf die Systemintegrität auswirken, z. B. das Reservieren großer temporärer Puffer oder die Erhöhung der Taktrate eines Geräts. Treiber, die nur eine begrenzte Anzahl gleichzeitiger Modelle vorbereiten können, müssen möglicherweise ihre Initialisierung bei der ersten Ausführung durchführen.
Wenn unter Android 10 oder höher mehrere Ausführungen mit demselben vorbereiteten Modell schnell nacheinander ausgeführt werden, kann der Client ein Ausführungs-Burst-Objekt verwenden, um zwischen Anwendungs- und Treiberprozessen zu kommunizieren. Weitere Informationen finden Sie unter Burst-Ausführungen und schnelle Nachrichtenwarteschlangen.
Um die Leistung bei mehreren Ausführungen in schneller Folge zu verbessern, kann der Treiber temporäre Puffer beibehalten oder Taktraten erhöhen. Es wird empfohlen, einen Watchdog-Thread zu erstellen, um Ressourcen freizugeben, wenn nach einer bestimmten Zeit keine neuen Anfragen erstellt werden.
Ausgabeform
Bei Anfragen, bei denen für einen oder mehrere Ausgabeoperanden nicht alle Dimensionen angegeben sind, muss der Treiber nach der Ausführung eine Liste von Ausgabeformen mit den Dimensionsinformationen für jeden Ausgabeoperanden bereitstellen. Weitere Informationen zu Dimensionen finden Sie unter OutputShape
.
Wenn eine Ausführung aufgrund eines zu kleinen Ausgabepuffers fehlschlägt, muss der Treiber in der Liste der Ausgabeformen angeben, welche Ausgabeoperanden eine unzureichende Puffergröße haben. Außerdem sollte er so viele Dimensionsinformationen wie möglich melden und für unbekannte Dimensionen Null verwenden.
Timing
Unter Android 10 kann eine App nach der Ausführungszeit fragen, wenn sie ein einzelnes Gerät für die Kompilierung angegeben hat. Weitere Informationen finden Sie unter MeasureTiming
und Geräteerkennung und -zuweisung.
In diesem Fall muss ein NN HAL 1.2-Treiber beim Ausführen einer Anfrage die Ausführungsdauer messen oder UINT64_MAX
melden, um anzugeben, dass die Dauer nicht verfügbar ist. Der Treiber sollte alle Leistungseinbußen minimieren, die sich aus der Messung der Ausführungsdauer ergeben.
Der Treiber meldet die folgenden Zeiträume in Mikrosekunden in der Struktur Timing
:
- Ausführungszeit auf dem Gerät: Beinhaltet nicht die Ausführungszeit im Treiber, der auf dem Hostprozessor ausgeführt wird.
- Ausführungszeit im Treiber:Beinhaltet die Ausführungszeit auf dem Gerät.
Diese Zeitspannen müssen die Zeit enthalten, zu der die Ausführung angehalten wird, z. B. wenn die Ausführung von anderen Aufgaben vorzeitig beendet wurde oder darauf wartet, dass eine Ressource verfügbar wird.
Wenn der Treiber nicht aufgefordert wurde, die Ausführungsdauer zu messen, oder ein Ausführungsfehler auftritt, muss der Treiber die Dauer als UINT64_MAX
melden. Selbst wenn der Treiber aufgefordert wurde, die Ausführungsdauer zu messen, kann er stattdessen UINT64_MAX
für die Zeit auf dem Gerät, die Zeit im Treiber oder beides melden. Wenn der Treiber beide Dauern als anderen Wert als UINT64_MAX
meldet, muss die Ausführungszeit im Treiber der Zeit auf dem Gerät entsprechen oder diese überschreiten.
Umzäunte Ausführung
In Android 11 ermöglicht NNAPI Ausführungen, auf eine Liste von sync_fence
-Handles zu warten und optional ein sync_fence
-Objekt zurückzugeben, das signalisiert, wenn die Ausführung abgeschlossen ist. Dies reduziert den Aufwand für kleine Sequenzmodelle und Streaminganwendungsfälle. Die abgegrenzte Ausführung ermöglicht auch eine effizientere Interoperabilität mit anderen Komponenten, die sync_fence
signalisieren oder warten können. Weitere Informationen zu sync_fence
finden Sie unter Synchronisierungs-Framework.
Bei einer abgegrenzten Ausführung ruft das Framework die Methode IPreparedModel::executeFenced
auf, um eine abgegrenzte, asynchrone Ausführung auf einem vorbereiteten Modell mit einem Vektor von Synchronisationsschranken zu starten, auf die gewartet werden soll. Wenn die asynchrone Aufgabe vor dem Rückgabewert des Aufrufs abgeschlossen ist, kann für sync_fence
ein leerer Handle zurückgegeben werden. Außerdem muss ein IFencedExecutionCallback
-Objekt zurückgegeben werden, damit das Framework den Fehlerstatus und die Dauer abfragen kann.
Nach Abschluss einer Ausführung können die folgenden beiden Zeitangaben, die die Dauer der Ausführung messen, über IFencedExecutionCallback::getExecutionInfo
abgefragt werden.
timingLaunched
: Dauer zwischen dem Aufruf vonexecuteFenced
und dem Signalisieren der zurückgegebenensyncFence
durchexecuteFenced
.timingFenced
: Dauer zwischen dem Signalisieren aller Synchronisationsschranken, auf die die Ausführung wartet, und dem Signalisieren der zurückgegebenensyncFence
durchexecuteFenced
.
Kontrollfluss
Auf Geräten mit Android 11 oder höher enthält die NNAPI zwei Kontrollfluss-Vorgänge, IF
und WHILE
, die andere Modelle als Argumente annehmen und bedingt (IF
) oder wiederholt (WHILE
) ausführen. Weitere Informationen zur Implementierung finden Sie unter Kontrollfluss.
Dienstqualität
In Android 11 bietet die NNAPI eine verbesserte Dienstqualität, da eine Anwendung die relativen Prioritäten ihrer Modelle, die erwartete maximale Zeit für die Vorbereitung eines Modells und die erwartete maximale Zeit für die Ausführung einer Ausführung angeben kann. Weitere Informationen finden Sie unter Qualität des Dienstes.
Bereinigung
Wenn eine App die Verwendung eines vorbereiteten Modells beendet hat, gibt das Framework die Referenz auf das @1.3::IPreparedModel
-Objekt frei. Wenn das Objekt IPreparedModel
nicht mehr referenziert wird, wird es automatisch in dem Treiberdienst gelöscht, mit dem es erstellt wurde. Modellspezifische Ressourcen können derzeit in der Treiberimplementierung des Destruktors zurückgefordert werden. Wenn der Treiberdienst möchte, dass das IPreparedModel
-Objekt automatisch gelöscht wird, wenn es vom Client nicht mehr benötigt wird, darf es nach der Rückgabe des IPreparedeModel
-Objekts über IPreparedModelCallback::notify_1_3
keine Verweise mehr auf das IPreparedModel
-Objekt enthalten.
CPU-Auslastung
Es wird erwartet, dass Treiber die CPU verwenden, um Berechnungen einzurichten. Treiber sollten die CPU nicht für Graphberechnungen verwenden, da dies die Fähigkeit des Frameworks beeinträchtigt, Arbeit richtig zuzuordnen. Der Treiber sollte die Teile, die er nicht verarbeiten kann, an das Framework melden und den Rest dem Framework überlassen.
Das Framework bietet eine CPU-Implementierung für alle NNAPI-Vorgänge mit Ausnahme der vom Anbieter definierten Vorgänge. Weitere Informationen finden Sie unter Anbietererweiterungen.
Für die in Android 10 eingeführten Vorgänge (API-Level 29) gibt es nur eine Referenz-CPU-Implementierung, um zu prüfen, ob die CTS- und VTS-Tests korrekt sind. Die in mobilen Frameworks für maschinelles Lernen enthaltenen optimierten Implementierungen werden gegenüber der NNAPI-CPU-Implementierung bevorzugt.
Dienstfunktionen
Die NNAPI-Codebase enthält Dienstprogrammfunktionen, die von Treiberdiensten verwendet werden können.
Die Datei frameworks/ml/nn/common/include/Utils.h
enthält verschiedene Dienstfunktionen, z. B. für das Logging und die Konvertierung zwischen verschiedenen NN HAL-Versionen.
VLogging:
VLOG
ist ein Wrapper-Makro für dieLOG
von Android, mit dem die Nachricht nur protokolliert wird, wenn das entsprechende Tag im Attributdebug.nn.vlog
festgelegt ist.initVLogMask()
muss vor allen Aufrufen vonVLOG
aufgerufen werden. Mit dem MakroVLOG_IS_ON
kann geprüft werden, obVLOG
derzeit aktiviert ist. Dadurch kann komplizierter Logging-Code übersprungen werden, wenn er nicht benötigt wird. Das Attribut muss einen der folgenden Werte haben:- Ein leerer String, der angibt, dass keine Protokollierung erfolgen soll.
- Das Token
1
oderall
, das angibt, dass alle Protokolle erstellt werden sollen. - Eine Liste von Tags, die durch Leerzeichen, Kommas oder Doppelpunkte getrennt ist und angibt, welche Protokollierung durchgeführt werden soll. Die Tags sind
compilation
,cpuexe
,driver
,execution
,manager
undmodel
.
compliantWithV1_*
: Gibttrue
zurück, wenn ein NN HAL-Objekt ohne Informationsverlust in den gleichen Typ einer anderen HAL-Version konvertiert werden kann. Wenn Sie beispielsweisecompliantWithV1_0
aufV1_2::Model
anwenden, wirdfalse
zurückgegeben, wenn das Modell Vorgangstypen enthält, die in NN HAL 1.1 oder NN HAL 1.2 eingeführt wurden.convertToV1_*
: Wandelt ein NN HAL-Objekt von einer Version in eine andere um. Wenn die Umwandlung zu einem Informationsverlust führt, also wenn der Wert durch die neue Version des Typs nicht vollständig dargestellt werden kann, wird eine Warnung protokolliert.Funktionen: Mit den Funktionen
nonExtensionOperandPerformance
undupdate
können Sie das FeldCapabilities::operandPerformance
erstellen.Attribute der Typen werden abgefragt:
isExtensionOperandType
,isExtensionOperationType
,nonExtensionSizeOfData
,nonExtensionOperandSizeOfData
,nonExtensionOperandTypeIsScalar
,tensorHasUnspecifiedDimensions
.
Die Datei frameworks/ml/nn/common/include/ValidateHal.h
enthält Dienstfunktionen, mit denen Sie prüfen können, ob ein NN-HAL-Objekt gemäß der Spezifikation der HAL-Version gültig ist.
validate*
: Gibttrue
zurück, wenn das NN HAL-Objekt gemäß der Spezifikation der HAL-Version gültig ist. OEM-Typen und Erweiterungstypen werden nicht überprüft.validateModel
gibt beispielsweisefalse
zurück, wenn das Modell einen Vorgang enthält, der auf einen nicht vorhandenen Operandenindex verweist, oder einen Vorgang, der in dieser HAL-Version nicht unterstützt wird.
Die Datei frameworks/ml/nn/common/include/Tracing.h
enthält Makros, um das Hinzufügen von Systracing-Informationen zum Code von neuronalen Netzwerken zu vereinfachen.
Ein Beispiel finden Sie in den NNTRACE_*
-Makroaufrufen im Beispieltreiber.
Die Datei frameworks/ml/nn/common/include/GraphDump.h
enthält eine Dienstfunktion, mit der der Inhalt einer Model
zur Fehlerbehebung in grafischer Form ausgegeben werden kann.
graphDump
: Schreibt eine Darstellung des Modells im Graphviz-Format (.dot
) in den angegebenen Stream (falls angegeben) oder in den Logcat (falls kein Stream angegeben ist).
Zertifizierungsstufe
Verwenden Sie zum Testen Ihrer Implementierung der NNAPI die VTS- und CTS-Tests, die im Android-Framework enthalten sind. VTS testet Ihre Treiber direkt (ohne Framework), während CTS sie indirekt über das Framework testet. Dabei wird jede API-Methode getestet und geprüft, ob alle von den Treibern unterstützten Vorgänge ordnungsgemäß funktionieren und Ergebnisse liefern, die den Genauigkeitsanforderungen entsprechen.
In CTS und VTS für NNAPI gelten folgende Präzisionsanforderungen:
Gleitkommazahl:abs(expected - actual) <= atol + rtol * abs(expected); Hierbei gilt:
- Für fp32, atol = 1e-5f, rtol = 5.0f * 1,1920928955078125e-7
- Für fp16 gilt: atol = rtol = 5.0f * 0.0009765625f
Quantisiert: Abweichung von 1 (außer
mobilenet_quantized
, bei dem es eine Abweichung von 3 gibt)Boolesch: genaue Übereinstimmung
Eine Möglichkeit, wie CTS NNAPI testet, besteht darin, feste pseudozufällige Graphen zu generieren, mit denen die Ausführungsergebnisse der einzelnen Treiber mit der NNAPI-Referenzimplementierung getestet und verglichen werden. Wenn Treiber mit NN HAL 1.2 oder höher die Ergebnisse nicht erfüllen, meldet CTS einen Fehler und gibt eine Spezifikationsdatei für das fehlgeschlagene Modell zur Fehlerbehebung unter /data/local/tmp
aus.
Weitere Informationen zu den Kriterien für die Genauigkeit finden Sie unter TestRandomGraph.cpp
und TestHarness.h
.
Fuzz-Test
Der Zweck von Fuzz-Tests besteht darin, Abstürze, Assertions, Speicherverstöße oder allgemeines undefiniertes Verhalten im zu testenden Code zu finden, der auf Faktoren wie unerwarteten Eingaben zurückzuführen ist. Für NNAPI-Fuzz-Tests verwendet Android Tests, die auf libFuzzer basieren. Diese sind effizient beim Fuzzing, da sie die Zeilenabdeckung früherer Testfälle verwenden, um neue Zufallseingaben zu generieren. LibFuzzer bevorzugt beispielsweise Testfälle, die in neuen Codezeilen ausgeführt werden. Dies reduziert die Zeit, die die Tests zum Auffinden von problematischem Code benötigen, erheblich.
Wenn Sie Fuzz-Tests zur Validierung Ihrer Treiberimplementierung ausführen möchten, ändern Sie frameworks/ml/nn/runtime/test/android_fuzzing/DriverFuzzTest.cpp
im libneuralnetworks_driver_fuzzer
-Testtool in AOSP, um Ihren Treibercode einzubinden. Weitere Informationen zu NNAPI-Fuzz-Tests finden Sie unter frameworks/ml/nn/runtime/test/android_fuzzing/README.md
.
Sicherheit
Da Anwendungsprozesse direkt mit dem Treiberprozess kommunizieren, müssen Treiber die Argumente der erhaltenen Aufrufe validieren. Diese Validierung wird von VTS durchgeführt. Der Validierungscode befindet sich in frameworks/ml/nn/common/include/ValidateHal.h
.
Die Fahrer sollten auch darauf achten, dass Apps nicht andere Apps beeinträchtigen, wenn sie dasselbe Gerät verwenden.
Android Machine Learning Test Suite
Die Android Machine Learning Test Suite (MLTS) ist ein NNAPI-Benchmark, der in CTS und VTS enthalten ist und die Genauigkeit echter Modelle auf Geräten von Anbietern validiert. Der Benchmark bewertet Latenz und Genauigkeit und vergleicht die Ergebnisse der Treiber mit den Ergebnissen, die mit TF Lite auf der CPU für dasselbe Modell und dieselben Datensätze erzielt wurden. So wird sichergestellt, dass die Genauigkeit eines Treibers nicht schlechter ist als die der CPU-Referenzimplementierung.
Android-Plattformentwickler verwenden außerdem MLTS, um die Latenz und Genauigkeit von Treibern zu bewerten.
Der NNAPI-Benchmark ist in zwei Projekten in AOSP zu finden:
platform/test/mlts/benchmark
(Benchmark-App)platform/test/mlts/models
(Modelle und Datasets)
Modelle und Datensätze
Die NNAPI-Benchmark verwendet die folgenden Modelle und Datasets.
- MobileNetV1 float und u8 in verschiedenen Größen quantisiert, werden für eine kleine Teilmenge (1.500 Bilder) des Open Images Dataset v4 ausgeführt.
- MobileNetV2 mit Quantisierung in Gleitkomma- und u8-Format in verschiedenen Größen, ausgeführt auf einer kleinen Teilmenge (1.500 Bilder) des Open Images Datasets Version 4.
- Auf LSTM basierendes, auf Langzeitspeicher basierendes akustisches Modell für die Sprachausgabe, das mit einer kleinen Teilmenge der arktischen CMU ausgeführt wird.
- LSTM-basiertes akustisches Modell für die automatische Spracherkennung, das auf einem kleinen Teil des LibriSpeech-Datasets ausgeführt wird.
Weitere Informationen finden Sie unter platform/test/mlts/models
.
Stresstests
Die Android Machine Learning Test Suite umfasst eine Reihe von Absturztests zur Überprüfung der Ausfallsicherheit von Treibern unter intensiver Nutzung oder in Grenzfällen des Verhaltens von Clients.
Alle Crashtests bieten die folgenden Funktionen:
- Hangerkennung: Wenn der NNAPI-Client während eines Tests hängt, schlägt der Test mit der Fehlerursache
HANG
fehl und die Testsuite geht zum nächsten Test. - NNAPI-Clientabsturzerkennung: Tests überstehen Clientabsturz und schlagen mit der Fehlerursache
CRASH
fehl. - Treiberabsturzerkennung: Mit Tests können Treiberabstürze erkannt werden, die einen Fehler bei einem NNAPI-Aufruf verursachen. Es kann Abstürze in Treiberprozessen geben, die keinen NNAPI-Fehler verursachen und nicht zum Scheitern des Tests führen. Zur Abdeckung dieser Art von Fehlern wird empfohlen, den Befehl
tail
im Systemprotokoll für treiberbezogene Fehler oder Abstürze auszuführen. - Ausrichtung auf alle verfügbaren Accelerators: Die Tests werden auf alle verfügbaren Treiber ausgeführt.
Bei allen Crashtests sind die folgenden vier Ergebnisse möglich:
SUCCESS
: Die Ausführung wurde ohne Fehler abgeschlossen.FAILURE
: Fehler bei der Ausführung. Wird in der Regel durch einen Fehler beim Testen eines Modells verursacht, der darauf hinweist, dass der Treiber das Modell nicht kompilieren oder ausführen konnte.HANG
: Der Testprozess reagiert nicht mehr.CRASH
: Der Testprozess ist abgestürzt.
Weitere Informationen zu Belastungstests und eine vollständige Liste der Absturztests findest du unter platform/test/mlts/benchmark/README.txt
.
MLTS verwenden
So verwenden Sie die MLTS:
- Verbinden Sie ein Zielgerät mit Ihrer Workstation und prüfen Sie, ob es über adb erreichbar ist.
Exportieren Sie die Umgebungsvariable
ANDROID_SERIAL
des Zielgeräts, wenn mehr als ein Gerät verbunden ist. cd
in das oberste Android-Quellverzeichnis.source build/envsetup.sh lunch aosp_arm-userdebug # Or aosp_arm64-userdebug if available. ./test/mlts/benchmark/build_and_run_benchmark.sh
Am Ende eines Benchmark-Laufs werden die Ergebnisse als HTML-Seite angezeigt und an
xdg-open
übergeben.
Weitere Informationen finden Sie unter platform/test/mlts/benchmark/README.txt
.
HAL-Versionen für neuronale Netzwerke
In diesem Abschnitt werden die Änderungen beschrieben, die in den HAL-Versionen von Android und Neural Networks eingeführt wurden.
Android 11
In Android 11 wird NN HAL 1.3 eingeführt. Diese Version enthält die folgenden wichtigen Änderungen:
- Unterstützung für signierte 8-Bit-Quantisierung in NNAPI. Fügt den Operandentyp
TENSOR_QUANT8_ASYMM_SIGNED
hinzu. Treiber mit NN HAL 1.3, die Operationen mit unsignierter Quantisierung unterstützen, müssen auch die signierten Varianten dieser Operationen unterstützen. Wenn signierte und unsignierte Versionen der meisten quantisierten Vorgänge ausgeführt werden, müssen die Treiber bis zu einem Offset von 128 dieselben Ergebnisse liefern. Es gibt fünf Ausnahmen von dieser Anforderung:CAST
,HASHTABLE_LOOKUP
,LSH_PROJECTION
,PAD_V2
undQUANTIZED_16BIT_LSTM
. DerQUANTIZED_16BIT_LSTM
-Vorgang unterstützt keine signierten Operanden. Die anderen vier Vorgänge unterstützen die Quantisierung mit Vorzeichen, erfordern jedoch nicht, dass die Ergebnisse gleich sind. - Unterstützung für abgegrenzte Ausführungen, bei denen das Framework die Methode
IPreparedModel::executeFenced
aufruft, um eine abgegrenzte, asynchrone Ausführung auf einem vorbereiteten Modell mit einem Vektor von Synchronisationsschranken zu starten, auf die gewartet werden soll. Weitere Informationen finden Sie unter Fenced Ausführung. - Unterstützung für die Ablaufsteuerung. Hiermit werden die Vorgänge
IF
undWHILE
hinzugefügt, die andere Modelle als Argumente annehmen und diese bedingt (IF
) oder wiederholt (WHILE
) ausführen. Weitere Informationen finden Sie unter Kontrollfluss. - Verbesserte Dienstqualität, da Anwendungen die relativen Prioritäten ihrer Modelle, die erwartete maximale Vorbereitungszeit eines Modells und die erwartete maximale Zeit bis zum Abschluss einer Ausführung angeben können. Weitere Informationen finden Sie unter Qualität des Dienstes.
- Unterstützung von Arbeitsspeicherdomains, die Zuweisungsschnittstellen für von Treibern verwaltete Zwischenspeicher bereitstellen. So können geräteeigene Speicher zwischen Ausführungen übergeben werden, wodurch unnötiges Kopieren und Transformieren von Daten zwischen aufeinanderfolgenden Ausführungen desselben Treibers vermieden wird. Weitere Informationen finden Sie unter Arbeitsspeicherbereiche.
Android 10
In Android 10 wird NN HAL 1.2 eingeführt. Diese Version enthält die folgenden wichtigen Änderungen:
- Das
Capabilities
-Objekt enthält alle Datentypen, einschließlich Skalardatentypen, und stellt die Leistung ohne Entspannung mit einem Vektor anstelle von benannten Feldern dar. - Mit den Methoden
getVersionString
undgetType
kann das Framework Informationen zum Gerätetyp (DeviceType
) und zur Version abrufen. Weitere Informationen finden Sie unter Geräteerkennung und ‑zuweisung. - Die Methode
executeSynchronously
wird standardmäßig aufgerufen, um eine Ausführung synchron auszuführen. Die Methodeexecute_1_2
weist das Framework an, eine Ausführung asynchron auszuführen. Siehe Ausführung. - Der Parameter
MeasureTiming
fürexecuteSynchronously
,execute_1_2
und die Burst-Ausführung gibt an, ob der Treiber die Ausführungsdauer messen soll. Die Ergebnisse werden in derTiming
-Struktur erfasst. Weitere Informationen finden Sie unter Timing. - Unterstützung für Ausführungen, bei denen mindestens ein Ausgabeoperand eine unbekannte Dimension oder einen unbekannten Rang hat. Siehe Ausgabeform.
- Unterstützung für Anbietererweiterungen, also Sammlungen von vom Anbieter definierten Vorgängen und Datentypen. Der Treiber meldet unterstützte Erweiterungen über die Methode
IDevice::getSupportedExtensions
. Weitere Informationen finden Sie unter Anbietererweiterungen. - Ein Burst-Objekt kann eine Reihe von Burst-Ausführungen mithilfe von schnellen Nachrichtenwarteschlangen (Fast Message Queues, FMQs) steuern, um zwischen App- und Treiberprozessen zu kommunizieren und so die Latenz zu reduzieren. Weitere Informationen finden Sie unter Burst-Ausführungen und schnelle Nachrichtenwarteschlangen.
- Unterstützung für AHardwareBuffer, damit der Treiber Ausführungen ausführen kann, ohne Daten zu kopieren. Siehe AHardwareBuffer.
- Verbesserte Unterstützung für das Caching von Kompilierungsartefakten, um die Zeit für die Kompilierung beim Starten einer App zu reduzieren. Siehe Kompilierung-Caching.
In Android 10 werden die folgenden Operandentypen und -vorgänge eingeführt.
-
ANEURALNETWORKS_BOOL
ANEURALNETWORKS_FLOAT16
ANEURALNETWORKS_TENSOR_BOOL8
ANEURALNETWORKS_TENSOR_FLOAT16
ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
ANEURALNETWORKS_TENSOR_QUANT16_SYMM
ANEURALNETWORKS_TENSOR_QUANT8_SYMM
ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
-
ANEURALNETWORKS_ABS
ANEURALNETWORKS_ARGMAX
ANEURALNETWORKS_ARGMIN
ANEURALNETWORKS_AXIS_ALIGNED_BBOX_TRANSFORM
ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_LSTM
ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_RNN
ANEURALNETWORKS_BOX_WITH_NMS_LIMIT
ANEURALNETWORKS_CAST
ANEURALNETWORKS_CHANNEL_SHUFFLE
ANEURALNETWORKS_DETECTION_POSTPROCESSING
ANEURALNETWORKS_EQUAL
ANEURALNETWORKS_EXP
ANEURALNETWORKS_EXPAND_DIMS
ANEURALNETWORKS_GATHER
ANEURALNETWORKS_GENERATE_PROPOSALS
ANEURALNETWORKS_GREATER
ANEURALNETWORKS_GREATER_EQUAL
ANEURALNETWORKS_GROUPED_CONV_2D
ANEURALNETWORKS_HEATMAP_MAX_KEYPOINT
ANEURALNETWORKS_INSTANCE_NORMALIZATION
ANEURALNETWORKS_LESS
ANEURALNETWORKS_LESS_EQUAL
ANEURALNETWORKS_LOG
ANEURALNETWORKS_LOGICAL_AND
ANEURALNETWORKS_LOGICAL_NOT
ANEURALNETWORKS_LOGICAL_OR
ANEURALNETWORKS_LOG_SOFTMAX
ANEURALNETWORKS_MAXIMUM
ANEURALNETWORKS_MINIMUM
ANEURALNETWORKS_NEG
ANEURALNETWORKS_NOT_EQUAL
ANEURALNETWORKS_PAD_V2
ANEURALNETWORKS_POW
ANEURALNETWORKS_PRELU
ANEURALNETWORKS_QUANTIZE
ANEURALNETWORKS_QUANTIZED_16BIT_LSTM
ANEURALNETWORKS_RANDOM_MULTINOMIAL
ANEURALNETWORKS_REDUCE_ALL
ANEURALNETWORKS_REDUCE_ANY
ANEURALNETWORKS_REDUCE_MAX
ANEURALNETWORKS_REDUCE_MIN
ANEURALNETWORKS_REDUCE_PROD
ANEURALNETWORKS_REDUCE_SUM
ANEURALNETWORKS_RESIZE_NEAREST_NEIGHBOR
ANEURALNETWORKS_ROI_ALIGN
ANEURALNETWORKS_ROI_POOLING
ANEURALNETWORKS_RSQRT
ANEURALNETWORKS_SELECT
ANEURALNETWORKS_SIN
ANEURALNETWORKS_SLICE
ANEURALNETWORKS_SPLIT
ANEURALNETWORKS_SQRT
ANEURALNETWORKS_TILE
ANEURALNETWORKS_TOPK_V2
ANEURALNETWORKS_TRANSPOSE_CONV_2D
ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_LSTM
ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_RNN
In Android 10 wurden viele der vorhandenen Vorgänge aktualisiert. Die Änderungen betreffen hauptsächlich Folgendes:
- Unterstützung für das NCHW-Speicherlayout
- Unterstützung für Tensoren mit einem anderen Rang als 4 bei Softmax- und Normalisierungsoperationen
- Unterstützung für erweiterte Convolutionen
- Unterstützung für Eingaben mit gemischter Quantisierung in
ANEURALNETWORKS_CONCATENATION
In der folgenden Liste sind die Vorgänge aufgeführt, die in Android 10 geändert werden. Ausführliche Informationen zu den Änderungen finden Sie in der NNAPI-Referenzdokumentation unter OperationCode.
ANEURALNETWORKS_ADD
ANEURALNETWORKS_AVERAGE_POOL_2D
ANEURALNETWORKS_BATCH_TO_SPACE_ND
ANEURALNETWORKS_CONCATENATION
ANEURALNETWORKS_CONV_2D
ANEURALNETWORKS_DEPTHWISE_CONV_2D
ANEURALNETWORKS_DEPTH_TO_SPACE
ANEURALNETWORKS_DEQUANTIZE
ANEURALNETWORKS_DIV
ANEURALNETWORKS_FLOOR
ANEURALNETWORKS_FULLY_CONNECTED
ANEURALNETWORKS_L2_NORMALIZATION
ANEURALNETWORKS_L2_POOL_2D
ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION
ANEURALNETWORKS_LOGISTIC
ANEURALNETWORKS_LSH_PROJECTION
ANEURALNETWORKS_LSTM
ANEURALNETWORKS_MAX_POOL_2D
ANEURALNETWORKS_MEAN
ANEURALNETWORKS_MUL
ANEURALNETWORKS_PAD
ANEURALNETWORKS_RELU
ANEURALNETWORKS_RELU1
ANEURALNETWORKS_RELU6
ANEURALNETWORKS_RESHAPE
ANEURALNETWORKS_RESIZE_BILINEAR
ANEURALNETWORKS_RNN
ANEURALNETWORKS_ROI_ALIGN
ANEURALNETWORKS_SOFTMAX
ANEURALNETWORKS_SPACE_TO_BATCH_ND
ANEURALNETWORKS_SPACE_TO_DEPTH
ANEURALNETWORKS_SQUEEZE
ANEURALNETWORKS_STRIDED_SLICE
ANEURALNETWORKS_SUB
ANEURALNETWORKS_SVDF
ANEURALNETWORKS_TANH
ANEURALNETWORKS_TRANSPOSE
Android 9
NN HAL 1.1 wurde mit Android 9 eingeführt und enthält die folgenden wichtigen Änderungen.
IDevice::prepareModel_1_1
enthält einenExecutionPreference
-Parameter. Ein Fahrer kann dies nutzen, um seine Vorbereitung anzupassen, da er weiß, dass die App den Akku bevorzugt schont oder das Modell in schnellen aufeinanderfolgenden Aufrufen ausführt.- Neun neue Vorgänge wurden hinzugefügt:
BATCH_TO_SPACE_ND
,DIV
,MEAN
,PAD
,SPACE_TO_BATCH_ND
,SQUEEZE
,STRIDED_SLICE
,SUB
undTRANSPOSE
. - In einer Anwendung kann angegeben werden, dass 32-Bit-Gleitkommaberechnungen mit einem 16-Bit-Gleitkommabereich und/oder mit einer Genauigkeit ausgeführt werden können. Dazu wird
Model.relaxComputationFloat32toFloat16
auftrue
gesetzt. Die StrukturCapabilities
enthält das zusätzliche FeldrelaxedFloat32toFloat16Performance
, damit der Treiber dem Framework seine reduzierte Leistung melden kann.
Android 8.1
Der ursprüngliche HAL (1.0) für neuronale Netzwerke wurde in Android 8.1 veröffentlicht. Weitere Informationen finden Sie unter /neuralnetworks/1.0/
.