SENet

论文:https://arxiv.org/abs/1709.01507

1 核心思想

SENet论文提出了一种新的特征处理方法,就是SE Block,全称为Squeeze-and-Excitation block。其处理过程如下图所示:

在这里插入图片描述
对于SE Block,输入为图像 X ∈ R H ′ × W ′ × H ′ X \in R^{H^{'}\times W^{'}\times H^{'}} XRH×W×H经过变换后的特征图 U ∈ R H × W × H U \in R^{H \times W \times H} URH×W×H。输出仍然是相同大小的特征图。处理过程是实现了不同channel之间的特征相互影响。

SE Block主要处理过程包括Squeeze和Excitation两个过程。

Squeeze

Squeeze模块是对输入特征应用全局平均池化操作,即将大小为 H × W × C H \times W \times C H×W×C的特征图变为 1 × 1 × C 1 \times 1 \times C 1×1×C,计算公式是:
在这里插入图片描述
普通的卷积操作只把卷积核作用于局部的输入特征块,Squeeze操作对特征进行逐通道全局平均池化操作后,就使得输出特征不只是受到局部输入块的影响,而是能够受到空间全局特征的影响。作者在论文中对比了全局平均池化和全局最大池化,都可以取得较好的正向分类和检测效果,只不过全局平均池化的效果更好一些,当然也可以其他空间聚合手段。

Excitation:
Excitation对输入的 1 × 1 × C 1 \times 1 \times C 1×1×C的特征进行跨通道的相互影响,论文中的做法是使用了两个全连接层。第一个全连接层使用了bottleneck的操作,输入通道数为C,输出通道数为 C r \frac{C}{r} rC r r r是超参数,论文中设置为16。第一个全连接层之后使用Relu激活函数。第二个全连接层输入通道数为 C r \frac{C}{r} rC,输出通道数为C,把特征的通道数再变换回去。最后使用Sigmoid激活函数,得到 1 × 1 × C 1 \times 1 \times C 1×1×C的输出。

Excitation的具体处理过程为:
在这里插入图片描述
最终将Excitation输出和SE Block的输入 U ∈ R H × W × H U \in R^{H \times W \times H} URH×W×H相乘得到最终变换后的输出。

小结
SE Block的作用是对不同的通道应用不同的权重系数,使得模型对更加关注信息量大的channel,这也可以看作是一种attention机制。

SE Block增加的参数数量很有限,具体增加的参数数量为:
2 r ∑ s = 1 S N s C s 2 \frac{2}{r}\sum_{s=1}^{S}N_s C_s^2 r2s=1SNsCs2
论文实验中 r r r取16,但作者也提到可以对不同层的SE Block应用不同大小的 r r r

2 应用

SE Block可以应用到已有的各CNN网络中,作者实验了将其应用于VGG、Inception、ResNet、mobilenet及shufflenet结构中,都在参数数量略有增加的情况下取得了更好的分类和识别效果。

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
作者在实验中捕获了不同深度网络层应用SE块之后的激活值,发现前面层不同类别的激活值很接近,这是因为低层网络在学习通用的特征。随着网络层数的加深,不同类别的激活值之间的差异在变大,说明开始学习具有辨别力的特征。但随着网络深度的进一步加深,发现激活值趋于饱和,所以作者提到可以移除最高层的SE块,这样可以减少参数数量,同时实验证明也没有造成太大的准确率下降。

总之,SE块通过进行跨通道的特征修正,在基本不增加计算量的前提下提升了分类精度,同时由于SE块具有良好的普适性,可以应用于各成熟的网络结构中,因此具有良好的应用前景

3 pyTorch实现

代码参考自:https://zhuanlan.zhihu.com/p/65459972

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)

对于SE-ResNet模型,只需要将SE模块加入到残差单元(应用在残差学习那一部分)就可以:

class SEBottleneck(nn.Module):
        expansion = 4

        def __init__(self, inplanes, planes, stride=1, downsample=None, reduction=16):
            super(SEBottleneck, self).__init__()
            self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
            self.bn1 = nn.BatchNorm2d(planes)
            self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
                                   padding=1, bias=False)
            self.bn2 = nn.BatchNorm2d(planes)
            self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
            self.bn3 = nn.BatchNorm2d(planes * 4)
            self.relu = nn.ReLU(inplace=True)
            self.se = SELayer(planes * 4, reduction)
            self.downsample = downsample
            self.stride = stride

        def forward(self, x):
            residual = x

            out = self.conv1(x)
            out = self.bn1(out)
            out = self.relu(out)

            out = self.conv2(out)
            out = self.bn2(out)
            out = self.relu(out)

            out = self.conv3(out)
            out = self.bn3(out)
            out = self.se(out)

            if self.downsample is not None:
                residual = self.downsample(x)

            out += residual
            out = self.relu(out)

            return out
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值