引言
先簡單回顧一下R-CNN的問題,每張圖片,通過 Selective Search 選擇2000個建議框,通過變形,利用CNN提取特征,這是非常耗時的,而且,形變必然導致信息失真,最終影響模型的性能。
由此引出了一系列問題
問題1:形變耗時又損失信息,為什么要形變
很簡單,因為CNN的輸入必須是固定尺寸。
問題2:為什么CNN的輸入必須固定尺寸
CNN主要由兩部分組成,卷積層和全連接層,卷積層可以接受任意尺寸的圖像,只是不同的輸入卷積后的特征圖尺寸不同,而全連接必須是固定的輸入,所以任意尺寸生成了不同的特征圖,不符合全連接的輸入,由此我們發現,CNN固定輸入的需求完全來自於全連接層。
SPPNet 誕生
何凱明,中國人,有興趣可以搜一下,曾發明ResNet,SPPNet也出自他手。
既然只有全連接需要固定輸入,那么能否在全連接前面加上一個網絡層,使得卷積的不同輸出被轉化成固定尺寸呢?
何凱明團隊發明了空間金字塔池化(spatial pyramid pooling,SPP)層來解決這個問題。
SPP層放在最后一個卷積層后面,對特征圖進行池化操作,並產生固定長度的輸出,喂給全連接層。
網絡結構如下
這個網絡就叫做SPPNet。
這種方法不僅解決了形變的問題,還有一個有意思的說法,就是通過裁剪或者縮放的形變使得信息在一開始就被暴力的刪減,可能損失有用信息,而SPP是在卷積之后,對信息的一種匯總,放棄無效信息,這有助於提高模型的精度,作者也通過實驗證明了這個觀點。
空間金字塔池化
SPP其實借鑒了傳統圖像處理的方法SPM,SPM主要思路是把圖像分成不同尺度的一些塊,比如一幅圖像分成1份、4份、8份等,然后對每塊提取特征后融合在一起,得到多個尺度的特征。
SPPNet首次將這種思想應用到CNN中,思路如出一轍
黑色代表特征映射圖
把不同尺寸的特征映射圖分為1份、4份、16份,然后在每個塊上進行最大池化,池化后的特征拼接到一起,形成固定輸出
最終生成1+4+16=21個特征
這里我們用符號表示,輸出特征數為MK,M=#bins,總塊數,K=#filters,卷積核個數,
上例中MK=21x256,注意這里只是舉例,實際中M、K根據實際情況確定。
卷積層特征圖
為了便於理解空間金字塔池化在做什么,作者可視化了卷積后的特征圖
SPPNet通過可視化conv5層特征,發現卷積其實保留了空間位置信息,如車窗和車輪卷積后還在對應位置,而且每一個卷積核負責提取不同的特征,如filter#175負責提取車窗特征,(長得像車窗,並不一定是車窗)
filter#55負責提取車輪的特征,(長得像車輪,並不一定是車輪),最終融合的就是這些特征。
SPP誤區
這里記錄一下我在學習SPPNet時犯的一個錯誤。
根據上面講的SPP方法,我們可能認為是這樣做的
假如分成4塊,pool2x2,那么是對每個小方塊池化
其實不然
也可以是這樣池化,每種顏色為一次池化,最終也是4塊,pool2x2
由此我們發現分成幾塊 pool n*n,跟我們的池化野沒有關系,池化野可以是任意的合理尺寸,只要最終能生成 n*n 塊就行了。
SPPNet 的訓練
好了,現在我們可以輸入任意尺寸圖片,然后卷積,空間金字塔池化,固定輸出,全連接,貌似整個網絡沒問題了,但事與願違。
什么問題呢?
因為深度學習框架是需要固定輸入的:我的理解,每次喂給網絡batch個樣本,如果樣本尺寸不一樣,那怎么卷積呢?ok,如果你說一張一張卷積,也可以,但是這樣卷積后的尺寸不同,要分開存儲,而且,GPU是並行計算的,屬於矩陣間的計算,尺寸不同,根本無法存儲在一個矩陣里,何談並行,所以肯定要固定輸入。
那SPPNet 怎么訓練呢?
作者將網絡的訓練分為兩種:Single-size 和 Multi-size
Single-size
單一尺寸訓練,仍然把輸入限制在固定尺寸,只是在卷積之后加上空間金字塔池化層,這個嘗試的目的是開啟多級別池化行為。
難點在於如何根據特征映射圖和金字塔層級來確定池化野和步長
假設卷積后的特征映射圖尺寸為 axa(如13x13),對於 n*n 的金字塔級,要實現一個滑框池化過程,池化野大小為 win=上取整[a/n],步長為 stride=下取整[a/n]
作者展示了3層金字塔池化的例子
這里可以回看我講的SPP誤區,幫助理解,其實是這樣的,以pool3x3為例
可以看到池化完剛好 3x3
不禁有人要問了,feature map 不是正方形怎么辦?這里作者沒講到,自由發揮吧,比如拿0填充成正方形,以后我查到這方面的資料,再補充。
Multi-size
多尺寸訓練,輸入為不同尺寸,並且包含空間金字塔池化,目的是模擬多尺度輸入的訓練。
作者預先設定了2個尺寸,224*224 和 180*180,224通過裁剪得到,180通過縮放得到,
對於輸入為180的網絡,卷積層一樣,空間金字塔池化層設計池化野和步長,接上全連接,
對於輸入為224的網絡,卷積層一樣,空間金字塔池化層設計池化野和步長,接上全連接,
這樣兩個網絡的參數就一樣了,池化不需要參數
在訓練時,我們一個epoch輸入224(或者180)的圖片,訓練參數,保存參數
下一個回合先讀取參數,再輸入180(或者224)的圖片,進行訓練,保存參數
依次交替進行。
這樣的本質是通過共享參數的多個固定輸入的網絡實現了不同尺寸輸入的SPPNet。
到此為止,整個網絡可以正常訓練了。
SPPNet 用於圖像識別
暫時飄過...
SPPNet 用於目標檢測
之前在R-CNN中講到,R-CNN對2000個建議框進行特征提取,每次是個卷積過程,非常耗時,而且這2000個建議框很可能存在重復區域,所以存在重復計算,這是R-CNN一個很大的瓶頸。
SPPNet 除了多尺寸輸入外,也解決了上個問題。
SPPNet 只需要對整張圖做一次卷積,然后直接從特征圖中抽取建議框的特征。
只做一次卷積,效果大大提高,所以SPPNet對目標檢測是個非常大的突破。
Mapping a Window to Feature Maps
之前在卷積層特征圖中講到,卷積仍然保留了空間位置關系,也就是說原圖上的位置與特征映射圖的位置是對應的,之間存在了某種關系,所以可以根據原圖的位置找到對應的特征映射圖的位置,從而得到特征。
具體映射關系是什么呢?這部分要根據實際情況來算,沒什么難的,細心就好,大致方法如下
先定義幾個參數
類型 | 大小 |
---|---|
第![]() |
![]() |
第![]() |
![]() |
第![]() |
![]() |
第![]() |
![]() |
第![]() |
![]() |
輸入尺寸與輸出尺寸的關系
這是整個區域之間的映射。
坐標之間的映射又如何呢?
含義 | 符號 |
---|---|
在i層的坐標值 | ![]() |
i層的步長 | ![]() |
i層的卷積核大小 | ![]() |
i層填充的大小 | padding |
SPPNet對上面的映射關系做了一定的簡化,過程如下:
令padding=ki/2
當 k_i 為奇數 所以
當k_i 為偶數所以
而是坐標值,不可能取小數 所以基本上可以認為
。公式得到了化簡:感受野中心點的坐標
只跟前一層
有關。
從原圖坐標到特征圖中坐標
的映射關系為
- 前面每層都填充padding/2 得到的簡化公式 :
- 需要把上面公式進行級聯得到
其中
- 對於feature map 上的
它在原始圖的對應點為
- 論文中的最后做法:把原始圖片中的ROI映射為 feature map中的映射區域(上圖橙色區域)其中 左上角取:
右下角的點取:界取
的
值:
。

記住做后的結論就好了,過程不重要。
檢測算法
對於檢測算法,論文中是這樣做到:使用ss生成~2k個候選框,縮放圖像min(w,h)=s之后提取特征,每個候選框使用一個4層的空間金字塔池化特征,網絡使用的是ZF-5的SPPNet形式。之后將12800d的特征輸入全連接層,SVM的輸入為全連接層的輸出。
這個算法可以應用到多尺度的特征提取:先將圖片resize到五個尺度:480,576,688,864,1200,加自己6個。然后在map window to feature map一步中,選擇ROI框尺度在{6個尺度}中大小最接近224x224的那個尺度下的feature maps中提取對應的roi feature。這樣做可以提高系統的准確率。
SPPNet VS R-CN
總結

參考資料:
http://www.dengfanxin.cn/?p=403 SPPNet 論文翻譯-空間金字塔池化
https://zhuanlan.zhihu.com/p/27485018
https://blog.csdn.net/v1_vivian/article/details/73275259
https://github.com/peace195/sppnet