当前位置: 首页 > news >正文

注意力机制(一)SE模块(Squeeze-and-Excitation Networks)论文总结和代码实现

Squeeze-and-Excitation Networks(压缩和激励网络)

论文地址:Squeeze-and-Excitation Networks

论文中文版:Squeeze-and-Excitation Networks_中文版

代码地址:GitHub - hujie-frank/SENet: Squeeze-and-Excitation Networks

目录

一、论文出发点

二、论文的主要工作

三、Squeeze-and-Excitation模块

(1)Transformation(Ftr): 转型

(2)Squeeze:全局信息嵌入

(3)Excitation:自适应重新校正

(4)Scale:重新加权

四、模型:SE-Inception和SE-ResNet

五、实验

六、结论

七、源码分析

 (1)SE模块

 (2)SE-ResNet完整代码


一、论文出发点

为了提高网络的表示能力,许多现有的工作已经显示出增强空间编码的好处。而作者专注于通道,希望能够提出了一种新的架构单元,通过显式地建模出卷积特征通道之间的相互依赖性来提高网络的表示能力。

这里引用“博文:Squeeze-and-Excitation Networks解读”中的总结:核心思想是不同通道的权重应该自适应分配,由网络自己学习出来的,而不是像Inception net一样留下过多人工干预的痕迹。

二、论文的主要工作

1.提出了一种新的架构单元Squeeze-and-Excitation模块,该模块可以显式地建模卷积特征通道之间的相互依赖性来提高网络的表示能力。

2.提出了一种机制,使网络能够执行特征重新校准,通过这种机制可以学习使用全局信息来选择性地强调信息特征并抑制不太有用的特征。

三、Squeeze-and-Excitation模块

(1)Transformation(Ftr): 转型

F_{tr}:X\rightarrow U,经过F_{tr}特征图X变为特征图U。
F_{tr}可以看作一个标准的卷积算子。该卷积算子公式为U_{c}=V_{c}*X=\sum_{s=1}^{C'}V_{c}^{s}*X^{s}

其中:

1.   U=[U_{1},U_{2}...U_{c}],这里U_c指输出特征图的一个单通道2D特征层。

2.   V=[V_{1},V_{2}...V_{c}]表示学习到的一组滤波器核,Vc指的是第c个滤波器的参数,此外V_{c}=[V_{c}^{1},V_{c}^{2}...V_{c}^{c'}]这里 V_{c}^{s}是指一个通道数为1的2D空间核

3.  X=[X^{1},X^{2}...X^{c'}]这里X^{s}是指输入特征图的一个单通道2D特征层

该卷积算子公式表示,输入特征图X的每一层都经过一个2D空间核的卷积最终得到C个输出的feature map,组成特征图U。

原文内容如下:

  • X∈R^(H′×W′×C′):输入特征图
  • U∈R^(H×W×C):输出特征图
  • V:表示学习到的一组滤波器核
  • Vc:指的是第c个滤波器的参数
  • V_{c}^{s}​:表示一个2D的空间核
  • *:卷积操作

(2)Squeeze:全局信息嵌入

Fsq就是使用通道的全局平均池化。
原文中为了解决利用通道依赖性的问题,选择将全局空间信息压缩到一个信道描述符中,即使用通道的全局平均池化,将包含全局信息的W×H×C 的特征图直接压缩成一个1×1×C的特征向量Z,C个feature map的通道特征都被压缩成了一个数值,这样使得生成的通道级统计数据Z就包含了上下文信息,缓解了通道依赖性的问题。
算子公式如下:

Zc为Z的第c个元素。

(3)Excitation:自适应重新校正

目的为了利用压缩操作中汇聚的信息,我们接下来通过Excitation操作来全面捕获通道依赖性。
实现方法
为了实现这个目标,这个功能必须符合两个标准
第一,它必须是灵活的 (它必须能够学习通道之间的非线性交互)
第二,它必须学习一个非互斥的关系,因为独热激活相反,这里允许强调多个通道。
为了满足这些标准,作者采用了两层全连接构成的门机制,第一个全连接层把C个通道压缩成了C/r个通道来降低计算量,再通过一个RELU非线性激活层,第二个全连接层将通道数恢复回为C个通道,再通过Sigmoid激活得到权重s,最后得到的这个s的维度是1×1×C,它是用来刻画特征图U中C个feature map的权重。r是指压缩的比例。

为什么这里要有两个FC,并且通道先缩小,再放大?

因为一个全连接层无法同时应用relu和sigmoid两个非线性函数,但是两者又缺一不可。为了减少参数,所以设置了r比率。

(4)Scale:重新加权

目的:最后是Scale操作,将前面得到的注意力权重加权到每个通道的特征上

实现方法:
特征图U中的每个feature map乘以对应的权重,得到SE模块的最终输出\widetilde{X}

四、模型:SE-Inception和SE-ResNet

通过将一个整体的Inception模块看作SE模块中F_{tr},为Inception网络构建SE模块。

同理, 将一个整体的Residual模块看作SE模块中F_{tr},为ResNet网络构建SE模块。

五、实验

六、结论

本文提出的SE模块,这是一种新颖的架构单元,旨在通过使网络能够执行动态通道特征重新校准来提高网络的表示能力。大量实验证明了SENets的有效性,其在多个数据集上取得了最先进的性能。

七、源码分析

将SEblock嵌入ResNet的残差模块中

 (1)SE模块

'''-------------一、SE模块-----------------------------'''
#全局平均池化+1*1卷积核+ReLu+1*1卷积核+Sigmoid
class SE_Block(nn.Module):def __init__(self, inchannel, ratio=16):super(SE_Block, self).__init__()# 全局平均池化(Fsq操作)self.gap = nn.AdaptiveAvgPool2d((1, 1))# 两个全连接层(Fex操作)self.fc = nn.Sequential(nn.Linear(inchannel, inchannel // ratio, bias=False),  # 从 c -> c/rnn.ReLU(),nn.Linear(inchannel // ratio, inchannel, bias=False),  # 从 c/r -> cnn.Sigmoid())def forward(self, x):# 读取批数据图片数量及通道数b, c, h, w = x.size()# Fsq操作:经池化后输出b*c的矩阵y = self.gap(x).view(b, c)# Fex操作:经全连接层输出(b,c,1,1)矩阵y = self.fc(y).view(b, c, 1, 1)# Fscale操作:将得到的权重乘以原来的特征图xreturn x * y.expand_as(x)

 (2SE-ResNet完整代码

不同版本的ResNet各层主要是由BasicBlock模块(18-layer、34-layer)或Bottleneck模块(50-layer、101-layer、152-layer)构成的,因此只要在BasicBlock模块或Bottleneck模块尾部添加SE模块即可,但是要注意放在shortcut之前,因为shortcut仅是为了保存梯度,把SE模块加在作为提取信息的主干上即可。

import torch
import torch.nn as nn
import torch.nn.functional as F
from torchsummary import summary'''-------------一、SE模块-----------------------------'''
#全局平均池化+1*1卷积核+ReLu+1*1卷积核+Sigmoid
class SE_Block(nn.Module):def __init__(self, inchannel, ratio=16):super(SE_Block, self).__init__()# 全局平均池化(Fsq操作)self.gap = nn.AdaptiveAvgPool2d((1, 1))# 两个全连接层(Fex操作)self.fc = nn.Sequential(nn.Linear(inchannel, inchannel // ratio, bias=False),  # 从 c -> c/rnn.ReLU(),nn.Linear(inchannel // ratio, inchannel, bias=False),  # 从 c/r -> cnn.Sigmoid())def forward(self, x):# 读取批数据图片数量及通道数b, c, h, w = x.size()# Fsq操作:经池化后输出b*c的矩阵y = self.gap(x).view(b, c)# Fex操作:经全连接层输出(b,c,1,1)矩阵y = self.fc(y).view(b, c, 1, 1)# Fscale操作:将得到的权重乘以原来的特征图xreturn x * y.expand_as(x)'''-------------二、BasicBlock模块-----------------------------'''
# 左侧的 residual block 结构(18-layer、34-layer)
class BasicBlock(nn.Module):expansion = 1def __init__(self, inchannel, outchannel, stride=1):super(BasicBlock, self).__init__()self.conv1 = nn.Conv2d(inchannel, outchannel, kernel_size=3,stride=stride, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(outchannel)self.conv2 = nn.Conv2d(outchannel, outchannel, kernel_size=3,stride=1, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(outchannel)# SE_Block放在BN之后,shortcut之前self.SE = SE_Block(outchannel)self.shortcut = nn.Sequential()if stride != 1 or inchannel != self.expansion*outchannel:self.shortcut = nn.Sequential(nn.Conv2d(inchannel, self.expansion*outchannel,kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(self.expansion*outchannel))def forward(self, x):out = F.relu(self.bn1(self.conv1(x)))out = self.bn2(self.conv2(out))SE_out = self.SE(out)out = out * SE_outout += self.shortcut(x)out = F.relu(out)return out'''-------------三、Bottleneck模块-----------------------------'''
# 右侧的 residual block 结构(50-layer、101-layer、152-layer)
class Bottleneck(nn.Module):expansion = 4def __init__(self, inchannel, outchannel, stride=1):super(Bottleneck, self).__init__()self.conv1 = nn.Conv2d(inchannel, outchannel, kernel_size=1, bias=False)self.bn1 = nn.BatchNorm2d(outchannel)self.conv2 = nn.Conv2d(outchannel, outchannel, kernel_size=3,stride=stride, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(outchannel)self.conv3 = nn.Conv2d(outchannel, self.expansion*outchannel,kernel_size=1, bias=False)self.bn3 = nn.BatchNorm2d(self.expansion*outchannel)# SE_Block放在BN之后,shortcut之前self.SE = SE_Block(self.expansion*outchannel)self.shortcut = nn.Sequential()if stride != 1 or inchannel != self.expansion*outchannel:self.shortcut = nn.Sequential(nn.Conv2d(inchannel, self.expansion*outchannel,kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(self.expansion*outchannel))def forward(self, x):out = F.relu(self.bn1(self.conv1(x)))out = F.relu(self.bn2(self.conv2(out)))out = self.bn3(self.conv3(out))SE_out = self.SE(out)out = out * SE_outout += self.shortcut(x)out = F.relu(out)return out'''-------------四、搭建SE_ResNet结构-----------------------------'''
class SE_ResNet(nn.Module):def __init__(self, block, num_blocks, num_classes=10):super(SE_ResNet, self).__init__()self.in_planes = 64self.conv1 = nn.Conv2d(3, 64, kernel_size=3,stride=1, padding=1, bias=False)                  # conv1self.bn1 = nn.BatchNorm2d(64)self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)       # conv2_xself.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)      # conv3_xself.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)      # conv4_xself.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)      # conv5_xself.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.linear = nn.Linear(512 * block.expansion, num_classes)def _make_layer(self, block, planes, num_blocks, stride):strides = [stride] + [1]*(num_blocks-1)layers = []for stride in strides:layers.append(block(self.in_planes, planes, stride))self.in_planes = planes * block.expansionreturn nn.Sequential(*layers)def forward(self, x):x = F.relu(self.bn1(self.conv1(x)))x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = self.avgpool(x)x = torch.flatten(x, 1)out = self.linear(x)return outdef SE_ResNet18():return SE_ResNet(BasicBlock, [2, 2, 2, 2])def SE_ResNet34():return SE_ResNet(BasicBlock, [3, 4, 6, 3])def SE_ResNet50():return SE_ResNet(Bottleneck, [3, 4, 6, 3])def SE_ResNet101():return SE_ResNet(Bottleneck, [3, 4, 23, 3])def SE_ResNet152():return SE_ResNet(Bottleneck, [3, 8, 36, 3])'''
if __name__ == '__main__':model = SE_ResNet50()print(model)input = torch.randn(1, 3, 224, 224)out = model(input)print(out.shape)
# test()
'''
if __name__ == '__main__':device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")net = SE_ResNet50().to(device)# 打印网络结构和参数summary(net, (3, 224, 224))

http://www.lryc.cn/news/90086.html

相关文章:

  • L2-001 紧急救援(dijkstra算法练习)
  • redis问题汇总
  • 调用华为API实现情感分析
  • C# 静态构造函数
  • 【C++】哈希表特性总结及unordered_map和unordered_set的模拟实现
  • Qt在Linux内核中的应用及解析(qtlinux内核)
  • Xpdf 阅读器源码编译后查看文件中文乱码问题解决
  • Java - AQS-CountDownLatch实现类(二)
  • rsut基础
  • 高压放大器和示波器的关系是什么
  • 5个超实用视频素材网站,免费下载~
  • 【NLP模型】文本建模(1)(BoW、N-gram、tf-idf)
  • Java——网络编程套接字
  • 160套小程序源码
  • 有效项目进度管理的 10 条规则
  • javaWebssh服装租赁店信息管理系统台myeclipse开发mysql数据库MVC模式java编程计算机网页设计
  • 概率论:样本与总体分布,Z分数与概率
  • 【JavaSE】Java基础语法(十二):ArrayList
  • c++—封装:运算符重载、友元
  • 【K8s】安全认证与DashBoard
  • SpringMVC第七阶段:SpringMVC的增删改查(01)
  • 接口测试-Mock测试方法
  • 关于宝塔部署jar包和war包
  • SpringMVC框架面试专题(初级-中级)-第十节
  • PCIe TLB事务层详解过程
  • RK3588平台开发系列讲解(项目篇)YOLOv5部署测试
  • 基于变形模板的弱监督体图像分割
  • python实现单例模式及其应用
  • SSM 如何使用 Seata 框架实现分布式事务?
  • FreeRTOS任务相关API函数