faster rcnn中 損失函數(一)——softmax,softmax loss和cross entropy的講解

先理清下從全連接層到損失層之間的計算。來看下面這張圖,(非常好的圖)

 

T類  N表示前一層特徵層flatten後的數字   fltten後的特徵      無限大小的T類                     從0-1的T類向量                                     

這張圖的等號左邊部分就是全連接層做的事,W是全連接層的參數,我們也稱爲權值X是輸入,也就是特徵。從圖上可以看出特徵X是N*1的向量,這是怎麼得到的呢?【答:flat成N*1的向量】這個特徵就是由全連接層前面多個卷積層和池化層處理後得到的,假設全連接層前面連接的是一個卷積層,這個卷積層的輸出是100個特徵(也就是我們常說的feature map的channel爲100),每個特徵的大小是4*4,那麼在將這些特徵輸入給全連接層之前會將這些特徵flat成N*1的向量(這個時候N就是100*4*4=1600)。

解釋完X,再來看W,W是全連接層的參數,是個T*N的矩陣,這個N和X的N對應,T表示類別數,比如你是7分類,那麼T就是7。我們所說的訓練一個網絡,對於全連接層而言就是尋找最合適的W矩陣。因此全連接層就是執行WX得到一個T*1的向量(也就是圖中的logits[T*1]),這個向量裏面的每個數都沒有大小限制的,也就是從負無窮大到正無窮大。然後如果你是多分類問題,一般會在全連接層後面接一個softmax層,這個softmax的輸入是T*1的向量,輸出也是T*1的向量(也就是圖中的prob[T*1],這個向量的每個值表示這個樣本屬於每個類的概率),只不過輸出的向量的每個值的大小範圍爲0到1。

現在你知道softmax的輸出向量是什麼意思了,就是概率,該樣本屬於各個類的概率!

那麼softmax執行了什麼操作可以得到0到1的概率呢?先來看看softmax的公式(以前自己看這些內容時候對公式也很反感,不過靜下心來看就好了):

Sj是softmax的輸出向量S的第j個值

公式非常簡單,前面說過softmax的輸入是WX,假設模型的輸入樣本是I,討論一個3分類問題(類別用1,2,3表示),樣本I的真實類別是2,那麼這個樣本I經過網絡所有層到達softmax層之前就得到了WX,也就是說WX是一個3*1的向量,那麼上面公式中的aj就表示這個3*1的向量中的第j個值最後會得到S1,S2,S3);而分母中的ak則表示3*1的向量中的3個值,所以會有個求和符號(這裏求和是k從1到T,T和上面圖中的T是對應相等的,也就是類別數的意思,j的範圍也是1到T)。因爲e^x恆大於0,所以分子永遠是正數,分母又是多個正數的和,所以分母也肯定是正數,因此Sj是正數,而且範圍是(0,1)。如果現在不是在訓練模型,而是在測試模型,那麼當一個樣本經過softmax層並輸出一個T*1的向量時,就會取這個向量中值最大的那個數的index作爲這個樣本的預測標籤。

因此我們訓練全連接層的W的目標就是使得其輸出的WX在經過softmax層計算後其對應於真實標籤的預測概率要最高。

舉個例子:假設你的WX=[1,2,3],那麼經過softmax層後就會得到[0.09,0.24,0.67],這三個數字表示這個樣本屬於第1,2,3類的概率分別是0.09,0.24,0.67。

#_RPN Module中間內部的就加  _
class _RPN(nn.Module):
    """ region proposal network """
    def __init__(self, din):
        super(_RPN, self).__init__()
        
        self.din = din  # get depth of input feature map, e.g., 512 /zf256 vgg 512
        self.anchor_scales = cfg.ANCHOR_SCALES#[8 16 32]
        self.anchor_ratios = cfg.ANCHOR_RATIOS#[0.5 1 2]
        self.feat_stride = cfg.FEAT_STRIDE[0]#16

        # define the conv+relu layers processing input feature map
        self.RPN_Conv = nn.Conv2d(self.din, 512, 3, 1, 1, bias=True)#input_c,output_c,kenal,stride,padding

        #relu不需要自定義,直接放到foward裏面
         
        # define bg/fg classifcation score layer
        self.nc_score_out = len(self.anchor_scales) * len(self.anchor_ratios) * 2 # 2(bg/fg) * 9 (anchors)
        self.RPN_cls_score = nn.Conv2d(512, self.nc_score_out, 1, 1, 0)

        # define anchor box offset prediction layer
        self.nc_bbox_out = len(self.anchor_scales) * len(self.anchor_ratios) * 4 #       4(coords) * 9 (anchors)
        self.RPN_bbox_pred = nn.Conv2d(512, self.nc_bbox_out, 1, 1, 0)

        # define proposal layer
        self.RPN_proposal = _ProposalLayer(self.feat_stride, self.anchor_scales, self.anchor_ratios)

        # define anchor target layer #初始化自定義_AnchorTargetLayer裏面的__init__
        self.RPN_anchor_target = _AnchorTargetLayer(self.feat_stride, self.anchor_scales, self.anchor_ratios)

        self.rpn_loss_cls = 0
        self.rpn_loss_box = 0

    @staticmethod
    def reshape(x, d):
    #d表示第二個維度
        input_shape = x.size()#
        x = x.view(
            input_shape[0],#輸入的維度 n c h w
            int(d),
            int(float(input_shape[1] * input_shape[2]) / float(d)),
            input_shape[3]
        )
        return x

    def forward(self, base_feat, im_info, gt_boxes, num_boxes):
        # base_feat的來源
        #faster_rcnn中 feed image data to base model to obtain base feature map
        # base_feat = self.RCNN_base(im_data)
        
        #RCNN_base在vgg16的_init_modules中
        # not using the last maxpool layer
        #  self.RCNN_base = nn.Sequential(*list(vgg.features._modules.values())[:-1])
        
        batch_size = base_feat.size(0)

        # return feature map after convrelu layer
        rpn_conv1 = F.relu(self.RPN_Conv(base_feat), inplace=True)
        # get rpn classification score
        rpn_cls_score = self.RPN_cls_score(rpn_conv1)

        rpn_cls_score_reshape = self.reshape(rpn_cls_score, 2)#n 2 9*h w
        rpn_cls_prob_reshape = F.softmax(rpn_cls_score_reshape)

———————————–華麗的分割線——————————————

弄懂了softmax,就要來說說softmax loss了。
那softmax loss是什麼意思呢?如下:

L是損失。

y是標籤向量如y=[0,0,0,1,0]

Sj是softmax的輸出向量S的第j個值,前面已經介紹過了,表示的是預測的這個樣本屬於第j個類別的概率

yj前面有個求和符號,j的範圍也是1到類別數T,因此y是一個1*T的向量裏面的T個值,而且只有1個值是1,其他T-1個值都是0。那麼哪個位置的值是1呢?答案是真實標籤對應的位置的那個值是1,其他都是0。所以這個公式其實有一個更簡單的形式:

當然此時要限定j是指向當前樣本的真實標籤。

來舉個例子吧。假設一個5分類問題,然後一個樣本I的標籤y=[0,0,0,1,0],也就是說樣本I的真實標籤是4,假設模型預測的結果概率(softmax的輸出)p=[0.1,0.15,0.05,0.6,0.1],可以看出這個預測是對的,那麼對應的損失L=-log(0.6),也就是當這個樣本經過這樣的網絡參數產生這樣的預測p時,它的損失是-log(0.6)。那麼假設p=[0.15,0.2,0.4,0.1,0.15],這個預測結果就很離譜了,因爲真實標籤是4,而你覺得這個樣本是4的概率只有0.1(遠不如其他概率高,如果是在測試階段,那麼模型就會預測該樣本屬於類別3),對應損失L=-log(0.1)。那麼假設p=[0.05,0.15,0.4,0.3,0.1],這個預測結果雖然也錯了,但是沒有前面那個那麼離譜,對應的損失L=-log(0.3)。我們知道log函數在輸入小於1的時候是個負數,而且log函數是遞增函數,所以-log(0.6) < -log(0.3) < -log(0.1)。簡單講就是你預測錯比預測對的損失要大,預測錯得離譜比預測錯得輕微的損失要大。

以rpn中的SoftmaxLoss爲例,輸入:(1,2,9*h,w)和標籤(1,1,9*h,w),每一個子loss,就是把(1,2,9*h,w)中的2分類概率和(1,1,9*h,w)對比,找出屬於哪一個,再用求。

———————————–華麗的分割線———————————–

理清了softmax loss,就可以來看看cross entropy了。
corss entropy是交叉熵的意思,它的公式如下:

是不是覺得和softmax loss的公式很像。當cross entropy的輸入P是softmax的輸出時,cross entropy等於softmax loss。

Pj是輸入的概率向量P的第j個值,所以如果你的概率是通過softmax公式得到的,那麼cross entropy就是softmax loss。這是我自己的理解,如果有誤請糾正。

 
            #cross_entropy要求輸入是Variable預測的是2D,label是1D
            self.rpn_loss_cls = F.cross_entropy(rpn_cls_score, rpn_label)#  (b*9*h*w,2)   (b*9*h*w,)

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