赞
踩
此篇也是作为学习笔记:
注意力机制:网络自适应的一个方式
注意力机制的核心重点就是让网络关注到它更需要关注的地方。
通道注意力机制:一般分为注意力机制分为通道注意力机制,空间注意力机制,以及二者的结合。
空间注意力机制,实际就是在一个CHW的层中有各种各样的东西的像素点,比如有兔子,乌龟,鸟等,但是我们要去自适应的检测兔子,那么就会去关注兔子。
通道注意力机制,关注哪个通道是重要的。实际上是重要的权值比较高
图片和解析 来源听取B站up主讲解Bubbliiiing总结
SENet是一种通道注意力机制。
其实现示意图如下所示,对于输入进来的特征层,我们关注其每一个通道的权重,对于SENet而言,其重点是获得输入进来的特征层,每一个通道的权值。利用SENet,我们可以让网络关注它最需要关注的通道。
其具体实现方式就是:
1、对输入进来的特征层进行全局平均池化。
2、然后进行两次全连接,第一次全连接神经元个数较少,第二次全连接神经元个数和输入特征层相同。
3、在完成两次全连接后,我们再取一次Sigmoid将值固定到0-1之间,此时我们获得了输入特征层每一个通道的权值(0-1之间)。
4、在获得这个权值后,我们将这个权值乘上原输入特征层即可。
- import torch
- import torch.nn as nn
- class senet(nn.Module):
- def __init__(self,channel,ratio =16):
- super(senet,self).__init__()
- self.avg_pool=nn.AdaptiveAvgPool2d(1)
- self.fc =nn.Sequential(
- nn.Linear(channel,channel//ratio,False),
- nn.ReLU(),
- nn.Linear(channel//ratio,channel,False),
- nn.Sigmoid(),
-
- )
- def forward(self,x):
- b,c,h,w=x.size()
- #b,c,h,w->b,c,1,1
- print("x",x.size())
- avg=self.avg_pool(x).view(b,c)
- print("avg",avg.size())
- #b,c->b,c//ratio->b,c->b,c,1,1
- fc=self.fc(avg).view(b,c,1,1)
- print("fc",fc)
- return x*fc
- model=senet(512)
- inputs=torch.ones([2,512,26,26])
- outputs=model(inputs)

CBAM是通道注意力和空间注意力的结合。
下图是通道注意力机制和空间注意力机制的具体实现方式:
图像的上半部分为通道注意力机制,通道注意力机制的实现可以分为两个部分,我们会对输入进来的单个特征层,分别进行全局平均池化和全局最大池化。之后对平均池化和最大池化的结果,利用共享的全连接层进行处理,我们会对处理后的两个结果进行相加,然后取一个Sigmoid,此时我们获得了输入特征层每一个通道的权值(0-1之间)。在获得这个权值后,我们将这个权值乘上原输入特征层即可。
图像的下半部分为空间注意力机制,我们会对输入进来的特征层,在每一个特征点的通道上取最大值和平均值。之后将这两个结果进行一个堆叠,利用一次通道数为1的卷积调整通道数,然后取一个Sigmoid,此时我们获得了输入特征层每一个特征点的权值(0-1之间)。在获得这个权值后,我们将这个权值乘上原输入特征层即可。
代码实现
- import torch
- import torch.nn as nn
- class ChannelAttention(nn.Module):
- def __init__(self, channel, ratio=16):
- super(ChannelAttention, self).__init__()
- self.avg_pool = nn.AdaptiveAvgPool2d(1)
- self.max_pool = nn.AdaptiveMaxPool2d(1)
-
- self.fc =nn.Sequential(
- nn.Linear(channel, channel // ratio, bias=False),
- nn.ReLU(),
- nn.Linear(channel // ratio, channel, bias=False),
- )
- self.sigmoid = nn.Sigmoid()
-
- def forward(self, x):
- b,c,h,w=x.size()
- avg_pool_out = self.avg_pool(x).view([b,c])
- max_pool_out = self.max_pool(x).view([b,c])
-
- avg_fc_out=self.fc(avg_pool_out)
- max_fc_out=self.fc(max_pool_out)
- out = avg_fc_out + max_fc_out
- out=self.sigmoid(out).view([b,c,1,1])
- # print(out)
- return out*x
-
- class SpatialAttention(nn.Module):
- def __init__(self, kernel_size=7):
- super(SpatialAttention, self).__init__()
-
- padding=7//2
- self.conv1 = 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)
- pool_out = torch.cat([avg_out, max_out], dim=1)
- out = self.conv1(pool_out)
- out=self.sigmoid(out)
- print("out",out)
- return out*x
-
- class cbam_block(nn.Module):
- def __init__(self, channel, ratio=16, kernel_size=7):
- super(cbam_block, self).__init__()
- self.channelattention = ChannelAttention(channel, ratio=ratio)
- self.spatialattention = SpatialAttention(kernel_size=kernel_size)
-
- def forward(self, x):
- x = x * self.channelattention(x)
- x = x * self.spatialattention(x)
- return x
-
- model=cbam_block(512)
- inputs=torch.ones([2,512,26,26])
- outputs=model(inputs)

ECANet是也是通道注意力机制的一种实现形式。ECANet可以看作是SENet的改进版。
ECANet的作者认为SENet对通道注意力机制的预测带来了副作用,捕获所有通道的依赖关系是低效并且是不必要的。
在ECANet的论文中,作者认为卷积具有良好的跨通道信息获取能力。
ECA模块的思想是非常简单的,它去除了原来SE模块中的全连接层,直接在全局平均池化之后的特征上通过一个1D卷积进行学习。
既然使用到了1D卷积,那么1D卷积的卷积核大小的选择就变得非常重要了,了解过卷积原理的同学很快就可以明白,1D卷积的卷积核大小会影响注意力机制每个权重的计算要考虑的通道数量。用更专业的名词就是跨通道交互的覆盖率。
代码
- import torch
- import math
- import torch.nn as nn
- class eca_block(nn.Module):
- def __init__(self, channel, b=1, gamma=2):
- super(eca_block, self).__init__()
- kernel_size = int(abs((math.log(channel, 2) + b) / gamma))
- kernel_size = kernel_size if kernel_size % 2 else kernel_size + 1
-
- self.avg_pool = nn.AdaptiveAvgPool2d(1)
- self.conv = nn.Conv1d(1, 1, kernel_size=kernel_size, padding=(kernel_size - 1) // 2, bias=False)
- self.sigmoid = nn.Sigmoid()
-
- def forward(self, x):
- b,c,h,w=x.size()
-
- avg=self.avg_pool(x).view([b,1,c])
- out=self.conv(avg)
- out=self.sigmoid(out).view([b,c,1,1])
- print("out",out)
- print((out*x).size())
- return out*x
-
- model=eca_block(512)
- inputs=torch.ones([2,512,26,26])
- outputs=model(inputs)

CA注意力机制是最近提出的一种注意力机制,全面关注特征层的空间信息和通道信息。
CA注意力的实现如图所示,可以认为分为两个并行阶段:
将输入特征图分别在为宽度和高度两个方向分别进行全局平均池化,分别获得在宽度和高度两个方向的特征图。假设输入进来的特征层的形状为[C, H, W],在经过宽方向的平均池化后,获得的特征层shape为[C, H, 1],此时我们将特征映射到了高维度上;在经过高方向的平均池化后,获得的特征层shape为[C, 1, W],此时我们将特征映射到了宽维度上。
然后将两个并行阶段合并,将宽和高转置到同一个维度,然后进行堆叠,将宽高特征合并在一起,此时我们获得的特征层为:[C, 1, H+W],利用卷积+标准化+激活函数获得特征。
之后再次分开为两个并行阶段,再将宽高分开成为:[C, 1, H]和[C, 1, W],之后进行转置。获得两个特征层[C, H, 1]和[C, 1, W]。
然后利用1x1卷积调整通道数后取sigmoid获得宽高维度上的注意力情况。乘上原有的特征就是CA注意力机制。
代码:
- import torch
- import torch.nn as nn
-
- class CA_Block(nn.Module):
- def __init__(self, channel, reduction=16):
- super(CA_Block, self).__init__()
-
- self.conv_1x1 = nn.Conv2d(in_channels=channel, out_channels=channel // reduction, kernel_size=1, stride=1,
- bias=False)
-
- self.relu = nn.ReLU()
- self.bn = nn.BatchNorm2d(channel // reduction)
-
- self.F_h = nn.Conv2d(in_channels=channel // reduction, out_channels=channel, kernel_size=1, stride=1,
- bias=False)
- self.F_w = nn.Conv2d(in_channels=channel // reduction, out_channels=channel, kernel_size=1, stride=1,
- bias=False)
-
- self.sigmoid_h = nn.Sigmoid()
- self.sigmoid_w = nn.Sigmoid()
-
-
- def forward(self, x):
- # batch_size,c,h,w
- _, _, h, w = x.size()
-
- # batch_size,c,h,w =>batch_size,c,h,1=>batch_size,c,1,h
- x_h = torch.mean(x, dim=3, keepdim=True).permute(0, 1, 3, 2)
- # batch_size,c,h,w=>batch_size,c,1,w
- x_w = torch.mean(x, dim=2, keepdim=True)
-
- # batch_size,c,1,w cat bachtch_size,c,1,h=>batch_size,c,1,w+h
- # batch_size,c,1,w+h=>batch_size,c/r,1,w+h
- x_cat_conv_relu = self.relu(self.bn(self.conv_1x1(torch.cat((x_h, x_w), 3))))
-
- # batch_size , c/r,1,w+h=>batch_size,c/r,1,h and batch_size,c/r,1,w
- x_cat_conv_split_h, x_cat_conv_split_w = x_cat_conv_relu.split([h, w], 3)
-
- # batch_size , c/r,1,h=>batch_size,c/r,h,1=>batch_size,c/r,h,1=>batch_size,c,h,1
- s_h = self.sigmoid_h(self.F_h(x_cat_conv_split_h.permute(0, 1, 3, 2)))
- # batch_size , c/r,1,w=>batch_size,c,1,w
- s_w = self.sigmoid_w(self.F_w(x_cat_conv_split_w))
-
- out = x * s_h.expand_as(x) * s_w.expand_as(x)
- print(out.size())
- return out
-
- model=CA_Block(512)
- inputs=torch.ones([2,512,26,26])
- outputs=model(inputs)

注意力机制是一个即插即用的模块,在理论的情况下是可以放在任何一个特征层后面,可以放在主干网络,也可以放在加强特征提取网络。
由于放置在主干会导致网络的预训练权重无法使用,所以我们在加注意力机制的时候还是要分情况加入,在加强特征提取网络中更容易一些。
以上为学习笔记,如有错误敬请指正感谢!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。