TPU-MLIR实现技术详细介绍

TPU-MLIR实现技术详细介绍
TPU-MLIR简介
后文假定用户已经处于docker里面的/workspace目录。
编译ONNX模型
以 yolov5s.onnx 为例, 介绍如何编译迁移一个onnx模型至BM1684X TPU平台运行。
需要如下文件(其中xxxx对应实际的版本信息):
tpu-MLIR_xxxx.tar.gz (tpu-MLIR的发布包)
加载tpu-MLIR
以下操作需要在Docker容器中,代码如下:

//第1章/ tpu-MLIR_envsetup.sh

$ tar zxf tpu-MLIR_xxxx.tar.gz

$ source tpu-MLIR_xxxx/envsetup.sh

envsetup.sh 会添加以下环境变量,见表6-11。
表6-11  环境变量

变量名

说明

TPUC_ROOT

tpu-MLIR_xxx

解压后SDK包的位置

MODEL_ZOO_PATH

${TPUC_ROOT}/../model-zoo

model-zoo文件夹位置, 与SDK在同一级目录

envsetup.sh 对环境变量的修改内容,代码如下:

//第6章/envsetup.sh

export PATH=${TPUC_ROOT}/bin:$PATH

export PATH=${TPUC_ROOT}/python/tools:$PATH

export PATH=${TPUC_ROOT}/python/utils:$PATH

export PATH=${TPUC_ROOT}/python/test:$PATH

export PATH=${TPUC_ROOT}/python/samples:$PATH

export LD_LIBRARY_PATH=$TPUC_ROOT/lib:$LD_LIBRARY_PATH

export PYTHONPATH=${TPUC_ROOT}/python:$PYTHONPATH

export MODEL_ZOO_PATH=${TPUC_ROOT}/../model-zoo

准备工作目录
建立 model_yolov5s 目录, 注意是与tpu-MLIR同级目录; 并把模型文件与图片文件都 放入 model_yolov5s 目录中。
操作代码如下:

//第6章/envsetup.sh

$ mkdir model_yolov5s && cd model_yolov5s

$ cp $TPUC_ROOT/regression/model/yolov5s.onnx.

$ cp -rf $TPUC_ROOT/regression/dataset/COCO2017.

$ cp -rf $TPUC_ROOT/regression/image.

$ mkdir workspace && cd workspace

这里的 $TPUC_ROOT 是环境变量, 对应tpu-MLIR_xxxx目录。
ONNX转MLIR
如果模型是图片输入, 在转模型之前需要了解模型的预处理。如果模型用预处理后的npz文件做输入, 则不需要考虑预处理。 预处理过程用如下公式表达 (

代表输入):

      (6-34)

官网yolov5的图片是rgb, 每个值会乘以 1/255, 转换成均值与缩放对应为 0.0,0.0,0.0 与 0.0039216,0.0039216,0.0039216。
模型转换命令,代码如下:

//第6章/model_transform.py

$ model_transform.py \

    --model_name yolov5s \

    --model_def../yolov5s.onnx \

    --input_shapes [[1,3,640,640]] \

    --mean 0.0,0.0,0.0 \

    --scale 0.0039216,0.0039216,0.0039216 \

    --keep_aspect_ratio \

    --pixel_format rgb \

    --output_names 350,498,646 \

    --test_input../image/dog.jpg \

    --test_result yolov5s_top_outputs.npz \

    --MLIR yolov5s.MLIR \

--post_handle_type yolo

model_transform.py 支持的参数如下:
model_transform 参数功能
 
MLIR转F32模型
将MLIR文件转换成f32的bmodel, 操作方法如下:

//第6章/model_deploy.py

$ model_deploy.py \

    --MLIR yolov5s.MLIR \

    --quantize F32 \

    --chip bm1684x \

    --test_input yolov5s_in_f32.npz \

    --test_reference yolov5s_top_outputs.npz \

    --tolerance 0.99,0.99 \

--model yolov5s_1684x_f32.bmodel

MLIR转INT8模型
生成校准表
转INT8模型前需要跑calibration, 得到校准表; 输入数据的数量根据情况准备100~1000张左右。
然后用校准表, 生成对称或非对称bmodel。如果对称符合需求, 一般不建议用非对称, 因为 非对称的性能会略差于对称模型。
这里用现有的100张来自COCO2017的图片举例, 执行校准,代码如下:

//第6章/run_calibration.py

$ run_calibration.py yolov5s.MLIR \

    --dataset../COCO2017 \

    --input_num 100 \

    -o yolov5s_cali_table

运行完成后会生成名为 ${model_name}_cali_table 的文件, 该文件用于后续编译INT8 模型的输入文件。
编译为INT8对称量化模型
转成INT8对称量化模型, 执行命令如下:

//第6章/model_deploy.py

$ model_deploy.py \

    --MLIR yolov5s.MLIR \

    --quantize INT8 \

    --calibration_table yolov5s_cali_table \

    --chip bm1684x \

    --test_input yolov5s_in_f32.npz \

    --test_reference yolov5s_top_outputs.npz \

    --tolerance 0.85,0.45 \

--model yolov5s_1684x_int8_sym.bmodel

编译完成后, 会生成名为 ${model_name}_1684x_int8_sym.bmodel 的文件。
编译为INT8非对称量化模型
转成INT8非对称量化模型, 执行命令如下:

//第6章/model_deploy_yolov5s.py

$ model_deploy.py \

    --MLIR yolov5s.MLIR \

    --quantize INT8 \

    --asymmetric \

    --calibration_table yolov5s_cali_table \

    --chip bm1684x \

    --test_input yolov5s_in_f32.npz \

    --test_reference yolov5s_top_outputs.npz \

    --tolerance 0.90,0.55 \

--model yolov5s_1684x_int8_asym.bmodel

编译完成后, 会生成名为 ${model_name}_1684x_int8_asym.bmodel 的文件。
效果对比
在本发布包中有用python写好的yolov5用例, 源码路径 $TPUC_ROOT/python/samples/detect_yolov5.py, 用于对图片进行目标检测。阅读该 代码可以了解模型是如何使用的: 先预处理得到模型的输入, 然后推理得到输出, 最后做后处理。
验证onnx/f32/int8的执行结果,代码如下:

//第6章/detect_bmodel_yolov5.py

onnx模型的执行方式如下, 得到 dog_onnx.jpg:

$ detect_yolov5.py \

    --input../image/dog.jpg \

    --model../yolov5s.onnx \

    --output dog_onnx.jpg

f32 bmodel的执行方式如下, 得到 dog_f32.jpg:

$ detect_yolov5.py \

    --input../image/dog.jpg \

    --model yolov5s_1684x_f32.bmodel \

    --output dog_f32.jpg

int8对称bmodel的执行方式如下, 得到 dog_int8_sym.jpg:

$ detect_yolov5.py \

    --input../image/dog.jpg \

    --model yolov5s_1684x_int8_sym.bmodel \

    --output dog_int8_sym.jpg

int8非对称bmodel的执行方式如下, 得到 dog_int8_asym.jpg:

$ detect_yolov5.py \

    --input../image/dog.jpg \

    --model yolov5s_1684x_int8_asym.bmodel \

--output dog_int8_asym.jpg

四张图片对比,如图6-29所示。
 
图6-29  四张图片结果对比示例
由于运行环境不同, 最终的效果与精度与图6-29 会有些差异。
模型性能测试
以下操作需要在Docker外执行,
安装 libsophon 环境
请参考 libsophon 使用手册安装 libsophon。
检查 BModel 的性能
安装好 libsophon 后, 可以使用 bmrt_test 来测试编译出的 bmodel 的正确 性及性能。可以根据 bmrt_test 输出的性能结果, 来估算模型最大的fps, 来选择合适的模型,代码如下:

//第6章/test_bmodel_yolov5s.py

# 下面测试上面编译出的bmodel

# --bmodel参数后面接bmodel文件,

 

$ cd $TPUC_ROOT/../model_yolov5s/workspace

$ bmrt_test --bmodel yolov5s_1684x_f32.bmodel

$ bmrt_test --bmodel yolov5s_1684x_int8_asym.bmodel

$ bmrt_test --bmodel yolov5s_1684x_int8_sym.bmodel

以最后一个命令输出为例(此处对日志做了部分截断处理):

[BMRT][load_bmodel:983] INFO:pre net num: 0, load net num: 1

[BMRT][show_net_info:1358] INFO: ########################

[BMRT][show_net_info:1359] INFO: NetName: yolov5s, Index=0

[BMRT][show_net_info:1361] INFO: ---- stage 0 ----

[BMRT][show_net_info:1369] INFO:   input 0) 'images' shape=[ 1 3 640 640 ] dtype=FLOAT32

[BMRT][show_net_info:1378] INFO:   output 0) '350_Transpose_f32' shape=[ 1 3 80 80 85 ]...

[BMRT][show_net_info:1378] INFO:   output 1) '498_Transpose_f32' shape=[ 1 3 40 40 85 ]...

[BMRT][show_net_info:1378] INFO:   output 2) '646_Transpose_f32' shape=[ 1 3 20 20 85 ]...

[BMRT][show_net_info:1381] INFO: ########################

[BMRT][bmrt_test:770] INFO:==> running network #0, name: yolov5s, loop: 0

[BMRT][bmrt_test:834] INFO:reading input #0, bytesize=4915200

[BMRT][print_array:702] INFO:  --> input_data: < 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0...

[BMRT][bmrt_test:982] INFO:reading output #0, bytesize=6528000

[BMRT][print_array:702] INFO:  --> output ref_data: < 0 0 0 0 0 0 0 0 0 0 0 0 0 0...

[BMRT][bmrt_test:982] INFO:reading output #1, bytesize=1632000

[BMRT][print_array:702] INFO:  --> output ref_data: < 0 0 0 0 0 0 0 0 0 0 0 0 0 0...

[BMRT][bmrt_test:982] INFO:reading output #2, bytesize=408000

[BMRT][print_array:702] INFO:  --> output ref_data: < 0 0 0 0 0 0 0 0 0 0 0 0 0 0...

[BMRT][bmrt_test:1014] INFO:net[yolov5s] stage[0], launch total time is 4122 us (npu 4009 us, cpu 113 us)

[BMRT][bmrt_test:1017] INFO:+++ The network[yolov5s] stage[0] output_data +++

[BMRT][print_array:702] INFO:output data #0 shape: [1 3 80 80 85 ] < 0.301003   ...

[BMRT][print_array:702] INFO:output data #1 shape: [1 3 40 40 85 ] < 0 0.228689 ...

[BMRT][print_array:702] INFO:output data #2 shape: [1 3 20 20 85 ] < 1.00135    ...

[BMRT][bmrt_test:1058] INFO:load input time(s): 0.008914

[BMRT][bmrt_test:1059] INFO:calculate  time(s): 0.004132

[BMRT][bmrt_test:1060] INFO:get output time(s): 0.012603

[BMRT][bmrt_test:1061] INFO:compare    time(s): 0.006514

从上面输出可以看到以下信息:
1)05-08行是bmodel的网络输入输出信息
2)19行是在TPU上运行的时间, 其中TPU用时4009us, CPU用时113us。这里CPU用时主要是指在HOST端调用等待时间
3)24行是加载数据到NPU的DDR的时间
4)25行相当于12行的总时间
5)26行是输出数据取回时间
编译TFLite模型
以 resnet50_int8.tflite 模型为例, 介绍如何编译迁移一个TFLite模型至BM1684X TPU平台运行。
需要如下文件(其中xxxx对应实际的版本信息):
tpu-MLIR_xxxx.tar.gz (tpu-MLIR的发布包)
加载tpu-MLIR
以下操作需要在Docker容器中,命令如下:

//第6章/envsetup_tpu-MLIR.py

$ tar zxf zxf tpu-MLIR_xxxx.tar.gz

$ source tpu-MLIR_xxxx/envsetup.sh

envsetup.sh 会添加以下环境变量:

环境变量

envsetup.sh 对环境变量的修改内容为:

export PATH=${TPUC_ROOT}/bin:$PATH

export PATH=${TPUC_ROOT}/python/tools:$PATH

export PATH=${TPUC_ROOT}/python/utils:$PATH

export PATH=${TPUC_ROOT}/python/test:$PATH

export PATH=${TPUC_ROOT}/python/samples:$PATH

export LD_LIBRARY_PATH=$TPUC_ROOT/lib:$LD_LIBRARY_PATH

export PYTHONPATH=${TPUC_ROOT}/python:$PYTHONPATH

export MODEL_ZOO_PATH=${TPUC_ROOT}/../model-zoo

准备工作目录
建立 model_resnet50_tf 目录, 注意是与tpu-MLIR同级目录; 并把测试图片文件放入 model_resnet50_tf 目录中。
操作命令如下:

//第6章/model_resnet50_tf.py

$ mkdir model_resnet50_tf && cd model_resnet50_tf

$ cp $TPUC_ROOT/regression/model/resnet50_int8.tflite.

$ cp -rf $TPUC_ROOT/regression/image.

$ mkdir workspace && cd workspace

这里的 $TPUC_ROOT 是环境变量, 对应tpu-MLIR_xxxx目录。
TFLite转MLIR
本例中的模型是bgr输入, 均值为 103.939,116.779,123.68, 缩放为 1.0,1.0,1.0
模型转换,命令如下:

//第6章/model_transform_resnet50.py

$ model_transform.py \

    --model_name resnet50_tf \

    --model_def ../resnet50_int8.tflite \

    --input_shapes [[1,3,224,224]] \

    --mean 103.939,116.779,123.68 \

    --scale 1.0,1.0,1.0 \

    --pixel_format bgr \

    --test_input../image/cat.jpg \

    --test_result resnet50_tf_top_outputs.npz \

    --MLIR resnet50_tf.MLIR

转成MLIR文件后, 会生成一个 resnet50_tf_in_f32.npz 文件, 该文件是模型的输入文件。
MLIR转模型
该模型是tflite非对称量化模型, 可以按参数转成模型,代码如下:

//第6章/model_deploy_resnet50.py

$ model_deploy.py \

    --MLIR resnet50_tf.MLIR \

    --quantize INT8 \

    --asymmetric \

    --chip bm1684x \

    --test_input resnet50_tf_in_f32.npz \

    --test_reference resnet50_tf_top_outputs.npz \

    --model resnet50_tf_1684x.bmodel

编译完成后, 会生成名为 resnet50_tf_1684x.bmodel 的文件。
编译Caffe模型
以 mobilenet_v2_deploy.prototxt 与 mobilenet_v2.caffemodel 为例, 介绍如何编译迁移一个caffe模型至BM1684X TPU平台运行。
需要如下文件(其中xxxx对应实际的版本信息):
tpu-MLIR_xxxx.tar.gz (tpu-MLIR的发布包)
加载tpu-MLIR
以下操作需要在Docker容器中,代码如下:

//第6章/model_envsetup_tpu-MLIR.py

$ tar zxf tpu-MLIR_xxxx.tar.gz

$ source tpu-MLIR_xxxx/envsetup.sh

envsetup.sh 会添加以下环境变量:

环境变量

envsetup.sh 对环境变量的修改内容为:

export PATH=${TPUC_ROOT}/bin:$PATH

export PATH=${TPUC_ROOT}/python/tools:$PATH

export PATH=${TPUC_ROOT}/python/utils:$PATH

export PATH=${TPUC_ROOT}/python/test:$PATH

export PATH=${TPUC_ROOT}/python/samples:$PATH

export LD_LIBRARY_PATH=$TPUC_ROOT/lib:$LD_LIBRARY_PATH

export PYTHONPATH=${TPUC_ROOT}/python:$PYTHONPATH

export MODEL_ZOO_PATH=${TPUC_ROOT}/../model-zoo

准备工作目录
建立 mobilenet_v2 目录, 注意是与tpu-MLIR同级目录; 并把模型文件与图片文件都 放入 mobilenet_v2 目录中。
操作命令如下:

//第6章/model_mobilenet_v2.py

$ mkdir mobilenet_v2 && cd mobilenet_v2

$ cp $TPUC_ROOT/regression/model/mobilenet_v2_deploy.prototxt.

$ cp $TPUC_ROOT/regression/model/mobilenet_v2.caffemodel.

$ cp -rf $TPUC_ROOT/regression/dataset/ILSVRC2012.

$ cp -rf $TPUC_ROOT/regression/image.

$ mkdir workspace && cd workspace

这里的 $TPUC_ROOT 是环境变量, 对应tpu-MLIR_xxxx目录。
Caffe转MLIR
本例中的模型是 BGR 输入, 均值与缩放分别为 103.94,116.78,123.68 与 0.017,0.017,0.017。
模型转换,命令如下:

//第6章/transform_mobilenet_v2.py

$ model_transform.py \

    --model_name mobilenet_v2 \

    --model_def../mobilenet_v2_deploy.prototxt \

    --model_data../mobilenet_v2.caffemodel \

    --input_shapes [[1,3,224,224]] \

    --resize_dims=256,256 \

    --mean 103.94,116.78,123.68 \

    --scale 0.017,0.017,0.017 \

    --pixel_format bgr \

    --test_input../image/cat.jpg \

    --test_result mobilenet_v2_top_outputs.npz \

--MLIR mobilenet_v2.MLIR

 
转成MLIR文件后, 会生成一个 ${model_name}_in_f32.npz 文件, 该文件是模型的输入文件。
MLIR转F32模型
将MLIR文件转换成f32的bmodel, 操作方法如下:

//第6章/model_deploy_mobilenet_v2.py

$ model_deploy.py \

    --MLIR mobilenet_v2.MLIR \

    --quantize F32 \

    --chip bm1684x \

    --test_input mobilenet_v2_in_f32.npz \

    --test_reference mobilenet_v2_top_outputs.npz \

    --tolerance 0.99,0.99 \

    --model mobilenet_v2_1684x_f32.bmodel

编译完成后, 会生成名为 ${model_name}_1684x_f32.bmodel 的文件。
MLIR转INT8模型
生成校准表
转INT8模型前需要跑校准, 得到校准表; 输入数据的数量根据情况准备100~1000张左右。
然后用校准表, 生成对称或非对称bmodel。如果对称符合需求, 一般不建议用非对称, 因为 非对称的性能会略差于对称模型。
这里用现有的100张来自ILSVRC2012的图片举例, 执行校准,命令如下:

//第6章/calibration _mobilenet_v2.py

$ run_calibration.py mobilenet_v2.MLIR \

    --dataset../ILSVRC2012 \

    --input_num 100 \

    -o mobilenet_v2_cali_table

运行完成后会生成名为 ${model_name}_cali_table 的文件, 该文件用于后续编译INT8 模型的输入文件。
编译为INT8对称量化模型
转成INT8对称量化模型, 执行命令如下:

//第6章/mlir_calibration_mobilenet_v2.py

$ model_deploy.py \

    --MLIR mobilenet_v2.MLIR \

    --quantize INT8 \

    --calibration_table mobilenet_v2_cali_table \

    --chip bm1684x \

    --test_input mobilenet_v2_in_f32.npz \

    --test_reference mobilenet_v2_top_outputs.npz \

    --tolerance 0.96,0.70 \

--model mobilenet_v2_1684x_int8_sym.bmodel

编译完成后, 会生成名为 ${model_name}_1684x_int8_sym.bmodel 的文件。
编译为INT8非对称量化模型
转成INT8非对称量化模型, 执行命令如下:

//第6章/mlir_asymmetric_mobilenet_v2.py

$ model_deploy.py \

    --MLIR mobilenet_v2.MLIR \

    --quantize INT8 \

    --asymmetric \

    --calibration_table mobilenet_v2_cali_table \

    --chip bm1684x \

    --test_input mobilenet_v2_in_f32.npz \

    --test_reference mobilenet_v2_top_outputs.npz \

    --tolerance 0.95,0.69 \

--model mobilenet_v2_1684x_int8_asym.bmodel

编译完成后, 会生成名为 ${model_name}_1684x_int8_asym.bmodel 的文件。
混精度使用方法
以检测网络 yolov3 tiny 网络模型为例, 介绍如何使用混精度。 该模型来自https://github.com/onnx/models/tree/main/vision/object_detection_segmentation/tiny-yolov3。
需要如下文件(其中xxxx对应实际的版本信息):
tpu-MLIR_xxxx.tar.gz (tpu-MLIR的发布包)
加载tpu-MLIR
以下操作需要在Docker容器中,命令如下:

//第6章/tpu_mlir_envsetup.py

$ tar zxf tpu-MLIR_xxxx.tar.gz

$ source tpu-MLIR_xxxx/envsetup.sh

envsetup.sh 会添加以下环境变量:

envsetup.sh 对环境变量的修改内容为:

export PATH=${TPUC_ROOT}/bin:$PATH

export PATH=${TPUC_ROOT}/python/tools:$PATH

export PATH=${TPUC_ROOT}/python/utils:$PATH

export PATH=${TPUC_ROOT}/python/test:$PATH

export PATH=${TPUC_ROOT}/python/samples:$PATH

export LD_LIBRARY_PATH=$TPUC_ROOT/lib:$LD_LIBRARY_PATH

export PYTHONPATH=${TPUC_ROOT}/python:$PYTHONPATH

export MODEL_ZOO_PATH=${TPUC_ROOT}/../model-zoo

准备工作目录
建立 yolov3_tiny 目录, 注意是与tpu-MLIR同级目录; 并把模型文件与图片文件都 放入 yolov3_tiny 目录中。
操作命令如下:

//第6章/wget_envsetup.py

$ mkdir yolov3_tiny && cd yolov3_tiny

 $ wget https://github.com/onnx/models/raw/main/vision/object_detection_segmentation/tiny-yolov3/model/tiny-yolov3-11.onnx

 $ cp -rf $TPUC_ROOT/regression/dataset/COCO2017.

 $ mkdir workspace && cd workspace

$ tar zxf tpu-MLIR_xxxx.tar.gz

$ source tpu-MLIR_xxxx/envsetup.sh

envsetup.sh 会添加以下环境变量:

envsetup.sh 对环境变量的修改内容为:

export PATH=${TPUC_ROOT}/bin:$PATH

export PATH=${TPUC_ROOT}/python/tools:$PATH

export PATH=${TPUC_ROOT}/python/utils:$PATH

export PATH=${TPUC_ROOT}/python/test:$PATH

export PATH=${TPUC_ROOT}/python/samples:$PATH

export LD_LIBRARY_PATH=$TPUC_ROOT/lib:$LD_LIBRARY_PATH

export PYTHONPATH=${TPUC_ROOT}/python:$PYTHONPATH

export MODEL_ZOO_PATH=${TPUC_ROOT}/../model-zoo

这里的 $TPUC_ROOT 是环境变量, 对应tpu-MLIR_xxxx目录。 注意如果 tiny-yolov3-11.onnx 用wget下载失败, 请用其他方式下载后放到 yolov3_tiny 目录。
验证原始模型
detect_yolov3.py 是已经写好的验证程序, 可以用来对 yolov3_tiny 网络进行验证。执行过程,命令如下:

//第6章/detect_yolov3.py

$ detect_yolov3.py \

     --model../tiny-yolov3-11.onnx \

     --input../COCO2017/000000366711.jpg \

     --output yolov3_onnx.jpg

执行完后输出检测到的结果如下:

person:60.7%

orange:77.5%

并得到图片 yolov3_onnx.jpg, yolov3_tiny ONNX执行效果,如图6-30所示。                                                                                                                                       
 
图6-30  yolov3_tiny ONNX执行效果示例
转成INT8对称量化模型
如前面介绍的转模型方法, 这里不做参数说明, 只有操作过程,命令如下:

//第6章/yolov3_tiny.py

第一步: 转成F32 MLIR

$ model_transform.py \

    --model_name yolov3_tiny \

    --model_def../tiny-yolov3-11.onnx \

    --input_shapes [[1,3,416,416]] \

    --scale 0.0039216,0.0039216,0.0039216 \

    --pixel_format rgb \

    --keep_aspect_ratio \

    --pad_value 128 \

    --output_names=transpose_output1,transpose_output \

    --MLIR yolov3_tiny.MLIR

第二步: 生成calibartion table

$ run_calibration.py yolov3_tiny.MLIR \

    --dataset../COCO2017 \

    --input_num 100 \

    -o yolov3_cali_table

第三步: 转对称量化模型

$ model_deploy.py \

    --MLIR yolov3_tiny.MLIR \

    --quantize INT8 \

    --calibration_table yolov3_cali_table \

    --chip bm1684x \

    --model yolov3_int8.bmodel

第四步: 验证模型

$ detect_yolov3.py \

     --model yolov3_int8.bmodel \

     --input../COCO2017/000000366711.jpg \

     --output yolov3_int8.jpg

执行完后有如下输出信息,表示检测到一个目标:
                  orange:73.0%
得到图片 yolov3_int8.jpg,  yolov3_tiny int8对称量化执行效果,如图6-31所示。
 
图6-31  yolov3_tiny ONNX执行效果示例
可以看出int8对称量化模型相对原始模型, 在这张图上效果不佳,只检测到一个目标。
转成混精度量化模型
在转int8对称量化模型的基础上, 执行如下步骤。
第一步: 生成混精度量化表
使用 run_qtable.py 生成混精度量化表, 相关参数说明如下:
run_qtable.py 参数功能
本例中采用默认10张图片校准, 对于CV18xx系列的芯片,将chip设置为对应的芯片名称即可,执行命令,代码如下:

//第6章/run_qtable_yolov3_tiny.py

$ run_qtable.py yolov3_tiny.MLIR \

    --dataset../COCO2017 \

    --calibration_table yolov3_cali_table \

    --chip bm1684x \

    --min_layer_cos 0.999 \ #若这里使用默认的0.99时,程序会检测到原始int8模型已满足0.99的cos,从而直接不再搜素

    --expected_cos 0.9999 \

    -o yolov3_qtable

执行完后最后输出如下输出:

int8 outputs_cos:0.999317

mix model outputs_cos:0.999739

output mix quantization table to yolov3_qtable

total time:44 second

上面int8 outputs_cos表示int8模型原本网络输出与fp32的cos相似度,mix model outputs_cos表示部分层使用混精度后网络输出的cos相似度,total time表示搜索时间为44秒, 另外,生成的混精度量化表 yolov3_qtable, 内容如下:

# op_name   quantize_mode

convolution_output11_convF16

model_1/leaky_re_lu_2/LeakyRelu:0_LeakyRelu F16

model_1/leaky_re_lu_2/LeakyRelu:0_pooling0_MaxPool F16

convolution_output10_convF16

convolution_output9_convF16

model_1/leaky_re_lu_4/LeakyRelu:0_LeakyRelu F16

model_1/leaky_re_lu_5/LeakyRelu:0_LeakyRelu F16

model_1/leaky_re_lu_5/LeakyRelu:0_pooling0_MaxPool F16

model_1/concatenate_1/concat:0_Concat F16

该表中, 第一列表示相应的layer, 第二列表示类型, 支持的类型有F32/F16/BF16/INT8。 另外同时也会生成一个loss表文件 full_loss_table.txt, 内容如下:

# chip: bm1684x  mix_mode: F16

###

No.0  : Layer:convolution_output11_conv                           Cos: 0.984398

No.1  : Layer: model_1/leaky_re_lu_5/LeakyRelu:0_LeakyRelu          Cos: 0.998341

No.2  : Layer: model_1/leaky_re_lu_2/LeakyRelu:0_pooling0_MaxPool   Cos: 0.998500

No.3  : Layer:convolution_output9_conv                            Cos: 0.998926

No.4  : Layer:convolution_output8_conv                            Cos: 0.999249

No.5  : Layer: model_1/leaky_re_lu_4/LeakyRelu:0_pooling0_MaxPool   Cos: 0.999284

No.6  : Layer: model_1/leaky_re_lu_1/LeakyRelu:0_LeakyRelu          Cos: 0.999368

No.7  : Layer: model_1/leaky_re_lu_3/LeakyRelu:0_LeakyRelu          Cos: 0.999554

No.8  : Layer: model_1/leaky_re_lu_1/LeakyRelu:0_pooling0_MaxPool   Cos: 0.999576

No.9  : Layer: model_1/leaky_re_lu_3/LeakyRelu:0_pooling0_MaxPool   Cos: 0.999723

No.10 : Layer:convolution_output12_conv                           Cos: 0.999810

该表按cos从小到大顺利排列, 表示该层的前驱Layer根据各自的cos已换成相应的浮点模式后, 该层计算得到的cos, 若该cos仍小于前面min_layer_cos参数,则会将该层及直接后继层设置为浮点计算。 run_qtable.py 会在每次设置某相邻2层为浮点计算后,接续计算整个网络的输出cos,若该cos大于指定的expected_cos,则退出搜素。因此,若设置更大的expected_cos,会尝试将更多层设为浮点计算

第二步: 生成混精度量化模型

$ model_deploy.py \

    --MLIR yolov3_tiny.MLIR \

    --quantize INT8 \

    --quantize_table yolov3_qtable \

    --calibration_table yolov3_cali_table \

    --chip bm1684x \

    --model yolov3_mix.bmodel

第三步: 验证混精度模型

$ detect_yolov3.py \

     --model yolov3_mix.bmodel \

     --input../COCO2017/000000366711.jpg \

     --output yolov3_mix.jpg

执行完后输出结果为:
person:63.9%
orange:73.0%
得到图片yolov3_mix.jpg, yolov3_tiny 混精度对称量化执行效果,如图6-32所示。
 
可以看出混精度后, 检测结果更接近原始模型的结果。
需要说明的是,除了使用run_qtable生成量化表外,也可根据模型中每一层的相似度对比结果,自行设置量化表中需要做混精度量化的OP的名称与量化类型。
使用TPU做前处理
目前TPU-MLIR支持的两个主要系列芯片BM168x与CV18xx均支持将图像常见的预处理加入到模型中进行计算。开发者可以在模型编译阶段,通过编译选项传递相应预处理参数,由编译器直接在模型运算前插⼊相应前处理算⼦,⽣成的bmodel或cvimodel即可以直接以预处理前的图像作为输⼊,随模型推理过程使⽤TPU处理前处理运算,见表6-12。
表6-12  预处理类型支持情况

预处理类型

BM168X

CV18XX

图像裁剪

True

True

归一化计算

True

True

NHWC to NCHW

True

True

BGR/RGB 转换

True

True

其中图像裁剪会先将图片按使用model_transform工具时输入的–resize_dims参数将图片调整为对应的大小, 再裁剪成模型输入的尺寸。而归一化计算支持直接将未进行预处理的图像数据(即unsigned int8格式的数据)做归一化处理。
若要将预处理融入到模型中, 则需要在使用model_deploy工具进行部署时使用–fuse_preprocess参数。如果要做验证, 则传入的test_input需要是图像原始格式的输入(即jpg, jpeg与png格式), 相应地会生成原始图像输入对应的npz文件,名称为 ${model_name}_in_ori.npz。
此外, 当实际外部输入格式与模型的格式不相同时, 用–customization_format指定实际的外部输入格式, 支持的格式说明,见表3-13。
表3-13  customization_format格式与说明

CUSTOMIZATION_FORMAT

说明

BM1684X

CV18XX

None

与原始模型输入保持一致, 不做处理。默认

True

True

RGB_PLANAR

rgb顺序,按照nchw摆放

True

True

RGB_PACKED

rgb顺序,按照nhwc摆放

True

True

BGR_PLANAR

bgr顺序,按照nchw摆放

True

True

BGR_PACKED

bgr顺序,按照nhwc摆放

True

True

GRAYscale

仅有⼀个灰⾊通道,按nchw摆

True

True

YUV420_PLANAR

yuv420 planner格式,来⾃vpss的输⼊

False

True

YUV_NV21

yuv420的NV21格式,来⾃vpss的输⼊

False

True

YUV_NV12

yuv420的NV12格式,来⾃vpss的输⼊

False

True

RGBA_PLANAR

rgba格式,按照nchw摆放

False

True

其中YUV*类格式为CV18xx系列芯片特有的输入格式。当customization_format中颜色通道的顺序与模型输入不同时, 将会进行通道转换操作。若指令中未设置customization_format参数,则根据使用model_transform工具时定义的pixel_format与channel_format参数自动获取对应的customization_format。
模型部署样例
以mobilenet_v2模型为例, 参考编译Caffe模型, 在tpu-MLIR/regression/regression_out/目录下使用model_transform工具生成原始MLIR, 并通过run_calibration工具生成校准表。
BM1684X部署
生成融合预处理的INT8对称量化bmodel模型,指令如下:

//第6章/test_model_deploy.py

$ model_deploy.py \

    --MLIR mobilenet_v2.MLIR \

    --quantize INT8 \

    --calibration_table mobilenet_v2_cali_table \

    --chip bm1684x \

    --test_input../image/cat.jpg \

    --test_reference mobilenet_v2_top_outputs.npz \

    --tolerance 0.96,0.70 \

    --fuse_preprocess \

--model mobilenet_v2_bm1684x_int8_sym_fuse_preprocess.bmodel

CV18xx部署
生成融合预处理的INT8对称量化cvimodel模型,指令如下:

//第6章/sym_fuse_preprocess.cvimodel.py

$ model_deploy.py \

    --MLIR mobilenet_v2.MLIR \

    --quantize INT8 \

    --calibration_table mobilenet_v2_cali_table \

    --chip cv183x \

    --test_input../image/cat.jpg \

    --test_reference mobilenet_v2_top_outputs.npz \

    --tolerance 0.96,0.70 \

    --fuse_preprocess \

    --customization_format RGB_PLANAR \

--model mobilenet_v2_cv183x_int8_sym_fuse_preprocess.cvimodel

当输入数据是来自于CV18xx提供的视频后处理模块VPSS时,则会有数据对齐要求,⽐如w 按照32字节对齐,此时生成融合预处理的cvimodel模型,指令如下:

//第6章/sym_fuse_preprocess_aligned.cvimodel.py

$ model_deploy.py \

    --MLIR mobilenet_v2.MLIR \

    --quantize INT8 \

    --calibration_table mobilenet_v2_cali_table \

    --chip cv183x \

    --test_input../image/cat.jpg \

    --test_reference mobilenet_v2_top_outputs.npz \

    --tolerance 0.96,0.70 \

    --fuse_preprocess \

    --customization_format RGB_PLANAR \

    --aligned_input \

--model mobilenet_v2_cv183x_int8_sym_fuse_preprocess_aligned.cvimodel

上述指令中,aligned_input指定了模型需要做输入的对齐。需要注意的是,YUV格式的输入数据fuse_preprocess与aligned_input需要都做,其它格式的fuse_preprocess与aligned_input的操作可选择只做其中一个或两个都做, 若只做aligned_input操作,则需要设置test_input为做过预处理的 ${model_name}_in_f32.npz 格式,与编译ONNX模型的设置是一致的。
各框架模型转ONNX参考
主要提供了将PyTorch, Tensorflow与PaddlePaddle模型转为ONNX模型的方式参考,读者也可以参考ONNX官方仓库提供的转模型教程: https://github.com/onnx/tutorials
PyTorch模型转ONNX
以一个自主搭建的简易PyTorch模型为例进行onnx转换,代码如下:

//第6章/torch_model.py

步骤0:创建工作目录

在命令行中创建并进入torch_model目录。

$ mkdir torch_model

$ cd torch_model

步骤1:搭建并保存模型

在该目录下创建名为simple_net.py的脚本并运行,脚本的具体内容如下:

#!/usr/bin/env python3

import torch

 

# 构建简单的神经网络模型

class SimpleModel(torch.nn.Module):

 

   def __init__(self):

      super(SimpleModel, self).__init__()

      self.m1 = torch.nn.conv2d(3, 8, 3, 1, 0)

      self.m2 = torch.nn.conv2d(8, 8, 3, 1, 1)

 

   def forward(self,x):

      y0 = self.m1(x)

      y1 = self.m2(y0)

      y2 = y0 + y1

      return y2

 

# 创建一个SimpleModel并将其权重保存在当前目录中

model = SimpleModel()

torch.save(model.state_dict(), weight.pth)

运行完后会在当前目录下获得一个weight.pth的权重文件。

步骤2:导出ONNX模型

在该目录下创建另一个名为export_onnx.py的脚本并运行,脚本的具体内容如下:

#!/usr/bin/env python3

import torch

from simple_net import SimpleModel

 

# 加载预训练的模型并将其导出为onnx

model = SimpleModel()

model.eval()

checkpoint = torch.load(weight.pth, map_location=cpu)

model.load_state_dict(checkpoint)

 

# 准备输入张量

input = torch.randn(1, 3, 16, 16, requires_grad=True)

 

# 将torch模型导出为onnx

torch.onnx.export(model,

                  input,

                  'model.onnx', # 导出的onnx模型的名称

                  opset_version=13,

                  export_params=True,

                  do_constant_folding=True)

运行完脚本后,即可在当前目录下得到名为model.onnx的onnx模型。
Tensorflow模型转ONNX
以Tensorflow官方仓库中提供的mobilenet_v1_0.25_224模型作为转换样例,命令如下:

//第6章/tf_model_onnx.py

步骤0:创建工作目录

在命令行中创建并进入tf_model目录。

$ mkdir tf_model

$ cd tf_model

步骤1:准备并转换模型

命令行中通过以下命令下载模型并利用tf2onnx工具将其导出为ONNX模型:

$ wget -nc http://download.tensorflow.org/models/mobilenet_v1_2018_08_02/mobilenet_v1_0.25_224.tgz

# tar获取*.pb模型定义文件

$ tar xzf mobilenet_v1_0.25_224.tgz

$ python -m tf2onnx.convert --graphdef mobilenet_v1_0.25_224_frozen.pb \

    --output mnet_25.onnx --inputs input:0 \

    --inputs-as-nchw input:0 \

    --outputs MobilenetV1/Predictions/Reshape_1:0

运行以上所有命令后即可在当前目录下得到名为mnet_25.onnx的onnx模型。
PaddlePaddle模型转ONNX
以PaddlePaddle官方仓库中提供的SqueezeNet1_1模型作为转换样例,命令如下:

//第6章/pp_model_onnx.py

步骤0:创建工作目录

在命令行中创建并进入pp_model目录。

$ mkdir pp_model

$ cd pp_model

步骤1:准备模型

在命令行中通过以下命令下载模型:

$ wget https://bj.bcebos.com/paddlehub/fastdeploy/SqueezeNet1_1_infer.tgz

$ tar xzf SqueezeNet1_1_infer.tgz

$ cd SqueezeNet1_1_infer

并用PaddlePaddle项目中的paddle_infer_shape.py脚本对模型进行shape推理,此处将输入shape以NCHW的格式设置为[1,3,224,224]:

$ wget https://raw.githubusercontent.com/PaddlePaddle/Paddle2ONNX/develop/tools/paddle/paddle_infer_shape.py

$ python paddle_infer_shape.py  --model_dir. \

                          --model_filename inference.pdmodel \

                          --params_filename inference.pdiparams \

                          --save_dir new_model \

                          --input_shape_dict={'inputs':[1,3,224,224]}

运行完以上所有命令后将处于SqueezeNet1_1_infer目录下,并在该目录下有一个new_model的目录。

步骤2:转换模型

在命令行中通过以下命令安装paddle2onnx工具,并利用该工具将PaddlePaddle模型转为ONNX模型:

$ pip install paddle2onnx

$ paddle2onnx  --model_dir new_model \

          --model_filename inference.pdmodel \

          --params_filename inference.pdiparams \

          --opset_version 13 \

          --save_file squeezenet1_1.onnx

运行完以上所有命令后将获得一个名为squeezenet1_1.onnx的onnx模型。
CV18xx芯片使用指南
CV18xx支持ONNX系列与Caffe模型,目前不支持TFLite模型。在量化数据类型方面,CV18xx支持BF16格式的量化 与INT8格式的非对称量化。节以CV183X芯片为例,介绍CV18xx系列芯片编译模型与运行runtime sample。
编译yolov5模型
加载tpu-MLIR
以下操作需要在Docker容器中,命令如下:

//第6章/tpu-MLIR_envsetup.py

$ tar zxf tpu-MLIR_xxxx.tar.gz

$ source tpu-MLIR_xxxx/envsetup.sh

envsetup.sh 会添加以下环境变量:

操作如下:

$ mkdir model_yolov5s && cd model_yolov5s

$ cp $TPUC_ROOT/regression/model/yolov5s.onnx.

$ cp -rf $TPUC_ROOT/regression/dataset/COCO2017.

$ cp -rf $TPUC_ROOT/regression/image.

$ mkdir workspace && cd workspace

这里的 $TPUC_ROOT 是环境变量, 对应tpu-MLIR_xxxx目录。

这里的 $TPUC_ROOT 是环境变量, 对应tpu-MLIR_xxxx目录。
ONNX转MLIR
如果模型是图片输入, 在转模型之前需要了解模型的预处理。如果模型用预处理后的npz文件做输入, 则不需要考虑预处理。 预处理过程用公式表达如下(

 代表输入):

 官网yolov5的图片是rgb, 每个值会乘以 1/255, 转换成均值与缩放对应为 0.0,0.0,0.0 与 0.0039216,0.0039216,0.0039216,命令如下:

//第6章/model_transform_deploy_calibration.py

模型转换命令如下:

$ model_transform.py \

    --model_name yolov5s \

    --model_def../yolov5s.onnx \

    --input_shapes [[1,3,640,640]] \

    --mean 0.0,0.0,0.0 \

    --缩放 0.0039216,0.0039216,0.0039216 \

    --keep_aspect_ratio \

    --pixel_format rgb \

    --output_names 350,498,646 \

    --test_input../image/dog.jpg \

    --test_result yolov5s_top_outputs.npz \

    --MLIR yolov5s.MLIR

model_transform 的相关参数说明参考编译ONNX模型-ONNX转MLIR部分。

MLIR转BF16模型

将MLIR文件转换成bf16的cvimodel, 操作方法如下:

$ model_deploy.py \

    --MLIR yolov5s.MLIR \

    --quantize BF16 \

    --chip cv183x \

    --test_input yolov5s_in_f32.npz \

    --test_reference yolov5s_top_outputs.npz \

    --tolerance 0.99,0.99 \

    --model yolov5s_cv183x_bf16.cvimodel

model_deploy.py 的相关参数说明参考编译ONNX模型-MLIR转F32模型部分。

MLIR转INT8模型

转INT8模型前需要跑calibration, 得到校准表; 输入数据的数量根据情况准备100~1000张左右。然后用校准表, 生成INT8对称cvimodel

这里用现有的100张来自COCO2017的图片举例, 执行calibration:

$ run_calibration.py yolov5s.MLIR \

    --dataset../COCO2017 \

    --input_num 100 \

    -o yolov5s_cali_table

运行完成后会生成名为 ${model_name}_cali_table 的文件, 该文件用于后续编译INT8 模型的输入文件。

转成INT8对称量化cvimodel模型, 执行如下命令:

$ model_deploy.py \

    --MLIR yolov5s.MLIR \

    --quantize INT8 \

    --calibration_table yolov5s_cali_table \

    --chip cv183x \

    --test_input yolov5s_in_f32.npz \

    --test_reference yolov5s_top_outputs.npz \

    --tolerance 0.85,0.45 \

    --model yolov5s_cv183x_int8_sym.cvimodel

编译完成后, 会生成名为 ${model_name}_cv183x_int8_sym.cvimodel 的文件。

效果对比

onnx模型的执行方式如下, 得到 dog_onnx.jpg:

$ detect_yolov5.py \

    --input../image/dog.jpg \

    --model../yolov5s.onnx \

    --output dog_onnx.jpg

FP32 MLIR模型的执行方式如下,得到 dog_MLIR.jpg:

$ detect_yolov5.py \

    --input../image/dog.jpg \

    --model yolov5s.MLIR \

    --output dog_MLIR.jpg

BF16 cvimodel的执行方式如下, 得到 dog_bf16.jpg:

$ detect_yolov5.py \

    --input../image/dog.jpg \

    --model yolov5s_cv183x_bf16.cvimodel \

    --output dog_bf16.jpg

INT8 cvimodel的执行方式如下, 得到 dog_int8.jpg:

$ detect_yolov5.py \

    --input../image/dog.jpg \

    --model yolov5s_cv183x_int8_sym.cvimodel \

--output dog_int8.jpg

四张图片对比,如图 6-33所示,由于运行环境不同, 最终的效果与精度与图6-33会有些差异。
 
图6-33  yolov3_tiny ONNX执行效果示例
前面已经介绍了TPU-MLIR编译CV18xx系列芯片的ONNX模型的过程,caffe模型的转换过程可参考编译Caffe模型,只需要将对应的芯片名称换成实际的CV18xx芯片名称即可。
合并cvimodel模型文件
对于同一个模型,可以依据输入的batch size以及分辨率(不同的h与w)分别生成独立的cvimodel文件。不过为了节省外存与运存,可以选择将这些相关的cvimodel文件合并为一个cvimodel文件,共享其权重部分。具体步骤如下:

//第6章/model_transform_cvimodel.py

步骤0:生成batch 1的cvimodel

新建workspace目录,通过model_transform.py将yolov5s转换成MLIR fp32模型。

注意:

1.需要合并的cvimodel使用同一个workspace目录,并且不要与不需要合并的cvimodel 共用一个workspace;

2.步骤0、步骤1中 –merge_weight是必需选项。

$ model_transform.py \

    --model_name yolov5s \

    --model_def../yolov5s.onnx \

    --input_shapes [[1,3,640,640]] \

    --mean 0.0,0.0,0.0 \

    --缩放 0.0039216,0.0039216,0.0039216 \

    --keep_aspect_ratio \

    --pixel_format rgb \

    --output_names 350,498,646 \

    --test_input../image/dog.jpg \

    --test_result yolov5s_top_outputs.npz \

    --MLIR yolov5s_bs1.MLIR

使用前述生成的yolov5s_cali_table;如果没有,则通过run_calibration.py工具对yolov5s.MLIR进行量化校验获得校准表文件。 然后将模型量化并生成cvimodel:

# 加上 --merge_weight参数

 $ model_deploy.py \

     --MLIR yolov5s_bs1.MLIR \

     --quantize INT8 \

     --calibration_table yolov5s_cali_table \

     --chip cv183x \

     --test_input yolov5s_in_f32.npz \

     --test_reference yolov5s_top_outputs.npz \

     --tolerance 0.85,0.45 \

     --merge_weight \

     --model yolov5s_cv183x_int8_sym_bs1.cvimodel

步骤1:生成batch 2的cvimodel

同步骤0,在同一个workspace中生成batch为2的MLIR fp32文件:

$ model_transform.py \

    --model_name yolov5s \

    --model_def../yolov5s.onnx \

    --input_shapes [[2,3,640,640]] \

    --mean 0.0,0.0,0.0 \

    --缩放 0.0039216,0.0039216,0.0039216 \

    --keep_aspect_ratio \

    --pixel_format rgb \

    --output_names 350,498,646 \

    --test_input../image/dog.jpg \

    --test_result yolov5s_top_outputs.npz \

    --MLIR yolov5s_bs2.MLIR

# 加上 --merge_weight参数

 $ model_deploy.py \

     --MLIR yolov5s_bs2.MLIR \

     --quantize INT8 \

     --calibration_table yolov5s_cali_table \

     --chip cv183x \

     --test_input yolov5s_in_f32.npz \

     --test_reference yolov5s_top_outputs.npz \

     --tolerance 0.85,0.45 \

     --merge_weight \

     --model yolov5s_cv183x_int8_sym_bs2.cvimodel

步骤2:合并batch 1与batch 2的cvimodel

使用model_tool合并两个cvimodel文件:

model_tool \

  --combine \

    yolov5s_cv183x_int8_sym_bs1.cvimodel \

    yolov5s_cv183x_int8_sym_bs2.cvimodel \

    -o yolov5s_cv183x_int8_sym_bs1_bs2.cvimodel

步骤3:runtime接口调用cvimodel

可以通过以下命令查看bs1与bs2指令的程序id:

model_tool --info yolov5s_cv183x_int8_sym_bs1_bs2.cvimodel

在运行时可以通过如下方式去运行不同的batch命令:

CVI_MODEL_HANDEL bs1_handle;

CVI_RC ret = CVI_NN_RegisterModel(yolov5s_cv183x_int8_sym_bs1_bs2.cvimodel, &bs1_handle);

assert(ret == CVI_RC_SUCCESS);

// 选择bs1的程序id

CVI_NN_SetConfig(bs1_handle, OPTION_PROGRAM_INDEX, 0);

CVI_NN_Get输入输出tensors(bs1_handle,...);

....

 

CVI_MODEL_HANDLE bs2_handle;

// 复用已加载的模型

CVI_RC ret = CVI_NN_CloneModel(bs1_handle, &bs2_handle);

assert(ret == CVI_RC_SUCCESS);

// 选择bs2的程序id

CVI_NN_SetConfig(bs2_handle, OPTION_PROGRAM_INDEX, 1);

CVI_NN_Getinputoutputtensors(bs2_handle,...);

...

 

// 最后销毁bs1_handle, bs2_handel

CVI_NN_CleanupModel(bs1_handle);

CVI_NN_CleanupModel(bs2_handle);

综述:合并过程
使用上面命令,不论是相同模型还是不同模型,均可以进行合并。合并的原理是:模型生成过程中,会叠加前面模型的权重(如果相同则共用)。
主要步骤在于:
1)用model_deploy.py生成模型时,加上–merge_weight参数
2)要合并的模型的生成目录必须是同一个,且在合并模型前不要清理任何中间文件(叠加前面模型权重通过中间文件_weight_map.csv实现)
3)用model_tool –combine 将多个cvimodel合并
编译与运行运行时示例
首先介绍EVB如何运行sample应用程序,然后介绍如何交叉编译sample应用程序,最后介绍docker仿真编译与运行sample。具体包括4个samples: * Sample-1: classifier (mobilenet_v2)
1)Sample-2: classifier_bf16 (mobilenet_v2)
1) Sample-3: 分类器融合预处理 (mobilenet_v2)
3)Sample-4: 分类器多批 (mobilenet_v2)
4) 在EVB运行release提供的示例预编译程序
将根据chip类型选择所需文件加载至EVB的文件系统,于evb上的linux控制台执行,以cv183x为例:
解压示例使用的模型文件(以cvimodel格式交付),并解压TPU_SDK,并进入示例目录,执行测试过程,命令如下:

//第6章/cvimodel_samples.py

#env

tar zxf cvimodel_samples_cv183x.tar.gz

export MODEL_PATH=$PWD/cvimodel_samples

tar zxf cvitek_tpu_sdk_cv183x.tar.gz

export TPU_ROOT=$PWD/cvitek_tpu_sdk

cd cvitek_tpu_sdk && source./envs_tpu_sdk.sh

# get cvimodel info

cd samples

./bin/cvi_sample_model_info $MODEL_PATH/mobilenet_v2.cvimodel

 

####################################

# sample-1: 分类器

###################################

./bin/cvi_sample_classifier \

    $MODEL_PATH/mobilenet_v2.cvimodel \

   ./data/cat.jpg \

   ./data/synset_words.txt

 

# TOP_K[5]:

#  0.326172, idx 282, n02123159 tiger cat

#  0.326172, idx 285, n02124075 Egyptian cat

#  0.099609, idx 281, n02123045 tabby, tabby cat

#  0.071777, idx 287, n02127052 lynx, catamount

#  0.041504, idx 331, n02326432 hare

 

####################################

# sample-2: classifier_bf16

###################################

./bin/cvi_sample_classifier_bf16 \

    $MODEL_PATH/mobilenet_v2_bf16.cvimodel \

   ./data/cat.jpg \

   ./data/synset_words.txt

 

# TOP_K[5]:

#  0.314453, idx 285, n02124075 Egyptian cat

#  0.040039, idx 331, n02326432 hare

#  0.018677, idx 330, n02325366 wood rabbit, cottontail, cottontail rabbit

#  0.010986, idx 463, n02909870 bucket, pail

#  0.010986, idx 852, n04409515 tennis ball

 

 

############################################

# sample-3: 分类器融合预处理

############################################

./bin/cvi_sample_classifier_fused_preprocess \

    $MODEL_PATH/mobilenet_v2_fused_preprocess.cvimodel \

   ./data/cat.jpg \

   ./data/synset_words.txt

 

# TOP_K[5]:

#  0.326172, idx 282, n02123159 tiger cat

#  0.326172, idx 285, n02124075 Egyptian cat

#  0.099609, idx 281, n02123045 tabby, tabby cat

#  0.071777, idx 287, n02127052 lynx, catamount

#  0.041504, idx 331, n02326432 hare

 

############################################

# sample-4: 分类器多批

############################################

./bin/cvi_sample_classifier_multi_batch \

    $MODEL_PATH/mobilenet_v2_bs1_bs4.cvimodel \

   ./data/cat.jpg \

   ./data/synset_words.txt

 

# TOP_K[5]:

#  0.326172, idx 282, n02123159 tiger cat

#  0.326172, idx 285, n02124075 Egyptian cat

#  0.099609, idx 281, n02123045 tabby, tabby cat

#  0.071777, idx 287, n02127052 lynx, catamount

#  0.041504, idx 331, n02326432 hare

同时提供脚本作为参考,执行效果与直接运行相同,如下:

./run_classifier.sh

./run_classifier_bf16.sh

./run_classifier_fused_preprocess.sh

./run_classifier_multi_batch.sh

在cvitek_tpu_sdk/samples/samples_extra目录下有更多的samples,可供参考:

./bin/cvi_sample_detector_yolo_v3_fused_preprocess \

    $MODEL_PATH/yolo_v3_416_fused_preprocess_with_detection.cvimodel \

   ./data/dog.jpg \

    yolo_v3_out.jpg

 

./bin/cvi_sample_detector_yolo_v5_fused_preprocess \

    $MODEL_PATH/yolov5s_fused_preprocess.cvimodel \

   ./data/dog.jpg \

    yolo_v5_out.jpg

 

./bin/cvi_sample_detector_yolox_s \

    $MODEL_PATH/yolox_s.cvimodel \

   ./data/dog.jpg \

    yolox_s_out.jpg

 

./bin/cvi_sample_alphapose_fused_preprocess \

    $MODEL_PATH/yolo_v3_416_fused_preprocess_with_detection.cvimodel \

    $MODEL_PATH/alphapose_fused_preprocess.cvimodel \

   ./data/pose_demo_2.jpg \

    alphapose_out.jpg

 

./bin/cvi_sample_fd_fr_fused_preprocess \

    $MODEL_PATH/retinaface_mnet25_600_fused_preprocess_with_detection.cvimodel \

    $MODEL_PATH/arcface_res50_fused_preprocess.cvimodel \

   ./data/obama1.jpg \

   ./data/obama2.jpg

2) 交叉编译示例程序
发布包有samples的源代码,按照方法在Docker环境下交叉编译samples程序,然后在evb上运行,命令如下:

//第6章/cvitek_tpu_sdk.py

需要如下文件:

1)cvitek_tpu_sdk_[cv182x|cv182x_uclibc|cv183x|cv181x_glibc32|cv181x_musl_riscv64_rvv|cv180x_musl_riscv64_rvv]].tar.gz

2)cvitek_tpu_samples.tar.gz

aarch 64位 (如cv183x aarch64位平台)

TPU sdk准备:

tar zxf host-tools.tar.gz

tar zxf cvitek_tpu_sdk_cv183x.tar.gz

export PATH=$PWD/host-tools/gcc/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin:$PATH

export TPU_SDK_PATH=$PWD/cvitek_tpu_sdk

cd cvitek_tpu_sdk && source./envs_tpu_sdk.sh && cd..

编译samples,安装至install_samples目录:

tar zxf cvitek_tpu_samples.tar.gz

cd cvitek_tpu_samples

mkdir build_soc

cd build_soc

cmake -G Ninja \

    -DCMAKE_BUILD_TYPE=RELEASE \

    -DCMAKE_C_FLAGS_RELEASE=-O3 \

    -DCMAKE_CXX_FLAGS_RELEASE=-O3 \

    -DCMAKE_TOOLCHAIN_FILE=$TPU_SDK_PATH/cmake/toolchain-aarch64-linux.cmake \

    -DTPU_SDK_PATH=$TPU_SDK_PATH \

    -DOPENCV_PATH=$TPU_SDK_PATH/opencv \

    -DCMAKE_INSTALL_PREFIX=../install_samples \

   ..

cmake --build. --target install

arm 32位 (如cv183x平台32位、cv182x平台)

TPU sdk准备:

tar zxf host-tools.tar.gz

tar zxf cvitek_tpu_sdk_cv182x.tar.gz

export TPU_SDK_PATH=$PWD/cvitek_tpu_sdk

export PATH=$PWD/host-tools/gcc/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin:$PATH

cd cvitek_tpu_sdk && source./envs_tpu_sdk.sh && cd..

如果docker版本低于1.7,则需要更新32位系统库(只需一次):

dpkg --add-architecture i386

apt-get update

apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386

编译samples,安装至install_samples目录:

tar zxf cvitek_tpu_samples.tar.gz

cd cvitek_tpu_samples

mkdir build_soc

cd build_soc

cmake -G Ninja \

    -DCMAKE_BUILD_TYPE=RELEASE \

    -DCMAKE_C_FLAGS_RELEASE=-O3 \

    -DCMAKE_CXX_FLAGS_RELEASE=-O3 \

    -DCMAKE_TOOLCHAIN_FILE=$TPU_SDK_PATH/cmake/toolchain-linux-gnueabihf.cmake \

    -DTPU_SDK_PATH=$TPU_SDK_PATH \

    -DOPENCV_PATH=$TPU_SDK_PATH/opencv \

    -DCMAKE_INSTALL_PREFIX=../install_samples \

   ..

cmake --build. --target install

uclibc 32位平台 (cv182x uclibc平台)

TPU sdk准备:

tar zxf host-tools.tar.gz

tar zxf cvitek_tpu_sdk_cv182x_uclibc.tar.gz

export TPU_SDK_PATH=$PWD/cvitek_tpu_sdk

export PATH=$PWD/host-tools/gcc/arm-cvitek-linux-uclibcgnueabihf/bin:$PATH

cd cvitek_tpu_sdk && source./envs_tpu_sdk.sh && cd..

如果docker版本低于1.7,则需要更新32位系统库(只需一次):

dpkg --add-architecture i386

apt-get update

apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386

编译samples,安装至install_samples目录:

tar zxf cvitek_tpu_samples.tar.gz

cd cvitek_tpu_samples

mkdir build_soc

cd build_soc

cmake -G Ninja \

    -DCMAKE_BUILD_TYPE=RELEASE \

    -DCMAKE_C_FLAGS_RELEASE=-O3 \

    -DCMAKE_CXX_FLAGS_RELEASE=-O3 \

    -DCMAKE_TOOLCHAIN_FILE=$TPU_SDK_PATH/cmake/toolchain-linux-uclibc.cmake \

    -DTPU_SDK_PATH=$TPU_SDK_PATH \

    -DOPENCV_PATH=$TPU_SDK_PATH/opencv \

    -DCMAKE_INSTALL_PREFIX=../install_samples \

   ..

cmake --build. --target install

riscv64位 musl平台 (如cv181x、cv180x riscv64位 musl平台)

TPU sdk准备:

tar zxf host-tools.tar.gz

tar zxf cvitek_tpu_sdk_cv181x_musl_riscv64_rvv.tar.gz

export TPU_SDK_PATH=$PWD/cvitek_tpu_sdk

export PATH=$PWD/host-tools/gcc/riscv64-linux-musl-x86_64/bin:$PATH

cd cvitek_tpu_sdk && source./envs_tpu_sdk.sh && cd..

编译samples,安装至install_samples目录:

tar zxf cvitek_tpu_samples.tar.gz

cd cvitek_tpu_samples

mkdir build_soc

cd build_soc

cmake -G Ninja \

    -DCMAKE_BUILD_TYPE=RELEASE \

    -DCMAKE_C_FLAGS_RELEASE=-O3 \

    -DCMAKE_CXX_FLAGS_RELEASE=-O3 \

    -DCMAKE_TOOLCHAIN_FILE=$TPU_SDK_PATH/cmake/toolchain-riscv64-linux-musl-x86_64.cmake \

    -DTPU_SDK_PATH=$TPU_SDK_PATH \

    -DOPENCV_PATH=$TPU_SDK_PATH/opencv \

    -DCMAKE_INSTALL_PREFIX=../install_samples \

   ..

cmake --build. --target install

riscv64位 glibc平台 (如cv181x、cv180x riscv64位glibc平台)

TPU sdk准备:

tar zxf host-tools.tar.gz

tar zxf cvitek_tpu_sdk_cv181x_glibc_riscv64.tar.gz

export TPU_SDK_PATH=$PWD/cvitek_tpu_sdk

export PATH=$PWD/host-tools/gcc/riscv64-linux-x86_64/bin:$PATH

cd cvitek_tpu_sdk && source./envs_tpu_sdk.sh && cd..

编译samples,安装至install_samples目录:

tar zxf cvitek_tpu_samples.tar.gz

cd cvitek_tpu_samples

mkdir build_soc

cd build_soc

cmake -G Ninja \

    -DCMAKE_BUILD_TYPE=RELEASE \

    -DCMAKE_C_FLAGS_RELEASE=-O3 \

    -DCMAKE_CXX_FLAGS_RELEASE=-O3 \

    -DCMAKE_TOOLCHAIN_FILE=$TPU_SDK_PATH/cmake/toolchain-riscv64-linux-x86_64.cmake \

    -DTPU_SDK_PATH=$TPU_SDK_PATH \

    -DOPENCV_PATH=$TPU_SDK_PATH/opencv \

    -DCMAKE_INSTALL_PREFIX=../install_samples \

   ..

cmake --build. --target install

3) docker环境仿真运行的samples程序,命令如下:

//第6章/cvimodel_samples.py

需要如下文件:

1)cvitek_MLIR_ubuntu-18.04.tar.gz

2)cvimodel_samples_[cv182x|cv183x|cv181x|cv180x].tar.gz

3)cvitek_tpu_samples.tar.gz

TPU sdk准备:

tar zxf cvitek_MLIR_ubuntu-18.04.tar.gz

source cvitek_MLIR/cvitek_envs.sh

编译samples,安装至install_samples目录:

tar zxf cvitek_tpu_samples.tar.gz

cd cvitek_tpu_samples

mkdir build_soc

cd build_soc

cmake -G Ninja \

   -DCMAKE_BUILD_TYPE=RELEASE \

   -DCMAKE_C_FLAGS_RELEASE=-O3 \

   -DCMAKE_CXX_FLAGS_RELEASE=-O3 \

   -DTPU_SDK_PATH=$MLIR_PATH/tpuc \

   -DCNPY_PATH=$MLIR_PATH/cnpy \

   -DOPENCV_PATH=$MLIR_PATH/opencv \

   -DCMAKE_INSTALL_PREFIX=../install_samples \

  ..

cmake --build. --target install

运行samples程序:

# envs

tar zxf cvimodel_samples_cv183x.tar.gz

export MODEL_PATH=$PWD/cvimodel_samples

source cvitek_MLIR/cvitek_envs.sh

 

# get cvimodel info

cd../install_samples

./bin/cvi_sample_model_info $MODEL_PATH/mobilenet_v2.cvimodel

其他samples运行命令参照EVB运行命令

BM168x测试指南

配置系统环境

如果是首次使用Docker, 那么请使用 开发环境配置 中的方法安装 并配置Docker。同时, 中会使用到 git-lfs, 如果首次使用 git-lfs 可执行下述命 令进行安装与配置(仅首次执行, 同时该配置是在用户自己系统中, 并非Docker 容器中):

$ curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash

$ sudo apt-get install git-lfs

获取 model-zoo 模型 1

在 tpu-MLIR_xxxx.tar.gz (tpu-MLIR的发布包)的同级目录下, 使用以下命令克隆 model-zoo 工程:

$ git clone --depth=1 需要如下文件:

1)cvitek_MLIR_ubuntu-18.04.tar.gz

2)cvimodel_samples_[cv182x|cv183x|cv181x|cv180x].tar.gz

3)cvitek_tpu_samples.tar.gz

TPU sdk准备:

tar zxf cvitek_MLIR_ubuntu-18.04.tar.gz

source cvitek_MLIR/cvitek_envs.sh

编译samples,安装至install_samples目录:

tar zxf cvitek_tpu_samples.tar.gz

cd cvitek_tpu_samples

mkdir build_soc

cd build_soc

cmake -G Ninja \

   -DCMAKE_BUILD_TYPE=RELEASE \

   -DCMAKE_C_FLAGS_RELEASE=-O3 \

   -DCMAKE_CXX_FLAGS_RELEASE=-O3 \

   -DTPU_SDK_PATH=$MLIR_PATH/tpuc \

   -DCNPY_PATH=$MLIR_PATH/cnpy \

   -DOPENCV_PATH=$MLIR_PATH/opencv \

   -DCMAKE_INSTALL_PREFIX=../install_samples \

  ..

cmake --build. --target install

运行samples程序:

# envs

tar zxf cvimodel_samples_cv183x.tar.gz

export MODEL_PATH=$PWD/cvimodel_samples

source cvitek_MLIR/cvitek_envs.sh

 

# get cvimodel info

cd../install_samples

./bin/cvi_sample_model_info $MODEL_PATH/mobilenet_v2.cvimodel

其他samples运行命令参照EVB运行命令

BM168x测试指南

配置系统环境

如果是首次使用Docker, 那么请使用 开发环境配置 中的方法安装 并配置Docker。同时, 中会使用到 git-lfs, 如果首次使用 git-lfs 可执行下述命 令进行安装与配置(仅首次执行, 同时该配置是在用户自己系统中, 并非Docker 容器中):

$ curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash

$ sudo apt-get install git-lfs

获取 model-zoo 模型 1

在 tpu-MLIR_xxxx.tar.gz (tpu-MLIR的发布包)的同级目录下, 使用以下命令克隆 model-zoo 工程:

$ git clone --depth=1 https://github.com/sophgo/model-zoo

$ cd model-zoo

$ git lfs pull --include *.onnx,*.jpg,*.JPEG --exclude=""

$ cd../

如果已经克隆过 model-zoo 可以执行以下命令同步模型到最新状态:

$ cd model-zoo

$ git pull

$ git lfs pull --include "*.onnx,*.jpg,*.JPEG" --exclude=""

$ cd../

此过程会从 GitHub 上下载大量数据。由于具体网络环境的差异, 此过程可能耗时较长。

如果获得了SOPHGO提供的 model-zoo 测试包, 可以执行以下操作创建并 设置好 model-zoo。完成此步骤后直接获取 tpu-perf 工具。

$ mkdir -p model-zoo

$ tar -xvf path/to/model-zoo_.tar.bz2 --strip-components=1 -C model-zoo

https://github.com/sophgo/model-zoo

$ cd model-zoo

$ git lfs pull --include *.onnx,*.jpg,*.JPEG --exclude=""

$ cd../

如果已经克隆过 model-zoo 可以执行以下命令同步模型到最新状态:

$ cd model-zoo

$ git pull

$ git lfs pull --include "*.onnx,*.jpg,*.JPEG" --exclude=""

$ cd../

此过程会从 GitHub 上下载大量数据。由于具体网络环境的差异, 此过程可能耗时较长。

如果获得了SOPHGO提供的 model-zoo 测试包, 可以执行以下操作创建并 设置好 model-zoo。完成此步骤后直接获取 tpu-perf 工具。

$ mkdir -p model-zoo

$ tar -xvf path/to/model-zoo_.tar.bz2 --strip-components=1 -C model-zoo

获取 tpu-perf 工具
从 https://github.com/sophgo/tpu-perf/releases 地址下载最新的 tpu-perf wheel安装包。例如: tpu_perf-x.x.x-py3-none-manylinux2014_x86_64.whl。并将 tpu-perf 包放置到与 model-zoo 同一级目录下。此时的目录结构应该为如下形式:
├── tpu_perf-x.x.x-py3-none-manylinux2014_x86_64.whl
├── tpu-MLIR_xxxx.tar.gz
└── model-zoo
测试流程
解压SDK并创建Docker容器
在 tpu-MLIR_xxxx.tar.gz 目录下(注意, tpu-MLIR_xxxx.tar.gz 与 model-zoo 需要在同一级目录), 执行命令如下:

//第6章/tpu-MLIR_xxxx.sh

$ tar zxf tpu-MLIR_xxxx.tar.gz

$ docker pull sophgo/tpuc_dev:latest

$ docker run --rm --name myname -v $PWD:/workspace -it sophgo/tpuc_dev:latest

运行命令后会处于Docker的容器中。

设置环境变量并安装 tpu-perf

使用以下命令完成设置运行测试所需的环境变量:

$ cd tpu-MLIR_xxxx

$ source envsetup.sh

该过程结束后不会有任何提示。之后使用以下命令安装 tpu-perf:

$ pip3 install../tpu_perf-x.x.x-py3-none-manylinux2014_x86_64.whl

运行测试

编译模型

model-zoo 的相关 confg.yaml 配置了SDK的测试内容。例如: resnet18的 配置文件为 model-zoo/vision/classification/resnet18-v2/config.yaml。

执行以下命令, 运行全部测试样例:

$ cd../model-zoo

$ python3 -m tpu_perf.build --MLIR -l full_cases.txt

此时会编译以下模型:

* efficientnet-lite4

* mobilenet_v2

* resnet18

* resnet50_v2

* shufflenet_v2

* squeezenet1.0

* vgg16

* yolov5s

命令正常结束后, 会看到新生成的输出文件夹(测试输出内容都在该文件夹中)。 修改 输出文件夹的属性, 以保证其可以被Docker外系统访问。

$ chmod -R a+rw output

测试模型性能
配置SOC设备
注意: 如果设备是 PCIE 板卡, 可以直接跳过该节内容。
性能测试只依赖于 libsophon 运行环境, 所以在工具链编译环境编译完的模型连同 model-zoo 整个打包, 就可以在 SOC 环境使用 tpu_perf 进行性能与精度测试。 但是, SOC设备上存储有限, 完整的 model-zoo 与编译输出内容可能无法完整拷贝到 SOC 中。这里介绍一种通过 linux nfs 远程文件系统挂载来实现在 SOC 设备上运行测试的方法,命令如下:

//第6章/chmod_rw_output.sh

$ chmod -R a+rw output

首先, 在工具链环境服务器host 系统安装 nfs 服务:

$ sudo apt install nfs-kernel-server

在 /etc/exports 中添加以下内容(配置共享目录):

/the/absolute/path/of/model-zoo *(rw,sync,no_subtree_check,no_root_squash)

其中 * 表示所有人都可以访问该共享目录, 也可以配置成特定网段或 IP 可访问, 如:

/the/absolute/path/of/model-zoo 192.168.43.0/24(rw,sync,no_subtree_check,no_root_squash)

然后执行如下命令使配置生效:

$ sudo exportfs -a

$ sudo systemctl restart nfs-kernel-server

另外, 需要为 dataset 目录下的图片添加读取权限:

chmod -R +r path/to/model-zoo/dataset

在 SOC 设备上安装客户端并挂载该共享目录:

$ mkdir model-zoo

$ sudo apt-get install -y nfs-common

$ sudo mount -t nfs:/path/to/model-zoo./model-zoo

这样便可以在 SOC 环境访问测试目录。SOC 测试其余的操作与 PCIE 基本一致, 请参考下 文进行操作; 运行环境命令执行位置的差别, 已经在执行处添加说明。

运行测试

运行测试需要在 Docker 外面的环境(此处假设已经安装并配置好了1684X设备与 驱动)中进行, 可以退出 Docker 环境:

$ exit

1.  PCIE 板卡下运行以下命令, 测试生成的 bmodel 性能。

$ pip3 install./tpu_perf-*-py3-none-manylinux2014_x86_64.whl

$ cd model-zoo

$ python3 -m tpu_perf.run --MLIR -l full_cases.txt

注意:如果主机上安装了多块SOPHGO的加速卡,可以在使用 tpu_perf 时,通过添加 --devices id 来指定 tpu_perf 的运行设备。如:

$ python3 -m tpu_perf.run --devices 2 --MLIR -l full_cases.txt

1.  SOC 设备使用以下步骤, 测试生成的 bmodel 性能。

从 https://github.com/sophgo/tpu-perf/releases 地址下载最新的 tpu-perf tpu_perf-x.x.x-py3-none-manylinux2014_aarch64.whl 文件到SOC设备上并执行 以下操作:

$ pip3 install./tpu_perf-x.x.x-py3-none-manylinux2014_aarch64.whl

$ cd model-zoo

$ python3 -m tpu_perf.run --MLIR -l full_cases.txt

运行结束后, 性能数据在output/stats.csv 中可以获得。该文件中记录了相关模型的运行时间、计算资源利用率与带宽利用率。
posted @ 2024-04-17 05:31  吴建明wujianming  阅读(80)  评论(0编辑  收藏  举报