Yolov5学习笔记5——v5.0源码剖析——Backbone部分2
Yolov5学习笔记5——v5.0源码剖析——Backbone部分2
用netron得到yolov5s的框架结构图如下,可以非常直观的得到关于backbone部分的网络结构图。
注:所使用的代码版本为 2020年11月24日发布的Yolov5-master。
YOLOv5s结构图如下所示:
Backbone结构图
backbone的意义是:在不同图像细粒度上聚合并形成图像特征的卷积神经网络;
backbone所需的主要模块在common.py里面可以找到。
从整体结构中我们抽出backbone部分学习:
backbone的结构图如下:
Netron打开yolov5s.pt导出的BackBone部分的框架图如下:
backbone对应的代码为:
1 | # YOLOv5 backbone |
从上述代码中我们可以看到backbone由如下组成:
BACKBONE =FOCUS(1个)+CONV (1个)+BCSP(3个)+CONV (1个)+BCSP(9个)+CONV (1个)+SPP(1个)+BCSP(1个)
- 注:这里的BCSP相当于CSP
Backbone源码解析
Focus模块
Focus()函数在common.py中,对应的源码如下:
1 | class Focus(nn.Module): |
根据前述代码我们知道,输入的图片尺寸为640×640×3,而Focus()函数的功能为:将640 × 640 × 3的图像输入Focus结构,采用切片操作,先变成320 × 320 × 12的特征图,再经过3 × 3的卷积操作,输出通道32,最终变成320 × 320 × 32的特征图
Focus模块的结构图如下:
CBL模块(CBH)
该部分对应的源码如下:
1 | class Conv(nn.Module): |
CONV是一个标准的卷积模块。
从源码第5 6 7行可以看出,激活函数变成了Hardwish(),实际上这里不应该叫CBL模块了,应该是CBH模块。
- conv:来自于代码的torch.nn.Conv2d,是一个卷积操作
- bn:来自于代码的torch.nn.BatchNorm2d:归一化处理,使batch里面的feature map 满足均值为1,方差为0 的正太分布
- Hardswish:激活函数
故:CBL=CONV+BN+Hardswish
BottleneckCSP模块
3个BCSP相当于是几个标准的Bottleneck的堆叠+几个标准卷积层。
BottleneckCSP的网络结构如下:
残差组件Resunit的网络结构如下:
BottleneckCSP的源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class 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))))
注:nn.sequential将所有的块链接在一起。self.bn = nn.BatchNorm2d(2 * c_)就是concat 块,cv2,cv3对应于图中的concat;
Resunit的源码如下:
1
2
3
4
5
6
7
8
9
10
11class 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 == c2
def forward(self, x):
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
注:cv1、cv2对应于图中的CBL模块,add不变。
SPP某块
SPP模块,就是常说的空间金字塔池化模块,分别采用5/9/13的最大池化,在进行concat融合,提高感受野。
SPP的输入时512×512×20,经过1×1的卷积层后输出256×20×20,然后经过并列的三个Maxpool进行下采样,将结果与其初始特征相加,输出1024×20×20,最后用512的卷积核将其恢复到512×20×20.
SPP模块的结构图如下:
SPP模块对应的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12class 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))