文章目录
- 一、前言——从YOLOv3到YOLOv5
- 二、代码解析
- 2.1 运行起来项目 detect.py
- 2.2 网络结构 models/yolo.py
- 2.3 配置文件 yolov5s.yaml
- 2.4 网络子结构 models/common.py
- 2.4.1 Conv与Focus
- 2.4.2 Bottleneck与BottleneckCSP
- 2.5 训练 train.py
先放官网大图
YOLOv5官方Pytorch实现:https://github.com/ultralytics/yolov5
再放干货:YOLOv3-YOLOv5的所有权重:(YOLOv5权重需要翻墙下载)
链接:https://pan.baidu.com/s/1rZAiccpdmUaaWDQ5tUwFcA
提取码:6qvy
一、前言——从YOLOv3到YOLOv5
YOLOv3
YOLO原项目darknet(官方)截止2020年5月31日,并没有更新添加这个"YOLOv5"的链接。最新的一次update还是上个月YOLOv4重磅出炉的那次,官方正式添加了YOLOv4项目链接。
"YOLOv5"的项目团队是Ultralytics LLC 公司,很多人应该没有听过这家公司。但提到他们公司的一个项目,很多人应该就知道了,因为不少同学用过。那就是基于PyTorch复现的YOLOv3,按目前github上star数来看,应该是基于PyTorch复现YOLOv3中的排名第一。Amusi 之前还分享过此项目。
附上Pytorch版的YOLOv3:https://github.com/ultralytics/yolov3
他们复现的YOLOv3版而且还有APP版本
YOLOv3 in PyTorch > ONNX > CoreML > iOS
其实这个公司团队在YOLOv3上花的功夫蛮多的,不仅有APP版,还对YOLOv3进行了改进,官方介绍的性能效果可以说相当炸裂!另外项目维护的也很牛逼,star数已达4.7 k,commits 都快逼近2500次!
可见Ultralytics LLC 公司在YOLO社区上的贡献还是有一定影响力的,这也是为什么他们刚推出"YOLOv5",就得到不少人的关注。
YOLOv5
据官方称:“YOLOv5” 实际上还处于开发的阶段,预计2020年第2季度/第3季度将全部开发完成。目前放出来的版本,是集成了YOLOv3-SPP和YOLOv4部分特性。
那么"YOLOv5"的性能有多强呢,Ultralytics LLC给出的数据如下:
这里说一下,YOLOv5-x的性能已经达到:47.2 AP / 63 FPS,但项目是在 image size = 736的情况下测得。但Ultralytics LLC并没有给出"YOLOv5"的算法介绍(论文、博客其实都没有看到),所以我们只能通过代码查看"YOLOv5"的特性。只能说现在版本的"YOLOv5"集成了YOLOv3-SPP和YOLOv4的部分特性等。
二、代码解析
代码目录
2.1 运行起来项目 detect.py
代码目录:
1)拿到yolov5的代码,我们首先要把项目运行起来。在这里我们需要下载得到权重,在这里我下载了yolov5s.pt文件,把其放到weights文件夹下;然后找到detect.py运行,inference文件下推理得到output文件夹:
这样我们的第一步项目就跑通了,这是我们拿到源码首先要完成的任务。
2.2 网络结构 models/yolo.py
根据配置文件,models文件夹下选择yolov5s.yaml(根据选择的权重观察),在yolo.py文件里运行下面代码,使用netron可视化网络结构。(关于netron模型可视化,我们只需要在命令行安装pip install netron即可,然后进入netron,复制地址进入,打包我们需要的模型,即可使用可视化。这个工具有助于我们分析网络的整体架构)
1 2 | model = Model(opt.cfg).to(device) torch.save(model,"m.pt") |
这个结构看起来比较简单,我们用torch.jit导出jit格式来看模型详细架构,运行以下代码:
1 2 3 4 5 | # Create model model = Model(opt.cfg).to(device) x = torch.randn(1,3,384,640) script_models = torch.jit.trace(model,x) script_models.save("m.jit") |
导出m.jit格式后,将其重命名为m1.pt,再进行netron可视化分析,这里由于图片较长,我就不展示了。
2.3 配置文件 yolov5s.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | # parameters nc: 80 # number of classes depth_multiple: 0.33 # model depth multiple width_multiple: 0.50 # layer channel multiple # anchors anchors: - [116,90, 156,198, 373,326] # P5/32 - [30,61, 62,45, 59,119] # P4/16 - [10,13, 16,30, 33,23] # P3/8 # YOLOv5 backbone backbone: # [from, number, module, args] [[-1, 1, Focus, [64, 3]], # 0-P1/2 [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [-1, 3, BottleneckCSP, [128]], [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 [-1, 9, BottleneckCSP, [256]], [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 [-1, 9, BottleneckCSP, [512]], [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 [-1, 1, SPP, [1024, [5, 9, 13]]], ] # YOLOv5 head head: [[-1, 3, BottleneckCSP, [1024, False]], # 9 [-1, 1, Conv, [512, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 6], 1, Concat, [1]], # cat backbone P4 [-1, 3, BottleneckCSP, [512, False]], # 13 [-1, 1, Conv, [256, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 4], 1, Concat, [1]], # cat backbone P3 [-1, 3, BottleneckCSP, [256, False]], [-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]], # 18 (P3/8-small) [-2, 1, Conv, [256, 3, 2]], [[-1, 14], 1, Concat, [1]], # cat head P4 [-1, 3, BottleneckCSP, [512, False]], [-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]], # 22 (P4/16-medium) [-2, 1, Conv, [512, 3, 2]], [[-1, 10], 1, Concat, [1]], # cat head P5 [-1, 3, BottleneckCSP, [1024, False]], [-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]], # 26 (P5/32-large) [[], 1, Detect, [nc, anchors]], # Detect(P5, P4, P3) ] |
解析:[-1, 1, Focus, [64, 3]] , [[-1, 6], 1, Concat, [1]]
① -1代表动态计算上一层的通道数(-2代表计算上两层的通道数),设计的原因是一层一层下来的,但存在残差路由结构;[-1,6]代表把上一层与第六层cat起来。
② [64,3]:网络第一层输出是32个通道(把模型打印出来可以看到),但这里是64,这就与采样率有关:64乘以width_multiple=32,与网络第一层输出一致。
3代表这一层复制3次,3乘以depth_multiple等于1,即1层。最少也要有1层。
width_multiple: 0.50这个参数与网络设计有关,现在设计网络一般都不设计一个网络,如yolov3-tiny,yolov3-darknet53,yolov3-spp,但都是单独设计,不太好;如果我们设计几种网络,一般设计常规网络(不大不小),进行训练,效果不错我们再进行缩放,包含深度缩放depth_multiple和宽度缩放width_multiple(通道数),这样的网络被证明效果是不错的,所以可以得到n个网络,减轻了设计负担。
缩放规则:r^2βw<2,r(分辨率)β(深度即层数)w(通道数),希望网络的这些参数变大1倍,但计算量小于2。
2.4 网络子结构 models/common.py
这部分我们需要根据 yolov5s.yaml 配置文件查看主干网backbone和侦测网head查看不同的子结构。
2.4.1 Conv与Focus
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Conv(nn.Module):# 自定义卷积块:卷积_BN_激活。类比yolov4里的CBL结构 # Standard convolution def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out, kernel, stride, groups super(Conv, self).__init__() p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # padding self.conv = nn.Conv2d(c1, c2, k, s, p, groups=g, bias=False) self.bn = nn.BatchNorm2d(c2) self.act = nn.LeakyReLU(0.1, inplace=True) if act else nn.Identity() def forward(self, x): return self.act(self.bn(self.conv(x))) def fuseforward(self, x): return self.act(self.conv(x)) class Focus(nn.Module):# Focus模块:将W、H信息集中到通道空间 # Focus wh information into c-space def __init__(self, c1, c2, k=1): super(Focus, self).__init__() self.conv = Conv(c1 * 4, c2, k, 1) def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2) return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1)) |
Focus模块,输入通道扩充了4倍,作用是可以使信息不丢失的情况下提高计算力。具体操作为把一张图片每隔一个像素拿到一个值,类似于邻近下采样,这样我们就拿到了4张图,4张图片互补,长的差不多,但信息没有丢失,拼接起来相当于RGB模式下变为12个通道,通道多少对计算量影响不大,但图像缩小,大大减少了计算量。可以当成下图理解:
2.4.2 Bottleneck与BottleneckCSP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class BottleneckCSP(nn.Module): # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion super(BottleneckCSP, self).__init__() c_ = int(c2 * e) # hidden channels self.cv1 = Conv(c1, c_, 1, 1) self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False) self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False) self.cv4 = Conv(c2, c2, 1, 1) self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3) self.act = nn.LeakyReLU(0.1, inplace=True) self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) def forward(self, x): y1 = self.cv3(self.m(self.cv1(x))) y2 = self.cv2(x) return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1)))) |
在这里我们可以画出其网络结构图如下图所示,其实我们还可以类比YOLOv4的子结构,我们发现其实他们大概类似,只不过YOLOv4第一个是CBM结构,其他的CBM结构换成了卷积,这也解释了我在前一篇博文提到的CBM结构换卷积会有更好的效果,有兴趣可以跳到这篇博文DeepLearing—CV系列(十三)——YOLOv4完整核心理论详解。
下图为YOLOv4类似的子结构图: