TensorFlow Lite NNAPI デリゲート

Android Neural Networks API (NNAPI) は、 Android 8.1 (API レベル 27) 以降を実行しているすべての Android デバイスで利用できます。次のようなサポートされているハードウェア アクセラレータを使用して、Android デバイス上の TensorFlow Lite モデルのアクセラレーションを提供します。

  • グラフィックス プロセッシング ユニット (GPU)
  • デジタルシグナルプロセッサー(DSP)
  • ニューラル プロセッシング ユニット (NPU)

パフォーマンスは、デバイスで利用可能な特定のハードウェアによって異なります。

このページでは、Java および Kotlin の TensorFlow Lite インタープリターで NNAPI デリゲートを使用する方法について説明します。 Android C API については、 Android Native Developer Kit のドキュメントを参照してください。

独自のモデルで NNAPI デリゲートを試してみる

Gradleインポート

NNAPI デリゲートは、TensorFlow Lite Android インタープリター、リリース 1.14.0 以降の一部です。以下をモジュール gradle ファイルに追加することで、プロジェクトにインポートできます。

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

NNAPI デリゲートの初期化

TensorFlow Lite インタープリターを初期化する前に、NNAPI デリゲートを初期化するコードを追加します。

コトリン

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

...

ジャワ

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

...

ベストプラクティス

導入前にパフォーマンスをテストする

実行時のパフォーマンスは、モデルのアーキテクチャ、サイズ、操作、ハードウェアの可用性、および実行時のハードウェアの使用率によって大きく異なる可能性があります。たとえば、アプリがレンダリングに GPU を頻繁に利用する場合、リソースの競合により、NNAPI アクセラレーションによってパフォーマンスが向上しない可能性があります。デバッグ ロガーを使用して簡単なパフォーマンス テストを実行し、推論時間を測定することをお勧めします。運用環境で NNAPI を有効にする前に、ユーザー ベースを代表する、異なるチップセット (メーカーまたは同じメーカーのモデル) を搭載した複数の電話機でテストを実行します。

上級開発者向けに、TensorFlow Lite はAndroid 用のモデル ベンチマーク ツールも提供します。

デバイス除外リストを作成する

運用環境では、NNAPI が期待どおりに動作しない場合があります。開発者には、NNAPI アクセラレーションを特定のモデルと組み合わせて使用​​すべきではないデバイスのリストを維持することをお勧めします。このリストは、次のコード スニペットを使用して取得できる"ro.board.platform"の値に基づいて作成できます。

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

上級開発者の場合は、リモート構成システムを介してこのリストを維持することを検討してください。 TensorFlow チームは、最適な NNAPI 構成の検出と適用を簡素化および自動化する方法に積極的に取り組んでいます。

量子化

量子化では、計算に 32 ビット浮動小数点の代わりに 8 ビット整数または 16 ビット浮動小数点を使用することにより、モデルのサイズが削減されます。 8 ビット整数モデルのサイズは、32 ビット浮動小数点バージョンの 4 分の 1 です。 16 ビット浮動小数点のサイズは半分です。量子化によりパフォーマンスが大幅に向上しますが、このプロセスによりモデルの精度が犠牲になる可能性があります。

利用可能なトレーニング後の量子化手法には複数の種類がありますが、現在のハードウェアで最大限のサポートと高速化を実現するには、完全な整数量子化をお勧めします。このアプローチでは、重みと演算の両方が整数に変換されます。この量子化プロセスでは、代表的なデータセットが機能する必要があります。

サポートされているモデルとオペレーションを使用する

NNAPI デリゲートがモデル内の一部の操作またはパラメーターの組み合わせをサポートしていない場合、フレームワークはグラフのサポートされている部分のみをアクセラレータで実行します。残りは CPU 上で実行されるため、分割実行になります。 CPU/アクセラレータの同期にはコストがかかるため、CPU のみでネットワーク全体を実行する場合よりもパフォーマンスが低下する可能性があります。

NNAPI は、モデルがサポートされている opsのみを使用する場合に最高のパフォーマンスを発揮します。次のモデルは NNAPI と互換性があることがわかっています。

モデルに動的サイズの出力が含まれている場合、NNAPI アクセラレーションもサポートされません。この場合、次のような警告が表示されます。

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

NNAPI CPU実装を有効にする

アクセラレータで完全に処理できないグラフは、NNAPI CPU 実装にフォールバックできます。ただし、これは一般に TensorFlow インタープリターよりもパフォーマンスが低いため、Android 10 (API レベル 29) 以降の NNAPI デリゲートではこのオプションはデフォルトで無効になっています。この動作をオーバーライドするには、 NnApiDelegate.OptionsオブジェクトでsetUseNnapiCputrueに設定します。