1. 程式人生 > >faster rcnn的損失函式理解

faster rcnn的損失函式理解

1. 使用Smoooh L1 Loss的原因

對於邊框的預測是一個迴歸問題。通常可以選擇平方損失函式(L2損失)f(x)=x^2。但這個損失對於比較大的誤差的懲罰很高。

我們可以採用稍微緩和一點絕對損失函式(L1損失)f(x)=|x|,它是隨著誤差線性增長,而不是平方增長。但這個函式在0點處導數不存在,因此可能會影響收斂。

一個通常的解決辦法是,分段函式,在0點附近使用平方函式使得它更加平滑。它被稱之為平滑L1損失函式。它通過一個引數σ 來控制平滑的區域。一般情況下σ = 1,在faster rcnn函式中σ = 3

2. faster rcnn的損失函式

上述公式分為兩個部分,第一部分為分類損失,第二部分為bbox regression損失。其中:

2.1 分類損失 

 其中:

可以看到這是一個這經典的二分類交叉熵損失,對於每一個anchor計算對數損失,然後求和除以總的anchor數量Ncls。在訓練RPN的階段,Ncls = 256,在訓練fast rcnn的階段,Ncls = 128。

2.2 bbox regression損失

其中:

  •   是一個向量,表示該anchor預測的偏移量
  • 是與ti維度相同的向量,表示anchor相對於gt實際的偏移量

R是smoothL1 函式,就是我們上面說的,不同之處是這裡σ = 3,

對於每一個anchor 計算完部分後還要乘以P*,如前所述,P*有物體時(positive)為1,沒有物體(negative)時為0,意味著只有前景才計算損失,背景不計算損失。inside_weights就是這個作用。

對於和Nreg的解釋在RPN訓練過程中如下(之所以以RPN訓練為前提因為此時batch size = 256,如果是fast rcnn,batchsize = 128):

所以就是outside_weights,沒有前景(fg)也沒有後景(bg)的為0,其他為1/(bg+fg)=Ncls

2.3 tensorflow實現

    def _smooth_l1_loss(self, bbox_pred, bbox_targets, bbox_inside_weights, bbox_outside_weights, sigma=1.0, dim=[1]):
        sigma_2 = sigma ** 2
        box_diff = bbox_pred - bbox_targets #ti-ti* 
        in_box_diff = bbox_inside_weights * box_diff  #前景才有計算損失的資格
        abs_in_box_diff = tf.abs(in_box_diff) #x = |ti-ti*|
        smoothL1_sign = tf.stop_gradient(tf.to_float(tf.less(abs_in_box_diff, 1. / sigma_2))) #判斷smoothL1輸入的大小,如果x = |ti-ti*|小於就返回1,否則返回0
        #計算smoothL1損失
        in_loss_box = tf.pow(in_box_diff, 2) * (sigma_2 / 2.) * smoothL1_sign + (abs_in_box_diff - (0.5 / sigma_2)) * (1. - smoothL1_sign)
        out_loss_box = bbox_outside_weights * in_loss_box
        loss_box = tf.reduce_mean(tf.reduce_sum(
            out_loss_box,
            axis=dim
        ))
        return loss_box

一些感悟

論文中把Ncls,Nreg和都看做是平衡分類損失和迴歸損失的歸一化權重,但是我在看tensorflow程式碼實現faster rcnn的損失時發現(這裡以fast rcnn部分的分類損失和box迴歸損失為例,如下),可以看到在計算分類損失時,並沒有輸入Ncls這個引數,只是在計算box迴歸損失的時候輸入了outside_weights這個引數。這時候我才意識到分類損失是交叉熵函式,求和後會除以總數量,除以Ncls已經包含到交叉熵函式本身。

為了平衡兩種損失的權重,outside_weights的取值取決於Ncls,而Ncls的取值取決於batch_size。因此才會有

            # RCNN, class loss
            cls_score = self._predictions["cls_score"]
            label = tf.reshape(self._proposal_targets["labels"], [-1])

            cross_entropy = tf.reduce_mean(
                tf.nn.sparse_softmax_cross_entropy_with_logits(
                    logits=tf.reshape(cls_score, [-1, self._num_classes]), labels=label))

            # RCNN, bbox loss
            bbox_pred = self._predictions['bbox_pred'] #(128,12)
            bbox_targets = self._proposal_targets['bbox_targets'] #(128,12)
            bbox_inside_weights = self._proposal_targets['bbox_inside_weights']#(128,12)
            bbox_outside_weights = self._proposal_targets['bbox_outside_weights']#(128,12)

            loss_box = self._smooth_l1_loss(bbox_pred, bbox_targets, bbox_inside_weights, bbox_outside_weights)