<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          我卷我自己——cvpr2021:Involution

          共 8230字,需瀏覽 17分鐘

           ·

          2021-03-16 00:11

          引言

          本文重新回顧了常規(guī)卷積的設(shè)計(jì),其具有兩個(gè)重要性質(zhì),一個(gè)是空間無關(guān)性,比如3x3大小的卷積核是以滑窗的形式,滑過特征圖每一個(gè)像素(即我們所說的參數(shù)共享)。另外一個(gè)是頻域特殊性,體現(xiàn)在卷積核在每個(gè)通道上的權(quán)重是不同的

          我們對(duì)以上的設(shè)計(jì)原則進(jìn)行了"反轉(zhuǎn)",設(shè)計(jì)了一種 involution(內(nèi)卷???)的操作,一方面能降低模型的參數(shù)量,另一方面也能提升模型性能,還能和最近很火的自注意力機(jī)制聯(lián)系起來。該模塊在各大圖像任務(wù)上都有不錯(cuò)的性能提升。

          簡單回顧卷積

          最初的神經(jīng)網(wǎng)絡(luò)都是由一層層全連接層網(wǎng)絡(luò)疊加起來,對(duì)于簡單的任務(wù)來說參數(shù)量還好。但是對(duì)于圖像任務(wù),動(dòng)輒幾百上千的像素,則全連接層的參數(shù)量會(huì)十分巨大。如果是全連接層處理二維圖像,那么大致形式如下

          全連接層

          而卷積神經(jīng)網(wǎng)絡(luò)考慮了局部連接性,只考慮了局部的像素,從而讓參數(shù)量大大減少,形式如下

          卷積

          由于常規(guī)卷積核是對(duì)所有輸入通道進(jìn)行計(jì)算,在起初的一些低算力設(shè)備上計(jì)算損耗還是很大,Alexnet提出分組卷積,對(duì)輸入通道進(jìn)行分組,然后單獨(dú)卷積,形式如下

          Group Conv

          而谷歌提出的Depthwise Conv則將分組卷積推向了極端——分組數(shù)是輸入通道數(shù)目,即每個(gè)輸入通道單獨(dú)卷積,形式如下

          Depthwise

          卷積核形式的演進(jìn)還是基于通道做的,最基礎(chǔ)的兩個(gè)性質(zhì)空間無關(guān)性和頻域特殊性依舊沒有改變。而Involution操作給出了一個(gè)不同的思路。

          Involution的設(shè)計(jì)原則

          Involution的設(shè)計(jì)原則就是顛倒常規(guī)卷積核的兩個(gè)設(shè)計(jì)原則,即從空間無關(guān)性,頻域特殊性轉(zhuǎn)變成空間特殊性,頻域無關(guān)性

          在這里插入圖片描述

          卷積神經(jīng)網(wǎng)絡(luò)存在下采樣層,導(dǎo)致各個(gè)階段的特征圖長寬會(huì)變化。既然要與空間域聯(lián)系起來,那么第一個(gè)問題是如何參數(shù)化一個(gè)Invotion的卷積核。一個(gè)很自然的想法就是設(shè)置一個(gè)函數(shù) ,讓他根據(jù)輸入的張量,輸出一個(gè)跟特征圖長寬相關(guān)的張量,再把它作為卷積核。

          該函數(shù)公式寫為

          在實(shí)際的代碼中,作者用一個(gè)類似BottleNeck的形式,可以通過控制縮放比例調(diào)整參數(shù)量,用兩個(gè)1x1卷積對(duì)通道進(jìn)行縮放,最后一個(gè)卷積輸出通道數(shù)為(K * K * Groups),其中K代表后續(xù)involution卷積核大小,Groups代表involution操作的分組數(shù)。(如果遇到需要下采樣的情況,則接一個(gè)步長為2的平均池化層。),最后我們可以得到一個(gè)形狀為N*(K * K * Groups)HW的張量,下面是這部分操作的代碼

           ...
              reduction_ratio = 4
              self.group_channels = 16
              self.groups = self.channels // self.group_channels
              self.conv1 = ConvModule(
                      in_channels=channels,
                      out_channels=channels // reduction_ratio,
                      kernel_size=1,
                      conv_cfg=None,
                      norm_cfg=dict(type='BN'),
                      act_cfg=dict(type='ReLU'))
              self.conv2 = ConvModule(
                      in_channels=channels // reduction_ratio,
                      out_channels=kernel_size**2 * self.groups,
                      kernel_size=1,
                      stride=1,
                      conv_cfg=None,
                      norm_cfg=None,
                      act_cfg=None)
          def forward(self, x): 
              weight = self.conv2(self.conv1(x if self.stride == 1 else self.avgpool(x)))
              ...

          下面就會(huì)拿這個(gè)weight來做當(dāng)作一個(gè)卷積核,對(duì)x卷積。

          讀到這里可能會(huì)比較奇怪,為什么卷積核形狀長這樣,我們常見的卷積核應(yīng)該是(C_in, C_out, K, K)。這其實(shí)也是這篇工作的關(guān)鍵之處,上面我們提到他這里注重的是頻域無關(guān)性,空間特殊性。因此它分組卷積的做法是 每一組內(nèi)的特征圖共享一個(gè)卷積核的參數(shù),但是 同一組內(nèi),不同空間位置,使用的是不同的卷積核。

          原文是 an involution kernel located at the corresponding coordinate (i, j), but shared over the channels.

          這段比較費(fèi)解,我畫了一個(gè)簡單的示意圖

          Involution

          為了方便演示,這里設(shè)置N為1,特征圖通道為16個(gè),分組數(shù)為4,ksize=3

          首先輸入特征圖被分為四組,每組有4個(gè)特征圖 之前經(jīng)過兩次1x1卷積,我們得到了involution所需的權(quán)重,形狀為(N, Groups, ksize * ksize, H, W), 在該例子中為(1, 4, 3 * 3, H, W) ,那么分配給每個(gè)組的,就是一個(gè)(1, 3 * 3, H, W),不考慮Batchsize的話,那么每組就有H * W個(gè)3x3的卷積核。

          在通道維上,每組的特征圖共享一個(gè)卷積核,而在同一組的不同空間位置,使用不同的卷積核。

          處理完后,再把各組的結(jié)果拼接回來,下面是完整的involution操作代碼

          import torch.nn as nn
          from mmcv.cnn import ConvModule


          class involution(nn.Module):

              def __init__(self,
                           channels,
                           kernel_size,
                           stride)
          :

                  super(involution, self).__init__()
                  self.kernel_size = kernel_size
                  self.stride = stride
                  self.channels = channels
                  reduction_ratio = 4
                  self.group_channels = 16
                  self.groups = self.channels // self.group_channels
                  self.conv1 = ConvModule(
                      in_channels=channels,
                      out_channels=channels // reduction_ratio, # 通過reduction_ratio控制參數(shù)量
                      kernel_size=1,
                      conv_cfg=None,
                      norm_cfg=dict(type='BN'),
                      act_cfg=dict(type='ReLU'))
                  self.conv2 = ConvModule(
                      in_channels=channels // reduction_ratio,
                      out_channels=kernel_size**2 * self.groups,
                      kernel_size=1,
                      stride=1,
                      conv_cfg=None,
                      norm_cfg=None,
                      act_cfg=None)
                  if stride > 1:
                   # 如果步長大于1,則加入一個(gè)平均池化
                      self.avgpool = nn.AvgPool2d(stride, stride)
                  self.unfold = nn.Unfold(kernel_size, 1, (kernel_size-1)//2, stride)

              def forward(self, x):
                  weight = self.conv2(self.conv1(x if self.stride == 1 else self.avgpool(x))) # 得到involution所需權(quán)重
                  b, c, h, w = weight.shape
                  weight = weight.view(b, self.groups, self.kernel_size**2, h, w).unsqueeze(2# 將權(quán)重reshape成 (B, Groups, 1, kernelsize*kernelsize, h, w)
                  out = self.unfold(x).view(b, self.groups, self.group_channels, self.kernel_size**2, h, w) # 將輸入reshape
                  out = (weight * out).sum(dim=3).view(b, self.channels, h, w) # 求和,reshape回NCHW形式
                  return out

          實(shí)驗(yàn)結(jié)果

          作者基于ResNet模型,將Bottleneck模塊的中間卷積塊,替換成7x7大小的involution操作。改進(jìn)后的模型稱為RedNet

          實(shí)驗(yàn)表格

          可以看到實(shí)驗(yàn)結(jié)果還是很不錯(cuò)的,不僅壓縮了網(wǎng)絡(luò)參數(shù),在中小網(wǎng)絡(luò)也能提升模型精度。(但我更好奇的是實(shí)際運(yùn)行的速度,如每秒能處理多少圖片),在其他圖像任務(wù)上也有提升,這里就不放出來了,有興趣的讀者可以去讀下原文。

          對(duì)于Involution操作的分組數(shù),Kernel大小,作者也做了相關(guān)消融實(shí)驗(yàn)

          消融實(shí)驗(yàn)

          可以看到從3x3到7x7,精度是穩(wěn)定提高的,但是加到9x9以后提升有限。為了平衡參數(shù)量和精度,作者選擇了7x7大小的Kernel,分組通道數(shù)為16,生成Kernel的卷積模塊里,reduction參數(shù)設(shè)為4。

          總結(jié)

          這篇論文還是挺有意思的,作者陣容也很豪華,其中包括SENet的作者HuJie?,F(xiàn)在的卷積核改進(jìn)基本都是從通道維度去做,而這篇工作顛覆了這種思想,跟常規(guī)卷積反著來,做了一個(gè)自己卷自己的內(nèi)卷操作。

          論文還提到了這個(gè)操作和自注意力機(jī)制的關(guān)系,但是筆者并沒有讀太懂,就沒有闡述(還望相關(guān)作者解答下)。作者還留了一些坑,我未來也很期待NAS在該模塊上更多的探索。


          歡迎關(guān)注GiantPandaCV, 在這里你將看到獨(dú)家的深度學(xué)習(xí)分享,堅(jiān)持原創(chuàng),每天分享我們學(xué)習(xí)到的新鮮知識(shí)。( ? ?ω?? )?

          有對(duì)文章相關(guān)的問題,或者想要加入交流群,歡迎添加BBuf微信:

          二維碼

          為了方便讀者獲取資料以及我們公眾號(hào)的作者發(fā)布一些Github工程的更新,我們成立了一個(gè)QQ群,二維碼如下,感興趣可以加入。

          公眾號(hào)QQ交流群


          瀏覽 174
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费蜜桃网站 | 豆花成人在线视频 | 欧美在线大香蕉 | 狼人综合色 | 色情片免费观看 |