YOLOv5 组件

1、标准卷积: Conv + BN + activate

class Conv(nn.Module):
# Standard convolution
# ch_in, ch_out, kernel, stride, padding, groups
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):
super(Conv, self).__init__()
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
self.bn = nn.BatchNorm2d(c2)
self.act = nn.Hardswish() if act is True else (act if isinstance(act, nn.Module) 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))


• g：groups，通道分组的参数，输入通道数、输出通道数必须同时满足被groups整除；

groups: 如果输出通道为6，输入通道也为6，假设groups为3，卷积核为 1x1 ; 则卷积核的shape为2x1x1，即把输入通道分成了3份；那么卷积核的个数呢？之前是由输出通道决定的，这里也一样，输出通道为6，那么就有6个卷积核！这里实际上是将卷积核也平分为groups份，在groups份特征图上计算，以输入、输出都为6为例，每个2xhxw的特征图子层就有且仅有2个卷积核，最后相加恰好是6。这里可以起到的作用是不同通道分别计算特征！

参数的参考资料1参考资料2

Top  ---  Bottom

2、DWConv深度可分离卷积

def DWConv(c1, c2, k=1, s=1, act=True):
# Depthwise convolution
return Conv(c1, c2, k, s, g=math.gcd(c1, c2), act=act)


3、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 == c2

def forward(self, x):
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))


• c1：bottleneck 结构的输入通道维度；
• c2：bottleneck 结构的输出通道维度；
• shortcut：是否给bottleneck 结构添加shortcut连接，添加后即为ResNet模块；
• ggroups，通道分组的参数，输入通道数、输出通道数必须同时满足被groups整除；
• e：expansion: bottleneck 结构中的瓶颈部分的通道膨胀率，使用0.5即为变为输入的$$\frac{1}{2}$$

Top  ---  Bottom

4、BottleneckCSP-CSP瓶颈层

class BottleneckCSP(nn.Module):
# CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
# ch_in, ch_out, number, shortcut, groups, expansion
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
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))))


• c1：BottleneckCSP 结构的输入通道维度；
• c2：BottleneckCSP 结构的输出通道维度；
• n：bottleneck 结构 结构的个数；
• shortcut：是否给bottleneck 结构添加shortcut连接，添加后即为ResNet模块；
• ggroups，通道分组的参数，输入通道数、输出通道数必须同时满足被groups整除；
• e：expansion: bottleneck 结构中的瓶颈部分的通道膨胀率，使用0.5即为变为输入的$$\frac{1}{2}$$
• torch.cat((y1, y2), dim=1)：这里是指定在第$$1$$个维度上进行合并，即在channel维度上合并;
• c_：BottleneckCSP 结构的中间层的通道数，由膨胀率e决定。

CSP瓶颈层结构在Bottleneck部分存在一个可修改的参数n，标识使用的Bottleneck结构个数！这一条也是我们的主分支，是对残差进行学习的主要结构，右侧分支nn.Conv2d实际上是shortcut分支实现不同stage的连接。

Top  ---  Bottom

Top  ---  Bottom

6、SPP空间金字塔池化模块

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))


SPP即为空间金字塔池化模块！上面的代码是yolov5的模型代码，可视化为：

Top  ---  Bottom