API-Treiber für neuronale Netze

Diese Seite bietet einen Überblick über die Implementierung eines Neural Networks API (NNAPI)-Treibers. Weitere Einzelheiten finden Sie in der Dokumentation in den HAL-Definitionsdateien unter hardware/interfaces/neuralnetworks . Eine Beispieltreiberimplementierung finden Sie in frameworks/ml/nn/driver/sample .

Weitere Informationen zur Neural Networks API finden Sie unter Neural Networks API .

Neuronale Netze HAL

Das Neural Networks (NN) HAL definiert eine Abstraktion der verschiedenen Geräte , wie z. B. Grafikprozessoren (GPUs) und digitale Signalprozessoren (DSPs), die in einem Produkt (z. B. einem Telefon oder Tablet) enthalten sind. Die Treiber für diese Geräte müssen dem NN HAL entsprechen. Die Schnittstelle ist in den HAL-Definitionsdateien unter hardware/interfaces/neuralnetworks angegeben.

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

Fluss neuronaler Netze

Abbildung 1. Fluss neuronaler Netze

Initialisierung

Bei der Initialisierung fragt das Framework den Treiber mithilfe IDevice::getCapabilities_1_3 nach seinen Fähigkeiten ab. Die @1.3::Capabilities Struktur umfasst alle Datentypen und stellt die nicht entspannte Leistung mithilfe eines Vektors dar.

Um zu bestimmen, wie Berechnungen den verfügbaren Geräten zugewiesen werden, nutzt das Framework die Fähigkeiten, um zu verstehen, wie schnell und wie energieeffizient jeder Treiber eine Ausführung durchführen kann. Um diese Informationen bereitzustellen, muss der Treiber standardisierte Leistungszahlen bereitstellen, die auf der Ausführung von Referenz-Workloads basieren.

Um die Werte zu ermitteln, die der Treiber als Reaktion auf IDevice::getCapabilities_1_3 zurückgibt, verwenden Sie die NNAPI-Benchmark-App, um die Leistung für entsprechende Datentypen zu messen. Die Modelle MobileNet v1 und v2, asr_float und tts_float werden zum Messen der Leistung für 32-Bit-Gleitkommawerte empfohlen, und die quantisierten Modelle MobileNet v1 und v2 werden für quantisierte 8-Bit-Werte 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 und keine skalaren 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 alle in diesem Abschnitt beschriebenen Abfragen immer dieselben Werte für einen bestimmten Treiber melden. Andernfalls kann es bei einer App, die diesen Treiber verwendet, zu Leistungseinbußen oder fehlerhaftem Verhalten kommen.

Zusammenstellung

Das Framework bestimmt, welche Geräte verwendet werden sollen, wenn es eine Anfrage von einer App erhält. In Android 10 können Apps die Geräte erkennen und angeben, aus denen das Framework auswählt. Weitere Informationen finden Sie unter Geräteerkennung und -zuweisung .

Zum Zeitpunkt der Modellkompilierung sendet das Framework das Modell an jeden Kandidatentreiber, indem es IDevice::getSupportedOperations_1_3 aufruft. Jeder Treiber gibt ein Array boolescher Werte zurück, das angibt, welche Operationen des Modells unterstützt werden. Ein Treiber kann aus verschiedenen Gründen feststellen, dass er einen bestimmten Vorgang nicht unterstützen kann. Zum Beispiel:

  • Der Treiber unterstützt den Datentyp nicht.
  • Der Treiber unterstützt nur Vorgänge mit bestimmten Eingabeparametern. Beispielsweise unterstützt ein Treiber möglicherweise 3x3- und 5x5-Faltungsoperationen, nicht jedoch 7x7-Faltungsoperationen.
  • Der Treiber weist Speicherbeschränkungen auf, die ihn daran hindern, große Diagramme oder Eingaben zu verarbeiten.

Während der Kompilierung können die Eingabe-, Ausgabe- und internen Operanden des Modells, wie in OperandLifeTime beschrieben, unbekannte Dimensionen oder Rang haben. Weitere Informationen finden Sie unter Ausgabeform .

Das Framework weist jeden ausgewählten Treiber an, sich durch Aufruf IDevice::prepareModel_1_3 auf die Ausführung einer Teilmenge des Modells vorzubereiten. Jeder Treiber kompiliert dann seine Teilmenge. Beispielsweise könnte ein Fahrer Code generieren oder eine neu geordnete Kopie der Gewichte erstellen. Da zwischen der Kompilierung des Modells und der Ausführung von Anforderungen viel Zeit vergehen kann, sollten während der Kompilierung keine Ressourcen wie große Teile des Gerätespeichers zugewiesen werden.

Bei Erfolg gibt der Treiber ein @1.3::IPreparedModel Handle zurück. Wenn der Treiber beim Vorbereiten seiner Teilmenge des Modells einen Fehlercode zurückgibt, führt das Framework das gesamte Modell auf der CPU aus.

Um die für die Kompilierung beim Start einer App benötigte Zeit zu reduzieren, kann ein Treiber Kompilierungsartefakte zwischenspeichern. Weitere Informationen finden Sie unter Kompilierungs-Caching .

Ausführung

Wenn eine App das Framework auffordert, eine Anforderung 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 „Abgeschirmte Ausführung “) oder mit einer Burst-Ausführung ausgeführt werden.

Synchrone Ausführungsaufrufe verbessern die Leistung und reduzieren den Threading-Overhead im Vergleich zu asynchronen Aufrufen, da die Kontrolle 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 App-Prozess über den Abschluss einer Ausführung zu informieren.

Mit der asynchronen Methode execute_1_3 kehrt die Kontrolle nach dem Start der Ausführung zum App-Prozess zurück, und der Treiber muss das Framework über @1.3::IExecutionCallback benachrichtigen, wenn die Ausführung abgeschlossen ist.

Der an die Ausführungsmethode übergebene Request Parameter listet die für die Ausführung verwendeten Eingabe- und Ausgabeoperanden auf. Der Speicher, in dem die Operandendaten gespeichert werden, muss die Zeilenreihenfolge verwenden, wobei die erste Dimension am langsamsten iteriert, und am Ende einer Zeile darf kein Abstand vorhanden sein. Weitere Informationen zu den Operandentypen finden Sie unter Operanden .

Bei NN HAL 1.2 oder höheren Treibern werden nach Abschluss einer Anforderung 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 Ausgabeoperand eine unbekannte Dimension oder einen unbekannten Rang hat, muss der Treiber Ausgabeinformationen mit 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. Damit die Ausführung erfolgreich abgeschlossen werden kann, müssen die Dimensionen für Eingabe- und Ausgabeoperanden vollständig angegeben werden. Interne Operanden können eine oder mehrere unbekannte Dimensionen haben, sie müssen jedoch einen bestimmten Rang haben.

Bei Benutzeranfragen, die sich über mehrere Treiber erstrecken, ist das Framework für die Reservierung von Zwischenspeicher und die Reihenfolge der Aufrufe an jeden Treiber verantwortlich.

Mehrere Anfragen können parallel auf demselben @1.3::IPreparedModel 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 beizubehalten. Bereiten Sie beispielsweise das Modell m1 vor, bereiten Sie m2 vor, führen Sie die Anforderung r1 auf m1 aus, führen Sie r2 auf m2 aus, führen Sie r3 auf m1 aus, führen Sie r4 auf m2 aus, geben Sie m1 frei (beschrieben unter „Bereinigung “) und geben Sie m2 frei.

Um eine langsame erste Ausführung zu vermeiden, die zu einer schlechten Benutzererfahrung führen könnte (z. B. ein Stottern des ersten Frames), sollte der Treiber die meisten Initialisierungen in der Kompilierungsphase durchführen. Die Initialisierung bei der ersten Ausführung sollte auf Aktionen beschränkt werden, die sich bei früher Ausführung negativ auf den Systemzustand auswirken, z. B. das Reservieren großer temporärer Puffer oder das Erhöhen der Taktrate eines Geräts. Treiber, die nur eine begrenzte Anzahl gleichzeitiger Modelle vorbereiten können, müssen ihre Initialisierung möglicherweise bei der ersten Ausführung durchführen.

In Android 10 oder höher kann der Client in Fällen, in denen mehrere Ausführungen mit demselben vorbereiteten Modell schnell hintereinander ausgeführt werden, ein Ausführungs-Burst-Objekt für die Kommunikation zwischen App- und Treiberprozessen verwenden. Weitere Informationen finden Sie unter Burst-Ausführungen und Fast Message Queues .

Um die Leistung bei mehreren Ausführungen in schneller Folge zu verbessern, kann der Treiber temporäre Puffer beibehalten oder die Taktraten erhöhen. Es wird empfohlen, einen Watchdog-Thread zu erstellen, um Ressourcen freizugeben, wenn nach einem festgelegten Zeitraum keine neuen Anforderungen erstellt werden.

Ausgabeform

Bei Anforderungen, 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 enthält. 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 über eine unzureichende Puffergröße verfügen, und sollte so viele Dimensionsinformationen wie möglich melden, wobei für unbekannte Dimensionen Null verwendet wird.

Zeitliche Koordinierung

In Android 10 kann eine App nach der Ausführungszeit fragen, wenn die App während des Kompilierungsprozesses ein einzelnes Gerät zur Verwendung angegeben hat. Einzelheiten finden Sie unter MeasureTiming und Geräteerkennung und -zuweisung . In diesem Fall muss ein NN HAL 1.2-Treiber die Ausführungsdauer messen oder UINT64_MAX melden (um anzuzeigen, dass die Dauer nicht verfügbar ist), wenn er eine Anforderung ausführt. Der Treiber sollte jegliche Leistungseinbußen minimieren, die sich aus der Messung der Ausführungsdauer ergeben.

Der Treiber meldet die folgenden Dauern in Mikrosekunden in der Timing Struktur:

  • 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 Zeiträume müssen die Zeit umfassen, in der die Ausführung angehalten wird, beispielsweise wenn die Ausführung durch andere Aufgaben unterbrochen wurde oder wenn auf die Verfügbarkeit einer Ressource gewartet wird.

Wenn der Treiber nicht aufgefordert wurde, die Ausführungsdauer zu messen, oder wenn ein Ausführungsfehler vorliegt, 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 einen anderen Wert als UINT64_MAX meldet, muss die Ausführungszeit im Treiber der Zeit auf dem Gerät entsprechen oder diese überschreiten.

Eingezä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 wird, wenn die Ausführung abgeschlossen ist. Dies reduziert den Overhead für kleine Sequenzmodelle und Streaming-Anwendungsfälle. Die abgeschirmte Ausführung ermöglicht außerdem eine effizientere Interoperabilität mit anderen Komponenten, die sync_fence signalisieren oder darauf warten können. Weitere Informationen zu sync_fence finden Sie unter Synchronisierungsframework .

Bei einer eingezäunten Ausführung ruft das Framework die Methode IPreparedModel::executeFenced auf, um eine eingezäunte, asynchrone Ausführung auf einem vorbereiteten Modell mit einem Vektor von Synchronisierungszäunen zu starten, auf die gewartet werden muss. Wenn die asynchrone Aufgabe abgeschlossen ist, bevor der Aufruf zurückkehrt, kann ein leeres 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 Fehlerdauer abfragen kann.

Nachdem eine Ausführung abgeschlossen ist, können die folgenden zwei Zeitwerte , die die Dauer der Ausführung messen, über IFencedExecutionCallback::getExecutionInfo abgefragt werden.

  • timingLaunched : Dauer vom Aufruf executeFenced bis zum Signalisierung des zurückgegebenen syncFence executeFenced .
  • timingFenced : Dauer von der Signalisierung aller Synchronisierungs-Fences, auf die die Ausführung wartet, bis zum Signalisieren der zurückgegebenen syncFence executeFenced .

Kontrollfluss

Für Geräte mit Android 11 oder höher umfasst die NNAPI zwei Kontrollflussoperationen, IF und WHILE , die andere Modelle als Argumente verwenden und diese bedingt ( IF ) oder wiederholt ( WHILE ) ausführen. Weitere Informationen zur Implementierung finden Sie unter Kontrollfluss .

Servicequalität

In Android 11 umfasst die NNAPI eine verbesserte Dienstqualität (Quality of Service, QoS), indem sie es einer App ermöglicht, die relativen Prioritäten ihrer Modelle, die maximale erwartete Zeit für die Vorbereitung eines Modells und die maximale erwartete Zeit für eine Ausführung anzugeben zu vervollständigen. Weitere Informationen finden Sie unter Servicequalität .

Aufräumen

Wenn eine App mit der Verwendung eines vorbereiteten Modells fertig ist, gibt das Framework seinen Verweis auf das @1.3::IPreparedModel Objekt frei. Wenn auf das IPreparedModel Objekt nicht mehr verwiesen wird, wird es automatisch im Treiberdienst zerstört, der es erstellt hat. Modellspezifische Ressourcen können zu diesem Zeitpunkt in der Implementierung des Destruktors durch den Treiber zurückgefordert werden. Wenn der Treiberdienst möchte, dass das IPreparedModel Objekt automatisch zerstört wird, wenn es vom Client nicht mehr benötigt wird, darf es keine Verweise auf das IPreparedModel Objekt enthalten, nachdem das IPreparedeModel Objekt über IPreparedModelCallback::notify_1_3 zurückgegeben wurde.

CPU auslastung

Von den Fahrern wird erwartet, dass sie die CPU zum Einrichten von Berechnungen verwenden. Treiber sollten die CPU nicht zum Durchführen von Diagrammberechnungen verwenden, da dies die Fähigkeit des Frameworks beeinträchtigt, Arbeit korrekt zuzuweisen. Der Treiber sollte die Teile, die er nicht verarbeiten kann, dem Framework melden und das Framework den Rest erledigen lassen.

Das Framework stellt eine CPU-Implementierung für alle NNAPI-Operationen mit Ausnahme der vom Hersteller definierten Operationen bereit. Weitere Informationen finden Sie unter Anbietererweiterungen .

Die in Android 10 (API-Level 29) eingeführten Vorgänge verfügen lediglich über eine Referenz-CPU-Implementierung, um zu überprüfen, ob die CTS- und VTS-Tests korrekt sind. Die optimierten Implementierungen, die in Frameworks für mobiles maschinelles Lernen enthalten sind, werden der NNAPI-CPU-Implementierung vorgezogen.

Dienstprogrammfunktionen

Die NNAPI-Codebasis umfasst Hilfsfunktionen, die von Treiberdiensten verwendet werden können.

Die Datei frameworks/ml/nn/common/include/Utils.h enthält verschiedene Hilfsfunktionen, beispielsweise solche, die für die Protokollierung und die Konvertierung zwischen verschiedenen NN-HAL-Versionen verwendet werden.

  • VLogging: VLOG ist ein Wrapper-Makro um Androids LOG , das die Nachricht nur protokolliert, wenn das entsprechende Tag in der Eigenschaft debug.nn.vlog festgelegt ist. initVLogMask() muss vor allen Aufrufen von VLOG aufgerufen werden. Mit dem Makro VLOG_IS_ON kann überprüft werden, ob VLOG derzeit aktiviert ist, sodass komplizierter Protokollierungscode übersprungen werden kann, wenn er nicht benötigt wird. Der Wert der Immobilie muss einer der folgenden sein:

    • Eine leere Zeichenfolge, die angibt, dass keine Protokollierung durchgeführt werden soll.
    • Das Token 1 oder all gibt an, dass die gesamte Protokollierung durchgeführt werden soll.
    • Eine durch Leerzeichen, Kommas oder Doppelpunkte getrennte Liste von Tags, die angibt, welche Protokollierung durchgeführt werden soll. Die Tags sind compilation , cpuexe , driver , execution , manager und model .
  • compliantWithV1_* : Gibt true zurück, wenn ein NN-HAL-Objekt in den gleichen Typ einer anderen HAL-Version konvertiert werden kann, ohne dass Informationen verloren gehen. Beispielsweise gibt der Aufruf von compliantWithV1_0 für ein V1_2::Model false zurück, wenn das Modell Operationstypen enthält, die in NN HAL 1.1 oder NN HAL 1.2 eingeführt wurden.

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

  • Capabilities: Die Funktionen nonExtensionOperandPerformance und update können zum Erstellen des Felds Capabilities::operandPerformance verwendet werden.

  • Abfragen von Eigenschaften der Typen: isExtensionOperandType , isExtensionOperationType , nonExtensionSizeOfData , nonExtensionOperandSizeOfData , nonExtensionOperandTypeIsScalar , tensorHasUnspecifiedDimensions .

Die Datei frameworks/ml/nn/common/include/ValidateHal.h enthält Hilfsfunktionen zum Überprüfen, 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 seiner HAL-Version gültig ist. OEM-Typen und Erweiterungstypen werden nicht validiert. Beispielsweise gibt validateModel false zurück, wenn das Modell eine Operation enthält, die auf einen nicht vorhandenen Operandenindex verweist, oder eine Operation, die 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 Neural Networks-Code zu vereinfachen. Ein Beispiel finden Sie in den NNTRACE_* Makroaufrufen im Beispieltreiber .

Die Datei frameworks/ml/nn/common/include/GraphDump.h enthält eine Dienstprogrammfunktion, um den Inhalt eines Model zu Debugzwecken in grafischer Form auszugeben.

  • graphDump : Schreibt eine Darstellung des Modells im Graphviz-Format ( .dot ) in den angegebenen Stream (falls bereitgestellt) oder in den Logcat (falls kein Stream bereitgestellt wird).

Validierung

Um Ihre Implementierung des NNAPI zu testen, verwenden Sie die im Android-Framework enthaltenen VTS- und CTS-Tests. VTS übt Ihre Treiber direkt aus (ohne das Framework zu verwenden), während CTS sie indirekt über das Framework ausübt. Diese testen jede API-Methode und stellen sicher, dass alle von den Treibern unterstützten Vorgänge ordnungsgemäß funktionieren und Ergebnisse liefern, die den Präzisionsanforderungen entsprechen.

Die Präzisionsanforderungen in CTS und VTS für das NNAPI lauten wie folgt:

  • Gleitkomma: abs(erwartet – tatsächlich) <= atol + rtol * abs(erwartet); Wo:

    • Für fp32 ist atol = 1e-5f, rtol = 5,0f * 1,1920928955078125e-7
    • Für fp16 ist atol = rtol = 5,0f * 0,0009765625f
  • Quantisiert: um eins versetzt (außer mobilenet_quantized , das um drei versetzt ist)

  • Boolescher Wert: genaue Übereinstimmung

Eine Möglichkeit, wie CTS NNAPI testet, besteht darin, feste pseudozufällige Diagramme zu generieren, die zum Testen und Vergleichen der Ausführungsergebnisse jedes Treibers mit der NNAPI-Referenzimplementierung verwendet werden. Wenn die Ergebnisse bei Treibern mit NN HAL 1.2 oder höher die Genauigkeitskriterien nicht erfüllen, meldet CTS einen Fehler und speichert eine Spezifikationsdatei für das fehlgeschlagene Modell zum Debuggen unter /data/local/tmp . Weitere Einzelheiten zu den Präzisionskriterien finden Sie unter TestRandomGraph.cpp und TestHarness.h .

Fuzz-Test

Der Zweck von Fuzz-Tests besteht darin, Abstürze, Behauptungen, Speicherverletzungen oder allgemeines undefiniertes Verhalten im zu testenden Code aufgrund von Faktoren wie unerwarteten Eingaben zu finden. Für NNAPI-Fuzz-Tests verwendet Android auf libFuzzer basierende Tests, die beim Fuzzing effizient sind, da sie die Zeilenabdeckung früherer Testfälle nutzen, um neue Zufallseingaben zu generieren. Beispielsweise bevorzugt libFuzzer Testfälle, die auf neuen Codezeilen ausgeführt werden. Dies reduziert die Zeit, die Tests benötigen, um problematischen Code zu finden, erheblich.

Um Fuzz-Tests zur Validierung Ihrer Treiberimplementierung durchzuführen, ändern Sie frameworks/ml/nn/runtime/test/android_fuzzing/DriverFuzzTest.cpp im Testdienstprogramm libneuralnetworks_driver_fuzzer in AOSP, um Ihren Treibercode einzuschließen. Weitere Informationen zum NNAPI-Fuzz-Testen finden Sie unter frameworks/ml/nn/runtime/test/android_fuzzing/README.md .

Sicherheit

Da App-Prozesse direkt mit dem Prozess eines Fahrers kommunizieren, müssen Fahrer die Argumente der Anrufe, die sie erhalten, validieren. Diese Validierung wird von VTS überprüft. Der Validierungscode befindet sich in frameworks/ml/nn/common/include/ValidateHal.h .

Fahrer sollten außerdem sicherstellen, dass Apps andere Apps nicht beeinträchtigen können, wenn sie dasselbe Gerät verwenden.

Android-Testsuite für maschinelles Lernen

Die Android Machine Learning Test Suite (MLTS) ist ein in CTS und VTS enthaltener NNAPI-Benchmark zur Validierung der Genauigkeit realer Modelle auf Anbietergeräten. Der Benchmark bewertet Latenz und Genauigkeit und vergleicht die Ergebnisse der Treiber mit den Ergebnissen von TF Lite , das auf der CPU ausgeführt wird, für dasselbe Modell und dieselben Datensätze. Dadurch wird sichergestellt, dass die Genauigkeit eines Treibers nicht schlechter ist als die der CPU-Referenzimplementierung.

Entwickler von Android-Plattformen verwenden MLTS auch, um die Latenz und Genauigkeit von Treibern zu bewerten.

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

Modelle und Datensätze

Der NNAPI-Benchmark verwendet die folgenden Modelle und Datensätze.

  • MobileNetV1 float und u8, quantisiert in verschiedenen Größen, werden mit einer kleinen Teilmenge (1500 Bilder) des Open Images Dataset v4 ausgeführt.
  • MobileNetV2 float und u8, quantisiert in verschiedenen Größen, werden mit einer kleinen Teilmenge (1500 Bilder) des Open Images Dataset v4 ausgeführt.
  • Auf dem langen Kurzzeitgedächtnis (LSTM) basierendes akustisches Modell für Text-to-Speech, ausgeführt mit einer kleinen Teilmenge des CMU-Arctic-Sets.
  • LSTM-basiertes Akustikmodell für die automatische Spracherkennung, ausgeführt mit einer kleinen Teilmenge des LibriSpeech-Datensatzes.

Weitere Informationen finden Sie unter platform/test/mlts/models .

Belastbarkeitstest

Die Android Machine Learning Test Suite umfasst eine Reihe von Crashtests, um die Widerstandsfähigkeit von Treibern unter intensiven Nutzungsbedingungen oder in besonderen Fällen des Kundenverhaltens zu validieren.

Alle Crashtests bieten folgende Funktionen:

  • Hang-Erkennung: 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 über.
  • NNAPI-Client-Absturzerkennung: Tests überstehen Client-Abstürze und Tests schlagen mit der Fehlerursache CRASH fehl.
  • Erkennung von Treiberabstürzen: Tests können einen Treiberabsturz erkennen, der einen Fehler bei einem NNAPI-Aufruf verursacht. Beachten Sie, dass es bei Treiberprozessen zu Abstürzen kommen kann, die weder zu einem NNAPI-Fehler führen noch dazu führen, dass der Test fehlschlägt. Um diese Art von Fehlern abzudecken, wird empfohlen, den Befehl tail im Systemprotokoll für treiberbezogene Fehler oder Abstürze auszuführen.
  • Ausrichtung auf alle verfügbaren Beschleuniger: Tests werden für alle verfügbaren Treiber ausgeführt.

Alle Crashtests haben die folgenden vier möglichen Ergebnisse:

  • SUCCESS : Die Ausführung wurde ohne Fehler abgeschlossen.
  • FAILURE : Die Ausführung ist fehlgeschlagen. Wird normalerweise 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 reagierte nicht mehr.
  • CRASH : Der Testprozess ist abgestürzt.

Weitere Informationen zu Stresstests und eine vollständige Liste der Crashtests finden Sie platform/test/mlts/benchmark/README.txt .

Verwenden Sie MLTS

So verwenden Sie das MLTS:

  1. Verbinden Sie ein Zielgerät mit Ihrer Workstation und stellen Sie sicher, dass es über adb erreichbar ist. Exportieren Sie die Umgebungsvariable ANDROID_SERIAL des Zielgeräts, wenn mehr als ein Gerät angeschlossen 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 eines Benchmark-Laufs werden die Ergebnisse als HTML-Seite präsentiert und an xdg-open übergeben.

Weitere Informationen finden Sie unter platform/test/mlts/benchmark/README.txt .

HAL-Versionen für neuronale Netze

In diesem Abschnitt werden die Änderungen beschrieben, die in den HAL-Versionen für Android und Neural Networks eingeführt wurden.

Android 11

Mit Android 11 wird NN HAL 1.3 eingeführt, das die folgenden bemerkenswerten Änderungen enthält.

  • Unterstützung für vorzeichenbehaftete 8-Bit-Quantisierung in NNAPI. Fügt den Operandentyp TENSOR_QUANT8_ASYMM_SIGNED hinzu. Treiber mit NN HAL 1.3, die Vorgänge mit vorzeichenloser Quantisierung unterstützen, müssen auch die vorzeichenbehafteten Varianten dieser Vorgänge unterstützen. Bei der Ausführung vorzeichenbehafteter und vorzeichenloser Versionen der meisten quantisierten Operationen müssen Treiber bis zu einem Offset von 128 die gleichen Ergebnisse liefern. Es gibt fünf Ausnahmen von dieser Anforderung: CAST , HASHTABLE_LOOKUP , LSH_PROJECTION , PAD_V2 und QUANTIZED_16BIT_LSTM . Die Operation QUANTIZED_16BIT_LSTM unterstützt keine vorzeichenbehafteten Operanden und die anderen vier Operationen unterstützen die vorzeichenbehaftete Quantisierung, erfordern jedoch nicht, dass die Ergebnisse gleich sind.
  • Unterstützung für eingezäunte Ausführungen, bei denen das Framework die Methode IPreparedModel::executeFenced aufruft, um eine eingezäunte, asynchrone Ausführung auf einem vorbereiteten Modell mit einem Vektor von Synchronisierungszäunen zu starten, auf die gewartet werden muss. Weitere Informationen finden Sie unter Eingezäunte Ausführung .
  • Unterstützung für Kontrollfluss. Fügt die IF und WHILE -Operationen hinzu, die andere Modelle als Argumente verwenden und diese bedingt ( IF ) oder wiederholt ( WHILE ) ausführen. Weitere Informationen finden Sie unter Kontrollfluss .
  • Verbesserte Dienstqualität (Quality of Service, QoS), da Apps die relativen Prioritäten ihrer Modelle, die maximale erwartete Zeit für die Vorbereitung eines Modells und die maximale erwartete Zeit für den Abschluss einer Ausführung anzeigen können. Weitere Informationen finden Sie unter Servicequalität .
  • Unterstützung für Speicherdomänen, die Zuweisungsschnittstellen für vom Treiber verwaltete Puffer bereitstellen. Dies ermöglicht die Weitergabe geräteeigener Speicher über Ausführungen hinweg und unterdrückt unnötiges Kopieren und Transformieren von Daten zwischen aufeinanderfolgenden Ausführungen auf demselben Treiber. Weitere Informationen finden Sie unter Speicherdomänen .

Android 10

Mit Android 10 wird NN HAL 1.2 eingeführt, das die folgenden bemerkenswerten Änderungen enthält.

  • Die Capabilities Struktur umfasst alle Datentypen, einschließlich skalarer Datentypen, und stellt eine nicht entspannte Leistung mithilfe eines Vektors anstelle benannter Felder dar.
  • Mit den Methoden getVersionString und getType kann das Framework Gerätetyp- ( DeviceType ) und Versionsinformationen abrufen. Siehe Geräteerkennung und -zuweisung .
  • Die Methode executeSynchronously wird standardmäßig aufgerufen, um eine Ausführung synchron durchzuführen. Die execute_1_2 weist das Framework an, eine Ausführung asynchron durchzuführen. Siehe Ausführung .
  • Der Parameter MeasureTiming für executeSynchronously , execute_1_2 “ und „Burst-Ausführung“ gibt an, ob der Treiber die Ausführungsdauer messen soll. Die Ergebnisse werden in der Timing Struktur gemeldet. Siehe Timing .
  • Unterstützung für Ausführungen, bei denen einer oder mehrere Ausgabeoperanden eine unbekannte Dimension oder einen unbekannten Rang haben. Siehe Ausgabeform .
  • Unterstützung für Anbietererweiterungen, bei denen es sich um Sammlungen anbieterdefinierter Vorgänge und Datentypen handelt. Der Treiber meldet unterstützte Erweiterungen über die IDevice::getSupportedExtensions Methode. Siehe Anbietererweiterungen .
  • Möglichkeit für ein Burst-Objekt, eine Reihe von Burst-Ausführungen mithilfe von Fast Message Queues (FMQs) zu steuern, um zwischen App- und Treiberprozessen zu kommunizieren und so die Latenz zu reduzieren. Siehe Burst-Ausführungen und Fast Message Queues .
  • Unterstützung für AHardwareBuffer, damit der Treiber Ausführungen durchführen kann, ohne Daten zu kopieren. Siehe AHardwareBuffer .
  • Verbesserte Unterstützung für das Caching von Kompilierungsartefakten, um die für die Kompilierung beim Start einer App benötigte Zeit zu reduzieren. Siehe Kompilierungs-Caching .

Android 10 führt die folgenden Operandentypen und Operationen ein.

  • Operandentypen

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

    • 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

Android 10 führt Updates für viele der bestehenden Vorgänge ein. Die Aktualisierungen beziehen sich hauptsächlich auf Folgendes:

  • Unterstützung für das NCHW-Speicherlayout
  • Unterstützung für Tensoren mit einem anderen Rang als 4 in Softmax- und Normalisierungsoperationen
  • Unterstützung für erweiterte Windungen
  • Unterstützung für Eingaben mit gemischter Quantisierung in ANEURALNETWORKS_CONCATENATION

Die folgende Liste zeigt die Vorgänge, die in Android 10 geändert wurden. Ausführliche Informationen zu den Änderungen finden Sie unter OperationCode in der NNAPI-Referenzdokumentation.

  • 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 wird in Android 9 eingeführt und enthält die folgenden bemerkenswerten Änderungen.

  • IDevice::prepareModel_1_1 enthält einen ExecutionPreference Parameter. Ein Fahrer kann damit seine Vorbereitung anpassen, da er weiß, dass die App lieber Batterie spart oder das Modell in schnell aufeinanderfolgenden Aufrufen ausführt.
  • Neun neue Operationen wurden hinzugefügt: BATCH_TO_SPACE_ND , DIV , MEAN , PAD , SPACE_TO_BATCH_ND , SQUEEZE , STRIDED_SLICE , SUB , TRANSPOSE .
  • Eine App kann angeben, dass 32-Bit-Float-Berechnungen mit einem 16-Bit-Float-Bereich und/oder einer Genauigkeit ausgeführt werden können, indem Model.relaxComputationFloat32toFloat16 auf true gesetzt wird. Die Capabilities -Struktur verfügt über das zusätzliche Feld relaxedFloat32toFloat16Performance “, damit der Treiber seine entspannte Leistung an das Framework melden kann.

Android 8.1

Das erste Neural Networks HAL (1.0) wurde in Android 8.1 veröffentlicht. Weitere Informationen finden Sie unter /neuralnetworks/1.0/ .