前言:
几个小时前,我整理了一个新版(v7.0) YOLOv5
的 NCNN
部署教程:
详细记录u版YOLOv5目标检测ncnn实现(第三版) - 知乎
常言道用新不用旧,我顺便把 YOLOv8
的 NCNN
部署放出来吧!(把自己观念持续贯彻!)
本文将基于当前时间最新的 YOLOv8
进行模型转换和模型推理部署。
背景知识:
YOLO
系列在短短的两年多升级了不知道多少代了,但是最火的还是 YOLOv5
,同属于这个公司的 YOLOv8
想必也是精品,ultralytics
公司野心很大,发布 YOLOv8
的同时也想一统 YOLO
,我们需要持续观望一波!
版本号 v8.0.38
commit id: d99e04daa1290c226a3fae825401361b17ce164c
0x1 缘由
YOLOv8
相对于 YOLOv5
在主干上改动较小,最大的改动就是检测头了,变成了基于 DFL
的 anchor free
模型, 这部分变化较大的就是框解码了,我总结了下面 3 点:
DFL
模块 Reshape+Transpose+Softmax+Conv+Reshape 这套组合是真折腾- 解码已经是
ltrb
格式还要转回xywh
比较麻烦。 - 没有置信度预测分支了,需要自己做
argmax
。
本文同样,重新组织后处理代码,然后实现 NCNN
推理 YOLOv8
。
0x2 pytorch测试和导出torchscript
首先是下载当前时间最新的 YOLOv8 ,当前时间是 : 2023-02-15 16:00
git clone https://github.com/ultralytics/ultralytics.git # clone
cd ultralytics
git checkout d99e04daa1290c226a3fae825401361b17ce164c # switch to commit id
pip install -r requirements.txt # install
pip install -e . # install ultralytics as package
YOLOv8
开始支持命令行启动任务了,对于搞 YOLOv5
的人转过来调试还是不太舒服。
老样子,用默认的配置和预训练模型推理一张图片:
yolo predict model=yolov8s.pt source=ultralytics/assets/bus.jpg show=True
接下来动源码,ultralytics/nn/modules.py
中的 398-411 行注释掉,换成下面的内容:
# def forward(self, x):
# shape = x[0].shape # BCHW
# for i in range(self.nl):
# x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
# if self.training:
# return x
# elif self.dynamic or self.shape != shape:
# self.anchors, self.strides = (x.transpose(0, 1) for x in make_anchors(x, self.stride, 0.5))
# self.shape = shape
#
# box, cls = torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2).split((self.reg_max * 4, self.nc), 1)
# dbox = dist2bbox(self.dfl(box), self.anchors.unsqueeze(0), xywh=True, dim=1) * self.strides
# y = torch.cat((dbox, cls.sigmoid()), 1)
# return y if self.export else (y, x)
def forward(self, x):
z = [] # inference output
for i in range(self.nl):
boxes = self.cv2[i](x[i]).permute(0, 2, 3, 1)
scores = self.cv3[i](x[i]).sigmoid().permute(0, 2, 3, 1)
feat = torch.cat((boxes, scores), -1)
z.append(feat)
return tuple(z)
导出 torchscript:
yolo export model=yolov8s.pt format=torchscript
会在当前工作目录下生成 yolov8s.torchscript
。
用 Netron
看一眼清爽的后处理长什么样:
会有三个上述的结构,这是我们修改的 forward
中的内容,把分类预测分支的 sigmoid
弄到了模型,然后 permute
+ cat
。
0x3 转换
下载编译好的 pnnx 工具包:PNNX
指定 inputshape
并且额外指定 inputshape2
转换成支持动态 shape 输入的模型:
./pnnx yolov8s.torchscript inputshape=[1,3,640,640] inputshape2=[1,3,320,320]
转换日志:
转换主要产物:
看一眼 yolov8s.ncnn.param
:
in0
对应输入,尺寸是 3*640*640 或者动态输入的尺寸。
out0
对应输出,尺寸是 80*80*144 或者动态输出的尺寸。
out1
对应输出,尺寸是 40*40*144 或者动态输出的尺寸。
out2
对应输出,尺寸是 20*20*144 或者动态输出的尺寸。
注意,本文用pnnx转换后的模型,尾巴上有sigmoid层和permute层。
0x4 u版YOLOv8后处理
pytorch的后处理在ultralytics/nn/modules.py
Detect类 forward
函数,也就是我们注释掉的部分,对着它改写成 cpp。
本文最后的输出形状:
C = 80 或 40 或 20
H = 80 或 40 或 20
W = 144 = 64+80
64 对应了 DFL
中的 reg_max=16
, 最终回归的 bbox 格式是 ltrb
。
ncnn实现代码和转好的模型已上传到github:
GitHub - triple-Mu/ncnn-examples: Learning ncnn with some examples
使用方式:
git clone https://github.com/triple-Mu/ncnn-examples.git
cd ncnn-examples/yolov8
mkdir build
cmake .. && make -j$(nproc)
mv triplemu-yolov8 .. && cd ..
./triplemu-yolov8 ../bus.jpg
0x5 动态尺寸推理
与其他的教程相同,本文也支持动态输入尺寸,更多的探索请查看源码 src/triplemu-yolov8.cpp
。
0x6 总结
允许在不修改内容前提下转载本文!!
欢迎大家通过 triple-Mu 联系我哈~
顺便宣传一下我的 YOLOv8-TensorRT 部署仓库:
GitHub - triple-Mu/YOLOv8-TensorRT: YOLOv8 using TensorRT accelerate !