Delegato NNAPI TensorFlow Lite

L' API Android Neural Networks (NNAPI) è disponibile su tutti i dispositivi Android con Android 8.1 (livello API 27) o versioni successive. Fornisce accelerazione per i modelli TensorFlow Lite su dispositivi Android con acceleratori hardware supportati, tra cui:

  • Unità di elaborazione grafica (GPU)
  • Processore di segnale digitale (DSP)
  • Unità di elaborazione neurale (NPU)

Le prestazioni varieranno a seconda dell'hardware specifico disponibile sul dispositivo.

Questa pagina descrive come utilizzare il delegato NNAPI con l'interprete TensorFlow Lite in Java e Kotlin. Per le API Android C, fai riferimento alla documentazione di Android Native Developer Kit .

Provare il delegato NNAPI sul tuo modello

Importazione Gradle

Il delegato NNAPI fa parte dell'interprete Android TensorFlow Lite, versione 1.14.0 o successiva. Puoi importarlo nel tuo progetto aggiungendo quanto segue al file gradle del modulo:

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

Inizializzazione del delegato NNAPI

Aggiungi il codice per inizializzare il delegato NNAPI prima di inizializzare l'interprete 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)
}

...

Giava

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);
}

...

Migliori pratiche

Testare le prestazioni prima della distribuzione

Le prestazioni di runtime possono variare in modo significativo a causa dell'architettura del modello, delle dimensioni, delle operazioni, della disponibilità dell'hardware e dell'utilizzo dell'hardware di runtime. Ad esempio, se un'app utilizza in modo intensivo la GPU per il rendering, l'accelerazione NNAPI potrebbe non migliorare le prestazioni a causa del conflitto delle risorse. Ti consigliamo di eseguire un semplice test delle prestazioni utilizzando il logger di debug per misurare il tempo di inferenza. Esegui il test su diversi telefoni con chipset diversi (produttore o modelli dello stesso produttore) rappresentativi della tua base utenti prima di abilitare NNAPI in produzione.

Per gli sviluppatori avanzati, TensorFlow Lite offre anche uno strumento di benchmark dei modelli per Android .

Crea un elenco di esclusione dispositivi

Nella produzione, potrebbero esserci casi in cui NNAPI non funziona come previsto. Consigliamo agli sviluppatori di mantenere un elenco di dispositivi che non dovrebbero utilizzare l'accelerazione NNAPI in combinazione con modelli particolari. Puoi creare questo elenco in base al valore di "ro.board.platform" , che puoi recuperare utilizzando il seguente snippet di codice:

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);

Per gli sviluppatori avanzati, considera la possibilità di mantenere questo elenco tramite un sistema di configurazione remota. Il team di TensorFlow sta lavorando attivamente su modi per semplificare e automatizzare la scoperta e l'applicazione della configurazione NNAPI ottimale.

Quantizzazione

La quantizzazione riduce le dimensioni del modello utilizzando numeri interi a 8 bit o numeri in virgola mobile a 16 bit anziché in virgola mobile a 32 bit per il calcolo. Le dimensioni del modello intero a 8 bit sono un quarto delle versioni float a 32 bit; I float a 16 bit sono la metà delle dimensioni. La quantizzazione può migliorare significativamente le prestazioni anche se il processo potrebbe compromettere l'accuratezza del modello.

Sono disponibili diversi tipi di tecniche di quantizzazione post-training, ma, per il massimo supporto e accelerazione sull'hardware attuale, consigliamo la quantizzazione intera completa . Questo approccio converte sia il peso che le operazioni in numeri interi. Questo processo di quantizzazione richiede un set di dati rappresentativo per funzionare.

Utilizza modelli e operazioni supportati

Se il delegato NNAPI non supporta alcune operazioni o combinazioni di parametri in un modello, il framework esegue solo le parti supportate del grafico sull'acceleratore. Il resto viene eseguito sulla CPU, il che si traduce in un'esecuzione divisa. A causa del costo elevato della sincronizzazione CPU/acceleratore, ciò potrebbe comportare un rallentamento delle prestazioni rispetto all'esecuzione dell'intera rete sulla sola CPU.

NNAPI offre prestazioni migliori quando i modelli utilizzano solo le operazioni supportate . I seguenti modelli sono noti per essere compatibili con NNAPI:

Inoltre, l'accelerazione NNAPI non è supportata quando il modello contiene output di dimensioni dinamiche. In questo caso riceverai un avviso del tipo:

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

Abilita l'implementazione della CPU NNAPI

Un grafico che non può essere elaborato completamente da un acceleratore può ricorrere all'implementazione della CPU NNAPI. Tuttavia, poiché in genere è meno performante dell'interprete TensorFlow, questa opzione è disabilitata per impostazione predefinita nel delegato NNAPI per Android 10 (livello API 29) o versioni successive. Per sovrascrivere questo comportamento, impostare setUseNnapiCpu su true nell'oggetto NnApiDelegate.Options .