目標檢測網絡Faster RCNN詳解(一)


  在RCNN,Fast RCNN之后,Ross B. Girshick在2016年提出Faster RCNN,將特征提取(feature extraction),proposal提取,目標定位location,目標分類classification整合到了一個網絡中,性能大幅提升。作為Two-stage的代表,相比於yolo,ssd等one-stage檢測方法,Faster RCNN的檢測精度更高,速度相對較慢。

  為了加深對Faster RCNN的理解,還是從網絡結構,正負樣本分配,loss函數三個方面來記錄下自己的學習過程。原版Faster RCNN的backbone為VGG16, 而實際工作中,我主要使用Resnet50為backbone的Faster RCNN,這里以Resnet50_Faster_RCNN為例進行說明

1. Resnet50_Faster_RCNN 網絡結構

  下面兩張圖中,第一張是Resnet50_Faster_RCNN的網絡結構流程圖,第二張是詳細展開后的網絡卷積模塊。可以發現其網絡結構中主要包括Resnet50 Conv layers,RPN(Region Proposal Network), ROIPooling/ROIAlign, class/box Predictors四個模塊:

1.Resnet50 Conv layers: 包括了Head, Layer1, Layer2, Layer3, Layer4,主要負責特征提取;(Head, Layer1, Layer2, Layer3為最開始的特征提取,layer4是在確定正負樣本后第二次的特征提取)
2.RPN:主要負責proposal的提取,給出每個proposal的屬於正負樣本的分數,並修正其位置;
3.ROIPooling/ROIAlign:根據RPN給出的Proposal,從Feature map中得到每個proposal對應的局部feature,並進行匯總后輸入下一層網絡
4.class/box Predictors: 預測proposal所屬類別(class), 以及其位置偏移量(box regression),從而修正box

 圖1 resnet50_faster_rcnn流程結構圖

 

 

 圖2 resnet50_faster_rcnn網絡結構圖

   網絡結構中比較難理解的主要是RPN和ROIPooling/ROIAlign兩部分,需要詳細了解下。

RPN網絡流程

  1. 上述圖中Layer3得到的feature為B*1024*36*63,送入RPN網絡首先卷積得到B*1024*36*63,然后score分支預測proposal的概率,尺寸為B*15*36*63(每個位置有15個anchor),loc預測proposal的偏移量,尺寸為B*60*36*63;

  2. 根據B*15*36*63的分數置信度,B*60*36*63的位置偏移量,利用已經設置好的34020(36*63*15)個anchor和匹配規則,選擇128個proposal(32個正樣本,96個負樣本)

 ROIPooling/ROIAlign流程

  (ROIPooling和ROIAlign詳細解釋參見:https://zhuanlan.zhihu.com/p/73138740)

  1.上述圖中Layer3得到的feature為B*1024*36*63,RPN網絡得到proposal為B*128*4,對於每一個proposal根據其坐標位置,在feature中找到其對應的局部特征sub_feature(例如1024*20*30),對這個局部特征進行AvgPooling得到1024*14*14。共有B*128個proposal,因此所有proposal處理完成得到尺寸為[B*128, 1024, 14, 14]

 

2. 正負樣本分配

  faster_rcnn網絡結構中有兩次正負樣本分配,一是從設置好的anchor中挑選樣本給RPN網絡學習,從而使RPN網絡預測出精准的proposal;二是從RPN預測出的proposal挑選樣本給RCNN網絡(layer4+predictor部分)學習,使RCNN網絡預測proposal的類別,並修正其坐標位置

2.1 faster_rcnn網絡anchor設置

  faster_rcnn論文采用9個anchor(三個尺寸,三個比例),這里采用了15個anchor(5個尺寸,三個比例),增加對小目標的檢測。以網絡layer3輸出feature為基礎,其anchor設置的示意圖如下:

 

   產生anchor的簡單示例代碼如下:

import numpy as np

strides=16
# scales=(8, 16, 32)
scales=(2,4,8,16,32)
ratios=(0.5, 1, 2)
alloc_size=(36, 63)

scales = [i*strides for i in scales]
print(scales)
center_point = ((strides-1)/2, (strides-1)/2)
anchors = []
for scale in scales:
    for ratio in ratios:
        area = scale*scale
        ws = np.round(np.sqrt(area/ratio))
        hs = np.round(np.sqrt(area*ratio))
        anchor = [center_point[0]-(ws-1)/2, center_point[1]-(hs-1)/2,
                  center_point[0]+(ws-1)/2, center_point[1]+(hs-1)/2]
        anchors.append(anchor)

print(np.array(anchors))
產生anchor

2.2 RPN網絡正負樣本分配

  RPN網絡的目的是給出精確的proposal,其學習樣本來自於設置的anchor。如上一步共設置了34020個anchor,根據這些anchor和gt_box的IOU,挑選出256個anchor作為樣本給RPN網絡學習,anchor挑選流程如下:

1. 去掉anchor中坐標超出圖片邊界的(圖片為562*1000)
2. 計算所有anchor和gt_box的IOU,和gt_box具有最大IOU的anchor為正樣本(無論是否滿足IOU>0.7),剩余的anchor, IOU>0.7的為正樣本,0<IOU<0.3的為負樣本
3. 挑選出256個樣本,正負樣本各128個。(若正樣本不夠128個時,有多少取多少,若正本超過128個,隨機選取128個正樣本,多余的標注未忽略樣本;負樣本一般會多余128個,隨機選取128個負樣本,多余的標注未忽略樣本)
(最后會出現兩種情況,一是正負樣本各128個,總共256個樣本;二是正樣本少於128個(如50個),負樣本128個,總樣本少於256個)

2.3 RCNN網絡正負樣本分配

   同樣的,對於layer3 feture(36*63), 每個像素點預測15個proposl,因此RPN網絡會預測出34020個proposal,需要從這34020個proposal中選出128個proposal給RCNN網絡的(layer4+predictor)模塊來學習。proposal挑選流程如下:

1.共34020(36*63*15)個proposal,對proposal邊界進行clip,去掉寬高太小(小於16)的proposal;
2.根據預測分數排序,對前12000個proposal進行box_nms(閾值為0.7),nms后選擇排序最前面的2000個proposal
3.rpn_score>0, IOU>0.5的proposal為正樣本,IOU<0.5的為負樣本;rpn_score<=0的為忽略樣本
4.從2000個proposal中挑出128個樣本,正樣本最多32個,負樣本96個(128-32)

 

3. Loss函數理解

  faster_rcnn的損失包括RPN loss和RCNN loss兩部分,都包括置信度和位置偏移量損失;RPN Loss的置信度采用二分類交叉熵損失SigmoidBinaryCrossEntropyLoss,位置偏移量采用SmoothL1;RCNN Loss的置信度采用多分類交叉熵損失SoftmaxCrossEntrophyLoss, 位置偏移量采用SmoothL1。faster_rcnn的訓練策略上,原始論文中采用先訓練RPN網絡(只對RPN Loss進行backward),然后再訓練RCNN網絡(只對RCNN Loss進行backward),現在一般都同時訓練RPN和RCNN,即將RPN Loss和RCNN Loss匯總后一起backward,下面是摘錄的部分源碼,便於理解loss的計算和backwar

#self.rpn_cls_loss =mx.gluon.loss.SigmoidBinaryCrossEntropyLoss(from_sigmoid=False)
#self.rpn_box_loss = mx.gluon.loss.HuberLoss(rho=config.rpn_smoothl1_rho)  # == smoothl1
#self.rcnn_cls_loss = mx.gluon.loss.SoftmaxCrossEntropyLoss()
#self.rcnn_box_loss = mx.gluon.loss.HuberLoss(rho=config.rcnn_smoothl1_rho)  # == smoothl1

with autograd.record():
    gt_label = label[:, :, 4:5]
    gt_box = label[:, :, :4]
    cls_pred, box_pred, _, _, _Z, rpn_score, rpn_box, _, cls_targets, \
    box_targets, box_masks, _ = self.net(data, gt_box, gt_label)
    # losses of rpn
    rpn_score = rpn_score.squeeze(axis=-1)
    num_rpn_pos = (rpn_cls_targets >= 0).sum()
    rpn_loss1 = self.rpn_cls_loss(rpn_score, rpn_cls_targets,
                                  rpn_cls_targets >= 0) * rpn_cls_targets.size / num_rpn_pos   #rpn_cls_targets中1表示正樣本,0負樣本,-1忽略樣本
    rpn_loss2 = self.rpn_box_loss(rpn_box, rpn_box_targets,
                                  rpn_box_masks) * rpn_box.size / num_rpn_pos          #rpn_box_masks中1表示正樣本,0表示負樣本和忽略樣本
    # rpn overall loss, use sum rather than average
    rpn_loss = rpn_loss1 + rpn_loss2
    # losses of rcnn
    num_rcnn_pos = (cls_targets >= 0).sum()
    rcnn_loss1 = self.rcnn_cls_loss(
        cls_pred, cls_targets, cls_targets.expand_dims(-1) >= 0) * cls_targets.size/num_rcnn_pos     #cls_targets中1表示正樣本,0負樣本,-1忽略樣本       
    rcnn_loss2 = self.rcnn_box_loss(box_pred, box_targets, box_masks) * box_pred.size/num_rcnn_pos   #box_masks中1表示正樣本,0表示負樣本和忽略樣本
    rcnn_loss = rcnn_loss1 + rcnn_loss2
    # overall losses
    total_loss = rpn_loss.sum() * self.mix_ratio + rcnn_loss.sum() * self.mix_ratio
    total_loss.backward()

  

參考:https://zhuanlan.zhihu.com/p/273587749?utm_source=wechat_session   

      https://zhuanlan.zhihu.com/p/82185598?utm_source=wechat_session

      https://zhuanlan.zhihu.com/p/123962549

      https://zhuanlan.zhihu.com/p/31426458


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM