自从Redmon说他不在更新YOLO系列之后,我一度以为这么好用的框架就要慢慢淡入历史了,事实是我多虑了。YOLOv4在使用YOLO Loss的基础上,使用了新的backbone,并且集成了很多新的优化方法及模型策略,如Mosaic,PANet,CmBN,SAT训练,CIoU loss,Mish激活函数,label smoothing等等。可谓集SoAT之大成,也实现了很好的检测精度和速度。 这篇博客主要讨论YOLOv4中的backbone——CSP-DarkNet,以及其实现的所必需的Mish激活函数,CSP结构和DarkNet。
激活函数是为了提高网络的学习能力,提升梯度的传递效率。CNN常用的激活函数也在不断地发展,早期网络常用的有ReLU,LeakyReLU,softplus等,后来又有了Swish,Mish等。Mish激活函数的计算复杂度比ReLU要高不少,如果你的计算资源不是很够,可以考虑使用LeakyReLU代替Mish。在介绍之前,需要先了解softplus和tanh函数。
softplus激活函数的公式如下:
ζ
(
x
)
=
l
o
g
(
1
+
e
x
)
\zeta(x)=log(1+e^x)
ζ(x)=log(1+ex)
上图是其输出曲线,softplus和ReLU的曲线具有相似性,但是其比ReLU更为平滑。
目前的普遍看法是,平滑的激活函数允许更好的信息深入神经网络,从而得到更好的准确性和泛化。
tanh的公式如下:
t
a
n
h
(
x
)
=
e
x
−
e
−
x
e
x
+
e
−
x
tanh(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}
tanh(x)=ex+e−xex−e−x
Mish激活函数的公式为:
M
i
s
h
(
x
)
=
x
×
t
a
n
h
(
ζ
(
x
)
)
Mish(x) = x\times{tanh(\zeta(x))}
Mish(x)=x×tanh(ζ(x))
CSP和DarkNet的结构我在之前的博客中有介绍,如果不清楚的同学,欢迎戳链接:,。
这里为了方便对比,给出DarkNet-53的架构图:
博客画出了DarkNet-53的结构图,画得很简明清晰,我借过来用一下:
但是,有个地方看图还是不清楚——CSP输入的时候通道是什么比例划分的? 查看了一些源码,最终确认了结构,在一下部分进行讨论。
按照CSP论文中的思路,我开始认为的CSP结构应该是这样的——特征输入之后,通过一个比例将其分为两个部分(CSPNet中是二等份),然后再分别输入block结构,以及后面的Partial transition处理。这样符合CSPNet论文中的理论思路。
这个复现包括了全局池化和全连接层,YOLOv4中使用CSP-DarkNet只使用之前的卷积层用作特征提取。
class Mish(nn.Module):
def __init__(self):
super(Mish, self).__init__()
def forward(self, x):
return x * torch.tanh(F.softplus(x))
class BN_Conv_Mish(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride, padding, dilation=1, groups=1, bias=False):
super(BN_Conv_Mish, self).__init__()
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, dilation=dilation,
groups=groups, bias=bias)
self.bn = nn.BatchNorm2d(out_channels)
def forward(self, x):
out = self.bn(self.conv(x))
return Mish()(out)
使用的是残差结构,要注意的是:按照residual block的一贯思路,shortcut之前的最后一层卷积使用线性激活(不适使用激活函数)。
class ResidualBlock(nn.Module):
"""
basic residual block for CSP-Darknet
"""
def __init__(self, chnls, inner_chnnls=None):
super(ResidualBlock, self).__init__()
if inner_chnnls is None:
inner_chnnls = chnls
self.conv1 = BN_Conv_Mish(chnls, inner_chnnls, 1, 1, 0) # always use samepadding
self.conv2 = nn.Conv2d(inner_chnnls, chnls, 3, 1, 1, bias=False)
self.bn = nn.BatchNorm2d(chnls)
def forward(self, x):
out = self.conv1(x)
out = self.conv2(out)
out = self.bn(out) + x
return Mish()(out)
按照上图的结构实现CSP结构并搭建网络。需要注意的是,第一个CSP结构和后面的有略微差别:
class CSPFirst(nn.Module):
"""
First CSP Stage
"""
def __init__(self, in_chnnls, out_chnls):
super(CSPFirst, self).__init__()
self.dsample = BN_Conv_Mish(in_chnnls, out_chnls, 3, 2, 1) # same padding
self.trans_0 = BN_Conv_Mish(out_chnls, out_chnls, 1, 1, 0)
self.trans_1 = BN_Conv_Mish(out_chnls, out_chnls, 1, 1, 0)
self.block = ResidualBlock(out_chnls, out_chnls//2)
self.trans_cat = BN_Conv_Mish(2*out_chnls, out_chnls, 1, 1, 0)
def forward(self, x):
x = self.dsample(x)
out_0 = self.trans_0(x)
out_1 = self.trans_1(x)
out_1 = self.block(out_1)
out = torch.cat((out_0, out_1), 1)
out = self.trans_cat(out)
return out
class CSPStem(nn.Module):
"""
CSP structures including downsampling
"""
def __init__(self, in_chnls, out_chnls, num_block):
super(CSPStem, self).__init__()
self.dsample = BN_Conv_Mish(in_chnls, out_chnls, 3, 2, 1)
self.trans_0 = BN_Conv_Mish(out_chnls, out_chnls//2, 1, 1, 0)
self.trans_1 = BN_Conv_Mish(out_chnls, out_chnls//2, 1, 1, 0)
self.blocks = nn.Sequential(*[ResidualBlock(out_chnls//2) for _ in range(num_block)])
self.trans_cat = BN_Conv_Mish(out_chnls, out_chnls, 1, 1, 0)
def forward(self, x):
x = self.dsample(x)
out_0 = self.trans_0(x)
out_1 = self.trans_1(x)
out_1 = self.blocks(out_1)
out = torch.cat((out_0, out_1), 1)
out = self.trans_cat(out)
return out
class CSP_DarkNet(nn.Module):
"""
CSP-DarkNet
"""
def __init__(self, num_blocks: object, num_classes=1000) -> object:
super(CSP_DarkNet, self).__init__()
chnls = [, 128, 256, 512, 1024]
self.conv0 = BN_Conv_Mish(3, 32, 3, 1, 1) # same padding
self.neck = CSPFirst(32, chnls[0])
self.body = nn.Sequential(
*[CSPStem(chnls[i], chnls[i+1], num_blocks[i]) for i in range(4)])
self.global_pool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(chnls[4], num_classes)
def forward(self, x):
out = self.conv0(x)
out = self.neck(out)
out = self.body(out)
out = self.global_pool(out)
out = out.view(out.size(0), -1)
out = self.fc(out)
return F.softmax(out)
def csp_darknet_53(num_classes=1000):
return CSP_DarkNet([2, 8, 8, 4], num_classes)
net = csp_darknet_53()
summary(net, (3, 256, 256))
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- niushuan.com 版权所有 赣ICP备2024042780号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务