前言
SENet,胡杰(Momenta)在2017.9提出,通过显式地建模卷积特征通道之间的相互依赖性来提高网络的表示能力,SE块以微小的计算成本为现有的最先进的深层架构产生了显著的性能改进,SENet block和ResNeXt结合在ILSVRC 2017赢得第一名。

一、Motivation
提出背景:现有网络很多都是主要在空间维度方面来进行特征通道间的融合(如Inception的多尺度)。卷积核通常被看做是在局部感受野上,将空间上和特征维度上的信息进行聚合的信息聚合体。
channel维度的注意力机制:通过学习的方式来自动获取到每个特征通道的重要程度(即feature map层的权重),以增强有用的特征,抑制不重要的特征。
与1X1卷积相比,SE是为每个channel重新分配一个权重(即重要程度);而逐点卷积只是在做channel的融合,顺带进行升维和降维。
二、SE模块
Squeeze:全局信息嵌入,全局平均池化成1*1
Excitation:自适应调整,用两个全连接,训练参数W,得到每通道的权重s

在这里插入图片描述
总流程:

X经过一系列传统卷积得到U,对U先做一个Global Average Pooling ,输出的1x1xC数据
(即上图梯形短边的白色向量)。
再经过两个全连接后 ,用sigmoid限制到[0, 1]的范围,这时得到的输入可以看作每个通道的权重(即上图梯形短边的彩色向量)。
把这个1x1xC的值乘到U的C个通道上,这时就对channles进行了重要程度的重新分配。
Squeeze:
顺着空间维度来压缩特征,就是在空间上做全局平均池化,每个通道的二维特征变成了一个实数,这个实数某种程度上具有全局的感受野,并且输出的维度和输入的通道数相匹配。用全局平均池化是因为scale是对整个通道作用的,利用的是通道间的相关性,要屏蔽掉空间分布相关性的干扰。

Excitation:
用2个全连接来实现 ,第一个全连接把C个通道压缩成了C/ratio 个通道来降低计算量(后面跟了RELU,增加非线性并减少计算量),ratio是指压缩的比例一般取r=16;第二个全连接再恢复回C个通道(后面跟了Sigmoid归一化到0~1),Sigmoid后得到权重 s。s就是U中C个feature map的权重,通过训练的参数 w 来为每个特征通道生成权重s,其中参数 w 被学习用来显式地建模特征通道间的相关性。用全连接是因为全连接层可以融合全局池化后各通道的feature map信息。

三、效果
与SE 模块可以嵌入到现在几乎所有的网络结构中,而且都可以得到不错的效果提升,用过的都说好。

在所有的结构上SENet要比非SENet的准确率更高出1%左右,而计算复杂度上只是略微有提升(增加的主要是全连接层,全连接层其实主要还是增加参数量,对速度影响不会太大)。在MobileNet上提升更高。而且SE块会使训练和收敛更容易。CPU推断时间的基准测试:224×224的输入图像,ResNet-50 164ms,SE-ResNet-50 167ms。

在这里插入图片描述

在这里插入图片描述

四、代码
这里给出模型搭建的python代码(基于pytorch实现)。完整的代码是基于图像分类问题的(包括训练和推理脚本,自定义层等)详见我的GitHub: 完整代码链接

class SqueezeExcitation(nn.Module):
    def __init__(self, pervious_layer_channels, scale_channels=None, scale_ratio=4):
        super().__init__()
        if scale_channels is None:
            scale_channels = pervious_layer_channels
        # assert input_channels > 16, 'input channels too small, Squeeze-Excitation is not necessary'
        squeeze_channels = make_divisible8(scale_channels//scale_ratio, 8)
        self.fc1 = nn.Conv2d(pervious_layer_channels, squeeze_channels, kernel_size=1, padding=0)
        self.fc2 = nn.Conv2d(squeeze_channels, pervious_layer_channels, kernel_size=1, padding=0)
    def forward(self, x):
        weight = F.adaptive_avg_pool2d(x, output_size=(1,1))
        weight = self.fc1(weight)
        weight = F.relu(weight, True)
        weight = self.fc2(weight)
        weight = F.hardsigmoid(weight, True)
        return weight * x

总结

SE block 可以理解为channel维度上的注意力机制(有重点的重分配通道权重),与Stochastic Depth Net一样,本论文的贡献更像一种算法,而非模型。 在之后的模型中,会经常看见SE block的身影。例如,mobileNet等等。