【論文筆記】張航和李沐等提出:ResNeSt: Split-Attention Networks(ResNet改進版本)

github地址:https://github.com/zhanghang1989/ResNeStgit

論文地址:https://hangzhang.org/files/resnest.pdf github

 

核心就是:Split-attention blocks網絡

先看一組圖:架構

ResNeSt在圖像分類上中ImageNet數據集上超越了其前輩ResNet、ResNeXt、SENet以及EfficientNet。使用ResNeSt-50爲基本骨架的Faster-RCNN比使用ResNet-50的mAP要高出3.08%。使用ResNeSt-50爲基本骨架的DeeplabV3比使用ResNet-50的mIOU要高出3.02%。漲點效果很是明顯。ide

一、提出的動機性能

他們認爲像ResNet等一些基礎卷積神經網絡是針對於圖像分類而設計的。因爲有限的感覺野大小以及缺少跨通道之間的相互做用,這些網絡可能不適合於其它的一些領域像目標檢測、圖像分割等。這意味着要提升給定計算機視覺任務的性能,須要「網絡手術」來修改ResNet,以使其對特定任務更加有效。 例如,某些方法添加了金字塔模塊[8,69]或引入了遠程鏈接[56]或使用跨通道特徵圖注意力[15,65]。 雖然這些方法確實能夠提升某些任務的學習性能,但由此而提出了一個問題:咱們是否能夠建立具備通用改進功能表示的通用骨幹網,從而同時提升跨多個任務的性能?跨通道信息在下游應用中已被成功使用 [56,64,65],而最近的圖像分類網絡更多地關注組或深度卷積[27,28,54,60]。 儘管它們在分類任務中具備出色的計算能力和準確性,可是這些模型沒法很好地轉移到其餘任務,由於它們的孤立表示沒法捕獲跨通道之間的關係[2七、28]。所以,具備跨通道表示的網絡是值得作的。學習

二、本文的貢獻點spa

第一個貢獻點:提出了split-attention blocks構造的ResNeSt,與現有的ResNet變體相比,不須要增長額外的計算量。並且ResNeSt能夠做爲其它任務的骨架。設計

第二個貢獻點:圖像分類和遷移學習應用的大規模基準。 利用ResNeSt主幹的模型可以在幾個任務上達到最早進的性能,即:圖像分類,對象檢測,實例分割和語義分割。 與經過神經架構搜索生成的最新CNN模型[55]相比,所提出的ResNeSt性能優於全部現有ResNet變體,而且具備相同的計算效率,甚至能夠實現更好的速度精度折衷。單個Cascade-RCNN [3]使用ResNeSt-101主幹的模型在MS-COCO實例分割上實現了48.3%的box mAP和41.56%的mask mAP。 單個DeepLabV3 [7]模型一樣使用ResNeSt-101主幹,在ADE20K場景分析驗證集上的mIoU達到46.9%,比之前的最佳結果高出1%mIoU以上。3d

三、相關工做就不介紹了

四、Split-Attention網絡

直接看ResNeSt block:

首先是借鑑了ResNeXt網絡的思想,將輸入分爲K個,每個記爲Cardinal1-k ,而後又將每一個Cardinal拆分紅R個,每個記爲Split1-r,因此總共有G=KR個組。

而後是對於每個Cardinal中具體是什麼樣的:

這裏借鑑了squeeze-and-excitation network(SENet) 中的思想,也就是基於通道的注意力機制,對通道賦予不一樣的權重以建模通道的重要程度。

對於每個Cardinal輸入是:

通道權重統計量能夠經過全局平均池化得到:

用Vk表示攜帶了通道權重後的Cardinal輸出:

那麼最終每一個Cardinal的輸出就是:

而其中的是通過了softmax以後計算所得的權重:

若是R=1的話就是對該Cardinal中的全部通道視爲一個總體。

接着將每個Cardinal的輸出拼接起來:

假設每一個ResNeSt block的輸出是Y,那麼就有:

其中T表示的是跳躍鏈接映射。這樣的形式就和ResNet中的殘差塊輸出計算就一致了。

五、殘差網絡存在的問題 

(1)殘差網絡使用帶步長的卷積,好比3×3卷積來減小圖像的空間維度,這樣會損失掉不少空間信息。對於像目標檢測和分割領域,空間信息是相當重要的。並且卷積層通常使用0來填充圖像邊界,這在遷移到密集預測的其它問題時也不是最佳選擇。所以本文使用的是核大小爲3×3的平均池化來減小空間維度

(2)

  • 將殘差網絡中的7×7卷積用3個3×3的卷積代替,擁有一樣的感覺野。
  • 將跳躍鏈接中的步長爲2的1×1卷積用2×2的平均池化代替。

六、訓練策略

這裏就簡單地列下,相關細節能夠去看論文。

(1)大的min batch,使用cosine學習率衰減策略。warm up。BN層參數設置。

(2)標籤平滑

(3)自動加強

(4)mixup訓練

(5)大的切割設置

(6)正則化

六、相關結果 

附錄中還有一些結果,就再也不貼了。 

最後是split attention block的實現代碼,能夠結合看一看:

import torch
from torch import nn
import torch.nn.functional as F
from torch.nn import Conv2d, Module, Linear, BatchNorm2d, ReLU
from torch.nn.modules.utils import _pair

__all__ = ['SKConv2d']

class DropBlock2D(object):
    def __init__(self, *args, **kwargs):
        raise NotImplementedError

class SplAtConv2d(Module):
    """Split-Attention Conv2d
    """
    def __init__(self, in_channels, channels, kernel_size, stride=(1, 1), padding=(0, 0),
                 dilation=(1, 1), groups=1, bias=True,
                 radix=2, reduction_factor=4,
                 rectify=False, rectify_avg=False, norm_layer=None,
                 dropblock_prob=0.0, **kwargs):
        super(SplAtConv2d, self).__init__()
        padding = _pair(padding)
        self.rectify = rectify and (padding[0] > 0 or padding[1] > 0)
        self.rectify_avg = rectify_avg
        inter_channels = max(in_channels*radix//reduction_factor, 32)
        self.radix = radix
        self.cardinality = groups
        self.channels = channels
        self.dropblock_prob = dropblock_prob
        if self.rectify:
            from rfconv import RFConv2d
            self.conv = RFConv2d(in_channels, channels*radix, kernel_size, stride, padding, dilation,
                                 groups=groups*radix, bias=bias, average_mode=rectify_avg, **kwargs)
        else:
            self.conv = Conv2d(in_channels, channels*radix, kernel_size, stride, padding, dilation,
                               groups=groups*radix, bias=bias, **kwargs)
        self.use_bn = norm_layer is not None
        self.bn0 = norm_layer(channels*radix)
        self.relu = ReLU(inplace=True)
        self.fc1 = Conv2d(channels, inter_channels, 1, groups=self.cardinality)
        self.bn1 = norm_layer(inter_channels)
        self.fc2 = Conv2d(inter_channels, channels*radix, 1, groups=self.cardinality)
        if dropblock_prob > 0.0:
            self.dropblock = DropBlock2D(dropblock_prob, 3)

    def forward(self, x):
        x = self.conv(x)
        if self.use_bn:
            x = self.bn0(x)
        if self.dropblock_prob > 0.0:
            x = self.dropblock(x)
        x = self.relu(x)

        batch, channel = x.shape[:2]
        if self.radix > 1:
            splited = torch.split(x, channel//self.radix, dim=1)
            gap = sum(splited) 
        else:
            gap = x
        gap = F.adaptive_avg_pool2d(gap, 1)
        gap = self.fc1(gap)

        if self.use_bn:
            gap = self.bn1(gap)
        gap = self.relu(gap)

        atten = self.fc2(gap).view((batch, self.radix, self.channels))
        if self.radix > 1:
            atten = F.softmax(atten, dim=1).view(batch, -1, 1, 1)
        else:
            atten = F.sigmoid(atten, dim=1).view(batch, -1, 1, 1)

        if self.radix > 1:
            atten = torch.split(atten, channel//self.radix, dim=1)
            out = sum([att*split for (att, split) in zip(atten, splited)])
        else:
            out = atten * x
        return out.contiguous()

 

若有錯誤,歡迎指出。

相關文章
相關標籤/搜索