目录

1 TPU-MLIR简介

2 开发环境搭建

2.1 下载镜像

2.2 下载SDK

2.3 创建容器

2.4 加载tpu-mlir

3 准备工作目录

4 onnx转mlir文件

5 mlir转INT8 模型

5.1 生成校准表

5.2 便以为INT8对称量化模型

6 混精度

6.1 .1 加载tpu-mlir

6.1.2准备工作目录

6.1.3 验证原始模型

6.1.4 转成INT8对称量化模型

6.1.4.1 第一步:转成F32 mlir

6.1.4.2 第二步:生成calibartion table

6.1.4.3 第三步:转对称量化模型

6.1.5 转成混精度量化模型

6.1.5.1. 第一步: 生成混精度量化表

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

参考文献:


之前是用nntc转算能科技的模型的,这次技术支持建议我使用mlir工具进行模型转换,于是看一下mlir的介绍资料和使用方法,并做一下笔记。

1 TPU-MLIR简介

机器学习算法模型 部署运行 算法模型工具_算能科技

上图就是tpu-mlir的整体架构图,主要分两步, 一是通过 model_transform.py 将原始模型 转换成mlir文件, 二是通过 model_deploy.py 将mlir文件转换成bmodel/cvimodel。

如果要转INT8模型, 则需要调用 run_calibration.py 生成校准表, 然后传给 model_deploy.py

如果INT8模型不满足精度需要, 可以调用 run_qtable.py 生成量化表, 用来决定哪些层采用浮点计算, 然后传给 model_deploy.py 生成混精度模型。

2 开发环境搭建

2.1 下载镜像

docker pull sophgo/tpuc_dev:latest

官网文档上说的是用v2.2,这里直接拉取最新的镜像。

2.2 下载SDK

技术资料

从上面网址下载

机器学习算法模型 部署运行 算法模型工具_算能科技_02

 下载完之后解压,里面包含很多内容

机器学习算法模型 部署运行 算法模型工具_机器学习算法模型 部署运行_03

2.3 创建容器

docker run --privileged --name tpu_mlir -v $PWD:/workspace -it sophgo/tpuc_dev:latest

2.4 加载tpu-mlir

cd /workspace/Release_v2312-LTS/Release_v2312-LTS/tpu-mlir_20231116_054500/tpu-mlir_v1.3.140-g3180ff37-20231116
source envsetup.sh

3 准备工作目录

建立 model_yolov5s 目录, 注意是与tpu-mlir同级目录; 并把模型文件和图片文件都 放入 model_yolov5s 目录中。

cd ..
mkdir model_yolov5s
cd model_yolov5s
#然后把onnx模型和图片拷贝到当前目录
mkdir workspace
cd workspace

4 onnx转mlir文件

model_transform.py \
    --model_name yolov5s \
    --model_def ../jishui_20231007.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 339,391,443 \
    --test_input ../calib/00a3b6b267584c2ea01ec50a84432e56.jpg \
    --test_result yolov5s_top_outputs.npz \
    --mlir yolov5s.mlir

    --output_names 320,340,354 这里的三个具体名字通过用netron看onnx模型文件确定,

机器学习算法模型 部署运行 算法模型工具_MLIR_04

或者直接从输入那里也可以看

机器学习算法模型 部署运行 算法模型工具_python_05

转onnx的时候,models/yolo.py里面的forward函数内容如下:

#转BM模型
    def forward(self, x):
        z = []  # inference output
        for i in range(self.nl):
            x[i] = self.m[i](x[i])  # conv
            bs, _, ny, nx = x[i].shape  # x(bs,255,20,20) to x(bs,3,20,20,85)
            x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()

            if not self.training:  # inference
                if self.onnx_dynamic or self.grid[i].shape[2:4] != x[i].shape[2:4]:
                    self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i)

                y = x[i].sigmoid()
                if self.inplace:
                    y[..., 0:2] = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i]  # xy
                    y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # wh
                else:  # for YOLOv5 on AWS Inferentia https://github.com/ultralytics/yolov5/pull/2953
                    xy = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i]  # xy
                    wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # wh
                    y = torch.cat((xy, wh, y[..., 4:]), -1)
                z.append(y.view(bs, -1, self.no))
                
        #return x if self.training else x                       # 3个输出
        #return x if self.training else (torch.cat(z, 1))     # 1个输出
        return x if self.training else (torch.cat(z, 1), x)  # 4个输出

5 mlir转INT8 模型

5.1 生成校准表

run_calibration.py yolov5s.mlir \
    --dataset ../calib \
    --input_num 200 \
    -o yolov5s_cali_table

这里calib文件夹里面是校准图片,里面是200张图片和200个标注文件。

5.2 便以为INT8对称量化模型

model_deploy.py \
    --mlir yolov5s.mlir \
    --quantize INT8 \
    --calibration_table yolov5s_cali_table \
    --chip bm1684 \
    --test_input yolov5s_in_f32.npz \
    --test_reference yolov5s_top_outputs.npz \
    --tolerance 0.85,0.45 \
    --model yolov5s_1684x_int8_sym.bmodel

6 混精度

如果想得到3输出或者4输出的YOLOV5,那么用上面的步骤就可以了,但是如果想要1输出的yolov5模型,那么yolo.py的内容修改如下

#算能转模型
    def forward(self, x):
        z = []  # inference output
        for i in range(self.nl):
            x[i] = self.m[i](x[i])  # conv
            bs, _, ny, nx = x[i].shape  # x(bs,255,20,20) to x(bs,3,20,20,85)
            x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()

            if not self.training:  # inference
                if self.onnx_dynamic or self.grid[i].shape[2:4] != x[i].shape[2:4]:
                    self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i)

                y = x[i].sigmoid()
                if self.inplace:
                    y[..., 0:2] = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i]  # xy
                    y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # wh
                else:  # for YOLOv5 on AWS Inferentia https://github.com/ultralytics/yolov5/pull/2953
                    xy = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i]  # xy
                    wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # wh
                    y = torch.cat((xy, wh, y[..., 4:]), -1)
                z.append(y.view(bs, -1, self.no))
                
        #return x if self.training else x                     # 3个输出
        return x if self.training else (torch.cat(z, 1))      # 1个输出
        #return x if self.training else (torch.cat(z, 1), x)  # 4个输出

这样导出onnx模型之后,利用前面的步骤转模型会下面的错误:

Target    yolov5s_bm1684_int8_sym_tpu_outputs.npz
Reference yolov5s_top_outputs.npz
npz compare FAILED.
compare output_Concat: 100%|█████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00,  9.17it/s]
Traceback (most recent call last):
  File "/workspace/Release_v2312-LTS/Release_v2312-LTS/tpu-mlir_20231116_054500/tpu-mlir_v1.3.140-g3180ff37-20231116/python/tools/model_deploy.py", line 311, in <module>
    tool.lowering()
  File "/workspace/Release_v2312-LTS/Release_v2312-LTS/tpu-mlir_20231116_054500/tpu-mlir_v1.3.140-g3180ff37-20231116/python/tools/model_deploy.py", line 122, in lowering
    tool.validate_tpu_mlir()
  File "/workspace/Release_v2312-LTS/Release_v2312-LTS/tpu-mlir_20231116_054500/tpu-mlir_v1.3.140-g3180ff37-20231116/python/tools/model_deploy.py", line 211, in validate_tpu_mlir
    f32_blobs_compare(self.tpu_npz, self.ref_npz, self.tolerance, self.excepts)
  File "/workspace/Release_v2312-LTS/Release_v2312-LTS/tpu-mlir_20231116_054500/tpu-mlir_v1.3.140-g3180ff37-20231116/python/utils/mlir_shell.py", line 181, in f32_blobs_compare
    _os_system(cmd)
  File "/workspace/Release_v2312-LTS/Release_v2312-LTS/tpu-mlir_20231116_054500/tpu-mlir_v1.3.140-g3180ff37-20231116/python/utils/mlir_shell.py", line 50, in _os_system
    raise RuntimeError("[!Error]: {}".format(cmd_str))
RuntimeError: [!Error]: npz_tool.py compare yolov5s_bm1684_int8_sym_tpu_outputs.npz yolov5s_top_outputs.npz --tolerance 0.85,0.45 --except - -vv

这是因为比对没过,单输出有concat操作,需要混精度,具体操作如下:7. 量化与量化调优 — TPU-MLIR 1.6.215 文档

6.1 .1 加载tpu-mlir

这个如果前面已经做了就不需要做了

cd /workspace/Release_v2312-LTS/Release_v2312-LTS/tpu-mlir_20231116_054500/tpu-mlir_v1.3.140-g3180ff37-20231116
source envsetup.sh

6.1.2准备工作目录

准备工作目录的操作和前面一样,建立 model_yolov5s 目录, 注意是与tpu-mlir同级目录; 并把模型文件和图片文件都 放入 model_yolov5s 目录中。

cd ..
mkdir model_yolov5s
cd model_yolov5s
#然后把onnx模型和图片拷贝到当前目录
mkdir workspace
cd workspace

6.1.3 验证原始模型

6.1.4 转成INT8对称量化模型

6.1.4.1 第一步:转成F32 mlir

这个也跟前面的操作是一样的。

model_transform.py \
    --model_name yolov5s \
    --model_def ../firefog_20231115_7_class.onnx \
    --input_shapes [[1,3,640,640]] \
    --scale 0.0039216,0.0039216,0.0039216 \
    --pixel_format rgb \
    --keep_aspect_ratio \
    --output_names=output \
    --mlir yolov5s.mlir
6.1.4.2 第二步:生成calibartion table

这个也跟前面操作一样

run_calibration.py yolov5s.mlir     --dataset ../calib     --input_num 200     -o yolov5s_cali_table

6.1.5 转成混精度量化模型

在转int8对称量化模型的基础上, 执行如下步骤。

6.1.5.1. 第一步: 生成混精度量化表

官网上说明文档是,不要用这个:run_qtable.py yolov5s.mlir --dataset ../calib --calibration_table yolov5s_cali_table --chip bm1684 --min_layer_cos 0.999  --expected_cos 0.9999 -o yolov5s_qtable

改为用官网另一个地方的:7. 量化与量化调优 — TPU-MLIR 1.6.215 文档

机器学习算法模型 部署运行 算法模型工具_MLIR_06

 

机器学习算法模型 部署运行 算法模型工具_机器学习算法模型 部署运行_07

具体脚本是:

fp_forward.py yolov5s.mlir --fpfwd_outputs 326_Conv,378_Conv,430_Conv --chip bm1684 -o qtable

这里先说一下为什么要局部不量化,用netron打开onnx模型:

机器学习算法模型 部署运行 算法模型工具_加载_08

这里的12就是7个分类+5,5就是xywh和box的得分,但是这里得分肯定很小,而宽高比较大,他们之间差距太大了,要是量化那么可能得分就是0了,所以这里不量化。

然后讲一下命令里面的326_Conv,378_Conv,430_Conv这三个是怎么来的,首先用netron打开onnx模型,找到模型中最后一个conv的地方,

机器学习算法模型 部署运行 算法模型工具_加载_09

记住这里的326,然后用notepad打开/workspace/Release_v2312-LTS/Release_v2312-LTS/tpu-mlir_20231116_054500/model_yolov5s/workspace/yolov5s.mlir。里面可以看到

机器学习算法模型 部署运行 算法模型工具_算能科技_10

也就是给加了个conv后缀,所以命令里面写326_Conv,另外两个的名称查找方式也是类似的。

6.1.5.2. 第二步: 生成混精度量化模型
model_deploy.py \
    --mlir yolov5s.mlir \
    --quantize INT8 \
    --quantize_table qtable \
    --calibration_table yolov5s_cali_table \
    --chip bm1684 \
    --model yolovs_firfog_7class.bmodel

参考文献:

TPU-MLIR快速入门手册 — TPU-MLIR 1.2 文档

https://github.com/sophgo/tpu-mlir

技术资料

TPU-MLIR敏感层搜索功能介绍 | TPUMLIR 开源工具链项目 | 通用 AI 编译器工具链项目,高效将模型编译生成 TPU 执行代码

1. 声明 — SOPHONSDKUserGuide v23.10.01 文档