Faster-Rcnn代碼實現的理解之損失函數

在《Faster-Rcnn代碼之網絡架構》中,我們採用DetectionTeamUCAS中的Faster-RCNN Tensorflow實現,對Faster-Rcnn的網絡架構的代碼進行了理解,在這個文章中,我們將對Faster-RCNN的損失函數的定義有關的代碼做一些筆記。筆記中難免會出現一些理解的偏差,如有錯誤,還請各位看官不吝指出,共同學習,共同提高。

先放上faster-rcnn中RPN 網絡的loss的公式:

     

這裏的兩個slice 來源於:https://www.slideshare.net/xavigiro/faster-rcnn-towards-realtime-object-detection-with-region-proposal-networks

其中loss函數中的t參量以及在邊框迴歸中使用的smooth_l1 函數定義爲下圖:

之前的Fast-RCNN網絡引入了multi-task loss,在網絡採用了全連接網絡作爲分類和邊框迴歸,因此只有上圖中第二個slice的Loss函數,在Faster-RCNN網絡中,引入了RPN網絡,在訓練RPN網絡時候,則引入了RPN網絡的Loss函數,如上圖中第一個slice。

有了如上的概念後,根據我們的網絡,我們需要獲取的參量爲:

RPN網絡:

1.anchor的類別預測 pi 

2.ground truth的類別標籤 pi* 

3. 256個位置對應的 256×(scale×ratios) 個anchor對應的編碼後的 t 預測矩陣,

4.每一個anchor對應的最大重疊率的ground truth bbox的 t target 矩陣

Fast-RCNN網絡:

1.真實的類別標籤 u

2.預測的類概率 p

3.真實的ground truth 對應的 t 矩陣

4.預測的bbox 對應的 t 矩陣

有了這些基本的定義,我們回到代碼中:

loss_dict = self.build_loss(
                                        rpn_box_pred=rpn_box_pred,
                                        rpn_bbox_targets=rpn_bbox_targets,
                                        rpn_cls_score=rpn_cls_score,
                                        rpn_labels=rpn_labels,
                                        bbox_pred=bbox_pred,
                                        bbox_targets=bbox_targets,
                                        cls_score=cls_score,
                                        labels=labels)

final_bbox, final_scores, final_category = self.postprocess_fastrcnn(
                                        rois=rois,
                                        box_ppred=bbox_pred,
                                        scores=cls_prob,
                                        img_shape=img_shape)    

在代碼中,定義了一個loss_dict,用來保存RPN網絡和Faster-RCNN的loss,其接收的參量如代碼所示。回到build_loss 函數,其定義如下:

 with tf.variable_scope('build_loss') as sc:
            with tf.variable_scope('rpn_loss'):
                利用smooth_l1_loss_rpn 函數計算邊框迴歸誤差
                rpn_bbox_loss = losses.smooth_l1_loss_rpn(bbox_pred=rpn_box_pred,
                                                          bbox_targets=rpn_bbox_targets,
                                                          label=rpn_labels,
                                                          sigma=cfgs.RPN_SIGMA)、

                # rpn_bbox_loss = tfapi_loss.smooth_l1_loss_rpn(bbox_pred=rpn_box_pred,
                #                                               bbox_targets=rpn_bbox_targets,
                #                                               label=rpn_labels,
                #                                               sigma=cfgs.RPN_SIGMA)
                # rpn_cls_loss:
                # rpn_cls_score = tf.reshape(rpn_cls_score, [-1, 2])
                # rpn_labels = tf.reshape(rpn_labels, [-1])
                # ensure rpn_labels shape is [-1]
                只選出那些 label 爲 0 與 1 的框label 所對應的 rpn_cls_score 
                rpn_select = tf.reshape(tf.where(tf.not_equal(rpn_labels, -1)), [-1])
                rpn_cls_score = tf.reshape(tf.gather(rpn_cls_score, rpn_select), [-1, 2])               
                選出那些label所對應的 ground_truth 對應的標籤 函數接收的時候爲所有的anchor對應的,針對label的賦值,來小批量的處理
                rpn_labels = tf.reshape(tf.gather(rpn_labels, rpn_select), [-1])
                
                採用交叉熵衡量分類誤差,這裏只有 有目標與沒目標之間兩類
                rpn_cls_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=rpn_cls_score,
                                                                                             labels=rpn_labels))
                對分類以及迴歸誤差分別進行加權後return
                rpn_cls_loss = rpn_cls_loss * cfgs.RPN_CLASSIFICATION_LOSS_WEIGHT
                rpn_bbox_loss = rpn_bbox_loss * cfgs.RPN_LOCATION_LOSS_WEIGHT

在計算smooth_l1 誤差時候函數計算爲:

def _smooth_l1_loss_base(bbox_pred, bbox_targets, sigma=1.0):
    '''

    :param bbox_pred: [-1, 4] in RPN. [-1, cls_num+1, 4] in Fast-rcnn
    :param bbox_targets: shape is same as bbox_pred
    :param sigma:
    :return:
    '''
    sigma_2 = sigma**2

    box_diff = bbox_pred - bbox_targets
    
    abs_box_diff = tf.abs(box_diff)

    smoothL1_sign = tf.stop_gradient(
        tf.to_float(tf.less(abs_box_diff, 1. / sigma_2)))
    這裏得到的是根據smooth_l1 公式得到的 , 這裏 x 是 bbox的差值,如果真實值與預測
    值之間的差值小於1/sigma_2 則 採用第一種計算方式,如果大於 1/sigma_2 則採用第二種
    計算方式
    loss_box = tf.pow(box_diff, 2) * (sigma_2 / 2.0) * smoothL1_sign \
               + (abs_box_diff - (0.5 / sigma_2)) * (1.0 - smoothL1_sign)
    return loss_box

#這個代碼實現的作者,並沒有直接採用0.5 而是再引入了 一個sigma 因子來約束


def smooth_l1_loss_rpn(bbox_pred, bbox_targets, label, sigma=1.0):
    '''

    :param bbox_pred: [-1, 4]      all encode rois  every feature map location with 9 anchors
    #this is the RPN direct output
    :param bbox_targets: [-1, 4]   all anchors t matrix computed by gt_boxes
    :param label: [-1]             define all anchors pos or negative
    :param sigma:
    :return:
    '''
    value = _smooth_l1_loss_base(bbox_pred, bbox_targets, sigma=sigma)
    value = tf.reduce_sum(value, axis=1)  # to sum in axis 1
    這一塊對 相應的 tx+ty+tw+th 求和,獲得每一個anchor對應損失值
    rpn_select = tf.where(tf.greater(label, 0))
        
    # rpn_select = tf.stop_gradient(rpn_select) # to avoid
    在所有的value中,只選擇那些爲正例的樣本,因爲爲
    負例的樣本 label 爲 0 因此在公式中不參與貢獻,但是最後均值的時候確實 正負一起考慮的。
    selected_value = tf.gather(value, rpn_select)
    non_ignored_mask = tf.stop_gradient(
        1.0 - tf.to_float(tf.equal(label, -1))) # positve is 1.0 others is 0.0

    bbox_loss = tf.reduce_sum(selected_value) / tf.maximum(1.0, tf.reduce_sum(non_ignored_mask))  
    這裏講選出來的正樣本所有的bbox的loss求和後除以這個batch中 label爲0 與
    label爲1 的樣本的總數,起到了公式中除以N(pos+neg)的作用 

    return bbox_loss

 在計算RPN的分類誤差的時候,定義如下:

rpn_select = tf.reshape(tf.where(tf.not_equal(rpn_labels, -1)), [-1])
rpn_cls_score = tf.reshape(tf.gather(rpn_cls_score, rpn_select), [-1, 2])
rpn_labels = tf.reshape(tf.gather(rpn_labels, rpn_select), [-1])


rpn_cls_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=rpn_cls_score,
                                                                             labels=rpn_labels))

首先從所有的rpn_label 中選出僅僅作爲minibatch參與計算loss的label,獲得相關的rpn網絡對於minibatch中anchor的二分類值,最後計算標籤與預測值之間的交叉熵,得到分類的誤差,得到這兩類誤差後,就可以對其進行加權相加得到RPN網絡的總誤差值。在計算Fast-RCNN的邊框迴歸誤差以及分類誤差的時候,由於RPN與Fast-RCNN採用的loss相同,只是在具體的類別上不一致,因此採用了與RPN網絡同樣的誤差函數:

with tf.variable_scope('FastRCNN_loss'):
    if not cfgs.FAST_RCNN_MINIBATCH_SIZE == -1:
          bbox_loss = losses.smooth_l1_loss_rcnn(
                        bbox_pred=bbox_pred,
                        bbox_targets=bbox_targets,
                        label=labels,
                        num_classes=cfgs.CLASS_NUM + 1,
                        sigma=cfgs.FASTRCNN_SIGMA)

 這裏的輸入爲 bbox_pred ,是特徵圖上對應的roi經過Pooling,全連接層得到bbox_pred,bbox_targets 是 roi對應的具有最大重疊率的ground truth 框的映射因子t矩陣,labels 則是每一個target對應的類別。具體自己算Fast-RCNN的損失函數如下:

def smooth_l1_loss_rcnn(bbox_pred, bbox_targets, label, num_classes, sigma=1.0):
    '''

    :param bbox_pred: [-1, (cfgs.CLS_NUM +1) * 4]
    :param bbox_targets:[-1, (cfgs.CLS_NUM +1) * 4]
    :param label:[-1]
    :param num_classes:
    :param sigma:
    :return:
    '''

    outside_mask = tf.stop_gradient(tf.to_float(tf.greater(label, 0)))
    選出那些需要計算損失的roi所在的標籤
    bbox_pred = tf.reshape(bbox_pred, [-1, num_classes, 4]) 
    每一個roi,對所有的類別預測位置
    bbox_targets = tf.reshape(bbox_targets, [-1, num_classes, 4])

    value = _smooth_l1_loss_base(bbox_pred,
                                 bbox_targets,
                                 sigma=sigma)

    value = tf.reduce_sum(value, 2)
    value = tf.reshape(value, [-1, num_classes])
    得出roi 在每個類上的預測誤差
    inside_mask = tf.one_hot(tf.reshape(label, [-1, 1]),
                             depth=num_classes, axis=1)
    得到相關的roi的類別標籤的one-hot編碼
    inside_mask = tf.stop_gradient(
        tf.to_float(tf.reshape(inside_mask, [-1, num_classes])))
    
    normalizer = tf.to_float(tf.shape(bbox_pred)[0])  

    bbox_loss = tf.reduce_sum(
        tf.reduce_sum(value * inside_mask, 1)*outside_mask) / normalizer
    參與計算的roi(數量爲roi_per_img,定義在cfg文件中) 的平均迴歸誤差
    return bbox_loss

 在計算Fast-RCNN的分類誤差時,採用所有類的交叉熵,得到迴歸誤差與分類誤差加權。

cls_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
                        logits=cls_score,
                        labels=labels))  # beacause already sample before
cls_loss = cls_loss * cfgs.FAST_RCNN_CLASSIFICATION_LOSS_WEIGHT 
bbox_loss = bbox_loss * cfgs.FAST_RCNN_LOCATION_LOSS_WEIGHT

根據論文的公式,最後將RPN網絡的(分類,迴歸)誤差與Fast-RCNN的(分類,迴歸)誤差相加後作爲總的誤差進行訓練即可。

這一章主要介紹了經過RPN網絡以及Fast-RCNN網絡後得到的anchors與rois怎麼建立起和ground truth 類別與座標之間的關係,誤差的建立,下一章則介紹訓練的方式。

因爲代碼量較大,先開始整體理解了一下,後面分了幾次對於關鍵函數做了理解,寫作上免不了有疏漏,錯誤,理解上的問題等,如有問題,還請及時糾正,謝謝!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章