最近在学习yolov5的代码(因为项目需要),其实陆陆续续接触yolov5已经半年左右了,用yolov5也跑过了自己的数据集,但是一直没有上手对代码进行修改,主要还是因为用到了很多工程性的技术和代码,之前做科研的时候没有接触到,改代码方式如果不正确会引发很多BUG。
我们就先从yolov5的网络结构开始讲起吧~
一、Focus层 Focus层的代码如下:
class Focus(nn.Module):
# Focus wh information into c-space
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):# ch_in, ch_out, kernel, stride, padding, groups
super(Focus, self).__init__()
self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
# self.contract = Contract(gain=2)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))
# return self.conv(self.contract(x))
输入图片大小为[3,640,640],首先先对其进行切片操作,然后通过concat操作连接到一起变成[12,320,320],最后进行卷积。
文章图片
具体切片是如何操作的,可以参考下图。从图中或者代码中可以看出,切片操作是分别从(0,0),(0,1),(1,0),(1,1)这四个点开始,每隔两个步长取图片中的一个像素点形成的。
文章图片
Focus层虽然是yolov5中首先提出来的,但是这个操作非常类似于yolov2中的PassThrough层:将w-h平面上的信息转换到通道维度,再通过卷积的方式提取不同特征。采用Focus层的目的应该是下采样(下采样在神经网络中主要是为了减少参数量达到降维的作用,同时还能增加局部感受野),但是相比于使用使用步长为2的卷积层或者池化层,Focus层能够有效减少下采样带来的信息损失,同时减少计算量。说到这里,有必要提一下空洞卷积,操作过程也非常类似,在设计卷积神经网络的时候可以考虑替换使用看看哪个效果更好啊~
二、Bottleneck模块 Bottleneck结构就很简单了,一个1×1的卷积后接一个3×3的卷积,其中1×1的卷积将通道数减半,3×3的卷积将通道数加倍,然后加上输入(注意这里是add操作,不是concat操作)。所以经过Bottleneck模块之后输入大小是不会发生改变的。
class Bottleneck(nn.Module):
# Standard bottleneck
def __init__(self, c1, c2, shortcut=True, g=1, e=0.5):# ch_in, ch_out, shortcut, groups, expansion
super(Bottleneck, self).__init__()
c_ = int(c2 * e)# hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c_, c2, 3, 1, g=g)
self.add = shortcut and c1 == c2def forward(self, x):
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
文章图片
三、BottleneckCSP模块 由于yolov5一直在更新,上次我看的时候它使用的是BottleneckCSP模块,这次看它已经改成了C3,其实结构是一样的,写法略微有差异。BottleneckCSP中cv2和cv3调用的是系统的卷积层,使用concat连接之后加上BN层和激活函数;C3则直接使用了作者自己定义的卷积层(conv+batchnorm+SiLU),这里激活函数也有修改。(yolov5作为一个工程性的项目,作者一直在维护和修改,所以经常会有一些细节性的调整)
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(2 * c_, 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))))class C3(nn.Module):
# CSP Bottleneck with 3 convolutions
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):# ch_in, ch_out, number, shortcut, groups, expansion
super(C3, self).__init__()
c_ = int(c2 * e)# hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c1, c_, 1, 1)
self.cv3 = Conv(2 * c_, c2, 1)# act=FReLU(c2)
self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
# self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])def forward(self, x):
return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))
这个BottleneckCSP模块使用netron画出来非常大,放上来效果不太好,我就直接在网上扒图了。左边分支一就是使用了三个Bottleneck模块串联到一起,分支二就是输入经过一个conv卷积,然后两个分支cancat到一起,简单来说也是一个shortcut残差连接的思想。这个结构也是不改变输入尺寸的大小的。
文章图片
四、SPP模块 SPP模块非常经典了,从yolov3中开始使用到现在,yolo系列基本上都用到了。
class SPP(nn.Module):
# Spatial pyramid pooling layer used in YOLOv3-SPP
def __init__(self, c1, c2, k=(5, 9, 13)):
super(SPP, self).__init__()
c_ = c1 // 2# hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])def forward(self, x):
x = self.cv1(x)
return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))
【目标检测|YOLOv5网络结构学习】首先使用一个卷积使通道数减半1024->512,然后将输入经过三个不同尺寸大小的最大池化层,连同输入一起concat(四个部分512*4=2048),最后再经过一个卷积层使通道数减半2048->1024,所以SPP模块也是不改变输入尺寸大小的。
文章图片
五、整体架构 yolov5中所有网络结构相关的定义都在common.py文件里面,通过执行yolo.py文件可以查看整个网络结构(修改网络结构的话在对应的yaml里面,之后可能会出一个修改yolov5网络架构的文章,敬请期待~)
文章图片
网上关于yolov5的网络结构图太多了,但是我觉得都不太好,不适合新手理解。在Github看评论的时候,我看到不少大佬画了一些yolov5的结构图,感觉很不错,就都拿过来了。我一直看的都是第一个图,感觉这个画的比较详细且易懂(可能是因为很像yolov4,之前看yolov4看习惯了~)
文章图片
文章图片
文章图片
六、Write in the end 最后讲一讲自己对yolov5模型的看法吧。众所周知,yolo系列的作者从v3之后就退出CV界了,随后的yolov4、yolov5等模型都不再是原作者进行开发的了。yolov5作为一个工程性的项目,代码里面包含了大量工程上使用到的技巧,对于初学者甚至有一定python/pytorch基础的人来阅读或者修改代码都是比较困难的,因为改代码的时候很可能会导致一些未知的错误。但是你单纯地使用,训练/测试自己的数据集还是非常方便的,而且用过yolov5的人反响都还不错,最后贴上一张网上的评论吧~
文章图片
参考网址:
[1] 深度学习之Focus层
[2] yolov5中的Focus模块的理解
[3] yolov5网络结构梳理详解
[4]Github中各位大佬画的网络结构图
推荐阅读
- 深度学习|YOLO v4网络实现及解析
- pytorch|Grad-CAM简介
- 软件使用|更换python源以及anaconda源
- 大数据|火热的 Web 3,究竟离我们有多远()
- 中间件|Apache Member、ALC Beijing 发起人姜宁(一个人走的很快,但是一群人能走得更远)
- 区块链|活动回顾丨ALC Beijing 首场 Meetup(《开源到底有多难(》))
- 互联网资讯|ALC Beijing发起人、华为开源技术专家姜宁当选 ASF 董事
- 大数据|基于图卷积堆叠的双向单向LSTM神经网络的地铁客流预测
- 联邦学习|PyTorch实现联邦学习目标检测