Neural Networks API-Treiber

Auf dieser Seite erhalten Sie einen Überblick über die Implementierung eines NNAPI-Treibers (Neural Networks API). Weitere Informationen finden Sie in der Dokumentation in den HAL-Definitionsdateien in hardware/interfaces/neuralnetworks. Ein Beispiel für eine Treiberimplementierung befindet sich in 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 dem NN HAL entsprechen. Die Schnittstelle wird in den HAL-Definitionsdateien in hardware/interfaces/neuralnetworks angegeben.

Der allgemeine Fluss der Schnittstelle zwischen dem Framework und einem Treiber ist in Abbildung 1 dargestellt.

Fluss neuronaler Netzwerke

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.

Das Framework bestimmt anhand der Funktionen, wie schnell und wie energieeffizient jeder Treiber eine Ausführung ausführen kann, um zu bestimmen, wie den verfügbaren Geräten Berechnungen zugewiesen werden. Dazu muss der Treiber standardisierte Leistungszahlen basierend auf der Ausführung von Referenzarbeitslasten bereitstellen.

Verwenden Sie die NNAPI-Benchmarkanwendung, um die Werte zu bestimmen, die der Treiber als Antwort auf IDevice::getCapabilities_1_3 zurückgibt. Messen Sie dazu die Leistung der entsprechenden Datentypen. Für die Leistungsmessung bei 32-Bit-Gleitkommawerten werden die MobileNet-Modelle v1 und v2 sowie asr_float und tts_float empfohlen. Für 8-Bit-quantisierte Werte werden die quantisierten Modelle MobileNet v1 und v2 empfohlen. Weitere Informationen finden Sie unter Android Machine Learning Test Suite.

In Android 9 und niedriger enthält die Capabilities-Struktur nur Informationen zur Treiberleistung für Gleitkomma- und quantisierte Tensoren, nicht aber für skalare Datentypen.

Im Rahmen des Initialisierungsprozesses kann das Framework mithilfe von IDevice::getType, IDevice::getVersionString, IDevice:getSupportedExtensions und IDevice::getNumberOfCacheFilesNeeded weitere Informationen abfragen.

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 eine App, die diesen Treiber verwendet, eine verringerte Leistung oder falsches Verhalten aufweisen.

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 mit 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 Treiber unterstützt den Datentyp nicht.
  • 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, die Ausgabe und die 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, eine Teilmenge des Modells durch Aufrufen von IDevice::prepareModel_1_3 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ührungsaufrufe verbessern die Leistung und reduzieren den Threading-Aufwand im Vergleich zu asynchronen Aufrufen, da die Steuerung erst nach Abschluss der Ausführung an den Anwendungsprozess 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 Methode execute_1_3 wird die Steuerung nach Beginn der Ausführung an den Anwendungsprozess zurückgegeben und der Treiber muss das Framework mit der @1.3::IExecutionCallback benachrichtigen, wenn die Ausführung abgeschlossen ist.

Der an die Ausführungsmethode übergebene Parameter Request listet die für die Ausführung verwendeten Eingabe- und Ausgabe-Operanden auf. 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 Ausgabe-Operanden müssen vollständig angegeben werden, 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 Treiber auffordern, mehr als ein vorbereitetes Modell zu führen. Beispiel: Bereiten Sie das Modell m1 vor, bereiten Sie m2 vor, führen Sie die Anfrage r1 auf m1 aus, führen Sie r2 auf m2 aus, führen Sie r3 unter m1 aus, führen Sie r4 auf m2 aus, veröffentlichen Sie m1 (wie in Bereinigen beschrieben) und geben Sie m2 frei.

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 sich auf Aktionen beschränken, die sich bei frühzeitiger Ausführung negativ auf den Systemzustand auswirken, z. B. das Reservieren großer temporärer Zwischenspeicher oder das Erhöhen 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 sich der Client für die Kommunikation zwischen App- und Treiberprozessen für ein Ausführungs-Burst-Objekt entscheiden. Weitere Informationen finden Sie unter Burst-Ausführungen und schnelle Nachrichtenwarteschlangen.

Um die Leistung bei mehreren schnell hintereinander ausgeführten Ausführungen zu verbessern, kann der Treiber temporäre Zwischenspeicher beibehalten oder die 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 bereitstellen, die die Dimensionsinformationen für jeden Ausgabeoperanden enthalten. Weitere Informationen zu Dimensionen finden Sie unter OutputShape.

Wenn eine Ausführung aufgrund eines zu großen Ausgabepuffers fehlschlägt, muss der Treiber in der Liste der Ausgabeformen angeben, welche Ausgabeoperanden eine unzureichende Puffergröße haben, und so viele Dimensionsinformationen wie möglich melden, wobei Null für unbekannte Dimensionen verwendet wird.

Timing

In Android 10 kann eine App die Ausführungszeit abfragen, wenn sie während des Kompilierungsvorgangs ein einzelnes Gerät 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 folgende Dauer in Mikrosekunden in der Timing-Struktur:

  • Ausführungszeit auf dem Gerät:Die Ausführungszeit im Treiber, der auf dem Hostprozessor ausgeführt wird, ist nicht enthalten.
  • 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 Dauer als einen 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 außerdem 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 Fencing-Ausführung ruft das Framework die Methode IPreparedModel::executeFenced auf, um eine abgegrenzte, asynchrone Ausführung für ein vorbereitetes Modell mit einem Vektor von Synchronisierungszäunen zu starten, auf die gewartet werden soll. Wenn die asynchrone Aufgabe abgeschlossen ist, bevor der Aufruf zurückgegeben wird, kann ein leerer Handle für sync_fence zurückgegeben werden. Außerdem muss ein IFencedExecutionCallback-Objekt zurückgegeben werden, damit das Framework Informationen zum Fehlerstatus und zur 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 ab dem Aufruf von executeFenced bis zu dem Zeitpunkt, an dem executeFenced den zurückgegebenen syncFence anzeigt.
  • timingFenced: Zeitspanne ab dem Zeitpunkt, an dem alle Synchronisierungszäune, auf die die Ausführung wartet, signalisiert werden, bis executeFenced den zurückgegebenen syncFence signalisiert.

Ablauf steuern

Für Geräte mit Android 11 oder höher umfasst die NNAPI zwei Abläufe für die Steuerung, IF und WHILE, die andere Modelle als Argumente verwenden und sie bedingt (IF) oder wiederholt (WHILE) ausführen. Weitere Informationen zur Implementierung findest du unter Ablauf steuern.

Servicequalitä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 Dienstqualität.

Bereinigung

Wenn eine Anwendung die Verwendung eines vorbereiteten Modells beendet hat, gibt das Framework seinen Verweis auf das Objekt @1.3::IPreparedModel 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 er keine Verweise auf das IPreparedModel-Objekt enthalten, nachdem das IPreparedeModel-Objekt über IPreparedModelCallback::notify_1_3 zurückgegeben wurde.

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 zuzuweisen. Der Treiber sollte die Teile, die er nicht verarbeiten kann, an das Framework melden und das Framework den Rest überlassen.

Das Framework bietet eine CPU-Implementierung für alle NNAPI-Vorgänge mit Ausnahme von anbieterdefinierten Vorgängen. Weitere Informationen finden Sie unter Anbietererweiterungen.

Die in Android 10 eingeführten Vorgänge (API-Level 29) haben 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.

Dienstprogrammfunktionen

Die NNAPI-Codebasis enthält Dienstfunktionen, 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 die LOG von Android, mit dem die Nachricht nur protokolliert wird, wenn das entsprechende Tag im Attribut debug.nn.vlog festgelegt ist. initVLogMask() muss vor Aufrufen von VLOG aufgerufen werden. Mit dem Makro VLOG_IS_ON kann geprüft werden, ob VLOG 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 kein Logging erfolgen soll.
    • Das Token 1 oder all, das angibt, dass das gesamte Logging abgeschlossen sein soll.
    • Eine Liste von Tags, die durch Leerzeichen, Kommas oder Doppelpunkte getrennt sind, die angeben, welches Logging erfolgen soll. Die Tags sind compilation, cpuexe, driver, execution, manager und model.
  • compliantWithV1_*: Gibt true zurück, wenn ein NN HAL-Objekt ohne Informationsverlust in den gleichen Typ einer anderen HAL-Version konvertiert werden kann. Wenn Sie beispielsweise compliantWithV1_0 für eine V1_2::Model aufrufen, wird false zurückgegeben, wenn das Modell in NN HAL 1.1 oder NN HAL 1.2 eingeführte Vorgangstypen enthält.

  • convertToV1_*: Wandelt ein NN HAL-Objekt von einer Version in eine andere um. Eine Warnung wird protokolliert, wenn die Conversion zu einem Informationsverlust führt, d. h. wenn die neue Version des Typs den Wert nicht vollständig darstellen kann.

  • Funktionen: Mit den Funktionen nonExtensionOperandPerformance und update können Sie das Feld Capabilities::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 seiner HAL-Version gültig ist.

  • validate*: Gibt true zurück, wenn das NN HAL-Objekt gemäß der Spezifikation der HAL-Version gültig ist. OEM- und Erweiterungstypen werden nicht validiert. validateModel gibt beispielsweise false 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 unter 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 (wenn kein Stream angegeben ist).

Zertifizierungsstufe

Teste deine Implementierung der NNAPI mithilfe der im Android-Framework enthaltenen VTS- und CTS-Tests. Bei VTS werden Ihre Treiber direkt (ohne das Framework) ausgeführt, während CTS sie indirekt über das Framework ausführt. Damit werden alle API-Methoden getestet und geprüft, ob alle von den Treibern unterstützten Vorgänge ordnungsgemäß funktionieren und Ergebnisse liefern, die den Anforderungen an die Präzision 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: Der Abstand um eine Sekunde (mit Ausnahme von mobilenet_quantized, bei dem der Abstand um drei erhöht wird)

  • Boolesch: genaue Übereinstimmung

Eine Möglichkeit, wie CTS NNAPI testet, besteht darin, feste Pseudozufallsgrafiken zu generieren, die zum Testen und Vergleichen der Ausführungsergebnisse jedes Treibers mit der NNAPI-Referenzimplementierung verwendet 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 Genauigkeitskriterien finden Sie unter TestRandomGraph.cpp und TestHarness.h.

Fuzzing-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. Zum Beispiel bevorzugt libFuzzer Testfälle, die mit neuen Codezeilen ausgeführt werden. Dies reduziert den Zeitaufwand für die Tests zum Auffinden von problematischen Codes erheblich.

Wenn Sie Fuzz-Tests durchführen möchten, um die Treiberimplementierung zu validieren, ändern Sie frameworks/ml/nn/runtime/test/android_fuzzing/DriverFuzzTest.cpp im Testdienstprogramm libneuralnetworks_driver_fuzzer in AOSP so, dass Ihr Treibercode enthalten ist. 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 geprüft. 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 eine in CTS und VTS enthaltene NNAPI-Benchmark zur Validierung der Genauigkeit realer Modelle auf den Geräten der Anbieter. Die Benchmark bewertet die Latenz und Genauigkeit und vergleicht die Ergebnisse der Treiber mit den Ergebnissen unter Verwendung von TF Lite, die auf der CPU für dasselbe Modell und dieselben Datasets ausgeführt wird. Dadurch wird sichergestellt, dass die Genauigkeit eines Treibers nicht geringer ist als die der CPU-Referenzimplementierung.

Android-Plattformentwickler verwenden außerdem MLTS, um die Latenz und Genauigkeit von Treibern zu bewerten.

Die NNAPI-Benchmark ist in zwei Projekten in AOSP zu finden:

Modelle und Datasets

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 float und u8 in verschiedenen Größen quantisiert, werden für eine kleine Teilmenge (1.500 Bilder) des Open Images Dataset v4 ausgeführt.
  • Auf LSTM basierendes, auf Langzeitspeicher basierendes akustisches Modell für die Sprachausgabe, das mit einer kleinen Teilmenge der arktischen CMU ausgeführt wird.
  • Auf LSTM basierendes akustisches Modell für die automatische Spracherkennung, das für eine kleine Teilmenge 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 Absturztests 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.
  • Fahrunfallerkennung:Tests können einen Treiberabsturz erkennen, der bei einem NNAPI-Aufruf zu einem Fehler führt. 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.
  • Targeting aller verfügbaren Beschleuniger: Tests werden für alle verfügbaren Treiber ausgeführt.

Alle Absturztests haben die folgenden vier möglichen Ergebnisse:

  • SUCCESS: Ausführung 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: 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 das MLTS:

  1. Verbinden Sie ein Zielgerät mit der Workstation und sorgen Sie dafür, dass es über ADB erreichbar ist. Exportieren Sie die Umgebungsvariable ANDROID_SERIAL des Zielgeräts, wenn mehr als ein Gerät verbunden ist.
  2. cd in das Android-Quellverzeichnis der obersten Ebene.

    source build/envsetup.sh
    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 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 in HAL-Versionen von Android und Neural Networks beschrieben.

Android 11

Mit Android 11 wird NN HAL 1.3 eingeführt. Es 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 Vorgänge mit unsignierter Quantisierung unterstützen, müssen auch die signierten Varianten dieser Vorgänge 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. Von dieser Anforderung gibt es fünf Ausnahmen: CAST, HASHTABLE_LOOKUP, LSH_PROJECTION, PAD_V2 und QUANTIZED_16BIT_LSTM. Der QUANTIZED_16BIT_LSTM-Vorgang unterstützt keine signierten Operanden und die anderen vier Vorgänge unterstützen die signierte Quantisierung. Die Ergebnisse müssen jedoch nicht identisch sein.
  • Unterstützung für abgegrenzte Ausführungen, bei denen das Framework die Methode IPreparedModel::executeFenced aufruft, um eine abgegrenzte, asynchrone Ausführung für ein vorbereitetes Modell mit einem Vektor von Synchronisierungszäunen zu starten, auf die gewartet werden soll. Weitere Informationen finden Sie unter Fenced Ausführung.
  • Unterstützung für Ablaufsteuerung. Fügt die Vorgänge IF und WHILE hinzu, die andere Modelle als Argumente annehmen und bedingt (IF) oder wiederholt (WHILE) ausführen. Weitere Informationen finden Sie unter Ablauf steuern.
  • 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 Dienstqualität.
  • Unterstützung von Arbeitsspeicherdomains, die Zuweisungsschnittstellen für von Treibern verwaltete Zwischenspeicher bereitstellen. Dadurch können gerätespezifische Memorys über Ausführungen hinweg übergeben werden, wodurch unnötiges Kopieren und Transformieren von Daten zwischen aufeinanderfolgenden Ausführungen auf demselben Treiber unterdrückt wird. Weitere Informationen finden Sie unter Speicherdomains.

Android 10

Mit Android 10 wird NN HAL 1.2 eingeführt und enthält die folgenden wichtigen Änderungen.

  • Die Struktur Capabilities enthält alle Datentypen, einschließlich skalarer Datentypen, und stellt die unveränderliche Leistung mithilfe eines Vektors anstelle von benannten Feldern dar.
  • Mit den Methoden getVersionString und getType kann das Framework Informationen zum Gerätetyp (DeviceType) und zur Version abrufen. Siehe Geräteerkennung und -zuweisung.
  • Die Methode executeSynchronously wird standardmäßig aufgerufen, um eine Ausführung synchron auszuführen. Die Methode execute_1_2 weist das Framework an, die Ausführung asynchron auszuführen. Siehe Ausführung.
  • Der Parameter MeasureTiming für executeSynchronously, execute_1_2 und die Burst-Ausführung gibt an, ob der Treiber die Ausführungsdauer messen soll. Die Ergebnisse werden in der Timing-Struktur aufgeführt. Weitere Informationen finden Sie unter Zeitplan.
  • Unterstützung für Ausführungen, bei denen ein oder mehrere Ausgabeoperanden eine unbekannte Dimension oder einen unbekannten Rang haben. Siehe Ausgabeform.
  • Unterstützung von Anbietererweiterungen, d. h. Sammlungen von anbieterdefinierten Vorgängen und Datentypen. Der Treiber meldet unterstützte Erweiterungen über die Methode IDevice::getSupportedExtensions. Siehe Anbietererweiterungen.
  • Möglichkeit für ein Burst-Objekt, eine Reihe von Burst-Ausführungen mithilfe von schnellen Nachrichtenwarteschlangen (Fast Message Queues, FMQs) zu steuern, um zwischen Anwendungs- und Treiberprozessen zu kommunizieren und so die Latenz zu reduzieren. Siehe Burst-Ausführungen und schnelle Nachrichtenwarteschlangen.
  • Unterstützung für AHardwareBuffer, damit der Treiber Ausführungen ohne Kopieren von Daten ausführen kann. Siehe AHardwareBuffer.
  • Verbesserte Unterstützung für das Caching von Kompilierungsartefakten, um die für die Kompilierung beim Start einer Anwendung benötigte Zeit zu reduzieren. Siehe Kompilierung-Caching.

Unter Android 10 werden die folgenden Operandentypen und Vorgänge eingeführt.

  • Operandtypen

    • 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
  • Betrieb

    • 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

Unter Android 10 werden viele der bestehenden 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 in Softmax- und Normalisierungsvorgängen
  • Unterstützung von erweiterten Faltungen
  • 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 einen ExecutionPreference-Parameter. Ein Fahrer kann damit die Vorbereitung anpassen, da er weiß, dass die App den Akku schonen möchte 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 und TRANSPOSE.
  • In einer Anwendung kann angegeben werden, dass 32-Bit-Float-Berechnungen mit einem 16-Bit-Gleitkommabereich und/oder mit 16-Bit-Genauigkeit ausgeführt werden können. Dazu muss Model.relaxComputationFloat32toFloat16 auf true gesetzt werden. Die Struktur Capabilities hat das zusätzliche Feld relaxedFloat32toFloat16Performance, damit der Treiber seine gelockerte Leistung an das Framework 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/.