Delegat TensorFlow Lite NNAPI

Interfejs Android Neural Networks API (NNAPI) jest dostępny na wszystkich urządzeniach z systemem Android z systemem Android 8.1 (poziom API 27) lub nowszym. Zapewnia akcelerację dla modeli TensorFlow Lite na urządzeniach z systemem Android z obsługiwanymi akceleratorami sprzętowymi, w tym:

  • Jednostka przetwarzania grafiki (GPU)
  • Cyfrowy procesor sygnałowy (DSP)
  • Jednostka przetwarzania neuronowego (NPU)

Wydajność będzie się różnić w zależności od konkretnego sprzętu dostępnego w urządzeniu.

Na tej stronie opisano, jak używać delegata NNAPI z interpreterem TensorFlow Lite w języku Java i Kotlin. W przypadku interfejsów API systemu Android C zapoznaj się z dokumentacją zestawu Android Native Developer Kit .

Wypróbowanie delegata NNAPI na własnym modelu

Import stopniowy

Delegat NNAPI jest częścią interpretera TensorFlow Lite dla systemu Android w wersji 1.14.0 lub nowszej. Możesz zaimportować go do swojego projektu, dodając następujące elementy do pliku stopni modułu:

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

Inicjowanie delegata NNAPI

Dodaj kod, aby zainicjować delegata NNAPI przed zainicjowaniem interpretera 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)
}

...

Jawa

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

...

Najlepsze praktyki

Przetestuj wydajność przed wdrożeniem

Wydajność środowiska wykonawczego może się znacznie różnić w zależności od architektury modelu, rozmiaru, operacji, dostępności sprzętu i wykorzystania sprzętu w czasie wykonywania. Na przykład, jeśli aplikacja w dużym stopniu wykorzystuje procesor graficzny do renderowania, akceleracja NNAPI może nie poprawić wydajności ze względu na rywalizację o zasoby. Zalecamy przeprowadzenie prostego testu wydajności przy użyciu rejestratora debugowania w celu zmierzenia czasu wnioskowania. Przed włączeniem NNAPI w środowisku produkcyjnym przeprowadź test na kilku telefonach z różnymi chipsetami (producent lub modele tego samego producenta), które są reprezentatywne dla Twojej bazy użytkowników.

Dla zaawansowanych programistów TensorFlow Lite oferuje również narzędzie do testowania modeli dla systemu Android .

Utwórz listę wykluczeń urządzeń

W środowisku produkcyjnym mogą wystąpić przypadki, w których NNAPI nie będzie działać zgodnie z oczekiwaniami. Zalecamy programistom prowadzenie listy urządzeń, które nie powinny korzystać z akceleracji NNAPI w połączeniu z określonymi modelami. Możesz utworzyć tę listę w oparciu o wartość "ro.board.platform" , którą możesz pobrać za pomocą następującego fragmentu kodu:

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

W przypadku zaawansowanych programistów rozważ utrzymanie tej listy za pośrednictwem systemu zdalnej konfiguracji. Zespół TensorFlow aktywnie pracuje nad sposobami uproszczenia i automatyzacji odkrywania i stosowania optymalnej konfiguracji NNAPI.

Kwantyzacja

Kwantyzacja zmniejsza rozmiar modelu, używając do obliczeń 8-bitowych liczb całkowitych lub 16-bitowych liczb zmiennoprzecinkowych zamiast 32-bitowych zmiennoprzecinkowych. Rozmiary modeli 8-bitowych liczb całkowitych stanowią jedną czwartą 32-bitowych wersji zmiennoprzecinkowych; 16-bitowe pływaki są o połowę mniejsze. Kwantyzacja może znacznie poprawić wydajność, chociaż proces ten może kosztować pewną dokładność modelu.

Dostępnych jest wiele typów technik kwantyzacji po szkoleniu, ale w celu uzyskania maksymalnego wsparcia i przyspieszenia na obecnym sprzęcie zalecamy pełną kwantyzację na liczbach całkowitych . Podejście to przekształca zarówno wagę, jak i operacje na liczby całkowite. Ten proces kwantyzacji wymaga do działania reprezentatywnego zbioru danych.

Korzystaj z obsługiwanych modeli i operacji

Jeśli delegat NNAPI nie obsługuje niektórych kombinacji operacji lub parametrów w modelu, platforma uruchamia tylko obsługiwane części wykresu w akceleratorze. Pozostała część działa na procesorze, co skutkuje rozdzieleniem wykonania. Ze względu na wysoki koszt synchronizacji procesora/akceleratora może to skutkować niższą wydajnością niż wykonywanie całej sieci na samym procesorze.

NNAPI działa najlepiej, gdy modele korzystają tylko z obsługiwanych operacji . Wiadomo, że następujące modele są kompatybilne z NNAPI:

Przyspieszenie NNAPI również nie jest obsługiwane, gdy model zawiera dane wyjściowe o dynamicznych rozmiarach. W takim przypadku otrzymasz ostrzeżenie takie jak:

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

Włącz implementację procesora NNAPI

Wykres, którego nie można całkowicie przetworzyć za pomocą akceleratora, może zostać oparty na implementacji procesora NNAPI. Ponieważ jednak jest to zwykle mniej wydajne niż interpreter TensorFlow, ta opcja jest domyślnie wyłączona w delegacie NNAPI dla systemu Android 10 (poziom interfejsu API 29) lub nowszego. Aby zastąpić to zachowanie, ustaw setUseNnapiCpu na true w obiekcie NnApiDelegate.Options .