SPP-Net網絡結構分析
Author:Mr. Sun
Date:2019.03.18
Loacation: DaLian university of technology
論文名稱:《Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition》
摘要:
我們之前學習了基於深度學習進行目標檢測的R-CNN算法,它雖然是一個開創性的理論,但是本身存在很多缺點,是有很多可以改進的地方的。本篇研究的Paper是何愷明大神在2014年發表的。這篇Paper最大的創新點在於提出了空間金字塔池化(Spatial Pyramid Pooling),簡稱SPP-Net。這個算法(algorithm)比R-CNN算法的速度快了很多倍(20-104倍)。我們知道在現有的CNN中,對於結構已經確定的網絡,需要輸入一張固定大小的圖片,比如224*224、32*32、96*96等。這樣對於我們希望檢測各種大小的圖片的時候,需要經過裁剪(Crop),或者縮放等一系列操作,這樣往往會降低識別檢測的精度,於是paper提出了“空間金字塔池化”方法,這個算法的牛逼之處,在於使得我們構建的網絡,可以輸入任意大小的圖片,不需要經過裁剪縮放等操作,只要你喜歡,任意大小的圖片都可以。不僅如此,這個算法用了以后,精度也會有所提高,總之一句話:不得不學的一篇論文。
1、為什么提出SPP-Net?
R-CNN的候選框是通過Selective search方法得到的,一張圖片大概有2000個Region Proposals,然后通過crop/warp進行處理(歸一化),將每個Region Proposal送入CNN中進行卷積特征的提取,這樣導致:
(1)訓練時間非常慢,因為一張圖片產生2000個Region Proposals,都會送入CNN中進行訓練;
(2)識別准確率很低,因為產生的Region Proposals都會通過crop/warp操作,resize到同一大小送入CNN中進行訓練,這樣會造成圖片信息的缺失或者變形失真,會降低圖片識別的正確率。
如何解決上述兩個存在的問題呢?
(a)共享卷積計算:將一張完整圖片整體送入CNN中進行特征提取,然后將一張圖片的多個Region Proposals映射到最后的特征層上,形成每個Region Proposal的Feature Maps,進而加速特征的提取;
(b)設計可以處理任意尺度的圖像的結構:不同尺寸的Feature Map,映射稱同樣大小的特征。這個結構就是空間金字塔池化。
在傳統的CNN網絡和物體檢測相關的文章中,比如R-CNN,他們都要求輸入固定大小的圖片,這些圖片或者經過裁切(Crop)或者經過變形縮放(Warp),都在一定程度上導致圖片信息的丟失和變形,限制了識別精確度。兩種方式如下圖1所示。
圖1:圖像尺寸處理方式
究竟是什么原因導致卷積網絡中必須要固定輸入圖片的尺寸呢?
(a)首先我們來看卷積操作:卷積操作對圖片輸入的大小會有要求嗎?比如一個5*5的卷積核,我輸入的圖片是30*30的大小,可以得到(26,26)大小的圖片(這里並沒有進行padding,且stride=1),並不會影響卷積操作。我輸入600*500,它還是照樣可以進行卷積,也就是卷積對圖片輸入大小沒有要求,只要你喜歡,任意大小的圖片進入,都可以進行卷積。
(b)再來看看池化操作:池化對圖片大小會有要求嗎?比如我池化大小為(2,2)我輸入一張30*40的,那么經過池化后可以得到15*20的圖片。輸入一張53*22大小的圖片,經過池化后,我可以得到26*11大小的圖片。因此池化這一步也沒對圖片大小有要求。只要你喜歡,輸入任意大小的圖片,都可以進行池化。
(c)最后看全連接層:既然池化和卷積都對輸入圖片大小沒有要求,那么就只有全連接層對圖片結果又要求了。因為全連接層我們的連接勸值矩陣的大小W,經過訓練后,就是固定的大小了,比如我們從卷積到全連層,輸入和輸出的大小,分別是50、30個神經元,那么我們的權值矩陣(50,30)大小的矩陣了。因此空間金字塔池化,要解決的就是從卷積層到全連接層之間的一個過度。
總之,卷積層的參數和輸入圖像的尺寸無關,它僅僅是一個卷積核在圖像上滑動,不管輸入圖像是多少都沒關系,只是對不同大小的圖片卷積出不同大小的特征圖,池化層對輸入圖像的尺寸也沒有任何限制,只是獲得不同的特征圖而已,但是全連接層的參數就和輸入圖像大小有關,因為它要把輸入的所有像素點連接起來,需要指定輸入層神經元個數和輸出層神經元個數,所以需要規定輸入的feature的大小。
因此,對於圖片限制其長度的根源是全連接層。以下圖為例說明:
作為全連接層,如果輸入的x維數不等,那么參數w肯定也會不同,因此,全連接層是必須是固定的輸入,輸出個數的。
2、SPP-Net的網絡結構
SPP-Net在最后一個卷積層后,接入了金字塔池化層,使用這種方式,可以讓網絡輸入任意的圖片,而且還會生成固定大小的輸出。
3、什么是空間金字塔池化(Spatial Pyramid Pooling )?
在講解什么是空間金字塔池化之前,我們先從空間金字塔特征說起(這邊先不考慮池化)。如下圖4中,當我們輸入一張圖片的時候,我們利用不同大小的刻度,對一張圖片進行了划分。圖4中,利用了三種不同大小的刻度,對一張輸入的圖片進行了划分,最后總共可以得到16+4+1=21個塊,我們即將從這21個塊中,每個塊提取出一個特征,這樣剛好就是我們要提取的21維特征向量
圖4:空間金字塔的結構
空間金字塔最大池化(SPP-Max)的過程,其實就是從這21個圖片塊中,分別計算每個塊的最大值,從而得到一個輸出特征向量(跟上一層卷積核深度一樣可能是256)。最后把一張任意大小的圖片轉換成了一個固定大小的21維特征(當然你可以設計其它維數的輸出,增加金字塔的層數,或者改變划分網格的大小)。上面的三種不同刻度的划分,每一種刻度我們稱之為金字塔的一層,使用多個不同刻度的層,可以提高我們所提取特征的魯棒性。每一個圖片塊大小我們稱之為:Sliding Windows Size了。如果你希望,金字塔的某一層輸出n*n個特征,那么你就要用Windows Size大小為:(w/n,h/n)進行池化了。具體的SPP過程如下圖5所示:
圖5:SPP的處理過程
空間金字塔池化的意義是什么呢?
當網絡輸入的是一張任意大小的圖片,這個時候我們可以一直進行卷積、池化,直到網絡的倒數幾層的時候,也就是我們即將與全連接層連接的時候,就要使用金字塔池化,使得任意大小的特征圖都能夠轉換成固定大小的特征向量,這就是空間金字塔池化的意義(多尺度特征提取出固定大小的特征向量)。
空間金字塔池化的的公式是什么呢?
根據空間金字塔池化的意義可以知道,無論最后一個卷積層得到的feature maps的大小是怎樣的,我們都將其轉化為了(4*4+2*2+1*1)*256的全連接層,不過需要注意的是,可以轉化的條件是雖然這些feature maps的大小不同,但是feature maps的channels相同(這里為256),那么如何將不同大小的feature maps進行spp 呢?論文中給出的空間金字塔的三個Sliding Window Size 和 strides如下圖6所示:
圖6:論文中空間金字塔的三個池化窗口的尺寸
接下來我們來具體分析一下在空間金字塔中不同尺度的池化窗口和步長的計算方法:
假設輸入的大小為a*a*c(最后一個卷積核的特征),然后,將這 c 個 a * a 的Feature Maps分別分成了[1*1,2*2,4*4]大小的塊(這里的分,指的是移動窗口池化),期望的輸出為1*1*c,2*2*c,4*4*c,然后將reshape成(1*1+2*2+4*4)*c的二維數組,具體這兒是采用Max pooling操作來實現的,不過pool層的Sliding Window size和stride是不同的,具體有如下公式: 輸出為[n,n],輸入為[a,a],假設取上整運算為"++()",取下整運算為"--()",那么pool_size=++(n/a),stride=--(n/a),這樣我們就將其轉化為了n*n*c的矩陣,例如(13*13,10*10)都要轉化為(4*4),那么采用[p_s=4,,s=3],[p_s=3,s=2]的池化操作后便可以得到。(這里有一個問題哈,就是如果(7*7)也要得到4*4的話,計算得到的size=2,stride=1,利用公式算出來得到的池化為(6*6)與預期的4*4不符,這里暫時還有問題,不清楚具體原因是什么)。
我在這里有一個很大的疑問?
在做目標檢測的時候,SPP-Net是將Selective Search算法提取的2000個Region Proposal映射到要輸出的最后一個卷積層,大概是要除以所有步長的乘積,如果Region Proposal很小,映射之后的特征更小(比如3*3),這樣的話我們該如何應用(4*4)的金字塔池化呢?到底作者是怎么處理這種情況的呢?現在還是不能很好的理解這一點。
空間金字塔池化的代碼分析:
代碼返回的是SPP Layer之后要輸出的神經元個數,代碼中bins=[1,2,3],經過處理之后就可以得到對應的(1*1+2*2+3*3)*256=14*256=3584個神經元,即無論前面的Feature Map是多大的,經過SPP Layer處理之后得到固定大小的神經元,然后就可以和全連接層進行矩陣運算了!
1 import tensorflow as tf 2 import math 3 4 class SPPLayer(): 5 def __init__(self,bins,feature_map_size): 6 self.strides = [] 7 self.filters = [] 8 # print(type(feature_map_size)) 9 self.a = float(feature_map_size) 10 self.bins = bins 11 self.n = len(bins) 12 13 def spatial_pyramid_pooling(self,data): 14 self.input = data 15 self.batch_size = self.input.get_shape().as_list()[0] 16 for i in range(self.n): 17 x = int(math.floor(self.a/float(self.bins[i]))) 18 self.strides.append(x) 19 x = int (math.ceil(self.a/float(self.bins[i]))) 20 self.filters.append(x) 21 22 self.pooled_out = [] 23 for i in range(self.n): 24 self.pooled_out.append(tf.nn.max_pool(self.input, 25 ksize=[1, self.filters[i], self.filters[i], 1], 26 strides=[1, self.strides[i], self.strides[i], 1], 27 padding='VALID')) 28 29 for i in range(self.n): 30 self.pooled_out[i] = tf.reshape(self.pooled_out[i], [self.batch_size, -1]) 31 32 self.output = tf.concat(1, [self.pooled_out[0], self.pooled_out[1], self.pooled_out[2]]) 33 34 return self.output
4、SPP-Net網絡的訓練方式(單尺度和多尺度)
Paper中將網絡的訓練分為兩種:一種是Single-size,一種是Multi-size。
先講解single-size的訓練過程:
從理論上講,SPP-Net支持直接以多尺度的原始圖片作為輸入后直接反向傳播(Backpropagation)。實際上(Actually),Caffe等實現中,為了計算的方便,GPU,CUDA等比較適合固定尺寸的輸入,所以訓練的時候輸入的圖片是固定了尺度了的。以224*224的輸入為例:在Conv5之后的特征圖為:13x13*256(a*a*c);金字塔層bins: n*n;將pooling層作為Sliding window pooling。Windows_size=[a/n] 向上取整 , Stride_size=[a/n]向下取整。得到的特征是n*n個特征向量(向量的長度是256)。對於pool 3*3: Windows_size=5 的計算公式是:[13/3]向上取整=5 ,Stride_size= 4的計算公式是:[13/3]向下取整。如果輸入改成180x180,這時候Conv5出來的Reponse map為10x10,類似的方法,能夠得到新的pooling參數(滑動池化窗口的大小和步長)。
對於Multi-size training訓練:
使用兩個尺度進行訓練:224*224 和180*180,訓練的時候,224x224的圖片通過Crop得到,180x180的圖片通過縮放224x224的圖片得到。之后,迭代訓練,即用224的圖片訓練一個epoch,之后180的圖片訓練一個epoch,交替地進行。
兩種尺度下,在SSP后,輸出的特征維度都是(9+4+1)x256,參數是共享的,之后接全連接層即可。作者在論文中說,這樣訓練的好處是一樣的收斂速度,模型的測試精度卻提高了。