ArmNN神经网络技术分析
ArmNN神经网络技术分析
ArmNN 使用介绍
一. 概述
将带领各位如何在 ArmNN 的框架上,使用不同的硬件处理器( CPU 或 NPU 的切换 )。如下图,文章架构图所示 !! 此架构图隶属于 i.MX8M Plus 的方案中,并属于机器学习内的推理引擎(Inference Engine) 的 ArmNN 部分。
“ArmNN 使用介绍”
ArmNN -文章架构示意图
二. ArmNN 介绍
ArmNN 是一套开源的机器学习推理引擎框架,由 ARM 与 Linaro 主导开发的人工智能计划,能够提供用户更简单、轻松、快速的机器学习体验。特别的是 ArmNN 并非有自己的推理引擎,而是可以运行多种深度学习模块框架的 推理引擎(inference engine) 或 委托器(delegates) ,像是 Caffe, TensorFlow, TensorFlow Lite, 以及 ONNX 。 除此之外,亦提供 CPU / GPU/ NPU 等硬件加速资源,并可以搭配 C++ 或 Python 语言进行推理 !! 能够给予 Arm 的硬件更棒的加速体验 !!
ArmNN 后端推理框架图
如下图所示,首先由各家模块 解译(parser) 完成后,将托付给 Graph builder 与 Runtime Optimizer 至各硬设备进行优化,而后续则有三种处理方式 ; 第一种左侧路线,就是纯 CPU 的运算。第二种就是中间路线,利用 ARM Compute Library 与 ARM Neon 核心作多线程优化进行推理。而第三种则是右侧路线,委托给神经网络推理引擎(NNRT),并透过 OVXLIB 与 OpenVX driver 来启动 GPU 或 NPU 进行硬件加速来完成推理!!
ArmNN 后端推理框架图
下列提供关于 ArmNN 之 Caffe, TensorFlow, TensorFlow Lite, ONNX 等神经网络框架的推理范例,如下列表格所示。
若欲查看更多范例,请查看 i.MX Machine Learning User's Guide.pdf 的 ArmNN 章节。
原厂提供范例 (Caffe) :
直接进入开发板的 /usr/bin/armnn-21.02 底下,找到相关执行档(xxx-Armnn),操作下列指令即可使用!!
$ --data-dir=data --model-dir=models --compute=CpuAcc/VsiNpu/CpuRef
--data-dir : 数据路径
--model-dir : 模块路径
--compute : CpuRef 为不启用 CPU 加速 、CpuAcc 为使用 NEON Backend 启用 CPU 加速 、 VsiNpu 为使用 NPU 或是 GPU 加速运算。
若欲查看代码编译方式,请至 i.MX Machine Learning User's Guide.pdf 的 ArmNN 章节。
三. ArmNN 范例实现
1. Object Classification DEMO (TensorFlow Lite)
说明 : 此范例使用 Arm NN - TensorFlow Lite神经网络推理框架与 MobileNet 模型进行分类对象。
实现步骤 :
(1) 于开发板系统中,建立 ArmNN 文件夹
$ cd && mkdir
ArmNN
$ cd ~/ArmNN && mkdir models
$ cd ~/ArmNN && mkdir data
(2) 将测试图片与新的模块传送至开发板系统
$ scp mobilenet_v1_1.0_224_quant.tflite
root@xx.xx.xx.xx: ~/ArmNN/models
$ scp Dog.jpg root@xx.xx.xx.xx: ~/ArmNN/data
$ scp Cat.jpg root@xx.xx.xx.xx: ~/ArmNN/data
$ scp shark.jpg root@xx.xx.xx.xx: ~/ArmNN/data
(3) 范例之运行 CPU 使用方式
$ /usr/bin/armnn-21.08/TfLiteMobilenetQuantized-Armnn --data-dir=data --model-dir=models --compute=CpuAcc
(4) 范例之运行 NPU使用方式
$ /usr/bin/armnn-21.08/TfLiteMobilenetQuantized-Armnn --data-dir=data --model-dir=models --compute=VsiNpu
运行结果 :
如下图所示,分类结果为 第 209 号标签的黄金猎犬(golden retriever) ,准确率为 78.9 %。
在 ArmNN 框架下,使用 CPU-ACC 运算 TF Lite模块则平均花费 48 ms,NPU 运算花费 3 ms。
*** 此范例会同时测试三张影像( Dog / Cat /
Shark.jpg )。
2. Object Classification DEMO (ONNX)
说明 : 此范例使用 Arm NN - TensorFlow Lite神经网络推理框架与 MobileNet 模型进行分类对象。
实现步骤 :
(1) 于开发板系统中,建立 ArmNN 文件夹
$ cd && mkdir
ArmNN
$ cd ~/ArmNN && mkdir models
$ cd ~/ArmNN && mkdir data
(2) 将测试图片与新的模块传送至开发板系统
$ scp
mobilenetv2-1.0.onnx root@xx.xx.xx.xx: ~/ArmNN/models
$ scp Dog.jpg root@xx.xx.xx.xx: ~/ArmNN/data
$ scp Cat.jpg root@xx.xx.xx.xx: ~/ArmNN/data
$ scp shark.jpg root@xx.xx.xx.xx: ~/ArmNN/data
(3) 范例之运行 CPU 使用方式
$ /usr/bin/armnn-21.08/OnnxMobileNet-Armnn --data-dir=data --model-dir=models --compute=CpuAcc
(4) 范例之运行 NPU使用方式
$ /usr/bin/armnn-21.08/OnnxMobileNet-Armnn --data-dir=data --model-dir=models --compute=VsiNpu
运行结果 :
如下图所示,分类结果为 第 209 号标签的黄金猎犬(golden retriever) ,准确率为 15.54 %。
在 ArmNN 框架下,使用 CPU-ACC 运算 TF Lite模块则平均花费 272 ms,NPU 运算花费 545 ms。
目前 ONNX 的推理引擎与 AI 芯片的底层描述尚未完善故效能较差。
*** 此范例会同时测试三张影像( Dog / Cat /
Shark.jpg )。
四. 结语
上述已经介绍了 ArmNN 的使用方式,藉此可以选择 TensorFlow 或是 ONNX 的神经网络框架进行推理。并可任意使用至 CPU / GPU / NPU 等加速运算芯片。在量化后的 MobileNet 最快可达到每帧 3ms 的推理速度 !!
ArmNN如何编译
ArmNN是Arm机构开源的基于Arm嵌入式设备的inference框架,在Arm Cortex-A CPUs、Arm Mali GPUs、Arm Machine Learning processor都可以达到很高的加速效果。不过可惜的是,由于缺乏文档及教程,该框架在国内推广的程度不高,目前Github上star仅有359个。相对于其他竞品inference框架如NCNN、Tengine、Mace、MNN等等,ArmNN的知名度显得很小。不过笔者在项目的多次使用中,发现ArmNN是一个被低估的框架(在Arm设备上的性能几乎都优于目前的前传框架),不亏是Arm家精心调教的SDK,对自家核心的性能调教到了极致。
ArmNN基于Arm的另外一个开源计算引擎ComputeLibrary做后端的核心计算,前端支持多种离线训练框架,如TensorFlow、TFLITE、CAFFE以及ONNX。从功能上来说,几乎实现了与市面上所有离线训练框架无缝对接。而且ArmNN在FP32、FP16及INT8上的加速非常可观,笔者在RK3399上做300x300的Mobilenet-SSD(depth\_multiplier=1.0),效率可达90ms/帧,其余的框架大多在160ms左右徘徊。
笔者后续会在本专栏开源基于ArmNN的Mobilenet-SSD的部署流程及项目代码。接下来,将先跟大家讨论如何编译ArmNN。由于目标平台是RK3399-Android-8.1,将基于android-Arm64-v8a进行编译。下面开始:
● 下载NDK
笔者使用的版本为NDK-r17c,由于ArmNN使用了一些C++14的新特性,所以老一些的NDK版本在编译的时候会出很多莫名其妙的错误。出于稳妥考虑,建议大家用r17c以上的版本,准备完毕以后,需要配置NDK路径:
export NDK=~/armnn-devenv/toolchains/android-ndk-r17c
接下来制作自己的toolchain编译工具链,
$NDK/build/tools/make_standalone_toolchain.py \
--arch arm64 \
--api 26 \
--stl=libc++\
--install-dir=$HOME/armnn-devenv/toolchains/aarch64-android-r17b
export PATH=$HOME/armnn-devenv/toolchains/aarch64-android-r17b/bin:$PATH
如果想持久化以上环境变量到系统中,可以写进.bashrc文件中(或macOS的.bash\_profile文件)。
● 编译Boot
先下载对应版本的Boost库,
mkdir ~/armnn-devenv/boost
cd ~/armnn-devenv/boost
wget https://dl.bintray.com/boostorg/release/1.64.0/source/boost_1_64_0.tar.bz2tar xvf boost_1_64_0.tar.bz2
进行编译,
echo "using gcc : arm : aarch64-linux-android-clang++ ;" > $HOME/armnn-devenv/boost/user-config.jam
cd ~/armnn-devenv/boost/boost_1_64_0
./bootstrap.sh --prefix=$HOME/armnn-devenv/boost/install
./b2 install --user-config=$HOME/armnn-devenv/boost/user-config.jam \
toolset=gcc-arm link=static cxxflags=-fPIC --with-filesystem \ --with-test --with-log --with-program_options -j16
● 编译ComputeLibrary
先下载对应版本的ComputeLibrary,
cd ~/armnn-devenv
git clone https://github.com/ARM-software/ComputeLibrary.git
安装scons后(sudo apt-get install scons),进行编译
cd ComputeLibrary
scons arch=arm64-v8a neon=1 opencl=1 embed_kernels=1 extra_cxx_flags="-fPIC" \
benchmark_tests=0 validation_tests=0 os=android -j16
● 编译谷歌的ProtoBuf
首先仍是下载对应的源码,并切换至对应版本,
mkdir ~/armnn-devenv/google
cd ~/armnn-devenv/google
git clone https://github.com/google/protobuf.git
cd protobuf
git checkout -b v3.5.2 v3.5.2
需要注意的是,这里需要编译对应PC(笔者为x86)和目标平台(Arm64-v8a)的两个版本。编译过程中依赖cUrl、autoconf、llibtool等,可以通过如下命令安装 sudo apt-get install curl autoconf libtool build-essential g++。下面先编译x86版本的,
./autogen.sh
mkdir x86_build
cd x86_build
../configure --prefix=$HOME/armnn-devenv/google/x86_pb_install
make install -j16
cd ..
随后安装Arm64-v8a版本的,
mkdir arm64_build
cd arm64_build
CC=aarch64-linux-android-clang \
CXX=aarch64-linux-android-clang++ \
CFLAGS="-fPIE -fPIC" LDFLAGS="-pie -llog" \
../configure --host=aarch64-linux-android \
--prefix=$HOME/armnn-devenv/google/arm64_pb_install \
--with-protoc=$HOME/armnn-devenv/google/x86_pb_install/bin/protoc
make install -j16
cd ..
● 编译ArmNN
这里采用的frontend为tensorflow,所以需要先下载对应的tensorflow源码,
cd ~/armnn-devenv/google/
git clone https://github.com/tensorflow/tensorflow.git
随后下载ArmNN源码,cd ~/armnn-devenv/
git clone https://github.com/ARM-software/armnn.git
在PC中生成解析tensorflow的protobuf定义(使用x86平台中编译好的protoc),cd ~/armnn-devenv/google/tensorflow
~/armnn-devenv/armnn/scripts/generate_tensorflow_protobuf.sh \
$HOME/armnn-devenv/google/tf_pb $HOME/armnn-devenv/google/x86_pb_install
随后编译ArmNN,mkdir ~/armnn-devenv/armnn/build
cd ~/armnn-devenv/armnn/build
CXX=aarch64-linux-android-clang++ \
CC=aarch64-linux-android-clang \
CXX_FLAGS="-fPIE -fPIC" \
cmake .. \
-DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \
-DCMAKE_ANDROID_STANDALONE_TOOLCHAIN=$HOME/armnn-devenv/toolchains/aarch64-android-r17c/ \ -DCMAKE_EXE_LINKER_FLAGS="-pie -llog" \
-DARMCOMPUTE_ROOT=$HOME/armnn-devenv/ComputeLibrary/ \
-DARMCOMPUTE_BUILD_DIR=$HOME/armnn-devenv/ComputeLibrary/build \
-DBOOST_ROOT=$HOME/armnn-devenv/boost_1_64_0/install/ \
-DARMCOMPUTENEON=1 -DARMCOMPUTECL=1 \
-DTF_GENERATED_SOURCES=$HOME/armnn-devenv/google/tf_pb/ \ -DBUILD_TF_PARSER=1 \
-DPROTOBUF_ROOT=$HOME/armnn-devenv/google/arm64_pb_install/ \
-DBUILD_TESTS=1 \
-DTHIRD_PARTY_INCLUDE_DIRS=$HOME/armnn-devenv/armnn/third-party/stb \
-DCMAKE_BUILD_TYPE=Release -DBUILD_ARMNN_QUANTIZER=1 -DBUILD_ARMNN_SERIALIZER=1
make -j16
编译完以后,可以将库和测试用例推到目标平台中进行测试,adb push libarmnnTfParser.so /data/local/tmp/
adb push libarmnn.so /data/local/tmp/
adb push UnitTests /data/local/tmp/
adb push $NDK/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so /data/local/tmp/
adb push $HOME/armnn-devenv/google/arm64_pb_install/lib/libprotobuf.so /data/local/tmp/libprotobuf.so.15.0.1
adb shell 'ln -s libprotobuf.so.15.0.1 /data/local/tmp/libprotobuf.so.15'
adb shell 'ln -s libprotobuf.so.15.0.1 /data/local/tmp/libprotobuf.so'
执行测试用例,adb shell 'LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/UnitTests'
参考文献链接
https://www.wpgdadatong.com/blog/detail/45902
https://mp.weixin.qq.com/s/gPMfN6chEO37aOPRoKUsNg