Android Arm NN 图像分类





0/5 (0投票)
使用 Arm NN 在 Android 移动设备上进行图像分类。
如今的移动设备堪称强大的超级计算机,能够实现令人难以置信的、由硬件驱动的机器学习应用。您可能不知道的是,Arm 开发者一直在为核心 Android 开发做出贡献,包括在 Android NDK 中添加 Arm NN 功能以及对 Android 神经网络 API (NNAPI) 的支持。如果您正在利用最新 Android 设备上的 NN,您其实已经在使用 Arm NN 并获得性能提升。
让我们来看一个示例,展示在 Android 应用中使用 Arm NN 原生的性能优势。
在 Android 上使用 Arm NN
Arm NN SDK 是一套开源工具,可在节能设备上实现机器学习工作负载。它提供了现有神经网络框架与 Arm Cortex-A CPU、Arm Mali GPU 和 Ethos NPU 之间的桥梁。Arm NN 技术可在使用最新硬件(配备 Cortex 处理器和最新 Android 库)的标准消费级移动设备上使用。
使用 Arm NN 应该是透明的——您调用的是相同的标准 Android API,Arm NN 默认已存在。
为了让 NNAPI 充分利用设备的硬件,必须存在相应的硬件驱动程序。在运行 Android Q(Android 10)的三星较新设备上,驱动程序已开箱即用。对于较旧的设备,开发者可以自行编译和安装驱动程序。要完成此过程,需要对设备进行 root 访问。驱动程序的源代码可在 https://github.com/ARM-software/android-nn-driver 上找到。
使用 NNAPI 进行图像分类
让我们来看一个图像分类示例,以及它如何利用 NNAPI。在 Android 应用中,从高层来看,您需要执行以下操作才能使用带有 NNAPI 的 TensorFlow Lite 模型。
- 加载 TensorFlow Lite 模型的标签
- 创建一个 TensorFlow 解释器选项对象,并向其中添加一个 NNAPI 委托
- 创建 TensorFlow 解释器对象
- 预处理要识别的图像
- 运行解释器处理图像
- 检查结果
模型的标签通常存储在一个文本文件中。在示例代码中,标签存储方式是每行一个标签。这些标签会被添加到代码中一个名为 labelList 的字符串列表中。
Interpreter.Options 对象用于指定 TensorFlow Lite 解释器如何执行的设置。它可以控制的设置之一是模型是在 CPU 上运行还是在其他硬件上运行。这通过创建委托对象并将其添加到选项对象来完成。
委托对象可用于在设备的 GPU、数字信号处理器 (DSP) 或神经网络处理器 (NPU) 上加速模型。NNAPI 委托将确定哪种硬件最适合运行特定模型。如果这些硬件都不可用,NNAPI 将回退到在 CPU 上评估模型。
在创建解释器之前,必须将包含模型的字节加载到 ByteBuffer 中。ByteBuffer 和 Options 对象一起传递给解释器的构造函数。解释器现在已准备好开始处理提供给它的数据。
void prepareInterpretor() {
Interpreter.Options options = new Interpreter.Options();
nnApiDelegate = new NnApiDelegate();
options.addDelegate(nnApiDelegate);
MappedByteBuffer tfLiteModel =
FileUtil.loadMappedFile(context, MODEL_FILE_NAME);
tfLiteInterpreter = new Interpreter(tfLiteModel, options);
}
对于此代码使用的模型,图像预计尺寸为 224x224 像素。图像可以在提供给应用程序之前调整到适当的尺寸,或者由应用程序进行调整。在内存中调整图像大小以及进行其他调整(如偏移像素的值范围或数据类型)时,ImageProcessor 类可以执行必要的转换。
解释器的输入图像来自一个从 Bitmap初始化的 TFImage。TFImage 提供了一个用于我们模型输入的字节缓冲区。对于输出,我们必须创建一个模型期望大小的字节数组。对于此模式,输出将被写入一个 1001 字节的字节数组。
sourceImage = sourceImage.copy(Bitmap.Config.ARGB_8888, true);
final byte outputBuffer[][] = new byte[1][1001];
ImageProcessor imageProcessor = new ImageProcessor.Builder()
.add(new ResizeOp(IMAGE_HEIGHT, IMAGE_WIDTH, ResizeOp.ResizeMethod.BILINEAR))
.build();
TensorImage tfImage = new TensorImage(DataType.UINT8);
tfImage.load(sourceImage);
tfImage = imageProcessor.process(tfImage);
tfLiteInterpreter.run(inputBuffer, outputBuffer);
输出中的每个位置都与网络识别的对象类别之一相关联。该位置的数字表示图像包含该类对象的置信度。
要解释结果,必须找到输出数组中的最大值。一旦知道了最大值的确切位置,labelList 中相同位置就包含一个描述该类的字符串。
例如,如果位置 3 的值最高,那么标签是索引 3。包含标签的文件的前四行如下,标有行号。
0:unclassified 1:tench, Tinca tinca 2:goldfish, Carassius auratus 3:great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias
在这种情况下,如果值 3 是最高索引,那么模型得出结论,图像可能包含一条鲨鱼。
列表中的第一项(位置 0)是背景噪音的位置。如果这是置信度最高的位置,则图像无法识别。请注意,我们的模型返回无符号字节值。为了确保它们被正确解释,它们被转换为整数,这样 127 以上的值就不会被误解为负值。
public int getStongestPosition(byte[][] output) {
byte[] results = output[0];
int maxIndex = 0;
int maxValue = 0;
for(int i=0;i<results.length;++i) {
int unsignedResult = results[i] & 0xFF;
if(unsignedResult >maxValue) {
maxIndex = i;
maxValue = unsignedResult;
}
}
return maxIndex;
}
对于示例程序,解释器的执行时间被测量并与结果一起显示。
要查看模型在没有 NNAPI 的情况下如何执行,请在不向选项对象添加 NNAPI 委托的情况下创建解释器。启用 NNAPI 后,模型的执行时间可以快四倍。
不使用 Arm NN 进行测试
要不使用 NNAPI 执行,请创建一个其 Option 对象未添加 NNAPI 委托的 Interpreter。通过条件语句,程序可以在代码中启用或禁用 NNAPI 的使用。
void prepareInterpretor() {
Interpreter.Options options = new Interpreter.Options();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && this.useNNAPI) {
nnApiDelegate = new NnApiDelegate();
options.addDelegate(nnApiDelegate);
}
MappedByteBuffer tfLiteModel = FileUtil.loadMappedFile(
context, MODEL_FILE_NAME);
tfLiteInterpreter = new Interpreter(tfLiteModel, options);
}
启用 NNAPI 和未启用 NNAPI 时的性能差异非常明显。下面是运行在两张图像上的程序输出的截图。这两次测试的结果是通过在三星 Galaxy Note 20 上运行代码获得的。
在第一张图像中,程序在杯子图像上运行。对于第一张图像,模型仅在 CPU 上运行,而右侧的图像则使用 NNAPI 运行。比较两个输出的执行时间单位,我们看到启用 NNAPI 后,程序运行速度几乎快了 3 倍(2.807)。
用另一张图像运行程序,我们仍然可以看到启用 NNAPI 和禁用 NNAPI 之间的性能提升。对于这张第二张图像,启用 NNAPI 后,程序运行速度快了 2.405 倍。
总结
我已经展示了在 Android 项目中开始使用 NNAPI 是多么容易。既然 NNAPI 已内置,为什么开发者不使用它来加速他们的应用程序呢?您可能针对的是较旧的 Android 版本。但这提供了一个很好的例子,说明为什么针对最新版本可以获得 NNAPI 和性能提升。
虽然这个示例侧重于图像识别,但潜在的应用不限于识别。它可以应用于一系列应用,包括 ML 超分辨率,用于从低分辨率图像提供清晰图像,或用于姿态识别,用于检测图像或视频流中某人的位置和姿势(TF PoseNet)。
您可以在 神经网络 API 页面上继续了解有关使用神经网络的更多信息。有关优化和部署神经网络驱动程序的更多信息,请参阅这篇题为 使用 Arm NN API 在 Android 上实现神经风格转换 的文章。