為什麼要寫這篇文章呢?因為我算是深度學習的小白吧!接觸到深度學習1年整了,感覺自己學的都是皮毛,買了好多書,但真正看完太少了。本文只是針對圖像來說的,文本、語音我可以說一點都不會,只是瞭解個RNN。我們都是從圖像分類開始的,從簡單的Lenet到Xception…以及很多新興的模型,從圖片分類中我們瞭解最多的就是卷積、池化。然後我們又接觸到目標檢測從Two stage到Single stage,從Rcnn到Mask Rcnn,從Yolo到cornet。我就是整到這就遇到問題了,因為一個Mask Rcnn就給我這個小白整的有點迷糊,知道大概是怎麼回事,RPN,ROIalign等還好,整到Mask、FCN就稍微有點迷糊,因為這裡涉及到了反捲積(Deconvolution),所以我又要好好看看圖像分割的模型了。具體模型已經知道了下一步就是狠狠的嚼它們,但是嚼它們之前我感覺我應該好好的鞏固一下基礎了,所以寫下了這篇文章。本文是參考別人的文章寫的,其中加入了自己的想法,如果有錯誤的地方,大家可以指出,我一定改。

卷積操作(Convolution)

關於卷積操作,這篇相當不錯,A guide to convolution arithmetic for deep learning關於卷積的內容大部分都是這篇文章的。咱們主要看一下卷積運算。只是針對2維卷積操作,並且輸入、輸出、kernel都是正方形的,左右、上下移動的步長相等,都是0填充;這種操作同樣也適合多維卷積核非正方形的輸入、輸出、kernel。首先需要定義一些參數和符號:inputs(i1=i2=1),kernel size(k1=k2=k),strides(s1=s2=s),padding(p1=p2=p),outputs=o

1.No zero padding,unit strides,p=0,s=1,i=4,k=3,o=2

我們可以發現這是最基礎的卷積運算,kernel的左上角與輸入featuremap左上角對齊,從左到右,從上到下依次進行卷積運算。因此輸出featuremap(符號o)計算公式如下:

2.Zero padding,unit strides(p>0,strides=1)

2.1 p>0(p=2),s=1,i=5,k=3,o=6

這個p≠0,也就是需要在輸入的feature map外圍填充p圈零,看好這裡是p圈,這個很重要,這就代表輸入的feature map的有效範圍變為i+2p*i+2p。仍然是用kernel的左上角與有效的輸入feature map的左上角對齊,依次從左到右,從上到下進行卷積運算,輸出feature map計算公式如下:

2.2 Same padding p>0(p=1),s=1,i=5,k=3,o=i

這個的目的是輸出feature map和原feature map的size相同,即使i=o;我們的目的是計算出p的大小,具體計算公式如下:

具體是什麼意思呢?其實我們已知o=i,並且是strides=1,卷積後的輸出featuremap的尺寸o=i+2p-k+1,利用這個式子求解p,所以p=(k-1)/2;因為我定義k為奇數,k=2n+1,所以p=n。

其實same卷積還可以這麼理解,就是用kernel的中心對齊輸入featuremap的左上角。

2.3 Full padding p>0(p=2),s=1,i=5,k=3,o=7

這個叫全填充,其實真正的理解應該是,kernel對輸入的featuremap的每個像素都完全重疊,更好的理解就是,利用kernel的右下角和輸入featuremap的左上角對齊,kernel超出輸入featuremap的部分全0padding。

由於本案例是kernel=3,當利用kernel的右下角和輸入featuremap的左上角對齊時,超出2個像素,所以p=2,因此o=i+2p-k+1=5+4-3+1=7

3.No zero padding,non-unit strides(s>1,p=0)

這個也不難理解,就是kernel在輸入feature map上以我們定義的strides大小遍歷。一般情況如下圖:i=5,k=3,s=2,p=0,o=2.

怎麼計算出的o呢?公式如下:

但是有種特殊情況,就是當kernel從左到右,以strides遍歷輸入feature map時,如果最後kernel超出輸入feature map時,則被省略。如下圖:(i=6,k=3,s=3,p=1)

本案例如果我們利用公式計算o=(i+2p-k)/s+1=(6+2-3)/2+1=3.5,但我們不能直接用這個數,我們需要取下限,即對o向下取整,o=3。

4.Zero padding,non-unit strides(s>1,p>0)

這是更一般的情況,計算公式也被我們所熟知,如下

但是具體設計的時候較為繁瑣,為什麼這麼說呢?因為有可能input的feature map的size不同但output的feature map的size是相同的。如下兩個圖,

從以上的兩個圖,我們就能發現,input的featuremap的size是不同,一個是5,一個是6,但最後的輸出結果的size卻是相同的。這就導致轉置卷積或者叫轉置卷積(deconvolution)時候出現麻煩。

還有一個關於理解不同strides,output的feature map的size是多少的問題!下面的圖就能很好的理解這個問題。

上邊左面的圖代表strides為1,右邊的圖代表strides為2,怎麼知道output的size的呢?左邊的圖可以看到kernel在input的featuremap中向左移動兩次,kernel的右邊界就和featuremap的右邊界對齊了,向下移動也一樣。所以左邊的output的size就是2+1,1是什麼呢?1代表的就kernel在input的featuremap初始位置也進行了一次卷積運算。右邊的圖是同樣的道理,左移動1次,下移動1次,所以output的size就是1+1=2。

總結:

大家看到這是否感覺很亂呢?其實比較典型的模型中,這些超參數不會太沒有規律,一般卷積運算中都是strides為1的,same卷積的。但是既然我們想要好好的理解卷積運算那我就總結一下。按照A guide to convolution arithmetic for deep learning論文的作者,他把卷積分為4種情況。分別為(strides=1,p=0)、(strides=1,p>0)、(strides>1,p=0)、(strides>1,p>1)這四種情況。但是萬變不離其宗,最終通用公式就是:

首先,我們瞭解卷積運算有助於我們理解經典模型的結構;其次我們可以根據該公式設計自己的模型。但是切記不要太隨意的設計模型結構,因為經典的模型都是一個團隊的共同努力所得到的,其中必定有其不為人知的心酸試驗。所以我們設計的時候一定要到學習經典模型,並且有規律的去改善模型。

池化操作(Pooling)

池化有什麼用呢?一般的情況下是利用池化操作減小feature map的size,pooling區別於卷積,主要的原因是,卷積是使用學習到線性kernel,而pooling則是一種硬編碼方法,提取到固定區域內的max、average值。池化不設計padding,所以池化的公式一般為:

為什麼降低維度呢?因為圖像具有一種「靜態性」的屬性,表明一個圖像區域有用的特徵極有可能在另一個區域同樣適用,因此就需要對不同位置的特徵進行聚合統計。想法就是:計算圖像一個區域上的某個特定特徵的平均值(或最大值)來代表這個區域的特徵。這就演化出平均池化和最大池化。

一般池化(General Pooling):池化作用於圖像中不重合的區域(這與卷積操作不同)。一般池化由於每一池化窗口都是不重複的,所以sizeX=stride。

重疊池化(Overlapping Pooling):相鄰池化窗口之間會有重疊區域,所以sizeX>stride。

空間金字塔池化(Spatial Pyramid Pooling):把任何尺度的圖像的卷積特徵轉化成相同維度,不僅可以讓CNN處理任意尺度的圖像,還能避免cropping和warping操作,導致一些信息的丟失。一般的cnn需要固定輸入圖像的大小,這是因為全連接層的輸入需要固定輸入維度,先讓圖像進行卷積操作,然後轉化成維度相同的特徵輸入到全連接層。

池化的作用則體現在降採樣:保留顯著特徵、降低特徵維度,增大kernel的感受野。另外一點值得注意:pooling也可以提供一些旋轉不變性。池化層可對提取到的特徵信息進行降維,一方面使特徵圖變小,簡化網路計算複雜度並在一定程度上避免過擬合的出現;一方面進行特徵壓縮,提取主要特徵。

最大池採樣在計算機視覺中的價值體現在兩個方面:(1)、它減小了來自上層隱藏層的計算複雜度;(2)、這些池化單元具有平移不變性,即使圖像有小的位移,提取到的特徵依然會保持不變。由於增強了對位移的魯棒性,這樣可以忽略目標的傾斜、旋轉之類的相對位置的變化,以此提高精度,最大池採樣方法是一個高效的降低數據維度的採樣方法。

需要注意的是:這裡的pooling操作是特徵圖縮小,有可能影響網路的準確度,因此可以通過增加特徵圖的深度來彌補。在CNN網路中卷積之後會跟上一個池化層,池化層的作用是提取局部均值或最大值,根據計算出來的值不一樣就分為均值池化層與最大值池化層,一般常見的多為最大值池化層。池化的時候同樣需要提供filter的大小、步長。

轉置卷積(Deconvolution)

轉置卷積的概念第一次出現是Zeiler在2010年發表的論文Deconvolutional networks中,但是並沒有指定轉置卷積這個名字,轉置卷積這個術語正式的使用是在其之後的工作中。隨著轉置卷積在神經網路可視化上的成功應用,其被越來越多的工作所採納比如:場景分割、生成模型等。其中轉置卷積也有很多其他的叫法,比如:Transposed Convolution,Fractional Strided Convolution等等。具體的原理我們可以看一看這篇文章。

轉置卷積的作用我理解就是恢復圖像size的方法,我們正常情況下是使用卷積+池化把圖像size減小,而channel增大;而轉置卷積則是希望用卷積操作最終生成的feature map變回和原圖像大小。

那麼轉置卷積是怎麼來的呢?其實它是卷積操作中kernel矩陣的一個轉置。我們拿這個圖舉個例子。這是一個p=0,s=1,i=4,k=3,o=2的卷積運算。

我們重新寫一遍它們的形狀,inputs=4×4,kernel=3×3,outputs=2×2,現在我們把它們都當做矩陣,把inputs變成向量的形式,它們的size分別為inputs=16×1,outputs=4×1。怎麼才能把kernel表示成矩陣的形式呢?並且讓kernel與inputs做乘法,得到outputs呢?這裡我討論的都是它們的size。

我們知道矩陣的乘法運算,A(m×p) ×B(p×n)=C(m×n)。所以想要outputs=4×1,那麼kernel的形狀應該為4×16,這樣kernel(4×16)×inputs(16×1)=outputs(4×1)。我們就可以把kernel寫成如下形式。

以上就是卷積操作的矩陣表示形式,那麼轉置卷積呢?咱們說了它是一種反向,卷積操作是通過inputs和kernel知道outputs,而轉置卷積就是通過outputs和kernel知道inputs。即如下:

kernel(4×16)× inputs(16×1)=outputs(4×1)→→→→→kernel(16×4) × outputs(4×1)=inputs(16×1)

以上的兩個kernel是不同卷積操作的size是4×16,而轉置卷積操作的size是16×4。這就是轉置卷積的由來。

咱們首先看一些例子,然後在系統的分析一下轉置卷積,以及一些困惑的地方。

還是分為四種情況分別為(p=0,s=1)、(p>0,s=1)、(p=0,s>1)、(p>0,s>1)。

1.No zero padding,unit strides(original convolution,p=0,s=1)deconvolution

怎麼考慮這個案例呢,它是卷積操作(p=0,s=1,i=4,k=3,o=2)的一個逆向過程,也就是我們利用o=2,推回i=4的過程;但我們要保持kernel的大小不變,仍然k=3。怎麼辦呢?用一個小size推回大size呢?這就是上採樣的過程。我們試圖通過填充p圈0,仍然利用卷積操作,達到這個目的。如下圖:

從圖中我們看到我們填充p=2圈0,featuremap的大小變為6,在利用卷積操作,就可以得到size為4的featuremap。

還有一種方法來理解本例子中的轉置卷積,其實就是一個full卷積,使用kernel的右下角和featuremap的左上角對齊的一種卷積方法。也是有相應的計算公式如下:i/=2,p/=k-1=2,o/=4。

2.Zero padding,unit strides(original convolution,p>0,strides=1)

原始的卷積操作是把(s=1,p=2,i=5,k=4)卷積成o=6,deconvolution的目的是把o=6卷積成i=5,仍然是kernel的size不變。原始的計算公式為6=5+2*2-4+1,但是此時應用卷積操作公式為5=6+2p-4+1,我們可知p=1,所以deconvolution時要填充1圈0。也是有相應的計算公式:

我們從公式中發現:如果你原始卷積操作時,填充了p圈0,則新的deconvolution操作時,p/則不大於p,p/=k-p-1。i/=6,p/=k-p-1=1,o/=5。

2.1Same convolution[ p>0(p=1),s=1,i=5,k=3,o=5],deconvolution?

轉置卷積第2點說的是p>0,s=1的任意時候,但有時我們需要一些特殊處理,比如我們想讓卷積前後的featuremap的size不變,或者size變大。我們也看一下deconvolution的變化。先看第一種就是same convolution。

其實這種情況都不用想,保證convolution和deconvolution操作都是一樣的。就算我們用公式計算也是一樣的。i/=5,p/=k-p-1=1,o/=5。這就不過多說了,咱們繼續看第二種就是fullpadding。

2.2Full convolution[ p>0(p=2),s=1,i=5,k=3,o=7],deconvolution?

這個就是關於Full convolution的deconvolution操作,原有的是我們從小size卷積到大size,並且保證每個像素都接受到相同次數的kernel的計算。而我們想逆過去這個操作,則是從大size卷積到小size。這個很簡單5=7+2p-3+1,p=0,我們應用計算公式也是一樣的。i/=7,p/=k-p-1=0,o/=5。

小結:

為什麼這裡要小結一下呢?因為下面要說的就不是簡單的外圈填充了,涉及到對featuremap內部插入,所以我們小結一下。不知道大家看到這裡,理解到什麼是deconvolution了?其實deconvolution根本不是一個什麼新型的數學計算,也就是說它跟加、減、乘、除、卷積、池化等等這些東西根本就不是一路人;deconvolution它只是一個形式,只是一種說法,或者說我們怎麼把原有的卷積運算給倒過取得卷積運算;說一千道一萬,deconvolution還是卷積運算,只不過我們想法設法的設計p,怎麼獲得p的大小。永遠記住當s=1時,p/=k-p-1。我以上說的這些都是僅僅考慮到size上,但具體kernel的內部值得大小都是通過學習得來的,我們只要某個size是怎麼來的就行。

3.No Zero padding,non-unit strides(original convolution,p=0,strides>1)transposed

下面的內容稍微有點難理解,以上說的轉置卷積strides都是為1的情況。但當我們選擇strides>1時候,怎麼給這個運算逆過去呢?我們知道strides為1的卷積,轉置之後strides仍是1,我們可以想像這是一種逆運算,正向為1,反向也為1,;那麼正向strides>1,逆運算時候s<1吧?我們期望是這樣操作來進行deconvolution的,但是strides根本就不可能小於1呀!所以怎麼辦呢?kernel的size不變,我們只能改變輸入的featuremap的size和strides,但我們知道只要strides=1或者strides>1,卷積操作就是一個下採樣的過程,就是減小原featuremap的size的過程,而我們需要一個上採樣的過程,所以在deconvolution操作時,strides只能為1,;並且我們只能改變輸入的featuremap。我們又知道原有的convolution操作,當strides>1時候,下採樣的輸出的featuremap的size相當小,我們利用外圍填充多圈0的方法雖然能夠卷積到原有大小,但是kernel與外圍填充0的featuremap卷積時候有許多沒有意義的操作,或者不是一個偶數或是整數的填充0。如下圖:

原有的卷積操作是(i=5,k=3,s=2,p=0,o=2.),而我們希望逆過來,就是利用i』=2,進行卷積操作輸出featuremap的size為o』=5,上面已經分析,deconvolution的strides最好為1,k『=k=3,應用通用的卷積公式o』=i』+2p』-k』+1→→5=2+2p』-3+1,通過計算可知p『=2.5。如上圖,我們需要在輸入的featuremap的周圍填充2.5圈0,怎麼運算才能卷積出featuremap的size為5呢?可以看出這種卷積是不能完成的。也就是如果我們計算出填充的圈數p』是個小數,原有的外圍填充是工作不了的。所以就有下圖的方法:

把多餘出來的半圈,讓他左右整合成一列,上下整合成一列,放入到輸入featuremap的內部。這樣可以看出填充的2p』=5,5排、5列的填充;和原有的p=2.5』是一樣。

我參考的論文給出了公式:

我參考的論文的作者的解釋我看的稍微有點迷糊,不過有許多值得我們借鑒的,首先deconvolution操作的featuremap的外圍的填充p』=k-1,每個像素塊之間插入s-1排0,計算公式就不用說了,只是把convolution運算公式變了一下型。下面要說的就是最複雜的s>1,p>0的卷積運算進行逆運算的deconvolution。

4. Zero padding,non-unit strides(original convolution,p>0,strides>1)transposed

我們仍然是從案例入手,然後在細緻分析。還記得下邊的這兩個圖嗎?對不同輸入的featuremap利用相同的strides和相同size的kernel卻得到了相同的輸出featuremap。為什麼會出現這種情況呢?主要是(i+2p-k)/s得到的是整數和小數的區別,第一個是整數;第二個是小數並取下限。那麼可想而知deconvolution時,輸入相同的featuremap,會deconvolution出不同的featuremap,這怎麼辦呢?

這是我們仍然要考慮,(i+2p-k)/s是整數還是小數!如果為小數,我們就用下邊的方法,如圖:

本案例是(p=1,s=2,i=6,k=3,o=3)的卷積運算,我們要做的deconvolution就利用i』=3,k』=k,s』=1卷積到o』=6,先上公式o』=i』+2p』-k+1→→6=3+2p』-3+1,可知p』=2.5,又出現小數了,先在每個像素塊之間插入s-1排0,外圍填充k-p-1圈0,餘下的插入右側和下邊,然後在卷積運算。

用我自己的意思可能就解釋不明白了!咱們直接看原文,作者說首先要設計一個參數a,a是(i+2p-k)/s的餘數,餘數有s種可能,,也就是數如果在(s>1,p>0)的這種情況,輸入相同的featuremap進行deconvolution操作時,可能會有s中結果;但是到底是哪種結果呢?如果能(i+2p-k)/s是整數的時候,我們利用每個像素塊之間插入s-1排0,外圍填充k-p-1圈0。如果不能整除時,我們也是先利用每個像素塊之間插入s-1排0,外圍填充k-p-1圈0,但是還有個餘數a,我們把a排0填充在最右側和最下邊。上邊的案例是(i+2p-k)/s為小數時,下邊的案例是(i+2p-k)/s為整數時。

總結:

不知道大家看明白了嗎?可能我說糊塗了!無論如何我們總結一下。

Deconvolution,它根本就是運算,只是卷積逆運算的一種叫法,實際上它就是卷積運算;不過我們要重新設計輸入的featuremap。通過以上四種分類,我們要記得deconvolution操作時,s』=1,k』=k,p』=k-p-1這是固定不變的。然後在原卷積運算的strides,如果s=1,外圍填充p』圈0就可以了;如果s>1,考慮(i+2p-k)/s是整數還是小數,當是整數時:外圍填充p』圈0,同時每個像素塊之間插入s-1個0;當是小數時,外圍填充p』圈0,同時每個像素塊之間插入s-1個0,並且繼續在填充後的featuremap的最右側和最下邊填充a排0。

空洞卷積(Dilated convolutions)

感受野

說空洞卷積之前,咱們先介紹一下感受野這個名詞,因為我一直不是很理解感受野。咱們先看定義:感受野用來表示網路內部的不同神經元對原圖像的感受範圍的大小。這裡做個辨析,是針對原圖像的感受範圍,不是所對應的featuremap,為什麼呢?因為我看了一下醫學上的定義,我也看不懂,不過有一句話我看懂了!「皮膚感覺末梢神經元的感覺野,就是由一個感覺神經元的神經纖維所支配的許多感受器在皮膚表面所能反應的範圍」突出的是皮膚表面。在CNN中直觀理解就是某一層輸出featuremap中的一個像素塊,代表了初始輸入層元素區域的大小。例如咱們舉個例子:咱們定義一個cnn結構,3層的神經網路,卷積核size為k1=k2=3,strides=1,channel1=channel2=channel3=1,inputs=6*6,outputs=2。具體如圖:

現在我們看Conv2層中的一個像素塊的感受野是多少?也就是這一個像素塊代表inputs中的多少個像素塊?咱們就從這個圖分析,首先Conv2中的一個像素塊,代表Conv1中的6個像素塊;而Conv1中的一個像素塊代表Inputs中6個像素塊,那麼是不是Conv2中的一個像素塊就代表Inputs中的6*6個像素塊呢?這個回答是錯誤的,正確的回答是代表5*5個像素塊。為什麼呢?為什麼呢?因為卷積運算有一個特點就是許多像素塊是重複參見卷積運算的,比如圖中標記出①的這三個像素塊,當kernel在初始位置的時候參加一次運算,當kernel向右移動1步的時候,它仍然參加捲積運算;並且,當kernel向下移動一步時候圖中①下邊兩個像素又再一次參加了卷積運算,所以說不能看每個卷積層featuremap中一個像素塊代表上一層幾個像素塊,然後在把它們乘起來。不知道我這麼解釋大家明白了嗎?

咱們還是先不看公式,咱們在把上圖換一種表示形式!如下圖:

畫的有點亂,不要慌,從Conv2中的一個像素塊出發,看看它代表Inputs多少塊像素?Conv2中的第一個像素,是通過Conv1中的編號1,2,3,5,6,7,9,10,11這9個像素塊與kernel卷積得到的,而且Conv1中的每個像素塊又是和Inputs中某9個像素塊卷積得到的,例如Conv1中的編號1像素塊是通過Inputs中的1,2,3,7,8,9,13,14,15,我們把Conv1中的編號1,2,3,5,6,7,9,10,11這9個像素塊都分析一遍,如圖中所示,可以看到Conv1中的編號1,2,3,5,6,7,9,10,11這9個像素塊共計與Inputs中編號為1,2,3,4,5,7,8,9,10,11,13,14,15,16,17,19,20,21,22,23,25,26,27,28,29這25個像素塊有不正當關係,所以我們判斷Conv2中的一個像素塊,代表了Inputs中的25個像素塊,也就是我們說的感受野為5*5。

從以上的分析來看,感受野的大小是有kernel的size決定的,但是還有一個因素就是strides的size。很好理解,strides越大,感受野越大。我也看了好多別人分析,在這裡就拿來借鑒一下,參考文章。以上說的都是簡單的、容易理解的,strides為1,padding=0。下面我們來看一篇就是關於感受野介紹的英文文章(A guide to receptive field arithmetic for Convolutional Neural Networks),原文我沒有登錄上,這是別人的資源。我英文不好,翻譯或者講解的不好大家理解。

作者說一個感受野用中心位置和大小來表徵。並且感受野中的每個像素值(pixel)並不是同等重要的。一個像素點越接近感受野中心,它對輸出特徵的計算所起的作用越大。這意味著某一個特徵不僅僅是受限在輸入圖片中某個特定的區域(感受野),並且呈指數級聚焦在區域的中心。直接上圖了,就不一一翻譯了!

先看左邊的圖,這是一個關於(i=5,p=1,s=2,k=3)和(i=3,p=1,s=2,k=3)的兩次卷積操作,這是一個正常的卷積操作圖。咱們先分析圖①,應該很簡單吧,外邊填充1圈0,然後用公式o=(i+2p-n)/s+1=(5+2-3)/2+1=3,所以輸出的featuremap是3×3的。在分析圖③,稍微有點蒙,為什麼外邊填充3圈0呢?其實這裡有個過渡,作者沒有給出。第二次卷積操作也需要在第一次輸出的featuremap的外圍填充1圈0,填充之後第一次輸出的featuremap就變成5×5了,但是如果我要得到5×5的featuremap,原圖要多大呢?用公式o=(i+2p-n)/s+1→→5=(5+2p-3)/2+1→→p=3,所以圖③外圍填充了3圈0,才能輸出size為2×2的featuremap。

圖②和圖④可能有點難理解,這是文章作者提出的一種方法叫做固定大小的CNN特徵圖可視化。就是讓所有的輸出featuremap與輸入的圖像的size一樣,並把每個輸出featuremap中的特徵(像素塊)標記在感受野的中心。

如圖②原始的featuremap時5×5的,經過卷積之變成3×3的,但是作者不想讓它是3×3,也想把它變成5×5的,並且讓卷積之後featuremap中的每個像素塊對應著原始卷積運算的中心。如果想把輸出的featuremap的size為3×3變成5×5,作者提出了一種方法就是在輸出的featuremap的每個像素塊之間插入一個值為0的像素塊,為什麼這麼做呢?因為針對某一個featuremap中的每一個feature都有相同的感受野,所以我們可以簡單地在每個feature周圍畫出邊界框,從而獲得感受野的大小。這裡提一句,我引用的圖是文章中的原圖,可能作者的原圖稍微有些錯位,應該是填充一圈0,不知道為什麼圖②的原始featuremap的右側多出一列。為什麼每個像素塊之間填充一個0呢?因為我們的strides為2,kernel也要移動2步,因此卷積之後的feature也移動2步(走兩步中間只差一步),所以卷積操作之後的每個像素塊之間填充一個0,就可以和原featuremap同樣大小。

咱們看第二次卷積,k=3,p=1,s=2,基本步驟如下圖:

從這兩個例子中我們能發現,無論輸出的featuremap的size是多少,相同kernel,相同padding,相同strides,感受野的大小都是一樣的,如上圖,第一次卷積的感受野都是3×3,第二次卷積的感受野都是7×7。也就是說感受野的大小可能只與這三個參數有關(kernel,padding,strides)。

作者給出了計算方法,我們需要了解四個公式,並要掌握各個相關參數的定義。

這個公式①,我們很熟悉,正常的卷積操作,就是這樣計算的,不用過多介紹。

這個公式②,我們第一次見到,通過上文的講解,可能大家能看明白,並且我們說過感受野有兩個重要的參數,一個是中心,一個是大小。中心是啥?

這個公式②有一句話我們要注意,間隔值是按照strides呈指數級增長,也就是說每層卷積的strides很重要,下面給出公式③,計算出感受野的大小。

從公式③可以看出,感受野大小與上一層的感受野大小、kernel的size以及輸入的featuremap的feature之間的間隔值有關。感受野也是呈指數級增加。

公式④計算輸出特徵圖的第一個特徵感受野的中心坐標,其等於第一層的中心坐標加上(k?1)/2?jin,再減去p?jin,注意兩項都要乘以前一層的間隔距離以得到實際距離。下面是利用公式計算具體實例。

對於感受野大小的計算還有一篇文章更為簡潔,上面說的都不是廢話,有助於你更好的理解感受野,但這個公式我們最好記住。

空洞卷積

上文了解了一下感受野的問題,接下來咱們言歸正傳,卷積(Convolution)、池化(Pooling)一般用於下採樣,而轉置卷積(Deconvolution)用於上採樣。空洞卷積(Dilated)是幹什麼用的呢?參考論文Multi-Scale Context Aggregation by Dilated Convolutions。它的產生是有背景的,2016年才被提出,它主要是為瞭解決圖像分割領域的問題,圖像分割需要輸入和輸出在像素的shape保持一致,但由於池化層的存在,因此導致FCN需要通過上採樣擴增size,但是上採樣並不能將丟失的信息無損的找回。這句話什麼意思?就是說池化是有缺點的,池化會帶來信息損失,那麼就把池化層去掉;但是池化去掉又產生缺點,就是感受野變小,這樣就會降低整個模型的預測精度。怎麼辦呢?如何在去掉池化下採樣操作的同時,而不降低網路的感受野呢?空洞(Dilated convolution)卷積應運而生!

空洞卷積通過在kernel的像素塊之間插入空格來產生新的空洞kernel,如下圖,而這個空格插入幾個呢?這時候就引入一個超參數d,叫膨脹率。通常在原有kernel的像素塊之間插入d-1個空格;而d=1對應一個常規卷積。大體形式還是看下圖,初次認識一下!

(a)圖:按照空洞卷積的叫法,被稱為3×3的1-dilated convolution。其中的1就是d=1,膨脹率為1的空洞卷積;但實際上也是一個常規3×3的卷積核。

(b)圖:3×3的2-dilated convolution。d=2,膨脹率為2的空洞卷積。

(c)圖:3×3的4-dilated convolution。d=4,膨脹率為4的空洞卷積。

現在我們大概知道空洞卷積長什麼樣子了,但是如果給一個輸入的featuremap,進行空洞卷積運算,怎麼得到輸出呢?或者更簡單的說,通過給定i、p、s、k、d怎麼計算出o呢?我們首先知道kernel變了,不在是原始的k×k了,變成如下:

所以outputs的size,如下:

我們可以看一個例子,原始的kernel的size為3×3,使用膨脹率為2的空洞卷積,kernel的size變為5×5(k』=k+(k-1)(d-1)=3+(3-1)(2-1)=5)。利用公式:o=(i+2p-k』)/s+1=(7+0-5)/1+1=3。利用上面的公式就可以計算了,如下圖。

但是還要繼續墨跡墨跡,空洞卷積中的洞是不參加捲積運算的,只是在幫助增大kernel的size,並且增加了感受野的大小,下面我們就說說空洞卷積感受野的問題,這時候我們就要想想上面說的感受野的相關知識點。

我們還記得感受野的計算公式嗎?

還是上邊這個圖,第一個圖代表第一層的3×3的1-dilated conv,中間的圖代表第二層的3×3的2-dilated conv,最後一個圖代表第三層的3×3的4-dilated conv,strides都為1。我們可以看到第一層的感受野為l1=3×3;第二層的感受野l2=l1+((f2-1)*1),f2就是上邊說的使用膨脹率2的kernel的變形,所以f2=5,因此第二層的感受野l2為7×7;同理第三層的感受野為l3=l2+((f3-1)*1),f3=k+(k-1)(d-1)=3+(3-1)(4-1)=9,因此第三層的感受野l3為15×15。

其實還有一種方法理解空洞卷積的感受野問題,第一個圖不用解釋了,感受野就是3×3,而第二個圖的感受野,可以這麼理解,其中的每一個紅點都是上一層3×3感受野卷積得到的,因此以每個紅點為中心,畫出3×3的區域,疊加之後為圖中7×7的感受野。第三個圖的每個紅點是7×7感受野卷積得到的,因此以每個紅點為中心,畫出7×7區域,疊加之後為圖中15×15的感受野。

不知道說明白了沒有,空洞卷積,在不使用pooling的情況下,同樣下採樣,並且感受野也明顯提高。但是它也有一定的弊端,網格效應和不利於小物體分割。解決辦法為

Understanding Convolution for Semantic Segmentation

Rethinking Atrous Convolution for Semantic Image Segmentation


推薦閱讀:
相關文章