TensorFlow Lite NNAPI 代理

在运行 Android 8.1(API 级别 27)或更高版本的 Android 设备上,Android 神经网络 API (NNAPI) 可用。它为支持硬件加速器的 Android 设备上的 TensorFlow Lite 模型提供加速,包括

  • 图形处理单元 (GPU)
  • 数字信号处理器 (DSP)
  • 神经处理单元 (NPU)

性能将根据设备上可用的特定硬件而有所不同。

本页面介绍了如何在 Java 和 Kotlin 中使用 TensorFlow Lite 解释器中的 NNAPI 代理。有关 Android C API,请参阅 Android 原生开发工具包文档.

在您自己的模型上尝试 NNAPI 代理

Gradle 导入

NNAPI 代理是 TensorFlow Lite Android 解释器的一部分,版本 1.14.0 或更高版本。您可以通过将以下内容添加到模块 gradle 文件中来将其导入到您的项目中

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

初始化 NNAPI 代理

在初始化 TensorFlow Lite 解释器之前添加初始化 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 解释器性能更低,因此此选项在 Android 10(API 级别 29)或更高版本的 NNAPI 代理中默认情况下处于禁用状态。要覆盖此行为,请在 NnApiDelegate.Options 对象中将 setUseNnapiCpu 设置为 true