Android NNAPI

Paddle Lite 已支持 Android NNAPI 的预测部署。 其接入原理是加载并分析 Paddle 模型,将 Paddle 算子先转为统一的 NNAdapter 标准算子,再通过 Android NNAPI 进行网络构建,在线生成并执行模型。 需要注意由于不同 SoC 芯片对 Android NNAPI 的支持程度不同,其底层各个计算 IP(CPU、GPU、DSP、NPU 等)对 Android NNAPI 的支持也不同,性能也会有较大区别。 用户可以在运行日志中,搜索关键字『Available devices:』来查看当前 SoC 对 Android NNAPI 的支持情况。

支持现状

已支持的设备

  • 所有支持 Android NNAPI 的终端设备,系统版本 Android 8.1(Oreo) 及以上,Android SDK version 需在 27 及以上。

已支持的 Paddle 模型

性能

  • 测试环境

    • 编译环境

      • Ubuntu 16.04,NDK-r17c with GCC for Android armeabi-v7a

    • 硬件环境

      • 小米10

        • 高通 骁龙865

        • Android SDK version 29

      • 华为P40pro

        • 华为 Kirin990-5G

        • Android SDK version 29

      • 紫光展锐 T820 参考设计

        • 紫光展锐 T820

        • Android SDK version 33

  • 测试方法

    • warmup=1,repeats=5,统计平均时间,单位是 ms

    • 线程数为 1,paddle::lite_api::PowerMode CPU_POWER_MODE 设置为 paddle::lite_api::PowerMode::LITE_POWER_HIGH

    • 分类模型的输入图像维度是{1, 3, 224, 224},检测模型的维度是{1, 3, 300, 300}

    • 华为 Kirin NPU 对 Android NNAPI 的支持程度较高,但是由于其量化方式与 Paddle 有较大出入,量化模型无法发挥 NPU 加速特性,所以 fp32 模型性能较好

    • 高通 骁龙系列芯片(855 以后),DSP,GPU 等 IP 支持 Android NNAPI,但其 HTA|HTP 暂不支持 Android NNAPI

    • 紫光展锐的 T820/T760 的 VDSP,GPU,NPU 等 IP 支持 Android NNAPI,其中 NPU 对 Android NNAPI 量化模型支持程度较高,所以 int8 模型的性能较好

    • 不同 SoC 对 Android NNAPI 的支持程度不同,如下举例华为 Kirin990-5G 、高通 骁龙865 和紫光展锐 T820

  • 测试结果

模型
骁龙865(ms) Kirin990-5G(ms) 紫光展锐T820(ms)
mobilenet_v1_int8_224_per_layer 10.109 47.903 7.371
resnet50_int8_224_per_layer 18.622 354.566 12.156
ssd_mobilenet_v1_relu_voc_int8_300_per_layer 23.214 68.312 17.368
mobilenet_v1_fp32_224 12.250 3.563 20.699
resnet50_fp32_224 44.903 8.762 95.993
ssd_mobilenet_v1_relu_voc_fp32_300 23.112 8.647 36.312

已支持(或部分支持)的 Paddle 算子

您可以查阅 NNAdapter 算子支持列表获得各算子在不同新硬件上的最新支持信息。

参考示例演示

测试设备( 小米10)

准备设备环境

  • 进入手机shell 输入命令 getprop ro.build.version.sdk 查看 Android SDK version,需在 27 及以上

准备交叉编译环境

运行图像分类示例程序

  • 下载 Paddle Lite 通用示例程序 PaddleLite-generic-demo.tar.gz ,解压后目录主体结构如下:

      - PaddleLite-generic-demo
        - image_classification_demo
          - assets
            - configs
              - imagenet_224.txt # config 文件
              - synset_words.txt # 1000 分类 label 文件
            - datasets
              - test # dataset
                - inputs
                  - tabby_cat.jpg # 输入图片
                - outputs
                  - tabby_cat.jpg # 输出图片
                - list.txt # 图片清单
          - shell
            - CMakeLists.txt # 示例程序 CMake 脚本
            - build.android.arm64-v8a # arm64-v8a 编译工作目录
              - demo # 已编译好的,适用于 amd64-v8a 的示例程序
            - build.android.armeabi-v7a # armeabi-v7a 编译工作目录
              - demo # 已编译好的,适用于 arm64 的示例程序
              ...
            ...
            - demo.cc # 示例程序源码
            - build.sh # 示例程序编译脚本
            - run_with_adb.sh # 示例程序 adb 运行脚本
            — run_with_ssh.sh # 示例程序 ssh 运行脚本
            - run.sh # 示例程序运行脚本
        - libs
          - PaddleLite
            - android
              - armeabi-v7a
                - include
                - lib
                  - android_nnapi # NNAdapter 运行时库、Android NNAPI device HAL 库
                    - libnnadapter.so # NNAdapter 运行时库
                    - libandroid_nnapi.so # NNAdapter device HAL 库
                - libpaddle_full_api_shared.so # 预编译 Paddle Lite full api 库
                - libpaddle_light_api_shared.so # 预编译 Paddle Lite light api 库
              ...
          - OpenCV # OpenCV 预编译库
    
  • Android shell 端的示例程序

    • 按照以下命令分别运行转换后的 ARM CPU 模型和 Android NNAPI 模型,比较它们的性能和结果;

      注意:
      1`run_with_adb.sh` 不能在 Docker 环境执行,否则可能无法找到设备,也不能在设备上运行。
      2`run_with_ssh.sh` 不能在设备上运行,且执行前需要配置目标设备的 IP 地址、SSH 账号和密码。
      3`build.sh` 根据入参生成针对不同操作系统、体系结构的二进制程序,需查阅注释信息配置正确的参数值。
      4`run_with_adb.sh` 入参包括模型名称、操作系统、体系结构、目标设备、设备序列号等,需查阅注释信息配置正确的参数值。
      5`run_with_ssh.sh` 入参包括模型名称、操作系统、体系结构、目标设备、ip地址、用户名、用户密码等,需查阅注释信息配置正确的参数值。
      6)下述命令行示例中涉及的设备序列号等均为示例环境,请用户根据自身实际设备环境修改。
      在 ARM CPU 上运行 mobilenetv1 全量化模型
      $ cd PaddleLite-generic-demo/image_classification_demo/shell
      $ ./run_with_adb.sh mobilenet_v1_int8_224_per_layer imagenet_224.txt test android armeabi-v7a cpu d3869b25
      
        Top1 Egyptian cat - 0.502124
        Top2 tabby, tabby cat - 0.413927
        Top3 tiger cat - 0.071703
        Top4 lynx, catamount - 0.008436
        Top5 cougar, puma, catamount, mountain lion, painter, panther, Felis concolor - 0.000563
        Preprocess time: 4.948000 ms, avg 4.948000 ms, max 4.948000 ms, min 4.948000 ms
        Prediction time: 34.962000 ms, avg 34.962000 ms, max 34.962000 ms, min 34.962000 ms
        Postprocess time: 4.715000 ms, avg 4.715000 ms, max 4.715000 ms, min 4.715000 ms
      
      基于 Android NNAPI 上运行 mobilenetv1 全量化模型
      $ cd PaddleLite-generic-demo/image_classification_demo/shell
      $ ./run_with_adb.sh mobilenet_v1_int8_224_per_layer imagenet_224.txt test android armeabi-v7a android_nnapi d3869b25
      
        Top1 Egyptian cat - 0.497230
        Top2 tabby, tabby cat - 0.403634
        Top3 tiger cat - 0.076047
        Top4 lynx, catamount - 0.005850
        Top5 great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias - 0.000000
        Preprocess time: 5.271000 ms, avg 5.271000 ms, max 5.271000 ms, min 5.271000 ms
        Prediction time: 6.877000 ms, avg 6.877000 ms, max 6.877000 ms, min 6.877000 ms
        Postprocess time: 5.083000 ms, avg 5.083000 ms, max 5.083000 ms, min 5.083000 ms
      
  • 如果需要更改测试图片,可将图片拷贝到 PaddleLite-generic-demo/image_classification_demo/assets/datasets/test/inputs 目录下,同时将图片文件名添加到 PaddleLite-generic-demo/image_classification_demo/assets/datasets/test/list.txt 中;

  • 重新编译示例程序:

    注意:
    1)请根据 `buid.sh` 配置正确的参数值。
    2)需在 Docker 环境中编译。
    
    For arm64-v8a
    $ ./build.sh android arm64-v8a
    
    For armeabi-v7a
    $ ./build.sh android armeabi-v7a
    

更新模型

  • 通过 Paddle 训练,或 X2Paddle 转换得到 MobileNetv1 foat32 模型 mobilenet_v1_fp32_224_fluid

  • 如果需要使用量化模型,则参考模型量化使用 PaddleSlim 对 float32 模型进行量化(注意:由于 DSP 只支持量化 OP,在启动量化脚本时请注意相关参数的设置),最终得到全量化MobileNetV1 模型 mobilenet_v1_int8_224_per_layer

  • 参考模型转化方法,利用 opt 工具转换生成 Android NNAPI 模型,仅需要将 valid_targets 设置为 andriod_nnapi, arm 即可。

    注意:
    1)PaddleLite-generic-demo 中已经包含了类似 opt 工具优化生成 nb 模型的功能。
    
    $ cd PaddleLite-generic-demo/image_classification_demo/assets/models
    $ ./opt --model_dir=mobilenet_v1_int8_224_per_layer \
        --optimize_out_type=naive_buffer \
        --optimize_out=opt_model \
        --valid_targets=android_nnapi,arm
    
  • 注意:opt 生成的模型只是标记了 Android NNAPI 支持的 Paddle 算子,并没有真正生成 Android NNAPI 模型,只有在执行时才会将标记的 Paddle 算子转成 Android NNAPI 调用实现组网,最终生成并执行模型。

更新支持 Android NNAPI 的 Paddle Lite 库

  • 下载 Paddle Lite 源码;

    $ git clone https://github.com/PaddlePaddle/Paddle-Lite.git
    $ cd Paddle-Lite
    $ git checkout <release-version-tag>
    
  • 编译并生成 PaddleLite+Android NNAPI for armv8 and armv7 的部署库

    • For armv8

      • tiny_publish 编译方式

        $ ./lite/tools/build_android.sh --android_stl=c++_shared --with_extra=ON --with_cv=ON --with_log=ON --with_exception=ON --with_nnadapter=ON --nnadapter_with_android_nnapi=ON
        
      • full_publish 编译方式

        $ ./lite/tools/build_android.sh --android_stl=c++_shared --with_extra=ON --with_cv=ON --with_log=ON --with_exception=ON --with_nnadapter=ON --nnadapter_with_android_nnapi=ON full_publish
        
      • 替换头文件和库

        替换 include 目录
        $ cp -rf build.lite.android.armv8.gcc/inference_lite_lib.android.armv8.nnadapter/cxx/include/ PaddleLite-generic-demo/libs/PaddleLite/android/arm64-v8a/include/
        
        替换 NNAdapter 运行时库
        $ cp -rf build.lite.android.armv8.gcc/inference_lite_lib.android.armv8.nnadapter/cxx/lib/libnnadapter.so PaddleLite-generic-demo/libs/PaddleLite/android/arm64-v8a/lib/android_nnapi/
        
        替换 NNAdapter device HAL 库
        $ cp -rf build.lite.android.armv8.gcc/inference_lite_lib.android.armv8.nnadapter/cxx/lib/libandroid_nnapi.so PaddleLite-generic-demo/libs/PaddleLite/android/arm64-v8a/lib/android_nnapi/
        
        替换 libpaddle_light_api_shared.so
        $ cp -rf build.lite.android.armv8.gcc/inference_lite_lib.android.armv8.nnadapter/cxx/lib/libpaddle_light_api_shared.so PaddleLite-generic-demo/libs/PaddleLite/android/arm64-v8a/lib/
        
        替换 libpaddle_full_api_shared.so (仅在 full_publish 编译方式下)
        $ cp -rf build.lite.android.armv8.gcc/inference_lite_lib.android.armv8.nnadapter/cxx/lib/libpaddle_full_api_shared.so PaddleLite-generic-demo/libs/PaddleLite/android/arm64-v8a/lib/
        
    • For armv7

      • tiny_publis h编译方式

        $ ./lite/tools/build_android.sh --arch=armv7 --toolchain=clang --android_stl=c++_shared --with_extra=ON --with_cv=ON --with_log=ON --with_exception=ON --with_nnadapter=ON --nnadapter_with_android_nnapi=ON
        
      • full_publish 编译方式

        $ ./lite/tools/build_android.sh --arch=armv7 --toolchain=clang --android_stl=c++_shared --with_extra=ON --with_cv=ON --with_log=ON --with_exception=ON --with_nnadapter=ON --nnadapter_with_android_nnapi=ON full_publish
        
      • 替换头文件和库

        替换 include 目录
        $ cp -rf build.lite.android.armv7.gcc/inference_lite_lib.android.armv7.nnadapter/cxx/include/ PaddleLite-generic-demo/libs/PaddleLite/android/armeabi-v7a/include/
        
        替换 NNAdapter 运行时库
        $ cp -rf build.lite.android.armv7.gcc/inference_lite_lib.android.armv7.nnadapter/cxx/lib/libnnadapter.so PaddleLite-generic-demo/libs/PaddleLite/android/armeabi-v7a/lib/android_nnapi/
        
        替换 NNAdapter device HAL 库
        $ cp -rf build.lite.android.armv7.gcc/inference_lite_lib.android.armv7.nnadapter/cxx/lib/libandroid_nnapi.so PaddleLite-generic-demo/libs/PaddleLite/android/armeabi-v7a/lib/android_nnapi/
        
        替换 libpaddle_light_api_shared.so
        $ cp -rf build.lite.android.armv7.gcc/inference_lite_lib.android.armv7.nnadapter/cxx/lib/libpaddle_light_api_shared.so PaddleLite-generic-demo/libs/PaddleLite/android/armeabi-v7a/lib/
        
        替换 libpaddle_full_api_shared.so (仅在 full_publish 编译方式下)
        $ cp -rf build.lite.android.armv7.gcc/inference_lite_lib.android.armv7.nnadapter/cxx/lib/libpaddle_full_api_shared.so PaddleLite-generic-demo/libs/PaddleLite/android/armeabi-v7a/lib/
        
  • 替换头文件后需要重新编译示例程序

其它说明

  • 不同 SoC 芯片对 Android NNAPI 的支持差异较大,目前移动端芯片支持较好,边缘端、车载等需要根据具体芯片情况确认。