• 【CNN】SENet——将注意力机制引入通道维度


    前言

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

    论文下载链接:
    https://openaccess.thecvf.com/content_cvpr_2018/papers/Hu_Squeeze-and-Excitation_Networks_CVPR_2018_paper.pdf
    
    • 1
    • 2

    一、简介

    (1)提出背景:卷积核通常被看做是在局部感受野上,在空间上和通道维度上同时对信息进行相乘求和的计算。现有网络很多都是主要在空间维度方面来进行特征的融合(如Inception的多尺度)。

    (2)通道维度的注意力机制:在常规的卷积操作中,输入信息的每个通道进行计算后的结果会进行求和输出,这时每个通道的重要程度是相同的。而通道维度的注意力机制,则通过学习的方式来自动获取到每个特征通道的重要程度(即feature map层的权重),以增强有用的通道特征,抑制不重要的通道特征

    (3)说起卷积对通道信息的处理,有人或许会想到逐点卷积,即kernel大小为1X1的常规卷积。与1X1卷积相比,SENet是为每个channel重新分配一个权重(即重要程度)。而1X1卷积只是在做channel的融合计算,顺带进行升维和降维,也就是说每个channel在计算时的重要程度是相同的。

    二、SE模块

    Squeeze:

    顺着空间维度来压缩特征,就是在空间上做全局平均池化,每个通道上特征的空间维度从二维变成了一个scale,这个标量某种程度上具有全局的感受野。并且输出的维度和输入的通道数相匹配。用全局平均池化是因为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信息
    在这里插入图片描述
    详细流程说明:

    (1)X经过一系列传统卷积得到U,对U先做一个Global Average Pooling ,输出的1x1xC数据 (即,上图梯形短边的白色向量),这个特征向量一定程度上可以代表之前的输入信息,论文中称之为Squeeze操作
    (2)再经过两个全连接来学习通道间的重要性 ,用sigmoid限制到[0, 1]的范围,这时得到的输出可以看作每个通道重要程度的权重(即上图梯形短边的彩色向量),论文中称之为Excitation操作
    (3)最后,把这个1x1xC的权重乘到U的C个通道上,这时就根据权重对U的channles进行了重要程度的重新分配

    三、效果

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

    在大部分模型中嵌入SENet要比非SENet的准确率更高出1%左右,而计算复杂度上只是略微有提升,具体如下图所示。 而且SE块会使训练和收敛更容易。CPU推断时间的基准测试:224×224的输入图像,ResNet-50 164ms,SE-ResNet-50 167ms。
    在这里插入图片描述
    在这里插入图片描述

    四、代码

    这里给出模型搭建的python代码(基于pytorch实现)。

    ()
    def _make_divisible(ch, divisor=8, min_ch=None):
        """
        This function is taken from the original tf repo.
        It ensures that all layers have a channel number that is divisible by 8
        It can be seen here:
        https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
        """
        if min_ch is None:
            min_ch = divisor
        new_ch = max(min_ch, int(ch + divisor / 2) // divisor * divisor)
        # Make sure that round down does not go down by more than 10%.
        if new_ch < 0.9 * ch:
            new_ch += divisor
        return new_ch
    
    
    class SqueezeExcitation(nn.Module):
        def __init__(self, input_c: int, squeeze_factor: int = 4):
            super(SqueezeExcitation, self).__init__()
            squeeze_c = _make_divisible(input_c // squeeze_factor, 8)
            self.fc1 = nn.Conv2d(input_c, squeeze_c, 1)
            self.fc2 = nn.Conv2d(squeeze_c, input_c, 1)
    
        def forward(self, x):
            scale = F.adaptive_avg_pool2d(x, output_size=(1, 1))
            scale = self.fc1(scale)
            scale = F.relu(scale, inplace=True)
            scale = self.fc2(scale)
            scale = F.hardsigmoid(scale, inplace=True)
            return scale * x
    
    (二)
    class SqueezeExcitation(nn.Module):
        def __init__(self, channel, reduction=16):
            super(SqueezeExcitation, self).__init__()
    
            # // 返回1X1大小的特征图,通道数不变
            self.avg_pool = nn.AdaptiveAvgPool2d(1)
            self.fc = nn.Sequential(
                nn.Linear(channel, channel // reduction, bias=False),
                # nn.ReLU(inplace=True),
                nn.Hardswish(inplace=True),
                nn.Linear(channel // reduction, channel, bias=False),
                nn.Sigmoid()
            )
    
        def forward(self, x):
            b, c, _, _ = x.size()
    
            # // 全局平均池化,batch和channel和原来一样保持不变
            y = self.avg_pool(x).view(b, c)
    
            # // 全连接层 + 池化
            y = self.fc(y).view(b, c, 1, 1)
    
            # // 和原特征图相乘
            return x * y.expand_as(x)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    五、总结

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

  • 相关阅读:
    【云开发】在 React Native 中使用 AWS Textract 实现文本提取
    想买GPT4会员却只能排队?来看看背后的故事!
    《软件质量保证与测试》第 6 章——系统测试 重点部分总结
    lazarus:数据集快速导出为excel、csv、sql及其他多种格式
    前端实战|React18极客园——内容管理模块(表格筛选、频道、分页、删除、编辑跳转)
    镜像站制作 centos8
    vue 弹框中包含avue-curd /el-table第一次点击样式正常 再次点击表格序号列和其他列错位
    计算机视觉论文精度大纲
    【web前端开发】HTML知识点超详细总结
    不知道不 OK!53 个 Python 经典面试题详解
  • 原文地址:https://blog.csdn.net/lingchen1906/article/details/127991871