關於R-CNN:Uno Whoiam:R-CNN: 狼入羊群 || 5分鐘看懂CV頂刊論文

原論文鏈接:Fast R-CNN

Fast R-CNN 即 Fast Region-based Convolutional Network,你的直覺沒錯,它就是R-CNN的升級版。

在細說 Fast R-CNN 之前,不妨先看看 R-CNN 有什麼令人詬病的地方:

1、慢,實在是慢,別說實時檢測了,47s的等待讓坐在電腦前的記幾彷彿是一隻智障。

2、訓練麻煩,AlexNet、SVMs 以及 bounding-box regression 得一個接一個地訓練。

3、訓練佔用大量時間和空間(硬碟),除開訓練三個模型的時間,SVMs 和 bounding-box regression 的訓練樣本得用 AlexNet 一次又一次地前向傳播提取特徵、標註樣本數據、保存在硬碟里的喲,每一張圖片的每一個proposal都得跑一次喲,想想都覺得噁心。而作者而說明了,需要GPU花2.5天的時間才能處理完5K張VOC07trainval里的圖片,產生的訓練樣本佔用的空間得好幾百個GB。想想都覺得噁心嘔。順便溫馨提示一下,每張圖生成的樣本最好單獨生成一個文件夾保存,別把這這個數量級的樣本放在同一個文件夾里喲,即使是最好的SSD也招架不住這樣的文件夾,當你幡然醒悟想要rm -r -f dir 重新來過時,漫長的等待足夠讓您好好睡一覺了,別問我為什麼知道這麼多淚目。

隨著 Fast R-CNN 的到來,以上問題也就不復存在辣!相比 R-CNN,除了各種快(見下段原論文引用)Fast R-CNN 有以下幾個特性:

Fast R-CNN trains the very deep VGG16 network 9× faster than R-CNN, is 213× faster at test-time, and achieves a higher mAP on PASCAL VOC 2012. Compared to SPPnet, Fast R-CNN trains VGG16 3× faster, tests 10× faster, and is more accurate.

1、更高的mAP。

2、不用分段訓練,整個網路可以通過使用多任務損失函數,一次性訓練好。

3、訓練可以更新網路層中的所有權重。

4、無需苦逼生成訓練樣本緩存在硬碟上,節省了空間。

Fast R-CNN 的整體網路如下圖所示:

接下來按照物體檢測的大框架:候選框->特徵提取->分類->精調位置。一步步來說吧。

一、提出候選框

和R-CNN一樣,候選框的提出使用 selective search 方法。

selective search:

huppelen.nl/publicationblog.csdn.net/mao_kun/a

二、特徵提取

使用深度卷積神經網路進行特徵提取,在論文中作者分別使用了從小到大三種網路進行實驗:

  1. S:CaffeNet 即小型版的 AlexNet
  2. M:VGG\_CNN\_ M\_ 1024 一看名字就知道是小型一點的 VGG
  3. L:VGG-16

值得注意的是,以上網路的全連接層都被去掉了,這意味著:輸入的尺寸大小勞資不用管辣哈哈哈哈哈!!!(想起R-CNN里RBG大神辛辛苦苦想了四種辦法將Proposal區域變成 227	imes227 再餵給 AlexNet 就覺得熏疼)

也就是說,特徵提取網路最終輸出的是 C 層的 Feature Maps

等等,好像有什麼不對?我想要得到的是圖片中某個Proposal區域的特徵向量啊,沒特徵向量我怎麼判斷這個Proposal區域到底有沒有物體嘛!

這就需要用到 ROI max-pooling 了。對於這個細節網上很少有可以把它說清楚的,本文將結合 Pytorch 實現代碼,保證讓您看得明明白白的。

首先,啥是Proposal區域?在Fast R-CNN中,Proposal區域就是用 selective search 在圖片上生成的可能含有被檢測物體的框框們,而這個框框,可以用左上角和右下角的坐標表示,即: (x1,y1,x2,y2) ,它們都是 [0,1] 範圍內的 float 數,表示框框在圖片中的相對位置。

現在我們的目標是:對於一個Proposal框,在神經網路輸出的CFeature Maps找到對應的部分。具體該怎麼做呢?如果整張圖片的經過特徵提取網路生成的 Feature Maps 尺寸是 (C,W,H) ,那麼框框對應的坐標是:

(Floor(x1	imes H), Floor(y1 	imes W), Ceil(x2	imes H), Ceil(y2 	imes W))

其中 Floor 表示下取整, Ceil 表示上取整。這個框出來的部分就代表著Proposal區域經過神經網路提取到的特徵,另外 C 保持不變,將其平展開成一維向量後就表示Proposal區域的特徵向量辣。

新的問題來了,Proposal框大小不同也就意味著對應的 Feature Maps 上的大小不同,大小不同平鋪出來的一維特徵向量也不同,那怎麼辦?

這就是 ROI max-pooling 需要做的事情了:將不同尺寸Proposal區域所對應的 Feature Maps 變成相同尺寸的,在 Pytorch 中可以使用 torch.nn.AdaptiveAvgPool2d 來實現,無論輸入的 Feature Maps 是什麼尺寸,它都可以通過調整stride、padding等參數給你輸出成統一大小的尺寸。下面是 Pytorch 代碼:

class SlowROIPool(nn.Module):
def __init__(self, output_size):
super().__init__()
self.maxpool = nn.AdaptiveMaxPool2d(output_size)
self.size = output_size

def forward(self, images, rois, roi_idx):
n = rois.shape[0]
h = images.size(2)
w = images.size(3)
x1 = rois[:,0]
y1 = rois[:,1]
x2 = rois[:,2]
y2 = rois[:,3]

x1 = np.floor(x1 * w).astype(int)
x2 = np.ceil(x2 * w).astype(int)
y1 = np.floor(y1 * h).astype(int)
y2 = np.ceil(y2 * h).astype(int)

res = []
for i in range(n):
img = images[roi_idx[i]].unsqueeze(0)
img = img[:, :, y1[i]:y2[i], x1[i]:x2[i]]
img = self.maxpool(img)
res.append(img)
res = torch.cat(res, dim=0)
return res

註:

代碼來源:GitHberChen/Fast-RCNN-Object-Detection-Pytorch

images:shape為[N,C,H,W],為N張圖片經VGG輸出的 Feature Maps

rois:單張圖片中包含N組Proposal框的 (x1,y1,x2,y2)

roi_idx:rois對應的圖片序號

三、分類以及邊框回歸

簡單,分類通過將上面提取出的特徵向量使用全連接網路輸出 N+1 類的置信度即可,而邊框回歸也是通過全連接網路輸出 (N+1)	imes4 個數。

另外一個細節是,論文中採用了 SVD 對這兩個全連接層的計算進行了加速:

(這篇博客寫得不錯,推薦閱讀)

圖像分類任務中,用於卷積層計算的時間比用於全連接層計算的時間多,而在目標檢測任務中,selective search演算法提取的建議框比較多【約2k】,幾乎有一半的前向計算時間被花費於全連接層,就Fast R-CNN而言,RoI池化層後的全連接層需要進行約2k次【每個建議框都要計算】,因此在Fast R-CNN中可以採用SVD分解加速全連接層計算;

具體如何實現呢?

物體分類和窗口回歸都是通過全連接層實現的,假設全連接層輸入數據為 x ,輸出數據為 y ,全連接層參數為 W ,尺寸為 u×v ,那麼該層全連接計算為:

y=Wx 計算複雜度為 u×v ;若將W進行SVD分解,並用前t個特徵值近似代替,即: W=U∑VT≈U(u,1:t)?∑(1:t,1:t)?V(v,1:t)T 那麼原來的前向傳播分解成兩步: y=Wx=U?(∑?VT)?x=U?z 計算複雜度為 u×t+v×t ,若 t<min(u,v)t<min(u,v) ,則這種分解會大大減少計算量;在實現時,相當於把一個全連接層拆分為兩個全連接層,第一個全連接層不含偏置,第二個全連接層含偏置;實驗表明,SVD分解全連接層能使mAP只下降0.3%的情況下提升30%的速度,同時該方法也不必再執行額外的微調操作。作者:WoPawn

來源:CSDN

原文:Fast R-CNN論文詳解 - WoPawn的博客 - CSDN博客

4、損失函數

Fast R-CNN 雖是 two-stage 演算法,但可以通過 one-stage 訓練好,這意味著,損失函數包含多個任務目標:

L(p,u,t^u,v)=L_{cls}(p,u)+λ[u≥1]L_{loc}(t^u,v)

其中:

  • L_{cls}=?logp_u
  • [u≥1]=1  if u>1 otherwise 0
  • L_{loc}(t^u,v)=sum_{i∈{x,y,w,h}}smooth_{L1}(t^u_i?v_i)
  • smooth_{L1}(x)={^{0.5x^2, |x|<1}_{|x|?0.5,  otherwise}
  • u 表示樣本標籤, 0 表示背景類
  • t^u 為預測平移縮放參數 t^u=(t^u_x,t^u_y,t^u_w,t^u_h) ,與之對應的真實平移縮放參數為 v=(v_x,v_y,v_w,v_h) 具體求法和 R-CNN 中一致( P 為預測框位置, G 為真框位置): v_{ x}=(G_{ x}-P_{ x})/P_{w} \ v_{ y}=(G_{ y}-P_{ y})/P_{h} \ v_{w}=log(G_{w}/P_{w}) \ v_{h}=log(G_{h}/P_{h})
  • smooth_{L1}(x) 如下圖:
SmoothL1

最後附上 Fast R-CNN 結構圖和具體細節:

RCNN (
(seq): Sequential (
(0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True)
(2): ReLU (inplace)
(3): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True)
(5): ReLU (inplace)
(6): MaxPool2d (size=(2, 2), stride=(2, 2), dilation=(1, 1))
(7): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(8): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True)
(9): ReLU (inplace)
(10): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(11): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True)
(12): ReLU (inplace)
(13): MaxPool2d (size=(2, 2), stride=(2, 2), dilation=(1, 1))
(14): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(15): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True)
(16): ReLU (inplace)
(17): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(18): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True)
(19): ReLU (inplace)
(20): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(21): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True)
(22): ReLU (inplace)
(23): MaxPool2d (size=(2, 2), stride=(2, 2), dilation=(1, 1))
(24): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(25): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True)
(26): ReLU (inplace)
(27): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(28): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True)
(29): ReLU (inplace)
(30): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(31): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True)
(32): ReLU (inplace)
(33): MaxPool2d (size=(2, 2), stride=(2, 2), dilation=(1, 1))
(34): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(35): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True)
(36): ReLU (inplace)
(37): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(38): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True)
(39): ReLU (inplace)
(40): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(41): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True)
(42): ReLU (inplace)
)
(roipool): SlowROIPool (
(maxpool): AdaptiveMaxPool2d (output_size=(7, 7))
)
(feature): Sequential (
(0): Linear (25088 -> 4096)
(1): ReLU (inplace)
(2): Dropout (p = 0.5)
(3): Linear (4096 -> 4096)
(4): ReLU (inplace)
(5): Dropout (p = 0.5)
)
(cls_score): Linear (4096 -> 21)
(bbox): Linear (4096 -> 84)
(cel): CrossEntropyLoss (
)
(sl1): SmoothL1Loss (
)
)

參考鏈接:GitHberChen/Fast-RCNN-Object-Detection-Pytorch

PS:

廣告時間啦~理工生如何提高人文素養軟實力?快關注微信公眾號:
歡迎掃碼關注~

推薦閱讀:

相关文章