0
点赞
收藏
分享

微信扫一扫

《魔改Yolov5》替换MobileNet V3 backbone


作者:MuggleZero 

 《魔改Yolov5》专栏地址:魔改Yolov5

MobileNet V3 论文翻译地址:

MobileNet V3 论文翻译

《魔改Yolov5》替换MobileNet V3 backbone_ide

h_sigmoid复现:

common.py

class h_sigmoid(nn.Module):
def __init__(self, inplace=True):
super(h_sigmoid, self).__init__()
self.relu = nn.ReLU6(inplace=inplace)

def forward(self, x):
return self.relu(x + 3) / 6

h_swish复现:

common.py

至此已经实现了h_swish公式内容。

class h_swish(nn.Module):
def __init__(self, inplace=True):
super(h_swish, self).__init__()
self.sigmoid = h_sigmoid(inplace=inplace)

def forward(self, x):
y = self.sigmoid(x)
return x * y

网络结构复现

主体结构:

《魔改Yolov5》替换MobileNet V3 backbone_目标检测_02

《魔改Yolov5》替换MobileNet V3 backbone_yolov5_03


NL:使用的非线性层

HS:h-swish

RE:ReLU

NBN:没有批处理规范化

SE: squeeze-and-excite


第一行的意思:使用了h-swish的非线性层的3x3 conv2d,stride=2, out channels=16。复现代码:

class Conv3BN(nn.Module):
"""
This equals to
def conv_3x3_bn(inp, oup, stride):
return nn.Sequential(
nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
nn.BatchNorm2d(oup),
h_swish()
)
"""

def __init__(self, inp, oup, stride):
super(Conv3BN, self).__init__()
self.conv = nn.Conv2d(inp, oup, 3, stride, 1, bias=False)
self.bn = nn.BatchNorm2d(oup)
self.act = h_swish()

def forward(self, x):
return self.act(self.bn(self.conv(x)))

def fuseforward(self, x):
return self.act(self.conv(x))

squeeze-and-excite

MobileNet v3 中 引用的Squeeze-and-Excite是怎么回事

AdaptiveAvgPool2d
Linear
ReLU
Linear
h_sigmoid

《魔改Yolov5》替换MobileNet V3 backbone_2d_04

对应的操作:

  • Squeeze(Fsq)
    用AdaptiveAvgPool2d将H × W × C H的feature map压缩为1 × 1 × C
  • Excitation(Fex)
    得到Squeeze的1 × 1 × C 的feature map后,使用FC全连接层,对每个通道的重要性进行预测,得到不同channel的重要性大小。有两个全连接,一个降维,一个恢复维度。
  • Scale
    最后将学习到的各个channel的激活值(sigmoid激活,值0~1)乘以之前的feature map的对应channel上。
class SELayer(nn.Module):
def __init__(self, channel, reduction=4):
super(SELayer, self).__init__()
#用1x1的卷积
self.avg_pool = nn.AdaptiveAvgPool2d(1) #Squeeze,将H × W × C H的feature map压缩为1 × 1 × C
self.fc = nn.Sequential(
nn.Linear(channel, channel // reduction),
nn.ReLU(inplace=True),
nn.Linear(channel // reduction, channel),
h_sigmoid() # h_sigmoid,修改了x
) # 使用FC全连接层,对每个通道的重要性进行预测,得到不同channel的重要性大小

def forward(self, x):
# b=1,c=16,_=64,16个数据组成一行,一共64排数据。
b, c, _, _ = x.size() #返回x张量行数、列数、深度.
# Squeeze
y = self.avg_pool(x).view(b, c) #池化后的1 × 1 × C feature map重塑为 b*c 的张量

# Excitation 预测并重塑为 b*c*1*1张量
y = self.fc(y).view(b, c, 1, 1)

# Scale
return x * y

滤波器4维张量:

[height,width,input_channels,output_channels]   //滤波器张量
[卷积核高度,卷积核宽度,图像通道数,卷积核个数] //第三维input_channels为input张量的第四维。

图像4维张量张量:

[batch_size,height,width, channels]  //图像张量
[个数,高度,宽度,通道数]

InvertedResidual

InvertedResidual(论文中是bneck)是MobileNetv2中提出的,stride=1和stride=2在走向是有差异的。

stride=1的时候:


1、point-wise升维 2、depth-wise提取特征 3、通过Linear的point-wise降维。 4、input与结果 相加(残差结构)


《魔改Yolov5》替换MobileNet V3 backbone_mobilenet_05

《魔改Yolov5》替换MobileNet V3 backbone_2d_06

第一层bneck是支持relu6的3x3卷积,第二个箭头所指的bneck是支持hswish的5x5卷积。

整体结构如下所示,

《魔改Yolov5》替换MobileNet V3 backbone_2d_07

即先利用1x1卷积进行升维度,再进行下面的操作,并具有残差边:

《魔改Yolov5》替换MobileNet V3 backbone_ide_08


在输入1x1卷积进行升维度后,进行3x3深度可分离卷积:

《魔改Yolov5》替换MobileNet V3 backbone_2d_09

注意力机制调整每个通道的权重:

《魔改Yolov5》替换MobileNet V3 backbone_2d_10

1x1卷积:

《魔改Yolov5》替换MobileNet V3 backbone_ide_11

class InvertedResidual(nn.Module):
def __init__(self, inp, oup, hidden_dim, kernel_size, stride, use_se, use_hs):
super(InvertedResidual, self).__init__()
assert stride in [1, 2]

self.identity = stride == 1 and inp == oup
# 如果输入通道等于 隐藏层的维数,来一个dw
if inp == hidden_dim:
self.conv = nn.Sequential(
# dw Depth-wise
nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
bias=False),
nn.BatchNorm2d(hidden_dim),
h_swish() if use_hs else nn.ReLU(inplace=True),
# Squeeze-and-Excite
SELayer(hidden_dim) if use_se else nn.Sequential(),
# pw-linear
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
)
else:
self.conv = nn.Sequential(
# pw Point-wise 1x1
nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),
nn.BatchNorm2d(hidden_dim),
h_swish() if use_hs else nn.ReLU(inplace=True),
# dw Depth-wise
nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
bias=False),
nn.BatchNorm2d(hidden_dim),
# Squeeze-and-Excite
SELayer(hidden_dim) if use_se else nn.Sequential(),
h_swish() if use_hs else nn.ReLU(inplace=True),
# pw-linear
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
)

def forward(self, x):
y = self.conv(x)
# stride=1时,输入加输出
if self.identity:
return x + y
else:
return y

yaml定义:

依次是传入上面函数的[ oup, hidden_dim(exp size), kernel_size, stride, use_se, use_hs ]

# YOLOv5 backbone
backbone:
# MobileNetV3-small
# [from, number, module, args]
[[-1, 1, Conv3BN, [16, 2]], # 0-p1/2
[-1, 1, InvertedResidual, [16, 16, 3, 2, 1, 0]], # 1-p2/4
[-1, 1, InvertedResidual, [24, 72, 3, 2, 0, 0]], # 2-p3/8
[-1, 1, InvertedResidual, [24, 88, 3, 1, 0, 0]], # 3-p3/8
[-1, 1, InvertedResidual, [40, 96, 5, 2, 1, 1]], # 4-p4/16
[-1, 1, InvertedResidual, [40, 240, 5, 1, 1, 1]], # 5-p4/16
[-1, 1, InvertedResidual, [40, 240, 5, 1, 1, 1]], # 6-p4/16
[-1, 1, InvertedResidual, [48, 120, 5, 1, 1, 1]], # 7-p4/16
[-1, 1, InvertedResidual, [48, 144, 5, 1, 1, 1]], # 8-p4/16
[-1, 1, InvertedResidual, [96, 288, 5, 2, 1, 1]], # 9-p5/32
[-1, 1, InvertedResidual, [96, 576, 5, 1, 1, 1]], # 10-p5/32
[-1, 1, InvertedResidual, [96, 576, 5, 1, 1, 1]], # 11-p5/32
]


举报

相关推荐

0 条评论