SENet,ResNet自學

已經有很多工作在空間維度上來提升網絡的性能。那麼很自然想到,網絡是否可以從其他層面來考慮去提升性能,比如考慮特徵通道之間的關係?我們的工作就是基於這一點並提出了 Squeeze-and-Excitation Networks(簡稱 SENet)。在我們提出的結構中,Squeeze 和 Excitation 是兩個非常關鍵的操作,所以我們以此來命名。我們的動機是希望顯式地建模特徵通道之間的相互依賴關係。
具體來說,就是通過學習的方式來自動獲取到每個特徵通道的重要程度,然後依照這個重要程度去提升有用的特徵並抑制對當前任務用處不大的特徵。
在這裏插入圖片描述
給定一個輸入 x,其特徵通道數爲 c_1,通過一系列卷積等一般變換後得到一個特徵通道數爲 c_2 的特徵。與傳統的 CNN 不一樣的是,接下來我們通過三個操作來重標定前面得到的特徵。
首先是 Squeeze 操作,我們順着空間維度來進行特徵壓縮,將每個二維的特徵通道變成一個實數,這個實數某種程度上具有全局的感受野,並且輸出的維度和輸入的特徵通道數相匹配。它表徵着在特徵通道上響應的全局分佈,而且使得靠近輸入的層也可以獲得全局的感受野。
其次是 Excitation 操作,它是一個類似於循環神經網絡中門的機制。通過參數 w 來爲每個特徵通道生成權重,其中參數 w 被學習用來顯式地建模特徵通道間的相關性。
最後是一個 Reweight 的操作,我們將 Excitation 的輸出的權重看做是進過特徵選擇後的每個特徵通道的重要性,然後通過乘法逐通道加權到先前的特徵上,完成在通道維度上的對原始特徵的重標定。
首先Ftr這一步是轉換操作(嚴格講並不屬於SENet,而是屬於原網絡,可以看後面SENet和Inception及ResNet網絡的結合),在文中就是一個標準的卷積操作而已
在這裏插入圖片描述
那麼這個Ftr的公式就是下面的公式(卷積操作,vc表示第c個卷積核,xs表示第s個輸入,vcs 代表一個3D卷積核),在這裏插入圖片描述
*表示卷積操作
以卷積爲例,卷積核爲
在這裏插入圖片描述
,其中vc 表示第c個卷積核。那麼輸出 在這裏插入圖片描述

Ftr得到的U就是Figure1中的左邊第二個三維矩陣,也叫tensor,或者叫C個大小爲H*W的feature map。而uc表示U中第c個二維矩陣,下標c表示channel

接下來就是Squeeze操作
在這裏插入圖片描述
因此公式2就將HWC的輸入轉換成11C的輸出,對應Figure1中的Fsq操作。爲什麼會有這一步呢?這一步的結果相當於表明該層C個feature map的數值分佈情況,或者叫全局信息。

再接下來就是Excitation操作,如公式3。直接看最後一個等號,前面squeeze得到的結果是z,這裏先用W1乘以z,就是一個全連接層操作,W1的維度是C/r * C,這個r是一個縮放參數,在文中取的是16,這個參數的目的是爲了減少channel個數從而降低計算量。又因爲z的維度是11C,所以W1z的結果就是11C/r;然後再經過一個ReLU層,輸出的維度不變;然後再和W2相乘,和W2相乘也是一個全連接層的過程,W2的維度是CC/r,因此輸出的維度就是11C;最後再經過sigmoid函數,得到s。
在這裏插入圖片描述
也就是說最後得到的這個s的維度是1
1*C,C表示channel數目。這個s其實是本文的核心,它是用來刻畫tensor U中C個feature map的權重。而且這個權重是通過前面這些全連接層和非線性層學習得到的,因此可以end-to-end訓練。這兩個全連接層的作用就是融合各通道的feature map信息,因爲前面的squeeze都是在某個channel的feature map裏面操作。

在得到s之後,就可以對原來的tensor U操作了,就是下面的公式4。也很簡單,就是channel-wise multiplication,什麼意思呢?uc是一個二維矩陣,sc是一個數,也就是權重,因此相當於把uc矩陣中的每個值都乘以sc。對應Figure1中的Fscale。
在這裏插入圖片描述
基於pytorch實現

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

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章