Pilotes de l'API Neural Networks

Cette page explique comment implémenter un pilote de l'API Neural Networks (NNAPI). Pour en savoir plus, consultez la documentation disponible dans les fichiers de définition HAL de hardware/interfaces/neuralnetworks. Un exemple d'implémentation de pilote se trouve dans frameworks/ml/nn/driver/sample.

Pour en savoir plus sur l'API Neural Networks, consultez la page API Neural Networks.

HAL des réseaux de neurones

Le HAL des réseaux de neurones (NN) définit une abstraction des différents appareils, tels que les processeurs graphiques (GPU) et les processeurs de signal numérique (DSP), qui sont inclus dans un produit (par exemple, un téléphone ou une tablette). Les pilotes de ces appareils doivent être conformes au NN HAL. L'interface est spécifiée dans les fichiers de définition HAL du fichier hardware/interfaces/neuralnetworks.

Le flux général de l'interface entre le framework et un pilote est illustré dans la figure 1.

Flux de réseaux de neurones

Figure 1 : Flux de réseaux de neurones

Initialisation

Lors de l'initialisation, le framework interroge le pilote pour connaître ses fonctionnalités à l'aide de IDevice::getCapabilities_1_3. La structure @1.3::Capabilities inclut tous les types de données et représente les performances non relaxantes à l'aide d'un vecteur.

Pour déterminer comment allouer des calculs aux appareils disponibles, le framework utilise les capacités permettant de comprendre à quelle vitesse et avec quelle efficacité énergétique chaque pilote peut effectuer une exécution. Pour fournir ces informations, le pilote doit fournir des valeurs de performances standardisées basées sur l'exécution des charges de travail de référence.

Pour déterminer les valeurs renvoyées par le pilote en réponse à IDevice::getCapabilities_1_3, utilisez l'application de benchmark NNAPI afin de mesurer les performances des types de données correspondants. Les modèles MobileNet v1 et v2, asr_float et tts_float sont recommandés pour mesurer les performances des valeurs à virgule flottante 32 bits, tandis que les modèles quantifiés MobileNet v1 et v2 sont recommandés pour les valeurs quantifiées 8 bits. Pour en savoir plus, consultez la page Suite de test du machine learning Android.

Dans Android 9 et versions antérieures, la structure Capabilities n'inclut des informations sur les performances du pilote que pour les Tensors quantifiés et à virgule flottante, et n'inclut pas les types de données scalaires.

Dans le cadre du processus d'initialisation, le framework peut interroger davantage d'informations à l'aide de IDevice::getType, IDevice::getVersionString, IDevice:getSupportedExtensions et IDevice::getNumberOfCacheFilesNeeded.

Entre les redémarrages du produit, le framework s'attend à ce que toutes les requêtes décrites dans cette section signalent toujours les mêmes valeurs pour un pilote donné. Sinon, une application utilisant ce pilote risque de présenter des performances réduites ou un comportement incorrect.

Compilation

Le framework détermine les appareils à utiliser lorsqu'il reçoit une requête d'une application. Dans Android 10, les applications peuvent détecter et spécifier les appareils parmi lesquels le framework choisit. Pour en savoir plus, consultez la section Découverte et attribution des appareils.

Au moment de la compilation du modèle, le framework envoie le modèle à chaque pilote candidat en appelant IDevice::getSupportedOperations_1_3. Chaque pilote renvoie un tableau de valeurs booléennes indiquant les opérations du modèle compatibles. Un pilote peut déterminer qu'il ne peut pas prendre en charge une opération donnée pour plusieurs raisons. Par exemple :

  • Le pilote n'est pas compatible avec ce type de données.
  • Le pilote n'accepte que les opérations avec des paramètres d'entrée spécifiques. Par exemple, un pilote peut prendre en charge les opérations de convolution 3x3 et 5x5, mais pas les opérations de convolution 7x7.
  • Le pilote présente des contraintes de mémoire qui l'empêchent de gérer des entrées ou des graphiques volumineux.

Lors de la compilation, les opérandes d'entrée, de sortie et internes du modèle, comme décrit dans OperandLifeTime, peuvent avoir des dimensions ou un rang inconnus. Pour en savoir plus, consultez la section Forme de sortie.

Le framework demande à chaque pilote sélectionné de se préparer à exécuter un sous-ensemble du modèle en appelant IDevice::prepareModel_1_3. Chaque pilote compile ensuite son sous-ensemble. Par exemple, un pilote peut générer du code ou créer une copie réorganisée des pondérations. Étant donné qu'un délai important peut s'écouler entre la compilation du modèle et l'exécution des requêtes, des ressources telles que de grands segments de mémoire de l'appareil ne doivent pas être attribuées lors de la compilation.

En cas de réussite, le pilote renvoie un handle @1.3::IPreparedModel. Si le pilote renvoie un code d'échec lors de la préparation de son sous-ensemble du modèle, le framework exécute l'intégralité du modèle sur le processeur.

Pour réduire le temps de compilation au démarrage d'une application, un pilote peut mettre en cache les artefacts de compilation. Pour en savoir plus, consultez la section Mise en cache de la compilation.

Exécution

Lorsqu'une application demande au framework d'exécuter une requête, celui-ci appelle par défaut la méthode HAL IPreparedModel::executeSynchronously_1_3 pour effectuer une exécution synchrone sur un modèle préparé. Une requête peut également être exécutée de manière asynchrone à l'aide des méthodes execute_1_3 ou executeFenced (voir Exécution clôturée), ou bien être exécutée à l'aide d'une exécution intensive.

Les appels d'exécution synchrones améliorent les performances et réduisent les frais généraux liés aux threads par rapport aux appels asynchrones, car le contrôle n'est renvoyé au processus d'application qu'une fois l'exécution terminée. Cela signifie que le pilote n'a pas besoin d'un mécanisme distinct pour avertir le processus de l'application qu'une exécution est terminée.

Avec la méthode execute_1_3 asynchrone, le contrôle revient au processus de l'application une fois l'exécution démarrée et le pilote doit avertir le framework lorsque l'exécution est terminée, à l'aide de @1.3::IExecutionCallback.

Le paramètre Request transmis à la méthode d'exécution répertorie les opérandes d'entrée et de sortie utilisés pour l'exécution. La mémoire qui stocke les données d'opérande doit utiliser l'ordre de priorité des lignes, la première dimension itérative la plus lente et ne comporter aucun remplissage à la fin de chaque ligne. Pour en savoir plus sur les types d'opérandes, consultez la page Opérandes.

Pour les pilotes NN HAL version 1.2 ou ultérieure, lorsqu'une requête est terminée, l'état d'erreur, la forme de sortie et les informations de synchronisation sont renvoyés au framework. Lors de l'exécution, les opérandes de sortie ou internes du modèle peuvent avoir une ou plusieurs dimensions ou rangs inconnus. Lorsqu'au moins un opérande de sortie a une dimension ou un rang inconnu, le pilote doit renvoyer des informations de sortie de taille dynamique.

Pour les pilotes disposant de NN HAL 1.1 ou version antérieure, seul l'état d'erreur est renvoyé lorsqu'une requête est terminée. Pour que l'exécution aboutisse, les dimensions des opérandes d'entrée et de sortie doivent être entièrement spécifiées. Les opérandes internes peuvent avoir une ou plusieurs dimensions inconnues, mais ils doivent avoir un rang spécifique.

Pour les requêtes utilisateur couvrant plusieurs pilotes, le framework est chargé de réserver la mémoire intermédiaire et de séquencer les appels à chaque pilote.

Plusieurs requêtes peuvent être lancées en parallèle sur le même objet @1.3::IPreparedModel. Le pilote peut exécuter des requêtes en parallèle ou sérialiser les exécutions.

Le framework peut demander à un conducteur de conserver plusieurs modèles préparés. Par exemple, préparez le modèle m1, préparez m2, exécutez la requête r1 sur m1, exécutez r2 sur m2, exécutez r3 sur m1, exécutez r4 sur m2, libérez m1 (décrit dans Nettoyage) et libérez m2.

Pour éviter une première exécution lente qui pourrait nuire à l'expérience utilisateur (par exemple, un premier frame saccadé), le pilote doit effectuer la plupart des initialisations lors de la phase de compilation. L'initialisation lors de la première exécution doit être limitée aux actions qui affectent négativement l'état du système lorsqu'elles sont effectuées tôt, telles que la réservation de tampons temporaires volumineux ou l'augmentation de la fréquence d'horloge d'un appareil. Les pilotes qui ne peuvent préparer qu'un nombre limité de modèles simultanés peuvent être amenés à s'initialiser lors de la première exécution.

Sous Android 10 ou version ultérieure, dans les cas où plusieurs exécutions avec le même modèle préparé sont exécutées à la suite rapide, le client peut choisir d'utiliser un objet d'utilisation intensive pour communiquer entre les processus de l'application et du pilote. Pour plus d'informations, consultez la section Exécutions intensives et files d'attente de messages rapides.

Pour améliorer les performances de plusieurs exécutions successives rapidement, le pilote peut conserver des tampons temporaires ou augmenter les fréquences d'horloge. La création d'un thread watchdog est recommandée pour libérer des ressources si aucune nouvelle requête n'est créée après une période donnée.

Forme de sortie

Pour les requêtes dans lesquelles toutes les dimensions d'un ou de plusieurs opérandes de sortie ne sont pas spécifiées, le pilote doit fournir une liste de formes de sortie contenant les informations de dimension pour chaque opérande de sortie après l'exécution. Pour en savoir plus sur les dimensions, consultez OutputShape.

Si une exécution échoue en raison d'un tampon de sortie trop petit, le pilote doit indiquer les opérandes de sortie dont la taille de la mémoire tampon est insuffisante dans la liste des formes de sortie et signaler autant d'informations de dimension que possible, en utilisant zéro pour les dimensions inconnues.

Durée

Dans Android 10, une application peut demander l'heure d'exécution si elle a spécifié un seul appareil à utiliser lors du processus de compilation. Pour en savoir plus, consultez MeasureTiming et Découverte et attribution d'appareils. Dans ce cas, un pilote de réseau de neurones HAL 1.2 doit mesurer la durée d'exécution ou signaler UINT64_MAX (pour indiquer que la durée n'est pas disponible) lors de l'exécution d'une requête. Le pilote doit minimiser toute perte de performances résultant de la mesure de la durée d'exécution.

Le pilote signale les durées suivantes en microsecondes dans la structure Timing:

  • Temps d'exécution sur l'appareil:n'inclut pas le temps d'exécution dans le pilote, qui s'exécute sur le processeur hôte.
  • Temps d'exécution dans le pilote:inclut le temps d'exécution sur l'appareil.

Ces durées doivent inclure le moment où l'exécution est suspendue, par exemple lorsqu'elle a été préemptée par d'autres tâches ou lorsqu'elle attend qu'une ressource soit disponible.

Lorsque le pilote n'a pas été invité à mesurer la durée d'exécution ou en cas d'erreur d'exécution, il doit signaler les durées au format UINT64_MAX. Même lorsque le pilote a été invité à mesurer la durée d'exécution, il peut indiquer à la place UINT64_MAX pour le temps passé sur l'appareil, le temps passé sur le pilote ou les deux. Lorsque le pilote signale les deux durées en tant que valeur autre que UINT64_MAX, la durée d'exécution dans le pilote doit être égale ou supérieure à la durée sur l'appareil.

Exécution clôturée

Dans Android 11, NNAPI permet aux exécutions d'attendre une liste de handle sync_fence et de renvoyer éventuellement un objet sync_fence, qui est signalé à la fin de l'exécution. Cela réduit les frais généraux pour les petits modèles de séquence et les cas d'utilisation de flux de données. L'exécution délimitée permet également une interopérabilité plus efficace avec d'autres composants pouvant signaler ou attendre sync_fence. Pour en savoir plus sur sync_fence, consultez la page Framework de synchronisation.

Dans une exécution clôturée, le framework appelle la méthode IPreparedModel::executeFenced pour lancer une exécution asynchrone cloisonnée sur un modèle préparé avec un vecteur de barrières de synchronisation à attendre. Si la tâche asynchrone est terminée avant le retour de l'appel, un handle vide peut être renvoyé pour sync_fence. Un objet IFencedExecutionCallback doit également être renvoyé pour permettre au framework d'interroger les informations sur l'état et la durée des erreurs.

Une fois l'exécution terminée, les deux valeurs de durée suivantes, qui mesurent la durée de l'exécution, peuvent être interrogées via IFencedExecutionCallback::getExecutionInfo.

  • timingLaunched : durée entre le moment où executeFenced est appelé et le moment où executeFenced signale le syncFence renvoyé.
  • timingFenced : durée à partir du moment où toutes les barrières de synchronisation attendues par l'exécution sont signalées lorsque executeFenced signale le syncFence renvoyé.

Flux de contrôle

Pour les appareils équipés d'Android 11 ou version ultérieure, NNAPI inclut deux opérations de flux de contrôle, IF et WHILE, qui acceptent d'autres modèles comme arguments et les exécutent de manière conditionnelle (IF) ou de manière répétée (WHILE). Pour en savoir plus sur la mise en œuvre, consultez la section Flux de contrôle.

Qualité de service

Dans Android 11, la NNAPI inclut une meilleure qualité de service (QoS) en permettant à une application d'indiquer les priorités relatives de ses modèles, le temps maximal attendu pour la préparation d'un modèle et le temps maximal attendu pour l'exécution d'une exécution. Pour en savoir plus, consultez la page Qualité de service.

Effectuer un nettoyage

Lorsqu'une application a fini d'utiliser un modèle préparé, le framework publie sa référence à l'objet @1.3::IPreparedModel. Lorsque l'objet IPreparedModel n'est plus référencé, il est automatiquement détruit dans le service de pilote qui l'a créé. À ce stade, les ressources spécifiques au modèle peuvent être récupérées dans l'implémentation du pilote du destructeur. Si le service de pilote souhaite que l'objet IPreparedModel soit automatiquement détruit lorsqu'il n'est plus nécessaire par le client, il ne doit contenir aucune référence à l'objet IPreparedModel une fois que l'objet IPreparedeModel a été renvoyé via IPreparedModelCallback::notify_1_3.

Utilisation du processeur

Les pilotes doivent utiliser le processeur pour configurer les calculs. Les pilotes ne doivent pas utiliser le processeur pour effectuer des calculs graphiques, car cela interfère avec la capacité du framework à allouer correctement les tâches. Le pilote doit signaler au framework les parties qu'il ne peut pas gérer et laisser celui-ci gérer le reste.

Le framework fournit une mise en œuvre de processeur pour toutes les opérations NNAPI, à l'exception des opérations définies par le fournisseur. Pour en savoir plus, consultez la page Extensions de fournisseur.

Les opérations introduites dans Android 10 (niveau d'API 29) ne disposent que d'une implémentation de processeur de référence pour vérifier que les tests CTS et VTS sont corrects. Les implémentations optimisées incluses dans les frameworks de machine learning mobiles sont préférables à l'implémentation de processeur NNAPI.

Fonctions utilitaires

Le codebase NNAPI inclut des fonctions utilitaires pouvant être utilisées par les services de pilote.

Le fichier frameworks/ml/nn/common/include/Utils.h contient diverses fonctions utilitaires, telles que celles utilisées pour la journalisation et la conversion entre différentes versions de l'HAL du réseau de neurones.

  • VLogging: VLOG est une macro de wrapper autour de l'élément LOG d'Android qui ne consigne le message que si la balise appropriée est définie dans la propriété debug.nn.vlog. initVLogMask() doit être appelé avant tout appel à VLOG. La macro VLOG_IS_ON peut être utilisée pour vérifier si VLOG est actuellement activé, ce qui permet d'ignorer un code de journalisation compliqué s'il n'est pas nécessaire. La valeur de la propriété doit correspondre à l'une des valeurs suivantes:

    • Chaîne vide indiquant qu'aucune journalisation ne doit être effectuée.
    • Le jeton 1 ou all, indiquant que toute la journalisation doit être effectuée
    • Liste de balises, délimitées par des espaces, des virgules ou des deux-points, indiquant la journalisation à effectuer. Les tags sont compilation, cpuexe, driver, execution, manager et model.
  • compliantWithV1_*: renvoie true si un objet NN HAL peut être converti dans le même type d'une autre version de HAL sans perdre d'informations. Par exemple, l'appel de compliantWithV1_0 sur un V1_2::Model renvoie false si le modèle inclut des types d'opérations introduits dans NN HAL 1.1 ou NN HAL 1.2.

  • convertToV1_*: convertit un objet HAL NN d'une version à une autre. Un avertissement est consigné si la conversion entraîne une perte d'informations (c'est-à-dire, si la nouvelle version du type ne peut pas représenter entièrement la valeur).

  • Fonctionnalités: les fonctions nonExtensionOperandPerformance et update permettent de créer le champ Capabilities::operandPerformance.

  • Propriétés d'interrogation de types: isExtensionOperandType, isExtensionOperationType, nonExtensionSizeOfData, nonExtensionOperandSizeOfData, nonExtensionOperandTypeIsScalar, tensorHasUnspecifiedDimensions.

Le fichier frameworks/ml/nn/common/include/ValidateHal.h contient des fonctions utilitaires permettant de vérifier qu'un objet HAL NN est valide conformément à la spécification de sa version HAL.

  • validate*: renvoie true si l'objet NN HAL est valide conformément à la spécification de sa version HAL. Les types OEM et d'extension ne sont pas validés. Par exemple, validateModel renvoie false si le modèle contient une opération faisant référence à un index d'opérande inexistant ou une opération incompatible avec cette version de HAL.

Le fichier frameworks/ml/nn/common/include/Tracing.h contient des macros pour simplifier l'ajout d'informations de traçage système au code des réseaux de neurones. Pour obtenir un exemple, consultez les appels de macro NNTRACE_* dans l'exemple de pilote.

Le fichier frameworks/ml/nn/common/include/GraphDump.h contient une fonction utilitaire permettant de vider le contenu d'un Model sous forme graphique à des fins de débogage.

  • graphDump: écrit une représentation du modèle au format Graphviz (.dot) dans le flux spécifié (le cas échéant) ou dans le fichier logcat (si aucun flux n'est fourni).

Validation

Pour tester votre implémentation de NNAPI, utilisez les tests VTS et CTS inclus dans le framework Android. Le VTS utilise vos pilotes directement (sans utiliser le framework), tandis que CTS les exerce indirectement via le framework. Ils testent chaque méthode d'API et vérifient que toutes les opérations acceptées par les pilotes fonctionnent correctement et fournissent des résultats qui répondent aux exigences de précision.

Dans CTS et VTS pour la NNAPI, les exigences de précision sont les suivantes:

  • À virgule flottante:abs(expected - actual) <= atol + rtol * abs(expected); où:

    • Pour fp32, atol = 1e-5f, rtol = 5.0f * 1.1920928955078125e-7
    • Pour fp16, atol = rtol = 5.0f * 0,0009765625f
  • Quantifié:arrêt de un (à l'exception de mobilenet_quantized, qui est écarté par trois)

  • Booléen:mot clé exact

L'une des méthodes utilisées par CTS pour tester NNAPI consiste à générer des graphiques pseudo-aléatoires fixes, qui permettent de tester et de comparer les résultats d'exécution de chaque pilote avec l'implémentation de référence NNAPI. Pour les pilotes disposant de NN HAL version 1.2 ou ultérieure, si les résultats ne répondent pas aux critères de précision, CTS signale une erreur et transmet un fichier de spécification du modèle défaillant sous /data/local/tmp pour le débogage. Pour en savoir plus sur les critères de précision, consultez TestRandomGraph.cpp et TestHarness.h.

Tests de simulation

L'objectif des tests à données aléatoires est de détecter les plantages, les assertions, les violations de mémoire ou les comportements généraux non définis dans le code testé en raison de facteurs tels que des entrées inattendues. Pour les tests à données aléatoires NNAPI, Android utilise des tests basés sur libFuzzer, qui sont efficaces en termes de fuzzing, car ils utilisent la couverture de ligne des scénarios de test précédents pour générer de nouvelles entrées aléatoires. Par exemple, libFuzzer privilégie les scénarios de test qui s'exécutent sur de nouvelles lignes de code. Cela réduit considérablement le temps nécessaire aux tests pour identifier le code problématique.

Pour effectuer des tests à données aléatoires afin de valider l'implémentation de vos pilotes, modifiez frameworks/ml/nn/runtime/test/android_fuzzing/DriverFuzzTest.cpp dans l'utilitaire de test libneuralnetworks_driver_fuzzer disponible dans AOSP afin d'inclure votre code de pilote. Pour en savoir plus sur les tests à données aléatoires NNAPI, consultez frameworks/ml/nn/runtime/test/android_fuzzing/README.md.

Sécurité

Étant donné que les processus d'application communiquent directement avec les processus du pilote, les conducteurs doivent valider les arguments des appels qu'ils reçoivent. Cette validation est vérifiée par VTS. Le code de validation se trouve dans frameworks/ml/nn/common/include/ValidateHal.h.

Les pilotes doivent également s'assurer que les applications ne peuvent pas interférer avec d'autres applications lorsqu'elles utilisent le même appareil.

Suite de test de machine learning Android

La suite de tests de machine learning Android (MLTS) est un benchmark NNAPI inclus dans CTS et VTS pour valider la précision de modèles réels sur les appareils des fournisseurs. L'analyse comparative évalue la latence et la justesse, puis compare les résultats des pilotes aux résultats obtenus à l'aide de TF Lite exécuté sur le processeur, pour le même modèle et les mêmes ensembles de données. Cela garantit que la précision d'un pilote n'est pas inférieure à celle de la mise en œuvre de référence du processeur.

Les développeurs de la plate-forme Android utilisent également MLTS pour évaluer la latence et la précision des pilotes.

Le benchmark NNAPI se trouve dans deux projets dans AOSP:

Modèles et ensembles de données

Le benchmark NNAPI utilise les modèles et ensembles de données suivants.

  • Les valeurs flottantes et u8 de MobileNetV1, quantifiées dans différentes tailles, sont exécutées sur un petit sous-ensemble (1 500 images) de l'ensemble de données Open Images v4.
  • Les valeurs flottantes et u8 de MobileNetV2, quantifiées dans différentes tailles, sont exécutées sur un petit sous-ensemble (1 500 images) de l'ensemble de données Open Images v4.
  • Modèle acoustique basé sur la mémoire à long terme (LSTM) pour la synthèse vocale, exécuté sur un petit sous-ensemble de l'ensemble CMU Arctic.
  • Modèle acoustique basé sur LSTM pour la reconnaissance vocale automatique, exécuté sur un petit sous-ensemble de l'ensemble de données LibriSpeech.

Pour en savoir plus, consultez platform/test/mlts/models.

Test de contrainte

La suite de tests de machine learning Android comprend une série de tests de plantage pour valider la résilience des pilotes dans des conditions d'utilisation intensives ou dans les cas particuliers du comportement des clients.

Tous les tests de plantage offrent les fonctionnalités suivantes:

  • Détection de blocage:si le client NNAPI se bloque lors d'un test, celui-ci échoue pour le motif HANG et la suite de tests passe au test suivant.
  • Détection des plantages du client NNAPI:les tests survivent aux plantages du client et les tests échouent avec le motif d'échec CRASH.
  • Détection des accidents de la route:les tests peuvent détecter un accident de conducteur qui entraîne l'échec d'un appel NNAPI. Notez que des plantages peuvent se produire dans les processus du pilote n'entraînant pas de défaillance NNAPI ni l'échec du test. Pour couvrir ce type de défaillance, il est recommandé d'exécuter la commande tail dans le journal système pour détecter les erreurs ou les plantages liés au pilote.
  • Ciblage de tous les accélérateurs disponibles:les tests sont exécutés sur tous les pilotes disponibles.

Tous les tests de plantage peuvent donner les quatre résultats suivants:

  • SUCCESS: exécution terminée sans erreur.
  • FAILURE: échec de l'exécution. Généralement causée par un échec lors du test d'un modèle, indiquant que le pilote n'a pas pu compiler ou exécuter le modèle.
  • HANG: le processus de test ne répond plus.
  • CRASH: le processus de test a planté.

Pour en savoir plus sur les tests de contrainte et obtenir la liste complète des tests de plantage, consultez platform/test/mlts/benchmark/README.txt.

Utiliser MLTS

Pour utiliser le MLTS:

  1. Connectez un appareil cible à votre station de travail et assurez-vous qu'il est accessible via adb. Exportez la variable d'environnement ANDROID_SERIAL de l'appareil cible si plusieurs appareils sont connectés.
  2. cd dans le répertoire source Android de premier niveau.

    source build/envsetup.sh
    lunch aosp_arm-userdebug # Or aosp_arm64-userdebug if available.
    ./test/mlts/benchmark/build_and_run_benchmark.sh
    

    À la fin de l'exécution d'une analyse comparative, les résultats sont présentés sous forme de page HTML et transmis à xdg-open.

Pour en savoir plus, consultez platform/test/mlts/benchmark/README.txt.

Versions HAL des réseaux de neurones

Cette section décrit les modifications apportées aux versions HAL d'Android et de neurones pour les réseaux de neurones.

Android 11

Android 11 introduit NN HAL 1.3, qui inclut les modifications notables suivantes.

  • Prise en charge de la quantification signée 8 bits dans NNAPI. Ajoute le type d'opérande TENSOR_QUANT8_ASYMM_SIGNED. Les pilotes avec NN HAL 1.3 qui acceptent les opérations avec quantification non signée doivent également être compatibles avec les variantes signées de ces opérations. Lors de l'exécution de versions signées et non signées des opérations les plus quantifiées, les pilotes doivent produire les mêmes résultats jusqu'à un décalage de 128. Il existe cinq exceptions à cette exigence: CAST, HASHTABLE_LOOKUP, LSH_PROJECTION, PAD_V2 et QUANTIZED_16BIT_LSTM. L'opération QUANTIZED_16BIT_LSTM n'est pas compatible avec les opérandes signés et les quatre autres opérations sont compatibles avec la quantification signée, mais ne nécessitent pas que les résultats soient identiques.
  • Prise en charge des exécutions cloisonnées où le framework appelle la méthode IPreparedModel::executeFenced pour lancer une exécution asynchrone cloisonnée sur un modèle préparé avec un vecteur de barrières de synchronisation à attendre. Pour en savoir plus, consultez la section Exécution clôturée.
  • Prise en charge du flux de contrôle. Ajoute les opérations IF et WHILE, qui acceptent d'autres modèles comme arguments et les exécutent de manière conditionnelle (IF) ou de manière répétée (WHILE). Pour en savoir plus, consultez la section Flux de contrôle.
  • Amélioration de la qualité de service (QoS) dans la mesure où les applications peuvent indiquer les priorités relatives de ses modèles, le temps maximal attendu pour la préparation d'un modèle et le temps maximal attendu pour l'exécution d'une exécution. Pour en savoir plus, consultez la page Qualité de service.
  • Prise en charge des domaines de mémoire qui fournissent des interfaces d'allocation pour les tampons gérés par le pilote. Cela permet de transmettre les mémoires natives de l'appareil entre les exécutions, ce qui évite la copie et la transformation de données inutiles entre les exécutions consécutives d'un même pilote. Pour en savoir plus, consultez la section Domaines de mémoire.

Android 10

Android 10 introduit NN HAL 1.2, qui inclut les modifications notables suivantes.

  • La structure Capabilities inclut tous les types de données, y compris les types de données scalaires, et représente des performances non assouplies à l'aide d'un vecteur plutôt que de champs nommés.
  • Les méthodes getVersionString et getType permettent au framework de récupérer les informations sur le type d'appareil (DeviceType) et la version. Consultez la section Détection et attribution des appareils.
  • La méthode executeSynchronously est appelée par défaut pour effectuer une exécution de manière synchrone. La méthode execute_1_2 indique au framework d'effectuer une exécution de manière asynchrone. Voir Exécution.
  • Le paramètre MeasureTiming sur executeSynchronously, execute_1_2 et l'exécution intensive spécifie si le pilote doit mesurer la durée de l'exécution. Les résultats sont indiqués dans la structure Timing. Consultez la section Calendrier.
  • Prise en charge des exécutions dans lesquelles un ou plusieurs opérandes de sortie ont une dimension ou un rang inconnu. Consultez Forme de sortie.
  • Compatibilité avec les extensions de fournisseur, qui sont des ensembles d'opérations et de types de données définis par le fournisseur. Le pilote signale les extensions compatibles via la méthode IDevice::getSupportedExtensions. Consultez la page Extensions de fournisseur.
  • Possibilité pour un objet d'utilisation intensive de contrôler un ensemble d'exécutions intensives à l'aide de files d'attente de messages rapides (FMQ) pour communiquer entre les processus d'application et de pilote, ce qui réduit la latence. Consultez la section Exécutions intensives et files d'attente de messages rapides.
  • Prise en charge d'AHardwareBuffer pour permettre au pilote d'effectuer des exécutions sans copier de données. Consultez la section AHardwareBuffer.
  • Amélioration de la prise en charge de la mise en cache des artefacts de compilation afin de réduire le temps consacré à la compilation au démarrage d'une application. Consultez la section Mise en cache des compilations.

Android 10 introduit les types d'opérandes et les opérations suivants.

  • Types d'opérandes

    • 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
  • Opérations

    • 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 introduit des mises à jour de nombreuses opérations existantes. Les mises à jour sont principalement liées aux éléments suivants:

  • Compatibilité avec la disposition de mémoire NCHW
  • Prise en charge des Tensors dont le rang est différent de 4 dans les opérations softmax et de normalisation
  • Prise en charge des convolutions dilatées
  • Compatibilité des entrées avec une quantification mixte dans ANEURALNETWORKS_CONCATENATION

La liste ci-dessous présente les opérations modifiées dans Android 10. Pour en savoir plus sur les modifications, consultez la section OperationCode de la documentation de référence de NNAPI.

  • 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 est introduit dans Android 9 et inclut les modifications notables suivantes.

  • IDevice::prepareModel_1_1 inclut un paramètre ExecutionPreference. Un pilote peut l'utiliser pour ajuster sa préparation, sachant que l'application préfère économiser la batterie ou exécutera le modèle dans des appels successifs rapides.
  • Neuf opérations ont été ajoutées: BATCH_TO_SPACE_ND, DIV, MEAN, PAD, SPACE_TO_BATCH_ND, SQUEEZE, STRIDED_SLICE, SUB et TRANSPOSE.
  • Une application peut spécifier que des calculs de float 32 bits peuvent être exécutés en utilisant une plage de valeurs à virgule flottante et/ou une précision de 16 bits en définissant Model.relaxComputationFloat32toFloat16 sur true. La structure Capabilities contient le champ supplémentaire relaxedFloat32toFloat16Performance afin que le pilote puisse signaler ses performances assouplies au framework.

Android 8.1

La version 1.0 du HAL des réseaux de neurones a été publiée dans Android 8.1. Pour en savoir plus, consultez /neuralnetworks/1.0/.