PaddleLite使用ARM CPU预测部署¶
Lite支持在Android/IOS/ARMLinux等移动端设备上运行高性能的CPU预测库,目前支持Ubuntu环境下armv8、armv7的交叉编译。
1. 编译¶
1.2 编译Paddle-Lite ARM CPU库范例¶
注:以android/armv8目标、Docker容器的编译开发环境为例,CMake3.10,android-ndk-r17c位于/opt/
目录下。
针对 Lite 用户的编译命令(无单元测试,有编译产物,适用于benchmark)¶
arm_abi
:[armv7 | armv8]
;toolchain
:[gcc | clang]
;build_extra
:[OFF | ON]
,编译全量op和kernel,包含控制流NLP相关的op和kernel体积会大,编译时间长;build_cv
:[OFF | ON]
,编译arm cpu neon实现的的cv预处理模块;android_stl
:[c++_shared | c++_static | gnu_static | gnu_shared]
,paddlelite的库以何种方式链接android_stl
,选择c++_shared
得到的动态库体积更小,但使用时候记得上传paddlelite所编译版本(armv7或armv8)一致的libc++_shared.so
。默认使用c++_static
。
######################################
# 假设当前位于处于Lite源码根目录下 #
######################################
# 导入NDK_ROOT变量,注意检查NDK安装目录若与本示例是否不同
export NDK_ROOT=/opt/android-ndk-r17c
# 删除上一次CMake自动生成的.h文件
rm ./lite/api/paddle_use_kernels.h
rm ./lite/api/paddle_use_ops.h
# 设置编译参数并开始编译
# android-armv8:cpu+cv+extra
./lite/tools/build_android.sh \
--arch=armv8 \
--toolchain=clang \
--with_log=OFF \
--with_extra=ON \
--with_cv=ON
# android-armv7:cpu+cv+extra
./lite/tools/build_android.sh \
--arch=armv7 \
--toolchain=clang \
--with_log=OFF \
--with_extra=ON \
--with_cv=ON
# android-armv8-(v8.2+FP16):cpu+FP16+cv+extra
# update NDK version > 19
export NDK_ROOT=/opt/android-ndk-r20b
./lite/tools/build_android.sh \
--arch=armv8 \
--toolchain=clang \
--with_log=OFF \
--with_extra=ON \
--with_arm82_fp16=ON \
--with_cv=ON
# 注:编译帮助请执行: ./lite/tools/build_android.sh help
注1:该方式的编译产物中的demo/cxx/mobile_light
适用于做benchmark,该过程不会打印开发中加入的log,注意需要提前转好模型。关于使用,详见下文运行示例1: 编译产物demo示例
注2: 如果运行FP16 预测库,模型在OPT转换的时候需要加上--enable_fp16=1
选项,这样转换的模型会选择FP16 kernel实现。并且,FP16预测库和FP16模型只在支持ARMv8.2架构的手机上运行,如小米9,华为Meta30 等。
注3: 当前Paddle-Lite只支持ARMv8架构的FP16运算。
针对 Lite 开发者的编译命令(有单元测试,编译产物)¶
注:调用./lite/tools/ci_build.sh
执行编译,该命令会编译armv7和armv8的预测库。虽然有编译产物,但因编译单元测试,编译产物包体积可能较大,生产环境不推荐使用。
# 假设当前位于处于Lite源码根目录下
# 导入NDK_ROOT变量,注意检查您的安装目录若与本示例不同
export NDK_ROOT=/opt/android-ndk-r17c
# 删除上一次CMake自动生成的.h文件
rm ./lite/api/paddle_use_kernels.h
rm ./lite/api/paddle_use_ops.h
# 根据指定编译参数编译
./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--arm_lang=clang \
--build_extra=on \
--build_cv=on \
test
1.3 编译产物说明¶
编译产物位于build.lite.android.armv8.clang
下的lite
文件夹内。这里仅罗列关键产物:
api
: 包含了基于API接口和模型的各种可执行的单测文件tests
:该目录包含了多个层面的可执行的单测文件kernels
: 包含已支持OP的各种可执行的单测文件,如activation
OP单测;benchmark
: 提供便利化脚本用于convolution/pooling等算子性能的批量测试math
: 包含各类卷积算子如GEMM
、GEMV
等可执行的单测文件
.
|-- api
| |-- *.a
| |-- *.so
| |-- test_model_bin
| |-- test_mobilenetv1
| |-- test_mobilenetv1_int8
| |....
|-- kernel
| |-- apu
| |-- arm
| | |-- *.a(example:libconv_compute_arm.a, libmul_compute_arm.a etc.)
| |-- bm
| |-- cuda
| |-- host
| |....
|-- tests
| |-- api
| | |-- test_inception_v4_fp32_arm
| | |-- test_mobilenet_v1_int8_dygraph_arm
| | |-- test_nlp_lstm_int8_arm
| | |...
| |-- benchmark
| | |-- get_activation_latency
| | |-- get_batchnorm_latency
| | |-- get_conv_latency
| | |...
| |-- cv
| | |-- image_convert_test
| | |-- image_profiler_test
| |-- kernels
| | |-- test_kernel_activation_compute
| | |-- test_kernel_expand_as_compute
| | |-- test_kernel_group_norm_compute
| | |...
| |-- math
| | |-- conv_compute_test
| | |-- sgemm_compute_test
| | |-- sgemv_compute_test
| | |...
....
2. 运行示例¶
下面以android的环境为例,介绍3个示例,分别如何在手机上执行ARM CPU推理过程。
2.1 运行示例1: 编译产物demo示例和benchmark¶
需要提前用模型优化工具opt转好模型(下面假设已经转换好模型,且模型名为mobilenetv1_fp32.nb
)。
编译脚本为前文针对 Lite 用户的编译命令(无单元测试,有编译产物,适用于benchmark)。
注:产物demo需要用tiny_publish
或full_publish
方式编译才能获取。
#################################
# 假设当前位于build.xxx目录下 #
#################################
# prepare enviroment on phone
adb shell mkdir -p /data/local/tmp/arm_cpu/
# build demo
cd inference_lite_lib.android.armv8/demo/cxx/mobile_light/
make
cd -
# push executable binary, library to device
adb push inference_lite_lib.android.armv8/demo/cxx/mobile_light/mobilenetv1_light_api /data/local/tmp/arm_cpu/
adb shell chmod +x /data/local/tmp/arm_cpu/mobilenetv1_light_api
adb push inference_lite_lib.android.armv8/cxx/lib/libpaddle_light_api_shared.so /data/local/tmp/arm_cpu/
# push model with optimized(opt) to device
adb push ./mobilenetv1_fp32.nb /data/local/tmp/arm_cpu/
# run demo on device
adb shell "export LD_LIBRARY_PATH=/data/local/tmp/mobilenetv1_fp32/; \
/data/local/tmp/mobilenetv1_fp32/mobilenetv1_light_api \
/data/local/tmp/mobilenetv1_fp32/mobilenetv1_fp32.nb \
1,3,224,224 \
100 10 0 1 1 0"
# repeats=100, warmup=10
# power_mode=0 绑定大核, thread_num=1
# print_output=0 不打印模型输出 tensors 详细数据
注:如果要运行FP16模型,需要提前完成以下操作:
在编译预测库时,需要添加
with_arm82_fp16=ON
选项进行编译;OPT模型转换时,需要添加
--enable_fp16=1
选项,完成FP16模型转换推理执行过程同上
2.2 运行示例2: test_model_bin
单元测试¶
编译脚本为前文针对 Lite 开发者的编译命令(有单元测试,编译产物)。
运行文件准备
# 在/data/local/tmp目录下创建arm_cpu文件目录
adb shell mkdir -p /data/local/tmp/arm_cpu
# 将单元测试程序test_model_bin,推送到/data/local/tmp/arm_cpu目录下
adb push build.lite.android.armv8.clang/lite/api/test_model_bin /data/local/tmp/arm_cpu
执行推理过程
# 将转换好的模型文件推送到/data/local/tmp/arm_cpu目录下
adb push caffe_mv1_fp32.nb /data/local/tmp/arm_cpu/
adb shell chmod +x /data/local/tmp/arm_cpu/test_mobilenetv1
adb shell "export GLOG_v=1; \
/data/local/tmp/arm_cpu/test_mobilenetv1 \
--use_optimize_nb=1 \
--model_dir=/data/local/tmp/arm_cpu/caffe_mv1_fp32 \
--input_shape=1,3,224,224 \
--warmup=10 \
--repeats=100"
FP16 模型推理过程
单测编译的时候,需要添加
--build_arm82_fp16=ON
选项,即:
export NDK_ROOT=/disk/android-ndk-r20b #ndk_version > 19
./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--build_extra=on \
--build_cv=on \
--arm_lang=clang \
--build_arm82_fp16=ON \
test
模型在OPT转换的时候,需要添加
--enable_fp16=1
选项,完成FP16模型转换,即:
./build.opt/lite/api/opt \
--optimize_out_type=naive_buffer \
--enable_fp16=1 \
--optimize_out caffe_mv1_fp16 \
--model_dir ./caffe_mv1
执行
推送OPT转换后的模型至手机, 运行时请将
use_optimize_nb
设置为1
# 将转换好的模型文件推送到/data/local/tmp/arm_cpu目录下
adb push caffe_mv1_fp16.nb /data/local/tmp/arm_cpu/
adb shell chmod +x /data/local/tmp/arm_cpu/test_mobilenetv1
adb shell "\
/data/local/tmp/arm_cpu/test_mobilenetv1 \
--use_optimize_nb=1 \
--model_dir=/data/local/tmp/arm_cpu/caffe_mv1_fp16 \
--input_shape=1,3,224,224 \
--warmup=10 \
--repeats=100"
推送原始模型至手机, 运行时请将
use_optimize_nb
设置为0,use_fp16
设置为1;(use_fp16
默认为0)
# 将fluid 原始模型文件推送到/data/local/tmp/arm_cpu目录下
adb push caffe_mv1 /data/local/tmp/arm_cpu/
adb shell chmod +x /data/local/tmp/arm_cpu/test_mobilenetv1
adb shell "export GLOG_v=1; \
/data/local/tmp/arm_cpu/test_mobilenetv1 \
--use_optimize_nb=0 \
--use_fp16=1 \
--model_dir=/data/local/tmp/arm_cpu/caffe_mv1 \
--input_shape=1,3,224,224 \
--warmup=10 \
--repeats=100"
注:如果想输入真实数据,请将预处理好的输入数据用文本格式保存。在执行的时候加上--in_txt=./*.txt
选项即可
2.3 运行示例3: conv_compute_test 单元测试¶
编译脚本为前文针对 Lite 开发者的编译命令(有单元测试,编译产物)。
adb shell mkdir -p /data/local/tmp/arm_cpus
adb push build.lite.android.armv8.clang/lite/test/math/conv_compute_test /data/local/tmp/arm_cpu
adb shell chmod +x /data/local/tmp/arm_cpu/conv_compute_test
adb shell "export GLOG_v=4; \
/data/local/tmp/arm_cpu/conv_compute_test --basic_test=0" # basic_test 表示是否跑所有单测案例
# 如果想跑某个case的convolution单测:
adb shell "export GLOG_v=4; \
/data/local/tmp/arm_cpu/conv_compute_test --basic_test=0 --in_channel=3 \
--out_channel=32 --in_height=224 --in_width=224 --group=1 --kernel_h=3 --kernel_w=3 \
--stride_w=2 --stride_h=2 --pad_h0=1 --pad_h1=1 --pad_w0=1 --pad_w1=1 --flag_act=1 \
--flag_bias=0 --warmup=10 --repeats=100 --threads=1"
# 如果想跑GEMM单测:
adb shell "export GLOG_v=4; \
/data/local/tmp/arm_cpu/sgemm_compute_test --basic_test=0 --M=32 --N=128 --K=1024 \
--warmup=10 --repeats=100 --threads=1"