Délégué NNAPI TensorFlow Lite

L' API Android Neural Networks (NNAPI) est disponible sur tous les appareils Android exécutant Android 8.1 (API niveau 27) ou supérieur. Il fournit une accélération pour les modèles TensorFlow Lite sur les appareils Android avec des accélérateurs matériels pris en charge, notamment :

  • Unité de traitement graphique (GPU)
  • Processeur de signal numérique (DSP)
  • Unité de traitement neuronal (NPU)

Les performances varient en fonction du matériel spécifique disponible sur l'appareil.

Cette page décrit comment utiliser le délégué NNAPI avec l'interpréteur TensorFlow Lite en Java et Kotlin. Pour les API Android C, veuillez vous référer à la documentation du kit de développement natif Android .

Essayer le délégué NNAPI sur votre propre modèle

Importation progressive

Le délégué NNAPI fait partie de l'interpréteur Android TensorFlow Lite, version 1.14.0 ou ultérieure. Vous pouvez l'importer dans votre projet en ajoutant ce qui suit au fichier gradle de votre module :

dependencies {
   implementation 'org.tensorflow:tensorflow-lite:+'
}

Initialisation du délégué NNAPI

Ajoutez le code pour initialiser le délégué NNAPI avant d'initialiser l'interpréteur TensorFlow Lite.

Kotlin

import android.content.res.AssetManager
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.nnapi.NnApiDelegate
import java.io.FileInputStream
import java.io.IOException
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
...

val options = Interpreter.Options()
var nnApiDelegate: NnApiDelegate? = null
// Initialize interpreter with NNAPI delegate for Android Pie or above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    nnApiDelegate = NnApiDelegate()
    options.addDelegate(nnApiDelegate)
}
val assetManager = assets

// Initialize TFLite interpreter
val tfLite: Interpreter
try {
    tfLite = Interpreter(loadModelFile(assetManager, "model.tflite"), options)
} catch (e: Exception) {
    throw RuntimeException(e)
}

// Run inference
// ...

// Unload delegate
tfLite.close()
nnApiDelegate?.close()

...

@Throws(IOException::class)
private fun loadModelFile(assetManager: AssetManager, modelFilename: String): MappedByteBuffer {
    val fileDescriptor = assetManager.openFd(modelFilename)
    val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
    val fileChannel = inputStream.channel
    val startOffset = fileDescriptor.startOffset
    val declaredLength = fileDescriptor.declaredLength
    return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
}

...

Java

import android.content.res.AssetManager;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.nnapi.NnApiDelegate;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
...

Interpreter.Options options = (new Interpreter.Options());
NnApiDelegate nnApiDelegate = null;
// Initialize interpreter with NNAPI delegate for Android Pie or above
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    nnApiDelegate = new NnApiDelegate();
    options.addDelegate(nnApiDelegate);
}

AssetManager assetManager = getAssets();
// Initialize TFLite interpreter
try {
    tfLite = new Interpreter(loadModelFile(assetManager, "model.tflite"), options);
} catch (Exception e) {
    throw new RuntimeException(e);
}

// Run inference
// ...

// Unload delegate
tfLite.close();
if(null != nnApiDelegate) {
    nnApiDelegate.close();
}

...

private MappedByteBuffer loadModelFile(AssetManager assetManager, String modelFilename) throws IOException {
    AssetFileDescriptor fileDescriptor = assetManager.openFd(modelFilename);
    FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
    FileChannel fileChannel = inputStream.getChannel();
    long startOffset = fileDescriptor.getStartOffset();
    long declaredLength = fileDescriptor.getDeclaredLength();
    return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);
}

...

Les meilleures pratiques

Tester les performances avant le déploiement

Les performances d'exécution peuvent varier considérablement en raison de l'architecture du modèle, de sa taille, des opérations, de la disponibilité du matériel et de l'utilisation du matériel d'exécution. Par exemple, si une application utilise beaucoup le GPU pour le rendu, l'accélération NNAPI peut ne pas améliorer les performances en raison de conflits de ressources. Nous vous recommandons d'exécuter un simple test de performances à l'aide de l'enregistreur de débogage pour mesurer le temps d'inférence. Exécutez le test sur plusieurs téléphones dotés de chipsets différents (fabricant ou modèles du même fabricant) représentatifs de votre base d'utilisateurs avant d'activer NNAPI en production.

Pour les développeurs avancés, TensorFlow Lite propose également un outil de référence de modèles pour Android .

Créer une liste d'exclusion d'appareils

En production, il peut arriver que NNAPI ne fonctionne pas comme prévu. Nous recommandons aux développeurs de conserver une liste d'appareils qui ne doivent pas utiliser l'accélération NNAPI en combinaison avec des modèles particuliers. Vous pouvez créer cette liste en fonction de la valeur de "ro.board.platform" , que vous pouvez récupérer à l'aide de l'extrait de code suivant :

String boardPlatform = "";

try {
    Process sysProcess =
        new ProcessBuilder("/system/bin/getprop", "ro.board.platform").
        redirectErrorStream(true).start();

    BufferedReader reader = new BufferedReader
        (new InputStreamReader(sysProcess.getInputStream()));
    String currentLine = null;

    while ((currentLine=reader.readLine()) != null){
        boardPlatform = line;
    }
    sysProcess.destroy();
} catch (IOException e) {}

Log.d("Board Platform", boardPlatform);

Pour les développeurs avancés, pensez à maintenir cette liste via un système de configuration à distance. L'équipe TensorFlow travaille activement sur les moyens de simplifier et d'automatiser la découverte et l'application de la configuration NNAPI optimale.

Quantification

La quantification réduit la taille du modèle en utilisant des entiers de 8 bits ou des flottants de 16 bits au lieu de flottants de 32 bits pour le calcul. La taille des modèles entiers 8 bits représente un quart des versions flottantes 32 bits ; Les flotteurs 16 bits font la moitié de la taille. La quantification peut améliorer considérablement les performances, même si le processus peut compromettre la précision du modèle.

Il existe plusieurs types de techniques de quantification post-formation disponibles, mais, pour une prise en charge et une accélération maximales sur le matériel actuel, nous recommandons une quantification entière complète . Cette approche convertit à la fois le poids et les opérations en nombres entiers. Ce processus de quantification nécessite un ensemble de données représentatif pour fonctionner.

Utiliser les modèles et les opérations pris en charge

Si le délégué NNAPI ne prend pas en charge certaines opérations ou combinaisons de paramètres dans un modèle, le framework exécute uniquement les parties prises en charge du graphique sur l'accélérateur. Le reste s'exécute sur le processeur, ce qui entraîne une exécution fractionnée. En raison du coût élevé de la synchronisation CPU/accélérateur, cela peut entraîner des performances plus lentes que l'exécution de l'ensemble du réseau sur le seul CPU.

NNAPI fonctionne mieux lorsque les modèles utilisent uniquement les opérations prises en charge . Les modèles suivants sont connus pour être compatibles avec NNAPI :

L'accélération NNAPI n'est pas non plus prise en charge lorsque le modèle contient des sorties de taille dynamique. Dans ce cas, vous recevrez un avertissement du type :

ERROR: Attempting to use a delegate that only supports static-sized tensors \
with a graph that has dynamic-sized tensors.

Activer l'implémentation du processeur NNAPI

Un graphique qui ne peut pas être entièrement traité par un accélérateur peut recourir à l'implémentation du processeur NNAPI. Cependant, comme il est généralement moins performant que l'interpréteur TensorFlow, cette option est désactivée par défaut dans le délégué NNAPI pour Android 10 (API niveau 29) ou supérieur. Pour remplacer ce comportement, définissez setUseNnapiCpu sur true dans l'objet NnApiDelegate.Options .