日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

作者丨皮特潘

編輯丨極市平臺

前言

本文盤點一些CNN網絡中設計比較精巧而又實用的“插件”。所謂“插件”,就是不改變網絡主體結構, 可以很容易嵌入到主流網絡當中,提高網絡提取特征的能力,能夠做到plug-and-play。網絡也有很多類似盤點工作,都宣稱所謂的即插即用、無痛漲點。不過根據筆者經驗和收集,發現很多插件都是不實用、不通用、甚至不work的,于是有了這一篇。

首先,我的認識是:既然是“插件”,就要是錦上添花的,又容易植入,容易落地的,真正的即插即用。本文盤點的“插件”,在很多SOTA網絡中會看到它們的影子。是值得推廣的良心“插件”,真正能做到plug-and-play。總之一句話,就是能夠work的“插件”。很多“插件”都為提升CNN能力而推出的,例如平移、旋轉、scale等變性能力,多尺度特征提取能力,感受野等能力,感知空間位置能力等等。

入圍名單:STN、ASPP、Non-local、SE、CBAM、DCNv1&v2、CoordConv、Ghost、BlurPool、RFB、ASFF

STN

出自論文:Spatial Transformer Networks

論文鏈接
https://arxiv.org/pdf/1506.02025.pdf

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

在OCR等任務中,你會經常看到它的身影。對于CNN網絡,我們希望其具有對物體的姿態、位置等有一定的不變性。即在測試集上可以適應一定的姿態、位置的變化。不變性或等變性可以有效提高模型泛化能力。雖然CNN使用sliding-window卷積操作,在一定程度上具有平移不變性。但很多研究發現,下采樣會破壞網絡的平移不變性。所以可以認為網絡的不變性能力非常弱,更不用說旋轉、尺度、光照等不變性。一般我們利用數據增強來實現網絡的“不變性”。

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

本文提出STN模塊,顯式將空間變換植入到網絡當中,進而提高網絡的旋轉、平移、尺度等不變性。可以理解為“對齊”操作。STN的結構如上圖所示,每一個STN模塊由Localisation net,Grid generator和Sampler三部分組成。Localisation net用于學習獲取空間變換的參數,就是上式中的  六個參數。Grid generator用于坐標映射。Sampler用于像素的采集,是利用雙線性插值的方式進行。

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

STN的意義是能夠把原始的圖像糾正成為網絡想要的理想圖像,并且該過程為無監督的方式進行,也就是變換參數是自發學習獲取的,不需要標注信息。該模塊是一個獨立模塊,可以在CNN的任何位置插入。符合本次“插件”的盤點要求。

核心代碼:

class SpatialTransformer(nn.Module):
    def __init__(self, spatial_dims):
        super(SpatialTransformer, self).__init__()
        self._h, self._w = spatial_dims 
        self.fc1 = nn.Linear(32*4*4, 1024) # 可根據自己的網絡參數具體設置
        self.fc2 = nn.Linear(1024, 6)

    def forward(self, x): 
        batch_images = x #保存一份原始數據
        x = x.view(-1, 32*4*4)
        # 利用FC結構學習到6個參數
        x = self.fc1(x)
        x = self.fc2(x) 
        x = x.view(-1, 2,3) # 2x3
        # 利用affine_grid生成采樣點
        affine_grid_points = F.affine_grid(x, torch.Size((x.size(0), self._in_ch, self._h, self._w)))
        # 將采樣點作用到原始數據上
        rois = F.grid_sample(batch_images, affine_grid_points)
        return rois, affine_grid_points

ASPP

插件全稱:atrous spatial pyramid pooling

出自論文:DeepLab: Semantic Image Segmentation with Deep Convolutional Nets, Atrous Conv

論文鏈接
https://arxiv.org/pdf/1606.00915.pdf

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

本插件是帶有空洞卷積的空間金字塔池化模塊,主要是為了提高網絡的感受野,并引入多尺度信息而提出的。我們知道,對于語義分割網絡,通常面臨是分辨率較大的圖片,這就要求我們的網絡有足夠的感受野來覆蓋到目標物體。對于CNN網絡基本是靠卷積層的堆疊加上下采樣操作來獲取感受野的。本文的該模塊可以在不改變特征圖大小的同時控制感受野,這有利于提取多尺度信息。其中rate控制著感受野的大小,r越大感受野越大。

ASPP主要包含以下幾個部分:1. 一個全局平均池化層得到image-level特征,并進行1X1卷積,并雙線性插值到原始大小;2. 一個1X1卷積層,以及三個3X3的空洞卷積;3. 將5個不同尺度的特征在channel維度concat在一起,然后送入1X1的卷積進行融合輸出。

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心代碼

class ASPP(nn.Module):
    def __init__(self, in_channel=512, depth=256):
        super(ASPP,self).__init__()
        self.mean = nn.AdaptiveAvgPool2d((1, 1))
        self.conv = nn.Conv2d(in_channel, depth, 1, 1)
        self.atrous_block1 = nn.Conv2d(in_channel, depth, 1, 1)
        # 不同空洞率的卷積
        self.atrous_block6 = nn.Conv2d(in_channel, depth, 3, 1, padding=6, dilation=6)
        self.atrous_block12 = nn.Conv2d(in_channel, depth, 3, 1, padding=12, dilation=12)
        self.atrous_block18 = nn.Conv2d(in_channel, depth, 3, 1, padding=18, dilation=18)
        self.conv_1x1_output = nn.Conv2d(depth * 5, depth, 1, 1)

    def forward(self, x):
        size = x.shape[2:]
        # 池化分支
        image_features = self.mean(x)
        image_features = self.conv(image_features)
        image_features = F.upsample(image_features, size=size, mode='bilinear')
        # 不同空洞率的卷積
        atrous_block1 = self.atrous_block1(x)
        atrous_block6 = self.atrous_block6(x)
        atrous_block12 = self.atrous_block12(x)
        atrous_block18 = self.atrous_block18(x)
        # 匯合所有尺度的特征
        x = torch.cat([image_features, atrous_block1, atrous_block6,atrous_block12, atrous_block18], dim=1)
        # 利用1X1卷積融合特征輸出
        x = self.conv_1x1_output(x)
        return x

Non-local

出自論文:Non-local Neural Networks

論文鏈接
https://arxiv.org/abs/1711.07971

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

Non-Local是一種attention機制,也是一個易于植入和集成的模塊。Local主要是針對感受野(receptive field)來說的,以CNN中的卷積操作和池化操作為例,它的感受野大小就是卷積核大小,而我們常用3X3的卷積層進行堆疊,它只考慮局部區域,都是local的運算。不同的是,non-local操作感受野可以很大,可以是全局區域,而不是一個局部區域。捕獲長距離依賴(long-range dependencies),即如何建立圖像上兩個有一定距離的像素之間的聯系,是一種注意力機制。所謂注意力機制就是利用網絡生成saliency map,注意力對應的是顯著性區域,是需要網絡重點關注的區域。

  • 首先分別對輸入的特征圖進行 1X1的卷積來壓縮通道數,得到  特征。
  • 通過reshape操作,轉化三個特征的維度,然后對  進行矩陣乘操作,得到類似協方差矩陣, 這一步為了計算出特征中的自相關性,即得到每幀中每個像素對其他所有幀所有像素的關系。
  • 然后對自相關特征進行 Softmax 操作,得到0~1的weights,這里就是我們需要的 Self-attention系數。
  • 最后將 attention系數,對應乘回特征矩陣g上,與原輸入 feature map X 殘差相加輸出即可。

這里我們結合一個簡單例子理解一下,假設g為(我們暫時不考慮batch和channel維度):

g = torch.tensor([[1, 2],
                  [3, 4]).view(-1, 1).float()

 為:

theta = torch.tensor([2, 4, 6, 8]).view(-1, 1)

 為:

phi = torch.tensor([7, 5, 3, 1]).view(1, -1)

那么,  和  矩陣相乘如下:

tensor([[14., 10.,  6.,  2.],
        [28., 20., 12.,  4.],
        [42., 30., 18.,  6.],
        [56., 40., 24.,  8.]])

進過softmax(dim=-1)后如下,每一行代表著g里面的元素的重要程度,每一行前面的值比較大,因此希望多“注意”到g前面的元素,也就是1比較重要一點。或者這樣理解:注意力矩陣代表著g中每個元素和其他元素的依賴程度。

tensor([[9.8168e-01, 1.7980e-02, 3.2932e-04, 6.0317e-06],
        [9.9966e-01, 3.3535e-04, 1.1250e-07, 3.7739e-11],
        [9.9999e-01, 6.1442e-06, 3.7751e-11, 2.3195e-16],
        [1.0000e+00, 1.1254e-07, 1.2664e-14, 1.4252e-21]])

注意力作用上之后,整體值向原始g中的值都向1靠攏:

tensor([[1.0187, 1.0003],
        [1.0000, 1.0000]])

核心代碼

class NonLocal(nn.Module):
    def __init__(self, channel):
        super(NonLocalBlock, self).__init__()
        self.inter_channel = channel // 2
        self.conv_phi = nn.Conv2d(channel, self.inter_channel, 1, 1,0, False)
        self.conv_theta = nn.Conv2d(channel, self.inter_channel, 1, 1,0, False)
        self.conv_g = nn.Conv2d(channel, self.inter_channel, 1, 1, 0, False)
        self.softmax = nn.Softmax(dim=1)
        self.conv_mask = nn.Conv2d(self.inter_channel, channel, 1, 1, 0, False)

    def forward(self, x):
        # [N, C, H , W]
        b, c, h, w = x.size()
        # 獲取phi特征,維度為[N, C/2, H * W],注意是要保留batch和通道維度的,是在HW上進行的
        x_phi = self.conv_phi(x).view(b, c, -1)
        # 獲取theta特征,維度為[N, H * W, C/2]
        x_theta = self.conv_theta(x).view(b, c, -1).permute(0, 2, 1).contiguous()
        # 獲取g特征,維度為[N, H * W, C/2]
        x_g = self.conv_g(x).view(b, c, -1).permute(0, 2, 1).contiguous()
        # 對phi和theta進行矩陣乘,[N, H * W, H * W]
        mul_theta_phi = torch.matmul(x_theta, x_phi)
        # softmax拉到0~1之間
        mul_theta_phi = self.softmax(mul_theta_phi)
        # 與g特征進行矩陣乘運算,[N, H * W, C/2]
        mul_theta_phi_g = torch.matmul(mul_theta_phi, x_g)
        # [N, C/2, H, W]
        mul_theta_phi_g = mul_theta_phi_g.permute(0, 2, 1).contiguous().view(b, self.inter_channel, h, w)
        # 1X1卷積擴充通道數
        mask = self.conv_mask(mul_theta_phi_g)
        out = mask + x # 殘差連接
        return out

SE

出自論文:Squeeze-and-Excitation Networks

論文鏈接
https://arxiv.org/pdf/1709.01507.pdf

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

本文是ImageNet最后一屆比賽的冠軍作品,你會在很多經典網絡結構中看到它的身影,例如Mobilenet v3。其實是一種通道注意力機制。由于特征壓縮和FC的存在,其捕獲的通道注意力特征是具有全局信息的。本文提出了一種新的結構單元——“Squeeze-and Excitation(SE)”模塊,可以自適應的調整各通道的特征響應值,對通道間的內部依賴關系進行建模。有以下幾個步驟:

  • Squeeze: 沿著空間維度進行特征壓縮,將每個二維的特征通道變成一個數,是具有全局的感受野。
  • Excitation: 每個特征通道生成一個權重,用來代表該特征通道的重要程度。
  • Reweight:將Excitation輸出的權重看做每個特征通道的重要性,通過相乘的方式作用于每一個通道上。

核心代碼

class SE_Block(nn.Module):
    def __init__(self, ch_in, reduction=16):
        super(SE_Block, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)  # 全局自適應池化
        self.fc = nn.Sequential(
            nn.Linear(ch_in, ch_in // reduction, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(ch_in // reduction, ch_in, bias=False),
            nn.Sigmoid()
        )

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x).view(b, c) # squeeze操作
        y = self.fc(y).view(b, c, 1, 1) # FC獲取通道注意力權重,是具有全局信息的
        return x * y.expand_as(x) # 注意力作用每一個通道上

CBAM

出自論文:CBAM: Convolutional Block Attention Module

論文鏈接
https://openaccess.thecvf.com/content_ECCV_2018/papers/Sanghyun_Woo_Convolutional_Block_Attention_ECCV_2018_paper.pdf

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 


真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

SENet在feature map的通道上進行attention權重獲取,然后與原來的feature map相乘。這篇文章指出,該種attention方法法只關注了通道層面上哪些層會具有更強的反饋能力,但是在空間維度上并不能體現出attention。CBAM作為本文的亮點,將attention同時運用在channel和spatial兩個維度上, CBAM與SE Module一樣,可以嵌入在大部分的主流網絡中,在不顯著增加計算量和參數量的前提下能提升模型的特征提取能力。

通道注意力: 如上圖所示,輸入是一個 H×W×C 的特征F,我們先分別進兩個空間的全局平均池化和最大池化得到 兩個 1×1×C 的通道描述。再將它們分別送進一個兩層的神經網絡,第一層神經元個數為 C/r,激活函數為 Relu,第二層神經元個數為 C。注意,這個兩層的神經網絡是共享的。 然后,再將得到的兩個特征相加后經過一個 Sigmoid 激活函數得到權重系數 Mc。最后,拿權重系數和 原來的特征 F 相乘即可得到縮放后的新特征。 偽代碼:

def forward(self, x):
    # 利用FC獲取全局信息,和Non-local的矩陣相乘本質上式一樣的
    avg_out = self.fc2(self.relu1(self.fc1(self.avg_pool(x))))
    max_out = self.fc2(self.relu1(self.fc1(self.max_pool(x))))
    out = avg_out + max_out
    return self.sigmoid(out)

空間注意力: 與通道注意力相似,給定一個 H×W×C 的特征 F‘,我們先分別進行一個通道維度的平均池化和最大池化得到兩個 H×W×1 的通道描述,并將這兩個描述按照通道拼接在一起。然后,經過一個 7×7 的卷積層, 激活函數為 Sigmoid,得到權重系數 Ms。最后,拿權重系數和特征 F’ 相乘即可得到縮放后的新特征。 偽代碼:

def forward(self, x):
    # 這里利用池化獲取全局信息
    avg_out = torch.mean(x, dim=1, keepdim=True)
    max_out, _ = torch.max(x, dim=1, keepdim=True)
    x = torch.cat([avg_out, max_out], dim=1)
    x = self.conv1(x)
    return self.sigmoid(x)

DCN v1&v2

插件全稱:Deformable Convolutional

出自論文

v1: [Deformable Convolutional Networks]

https://arxiv.org/pdf/1703.06211.pdf

v2: [Deformable ConvNets v2: More Deformable, Better Results]

https://arxiv.org/pdf/1811.11168.pdf

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

變形卷積可以看作變形+卷積兩個部分,因此可以當作插件使用。在各大主流檢測網絡中,變形卷積真是漲點神器,網上解讀也非常之多。和傳統的固定窗口的卷積相比,變形卷積可以有效地對幾何圖形,因為它的“局部感受野”是可學習的,面向全圖的。這篇論文同時提出了deformable ROI pooling,這兩個方法都是增加額外偏移量的空間采樣位置,不需要額外的監督,是自監督的過程。

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

如上圖所示,a為不同的卷積,b為變形卷積,深色的點為卷積核實際采樣的位置,和“標準的”位置有一定的偏移。c和d為變形卷積的特殊形式,其中c為我們常見到的空洞卷積,d為具有學習旋轉特性的卷積,也具備提升感受野的能力。

變形卷積和STN過程非常類似,STN是利用網絡學習出空間變換的6個參數,對特征圖進行整體變換,旨在增加網絡對形變的提取能力。DCN是利用網絡學習數整圖offset,比STN的變形更“全面一點”。STN是仿射變換,DCN是任意變換。公式不貼了,可以直接看代碼實現過程。

變形卷積具有V1和V2兩個版本,其中V2是在V2的基礎上進行改進,除了采樣offset,還增加了采樣權重。V2認為3X3采樣點也應該具有不同的重要程度,因此該處理方法更具有靈活性和擬合能力。

核心代碼

def forward(self, x):
    # 學習出offset,包括x和y兩個方向,注意是每一個channel中的每一個像素都有一個x和y的offset
    offset = self.p_conv(x)
    if self.v2: # V2的時候還會額外學習一個權重系數,經過sigmoid拉到0和1之間
        m = torch.sigmoid(self.m_conv(x))
    # 利用offset對x進行插值,獲取偏移后的x_offset
    x_offset = self.interpolate(x,offset)
    if self.v2: # V2的時候,將權重系數作用到特征圖上
        m = m.contiguous().permute(0, 2, 3, 1)
        m = m.unsqueeze(dim=1)
        m = torch.cat([m for _ in range(x_offset.size(1))], dim=1)
        x_offset *= m
    out = self.conv(x_offset) # offset作用后,在進行標準的卷積過程
    return out

CoordConv

出自論文:An intriguing failing of convolutional neural networks and the CoordConv solution

論文鏈接
https://arxiv.org/pdf/1807.03247.pdf

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

在Solo語義分割算法和Yolov5中你可以看到它的身影。本文從幾個小實驗為出發點,探究了卷積網絡在坐標變換上的能力。就是它無法將空間表示轉換成笛卡爾空間中的坐標。如下圖所示,我們向一個網絡中輸入(i, j)坐標,要求它輸出一個64×64的圖像,并在坐標處畫一個正方形或者一個像素,然而網絡在測試集上卻無法完成。雖然這項任務是我們人類認為極其簡單的工作。分析原因是卷積作為一種局部的、共享權重的過濾器應用到輸入上時,它是不知道每個過濾器在哪,無法捕捉位置信息的。因此我們可以幫助卷積,讓它知道過濾器的位置。僅僅需要在輸入上添加兩個通道,一個是i坐標,另一個是j坐標。具體做法如上圖所示,送入濾波器之前增加兩個通道。這樣,網絡就具備了空間位置信息的能力,是不是很神奇?你可以隨機在分類、分割、檢測等任務中使用這種掛件。

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

如上面第一組圖片,傳統的CNN在根據坐標數值生成圖像的任務中,訓練集很好,測試集一團糟。第二組圖片增加了 CoordConv 之后可以輕松完成該任務,可見其增加了CNN空間感知的能力。

核心代碼

ins_feat = x # 當前實例特征tensor
# 生成從-1到1的線性值
x_range = torch.linspace(-1, 1, ins_feat.shape[-1], device=ins_feat.device)
y_range = torch.linspace(-1, 1, ins_feat.shape[-2], device=ins_feat.device)
y, x = torch.meshgrid(y_range, x_range) # 生成二維坐標網格
y = y.expand([ins_feat.shape[0], 1, -1, -1]) # 擴充到和ins_feat相同維度
x = x.expand([ins_feat.shape[0], 1, -1, -1])
coord_feat = torch.cat([x, y], 1) # 位置特征
ins_feat = torch.cat([ins_feat, coord_feat], 1) # concatnate一起作為下一個卷積的輸入

Ghost

插件全稱:Ghost module

出自論文:GhostNet: More Features from Cheap Operations

論文鏈接
https://arxiv.org/pdf/1911.11907.pdf

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

在ImageNet的分類任務上,GhostNet在相似計算量情況下Top-1正確率達75.7%,高于MobileNetV3的75.2%。其主要創新點就是提出了Ghost 模塊。在CNN模型中,特征圖是存在大量的冗余,當然這也是非常重要和有必要的。如下圖所示,其中標“小扳手”的特征圖都存在冗余的特征圖。那么能否降低卷積的通道數,然后利用某種變換生成冗余的特征圖?事實上這就是GhostNet的思路。

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

而本文就從特征圖冗余問題出發,提出一個僅通過少量計算(論文稱為cheap operations)就能生成大量特征圖的結構——Ghost Module。而cheap operations就是線性變換,論文中采用卷積操作實現。具體過程如下:

  • 使用比原始更少量卷積運算,比如正常用64個卷積核,這里就用32個,減少一半的計算量。
  • 利用深度分離卷積,從上面生成的特征圖中變換出冗余的特征。
  • 上面兩步獲取的特征圖concat起來輸出,送入后續的環節。

核心代碼

class GhostModule(nn.Module):
    def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True):
        super(GhostModule, self).__init__()
        self.oup = oup
        init_channels = math.ceil(oup / ratio)
        new_channels = init_channels*(ratio-1)

        self.primary_conv = nn.Sequential(
            nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size//2, bias=False),
            nn.BatchNorm2d(init_channels),
            nn.ReLU(inplace=True) if relu else nn.Sequential(), )
    # cheap操作,注意利用了分組卷積進行通道分離
        self.cheap_operation = nn.Sequential(
            nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size//2, groups=init_channels, bias=False),
            nn.BatchNorm2d(new_channels),
            nn.ReLU(inplace=True) if relu else nn.Sequential(),)

    def forward(self, x):
        x1 = self.primary_conv(x)  #主要的卷積操作
        x2 = self.cheap_operation(x1) # cheap變換操作
        out = torch.cat([x1,x2], dim=1) # 二者cat到一起
        return out[:,:self.oup,:,:]

BlurPool

出自論文:Making Convolutional Networks Shift-Invariant Again

論文鏈接
https://arxiv.org/abs/1904.11486

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

我們都知道,基于滑動窗口的卷積操作是具有平移不變性的,因此也默認為CNN網絡具有平移不變性或等變性,事實上真的如此嗎?實踐發現,CNN網絡真的非常敏感,只要輸入圖片稍微改一個像素,或者平移一個像素,CNN的輸出就會發生巨大的變化,甚至預測錯誤。這可是非常不具有魯棒性的。一般情況下我們利用數據增強獲取所謂的不變性。本文研究發現,不變性的退化根本原因就在于下采樣,無論是Max Pool還是Average Pool,抑或是stride>1的卷積操作,只要是涉及步長大于1的下采樣,均會導致平移不變性的丟失。具體示例如下圖所示,僅僅平移一個像素,Max pool的結果就差距很大。

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

為了保持平移不變性,可以在下采樣之前進行低通濾波。傳統的max pool可以分解為兩部分,分別是stride = 1的max + 下采樣 。因此作者提出的MaxBlurPool = max + blur + 下采樣來替代原始的max pool。實驗發現,該操作雖然不能徹底解決平移不變性的丟失,但是可以很大程度上緩解。

核心代碼

class BlurPool(nn.Module):
    def __init__(self, channels, pad_type='reflect', filt_size=4, stride=2, pad_off=0):
        super(BlurPool, self).__init__()
        self.filt_size = filt_size
        self.pad_off = pad_off
        self.pad_sizes = [int(1.*(filt_size-1)/2), int(np.ceil(1.*(filt_size-1)/2)), int(1.*(filt_size-1)/2), int(np.ceil(1.*(filt_size-1)/2))]
        self.pad_sizes = [pad_size+pad_off for pad_size in self.pad_sizes]
        self.stride = stride
        self.off = int((self.stride-1)/2.)
        self.channels = channels
        # 定義一系列的高斯核
        if(self.filt_size==1):
            a = np.array([1.,])
        elif(self.filt_size==2):
            a = np.array([1., 1.])
        elif(self.filt_size==3):
            a = np.array([1., 2., 1.])
        elif(self.filt_size==4):    
            a = np.array([1., 3., 3., 1.])
        elif(self.filt_size==5):    
            a = np.array([1., 4., 6., 4., 1.])
        elif(self.filt_size==6):    
            a = np.array([1., 5., 10., 10., 5., 1.])
        elif(self.filt_size==7):    
            a = np.array([1., 6., 15., 20., 15., 6., 1.])

        filt = torch.Tensor(a[:,None]*a[None,:])
        filt = filt/torch.sum(filt) # 歸一化操作,保證特征經過blur后信息總量不變
        # 非grad操作的參數利用buffer存儲
        self.register_buffer('filt', filt[None,None,:,:].repeat((self.channels,1,1,1)))
        self.pad = get_pad_layer(pad_type)(self.pad_sizes)

    def forward(self, inp):
        if(self.filt_size==1):
            if(self.pad_off==0):
                return inp[:,:,::self.stride,::self.stride]    
            else:
                return self.pad(inp)[:,:,::self.stride,::self.stride]
        else:
            # 利用固定參數的conv2d+stride實現blurpool
            return F.conv2d(self.pad(inp), self.filt, stride=self.stride, groups=inp.shape[1])

RFB

插件全稱:Receptive Field Block

出自論文:Receptive Field Block Net for Accurate and Fast Object Detection

論文鏈接
https://arxiv.org/abs/1711.07767

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

論文發現目標區域要盡量靠近感受野中心,這會有助于提升模型對小尺度空間位移的魯棒性。因此受人類視覺RF結構的啟發,本文提出了感受野模塊(RFB),加強了CNN模型學到的深層特征的能力,使檢測模型更加準確。RFB可以作為一種通用模塊嵌入到絕大多數網路當中。下圖可以看出其和inception、ASPP、DCN的區別,可以看作是inception+ASPP的結合。

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

具體實現如下圖,其實和ASPP類似,不過是使用了不同大小的卷積核作為空洞卷積的前置操作。

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心代碼

class RFB(nn.Module):
    def __init__(self, in_planes, out_planes, stride=1, scale = 0.1, visual = 1):
        super(RFB, self).__init__()
        self.scale = scale
        self.out_channels = out_planes
        inter_planes = in_planes // 8
        # 分支0:1X1卷積+3X3卷積
        self.branch0 = nn.Sequential(conv_bn_relu(in_planes, 2*inter_planes, 1, stride),
                conv_bn_relu(2*inter_planes, 2*inter_planes, 3, 1, visual, visual, False))
        # 分支1:1X1卷積+3X3卷積+空洞卷積
        self.branch1 = nn.Sequential(conv_bn_relu(in_planes, inter_planes, 1, 1),
                conv_bn_relu(inter_planes, 2*inter_planes, (3,3), stride, (1,1)),
                conv_bn_relu(2*inter_planes, 2*inter_planes, 3, 1, visual+1,visual+1,False))
        # 分支2:1X1卷積+3X3卷積*3代替5X5卷積+空洞卷積
        self.branch2 = nn.Sequential(conv_bn_relu(in_planes, inter_planes, 1, 1),
                conv_bn_relu(inter_planes, (inter_planes//2)*3, 3, 1, 1),
                conv_bn_relu((inter_planes//2)*3, 2*inter_planes, 3, stride, 1),
                conv_bn_relu(2*inter_planes, 2*inter_planes, 3, 1, 2*visual+1, 2*visual+1,False)  )
        self.ConvLinear = conv_bn_relu(6*inter_planes, out_planes, 1, 1, False)
        self.shortcut = conv_bn_relu(in_planes, out_planes, 1, stride, relu=False)
        self.relu = nn.ReLU(inplace=False)
    def forward(self,x):
        x0 = self.branch0(x)
        x1 = self.branch1(x)
        x2 = self.branch2(x)
        # 尺度融合
        out = torch.cat((x0,x1,x2),1)
        # 1X1卷積
        out = self.ConvLinear(out)
        short = self.shortcut(x)
        out = out*self.scale + short
        out = self.relu(out)
        return out

ASFF

插件全稱:Adaptively Spatial Feature Fusion

出自論文:Adaptively Spatial Feature Fusion Learning Spatial Fusion for Single-Shot Object Detection

論文鏈接
https://arxiv.org/abs/1911.09516v1

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

核心解析

為了更加充分的利用高層語義特征和底層細粒度特征,很多網絡都會采用FPN的方式輸出多層特征,但是它們都多用concat或者element-wise這種融合方式,本論文認為這樣不能充分利用不同尺度的特征,所以提出了Adaptively Spatial Feature Fusion,即自適應特征融合方式。FPN輸出的特征圖經過下面兩部分的處理:

Feature Resizing:特征圖的尺度不同無法進行element-wise融合,因此需要進行resize。對于上采樣:首先利用1X1卷積進行通道壓縮,然后利用插值的方法上采樣特征圖。對于1/2的下采樣:利用stride=2的3X3卷積同時進行通道壓縮和特征圖縮小。對于1/4的下采樣:在stride=2的3X3的卷積之前插入tride=2的maxpooling。

Adaptive Fusion:特征圖自適應融合,公式如下

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

其中x n→l表示在(i,j)位置的特征向量,來自n特征圖,經過上述resize到l尺度。Alpha。Beta,gamma為空間注意力權重,經過softmax處理,如下:

真正的即插即用!盤點11種CNN網絡設計中精巧通用的“小”插件

 

代碼解析

class ASFF(nn.Module):
    def __init__(self, level, rfb=False):
        super(ASFF, self).__init__()
        self.level = level
        # 輸入的三個特征層的channels, 根據實際修改
        self.dim = [512, 256, 256]
        self.inter_dim = self.dim[self.level]
        # 每個層級三者輸出通道數需要一致
        if level==0:
            self.stride_level_1 = conv_bn_relu(self.dim[1], self.inter_dim, 3, 2)
            self.stride_level_2 = conv_bn_relu(self.dim[2], self.inter_dim, 3, 2)
            self.expand = conv_bn_relu(self.inter_dim, 1024, 3, 1)
        elif level==1:
            self.compress_level_0 = conv_bn_relu(self.dim[0], self.inter_dim, 1, 1)
            self.stride_level_2 = conv_bn_relu(self.dim[2], self.inter_dim, 3, 2)
            self.expand = conv_bn_relu(self.inter_dim, 512, 3, 1)
        elif level==2:
            self.compress_level_0 = conv_bn_relu(self.dim[0], self.inter_dim, 1, 1)
            if self.dim[1] != self.dim[2]:
                self.compress_level_1 = conv_bn_relu(self.dim[1], self.inter_dim, 1, 1)
            self.expand = add_conv(self.inter_dim, 256, 3, 1)
        compress_c = 8 if rfb else 16  
        self.weight_level_0 = conv_bn_relu(self.inter_dim, compress_c, 1, 1)
        self.weight_level_1 = conv_bn_relu(self.inter_dim, compress_c, 1, 1)
        self.weight_level_2 = conv_bn_relu(self.inter_dim, compress_c, 1, 1)

        self.weight_levels = nn.Conv2d(compress_c*3, 3, 1, 1, 0)

    # 尺度大小 level_0 < level_1 < level_2
    def forward(self, x_level_0, x_level_1, x_level_2):
        # Feature Resizing過程
        if self.level==0:
            level_0_resized = x_level_0
            level_1_resized = self.stride_level_1(x_level_1)
            level_2_downsampled_inter =F.max_pool2d(x_level_2, 3, stride=2, padding=1)
            level_2_resized = self.stride_level_2(level_2_downsampled_inter)
        elif self.level==1:
            level_0_compressed = self.compress_level_0(x_level_0)
            level_0_resized =F.interpolate(level_0_compressed, 2, mode='nearest')
            level_1_resized =x_level_1
            level_2_resized =self.stride_level_2(x_level_2)
        elif self.level==2:
            level_0_compressed = self.compress_level_0(x_level_0)
            level_0_resized =F.interpolate(level_0_compressed, 4, mode='nearest')
            if self.dim[1] != self.dim[2]:
                level_1_compressed = self.compress_level_1(x_level_1)
                level_1_resized = F.interpolate(level_1_compressed, 2, mode='nearest')
            else:
                level_1_resized =F.interpolate(x_level_1, 2, mode='nearest')
            level_2_resized =x_level_2
        # 融合權重也是來自于網絡學習
        level_0_weight_v = self.weight_level_0(level_0_resized)
        level_1_weight_v = self.weight_level_1(level_1_resized)
        level_2_weight_v = self.weight_level_2(level_2_resized)
        levels_weight_v = torch.cat((level_0_weight_v, level_1_weight_v,
                                     level_2_weight_v),1)
        levels_weight = self.weight_levels(levels_weight_v)
        levels_weight = F.softmax(levels_weight, dim=1)   # alpha產生
        # 自適應融合
        fused_out_reduced = level_0_resized * levels_weight[:,0:1,:,:]+
                            level_1_resized * levels_weight[:,1:2,:,:]+
                            level_2_resized * levels_weight[:,2:,:,:]

        out = self.expand(fused_out_reduced)
        return out

結語

本文盤點了近年來比較精巧而又實用的CNN插件,希望大家活學活用,用在自己的實際項目中。

分享到:
標簽:設計 網絡 CNN
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定