1. 程式人生 > >如何正確的理解RPN網路的train和test

如何正確的理解RPN網路的train和test

剛開始學Faster RCNN時,遇到些困惑不知其他人有沒有:

1、RPN網路訓練的輸出是什麼?
2、RPN網路在train中的作用是什麼?
3、RPN網路在test中的作用是什麼?
其實這些我們如果不看原始碼都很難真正理解!

以Faster-RCNN_TF的原始碼為例,以下程式碼取自./lib/networks/VGGnet_train.py

 #========= RPN ============
 #以下程式碼的先後順序我調整了一下,便於理解
 (self.feed('conv5_3')
     .conv(3,3,512,1,1,name='rpn_conv/3x3'
) .conv(1,1,len(anchor_scales)*3*2 ,1 , 1, padding='VALID', relu = False, name='rpn_cls_score')) (self.feed('rpn_conv/3x3') .conv(1,1,len(anchor_scales)*3*4, 1, 1, padding='VALID', relu = False, name='rpn_bbox_pred')) .anchor_target_layer(_feat_stride, anchor_scales, name = 'rpn-data'
))

重點

anchor_target_layer的返回值’rpn-data’,這是一個字典
key分別是:rpn_labels, rpn_bbox_targets, rpn_bbox_inside_weights, rpn_bbox_outside_weights

rpn_labels
是 [1,1,A*height,width],如果把它reshape成[1,A,height,width]會更好理解,即feature map上每一點
都是一個anchor,每個anchor對應A個bbox,如果一個bbox與gt_box的重疊度大於0.7(其實還有一個條件),就認為這個bbox包含一個前景,則
rpn_labels 矩陣中相應位置就設定為1。
gt_box的label不能直接用來做訓練的目標(target),在訓練中使用rpn_labels作為訓練的目標
gt_box的唯一作用就在於判斷產生的共A*W*H個bbox哪些屬於前景,哪些不屬於

,將那些屬於前景的bbox設定為訓練目標去訓練rpn_cls_score_reshape。
在test中,正好相反,訓練好的網路會產生一個rpn_cls_score_reshape,它可以轉化成一個[1,A,height,width]的矩陣
#proposal_layer 產生的[1,A,height,width]個bbox哪些屬於前景,哪些屬於背景。我們會把屬於前景的挑出來,
按照得分排序,取前300個輸入後面的fc層,fc層會產生兩個輸出:
一個是cls_score,用於判斷bbox中物體的型別
另一個是bbox_pred,用於微調bbox,使其向gt_box進一步靠近(由於bbox都是從anchor產生的,他們不會和gt_box重合,還需要進一步微調)

rpn_bbox_targets
根據 rpn_labels 我們已經可以挑選出300個bbox,這些bbox都是在[1,W,H,A*4]中根據與gt_box的重合程度挑選出來的,與gt_box並不相同,有一些偏差,這些偏差表示為[dx,dy,dw,dh],這就是rpn_bbox_targets。
因為傳進後面全卷積網路的是bbox,與gt_boxes不完全重合,為了使最終的結果更加接近gt_box,還需要進一步微調
而全卷積層的輸出bbox_pred就是用於微調的,rpn_bbox_targets就是它訓練的目標(target)
損失函式的計算:

# RPN
# classification loss
rpn_cls_score = tf.reshape(self.net.get_output('rpn_cls_score_reshape'),[-1,2])
rpn_label = tf.reshape(self.net.get_output('rpn-data')[0],[-1])
rpn_cls_score = tf.reshape(tf.gather(rpn_cls_score,tf.where(tf.not_equal(rpn_label,-1))),[-1,2])
rpn_label = tf.reshape(tf.gather(rpn_label,tf.where(tf.not_equal(rpn_label,-1))),[-1])
rpn_cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=rpn_cls_score, labels=rpn_label))

# bounding box regression L1 loss
rpn_bbox_pred = self.net.get_output('rpn_bbox_pred')
rpn_bbox_targets = tf.transpose(self.net.get_output('rpn-data')[1],[0,2,3,1])
rpn_bbox_inside_weights = tf.transpose(self.net.get_output('rpn-data')[2],[0,2,3,1])
rpn_bbox_outside_weights = tf.transpose(self.net.get_output('rpn-data')[3],[0,2,3,1])

rpn_smooth_l1 = self._modified_smooth_l1(3.0, rpn_bbox_pred, rpn_bbox_targets, rpn_bbox_inside_weights, rpn_bbox_outside_weights)
rpn_loss_box = tf.reduce_mean(tf.reduce_sum(rpn_smooth_l1, reduction_indices=[1, 2, 3]))

其餘程式碼:

# Loss of rpn_cls & rpn_boxes

(self.feed('rpn_conv/3x3')
     .conv(1,1,len(anchor_scales)*3*4, 1, 1, padding='VALID', relu = False, name='rpn_bbox_pred'))

#========= RoI Proposal ============
(self.feed('rpn_cls_score')
     .reshape_layer(2,name = 'rpn_cls_score_reshape')
     .softmax(name='rpn_cls_prob'))

(self.feed('rpn_cls_prob')
     .reshape_layer(len(anchor_scales)*3*2,name = 'rpn_cls_prob_reshape'))

(self.feed('rpn_cls_prob_reshape','rpn_bbox_pred','im_info')
     .proposal_layer(_feat_stride, anchor_scales, 'TRAIN',name = 'rpn_rois'))

(self.feed('rpn_rois','gt_boxes')
     .proposal_target_layer(n_classes,name = 'roi-data'))


#========= RCNN ============
(self.feed('conv5_3', 'roi-data')
     .roi_pool(7, 7, 1.0/16, name='pool_5')
     .fc(4096, name='fc6')
     .dropout(0.5, name='drop6')
     .fc(4096, name='fc7')
     .dropout(0.5, name='drop7')
     .fc(n_classes, relu=False, name='cls_score')
     .softmax(name='cls_prob'))

(self.feed('drop7')
     .fc(n_classes*4, relu=False, name='bbox_pred'))