Faster-RCNN tensorflow 程序細節


tf-faster-rcnn github:https://github.com/endernewton/tf-faster-rcnn

backbone,例如vgg,conv層不改變feature大小,pooling層輸出(w/2, h/2),有4個pooling所以featuremap變為原圖1/16大小。

檢測RPN模塊

例如任意圖片reshape到800*600,輸入網絡過vgg,conv5_3->rpn_conv/3*3->rpn_relu 得到feature map (1,512,50,38),接下來兩個1*1的卷積分別用於每個點9個anchor前背景分類(1,18,50,38),anchor偏移量的預測(1,36,50,38),50 x 38 x 9 = 17100 從原圖中扣出來的anchor數。(rpn_bbox_pred(偏移)+rpn_cls_prob_reshape(前背景))->proposal_layer 修正后的proposal,濾掉超出原圖的proposal后NMS以及概率排序等操作后獲得最終的boundingbox。輸出大小為:(N,4),這里的N與NMS以及概率排序閾值有關,得到的就是boundingbox的四個坐標。

  • 生成anchors,利用tx, ty, tw, th對所有的anchors做bbox regression回歸(這里的anchors生成和訓練時完全一致)
  • 按照輸入的foreground softmax scores由大到小排序anchors,提取前pre_nms_topN(e.g. 6000)個anchors,即提取修正位置后的foreground anchors。
  • 限定超出圖像邊界的foreground anchors為圖像邊界(防止后續roi pooling時proposal超出圖像邊界)
  • 剔除非常小(width<threshold or height<threshold)的foreground anchors
  • 進行non maximum suppression
  • 再次按照nms后的foreground softmax scores由大到小排序fg anchors,提取前post_nms_topN(e.g. 300)結果作為proposal輸出。

1. 生成anchors,首先生成一個base anchor,然后以base anchor為基礎,在原圖像中移動,生成原圖像中的anchors。

2. 在conv5-3這一層中,第一個feature的點,對應的原圖像中[0,0]的點,第二個feature的點,對應原圖像中的[16,0]的點,通過乘以步長,可以建立feature map上的特征和原圖像中anchors的映射關系。

3. RPN層的第一步是用[3, 3, 512]的卷積核在conv5-3上進行卷積操作,conv5-3上的每一個像素點,對應的是原圖中的一個近似於16*16的區域,所以這也就是為什么文章中說的把每一個中心點的像素轉換為256-D的vector,但TensorFlow實現這里用了更厚的feature map,個數變為了512,於是比如[0, 0, :]就是一個512-D的vector。

4. 在這個512-D的vector基礎上又有兩個全卷積網絡,一個是[1, 1]的卷積核,但是輸出是9*2,因為9個anchor,每個anchor都有兩個值,有目標還是沒有目標,所以是18,所以這個輸出的大小的height和width與conv5-4的大小一致。用於定義objectness。

5. 同樣的,在這個512-D向量的基礎上,定義了一個[1, 1]的卷積核,輸出為9*4,因為9個anchor,每個anchor都有4個值,這四個值為預測的tx,ty,tw,th。所以這個輸出feature的height和width與conv5-3的大小一致。這是用於定義pred-box的。

6. 

rois, roi_scores = self._proposal_layer(rpn_cls_prob, rpn_bbox_pred, "rois")
 rpn_cls_prob: RPN層輸出的objectness的值
 rpn_bbox_pred: RPN層輸出的box的取值,即:tx,ty,tw,th

該方法首先根據rpn_bbox_pred來生成原始圖像中的anchor的預測坐標,由於rpn_bbox_pred是tx,ty,tw, th,這四個值的計算方法可看論文。對rpn_cls_prob進行排序,根據objectness分數的高低排序,然后選出需要保留的proposal的個數,論文中設置的為6000。然后從這些proposal中使用nms算法,篩選出最后的proposal。返回這些proposal和scores。注:scores和proposal都是排序后的。

7. 

rpn_labels = self._anchor_target_layer(rpn_cls_score, "anchor")
 rpn_cls_score: RPN層輸出的box的取值,即:tx, ty, tw, th

該方法首先把越過邊界的anchor都過濾掉,保留都在圖像范圍內的anchor。然后創建一個全部是-1的labels。接着計算每個anchor和ground-truth的overlap,overlap返回一個二維數組,行數代表的是anchor個數,列數代表的ground-truth的個數。從中選擇max-overlap,如果max-overlap大於某個閾值,那么這個anchor的label就設置為包含目標,用1表示,如果max-overlap小於某個閾值,那么這個anchor的label就設置為0。然后再找到每個ground-truth和anchor覆蓋最大的anchor的index,把這些anchor設置為1。避免某個ground-truth沒有對應的anchor。對每個anchor都設置是否含有目標后,利用anchors和每個anchors對應的max-overlap的ground-truth來計算該anchor對應的tx*, ty*, tw*, th*。然后設置bbox_inside_weights,這個權值起到的作用是論文中公式(1)中的pi*。bbox_outside_weights該權值用來設置在所有樣本中,positive和negitive的權值。由於上述所有操作都是在沒有越界的anchor中進行的,所以需要還原回到所有的anchors中。於是使用方法_unmap。 
該方法最后返回:

rpn_labels:這是真實的每個anchor是含有目標還是沒有目標. 用於loss計算。

rpn_bbox_targets:這個是真實的每個anchor與其覆蓋最大的ground-truth來計算得到的tx, ty, tw, th。

8. 

rois, _ = self._proposal_target_layer(rois, roi_scores, "rpn_rois")
rois: 為第6步方法中生成的rois
roi_scores: 為第6步中生成的roi的scores

該方法首先計算fg_rois_per_image也就是在一個batch中認為是前景的roi的個數,剩余的被認為是背景。然后計算每個rois和ground-truth的overlap,該overlap返回的數組形式為[roi_size, gt_size]。從這些roi中選擇隨機選擇一些正樣本和負樣本,max-overlap大於某個閾值的roi被認為是正樣本,找到正樣本后,建立label,label是每個正樣本的類別標簽,由於20類,所以就是某個數字。按照比率設定背景樣本,背景樣本的標簽為0。該方法中調用了一個_sample_rois的方法,該方法返回值為:labels,每個roi的類別標簽,rois,是對原來所有rois進行正負樣本過濾,選擇出來的正樣本和負樣本。roi_scores, 對應選擇出來的正負樣本的objectness scores。bbox_targets,該返回值為數組:target_nums = [num_rois, num_class*4]的數組,取其中的一行作為例子,比如target_nums[0],該vector的長度為80, 首先設置全部為0, 如果target_nums[0]的類別為3,那么設置target_nums[0, 3*4:(3*4+4)]的取值為tx,ty,tw,th。這個方法返回的rois會接着送到后面的fast-rcnn網絡。該方法中計算出來的labels,boxs都作為真實值,rois from feature conv5_3

9. loss

# RPN, class loss 
# 計算RPN的objectness的loss,首先獲取rpn網絡輸出的logits,然后從anchor和ground-truth中計算得到的label中選擇不為-1的anchors。然后對這些anchor來計算cross-entropy 
rpn_cls_score = tf.reshape(self._predictions[‘rpn_cls_score_reshape’], [-1,2]) 
rpn_label = tf.reshape(self._anchor_targets[‘rpn_labels’], [-1]) 
rpn_select = tf.where(tf.not_equal(rpn_label,-1)) 
rpn_cls_score = tf.reshape(tf.gather(rpn_cls_score, rpn_select),[-1,2]) 
rpn_label = tf.reshape(tf.gather(rpn_label, rpn_select),[-1]) 
rpn_cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=rpn_cls_score, labels=rpn_label)) 
# RPN, bbox loss 
# 采用smooth_l1_loss來計算bbox的loss。rpn_bbox_inside_weights用於把是object的box過濾出來,因為並不是所有的anchors都是有object的。rpn_bbox_inside_weights用於設置標記為1的box和標記為0的box的權值比率。 
rpn_bbox_pred = self._predictions[‘rpn_bbox_pred’] 
rpn_bbox_targets = self._anchor_targets[‘rpn_bbox_targets’] 
rpn_bbox_inside_weights = self._anchor_targets[‘rpn_bbox_inside_weights’] 
rpn_bbox_outside_weights = self._anchor_targets[‘rpn_bbox_outside_weights’]

 1 rpn_loss_box = self._smooth_l1_loss(rpn_bbox_pred, rpn_bbox_targets, rpn_bbox_inside_weights, rpn_bbox_outside_weights, sigma=sigma_rpn, dim=[1,2,3])
 2 
 3    # RCNN, class loss
 4    cls_score = self._predictions["cls_score"]
 5    label = tf.reshape(self._proposal_targets["labels"],[-1])
 6    cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=cls_score, labels=label))
 7 
 8    # RCNN, bbox loss
 9    bbox_pred = self._predictions['bbox_pred']
10    bbox_targets = self._proposal_targets['bbox_targets']
11    bbox_inside_weights = self._proposal_targets['bbox_inside_weights']
12    bbox_outside_weights = self._proposal_targets['bbox_outside_weights']
13 
14    loss_box = self._smooth_l1_loss(bbox_pred, bbox_targets, bbox_inside_weights, bbox_outside_weights)
15 
16    self._losses['cross_entropy'] = cross_entropy
17    self._losses['loss_box'] = loss_box
18    self._losses['rpn_cross_entropy'] = rpn_cross_entropy
19    self._losses['rpn_loss_box'] = rpn_loss_box
20 
21    loss = cross_entropy + loss_box + rpn_cross_entropy + rpn_loss_box
22    self._losses['total_loss'] = loss
23 
24    self._event_summaries.update(self._losses)
25 
26  return loss

 10 RoI pooling

對每一個RoI,將RoI的坐標從原圖映射到feature map就是簡單除以原圖到feature的放縮尺度16,從而得到feature map上的box坐標,由於box大小不一,所以要逆向考慮,在Pooling的過程中需要計算Pooling后的結果對應到feature map上所占的范圍,在這個范圍內做max pooling。

計算RoI在feature map上的寬高與pooled寬高的比值求得bin的大小[即pooling后featuremap上一個點與RoI上一個patch的映射關系,更具體的就是把feature map分割為 pooled_w*pooled_h 這么多份],由於roi的大小不一致,所以每次都需要計算一次bin的大小。最后在pooled上面循環遍歷channel,h,w這3個維度,將映射后的區域平動到RoI對應的位置[hstart, wstart, hend, wend],統計該bin區域的最大值。

11. 輸入處理

一般resize到固定尺寸,如果要求任意尺寸的輸入那么限制條件就是fc層,如何處理呢?方法一:采用spp-net,固定size的特征金字塔;方法二:直接把fc換為global average pooling


免責聲明!

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



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