Faster RCNN的代碼流程梳理【轉載整理】

Faster RCNN的代碼流程梳理【轉載整理】

          本文轉載自知乎鏈接:從編程角度學習Faster R-CNN

          在目標檢測領域, Faster R-CNN表現出了極強的生命力, 雖然是2015年的論文, 但它至今仍是許多目標檢測算法的基礎,這在日新月異的深度學習領域十分難得。Faster R-CNN還被應用到更多的領域中, 比如人體關鍵點檢測、目標追蹤、 實例分割還有圖像描述等。現在很多優秀的Faster R-CNN博客大都是針對論文講解,本文將嘗試從編程角度講解Faster R-CNN的實現。由於Faster R-CNN流程複雜,符號較多,容易混淆,本文以VGG16爲例,所有插圖、數值皆是基於VGG16+VOC2007 。

1.基本流程:

1.1 目標:

          從編程實現角度角度來講, 以Faster R-CNN爲代表的目標檢測任務,可以描述成:給定一張圖片, 找出圖中的有哪些對象,以及這些對象的位置和置信概率。

1.2 整體架構:

          Faster R-CNN的整體流程如下圖所示。

          從編程角度來說, Faster R-CNN主要分爲四部分(圖中四個綠色框):

               1)Dataset:數據,提供符合要求的數據格式(目前常用數據集是VOC和COCO);

               2)Extractor: 利用CNN提取圖片特徵features(原始論文用的是ZF和VGG16,後來又用ResNet101);

               3)RPN(Region Proposal Network): 負責提供候選區域ROIs(每張圖給出大概2000個候選框);

               4)ROIHead: 負責對rois分類和微調,對RPN找出的ROIs,判斷它是否包含目標,並修正框的位置和座標。

         Faster R-CNN整體的流程可以分爲三步:

               1)特徵提取: 圖片經過預訓練的網絡(Extractor),提取到了圖片的特徵;

               2)區域推薦: 利用提取到的特徵,經過RPN網絡,找出一定數量的ROIs;

               3)分類與迴歸:將ROIs和圖像特徵,輸入到ROIHead,對這些ROIs進行分類,判斷都屬於什麼類別,同時對這些ROIs的位置進行微調。

2.詳細實現:

2.1 數據:

        對於每張圖片,需要進行如下數據處理:

               1) 圖片進行縮放,使得長邊小於等於1000,短邊小於等於600(至少有一個等於);

               2)對相應的boundingboxes 也也進行同等尺度的縮放;

               3)對於Caffe 的VGG16 預訓練模型,需要圖片位於0-255,BGR格式,並減去一個均值,使得圖片像素的均值爲0。

       最後返回四個值供模型訓練:

               1) images : 3×H×W (B-G-R三通道,寬W,高H);

               2) bboxes: 4×K , K個bounding boxes,每個bounding box的左上角和右下角的座標,形如(Y_min,X_min, Y_max,X_max);

               3) labels:K, 對應K個bounding boxes的label(對於VOC取值範圍爲[0-19]);

               4) scale: 縮放的倍數, 原圖H' ×W'被resize到了HxW(scale=H/H' )。

       需要注意的是,目前大多數Faster R-CNN實現都只支持batch_size=1的訓練。

2.2 Extractor:

       Extractor使用的是預訓練好的模型提取圖片的特徵,論文中主要使用的是Caffe的預訓練模型VGG16。

       一張圖片,經過Extractor之後,會得到一個C×(H/16)×(W/16)的特徵圖。 修改如下圖所示:

               1) 爲了節省顯存,前四層卷積層的學習率設爲0;

               2) Conv5_3的輸出作爲圖片特徵,Conv5_3相比於輸入,下采樣了16倍,也就是說輸入的圖片尺寸爲3×H×W,那麼特徵圖的尺寸就是C×(H/16)×(W/16);

               3) VGG最後的三層全連接層的前兩層,一般用來初始化ROIHead的部分參數。

2.3 RPN:

       Faster R-CNN最突出的貢獻就在於提出了Region Proposal Network(RPN)代替了Selective Search,從而將候選區域提取的時間開銷幾乎降爲0(2s -> 0.01s)。

2.3.1 Anchor:

       在RPN中,作者提出了Anchor的概念,Anchor是大小和尺寸固定的候選框。論文中用到的Anchor有三種尺寸和三種比例,如下圖所示,三種尺寸分別是小(藍128)中(紅256)大(綠512),三個比例分別是1:1,1:2,2:1。

       3×3的組合總共有9種Anchor。

       然後用這9種Anchor在特徵圖左右上下移動,每一個特徵圖上的點都有9個Anchor,最終生成了 (H/16)× (W/16)×9個Anchor,對於一個512×62×37的特徵圖,有 62×37×9~ 20000個Anchor,也就是對一張圖片,有20000個左右的Anchor,類似於暴力窮舉的處理方式。

2.3.2 訓練RPN:

       RPN的總體架構如下圖所示:

       Anchor的數量和特徵圖相關,不同的特徵圖對應的Anchor數量也不一樣。RPN在Extractor輸出的特徵圖的基礎之上,先增加了一個卷積用來作語義空間轉換,然後利用兩個1x1的卷積分別進行二分類(是否爲正樣本)和位置迴歸。進行分類的卷積核通道數爲9×2(9個Anchor,每個Anchor二分類,使用交叉熵損失),進行迴歸的卷積核通道數爲9×4(9個Anchor,每個Anchor有4個位置參數)。RPN是一個全卷積網絡,這樣對輸入圖片的尺寸就沒有要求了。

       接下來RPN做的事情就是利用AnchorTargetCreator將20000多個候選的anchor選出256個Anchor進行分類和迴歸位置,選擇過程如下:

               1)對於每一個ground truth bounding box (gt_bbox),選擇和它重疊度(IOU)最高的一個Anchor作爲正樣本;

               2)對於剩下的Anchor,從中選擇和任意一個gt_bbox重疊度超過0.7的Anchor,作爲正樣本,正樣本的數目不超過128個;

               3)隨機選擇和gt_bbox重疊度小於0.3的Anchor作爲負樣本。負樣本和正樣本的總數爲256;

               4)對於每個Anchor, gt_label 要麼爲1(前景),要麼爲0(背景),而gt_loc則是由4個位置參數(tx,ty,tw,th)組成,這樣比直接回歸座標更好;

               5)計算分類損失用的是交叉熵損失,而計算迴歸損失用的是Smooth_l1_loss. 在計算迴歸損失的時候,只計算正樣本(前景)的損失,不計算負樣本的位置損失。

2.3.3 RPN生成ROIs:

       RPN在自身訓練的同時,還會提供ROIs給Fast RCNN(RoIHead)作爲訓練樣本,RPN生成ROIs的過程(ProposalCreator)如下:

               1)對於每張圖片,利用它的特徵圖, 計算 (H/16)× (W/16)×9(大概20000)個Anchor屬於前景的概率,以及對應的位置參數;

               2)選取其中概率較大的12000個Anchor;

               3)利用迴歸的位置參數,修正這12000個Anchor的位置,得到ROIs;

               4)利用非極大值((Non-maximum suppression, NMS)抑制,選出概率最大的2000個ROIs。

       注意:在測試推理階段,爲了提高處理速度,12000和2000分別變爲6000和300;

       注意:這部分的操作不需要進行反向傳播,因此可以利用numpy/tensor實現,RPN的輸出爲ROIs(形如2000×4或者300×4的tensor)。

2.4 ROIHead/Fast R-CNN:

       RPN只是給出了2000個候選框,ROIHead在給出的2000個候選框之上繼續進行分類和位置參數的迴歸。

2.4.1 網絡結構:

       由於ROIs給出的2000個候選框,分別對應特徵圖不同大小的區域。首先利用ProposalTargetCreator 挑選出128個sample_ROIs, 然後使用了ROIPooling 將這些不同尺寸的區域全部池化到同一個尺度(7×7)上。下圖就是一個例子,對於特徵圖上兩個不同尺度的ROI,經過RoIPooling之後,最後得到了3×3的特徵圖。

       ROIPooling 是一種特殊的池化操作,給定一張圖片的特徵圖 (512×H/16×W/16) ,和128個候選區域的座標(128×4),ROIPooling將這些區域統一下采樣到 (512×7×7),就得到了128×512×7×7的向量。可以看成是一個batch_size=128,通道數爲512,7×7的特徵圖。

       池化成7×7的尺度是爲了能夠共享權重,在之前講過除了用到VGG前幾層的卷積之外,最後的全連接層也可以繼續利用,當所有的ROIs都被池化成(512×7×7)的特徵圖後,將它reshape 成一個一維的向量,就可以利用VGG16預訓練的權重,初始化前兩層全連接。最後再接兩個全連接層,分別是:

               1)FC21:用來分類,預測RoIs屬於哪個類別(20個類+背景);

               2)FC84:用來回歸位置(21個類,每個類都有4個位置參數)。

2.4.2 訓練:

       前面講過,RPN會產生大約2000個ROIs,這2000個ROIs不是都拿去訓練,而是利用ProposalTargetCreator 選擇128個ROIs用以訓練。選擇的規則如下:

               1)ROIs和gt_bboxes 的IoU大於0.5的,選擇一些(比如32個);

               2)選擇 ROIs和gt_bboxes的IoU小於等於0(或者0.1)的選擇一些(比如 128-32=96個)作爲負樣本;

       爲了便於訓練,對選擇出的128個ROIs,還對他們的gt_roi_loc 進行標準化處理(減去均值除以標準差)。

       對於分類問題,直接利用交叉熵損失. 而對於位置的迴歸損失,一樣採用Smooth_L1Loss, 只不過只對正樣本計算損失,而且是隻對正樣本中的這個類別4個參數計算損失。舉例來說:一個ROI在經過FC 84後會輸出一個84維的loc 向量. 如果這個ROI是負樣本,則這84維向量不參與計算 L1_Loss;如果這個ROI是正樣本,屬於label K,那麼它的第 K×4, K×4+1 ,K×4+2, K×4+3 這4個數參與計算損失,其餘的不參與計算損失。

2.4.3 生成預測結果

        測試的時候對所有的ROIs(大概300個左右) 計算概率,並利用位置參數調整預測候選框的位置,然後再執行一遍非極大值抑制(之前在RPN的ProposalCreator用過)。

        注意:

               1)在RPN的時候,已經對Anchor做了一遍非極大值抑制,在RCNN測試的時候,還要再執行一遍;

               2)在RPN的時候,已經對Anchor的位置做了迴歸調整,在RCNN階段還要對ROI再執行一遍;

               3)在RPN階段分類是二分類,而Fast RCNN階段是21分類。

2.5 模型架構圖:

        最後整體的模型架構圖如下:

        需要注意的是: 藍色箭頭的線代表着計算圖,梯度反向傳播會經過。而紅色部分的線不需要進行反向傳播(論文了中提到了ProposalCreator生成ROIs的過程也能進行反向傳播,但需要專門的算法)。

3.概念對比:

        在Faster RCNN中有幾個概念,容易混淆,或者具有較強的相似性,在此列出來並做對比。

3.1 bbox/ anchor/ ROI/loc:

        BBox:全稱是bounding box,邊界框。其中Ground Truth Bounding Box是每一張圖中人工標註的框的位置。一張圖中有幾個目標,就有幾個框(一般小於10個框)。Faster R-CNN的預測結果也可以叫bounding box,不過一般叫 Predict Bounding Box;

        Anchor:錨是人爲選定的具有一定尺度、比例的框,一個特徵圖的錨的數目有上萬個(比如 20000);

        ROI:候選框,Faster R-CNN之前傳統的做法是利用selective search從一張圖上大概2000個候選框,現在利用RPN可以從上萬的Anchor中找出一定數目更有可能的候選框。在訓練RCNN的時候,這個數目是2000,在測試推理階段,這個數目是300(爲了速度)

        ROI不是單純的從anchor中選取一些出來作爲候選框,它還會利用迴歸位置參數,微調anchor的形狀和位置。

        可以這麼理解:在RPN階段,先窮舉生成千上萬個Anchor,然後利用Ground Truth Bounding Boxes,訓練這些Anchor,而後從Anchor中找出一定數目的候選區域(ROIs)。RoIs在下一階段用來訓練ROIHead,最後生成Predict Bounding Boxes。

        loc: bbox,Anchor和ROI,本質上都是一個框,可以用四個數(y_min, x_min, y_max, x_max)表示框的位置,即左上角的座標和右下角的座標。這裏之所以先寫y,再寫x是爲了數組索引方便,但不要混淆。 除了用這四個數表示一個座標之外,還可以用(y,x,h,w)表示,即框的中心座標和長寬,在訓練中進行位置迴歸的時候,用的是後一種的表示。

3.2 四類損失:

        雖然原始論文中用的4-Step Alternating Training 即四步交替迭代訓練。然而現在github上開源的實現大多是採用近似聯合訓練(Approximate joint training),端到端,一步到位,速度更快。

        在訓練Faster RCNN的時候有四個損失:

               1)RPN 分類損失:Anchor是否爲前景(二分類);

               2)RPN位置迴歸損失:Anchor位置微調;

               3)ROI分類損失:ROI所屬類別(21分類,多了一個類作爲背景);

               4)ROI位置迴歸損失:繼續對RoI位置微調。

        四個損失相加作爲最後的損失,反向傳播,更新參數。

3.3 三個creator:

        在一開始閱讀源碼的時候常常把Faster RCNN中用到的三個Creator弄混。

               1)AnchorTargetCreator : 負責在訓練RPN的時候,從上萬個anchor中選擇一些(比如256)進行訓練,以使得正負樣本比例大概是1:1. 同時給出訓練的位置參數目標。 即返回gt_rpn_loc和gt_rpn_label;

               2)ProposalTargetCreator: 負責在訓練ROIHead/Fast R-CNN的時候,從ROIs選擇一部分(比如128個)用以訓練。同時給定訓練目標, 返回(sample_RoI, gt_RoI_loc, gt_RoI_label);

               3)ProposalCreator: 在RPN中,從上萬個Anchor中,選擇一定數目(2000或者300),調整大小和位置,生成ROIs,用以Fast R-CNN訓練或者測試。

        其中AnchorTargetCreator和ProposalTargetCreator是爲了生成訓練的目標,只在訓練階段用到,ProposalCreator是RPN爲Fast R-CNN生成ROIs,在訓練和測試階段都會用到。三個共同點在於他們都不需要考慮反向傳播(因此不同框架間可以共享numpy實現)

3.4 感受野與scale:

        感受野:從直觀上講,感受野receptive field)就是視覺感受區域的大小,在卷積神經網絡中,感受野的定義是卷積神經網絡每一層輸出的特徵圖上的像素點在原始圖像上映射的區域大小。我的理解是,特徵圖上的某一點f對應輸入圖片中的一個區域,這個區域中的點發生變化,f可能隨之變化。而這個區域外的其它點不論如何改變,f的值都不會受之影響。VGG16的conv5_3的感受野爲228,即特徵圖上每一個點,都包含了原圖一個228×228區域的信息。

        Scale:輸入圖片的尺寸比/特徵圖的尺寸。比如輸入圖片是3×224×224,特徵圖是 512×14×14,那麼scale就是         14/224=1/16。可以認爲特徵圖中一個點對應輸入圖片的16個像素。由於相鄰的同尺寸、同比例的Anchor是在特徵圖上的距離是一個點,對應到輸入圖片中就是16個像素。在一定程度上可以認爲Anchor的精度爲16個像素。不過還需要考慮原圖相比於輸入圖片又做過縮放(這也是dataset返回的scale參數的作用,這個的scale指的是原圖和輸入圖片的縮放尺度,和上面的scale不一樣)。

4 實現方案:

         在@羅若天ruotianluo/pytorch-faster-rcnn 之前,PyTorch的Faster R-CNN並未有合格的實現(速度和精度)。最早PyTorch實現的Faster R-CNN有longcw/faster_rcnn_pytorch 和 fmassa/fast_rcnn 後者是當之無愧的最簡實現(1,245行代碼,包括空行註釋,純Python實現),然而速度太慢,效果較差,fmassa最後也放棄了這個項目。前者又太過複雜,map也比論文中差一點(0.661VS 0.699)。當前github上的大多數實現都是基於py-faster-rcnn,RBG大神的代碼很健壯,考慮的很全面,支持很豐富,基本上clone下來,準備一下數據模型就能直接跑起來。然而對我來說太過複雜,上百個文件,動不動就好幾層的嵌套封裝,很容易令人頭大。

        趁着最近時間充裕了一些,我決定從頭寫一個,最終寫了一個簡單版本的Faster R-CNN,代碼地址在 github:simple-faster-rcnn-pytorch

        這個實現主要有以下幾個特點:

               1)代碼簡單:除去空行,註釋,說明等,大概有2000行左右代碼,如果想學習如何實現Faster R-CNN,這是個不錯的參考;

               2)效果夠好:超過論文中的指標(論文mAP是69.9, 本程序利用caffe版本VGG16最低能達到0.70,最高能達到0.712,預訓練的模型在github中提供鏈接可以下載);

               3)速度足夠快:TITAN Xp上最快只要3小時左右(關閉驗證與可視化)就能完成訓練;

               4)顯存佔用較小:3G左右的顯存佔用。

       

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