华为麒麟 NPU 部署示例

Paddle Lite 是首款支持华为自研达芬奇架构 NPU(Kirin 810/990 SoC 搭载的 NPU)的预测框架。 原理是在线分析 Paddle 模型,首先将 Paddle 算子转成 NNAdapter 标准算子,其次再转换为 HiAI IR,最后调用HiAI IR/Builder/Runtime APIs 生成并执行 HiAI 模型。

支持现状

已支持的芯片

  • Kirin 810/820/985/990/990 5G/9000E/9000

已支持的设备

  • Kirin 9000:HUAWEI Mate 40pro 系列

  • Kirin 9000E:HUAWEI Mate 40 系列

  • Kirin 990 5G:HUAWEI Mate 30pro 系列,P40pro 系列

  • Kirin 990:HUAWEI Mate 30 系列,荣耀 V20 系列,nova 6 系列,P40 系列,Mate Xs

  • Kirin 985:HUAWEI nova 7 5G,nova 7 Pro 5G,荣耀 30

  • Kirin 820:HUAWEI nova 7 SE 5G,荣耀 30S

  • Kirin 810:HUAWEI nova 5 系列,nova 6 SE,荣耀 9X 系列,荣耀 Play4T Pro

已验证支持的 Paddle 模型

性能

  • 测试环境

    • 编译环境

      • Ubuntu 16.04,NDK-r17c with GCC for Android arm64-v8a

      • HIAI DDK 版本:v510

    • 硬件环境

      • Kirin 810

        • HUAWEI Nova 5,Kirin 810

        • CPU:2 x Cortex A76 2.27GHz + 6 x Cortex A55 1.88GHz

        • NPU:Da Vinci 架构,1 x Ascend D100 Lite

      • Kirin 990

        • HUAWEI Mate 30,Kirin 990

        • CPU:2 x Cortex-A76 Based 2.86 GHz + 2 x Cortex-A76 Based 2.09 GHz + 4 x Cortex-A55 1.86 GHz

        • NPU:Da Vinci 架构,1 x Ascend Lite + 1 x Ascend Tiny

      • Kirin 990 5G

        • HUAWEI P40pro,Kirin 990 5G

        • CPU:2 x Cortex-A76 Based 2.86GHz + 2 x Cortex-A76 Based 2.36GHz + 4 x Cortex-A55 1.95GHz

        • NPU:Da Vinci 架构,2 x Ascend Lite + 1 x Ascend Tiny

  • 测试方法

    • 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 810 Kirin 990 Kirin 990 5G
CPU(ms) NPU(ms) CPU(ms) NPU(ms) CPU(ms) NPU(ms)
mobilenet_v1_fp32_224 38.358801 5.903400 30.234800 3.352000 31.567600 2.992200
resnet50_fp32_224 224.719998 18.087400 176.660199 9.825800 186.572998 7.645400
ssd_mobilenet_v1_relu_voc_fp32_300 80.059001 30.157600 63.044600 22.901200 68.458200 21.399200

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

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

不经过 NNAdapter 标准算子转换,而是直接将 Paddle 算子转换成 HiAI IR 的方案可点击链接

参考示例演示

测试设备(HUAWEI Mate30 5G)

huwei_mate30_5g

准备设备环境

  • 由于 HiAI DDK 可能依赖特定版本的ROM,建议用户更新至最新版 EMUI 系统,具体参考华为官方手机升级指南

准备交叉编译环境

  • 为了保证编译环境一致,建议参考编译环境准备中的 Docker 开发环境进行配置。

运行图像分类示例程序

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

      - PaddleLite-generic-demo
        - image_classification_demo
          - assets
            - images
              - tabby_cat.jpg # 测试图片
              - tabby_cat.raw # 经过 convert_to_raw_image.py 处理后的 RGB Raw 图像
            - labels
              - synset_words.txt # 1000 分类 label 文件
            - models
              - mobilenet_v1_fp32_224 # Paddle non-combined 格式的 mobilenet_v1 float32 模型
                - __model__ # Paddle fluid 模型组网文件,可使用 netron 查看网络结构
                - conv1_bn_mean # Paddle fluid 模型参数文件
                - subgraph_partition_config_file.txt # 自定义子图分割配置文件
                ...
          - shell
            - CMakeLists.txt # 示例程序 CMake 脚本
            - build.android.arm64-v8a # arm64-v8a 编译工作目录
              - image_classification_demo # 已编译好的,适用于 amd64-v8a 的示例程序
            - build.android.armeabi-v7a # armeabi-v7a 编译工作目录
              - image_classification_demo # 已编译好的,适用于 arm64 的示例程序
              ...
            ...
            - image_classification_demo.cc # 示例程序源码
            - build.sh # 示例程序编译脚本
            - run_with_adb.sh # 示例程序 adb 运行脚本
        - libs
          - PaddleLite
            - android
              - arm64-v8a
                - include # Paddle Lite 头文件
                - lib
                  - huawei_kirin_npu # 华为麒麟 NPU HiAI DDK、NNAdapter 运行时库、device HAL 库
                    - libnnadapter.so # NNAdapter 运行时库
                    - libhuawei_kirin_npu.so # NNAdapter device HAL 库
                    - libhiai.so # HiAI DDK
                    ...
                  - libpaddle_full_api_shared.so # 预编译 Paddle Lite full api 库
                  - libpaddle_light_api_shared.so # 预编译 Paddle Lite light api 库
                  - libc++_shared.so
              - armeabi-v7a
              	- include
                - lib
              ...
          - OpenCV # OpenCV 预编译库
        - ssd_detection_demo # 基于 ssd 的目标检测示例程序
    
  • Android shell 端的示例程序

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

    1)由于 HiAI 的限制,需要 root 权限才能执行 shell 示例程序。
    2`run_with_adb.sh` 只能在连接设备的系统上运行,不能在 Docker 环境执行(可能无法找到设备),也不能在设备上运行。
    3`build.sh` 需要在 Docker 环境中执行,否则,需要将 `build.sh` 的 ANDROID_NDK 修改为当前环境下的 NDK 路径。
    4`build.sh` 根据入参生成针对不同操作系统、体系结构的二进制程序,需查阅注释信息配置正确的参数值。
    5`run_with_adb.sh` 入参包括模型名称、操作系统、体系结构、目标设备、设备序列号等,需查阅注释信息配置正确的参数值。
    6)对于需要使能自定义子图分割文件的模型,请注意将 `run_with_adb.sh` 中 line12 行首 '#' 删除。
    
    运行适用于 ARM CPU 的 mobilenetv1 模型
    $ cd PaddleLite-generic-demo/image_classification_demo/shell
    $ ./run_with_adb.sh mobilenet_v1_fp32_224 android arm64-v8a
      ...
    iter 0 cost: 30.349001 ms
    iter 1 cost: 30.517000 ms
    iter 2 cost: 30.040001 ms
    iter 3 cost: 30.358000 ms
    iter 4 cost: 30.187000 ms
    warmup: 1 repeat: 5, average: 30.290200 ms, max: 30.517000 ms, min: 30.040001 ms
    results: 3
    Top0  tabby, tabby cat - 0.529131
    Top1  Egyptian cat - 0.419681
    Top2  tiger cat - 0.045173
    Preprocess time: 0.576000 ms
    Prediction time: 30.290200 ms
    Postprocess time: 0.100000 ms
    
    运行适用于华为 Kirin NPU 的 mobilenetv1 模型
    $ cd PaddleLite-generic-demo/image_classification_demo/shell
    $ ./run_with_adb.sh mobilenet_v1_fp32_224 android arm64-v8a huawei_kirin_npu
      ...
    iter 0 cost: 3.503000 ms
    iter 1 cost: 3.406000 ms
    iter 2 cost: 3.401000 ms
    iter 3 cost: 3.402000 ms
    iter 4 cost: 3.423000 ms
    warmup: 1 repeat: 5, average: 3.427000 ms, max: 3.503000 ms, min: 3.401000 ms
    results: 3
    Top0  tabby, tabby cat - 0.534180
    Top1  Egyptian cat - 0.416016
    Top2  tiger cat - 0.044525
    Preprocess time: 0.572000 ms
    Prediction time: 3.427000 ms
    Postprocess time: 0.099000 ms
    
    • 如果需要更改测试图片,可将图片拷贝到 PaddleLite-generic-demo/image_classification_demo/assets/images 目录下,然后调用 convert_to_raw_image.py 生成相应的 RGB Raw 图像,最后修改 run_with_adb.sh 的 IMAGE_NAME 变量即可;

    • 重新编译示例程序:

    注意:
    1)请根据 `buid.sh` 配置正确的参数值。
    2)需在 `Docker` 环境中编译。
    
    # 对于 arm64-v8a
    ./build.sh android arm64-v8a
    
    # 对于 armeabi-v7a
    ./build.sh android armeabi-v7a
    
  • 注意:opt 生成的模型只是标记了华为 Kirin NPU 支持的 Paddle 算子,并没有真正生成华为 Kirin NPU 模型,只有在执行时才会将标记的 Paddle 算子转成 HiAI IR 并组网得到 HiAI IRGraph,然后生成并执行华为 Kirin NPU 模型(具体原理请参考 Pull Request#2576);

  • 不同模型,不同型号(ROM 版本)的华为手机,在执行阶段,由于某些 Paddle 算子无法完全转成 HiAI IR,或目标手机的 HiAI 版本过低等原因,可能导致 HiAI 模型无法成功生成,在这种情况下,Paddle Lite 会调用 ARM CPU 版算子进行运算完成整个预测任务。

更新支持华为 Kirin NPU 的 Paddle Lite 库

  • 下载 Paddle Lite 源码和最新版 HiAI DDK

    $ git clone https://github.com/PaddlePaddle/Paddle-Lite.git
    $ cd Paddle-Lite
    $ git checkout <release-version-tag>
    $ wget https://paddlelite-demo.bj.bcebos.com/devices/huawei/kirin/hiai_ddk_lib_510.tar.gz
    $ tar -xvf hiai_ddk_lib_510.tar.gz
    
  • 编译并生成 PaddleLite+NNAdapter+HuaweiKirinNPU for armv8 and armv7 的部署库

    • For armv8

      • tiny_publish 编译

        $ ./lite/tools/build_android.sh --toolchain=clang --android_stl=c++_shared --with_extra=ON --with_log=ON --with_nnadapter=ON --nnadapter_with_huawei_kirin_npu=ON --nnadapter_huawei_kirin_npu_sdk_root=$(pwd)/hiai_ddk_lib_510
        
      • full_publish 编译

        $ ./lite/tools/build_android.sh --toolchain=clang --android_stl=c++_shared --with_extra=ON --with_log=ON --with_nnadapter=ON --nnadapter_with_huawei_kirin_npu=ON --nnadapter_huawei_kirin_npu_sdk_root=$(pwd)/hiai_ddk_lib_510 full_publish
        
      • 替换头文件和库

        # 替换 include 目录
        $ cp -rf build.lite.android.armv8.clang/inference_lite_lib.android.armv8.nnadapter/cxx/include/ PaddleLite-generic-demo/libs/PaddleLite/android/arm64-v8a/include/
        # 替换 NNAdapter 运行时库
        $ cp -rf build.lite.android.armv8.clang/inference_lite_lib.android.armv8.nnadapter/cxx/lib/libnnadapter.so PaddleLite-generic-demo/libs/PaddleLite/android/arm64-v8a/lib/huawei_kirin_npu/
        # 替换 NNAdapter device HAL 库
        $ cp -rf build.lite.android.armv8.clang/inference_lite_lib.android.armv8.nnadapter/cxx/lib/libhuawei_kirin_npu.so PaddleLite-generic-demo/libs/PaddleLite/android/arm64-v8a/lib/huawei_kirin_npu/
        # 替换 libpaddle_light_api_shared.so
        $ cp -rf build.lite.android.armv8.clang/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.clang/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_publish 编译

        $ ./lite/tools/build_android.sh --arch=armv7 --toolchain=clang --android_stl=c++_shared --with_extra=ON --with_log=ON --with_nnadapter=ON --nnadapter_with_huawei_kirin_npu=ON --nnadapter_huawei_kirin_npu_sdk_root=$(pwd)/hiai_ddk_lib_510
        
      • full_publish 编译

        $ ./lite/tools/build_android.sh --arch=armv7 --toolchain=clang --android_stl=c++_shared --with_extra=ON --with_log=ON --with_nnadapter=ON --nnadapter_with_huawei_kirin_npu=ON --nnadapter_huawei_kirin_npu_sdk_root=$(pwd)/hiai_ddk_lib_510 full_publish
        
      • 替换头文件和库

        # 替换 include 目录
        $ cp -rf build.lite.android.armv7.clang/inference_lite_lib.android.armv7.nnadapter/cxx/include/ PaddleLite-generic-demo/libs/PaddleLite/android/armeabi-v7a/include/
        # 替换 NNAdapter 运行时库
        $ cp -rf build.lite.android.armv7.clang/inference_lite_lib.android.armv7.nnadapter/cxx/lib/libnnadapter.so PaddleLite-generic-demo/libs/PaddleLite/android/armeabi-v7a/lib/huawei_kirin_npu/
        # 替换 NNAdapter device HAL 库
        $ cp -rf build.lite.android.armv7.clang/inference_lite_lib.android.armv7.nnadapter/cxx/lib/libhuawei_kirin_npu.so PaddleLite-generic-demo/libs/PaddleLite/android/armeabi-v7a/lib/huawei_kirin_npu/
        # 替换 libpaddle_light_api_shared.so
        $ cp -rf build.lite.android.armv7.clang/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.clang/inference_lite_lib.android.armv7.nnadapter/cxx/lib/libpaddle_full_api_shared.so PaddleLite-generic-demo/libs/PaddleLite/android/armeabi-v7a/lib/
        

        备注:由于 HiAI DDK 的 so 库均基于 c++_shared 构建,建议将 android stl 设置为 c++_shared,更多选项还可以通过 ./lite/tools/build_android.sh help 查看。

  • 替换头文件后需要重新编译示例程序

Paddle Lite 是如何支持华为 Kirin NPU的?

  • Paddle Lite 是如何加载 Paddle 模型并执行一次推理的?

    • 如下图左半部分所示,Paddle 模型的读取和执行,经历了 Paddle 推理模型文件的加载和解析、计算图的转化、图分析和优化、运行时程序的生成和执行等步骤:

      how_to_intergrate_hiai_and_nnadapter_to_paddlelite

    • Paddle 推理模型文件的加载和解析:基于 ProtoBuf 协议对 Paddle 推理模型文件进行反序列化,解析生成网络结构(描述算子和张量的关系)和参数信息(包括算子属性和权重张量);

    • 计算图的转化:为了更好的描述网络拓扑结构和方便后续的优化,依据算子的输入、出张量关系,构建一个由算子节点、张量节点组成的有向无环图;

    • 图分析和优化:由一些列 pass(优化器)组成,pass 是用于描述一个计算图优化生成另一个计算图的过程;例如 conv2d_bn_fuse_pass,它用于将模型中每一个 conv2dbatch_norm 相连的算子对融合成一个 conv2d 算子以便获得性能上的提升;

    • 运行时程序的生成和执行:按照拓扑顺序遍历最终优化后的计算图,生成算子 kernel 列表,依次执行每一个算子 kernel 后即完成一次模型的推理。

  • Paddle Lite 是如何支持华为 NPU 呢?

    • 为了支持华为 Kirin NPU,我们额外增加了(如上图标黄的区域):NNAdapter subgraph detection pass、NNAdapter subgraph op kernel 和 Paddle2NNAdapter converters。其中 NNAdapter subgraph detection pass 是后续自定义子图划分涉及的关键步骤;

    • NNAdapter subgraph detection pass:该 pass 的作用是遍历计算图中所有的算子节点,标记能够转成 NNAdapter+HiAI 算子的节点,然后通过图分割算法,将那些支持转为 HiAI IR 的、相邻的算子节点融合成一个subgraph(子图)算子节点(需要注意的是,这个阶段算子节点并没有真正转为 HiAI IR,更没有生成 HiAI 模型);

    • NNAdapter subgraph op kernel:根据 NNAdapter subgraph detection pass 的分割结果,在生成的算子 kernel 列表中,可能存在多个 subgraph 算子 kernel ;每个 subgraph 算子 kernel,都会将它所包裹的、能够转成 NNAdapter+HiAI 算子的 Paddle 算子,如上图右半部所示,依次调用对应的 converter,组网生成一个 NNAdapter+HiAI model,最终,调用 HiAI Runtime APIs 生成并执行华为 Kirin NPU 模型;

    • Paddle2NNAdapter converters:Paddle 算子/张量转 NNAdapter+HiAI 算子的桥接器,其目的是将 Paddle 算子、输入、输出张量最终转为 HiAI 组网 IR 和常量张量。

编写配置文件完成自定义子图分割,生成华为 Kirin NPU 与 ARM CPU 的异构模型

  • 为什么需要进行手动子图划分?如果模型中存在不支持转 HiAI IR 的算子,NNAdapter subgraph detection pass 会在没有人工干预的情况下,可能将计算图分割为许多小的子图,而出现如下问题:

    • 过多的子图会产生频繁的 CPU<->NPU 数据传输和 NPU 任务调度,影响整体性能;

    • 由于华为 Kirin NPU 模型暂时不支持 dynamic shape,因此,如果模型中存在输入和输出不定长的算子(例如一些检测类算子,NLP 类算子),在模型推理过程中,可能会因输入、输出 shape 变化而不断生成 HiAI 模型,从而导致性能变差,更有可能使得 HiAI 模型生成失败。

    • Kirin NPU HiAI 内部存在少量 Bug,会导致 HiAI 模型生成失败或者错误的子图融合,最终导致模型推理失败或错误。

  • 实现原理

    • NNAdapter subgraph detection pass 在执行分割任务前,通过读取指定配置文件的方式获得禁用华为 Kirin NPU 的算子列表,实现人为干预分割结果的目的。

  • 具体步骤(以 ssd_mobilenet_v1_relu_voc_fp32_300 目标检测示例程序为例)

    • 步骤1:查看 ssd_mobilenet_v1_relu_voc_fp32_300 的模型结构,具体是将 PaddleLite-generic-demo/ssd_detection_demo/assets/models/ssd_mobilenet_v1_relu_voc_fp32_300 目录下的 __model__ 拖入 Netron 页面即得到如下图所示的网络结构(部分):

      ssd_mobilenet_v1_relu_voc_fp32_300_netron

    • 步骤2:由于 Kirin HiAI 内部进行了错误的子图融合,本例中将强制设置两个 transpose2 算子运行在 ARM CPU 上。

      注意:
      1. 在 `run_with_adb.sh` 可看到'#SUBGRAPH_PARTITION_CONFIG_FILE=subgraph_partition_config_file.txt',
      删除 '#' 即可使能自定义子图分割配置文件。
      2. demo 中已经包含了类似 opt 工具优化生成 nb 模型的功能。
      
      # 如果不使用自定义子图分割配置文件,Kirin NPU 将得出错误的预测结果
      $ cd PaddleLite-generic-demo/ssd_detection_demo/shell
      $ ./run_with_adb.sh ssd_mobilenet_v1_relu_voc_fp32_300 android arm64-v8a huawei_kirin_npu
      ...
      iter 0 cost: 14.114000 ms
      iter 1 cost: 14.051000 ms
      iter 2 cost: 13.990000 ms
      iter 3 cost: 16.572001 ms
      iter 4 cost: 16.872000 ms
      warmup: 1 repeat: 5, average: 15.119800 ms, max: 16.872000 ms, min: 13.990000 ms
      results: 200
      ...
      [110] aeroplane - 1.000000 0.806027,0.228254,0.987190,0.485760
      [111] aeroplane - 1.000000 0.881030,0.244493,1.077540,0.411632
      [112] bicycle - 1.000000 -0.062173,-0.070797,0.224258,0.255616
      ...
      [198] bicycle - 1.000000 0.656274,0.198462,0.854739,0.389812
      [199] bicycle - 1.000000 0.704027,0.133047,0.802967,0.346298
      Preprocess time: 1.038000 ms
      Prediction time: 15.119800 ms
      Postprocess time: 0.127000 ms
      
      --------------------------------------------------------------------
      
      # 如果使用自定义子图分割配置文件,Kirin NPU 将得出正确的预测结果
      $ cd PaddleLite-generic-demo/ssd_detection_demo/shell
      $ vim run_with_adb.sh 将'#SUBGRAPH_PARTITION_CONFIG_FILE=subgraph_partition_config_file.txt'行首'#'删除
      $ ./run_with_adb.sh ssd_mobilenet_v1_relu_voc_fp32_300 android arm64-v8a huawei_kirin_npu
      ...
      iter 0 cost: 23.389999 ms
      iter 1 cost: 23.167999 ms
      iter 2 cost: 23.010000 ms
      iter 3 cost: 23.030001 ms
      iter 4 cost: 23.152000 ms
      warmup: 1 repeat: 5, average: 23.150000 ms, max: 23.389999 ms, min: 23.010000 ms
      results: 3
      [0] bicycle - 0.998047 0.149730,0.234041,0.731353,0.802842
      [1] car - 0.947266 0.600478,0.132399,0.900813,0.300571
      [2] dog - 0.991211 0.166347,0.257502,0.434295,0.923455
      Preprocess time: 1.078000 ms
      Prediction time: 23.150000 ms
      Postprocess time: 0.007000 ms
      
    • 步骤3:如果直接使用 opt 工具生成华为 Kirin NPU 模型,会发现整个网络被分割成 1 个子图(即 1 个 subgraph op),它们都将运行在华为 Kirin NPU 上;

      注意:
      1)opt 工具日志中包含各个算子的详细信息。
      2)为了方便查看优化后的模型,opt 命令将 `optimize_out_type` 参数设置为 protobuf,执行成功后将opt_model 目录下的 `model` 文件复制为 `__model__` 并拖入 Netron 页面进行可视化。
      
      $ cd PaddleLite-generic-demo/ssd_detection_demo/assets/models
      $ GLOG_v=5 ./opt --model_dir=./ssd_mobilenet_v1_relu_voc_fp32_300 \
          --optimize_out_type=protobuf \
          --optimize_out=opt_model \
          --valid_targets=huawei_kirin_npu,arm
      
      Loading topology data from ./ssd_mobilenet_v1_relu_voc_fp32_300/__model__
      Loading non-combined params data from ./ssd_mobilenet_v1_relu_voc_fp32_300
      1. Model is successfully loaded!
      subgraph clusters: 1
      digraph G {
      node_1150[label="batch_norm_0.tmp_3"]
      node_1154[label="batch_norm_1.tmp_3"]
      node_1190[label="batch_norm_10.tmp_3"]
      node_1194[label="batch_norm_11.tmp_3"]
      ...
      node_1426->node_1427
      node_1427->node_1428
      node_1428->node_1429
      } // end G
      subgraph operators:
      feed:feed:image
      conv2d:image,conv1_weights,conv1_bn_offset:batch_norm_0.tmp_3
      depthwise_conv2d:batch_norm_0.tmp_3,conv2_1_dw_weights,conv2_1_dw_bn_offset:batch_norm_1.tmp_3
      conv2d:batch_norm_1.tmp_3,conv2_1_sep_weights,conv2_1_sep_bn_offset:batch_norm_2.tmp_3
      ...
      box_coder:concat_0.tmp_0,concat_1.tmp_0,reshape2_0.tmp_0:box_coder_0.tmp_0
      multiclass_nms:box_coder_0.tmp_0,transpose_12.tmp_0:save_infer_model/scale_0.tmp_0
      fetch:save_infer_model/scale_0.tmp_0:fetch
      

      ssd_mobilenet_v1_relu_voc_fp32_300_opt_auto_split_netron

    • 步骤4:为了获得正确的推理结果,我们需强制设置两个 transpose2 算子运行在 ARM CPU 上。那么,我们就需要通过环境变量 SUBGRAPH_PARTITION_CONFIG_FILE 设置『自定义子图分割配置文件』,实现人为干预分割结果;

      $ cd PaddleLite-generic-demo/ssd_detection_demo/assets/models
      $ cat ./ssd_mobilenet_v1_relu_voc_fp32_300/subgraph_custom_partition_config_file.txt
        transpose2:conv2d_22.tmp_1:transpose_0.tmp_0,transpose_0.tmp_1
        transpose2:conv2d_23.tmp_1:transpose_1.tmp_0,transpose_1.tmp_1
      $ export SUBGRAPH_PARTITION_CONFIG_FILE=./ssd_mobilenet_v1_relu_voc_fp32_300/subgraph_partition_config_file.txt
      $ GLOG_v=5 ./opt --model_dir=./ssd_mobilenet_v1_relu_voc_fp32_300 \
          --optimize_out_type=protobuf \
          --optimize_out=opt_model \
          --valid_targets=huawei_kirin_npu,arm
        ...
        [4  8/30 14:31:50.298 ...ite/lite/core/optimizer/mir/ssa_graph.cc:27 CheckBidirectionalConnection] node count 226
        [4  8/30 14:31:50.299 ...e/optimizer/mir/generate_program_pass.cc:46 Apply] Statement feed host/any/any
        [4  8/30 14:31:50.299 ...e/optimizer/mir/generate_program_pass.cc:46 Apply] Statement subgraph nnadapter/any/NCHW
        [4  8/30 14:31:50.299 ...e/optimizer/mir/generate_program_pass.cc:46 Apply] Statement transpose2 arm/any/NCHW
        [4  8/30 14:31:50.299 ...e/optimizer/mir/generate_program_pass.cc:46 Apply] Statement subgraph nnadapter/any/NCHW
        [4  8/30 14:31:50.299 ...e/optimizer/mir/generate_program_pass.cc:46 Apply] Statement transpose2 arm/any/NCHW
        [4  8/30 14:31:50.299 ...e/optimizer/mir/generate_program_pass.cc:46 Apply] Statement subgraph nnadapter/any/NCHW
        [4  8/30 14:31:50.299 ...e/optimizer/mir/generate_program_pass.cc:46 Apply] Statement box_coder arm/float/NCHW
        [4  8/30 14:31:50.299 ...e/optimizer/mir/generate_program_pass.cc:46 Apply] Statement multiclass_nms host/float/NCHW
        [4  8/30 14:31:50.299 ...e/optimizer/mir/generate_program_pass.cc:46 Apply] Statement fetch host/any/any
        [1  8/30 14:31:50.299 ...re/optimizer/mir/generate_program_pass.h:41 GenProgram] insts.size: 1
        [4  8/30 14:31:50.346 ...e-Lite/lite/model_parser/model_parser.cc:307 SaveModelPb] Save protobuf model in 'opt_model'' successfully
      

      ssd_mobilenet_v1_relu_voc_fp32_300_opt_manual_split_netron

    • 步骤5:上述步骤中,PaddleLite-generic-demo/ssd_detection_demo/assets/models/ssd_mobilenet_v1_relu_voc_fp32_300/subgraph_partition_config_file.txt 是示例自带的『自定义子图分割配置文件』,它的格式是什么样的呢?

      • 每行记录由『算子类型:输入张量名列表:输出张量名列表』组成(即以分号分隔算子类型、输入和输出张量名列表),以逗号分隔输入、输出张量名列表中的每个张量名;

      • 可省略输入、输出张量名列表中的部分张量名(如果不设置任何输入、输出张量列表,则代表计算图中该类型的所有算子节点均被强制运行在 ARM CPU 上);

      • 示例说明:

        op_type0:var_name0,var_name1:var_name2          表示将算子类型为 op_type0、输入张量为var_name0 和 var_name1、输出张量为 var_name2 的节点强制运行在 ARM CPU 上
        op_type1::var_name3                             表示将算子类型为 op_type1、任意输入张量、输出张量为 var_name3 的节点强制运行在 ARM CPU 上
        op_type2:var_name4                              表示将算子类型为 op_type2、输入张量为 var_name4、任意输出张量的节点强制运行在 ARM CPU 上
        op_type3                                        表示任意算子类型为 op_type3 的节点均被强制运行在 ARM CPU 上
        
    • 步骤6:对于 ssd_mobilenet_v1_relu_voc_fp32_300 的模型,我们如何得到 PaddleLite-generic-demo/ssd_detection_demo/assets/models/ssd_mobilenet_v1_relu_voc_fp32_300/subgraph_partition_config_file.txt 的配置呢?

      • 重新在 Netron 打开 PaddleLite-generic-demo/ssd_detection_demo/assets/models/ssd_mobilenet_v1_relu_voc_fp32_300 模型,以其中一个 transpose2 节点为例,点击改节点即可在右侧看到输入、输出张量信息:

        ssd_mobilenet_v1_relu_voc_fp32_300_find_custom_split_node_netron

其它说明

  • 华为达芬奇架构的 NPU 内部大量采用 float16 进行运算,因此,预测结果会存在偏差,但大部分情况下精度不会有较大损失,可参考 Paddle-Lite-Demo 中Image Classification Demo for Android 对同一张图片 CPU 与华为 Kirin NPU 的预测结果。

  • 华为 Kirin 810/990 Soc 搭载的自研达芬奇架构的 NPU,与 Kirin 970/980 Soc 搭载的寒武纪 NPU 不一样,同样的,与 Hi3559A、Hi3519A 使用的 NNIE 也不一样,Paddle Lite 只支持华为自研达芬奇架构 NPU。

  • 我们正在持续增加能够适配 HiAI IR 的 Paddle 算子 bridge/converter,以便适配更多 Paddle 模型,同时华为研发同学也在持续对 HiAI IR 性能进行优化。