ncnn yolov5-7.0

转载自:https://zhuanlan.zhihu.com/p/606440867 

大家可以去以上链接浏览,我只是怕原作把文章删除了。。哭。。

背景知识:

YOLOv5 升级迭代比较快,在知乎上搜索相关 NCNN 的教程,排在前两位的就是 NCNN 作者 nihui 大佬的文章了。

链接:

  1. 详细记录u版YOLOv5目标检测ncnn实现 - nihui的文章 - 知乎 https://zhuanlan.zhihu.com/p/275989233

2. 详细记录u版YOLOv5目标检测ncnn实现(第二版) - nihui的文章 - 知乎 https://zhuanlan.zhihu.com/p/471357671

本文的思路基于上两篇文章,但是稍稍有所不同。

本着用新不用旧的思想,本文将基于当前时间最新的 YOLOv5 进行模型转换和模型推理部署。

版本号 v7.0

commit id: 4d28fec3b8b663fa8225634ca8eeb4446505527e

0x1 缘由

YOLOv5 部署老生常谈的问题有:

  1. 前处理(letterbox resize)
  2. Focus 模块(在 v7.0 版本已经没有了)
  3. 后处理 (包括框解码和 NMS)

问题 1 代码层面 NCNN 已经有了很好的解决方案,并且支持动态形状。

问题 3 由于当前最新的 YOLOv5 中的 export.py 去掉了 --train 参数,导致这部分要自己动手修改源码了。

0x2 pytorch测试和导出torchscript

首先是下载当前时间最新的 YOLOv5 ,当前时间是 : 2023-02-15 10:35

git clone https://github.com/ultralytics/yolov5  # clone
cd yolov5
git checkout 4d28fec3b8b663fa8225634ca8eeb4446505527e # switch to commit id
pip install -r requirements.txt  # install

老样子,用默认的配置和预训练模型推理一张图片:

python detect.py --source data/bus.jpg --weights yolov5s.pt --view-img

接下来动源码,models/yolo.py 中的 56-79 行注释掉,换成下面的内容:

    # 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.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)
    #
    #             if isinstance(self, Segment):  # (boxes + masks)
    #                 xy, wh, conf, mask = x[i].split((2, 2, self.nc + 1, self.no - self.nc - 5), 4)
    #                 xy = (xy.sigmoid() * 2 + self.grid[i]) * self.stride[i]  # xy
    #                 wh = (wh.sigmoid() * 2) ** 2 * self.anchor_grid[i]  # wh
    #                 y = torch.cat((xy, wh, conf.sigmoid(), mask), 4)
    #             else:  # Detect (boxes only)
    #                 xy, wh, conf = x[i].sigmoid().split((2, 2, self.nc + 1), 4)
    #                 xy = (xy * 2 + self.grid[i]) * self.stride[i]  # xy
    #                 wh = (wh * 2) ** 2 * self.anchor_grid[i]  # wh
    #                 y = torch.cat((xy, wh, conf), 4)
    #             z.append(y.view(bs, self.na * nx * ny, self.no))
    #
    #     return x if self.training else (torch.cat(z, 1),) if self.export else (torch.cat(z, 1), x)

    def forward(self, x):
        z = []  # inference output
        for i in range(self.nl):
            feat = self.m[i](x[i])  # conv
            # x(bs,255,20,20) -> x(bs,20,20,255)
            feat = feat.permute(0, 2, 3, 1).contiguous()
            z.append(feat.sigmoid())
        return tuple(z)

导出 torchscript

python export.py --weights yolov5s.pt --include torchscript

会在当前工作目录下生成 yolov5s.torchscript 。

用 Netron 看一眼清爽的后处理长什么样:

会有三个上述的结构,这是我们修改的 forward 中的内容,由于 PNNX 会把最后的 permute reshape 干掉,所以不得已只能也把 sigmoid 导出了。据说模型最后是 sigmoid softmax 会导致量化掉点,等我之后实验再说。

0x3 转换

下载编译好的 pnnx 工具包:PNNX

指定 inputshape 并且额外指定 inputshape2 转换成支持动态 shape 输入的模型:

./pnnx yolov5s.torchscript inputshape=[1,3,640,640] inputshape2=[1,3,320,320] 

转换日志:

转换主要产物:

看一眼 yolov5s.ncnn.param

in0 对应输入,尺寸是 3*640*640 或者动态输入的尺寸。

out0 对应输出,尺寸是 80*80*255 或者动态输出的尺寸。

out1 对应输出,尺寸是 40*40*255 或者动态输出的尺寸。

out2 对应输出,尺寸是 20*20*255 或者动态输出的尺寸。

注意,本文用pnnx转换后的模型,尾巴上有permute和sigmoid层。

0x4 u版YOLOv5后处理

anchor信息是在 models/yolov5s.yaml

anchors:  
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32 

pytorch的后处理在 models/yolo.py Detect类 forward函数,也就是我们注释掉的部分,对着它改写成 cpp。

与其他 YOLOv5 教程不同的是,本文最后的输出形状:

C = 80 或 40 或 20

H = 80 或 40 或 20

W = 255 = 85 * 3,对应于 bbox 的 dx, dy, dw, dh, bbox 置信度,80 个分类的分数,3 对应于三种 anchor。

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/yolov5
mkdir build
cmake .. && make -j$(nproc) 
mv triplemu-yolov5 .. & cd ..
./triplemu-yolov5

0x5 动态尺寸推理

与其他的教程相同,本文也支持动态输入尺寸,更多的探索请查看源码 src/triplemu-yolov5.cpp 。

0x6 总结

允许在不修改内容前提下转载本文!!

欢迎大家通过 triple-Mu 联系我哈~

顺便宣传一下我的 YOLOv8-TensorRT 部署仓库:

GitHub - triple-Mu/YOLOv8-TensorRT: YOLOv8 using TensorRT accelerate !

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值