Squeeze-and-Excitation Networks

 1、引言

        论文链接:Squeeze-and-Excitation Networks (thecvf.com)

        为了提高 CNN(convolutional neural network) 的表示能力,最近的几种方法已经显示出增强空间编码的好处。而 Jie Hu[1] 等专注于通道关系并提出了 SE(Squeeze and Excitation) 块,它通过显式建模通道之间的相互依赖关系来自适应地重新校准通道特征响应。将 SE 块引入到某个网络中就得到对应的 SENet(Squeeze-and-Excitation Networks)[1],如在 ResNet-50[2] 的每个 Bottleneck 中引入 SE 块就得到 SE-ResNet-50[1]。SE 块以微小的额外计算成本对现有最先进的深度架构产生了显着的性能改进,Jie Hu[1] 等因此而获得了 ILSVRC 2017 分类任务的第一名。

2、方法

图1  挤压-激励块

        如图 1 所示,所谓的 SE 块主要由 Fsq(.)(挤压操作)和 Fex(., W)(激励操作)组成,假设SE 块的输入特征 U 的 shape=(C, H, W),经过 Fsq(.) 后 shape 变为 (C, 1, 1),再经过 Fex(., W) 后得到 U 的通道权重 S,最后执行 Fscale(., .),即 U*S 得到 SE 块的输出。

图2  原始残差模块(左)和 SE-ResNet 模块(右)的模式

        SE 块的细节如图 2(右)所示,可以看到 Fsq(.)= AdaptiveAvgPool,而 Fex(., W) 主要由两个 Linear 层组成:对于 shape=(C, 1, 1) 的输入 X,首先由一个 Linear 层将其通道数降为原来的 1/r,然后是一个 ReLU 层,再由一个 Linear 层将通道数恢复,最后经过 Sigmoid 激活得到 X 的通道权重 S。

3、总结

       [1] 中提出了可以通过动态通道特征重新校准来提高网络的表示能力的 SE 块。SENet 在多个数据集上达到 SOTA,是通道注意力机制的代表模型;此外,SE块还揭示了先前架构无法充分建模通道特性依赖关系的问题;最后,SE 块引起的特征重要性可能有助于相关领域,例如用于压缩的网络修剪。

        作者公开的代码和模型在:GitHub - hujie-frank/SENet: Squeeze-and-Excitation Networks,由于该代码基于 Caffe 实现,故本人基于 Pytorch 实现了 SE 块如下所示:

from torch import nn

__all__ = ['SENet']


class SENet(nn.Module):
    def __init__(self, in_c, ratio=16):
        super().__init__()
        self.squeeze = nn.AdaptiveAvgPool2d(1)
        self.excitation = nn.Sequential(
            nn.Conv2d(in_c, in_c // ratio, kernel_size=1, bias=False),
            nn.ReLU(True),
            nn.Conv2d(in_c // ratio, in_c, kernel_size=1, bias=False),
            nn.Sigmoid()
        )

    def forward(self, x):
        z = self.squeeze(x)
        w = self.excitation(z)
        return x * w

参考文献

[1] Jie Hu, Li Shen, and Gang Sun. Squeeze-and-Excitation Networks. In CVPR, 2018.

[2] Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian SunDeep Residual Learning for Image Recognition. In CVPR, 2016.

### Squeeze-and-Excitation Networks 的工作原理 Squeeze-and-Excitation (SE) 网络是一种用于提升卷积神经网络(CNN)性能的技术,它通过显式地建模特征通道之间的相互依赖关系来增强网络的表达能力。具体来说,SE 块引入了一个轻量级的门控机制,能够动态调整不同通道的重要性权重[^2]。 #### 工作流程概述 SE 块的核心思想在于捕获全局信息并通过自适应方式重新校准通道间的特性响应。这一过程分为三个主要阶段: 1. **Squeeze(挤压)**: 这一阶段的目标是从输入特征图中提取全局描述符。通过对每个通道上的特征图执行全局平均池化操作,得到一个关于整个空间维度的单一数值作为该通道的代表性统计量。假设输入张量大小为 \(H \times W \times C\),其中 \(C\) 是通道数,则经过此步后会生成一个长度为 \(C\) 的向量。 ```python import torch.nn as nn class SELayer(nn.Module): def __init__(self, channel, reduction=16): super(SELayer, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) def forward(self, x): b, c, _, _ = x.size() y = self.avg_pool(x).view(b, c) # Squeeze operation ... ``` 2. **Excitation(激励)**: 在获取到每条通道的重要程度之后,下一步是对这些重要性分数进行非线性变换以产生最终的激活权值。通常采用两个全连接层构成的小型子网路完成这项任务——第一个 FC 层负责降维至较小规模的空间;第二个则将其恢复回原始尺寸,并施加 sigmoid 函数确保输出位于 [0,1] 范围内。 ```python from torch.nn import functional as F class SELayer(nn.Module): ... def __init__(self, channel, reduction=16): ... self.fc = nn.Sequential( nn.Linear(channel, channel // reduction, bias=False), nn.ReLU(inplace=True), nn.Linear(channel // reduction, channel, bias=False), nn.Sigmoid() ) def forward(self, x): ... y = self.fc(y) # Excitation operation ... ``` 3. **Reweight(重缩放)**: 将上述产生的权重应用于对应的特征图上,即乘法运算实现逐像素级别的调制作用。这样做的效果相当于让模型学会关注哪些区域或者概念更重要,从而提高整体表现力。 ```python class SELayer(nn.Module): ... def forward(self, x): ... y = y.view(b, c, 1, 1) return x * y.expand_as(x) # Reweighting the features ``` ### 实现方法 为了将 SE 结构融入现有的 CNN 架构之中,只需在其基础组件如 ResNet 或 Inception 中嵌入相应位置即可。例如,在标准残差单元内部加入 SE 模块便构成了 SENet 版本的改进版本。 完整的 PyTorch 示例代码如下所示: ```python import torch import torch.nn as nn from torch.nn import functional as F class BasicBlock_SE(nn.Module): expansion = 1 def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, base_width=64, dilation=1, norm_layer=None, reduction_ratio=16): super(BasicBlock_SE, self).__init__() if norm_layer is None: norm_layer = nn.BatchNorm2d self.conv1 = conv3x3(inplanes, planes, stride) self.bn1 = norm_layer(planes) self.relu = nn.ReLU(inplace=True) self.conv2 = conv3x3(planes, planes) self.bn2 = norm_layer(planes) self.se = SELayer(planes, reduction=reduction_ratio) self.downsample = downsample self.stride = stride def forward(self, x): identity = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) out = self.se(out) # Apply SE block here if self.downsample is not None: identity = self.downsample(x) out += identity out = self.relu(out) return out def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1): """3x3 convolution with padding""" return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=dilation, groups=groups, bias=False, dilation=dilation) class SELayer(nn.Module): def __init__(self, channel, reduction=16): super(SELayer, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.fc = nn.Sequential( nn.Linear(channel, channel // reduction, bias=False), nn.ReLU(inplace=True), nn.Linear(channel // reduction, channel, bias=False), nn.Sigmoid() ) def forward(self, x): b, c, _, _ = x.size() y = self.avg_pool(x).view(b, c) y = self.fc(y).view(b, c, 1, 1) return x * y.expand_as(x) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值