1. 程式人生 > >R-FCN演算法及Caffe程式碼詳解

R-FCN演算法及Caffe程式碼詳解

本篇部落格一方面介紹R-FCN演算法(NISP2016文章),該演算法改進了Faster RCNN,另一方面介紹其Caffe程式碼,這樣對演算法的認識會更加深入。

要解決的問題:

這篇論文提出一種基於region的object detection演算法:R-FCN(Region-based Fully Convolutional Network),R-FCN可以看做是Faster RCNN的改進版,速度上提高了差不多3倍左右,mAP也有一點提升。另外一類object detection演算法像YOLO,SSD等object detection演算法是不基於region的。
為什麼R-FCN相比Faster RCNN會有明顯的提速呢?以主網路為ResNet101為例,在Faster RCNN中,ROI Pooling層的輸入是在conv4_x,在做完ROI Pooling後會繼續接conv5_x,conv5_x包含9個卷積層,另外在conv5_x後還有幾個全連線層,這些層的計算都直接作用在每個roi上,因此存在許多重複計算。如果主網路換成VGG也類似,只不過重複計算的層數會少一些,主要是一些全連線層。而在R-FCN中,所有能共享的層都在ROI Pooling之前做好了,因此在ROI Pooling後基本不會有太多的重複計算。為了要在ROI Pooling之前實現層共享,一方面將conv5_x的計算移到Pooling層之前,但這樣依然還存在一些全連線層的重複計算,因此再引入position-sensitive score map和position-sensitive ROI Pooling,使得經過Pooling後簡單地執行一些操作就能得到迴歸和分類結果,而不再像Faster RCNN一樣用幾個全連線層去得到結果。

基於101層的ResNet網路在VOC 2007資料集上達到mAP 83.6%。測試的時候每張影象所用時間是170ms,比Faster RCNN快2.5到20倍。
程式碼地址:https://github.com/daijifeng001/r-fcn

演算法概要:

首先之所以提出本文的演算法,簡單講是為了提高Faster RCNN的速度,因此一方面很直觀地想到要儘可能在網路中共享計算,所以就想到對原來ROI Pooling層進行改造和移動;另一方面希望基本網路可以更加強大,因此就想到了用類似Resnet等全卷積網路代替原來的VGG等網路。

這個演算法的網路主要是基於ResNet-101,ResNet-101包含100個卷積層、一個均值降取樣層和一個1000分類的全連線層。這裡作者僅採用前面的100個卷積層來提取特徵,其它層不用

。ResNet-101的最後一個卷積層輸出是2048維,這裡作者為了降維,添加了一個1024維的1*1卷積層(隨機初始化)。最後,一方面新增一個k^2(C+1)維的卷積層用於生成score maps,這些score maps主要是用來生成object的類別;另一方面為了做bounding box regression,作者添加了和Fast RCNN類似的bounding box regression卷積層,維度是4*k^2,該層和前面生成score maps的卷積層是並列的。除了這個主網路以外,該演算法還引入RPN網路生成ROI,生成的ROI將和分類的卷積層生成的score maps進行pooling並最終得到每個ROI屬於每個類別的概率(一共C+1類)。另外這個ROI還將和迴歸卷積層的輸出進行pooling,得到每個ROI的四個座標。損失函式方面基本上和Fast RCNN一樣。
因此,整個網路主要就是由全卷積網路(ResNet)和RPN網路構成,前者用於提取特徵,後者用於生成ROI。

注意:文中的ROI就是region proposal。另外Faster RCNN中的ROI Pooling和本文的ROI Pooling不是一個意思,前者只是簡單將每個region feature變換到統一的尺寸的feature,變換過程中採用Max pooling;而後者則是一種position-sensitive的ROI Pooling。

演算法詳解:

圖片分類問題是具有平移不變性的(translation invariance),什麼意思呢?就是說一張影象中目標的平移對這張圖片的分類結果影響不大,這也是為什麼全卷積網路可以在影象分類比賽中成績更好;目標檢測問題則具有平移敏感性(translation variance),也很容易理解,如果一張影象中目標平移了,那麼最後預測的框也會變化。

作者指出像VGG或AlexNet網路,一般由卷積層,每個卷積層後面跟降取樣(pooling)層,最後疊加幾個全連線層構成。但是像GooleNet或ResNet,基本上都是卷積層(很少有降取樣層或全連線層),作者將其歸為全卷積網路(FCN),同時假設全卷積網路具備平移不變性,所以如果簡單地在目標檢測問題中用ResNet代替VGG等網路,檢測效果並不好,根源就在於前者具有平移不變性,而檢測問題對平移敏感。為了解決這個問題,作者在ResNet的卷積層中插入了ROI pooling層,這種region-specific的操作打破了原來的平移不變性(普通網路因為卷積層和pooling層的交替,所以具有平移敏感性,所以如果在全卷積網路中增加ROI Pooling會增加平移敏感性),不過這種設計降低了訓練及測試的效率,因為其引入了一些region-wise層。

因此為了將平移敏感性引入全卷積網路,作者在全卷積網路的輸出位置新增一系列特定的卷積層用於生成position-sensitive的score map,每個score map儲存目標的空間位置資訊。然後再新增ROI Pooling層,該層後面不再跟卷積層或全連線層。這樣整個網路不僅可以end-to-end訓練,而且所有層的計算都是在整個影象上共享的。如下圖的table1,表示幾種演算法的共享層數情況。

這裡寫圖片描述

這裡講一個公式:position-sensitive ROI pooling,如下圖。作者將每個ROI劃分成k*k個bins,即如果一個ROI的大小是w*h,那麼每個bin的大小就是(w/k)*(h/k),對於裡面第(i,j)個bin進行pool操作可以得到rc(i,j),c表示類別。zi,j,c表示的是k^2(C+1)個maps中的第(i,j)個且屬於c類別的那個map。x,y表示這個bin的畫素點範圍,累加也是對x,y的不同取值進行累加,最後再除以n取均值。

這裡寫圖片描述

Figure1以分類支路為例介紹該公式,在Figure1中不同的顏色代表公式1中不同的(i,j)。所以對於分類支路而言,這個公式簡單講就是:對一個ROI中的某個bin(比如是(i,j)這個bin)進行pooling操作就是對卷積層輸出的k^2(C+1)個maps中的第(i,j)個map做均值pooling。所以一個ROI的某個bin進行pooling後會得到1*1*(C+1)大小的輸出,換句話說每個ROI進行pooling後會得到k*k*(C+1)大小的輸出,這個輸出進行vote操作得到C+1維的輸出,這個vote操作就是一個均值操作。最後再連線一個softmax層輸出每一類的概率。迴歸支路和分類支路類似,只不過接的卷積層的卷積核數量不是k^2(C+1)而是4*k^2(程式碼中採用的是4*2*k^2),因此在經過position-sensitive Roi Pooling後得到4*k*k維度的輸出,再經過vote操作得到4*1*1的輸出,表示預測的bbox座標offset。(這裡採用的是簡化版的迴歸支路,文章中也提到可以採用卷積核數量為C*4*k^2的卷積層去生成position-sensitive score map,那樣的話相當於對每個類別都進行迴歸)。

這裡寫圖片描述

關於損失函式,原文如下圖,這裡解釋一下。首先本文的損失函式基本上和Fast RCNN的一樣,都是分類損失和迴歸損失的和。分類損失採用的是交叉熵,這也是Caffe裡面的softmaxWithLoss層的做法,可能有同學看不懂這個交叉熵裡面的Sc代表什麼意思,這裡再貼一個文中的公式來解釋Sc。c>0是用來說明只有存在object的ROI才能參與迴歸(因為只有object才有四個座標,才能進行迴歸,才有loss)。最後一句是介紹訓練時候正負樣本是怎麼選的,和一個ground truth的IOU值大於0.5的ROI就是positive樣本。

這裡寫圖片描述

這個是介紹Sc的兩個公式,Sc其實就是Caffe裡面的softmax層的輸出,代表的是每一類的概率;其中Sc用到rc,而rc就是網路層輸出的每個類別的概率得分,就是上面Fig1的vote後的結果。具體參看另一篇博文:softmax,softmax-loss,BP的解釋

這裡寫圖片描述

如下圖,除了主網路ResNet以外,還有RPN網路用於生成ROI(region proposal),因此在訓練的時候,作者採用RPN網路和R-FCN交替訓練的方式來共享特徵。這裡有個細節,假設每個image有N個ROI,那麼在前向訓練的時候會計算所有N個ROI的loss,然後將這N個ROI(包括positive和negative)按照loss高低進行排序,最後在backpropagation階段只將loss最高的B個ROI的loss回傳。詳細可以參考OHEM演算法。

這裡寫圖片描述

因此再來簡單梳理一下網路結構:首先輸入影象經過一個全卷積網路(比如ResNet),然後一方面在最後一個卷積層後面新增特殊的卷積層生成position-sensitive的score map,另一方面全卷積網路的某個卷積層(可能是最後一個卷積層)輸出作為RPN網路的輸入,RPN網路最後生成ROI。最後的POI Pooling層將前面的socre map和ROI作為輸入,輸出類別資訊。另外迴歸部分和分類部分是並列的,詳解看後面的Caffe程式碼。

Caffe的程式碼:

首先是資料讀入操作,假設輸出的data是1*3*600*1000,im_info是1*3,gt_boxes是1*4,後面的所有維度都是以這個假設為前提。

這裡寫圖片描述

然後ResNet,結構如下圖。R-FCN主要是採用ResNet和RPN接面構來訓練。R-FCN的具體結構(以ResNet50為例):conv1,maxpooling,conv2_x(在程式碼中用res2a_branch2a到res2c_branch2c表示,前面的字母a,b,c表示在conv2_x層需要迴圈3個大層,後面的a,b,c表示每個大層裡面都有三個小層。另外還有res2a_branch1表示用1*1的256個卷積核卷積的結果。每個大層結束的時候都需要用Eltwise層合併,比如res2a_branch1和res2a_branch2c生成res2a,下一個大層則是res2a和res2b_branch2c座Eltwise合併),conv3_x,conv4_x,conv5_x。

這裡寫圖片描述

然後是RPN網路,RPN網路以一個3*3的卷積核,pad=1,stride=1的512個卷積核的卷積層開始,輸入是res4f層的輸出,res4f層的輸出即conv4_x最後的輸出。該rpn_conv/3*3層的輸出是1*512*38*63。

這裡寫圖片描述

然後是分類層和迴歸層分類層採用1*1的卷積核,pad=0,stride=1的18(2(back ground/fore ground)*9(anchors))個卷積核的卷積層,分類層的輸出是1*18*38*63。迴歸採用1*1的卷積核,pad=0,stride=1的36(4*9(anchors))個卷積核的卷積層,迴歸層的輸出是1*36*38*63。

這裡寫圖片描述

Reshape層對分類層的結果做了一次維度調整,從1*18*38*63變成1*2*342*63,後面的342*63就代表該層所有anchor的數量。

這裡寫圖片描述

下面這個層是用來從最開始讀取的資料得到label和target。這裡rpn_cls_score為1*1*342*63,rpn_bbox_targets為1*36*38*63,rpn_bbox_inside_weights為1*36*38*63,rpn_bbox_outside_weights為1*36*38*63。

這裡寫圖片描述

損失函式如下:分類的損失採用SoftmaxWithLoss,輸入是reshape後的預測的類別score(1*2*342*63)和真實的label(1*1*342*63)。迴歸的損失採用SmoothL1Loss,輸入是rpn_bbox_pred(1*36*38*63)即所有anchor的座標相關的預測,rpn_bbox_targets(1*36*38*63),rpn_bbox_inside_weights(1*36*38*63),rpn_bbox_outside_weights(1*36*38*63)。

這裡寫圖片描述

然後是ROI Proposal,先用一個softmax層算出概率(1*2*342*63),然後再reshape到1*18*38*63。

這裡寫圖片描述

然後是生成proposal,維度是1*5。

這裡寫圖片描述

這一層生成rois(1*5*1*1),labels(1*1*1*1),bbox_targets(1*8*1*1),bbox_inside_weights
(1*8*1*1),bbox_outside_weights(1*8*1*1)。

這裡寫圖片描述

至此RPN網路結束。

新的卷積層,其實就是在ResNet後面新增的卷積層,以res5c作為輸入,用1*1的卷積核,pad=0的1024個卷積核的卷積層。得到1*1024*38*63。

這裡寫圖片描述

然後再分別跟兩個卷積層,卷積核的大小都是1,pad=0,一個用於分類,一個用於迴歸。分類層如下:1*1029*38*63,其中1029的含義在下圖中也有解釋,21是代表類別(VOC的20類加上背景1類),7是和ROI要劃分成7*7的格子對應。

這裡寫圖片描述

這個分類層的輸出結果就是論文中的這個三維矩陣:

這裡寫圖片描述

然後是迴歸層的輸出:1*392*38*63,與分類層類似。

這裡寫圖片描述

開始進入ROI pooling操作了,上面一層,有兩個輸入:rfcn_cls(1*1029*38*63)是預測的結果,rois(1*5*1*1)是ROI,生成1*21*7*7的結果。下面一層是均值池化,得到1*21*1*1(cls_score),就是論文中vote的過程。

這裡寫圖片描述

所以上面這兩個操作就是對應論文中的這個圖:

這裡寫圖片描述

同理,迴歸也是類似的操作:生成1*8*7*7和1*8*1*1(bbox_pred)的結果。

這裡寫圖片描述

最後就是損失和計算準確率層:

這裡寫圖片描述

可以看出在ROI Pooling層後就沒有卷積層和全連線層了

總結:

R-FCN作為Faster RCNN的改進版,主要對原有的ROI Pooling層進行改進和移位,使得不會存在眾多region proposal都得經過全連線層的情況,這樣就加快了速度。另一方面改進是將原來的VGG16型別的主網路換成ResNet系列網路。而演算法的另一部分RPN網路則和Faster RCNN基本差不多。總的來講實驗效果還是很不錯的。