TensorFlow Lite NNAPI 委派

Android Neural Networks API (NNAPI) 在所有執行 Android 8.1 (API 級別 27) 或更高版本的 Android 裝置上均可使用。它為 Android 裝置上的 TensorFlow Lite 模型提供加速,支援的硬體加速器包括

  • 圖形處理單元 (GPU)
  • 數位訊號處理器 (DSP)
  • 神經網路處理單元 (NPU)

效能會因裝置上可用的特定硬體而異。

本頁說明如何在 Java 和 Kotlin 中搭配 TensorFlow Lite Interpreter 使用 NNAPI 委派。如需 Android C API,請參閱 Android Native Developer Kit 文件

在您自己的模型上試用 NNAPI 委派

Gradle 匯入

NNAPI 委派是 TensorFlow Lite Android Interpreter 1.14.0 或更高版本的一部分。您可以將以下內容新增至模組 gradle 檔案,將其匯入您的專案

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

初始化 NNAPI 委派

在初始化 TensorFlow Lite Interpreter 之前,新增程式碼以初始化 NNAPI 委派。

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

...

最佳做法

部署前測試效能

由於模型架構、大小、作業、硬體可用性和執行階段硬體使用率,執行階段效能可能會大幅變化。例如,如果應用程式大量使用 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 設定的方法。

量化

量化透過使用 8 位元整數或 16 位元浮點數而非 32 位元浮點數進行運算,來縮減模型大小。8 位元整數模型大小是 32 位元浮點數版本的四分之一;16 位元浮點數是大小的一半。量化可以顯著提升效能,但此程序可能會犧牲一些模型準確度。

有多種類型的訓練後量化技術可用,但是,為了在目前硬體上獲得最大支援和加速,我們建議使用完整整數量化。此方法會將權重和作業都轉換為整數。此量化程序需要代表性資料集才能運作。

使用支援的模型和運算

如果 NNAPI 委派不支援模型中的某些運算或參數組合,則架構只會在加速器上執行圖形的支援部分。其餘部分在 CPU 上執行,這會導致分割執行。由於 CPU/加速器同步處理成本高昂,這可能會導致效能比僅在 CPU 上執行整個網路還要慢。

當模型僅使用支援的運算時,NNAPI 效能最佳。已知下列模型與 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 Interpreter 的效能差,因此在 Android 10 (API 級別 29) 或更高版本的 NNAPI 委派中,預設會停用此選項。若要覆寫此行為,請在 NnApiDelegate.Options 物件中將 setUseNnapiCpu 設定為 true