1. 程式人生 > 實用技巧 >理解 Deformable Convolutional Networks

理解 Deformable Convolutional Networks

理解 Deformable Convolutional Networks

Feng Nie AI Scientist

1 空洞卷積

1.1 理解空洞卷積

在影象分割領域,影象輸入到CNN(典型的網路比如FCN)中,FCN先像傳統的CNN那樣對影象做卷積再pooling,降低影象尺寸的同時增大感受野,但是由於影象分割預測是pixel-wise的輸出,所以要將pooling後較小的影象尺寸upsampling到原始的影象尺寸進行預測,之前的pooling操作使得每個pixel預測都能看到較大感受野資訊。因此影象分割FCN中有兩個關鍵,一個是pooling減小影象尺寸增大感受野,另一個是upsampling擴大影象尺寸。在先減小再增大尺寸的過程中,肯定有一些資訊損失掉了,那麼能不能設計一種新的操作,不通過pooling也能有較大的感受野看到更多的資訊

呢?答案就是dilated conv。

(a) 普通卷積,1-dilated convolution,卷積核的感受野為3×3

(b) 擴張卷積,2-dilated convolution,卷積核的感受野為7×7

(c) 擴張卷積,4-dilated convolution,卷積核的感受野為15×15

(a)圖對應3x3的1-dilated conv,和普通的卷積操作一樣.

(b)圖對應3x3的2-dilated conv,實際的卷積kernel size還是3x3,但是空洞為1,也就是對於一個7x7的影象patch,只有9個紅色的點和3x3的kernel發生卷積操作,其餘的點略過也可以理解為kernel的size為7x7,但是隻有圖中的9個點的權重不為0,其餘都為0

。 可以看到雖然kernel size只有3x3,但是這個卷積的感受野已經增大到了7x7(如果考慮到這個2-dilated conv的前一層是一個1-dilated conv 的話,那麼每個紅點就是1-dilated的卷積輸出,所以感受野為3x3,所以1-dilated和2-dilated合起來就能達到7x7的conv).

(c)圖是4-dilated conv操作,同理跟在兩個1-dilated和2-dilated conv的後面,能達到15x15的感受野。對比傳統的conv操作,3層3x3的卷積加起來,stride為1的話,只能達到(kernel-1)*layer+1=7的感受野,也就是和層數layer成線性關係,而dilated conv的感受野是指數級的增長

擴張卷積與普通的卷積相比,除了卷積核的大小以外,還有一個擴張率(dilation rate)引數,主要用來表示擴張的大小。擴張卷積與普通卷積的相同點在於,卷積核的大小是一樣的,在神經網路中即引數數量不變,區別在於擴張卷積具有更大的感受野。

擴展卷積在保持引數個數不變的情況下增大了卷積核的感受野,同時它可以保證輸出的特徵對映(feature map)的大小保持不變。一個擴張率為2的3×3卷積核,感受野與5×5的卷積核相同,但引數數量僅為9個,是5×5卷積引數數量的36%。

dilated的好處是不做pooling損失資訊的情況下,加大了感受野,讓每個卷積輸出都包含較大範圍的資訊。在影象需要全域性資訊或者語音文字需要較長的sequence資訊依賴的問題中,都能很好的應用dilated conv。

1.2 Deconv和dilated conv的區別:

deconv的具體解釋可參見如何理解深度學習中的deconvolution networks?,deconv的其中一個用途是做upsampling,即增大影象尺寸。而dilated conv並不是做upsampling,而是增大感受野。可以形象的做個解釋:對於標準的k*k卷積操作,stride為s,分三種情況:

(1) s>1,即卷積的同時做了downsampling,卷積後圖像尺寸減小;

(2) s=1,普通的步長為1的卷積,比如在tensorflow中設定padding=SAME的話,卷積的影象輸入和輸出有相同的尺寸大小;

(3) 0<s<1,fractionally strided convolution,相當於對影象做upsampling。比如s=0.5時,意味著在影象每個畫素之間padding一個空白的畫素後stride改為1做卷積,得到的feature map尺寸增大一倍。而dilated conv不是在畫素之間padding空白的畫素,而是在已有的畫素上,skip掉一些畫素,或者輸入不變,對conv的kernel引數中插一些0的weight,達到一次卷積看到的空間範圍變大的目的。當然將普通的卷積stride步長設為大於1,也會達到增加感受野的效果,但是stride大於1就會導致downsampling,影象尺寸變小。

2 Deformable convolution

文章提出了可變卷積和可變ROI取樣。原理是一樣的,就是在這些卷積或者ROI取樣層上,添加了位移變數,這個變數根據資料的情況學習,偏移後,相當於卷積核每個方塊可伸縮的變化,從而改變了感受野的範圍,感受野成了一個多邊形。

上圖是在二維平面上deformable convolution和普通的convolution的描述圖。(a)是普通的卷積,卷積核大小為3*3,取樣點排列非常規則,是一個正方形。(b)是可變形的卷積,給每個取樣點加一個offset(這個offset通過額外的卷積層學習得到),排列變得不規則。(c)和(d)是可變形卷積的兩種特例。對於(c)加上offset,達到尺度變換的效果;對於(d)加上offset,達到旋轉變換的效果。

如上圖所示,有一個額外的conv層來學習offset,共享input feature maps。然後input feature maps和offset共同作為deformable conv層的輸入,deformable conv層操作取樣點發生偏移,再進行卷積。

下面是 MXNet 中關於 deformable conv 定義的程式碼,可以看到,首先有一個額外的channel為72(對應著3X3的kernel size , 每個點會有 X 方向 和 Y 方向的偏移,(x,y)組合起來就對應著一個方向向量,3*3 =9 個畫素點則需要 18 個output channel, 這樣的 offset 需要預測4個group,因此最後的輸出channel 是72) 的傳統卷積學習offset(偏移量),然後前面的input feature maps和offset共同作為deformable conv層的輸入,deformable conv層操作取樣點發生偏移,再進行卷積。

res5a_branch2a_relu = mx.symbol.Activation(name='res5a_branch2a_relu', data=scale5a_branch2a, act_type='relu')
res5a_branch2b_offset = mx.symbol.Convolution(name='res5a_branch2b_offset', data = res5a_branch2a_relu,
                                              num_filter=72, pad=(2, 2), kernel=(3, 3), stride=(1, 1), dilate=(2, 2), cudnn_off=True)
res5a_branch2b = mx.contrib.symbol.DeformableConvolution(name='res5a_branch2b', data=res5a_branch2a_relu, offset=res5a_branch2b_offset,
                                                         num_filter=512, pad=(2, 2), kernel=(3, 3), num_deformable_group=4,
                                                         stride=(1, 1), dilate=(2, 2), no_bias=True)

<1> 先用一個(2*3*3 = 18)的傳統卷積預測每個點的offset,這裡18是因為每個點的offset是一個二維向量(確定方向),一共有3*3=9個點,故channel為18;

<2> 根據算出來的offset,計算新的9個點在特徵圖的上的值,由於可能算出來的offset為(0.3, 0.5)這種小數,也就是可能會需要知道特徵圖上(3.3,4.5)位置的值,所以作者用雙線性差值計算這些經過offset修正的位置的響應;

<3> 最後就是與卷積核卷積得到最終的值。

3 Deformable RoI pooling

RoI pooling是把不同大小的RoI(w*h)對應的feature map 統一到固定的大小(k x k);可形變RoI pooling則是先對RoI對應的每個bin按照RoI的長寬比例的倍數進行整體偏移(同樣偏移後的位置是小數,使用雙線性差值來求),然後再pooling。

由於按照RoI長寬比例進行水平和豎直方向偏移,因此每一個bin的偏移量只需要一個引數來表示,具體可以用全連線來實現。

如上圖所示,RoI被分為3*3個bin,被輸入到一個額外的fc層來學習offset,然後通過一個deformable RoI pooling層來操作使每個bin發生偏移。

def get_deformable_roipooling(self, name, data, rois, output_dim, spatial_scale, param_name, group_size=1, pooled_size=7,
                              sample_per_part=4, part_size=7):
    offset = mx.contrib.sym.DeformablePSROIPooling(name='offset_' + name + '_t', data=data, rois=rois, group_size=group_size, pooled_size=pooled_size,
                                                   sample_per_part=sample_per_part, no_trans=True, part_size=part_size, output_dim=output_dim,
                                                   spatial_scale=spatial_scale)
    offset = mx.sym.FullyConnected(name='offset_' + name, data=offset, num_hidden=part_size * part_size * 2, lr_mult=0.01,
                                   weight=self.shared_param_dict['offset_' + param_name + '_weight'], bias=self.shared_param_dict['offset_' + param_name + '_bias'])
    offset_reshape = mx.sym.Reshape(data=offset, shape=(-1, 2, part_size, part_size), name='offset_reshape_' + name)
    output = mx.contrib.sym.DeformablePSROIPooling(name='deformable_roi_pool_' + name, data=data, rois=rois, trans=offset_reshape, group_size=group_size,
                                                   pooled_size=pooled_size, sample_per_part=sample_per_part, no_trans=False, part_size=part_size, output_dim=output_dim,
                                                   spatial_scale=spatial_scale, trans_std=0.1)
    return output

事實上,可變形卷積單元中增加的偏移量是網路結構的一部分,通過另外一個平行的標準卷積單元計算得到,進而也可以通過梯度反向傳播進行端到端的學習。加上該偏移量的學習之後,可變形卷積核的大小和位置可以根據當前需要識別的影象內容進行動態調整,其直觀效果就是不同位置的卷積核取樣點位置會根據影象內容發生自適應的變化,從而適應不同物體的形狀、大小等幾何形變,

上圖展示了兩層的3*3卷積層的對映。對於標準的卷積,後面層的feature map上的一個點,對映到前面層所對應的感受野是規則的,無法考慮到不同目標的外形、大小不同;而可變形的卷積則考慮到了目標的形變,對映到前面層的取樣點大多會覆蓋在目標上面,取樣到更多我們感興趣的資訊。

上圖是可變形卷積取樣點的一個視覺化。三張圖片為一組,綠點表示啟用點,紅點表示啟用點對映到原圖的取樣點,三張圖片分別對應背景、小目標和大目標的取樣點視覺化。

[1]github原始碼Deformable-ConvNets

[2]論文Deformable Convolutional Networks

編輯於 2018-12-29 目標檢測 計算機視覺 CVPR