RCNN -> Fast-RCNN -> Faster-RCNN

本文轉載自:

http://closure11.com/rcnn-fast-rcnn-faster-rcnn%E7%9A%84%E4%B8%80%E4%BA%9B%E4%BA%8B/


rbg大神的深度神經網絡檢測算法系列RCNN、Fast-RCNN、Faster-RCNN可謂是理論與實踐的經典範例,論文創新點足夠,在github上開源的代碼更是造福廣大碼農,本文以當前最新Faster-RCNN的python實現(https://github.com/rbgirshick/py-faster-rcnn)爲準,嘗試對rcnn系列算法中的幾個關鍵核心點進行詳細的分析:

  • RCNN -> Fast-RCNN -> Faster-RCNN
  • 圖片區域分析核心:ROI Pooling層
  • 對象bbox預測: Bounding-box Regression
  • 用神經網絡輸出proposal:RPN層
  • Faster-RCNN訓練步驟

RCNN -> Fast-RCNN -> Faster-RCNN

這裏不得不先提的就是爲什麼會有RCNN這一系列的檢測算法,以及爲什麼它們會被稱爲深度對象檢測的開山之作,我們知道,在CNN火起來之前,對象檢測這一問題基本是遵循着“設計手工特徵(Hand-crafted feature)+分類器”的思路,而且由於存在着區域搜索的步驟,所以可以認爲是計算機用一個小的矩形窗口不斷在圖像上滑動、縮放,然後用分類器預測當前滑動窗口所在區域是否存在一個感興趣的對象,自從CNN在CV領域流行起來以後,很多人都開始想,既然CNN的特徵比傳統手工特徵好這麼多,那麼爲什麼不用深度神經網絡做檢測呢?

RCNN算法的核心思想就是對每個區域通過CNN提取特徵,然後接上一個分類器預測這個區域包含一個感興趣對象的置信度,也就是說,轉換成了一個圖像分類問題(類似imagenet),後面接的這個分類器可以是獨立訓練的svm也可以是簡單的softmax分類。在RCNN論文裏,作者還提到兩個保證檢測速度的關鍵點:1.所有類別的分類器共享相同的特徵輸入;2.與傳統特徵相比,深度特徵維度一般比較低,比如VGG16裏的4096維。

但是很可惜,即使使用了selective search等預處理步驟來提取潛在的bounding box作爲輸入,但是RCNN仍會有嚴重的速度瓶頸,原因也很明顯,就是計算機對所有region進行特徵提取時會有重複計算,Fast-RCNN正是爲了解決這個問題誕生的,作者提出了一個可以看做單層sppnet的網絡層,叫做ROI Pooling,這個網絡層可以把不同大小的輸入映射到一個固定尺度的特徵向量,而我們知道,conv、pooling、relu等操作都不需要固定size的輸入,因此,在原始圖片上執行這些操作後,雖然輸入圖片size不同導致得到的feature map尺寸也不同,不能直接接到一個全連接層進行分類,但是可以加入這個神奇的ROI Pooling層,對每個region都提取一個固定維度的特徵表示,再通過正常的softmax進行類型識別。另外,之前RCNN的處理流程是先提proposal,然後CNN提取特徵,之後用SVM分類器,最後再做bbox regression,而在Fast-RCNN中,作者巧妙的把bbox regression放進了神經網絡內部,與region分類和併成爲了一個multi-task模型,實際實驗也證明,這兩個任務能夠共享卷積特徵,並相互促進。Fast-RCNN很重要的一個貢獻是成功的讓人們看到了Region Proposal+CNN這一框架實時檢測的希望,原來多類檢測真的可以在保證準確率的同時提升處理速度,也爲後來的Faster-RCNN做下了鋪墊。

Fast-RCNN之後的問題已經非常清晰,就是我們能不能把region proposal部分也放到GPU上?rbg大神給的答案當然又是yes,於是有了Faster-RCNN,出現了一個end-to-end的CNN對象檢測模型。作者提出,網絡中的各個卷積層特徵其實可以用來預測類別相關的region proposal,不需要事先執行諸如selective search之類的算法,但是如果簡單的在前面增加一個專門提proposal的網絡又顯得不夠elegant,所以最終把region proposal提取和Fast-RCNN部分融合進了一個網絡模型,雖然訓練階段仍然要分多步,但是檢測階段非常方便快捷,準確率也與原來的Fast-RCNN相差不多,從此,再也不用擔心region proposal提取耗時比實際對象檢測還多這種尷尬場景了。

ROI Pooling

首先需要介紹RCNN系列裏的一個核心算法模塊,即ROI Pooling。我們知道在ImageNet數據上做圖片分類的網絡,一般都是先把圖片crop、resize到固定的大小(i.e. 224*224),然後輸入網絡提取特徵再進行分類,而對於檢測任務這個方法顯然並不適合,因爲原始圖像如果縮小到224這種分辨率,那麼感興趣對象可能都會變的太小無法辨認。RCNN的數據輸入和SPPNet有點類似,並不對圖片大小限制,而實現這一點的關鍵所在,就是ROI Pooling網絡層,它可以在任意大小的圖片feature map上針對輸入的每一個ROI區域提取出固定維度的特徵表示,保證後續對每個區域的後續分類能夠正常進行。

ROI Pooling的具體實現可以看做是針對ROI區域的普通整個圖像feature map的Pooling,只不過因爲不是固定尺寸的輸入,因此每次的pooling網格大小得手動計算,比如某個ROI區域座標爲 (x1,y1,x2,y2)(x1,y1,x2,y2) ,那麼輸入size爲 (y2y1)(x2x1)(y2−y1)∗(x2−x1) ,如果pooling的輸出size爲 pooled_heightpooled_widthpooled_height∗pooled_width ,那麼每個網格的size爲 y2y1pooled_heightx2x1pooled_widthy2−y1pooled_height∗x2−x1pooled_width ,具體代碼可在roi_pooling_layer.cpp中的Forward_cpu
函數裏找到,比較簡單。

作者並沒有對Backward階段實現CPU代碼,所以只能在roi_pooling_layer.cu中查看,即ROIPoolBackward函數,其具體進行的操作可以用論文裏的一行公式形容,

Lx=rRyr[y pooled x]Ly∂L∂x=∑r∈R∑y∈r[y pooled x]∂L∂y

其中 RR 表示R個輸入ROI區域以及對應的R個輸出feature,x和y分別表示輸入的feature map和輸出的feature,整個公式的意思就是,"During back-propagation, derivatives flow through the RoI pooling layer. The RoI pooling layer's backwards function computes the partial derivative of the loss function with respect to each input variable x by summing over all RoIs that max-pooled x in the forward pass.",另外,由於實際實現是採用的是Max Pooling,因此y pooled x表示“x在該網格區域中最大,然後y被assign到x的值”,而具體每個網格中哪個點的值最大,也是在Forward過程中就已經記錄,存儲在了argmax_data變量裏。

Bounding-box Regression

有了ROI Pooling層其實就可以完成最簡單粗暴的深度對象檢測了,也就是先用selective search等proposal提取算法得到一批box座標,然後輸入網絡對每個box包含一個對象進行預測,此時,神經網絡依然僅僅是一個圖片分類的工具而已,只不過不是整圖分類,而是ROI區域的分類,顯然大家不會就此滿足,那麼,能不能把輸入的box座標也放到深度神經網絡裏然後進行一些優化呢?rbg大神於是又說了"yes"。在Fast-RCNN中,有兩個輸出層:第一個是針對每個ROI區域的分類概率預測, p=(p0,p1,,pK)p=(p0,p1,⋯,pK) ;第二個則是針對每個ROI區域座標的偏移優化, tk=(tkx,tky,tkw,tkh)tk=(txk,tyk,twk,thk) , 0kK0≤k≤K 是多類檢測的類別序號。這裏我們着重介紹第二部分,即座標偏移優化。

假設對於類別 kk∗ ,在圖片中標註了一個groundtruth座標: t=(tx,ty,tw,th)t∗=(tx∗,ty∗,tw∗,th∗) ,而預測值爲 t=(tx,ty,tw,th)t=(tx,ty,tw,th) ,二者理論上越接近越好,這裏定義損失函數:

Lloc(t,t)=i{x,y,w,h}smoothL1(ti,ti)Lloc(t,t∗)=∑i∈{x,y,w,h}smoothL1(ti,ti∗)

其中

smoothL1(x)={0.5x2|x|0.5|x|1otherwisesmoothL1(x)={0.5x2|x|≤1|x|−0.5otherwise

這裏, smoothL1(x)smoothL1(x) 中的x即爲 tititi−ti∗ (感覺前一個公式爲作者筆誤,該寫成 smoothL1(titi)smoothL1(ti−ti∗) ),即對應座標的差距。該函數在 (1,1)(−1,1) 之間爲二次函數,而其他區域爲線性函數,作者表示這種形式可以增強模型對異常數據的魯棒性,整個函數在matplotlib中畫出來是這樣的

smooth

對應的代碼在smooth_L1_loss_layer.cu中。

RPN層

Faster-RCNN最大一點貢獻應該算是其把proposal部分從網絡外邊嵌入了網絡裏邊,從此一個網絡模型即可完成end-to-end的檢測任務而不需要我們在前面手動先執行一遍proposal的搜索算法。其實如果回過頭來看看幾年前比較流行的檢測算法,比如HOG+SVM和DPM什麼的,同樣是需要用分類器逐個對一些矩形框裏提取出來的特徵進行分類,只不過那時是全圖設置好stride、scale等參數然後搜索,不像selective search這些算法會去對圖像進行內容分析,然後輸出一些可疑的矩形候選框。

某種程度上,RPN也可以算作一個全圖搜索的粗檢測器,圖片在輸入網絡後,依次經過一些卷積、池化層,然後得到的feature map被手動劃分爲 n×nn×n 個矩形窗口(論文中n=3),準備後續用來選取proposal,並且此時座標依然可以映射回原圖。需要注意兩點問題:1.在到達全連接層之前,卷積層和Pooling層對圖片輸入大小其實沒有size的限制,因此RCNN系列的網絡模型其實是不需要實現把圖片resize到固定大小的;2.n=3看起來很小,但是要考慮到這是非常高層的feature map,其size本身也沒有多大,因此 3×33×3 9個矩形中,每個矩形窗框都是可以感知到很大範圍的。

在劃分爲 n×nn×n 個窗口後,我們把每個矩形窗口的中心點當成一個基準點,然後圍繞這個基準點選取k(k=9)個不同scale、aspect ratio的anchor(論文中3個scale和3個aspect ratio),對於每個anchor,首先在後面接上一個二分類softmax,有2個score輸出用以表示其是一個物體的概率與不是一個物體的概率,然後再接上一個bounding box的regressor,以及4個座標輸出代表這個anchor的座標位置,因此RPN的總體Loss函數可以定義爲:

L({pi}{ti})=1NclsiLcls(pi,pi)+λ1NregipiLreg(ti,ti)L({pi}{ti})=1Ncls∑iLcls(pi,pi∗)+λ1Nreg∑ipi∗Lreg(ti,ti∗)

這個公式裏的 LregLreg 即爲上面提到的 smoothL1smoothL1 函數,而該項前面的 pipi∗ 表示這些regressor的loss指針對正樣本而言,負樣本的預測會直接捨去。

另外在RPN訓練中有一個需要注意的地方是正負樣本的選擇,文中提到如果對每幅圖的所有anchor都去優化loss function,那麼最終會因爲負樣本過多導致最終得到的模型對正樣本預測準確率很低(It is possible to optimize for the loss functions of all anchors, but this will bias towards negative samples as they are dominate)。

Faster-RCNN訓練步驟

說完了Fast-RCNN和RPN,現在是時候來講Faster-RCNN最精華的部分了,也就是如何把這兩者放在同一個網絡結構中,如何訓練出這樣一個Multi-task的網絡模型。

我們知道,如果是分別訓練兩種不同任務的網絡模型,即使它們的結構、參數完全一致,但各自的卷積層內的卷積核也會向着不同的方向改變,導致無法共享網絡權重,Faster-RCNN提出了三種可能的方式:

  1. Alternating training:此方法其實就是一個不斷迭代的訓練過程,既然分別訓練RPN和Fast-RCNN可能讓網絡朝不同的方向收斂,那麼我們可以先獨立訓練RPN,然後用這個RPN的網絡權重對Fast-RCNN網絡進行初始化,並且用之前RPN輸出proposal作爲此時Fast-RCNN的輸入,之後不斷迭代這個過程,即循環訓練RPN、Fast-RCNN。
  2. Approximate joint training:這裏與前一種方法不同,不再是串行訓練RPN和Fast-RCNN,而是嘗試把二者融入到一個網絡內,具體融合的網絡結構如下圖所示,可以看到,proposals是由中間的RPN層輸出的,而不是從網絡外部得到。需要注意的一點,名字中的"approximate"是因爲“this solution ignores the derivative w.r.t. the proposal boxes' coordinates that are also network responses”,也就是說,反向傳播階段RPN產生的cls score能夠獲得梯度用以更新參數,但是proposal的座標預測則直接把梯度捨棄了,這個設置可以使backward時該網絡層能得到一個解析解(closed results),並且相對於Alternating traing減少了25-50%的訓練時間。
  3. Non-approximate training:上面的Approximate joint training把proposal的座標預測梯度直接捨棄,所以被稱作approximate,那麼理論上如果不捨棄是不是能更好的提升RPN部分網絡的性能呢?作者把這種訓練方式稱爲“ Non-approximate joint training”,但是此方法在paper中只是一筆帶過,表示“This is a nontrivial problem and  a solution can be given by an “RoI warping” layer as developed in [15], which is beyond the scope of this paper”,o(╯□╰)o

Approximate joint trainingApproximate joint training

上面說完了三種可能的訓練方法,可非常神奇的是作者發佈的源代碼裏卻傲嬌的用了另外一種叫做4-Step Alternating Training的方法,思路和迭代的Alternating training有點類似,但是細節有點差別(rbg大神這樣介紹訓練方式我也是醉了),具體來說:

  1. 第一步:用ImageNet模型初始化,獨立訓練一個RPN網絡;
  2. 第二步:仍然用ImageNet模型初始化,但是使用上一步RPN網絡產生的proposal作爲輸入,訓練一個Fast-RCNN網絡,至此,兩個網絡每一層的參數完全不共享;
  3. 第三步:使用第二步的Fast-RCNN網絡參數初始化一個新的RPN網絡,但是把RPN、Fast-RCNN共享的那些卷積層的learning rate設置爲0,也就是不更新,僅僅更新RPN特有的那些網絡層,重新訓練,此時,兩個網絡已經共享了所有公共的卷積層;
  4. 第四步:仍然固定共享的那些網絡層,把Fast-RCNN特有的網絡層也加入進來,形成一個unified network,繼續訓練,fine tune Fast-RCNN特有的網絡層,此時,該網絡已經實現我們設想的目標,即網絡內部預測proposal並實現檢測的功能。

總結

至此,關於RCNN系列檢測算法的關鍵部分已經全部介紹完畢,但應該還有很多真正的細節問題無法涉及到(其實也是因爲好多細節實現我還沒看o(╯□╰)o),從我個人感受而言,rbg真心碉堡,算是難得一個既能寫paper又能coding的神人,而且最重要的一點,我的CVPR 2016論文裏自己弄的一個數據集就藉助了Fast-RCNN,否則應該沒有可能寫出這篇paper,在此再次跪謝rbg大神開放這麼優秀的源代碼造福我等低端代碼搬運工!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章