在yolov5的网络结构中添加注意力机制模块

自从学习这方面的知识之后,近总是想学很多知识,但是学的泛而不精通,长时间不用就会遗忘。在此将每一部分的学习在此做出记录,防止遗忘。



前言

自从学习这方面的知识之后,近总是想学很多知识,但是学的泛而不精通,长时间不用就会遗忘。在此将每一部分的学习在此做出记录,防止遗忘。

目录

前言

一、模块添加步骤

二、相应注意力机制介绍及其代码

1.SE注意力

2.CBAM注意力

3.ECA注意力

4.CA注意力

参考



常见涨点的注意力机制有CBAM, SE, CA, ECA等。该文章对以下模块进行总结,如有其他好用的模块再来更新。


一、模块添加步骤

先说模块添加的方法

1.将注意力机制的代码写进 models/common.py 或 models/experimental.py 中去;

2.结合注意力机制需要的参数以及自己模型的通道数等条件,更改自己的配置文件;

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注意力

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博客

おすすめ

転載: blog.csdn.net/vibration_xu/article/details/121561649