「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」
Attention
一、在图像处理中,注意力机制分为空间、通道注意力。
- 空间注意力机制:相对于一个层H∗WH*W而言,关注其中重要(权重高)的特征点
- 通道注意力机制:可以将C索引的作用∗H∗WC*H*W 通过平均池化将H∗WH*W浓缩为1∗11*1,最终形成1∗1∗C1*1*C线性特征向量,进而关注其中重要的通道(权重高)
二、常用的注意力机制模块
SENet模块
1.介绍:这里是单独使用通道注意力机制。2017卷积公式的使用条件年提出的SENet是最后一届ImageNet竞赛的冠军,其实索引符号和详图符号现示意图如下所示。对于SENet模块,其重点是获得输入进来的特征层的每一个通道C的权值。利用SENet,我们可以让网络关注它最需要关注的通道。
2索引符号.代码实现(pytorch)
import torch
from torch import nn
class senet(nn.Module):
def __init__(self, channel, ratio=16):
super(senet, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1) # 全局平均池化操作,将H*W降维为1*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 -> b, c
avg = self.avg_pool(x).view(b, c)
# b, c -> b, c //ratio -> b, c -> b, c, 1, 1
fc = self.fc(avg).view(b, c, 1, 1)
print(fc)
# 注意,这里的张量相乘是必须要两个张量的维度是对应相同才行,不同部分只能是1。
# 例如此处的是:2*512*26*26和2*512*1*1张量之间的乘积
# torch.Size([4, 4, 2])可以和torch.Size([4, 1, 1])乘积
return x * fc
model = senet(512)
print(model)
inputs = torch.ones([2, 512, 26, 26])
outputs = model(inputs)
# print(outputs)
CBAM模块
1.概要介绍:这索引超出矩阵维度里是将通道注意力机制和空间注意力机制结合使用。效果比SENet好,其实现卷积云示意图如下所示。
2.具体介绍:将通道注意力机卷积公式概率论制和空间注意力机制分别展开。 通道注意力机制:示意图如下,首先将输入的特征图C∗H∗WC*H*W分别对每个通道的H∗WH*W进行最大池化和平均池化处理,生成一维张量C∗1∗1C*1*1;卷积之后分别通过线性层Shared MLP(和SENet索引符号一样,第一层对C降维,第二层恢复成输入时的C维);最后将通过线性层卷积公式表大全的两者相加并通过Sigmoid索引是什么意思Sigmoid函数得到通道注意力输出结果McM_c。 空间注意力机制:示意卷积积分公式图如下,首先对经过通道注意力机制优化后的特征图进行通道的平均池化和最大值池化操作,让通道转化为索引超出矩阵维度1,即将C∗H∗WC*H*W转化为1∗H∗W1*H*W;之后将最大池化和平均池化的结果进行卷积神经网络堆叠,得到2∗H∗W2*H*W;最后利卷积积分公式用通道数为1的卷积层调整通道数为1(H和W不变)并取SigmoidSigmoid函数得到每个特征点的卷积神经网络权重,得到空间注意力机制输出结果MsM_s
3.代码实现(pytorch)
import torch
from torch import nn
class channel_attention(nn.Module):
def __init__(self, channel, ratio = 16):
super(channel_attention, self).__init__()
self.max_pool = nn.AdaptiveAvgPool2d(1)
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channel, channel // ratio, False),
nn.ReLU(),
nn.Linear(channel // ratio, channel, False)
)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
b, c, h, w = x.size()
max_pool_out = self.max_pool(x).view([b, c])
avg_pool_out = self.avg_pool(x).view([b, c])
max_fc_out = self.fc(max_pool_out).view([b, c, 1, 1])
avg_fc_out = self.fc(avg_pool_out).view([b, c, 1, 1])
out = max_fc_out + avg_fc_out
out = self.sigmoid(out)
print(x.size())
return x * out
class spatial_attention(nn.Module):
def __init__(self, kernel_size = 7):
super(spatial_attention, self).__init__()
padding = 7 // 2
self.conv = nn.Conv2d(2, 1, kernel_size, 1, padding, bias = False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
# torch.max()有两个输出,第一个是最大值,第二个是最大值的索引
max_pool_out, _ = torch.max(x, dim = 1, keepdim=True)
mean_pool_out = torch.mean(x, dim = 1, keepdim=True)
pool_out = torch.cat([max_pool_out, mean_pool_out], dim = 1)
out = self.conv(pool_out)
out = self.sigmoid(out)
return x * out
class CBAM(nn.Module):
def __init__(self, channel, ratio = 16, kernel_size = 7):
super(CBAM, self).__init__()
self.channel_attention = channel_attention(channel, ratio)
self.spatial_attention = spatial_attention(kernel_size)
def forward(self, x):
x = self.channel_attention(x)
x = self.spatial_attention(x)
return x
model = CBAM(512)
print(model)
inputs = torch.ones(2, 512, 26, 26)
outputs = model(inputs)
print(outputs)
ECANet模块
1.介绍:EC卷积神经网络ANet可以看成是SENet的改进版索引的作用,也是基于通道注意力机制的。ECANet的作者认为通过两个全连接层捕获所有通道的依赖关系是低效并且是不必要的,而使用卷积可以跨通道捕获信息。模块展示如下图所示,图左边是SENet模块,图右是改进之后的ECANet模块。
2.代码实现(pytorch)
import math
import torch
from torch import nn
class ECANet(nn.Module):
def __init__(self, channel, gamma = 2, b = 1):
super(ECANet, self).__init__()
# 通过输入的channel来自适应调节卷积核的大小
# math.log(channel, 2),此处是对channel取对数,底数为2
# abs是取绝对值
kernel_size = int(abs((math.log(channel, 2) + b) / gamma))
# 将kernel_size取奇数
kernel_size = kernel_size if kernel_size % 2 else kernel_size + 1
padding = kernel_size // 2
self.avg_pool = nn.AdaptiveAvgPool2d(1)
# 输入和输出都是1维,此处的kernel_size是控制输入和输出的大小为1*1*C
self.conv = nn.Conv1d(1, 1, kernel_size, padding = padding, bias = False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
b, c, h, w = x.size()
# 对池化后的特征进行调整适合一维卷积的输入
# b为batch_size,1为每一个step的特征长度,c代表每一个时序
avg = self.avg_pool(x).view([b, 1, c])
print(avg.size())
out = self.conv(avg)
print(out.size())
out = self.sigmoid(out).view([b, c, 1, 1])
print(out.size())
return x * out
model = ECANet(512)
print(model)
inputs = torch.ones([2, 512, 26, 26])
outputs = model(inputs)
#print(outputs.size())
3.关于代码的注意事项:使用padding填充来保证输入和输出的特征条数量是卷积公式相同的,代码中索引超出了数组界限什么意思经过计算可知卷积核为1∗51*5。