從結構、原理到實現,Faster R-CNN全解析(原創)
論文鏈接 Faster R-CNN Towards Real-Time Object:
https://arxiv.org/pdf/1506.01497.pdftensorflow源碼鏈接:https://github.com/smallcorgi/Faster-RCNN_TFFaster R-CNN是目標檢測界的大神Ross Girshick 2015年提出的一個很經典的檢測結構,它將傳統的Selective Search提取目標的方法替換成網路訓練來實現,使得全流程的檢測、分類速度大幅提升。
圖1是Faster R-CNN的基本結構,由以下4個部分構成:
1、特徵提取部分:用一串卷積+pooling從原圖中提取出feature map;2、RPN部分:這部分是Faster R-CNN全新提出的結構,作用是通過網路訓練的方式從feature map中獲取目標的大致位置;3、Proposal Layer部分:利用RPN獲得的大致位置,繼續訓練,獲得更精確的位置;4、ROI Pooling部分:利用前面獲取到的精確位置,從feature map中摳出要用於分類的目標,並pooling成固定長度的數據;一、特徵提取部分
特徵提取部分就是圖1中輸入圖片和feature map間的那一串卷積+pooling,這部分和普通的CNN網路中特徵提取結構沒有區別,可以用VGG、ResNet、Inception等各種常見的結構實現(只使用全連接層之前的部分),這部分不再詳述。
二、RPN部分
目標識別有兩個過程:首先你要知道目標在哪裡,要從圖片中找出要識別的前景,然後才是拿前景去分類。在Faster R-CNN提出之前常用的提取前景(本文稱為提取proposal)的方法是Selective Search,簡稱SS法,通過比較相鄰區域的相似度來把相似的區域合併到一起,反覆這個過程,最終就得到目標區域,這種方法相當耗時以至於提取proposal的過程比分類的過程還要慢,完全達不到實時的目的;到了Faster R-CNN時,作者就想出把提取proposal的過程也通過網路訓練來完成,部分網路還可以和分類過程共用,新的方法稱為Reginal Proposal Network(RPN),速度大大提升。
圖2粉色框內就是RPN,它做兩件事:1、把feature map分割成多個小區域,識別出哪些小區域是前景,哪些是背景,簡稱RPN Classification,對應粉色框中上半分支;2、獲取前景區域的大致坐標,簡稱RPN bounding box regression,對應下半分支;1、RPN Classification
RPN Classification的過程就是個二分類的過程。先要在feature map上均勻的劃分出KxHxW個區域(稱為anchor,K=9,H是feature map的高度,W是寬度),通過比較這些anchor和ground truth間的重疊情況來決定哪些anchor是前景,哪些是背景,也就是給每一個anchor都打上前景或背景的label。有了labels,你就可以對RPN進行訓練使它對任意輸入都具備識別前景、背景的能力。
在圖2上半分支可以看到rpn_cls_score_reshape模塊輸出的結構是[1,9*H,W,2],就是9xHxW個anchor二分類為前景、背景的概率;anchor_target_layer模塊輸出的是每一個anchor標註的label,拿它和二分類概率一比較就能得出分類的loss。一個feature map有9xHxW個anchor,就是說每個點對應有9個anchor,這9個anchor有1:1、1:2、2:1三種長寬比,每種長寬比都有三種尺寸(見圖3)。一般來說原始輸入圖片都要縮放到固定的尺寸才能作為網路的輸入,這個尺寸在作者源碼里限制成800x600,9種anchor還原到原始圖片上基本能覆蓋800x600圖片上各種尺寸的坐標。要注意的是在實際應用時並不是把全部HxWx9個anchor都拿來做label標註,這裡面有些規則來去除效果不好的anchor,具體的規則如下:
1、覆蓋到feature map邊界線上的anchor不參與訓練;2、前景和背景交界地帶的anchor不參與訓練。這些交界地帶即不作為前景也不作為背景,以防出現錯誤的分類。在作者原文里把IOU>0.7作為標註成前景的門限,把IOU<0.3作為標註成背景的門限,之間的值就不參與訓練,IOU是anchor與ground truth的重疊區域佔兩者總覆蓋區域的比例,見示意圖4;3、訓練時一個batch的樣本數是256,對應同一張圖片的256個anchor,前景的個數不能超過一半,如果超出,就隨機取128個做為前景,背景也有類似的篩選規則;2、RPN bounding box regression
RPN bounding box regression用於得出前景的大致位置,要注意這個位置並不精確,準確位置的提取在後面的Proposal Layer bounding box regression章節會介紹。提取的過程也是個訓練的過程,前面的RPN classification給所有的anchor打上label後,我們需用一個表達式來建立anchor與ground truth的關係,假設anchor中心位置坐標是[Ax, Ay],長高為Aw和Ah,對應ground truth的4個值為[Gx,Gy,Gw,Gh],他們間的關係可以用公式1來表示。[dx(A), dy(A), dw(A), dh(A)]就是anchor與ground truth之間的偏移量,由公式1可以推導出公式2,這裡用對數來表示長寬的差別,是為了在差別大時能快速收斂,差別小時能較慢收斂來保證精度:
有了這4個偏移量,你就可以拿他們去訓練圖2 RPN中下面一個分支的輸出。完成訓練後RPN就具備識別每一個anchor到與之對應的最優proposal偏移量的能力([dx(A), dy(A), dw(A), dh(A)]),換個角度看就是得到了所有proposal的位置和尺寸。要注意的是如果一個feature map中有多個ground truth,每個anchor只會選擇和它重疊度最高的ground truth來計算偏移量。
3、RPN的loss計算
RPN訓練時要把RPN classification和RPN bounding box regression的loss加到一起來實現聯合訓練。公式3中Ncls是一個batch的大小256,Lcls(pi, pi*)是前景和背景的對數損失,pi是anchor預測為目標的概率,就是前面rpn_cls_score_reshape輸出的前景部分score值,pi*是前景的label值,就是1,將一個batch所有loss求平均就是RPN classification的損失;公式3中Nreg是anchor的總數,λ是兩種 loss的平衡比例,ti是圖2中rpn_bbox_pred模塊輸出的[dx(A), dy(A), dw(A), dh(A)],t*i是訓練時每一個anchor與ground truth間的偏移量,t*i與ti用smooth L1方法來計算loss就是RPN bounding box regression的損失:
關於Smooth L1的原理,請參考:
http://pages.cs.wisc.edu/~gfung/GeneralL1/L1_approx_bounds.pdf
三、Proposal Layer部分
得到proposal大致位置後下一步就是要做準確位置的回歸了。在RPN的訓練收斂後我們能得到anchor相對於proposal的偏移量[dx(A), dy(A), dw(A), dh(A)](要注意這裡是想對於proposal的,而不是相對於ground truth的),有了偏移量再根據公式1就能算出proposal的大致位置。在這個過程中HxWx9個anchor能算出HxWx9個proposal,大多數都是聚集在ground truth周圍的候選框,這麼多相近的proposal完全沒必要反而增加了計算量,這時就要用一些方法來精選出最接近ground truth的proposal,Ross Girshick給了三個步驟:
1、先選出前景概率最高的N個proposal;2、做非極大值抑制(NMS)
3、NMS後再次選擇前景概率最高的M個proposal;經歷這三個步驟後能夠得到proposal的大致位置,但這還不夠,為了得到更精確的坐標,你還要利用公式2再反推出這個大致的proposal和真實的ground truth間還有多少偏移量,對這個新的偏移量再來一次回歸才是完成了精確的定位。上面的過程比較繞,反覆在偏移量、anchor、ground truth間切換,下面的示意圖可以加深理解:proposal精確位置回歸時計算loss的公式和公式3中RPN bounding box regression的loss計算方法完全相同,也用smooth L1方法。
四、ROI Pooling部分
結構篇:
ROI Pooling做了兩件事:1、從feature maps中「摳出」proposals(大小、位置由RPN生成)區域;2、把「摳出」的區域pooling成固定長度的輸出。
圖6是pooling過程的示意圖,feature map中有兩個不同尺寸的proposals,但pooling後都是7x7=49個輸出,這樣就能為後面的全連接層提供固定長度的輸入。這種pooling方式有別於傳統的pooling,沒有任何tensorflow自帶的函數能實現這種功能,你可以自己用python寫個ROI Pooling的過程,但這樣就調用不了GPU的並行計算能力,所以作者的源碼里用C++來實現整個ROI Pooling。釋疑篇:
為什麼要pooling成固定長度的輸出呢?這個其實來自於更早提出的SPP Net,RPN網路提取出的proposal大小是會變化的,而分類用的全連接層輸入必須固定長度,所以必須有個從可變尺寸變換成固定尺寸輸入的過程。在較早的R-CNN和Fast R-CNN結構中都通過對proposal進行拉升(warp)或裁減(crop)到固定尺寸來實現,拉升、裁減的副作用就是原始的輸入發生變形或信息量丟失(圖7),以致分類不準確。而ROI Pooling就完全規避掉了這個問題,proposal能完整的pooling成全連接的輸入,而且沒有變形,長度也固定。
源碼篇:
lib
oi_pooling_layer目錄下文件用來實現ROI Pooling。先來看http://roi_pooling_op.cc,裡面有4個類:
CPU和GPU版本的具體流程差異不大,只是後者通過CUDA來實現,具體的代碼就不貼了,下面一張圖裡各個參數的名稱對應著代碼里前向pooling計算時參數的取名(在RoiPoolOp類的Compute()函數中),圖中的proposal被分割成7x7個小方塊(圖中藍色的小方塊),每個藍色的大小是bin_size_w x bin_size_h ,其中的最大值就是一個pooling的結果,一個proposal共有49個pooling輸出。可以對照下圖和代碼來理解:
五、訓練過程
前面介紹了Faster R-CNN的結構,最後看下訓練方法,為了便於說明,我們把RPN中的rpn classification和rpn bounding box regression統稱為RPN訓練;把proposal layer中對proposal精確位置的訓練和最終的準確分類訓練統稱為R-CNN訓練。Ross Girshick在論文中介紹了3種訓練方法:
Alternating training:RPN訓練和R-CNN訓練交替進行,共交替兩次。訓練時先用ImageNet預訓練的結果來初始化網路,訓練RPN,用得到的proposal再訓練R-CNN,之後用R-CNN訓練出的參數來初始網路,再訓練一次RPN,最後用RPN訓練出的參數來初始化網路,最後訓練次R-CNN,就完成了全部的訓練過程。Approximate joint training:這裡與前一種方法不同,不再是串列訓練RPN和R-CNN,而是嘗試把二者融入到一個網路內一起訓練。這裡Approximate 的意思是指把RPN bounding box regression部分反向計算得到的梯度完全捨棄,不用做更新網路參數的權重。Approximate joint training相對於Alternating traing減少了25-50%的訓練時間。Non-approximate training:該方法和Approximate joint training基本一致,只是不再捨棄RPN bounding box regression部分得到的梯度。本文開頭提供的源碼使用的是第三種方法,把4個部分的loss都加到了一起來訓練,它的速度要更快。
推薦閱讀:
※Caffe2 教程--5. A Toy Regression
※讀Focal Loss
※【小林的OpenCV基礎課 15】剪刀手/分水嶺分割法
※【小林的OpenCV基礎課 10】Canny邊緣檢測
※深度學習的「警察」與「小偷」
TAG:深度學習DeepLearning | 計算機視覺 | 神經網路 |