PaddleLite使用ARM CPU预测部署

Lite支持在Android/IOS/ARMLinux等移动端设备上运行高性能的CPU预测库,目前支持Ubuntu环境下armv8、armv7的交叉编译。

1. 编译

1.1 编译环境

  1. Docker 容器环境;

  2. Linux(推荐 Ubuntu 16.04)环境。

详见 源码编译指南-环境准备 章节。

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的各种可执行的单测文件,如activationOP单测;

    • benchmark: 提供便利化脚本用于convolution/pooling等算子性能的批量测试

    • math: 包含各类卷积算子如GEMMGEMV等可执行的单测文件

.
|-- 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_publishfull_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 模型推理过程

  1. 单测编译的时候,需要添加--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
  1. 模型在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
  1. 执行

  1. 推送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"
  1. 推送原始模型至手机, 运行时请将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"

3. 性能分析和精度分析

Android 平台下分析:

1. 开启性能分析,会打印出每个 op 耗时信息和汇总信息

./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--build_extra=on \
--build_cv=on \
--arm_lang=clang \
--with_profile=ON \
test

2. 开启精度分析,会打印出每个 op 输出数据的均值和标准差信息

# 开启性能分析,会打印出每个 op 耗时信息和汇总信息
./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--build_extra=on \
--build_cv=on \
--arm_lang=clang \
--with_profile=ON \
--with_precision_profile=ON \
test

详细输出信息的说明可查阅调试工具