手把手教你将yolov8模型转换为int8模型
作者:开挖机的程序猿
文章作于 2024-8-9
一、前言
-
本教程的背景是使用maixcam时需要自己训练模型,使用yolov8训练出pt权重后转换为int8模型以在低算力嵌入式平台使用
-
本教程涉及:1、pt文件转onnx 2、根据sipeed教程使用TPU平台进行模型转换 3、输出为
.cvimodel
和.mud
文件 -
希望文章能够被sipeed看到吧
二、pt文件转onnx文件
-
我们需要将yolov8的pt文件转为onnx才可以进行int8模型的转换
-
在之前的文章中,已经教会大家如何部署yolov8以及使用yolov8训练自己的模型,还不会的读者可以先去阅读这两篇文章,跟着做一做,这里默认你已经知道best.pt文件的位置了
1、进入conda环境
-
这里还继续使用前面文章里创建的conda库
-
作者已经在pycharm中配置好了运行环境为"pytorch"的conda环境,配置教程,可以直接在pycharm项目里运行python脚本
2、安装onnx库
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple onnx
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple onnxruntime
3、编写模型转换脚本
export_onnx.py
import onnx
# 加载训练好的模型
model = YOLO("./datasets/runs/detect/train16/weights/best.pt")
#注意更改你的pt文件路径
# 导出模型为ONNX格式
model.export(format="onnx")
print("模型已成功导出为ONNX格式")
# 加载ONNX模型
onnx_model = onnx.load('./datasets/runs/detect/train16/weights/best.onnx')
# 检查模型是否有效
try:
onnx.checker.check_model(onnx_model)
print("ONNX模型验证成功")
except onnx.checker.ValidationError as e:
print(f"ONNX模型验证失败: {e}")
-
注意更改待转换的pt文件的路径
-
运行之前记得确保运行环境安装了
onnx
和onnxruntime
库 -
这个脚本放在项目根目录下就行,或者你随便找个地方存放
4、执行脚本
python3 export_onnx.py
- 或者直接点击pycharm的运行当前文件
5、保存onnx文件
- 执行上面脚本后,如果路径正确,模型没问题,你就能在脚本同级目录下看到onnx文件了,建议将其复制后重命名为一个看起来标准的模型名称,不要用中文
三、编写.mud文件
[basic]
type = cvimodel
model = yolov8n.cvimodel
[extra]
model_type = yolov8
input_type = rgb
mean = 0, 0, 0
scale = 0.00392156862745098, 0.00392156862745098, 0.00392156862745098
labels = person, bicycle, car, motorcycle, airplane, bus, train, truck, boat, traffic light, fire hydrant, stop sign, parking meter, bench, bird, cat, dog, horse, sheep, cow, elephant, bear, zebra, giraffe, backpack, umbrella, handbag, tie, suitcase, frisbee, skis, snowboard, sports ball, kite, baseball bat, baseball glove, skateboard, surfboard, tennis racket, bottle, wine glass, cup, fork, knife, spoon, bowl, banana, apple, sandwich, orange, broccoli, carrot, hot dog, pizza, donut, cake, chair, couch, potted plant, bed, dining table, toilet, tv, laptop, mouse, remote, keyboard, cell phone, microwave, oven, toaster, sink, refrigerator, book, clock, vase, scissors, teddy bear, hair drier, toothbrush
-
这是maix官方给的示例,原文章
-
你要做的是:
1、把这段配置文件复制下来,粘贴到一个文本文件里
2、修改
model = xxx.cvimodel
,xxx是你最终需要使用的模型的名称,刚刚导出的onnx如果已经重命名了,就使用同一个名称,不要用中文3、
model_type = yolov8
不要动,因为这里用的是yolov8的模型,之后会出yolov5系列4、
labels =
选项,查看你标注的训练集的classes.txt文件,将这里的值删掉,将你的类别粘贴进来,一样用英文逗号分隔,不要用中文5、其他的参数不懂就不要更改,作者也不太懂,没更改,有懂得佬可以评论讲解
-
注意,这里作者强烈建议将刚刚的训练好的.pt文件,.onnx文件,这里的.mud文件,都命名为一个名字,并且把它们放在一个单独的文件夹里,这样对于模型管理非常有好处,如图
四、编写转换脚本
convert_yolov8_to_cvimodel.sh
#!/bin/bash
set -e
net_name=model_24072801
input_w=640
input_h=640
# mean: 0, 0, 0
# std: 255, 255, 255
# mean
# 1/std
# mean: 0, 0, 0
# scale: 0.00392156862745098, 0.00392156862745098, 0.00392156862745098
mkdir -p workspace
cd workspace
# convert to mlir
model_transform.py \
--model_name ${net_name} \
--model_def ../${net_name}.onnx \
--input_shapes [[1,3,${input_h},${input_w}]] \
--mean "0,0,0" \
--scale "0.00392156862745098,0.00392156862745098,0.00392156862745098" \
--keep_aspect_ratio \
--pixel_format rgb \
--channel_format nchw \
--output_names "/model.22/dfl/conv/Conv_output_0,/model.22/Sigmoid_output_0" \
--test_input ../test.jpg \
--test_result ${net_name}_top_outputs.npz \
--tolerance 0.99,0.99 \
--mlir ${net_name}.mlir
# export bf16 model
# not use --quant_input, use float32 for easy coding
model_deploy.py \
--mlir ${net_name}.mlir \
--quantize BF16 \
--processor cv181x \
--test_input ${net_name}_in_f32.npz \
--test_reference ${net_name}_top_outputs.npz \
--model ${net_name}_bf16.cvimodel
echo "calibrate for int8 model"
# export int8 model
run_calibration.py ${net_name}.mlir \
--dataset ../images \
--input_num 110 \
-o ${net_name}_cali_table
echo "convert to int8 model"
# export int8 model
# add --quant_input, use int8 for faster processing in maix.nn.NN.forward_image
model_deploy.py \
--mlir ${net_name}.mlir \
--quantize INT8 \
--quant_input \
--calibration_table ${net_name}_cali_table \
--processor cv181x \
--test_input ${net_name}_in_f32.npz \
--test_reference ${net_name}_top_outputs.npz \
--tolerance 0.9,0.6 \
--model ${net_name}_int8.cvimodel
-
需要注意的参数:
1、
net_name=model_24072801
这里作者已经更改了,把这里的值改为你模型的名称2、
--output_names "/model.22/dfl/conv/Conv_output_0,/model.22/Sigmoid_output_0" \
这一行,作者已经更改过了,是针对yolov8的参数,不要动。根据sipeed的教程,这里需要你去这个网站https://netron.app/查看你的onnx模型是否支持转换,但是如果你的模型是通过yolo正常训练出来并转换的,就会满足转换条件,一般不需要操心,想了解的可以去看原文章3、
--test_input ../test.jpg \
这一行,需要你准备一张图片,可以是训练集里的一张,但是建议重新准备一张新的测试图片,命名为test.jpg
放进刚刚的文件夹里4、
--dataset ../images \
这一行,在刚刚的文件夹里创建一个images
文件夹,用来存放数据集图片,作者不太清楚应该用新的数据集还是训练用的,偷懒直接用之前的图片集,只需要图片就行,数量一般要100~300张,记住数量5、
--input_num 110 \
紧接着上面的一行,这一行指定要使用的图片数量,数字只能比存入images
的图片数目小或相等6、
--tolerance 0.9,0.6 \
这一行,通过测试的置信度阈值,不建议降低,会对准确度有影响,如果出现最后实在无法通过置信度检验,可以适当降低一点,作者没有更改就通过了 -
把这个.sh文件也放进刚刚的文件夹里
-
注意了,这个脚本自己又创建了一个workspace文件夹来存放中间文件,可能会和接下来的docker环境路径重名,有的用户没注意到,导致路径不正确
五、搭建模型转换环境
-
可以继续看原文章
-
作者这里使用 VMware 虚拟机,使用 Ubuntu22.04 进行转换环境的搭建,虚拟机部分作者不再多说了,注意的地方是,磁盘空间给到 60GB 比较保险,作者之前给了 30GB ,出现docker报空间不足的问题,给到 60GB 没有问题,内存给大点,内核多点,网络保持通畅,配置好sudo
-
注意开共享文件夹,方便和主机传输文件
1、安装docker
-
sipeed给的教程里docker安装对网络要求不太友好,懂得都懂
-
作者偷个懒,docker安装就不讲了,也给别的博主引个流
2、拉取docker镜像并运行
- 虽然上面一步的教程里配置了docker用户组,但是不知道为什么还是会有权限不足的问题,如果你也遇到了,就在前面加个sudo吧
拉取docker镜像:
docker pull sophgo/tpuc_dev:latest
拉取失败了用wget下载:
wget https://sophon-file.sophon.cn/sophon-prod-s3/drive/24/06/14/12/sophgo-tpuc_dev-v3.2_191a433358ad.tar.gz
wget后手动load一下:
docker load -i sophgo-tpuc_dev-v3.2_191a433358ad.tar.gz
再不行,复制这个链接:
https://sophon-file.sophon.cn/sophon-prod-s3/drive/24/06/14/12/sophgo-tpuc_dev-v3.2_191a433358ad.tar.gz`
然后粘贴到主机的浏览器下载,复制下载链接用迅雷也行,挂TZ也行,随你,搞下来就行,然后通过mobaxterm或者共享文件夹还是管他啥东西传到虚拟机里,放到根目录或者/home你能找到的地方,和上面一样load一下,作者好像wget就行了
- 运行docker镜像
docker run --privileged --name tpu-env -v /home/$USER/data:/home/$USER/data -it sophgo/tpuc_dev
官方说明:这就起了一个容器,名叫tpu-env
,并且把本机的~/data
目录挂载到了容器的~/data
,这样就实现了文件共享,并且和宿主机路径一致。
- 注意了,注意了,这里的/data指的是主机的/data路径,进入docker虚拟机后,data在/home/用户名/data下,这个共享路径真的很好用,否则传文件要打一大堆命令
下次启动容器用这个指令:
docker start tpu-env && docker attach tpu-env
退出docker容器用 exit
命令
进入容器后,终端会变成这样的提示:
root@5583eed9e111:/workspace#
5583eed9e111
是容器号,大家应该都不太一样,/workspace
是工作空间,到时候转换模型就在这个目录下进行(其实在哪都行,跟着作者走,保证没问题)
3、安装tpu-mlir
-
这个文件在github,Releases · sophgo/tpu-mlir (github.com),下载慢,进不去,自己用魔法吧
-
然后把这个文件想办法从主机放到Ununtu的data下,作者用的是共享文件夹,在主机上把文件放进共享文件夹,在Ubuntu里就能查看了,同时重命名了文件
teng@Maixcam:~/data$ sudo cp /mnt/hgfs/VM_share/tpu_mlir-1.9-py3-none-any.whl ~/data/tpu_mlir.whl
teng@Maixcam:~/data$ ls
tpu_mlir.whl
- 启动容器
docker start tpu-env && docker attach tpu-env
- 进入docker里的data,防止找不到路径,再演示一遍
root@5583eed9e111:~# cd /home
root@5583eed9e111:/home# ls
teng
root@5583eed9e111:/home# cd teng/
root@5583eed9e111:/home/teng# ls
data
root@5583eed9e111:/home/teng# cd data/
root@5583eed9e111:/home/teng/data# ls
tpu_mlir.whl
这里可以看见,经过几层辗转,whl文件也是进入docker了
- 安装tpu_mlir.whl
pip install tpu_mlir.whl
4、转换int8模型
-
还记得之前准备的文件夹吗,就是把模型文件,sh文件都放进去那个,把文件夹命名个英文名,转移到刚刚docker的workspace下,转移方法和上面whl差不多的,作者不想再详细说了QAQ
-
其实.mud文件,.pt文件可以不用传过去,必要的是onnx文件,.sh文件,images文件夹和里面的几百张图片,test.jpg图片,但是无所谓
-
在docker的workspace目录下找到你传进来的文件夹,cd进入,执行这条命令
chmod +x convert_yolov8_to_cvimodel.sh && ./convert_yolov8_to_cvimodel.sh
出现这些东西等着就行了
2024/08/09 10:29:02 - INFO : TPU-MLIR v1.9-20240715
2024/08/09 10:29:02 - INFO :
_____________________________________________________
| preprocess: |
| (x - mean) * scale |
'-------------------------------------------------------'
config Preprocess args :
resize_dims : same to net input dims
keep_aspect_ratio : True
keep_ratio_mode : letterbox
pad_value : 0
pad_type : center
--------------------------
mean : [0.0, 0.0, 0.0]
scale : [0.003921569, 0.003921569, 0.003921569]
--------------------------
pixel_format : rgb
channel_format : nchw
real input_num = 116
activation_collect_and_calc_th for sample: 0: 0%| | 0/116 [00:00<?, ?it/s][##################################################] 100%
activation_collect_and_calc_th for sample: 1: 2%|█ | 2/116 [00:05<04:56, 2.60s/it][##################################################] 100%
activation_collect_and_calc_th for sample: 2: 3%|█▌ | 3/116 [00:10<06:42, 3.56s/it][##################################################] 100%
activation_collect_and_calc_th for sample: 3: 3%|██▏ | 4/116 [00:15<07:36, 4.08s/it][##################################################] 100%
- 等进度一条条跑完………………
in_similiarity = (1.0, 1.0, inf)
Target model_24072801_cv181x_int8_sym_model_outputs.npz
Reference model_24072801_cv181x_int8_sym_tpu_outputs.npz
npz compare PASSED.
compare /model.24/m.2/Conv_output_0_Conv_f32: 100%|█████████████████████████████████████████████████████████████████████| 3/3 [00:00<00:00, 19.36it/s]
[Success]: npz_tool.py compare model_24072801_cv181x_int8_sym_model_outputs.npz model_24072801_cv181x_int8_sym_tpu_outputs.npz --tolerance 0.99,0.90 --except - -vv
root@5583eed9e111:/workspace#
-
出现这些就说明顺利转换成功了,大概用了10min
-
然后在这个目录下,还会有个workspace目录,cd进去,找到带INT8字样的.cvimodel文件,就是你要的轻量化模型了,想办法转出来,配合之前的.mud文件,还有maixcam的yolov8轻量化官方示例,就可以使用了,但是yolov8和v5转换出来的cvimodel文件不可以混用