目录
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
从上面网址下载
下载完之后解压,里面包含很多内容
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模型文件确定,
或者直接从输入那里也可以看
转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 文档
具体脚本是:
fp_forward.py yolov5s.mlir --fpfwd_outputs 326_Conv,378_Conv,430_Conv --chip bm1684 -o qtable
这里先说一下为什么要局部不量化,用netron打开onnx模型:
这里的12就是7个分类+5,5就是xywh和box的得分,但是这里得分肯定很小,而宽高比较大,他们之间差距太大了,要是量化那么可能得分就是0了,所以这里不量化。
然后讲一下命令里面的326_Conv,378_Conv,430_Conv这三个是怎么来的,首先用netron打开onnx模型,找到模型中最后一个conv的地方,
记住这里的326,然后用notepad打开/workspace/Release_v2312-LTS/Release_v2312-LTS/tpu-mlir_20231116_054500/model_yolov5s/workspace/yolov5s.mlir。里面可以看到
也就是给加了个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 执行代码