SENet

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

1 核心思想

SENet論文提出了一種新的特徵處理方法,就是SE Block,全稱爲Squeeze-and-Excitation block。其處理過程如下圖所示:

在這裏插入圖片描述
對於SE Block,輸入爲圖像XRH×W×HX \in R^{H^{'}\times W^{'}\times H^{'}}經過變換後的特徵圖URH×W×HU \in R^{H \times W \times H}。輸出仍然是相同大小的特徵圖。處理過程是實現了不同channel之間的特徵相互影響。

SE Block主要處理過程包括Squeeze和Excitation兩個過程。

Squeeze

Squeeze模塊是對輸入特徵應用全局平均池化操作,即將大小爲H×W×CH \times W \times C的特徵圖變爲1×1×C1 \times 1 \times C,計算公式是:
在這裏插入圖片描述
普通的卷積操作只把卷積核作用於局部的輸入特徵塊,Squeeze操作對特徵進行逐通道全局平均池化操作後,就使得輸出特徵不只是受到局部輸入塊的影響,而是能夠受到空間全局特徵的影響。作者在論文中對比了全局平均池化和全局最大池化,都可以取得較好的正向分類和檢測效果,只不過全局平均池化的效果更好一些,當然也可以其他空間聚合手段。

Excitation:
Excitation對輸入的1×1×C1 \times 1 \times C的特徵進行跨通道的相互影響,論文中的做法是使用了兩個全連接層。第一個全連接層使用了bottleneck的操作,輸入通道數爲C,輸出通道數爲Cr\frac{C}{r}rr是超參數,論文中設置爲16。第一個全連接層之後使用Relu激活函數。第二個全連接層輸入通道數爲Cr\frac{C}{r},輸出通道數爲C,把特徵的通道數再變換回去。最後使用Sigmoid激活函數,得到1×1×C1 \times 1 \times C的輸出。

Excitation的具體處理過程爲:
在這裏插入圖片描述
最終將Excitation輸出和SE Block的輸入URH×W×HU \in R^{H \times W \times H}相乘得到最終變換後的輸出。

小結
SE Block的作用是對不同的通道應用不同的權重係數,使得模型對更加關注信息量大的channel,這也可以看作是一種attention機制。

SE Block增加的參數數量很有限,具體增加的參數數量爲:
2rs=1SNsCs2\frac{2}{r}\sum_{s=1}^{S}N_s C_s^2
論文實驗中rr取16,但作者也提到可以對不同層的SE Block應用不同大小的rr

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
發佈了133 篇原創文章 · 獲贊 68 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章