1. 程式人生 > >卷積核膨脹(dilation)解析

卷積核膨脹(dilation)解析

第一位大神解析:

  1. deeplab方法概述 deeplab方法分為兩步走,第一步仍然採用了FCN得到 coarse score map並插值到原影象大小,然後第二步借用fully connected CRF對從FCN得到的分割結果進行細節上的refine。(有關FCN的內容介紹,可以參考我的前面得一篇部落格:http://blog.csdn.net/tangwei2014/article/details/46882257) 下面這張圖很清楚地展示了整個結構:這裡寫圖片描述 然後這張圖展示了CRF處理前後的效果對比,可以看出用了CRF以後,細節確實改善了很多:這裡寫圖片描述

  2. deeplab對FCN更加優雅的處理方式 在第一步中,deeplab

    仍然採用了FCN來得到score map,並且也是在VGG網路上進行fine-tuning。但是在得到score map的處理方式上,要比原FCN處理的優雅很多。 還記得CVPR 2015的FCN中是怎麼得到一個更加dense的score map的嗎? 是一張500x500的輸入影象,直接在第一個卷積層上conv1_1來了一個100的大padding。最終在fc7層勉強得到一個16x16的score map。雖然處理上稍顯粗糙,但是畢竟人家是第一次將影象分割在CNN上搞成end-to-end,並且在當時performance是state-of-the-art,也很理解。deeplab摒棄了這種做法,取而代之的是對VGG的網路結構上做了小改動:將VGG網路的pool4
    pool5層的stride由原來的2改為了1。就是這樣一個改動,使得vgg網路總的stride由原來的32變成8,進而使得在輸入影象為514x514,正常的padding時,fc7能得到67x67的score map, 要比FCN確實要dense很多很多。 但是這種改變網路結果的做法也帶來了一個問題: stride改變以後,如果想繼續利用vgg model進行fine tuning,會導致後面filter作用的區域發生改變,換句話說就是感受野發生變化。這個問題在下圖(a) (b)中通過花括號體現出來了:

  3. Hole演算法 於是乎,作者想出了一招,來解決兩個看似有點矛盾的問題:既想利用已經訓練好的模型進行fine-tuning,又想改變網路結構得到更加dense的score map.

    這個解決辦法就是採用Hole演算法。如下圖(a) (b)所示,在以往的卷積或者pooling中,一個filter中相鄰的權重作用在feature map上的位置都是物理上連續的。如下圖(c)所示,為了保證感受野不發生變化,某一層的stride由2變為1以後,後面的層需要採用hole演算法,具體來講就是將連續的連線關係是根據hole size大小變成skip連線的(圖(c)為了顯示方便直接畫在本層上了)。不要被(c)中的padding為2嚇著了,其實2個padding不會同時和一個filter相連。 pool4的stride由2變為1,則緊接著的conv5_1, conv5_2和conv5_3中hole size為2。接著pool5由2變為1, 則後面的fc6中hole size為4。這裡寫圖片描述

第二位大神解析:

原作者解析caffe的卷積層時,在conv_layer.cpp中有一個卷積核膨脹操作,在conv_layer.cpp的第17行有如下程式碼

const int kernel_extent = dilation_data[i] * (kernel_shape_data[i] - 1) + 1;

   上面的程式碼描述了卷積核的膨脹操作,我們不妨來做個假設,卷積核為3*3的,膨脹係數為2,那麼,卷積核膨脹之後,卷積核的單邊尺寸就變成了2*(3-1)+1,即卷積核的尺寸變成了5*5,筆者在最初看到這一行程式碼的時候相當疑惑,不太明白卷積核由3*3變成5*5是怎麼操作的,這個時候,caffe.proto就起了作用,我們開啟caffe.proto,找到卷積層的引數定義,在message ConvolutionParameter中,找到關於dilation的定義如下

  // Factor used to dilate the kernel, (implicitly) zero-filling the resulting
  // holes. (Kernel dilation is sometimes referred to by its use in the
  // algorithme à trous from Holschneider et al. 1987.)
  repeated uint32 dilation = 18; // The dilation; defaults to 1

   在caffe.proto中,闡述了卷積核膨脹的使用情景,即在à trous演算法中,這是一個有關小波分析的演算法。卷積核膨脹是將卷積核擴張到膨脹尺度約束的尺度中,並將原卷積核沒有佔用的區域填充零,為了使說明更加形象,下面筆者繪製卷積核膨脹示意圖為大家解析一下卷積核膨脹操作:

   在上面的示意圖中,卷積核由3*3膨脹到了5*5,讀者朋友們可以看到,膨脹後的卷積核中填充了一些0,讀者朋友們可以試著畫出其他尺寸的卷積核搭配不同膨脹係數的膨脹效果,而具體的膨脹操作是在img2col中實現的,img2col.cpp筆者將在後話解析。

下面筆者分析一下膨脹係數與卷積核膨脹的關係,首先回到卷積核膨脹公式:

膨脹的卷積核尺寸 = 膨脹係數 * (原始卷積核尺寸 - 1) + 1

   首先由於卷積的操作特性,卷積核尺寸是奇數,則原始卷積核尺寸減一為偶數。那麼,在上述公式中,膨脹係數*偶數-1為奇數,保證了膨脹的卷積核尺寸為奇數。其次,筆者認為,卷積核的膨脹係數刻畫了卷積核高和寬方向的擴張倍數,可將上述公式看作:

膨脹的卷積核尺寸 - 1 = 膨脹係數 * (原始卷積核尺寸 - 1)

   到此為止,卷積核膨脹就解析完畢了,通過對卷積核膨脹的理解,筆者感受到,在閱讀原始碼不明白時,尤其在對卷積這種抽象的操作中的細節不明白時,可以動筆畫一畫,將不形象轉為形象,問題就迎刃而解了!