在yolov5的网络结构中添加注意力机制模块
知足知不足,有为有不为
目录
前言
一、模块添加步骤
二、相应注意力机制介绍及其代码
1.SE注意力
2.CBAM注意力
3.ECA注意力
4.CA注意力
参考
常见涨点的注意力机制有CBAM, SE, CA, ECA等。该文章对以下模块进行总结,如有其他好用的模块请在评论区告诉我再来更新。
一、模块添加步骤
先说模块添加的方法
1.将注意力机制的代码写进 models/common.py 或 models/experimental.py 中去;
2.结合注意力机制需要的参数以及自己模型的通道数等条件,更改自己的.yaml配置文件;
3.在解析文件models/yolo.py中的parse_model()函数里再编写一段代码,使得配置文件里写的参数适应配置文件中width_multiple、depth_multiple的设置。
4.运行yolo.py文件,查看是否加如了相应模块。
二、相应注意力机制介绍及其代码
1.SE注意力
SE模块是ImageNet2017分类冠军Squeeze-and-Excitation Networks中提出的一个即插即用的网络模块。该模块的前世今生不在此叙述。
该模块将卷积到的特征图做出了三个操作:Squeeze(压缩)、Excitation(激励)、Scale。
##在Squeeze操作中,通过全局池化将特征图卷积成 1×1×C 的向量(C是channel数),这样我们就实现了空间维度的压缩,每一个数就是相应通道上的评价分数。
##在Excitation操作中,通过学习来为各个特征通道生成权重。两个FC层旨在捕获非线性跨通道交互。其中涉及降低维度以控制模型的复杂性,代码中paras中的r是ratio,即缩放参数,这个参数的目的是为了减少channel个数从而降低计算量。
##在Scale操作中,就是将Excitation中得到的通道权重与原特征图对应通道的二维矩阵相乘。即对原有的特称图进行通道方向上的加权。
SE注意力:
论文地址:https://arxiv.org/abs/1709.01507
代码参考地址:GitHub – moskomule/senet.pytorch: PyTorch implementation of SENet
class SELayer(nn.Module):
def __init__(self, c1, r=16):
super(SELayer, self).__init__()
self.avgpool = nn.AdaptiveAvgPool2d(1)
self.l1 = nn.Linear(c1, c1 // r, bias=False)
self.relu = nn.ReLU(inplace=True)
self.l2 = nn.Linear(c1 // r, c1, bias=False)
self.sig = nn.Sigmoid()
def forward(self, x):
b, c, _, _ = x.size()
y = self.avgpool(x).view(b, c)
y = self.l1(y)
y = self.relu(y)
y = self.l2(y)
y = self.sig(y)
y = y.view(b, c, 1, 1)
return x * y.expand_as(x)
2.CBAM注意力
SE注意力可以认为是对通道方向上进行权重加权,但是他并没有在位置上实现注意力。轻量型的CBAM模块则将注意力实现到channel和spatial两个维度上。
如上图所示我们可以看到,CBAN包含 channel注意力 和 spatia注意力 两个模块。channel模块类似于SE,spatial模块中对各坐标点进行跨通道的全局最大池化和全局平均池化。 对于两个模块的连接方式,论文作者建议先 channel attention再spatial attention ,以达到更好效果。
CBAM注意力:
论文地址:https://arxiv.org/abs/1807.06521
代码地址:https://github.com/Jongchan/attention-module
class ChannelAttention(nn.Module):
def __init__(self, in_planes, ratio=16):
super(ChannelAttention, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.max_pool = nn.AdaptiveMaxPool2d(1)
self.f1 = nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False)
self.relu = nn.ReLU()
self.f2 = nn.Conv2d(in_planes // ratio, in_planes, 1, bias=False)
# self.sharedMLP = nn.Sequential(
# nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False), nn.ReLU(),
# nn.Conv2d(in_planes // rotio, in_planes, 1, bias=False))
self.sigmoid = nn.Sigmoid()
def forward(self, x):
avg_out = self.f2(self.relu(self.f1(self.avg_pool(x))))
max_out = self.f2(self.relu(self.f1(self.max_pool(x))))
out = self.sigmoid(avg_out + max_out)
return out
class SpatialAttention(nn.Module):
def __init__(self, kernel_size=7):
super(SpatialAttention, self).__init__()
assert kernel_size in (3, 7), 'kernel size must be 3 or 7'
padding = 3 if kernel_size == 7 else 1
self.conv = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
avg_out = torch.mean(x, dim=1, keepdim=True)
max_out, _ = torch.max(x, dim=1, keepdim=True)
x = torch.cat([avg_out, max_out], dim=1)
x = self.conv(x)
return self.sigmoid(x)
class CBAMC3(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(CBAMC3, 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)
self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
self.channel_attention = ChannelAttention(c2, 16)
self.spatial_attention = SpatialAttention(7)
# self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])
def forward(self, x):
out = self.channel_attention(x) * x
print('outchannels:{}'.format(out.shape))
out = self.spatial_attention(out) * out
return out
3.ECA注意力
ECA也知识通道上的注意力机制。
还记得我们说过SE中的ratio吗?r可以降低模型的复杂性,但是ECA的作者发现降维会给通道注意力预测带来副作用,并且捕获所有通道之间的依存关系效率不高且不必要。故,作者在不降低维度的情况下进行逐通道全局平均池化后,通过考虑每个通道及其k个近邻来捕获本地跨通道交互 。
ECA注意力
class eca_layer(nn.Module):
"""Constructs a ECA module.
Args:
channel: Number of channels of the input feature map
k_size: Adaptive selection of kernel size
"""
def __init__(self, channel, k_size=3):
super(eca_layer, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.conv = nn.Conv1d(1, 1, kernel_size=k_size, padding=(k_size - 1) // 2, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
# feature descriptor on the global spatial information
y = self.avg_pool(x)
# Two different branches of ECA module
y = self.conv(y.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)
# Multi-scale information fusion
y = self.sigmoid(y)
x=x*y.expand_as(x)
return x * y.expand_as(x)
4.CA注意力
Coordinate Attention(CA),是一种新颖为移动网络设计的注意力机制。
这个原理我还没看哈哈哈哈 晚上更
CA注意力
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
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):
return x * self.sigmoid(x)
class CoordAtt(nn.Module):
def __init__(self, inp, oup, reduction=32):
super(CoordAtt, self).__init__()
self.pool_h = nn.AdaptiveAvgPool2d((None, 1))
self.pool_w = nn.AdaptiveAvgPool2d((1, None))
mip = max(8, inp // reduction)
self.conv1 = nn.Conv2d(inp, mip, kernel_size=1, stride=1, padding=0)
self.bn1 = nn.BatchNorm2d(mip)
self.act = h_swish()
self.conv_h = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0)
self.conv_w = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0)
def forward(self, x):
identity = x
n, c, h, w = x.size()
x_h = self.pool_h(x)
x_w = self.pool_w(x).permute(0, 1, 3, 2)
y = torch.cat([x_h, x_w], dim=2)
y = self.conv1(y)
y = self.bn1(y)
y = self.act(y)
x_h, x_w = torch.split(y, [h, w], dim=2)
x_w = x_w.permute(0, 1, 3, 2)
a_h = self.conv_h(x_h).sigmoid()
a_w = self.conv_w(x_w).sigmoid()
out = identity * a_w * a_h
return out
三、运行实例
参考
参考博客:(1条消息) CBAM注意力模块: Convolutional Block Attention Module_爱CV-CSDN博客_注意力模块
(1条消息) SE模块详解_Mounsey的博客-CSDN博客_se模块
(1条消息) yolov5加入CBAM,SE,CA,ECA注意力机制,纯代码_zqt321的博客-CSDN博客
来源:Time.Xu