【RCNN系列】Faster RCNN目標檢測模型

    上篇文章闡述了Fast RCNN網絡模型,介於Faster RCNN屬於RCNN系列的經典模型,以及是目前項目暫使用的目標檢測模型,本篇文章會結合論文以及tensorflow版本的代碼實現詳細的闡述該模型。【可能篇幅會很長,畢竟經典模型,慎重】

    Faster RCNN論文:https://arxiv.org/abs/1506.01497

    Faster RCNN論文翻譯:https://alvinzhu.xyz/2017/10/12/faster-r-cnn/

一、概述

    Faster RCNN(Fast Regions with CNN features)相對於Fast RCNN是一種更快速的目標檢測模型。相對於Fast RCNN 66%的mAP,其不僅在縮減訓練、測試時長的情況下,也提高了準確度。(主幹網絡VGG16, mAP70.7%, resnet101 mAP75%)。

【Faster RCNN目標檢測模型提出了與RCNN、SPPNet、Fast RCNN(選擇搜索算法)不一樣的區域提取模式RPN網絡模型,該模型優化了Fast RCNN在時間上的性能瓶頸。RPN網絡和檢測網絡共享全圖的卷積,並且可以在每個位置同時預測目標邊界和objectness得分。】

二、Faster RCNN網絡模型

主體結構
網絡細節

    Faster RCNN物體檢測系統由三個模塊組成:

  • 特徵提取網絡
  • RPN網絡
  • 區域歸一化、物體分類以及邊框迴歸

1、特徵提取網絡

    Faster RCNN提取特徵的主幹網絡可以是VGG16的前13層,13Conv+4次池化。

VGG16

2、RPN網絡

    RPN(Region Proposal Network) 區域提案網絡,較之Fast RCNN單獨的Selective Search選擇搜索算法提取候選框,將候選框提取融合到整個網絡中。

區域提案網絡(Region Proposal Network, RPN),它和檢測網絡共享全圖的卷積特徵,使得區域提案几乎不花時間。RPN是一個全卷積網絡,在每個位置同時預測目標邊界和objectness得分。RPN是端到端訓練的,生成高質量區域提案框,用於Fast R-CNN來檢測。我們通過共享其卷積特徵進一步將RPN和Fast R-CNN合併到一個網絡中。使用最近流行的神經網絡術語“注意力”機制,RPN模塊將某塊anchor box打分比較高,則後面的網絡對其進行訓練。

    說到RPN網絡,則需要提到錨點(anchor)和 邊框迴歸。

                                                                                     【Anchor】

    【原理】滑動窗口在特徵圖上滑動,每經過一個anchor點就會產生3種尺度和3種長寬比K(K = 9)個提案框。每個提案框包含2類信息,一個是該提案框是否包含物體,二是該提案框的座標編碼。每個anchor在cls分類層(softmax二分類)會輸出2*K個分類得分(每個anchor box 前景背景得分),在reg迴歸層(線性迴歸)會產生4K個輸出(每個anchor box都有4個座標),對於大小爲H*W的卷積特徵映射,總共會產生W*H*K個anchor boxes。

    【屬性】使用Anchor提取候選區域具有一些屬性:

    1、平移不變性,即圖片中物體的也會被其它的anchor的anchor box框選到;

    2、基於參照多個尺度和縱橫比設計的錨點,可以簡單地使用單尺度圖像上的卷積特徵,無需額外的成本來縮放尺寸。

(a) 圖像金字塔                                               (b)卷積金字塔                                                   (d)參考框金字塔(RPN)

                                                                         

                                                                                   【邊框迴歸】 

  【摘要】RCNN、Fast RCNN、Faster RCNN都需要用到邊界框迴歸來預測物體的目標檢測框。邊界框迴歸要做的就是利用某種映射關係,使得候選目標框經過映射後更加接近於真實目標框。

Anchor與預測
Anchro邊框、預測邊框、真實邊框

 

  【原理】

    設Anchor的座標                                 預測邊框G'的座標                                     真實邊框G的座標

             A=(A_x, A_y, A_w, A_h)                  G'=(G'_x, G'_y, G'_w, G'_h)                                 G=(G_x,G_y,G_w,G_h)

    邊框迴歸就是尋找一種變換f, 使得 

             f(A_x,A_y,A_w,A_h) = (G'_x,G'_y,G'_w,G'_h)                       (G'_x,G'_y,G'_w,G'_h) \approx (G_x,G_y,G_w,G_h)

    A和G'之間的關係:

    【平移】:

             G'_x=A_w \cdot d_x(A)+A_x; G'_y=A_w \cdot d_y(A)+A_y

    【縮放】:

             G'_w = A_w\cdot exp(d_w(A)); G'_h = A_h\cdot exp(d_h(A))

    邊框迴歸需要【學習】的就是:

                                                        d_x(A),d_y(A),d_w(A),d_h(A)   

   【當anchor A與GT相差較小時(在進行線性迴歸時會篩選anchor和gt IOU在一定範圍內的anchor,這也就保證了anchor與GT的相差不會很大),可以認爲這種變換時一種線性變換,則可以用線性迴歸建模。即Y = WX】

   【Y = WX 】輸入的X是 cnn feature map,定義爲\phi,那麼:

                                                       d_*(A) = W_*^T\cdot \phi (A)

   【d_*(A)表示的是Anchor的座標和預測的貼近真實框的預測的座標的線性關係,而Anchor的座標和真實框的座標的線性關係是怎麼樣呢,這兩個關係又是什麼關係呢,是否是學習與被學習的關係。

    根據上面Anchor和預測G’的座標關係可知Anchor座標和GT座標的關係是:

                  t_x(A)=(G_x-A_x)/A_w;t_y(A)=(G_y-A_y)/A_h;

                 t_w(A)=log(G_w/A_w);t_h(A)=log(G_h/A_h)

    【通過上面的公式,可得知d_*(A)就是要擬合(變成)t_*(A)】,d_*(A) = W_*^T\cdot \phi (A),所以整個線性迴歸其實就是學習一組

                                              W_*[W_x,W_y,W_w,W_y]

    優化目標爲:

                                   w_*=argmin_w_*\sum _i^N(t_*^i - w_*^T\iphi (A^i))^{2}+\lambda ||w_*||^{2}        \lambda ||w_*||^{2}是正則項防止過擬合

    Smooth_L1損失函數:

                                   Loss=\sum _i^N|t_*^i - w_*^T\iphi (A^i)|

                                                                      【Propoasl層】

    Proposal層輸入三個參數,anchor box前景、背景打分,以及anchor box偏移關係和im_Info(原圖信息),那該層的作用:

    對於所有的anchor boxes,結合輸入的偏移關係,進行迴歸,也就是將偏移疊加到anchor boxes上修正原始anchor boxes,

    利用anchor box的打分情況和NMS篩選出一定數目的偏移後的anchor boxes。

 

                                                                           【RPN】

    RPN主要作用是生成區域提案,裏面設計了分類和邊框迴歸。

    【分類】:anchor產生的anchor box 通過softmax二分類網絡判斷該區域提案爲前景(包含目標)和背景(不包含)的得分。基於該分類的得分,可以作爲篩選過多anchor的手段。

    【邊框迴歸】:

         通過anchor和真實邊框的關係(平移、縮放)t_*(A),來優化anchor和預測邊框(其實在這裏預測邊框是不存在,是anchor疊加回歸輸出的偏移)的關係d_*(A),邊框迴歸輸出的不是座標而是一種關係,也就是anchor和預測邊框之間的偏移情況,在Propoasl層纔將迴歸輸出的偏移疊加到anchor座標上,來修正anchor座標,這也是對anchor的第一次修正,後面還有第二次修正。

        邊框迴歸的偏移關係d_*(A) = W_*^T\cdot \phi (A)輸入是特徵向量,所以邊框迴歸要學習的是參數W_*^[W_x,W_y,W_w,W_h]

也就是通過不同目標的特徵,分別學習針對該目標特徵對應的W_*^[W_x,W_y,W_w,W_h]參數,也就是該參數的維度應該是包含可以檢測的所有物體類別數目。

      【輸出】

        RPN網絡最終得到訓練(256)、測試(300)對應的anchor box的前景、後景得分情況,以及該anchor box的座標。

3、區域歸一化、物體分類以及邊框迴歸

    區域歸一化、物體分類以及邊框迴歸這裏的結構就和Fast RCNN基本一致了,在此就不做闡述了。

三、Faster RCNN代碼解析

該圖來自:https://www.cnblogs.com/darkknightzh/p/10043864.html

下面就結合上面的圖,針對代碼一步一步分析:

代碼的主體結構在_build_network函數裏。

    def _build_network(self, is_training=True):
        """
        該函數總體流程:
            1、通過分類網絡(vgg16、resnet)得到特徵net_cov
            2、將net_cov送入rpn網絡得到候選區域anchors,訓練則篩選出2000個anchor,測試則篩選出 
               300個anchors,在進一步篩選出256個anchors用於分類
            3、將256個anchors進行rois_pooling操作得到pool5的7*7的特徵圖
            4、將pool5通過兩個fc得到fc7得到21維的cls_score和21*4的bbox_pred
        :param is_training:
        :return:
        """
        # 是否使用截斷正態分佈
        if cfg.TRAIN.TRUNCATED:
            initializer = tf.truncated_normal_initializer(mean=0.0, stddev=0.01)
            initializer_bbox = tf.truncated_normal_initializer(mean=0.0, stddev=0.001)
        else:
            initializer = tf.random_normal_initializer(mean=0.0, stddev=0.01)
            initializer_bbox = tf.random_normal_initializer(mean=0.0, stddev=0.001)
        # 分類網絡處理生成特徵圖
        net_conv = self._image_to_head(is_training)

        with tf.variable_scope(self._scope, self._scope):
            # 爲特徵圖創建anchors(特徵圖是原圖/16,anchors的個數是特徵圖像素數*9,而每個 
            #anchor是在原圖上的座標,該函數返回anchors的座標矩陣和anchors數量 )
            self._anchor_component()
            # RPN網絡對特徵進行處理,最終得到256(訓練)個anchors對應類別以及座標或者300(測 
            #試)個anchors對應類別以及座標
            rois = self._region_proposal(net_conv, is_training, initializer)
            # roi pooling層將特徵向量resize到指定大小
            if cfg.POOLING_MODE == 'crop':
                pool5 = self._crop_pool_layer(net_conv, rois, "pool5")
            else:
                raise NotImplementedError

        fc7 = self._head_to_tail(pool5, is_training)
        with tf.variable_scope(self._scope, self._scope):
            # region classification
            cls_prob, bbox_pred = self._region_classification(fc7, is_training,
                                                  initializer,initializer_bbox)

        self._score_summaries.update(self._predictions)

        """
          rois: 256個anchors的類別
          cls_prob: 256個anchor中每一類別的概率
          bbox_pred: 預測位置的偏移
        """
        return rois, cls_prob, bbox_pred

    self._image_to_head() 提取輸入圖片特徵

def _image_to_head(self, is_training, reuse=None):
    with tf.variable_scope(self._scope, self._scope, reuse=reuse):
        net = slim.repeat(self._image, 2, slim.conv2d, 64, [3, 3],
                          trainable=False, scope='conv1')
        net = slim.max_pool2d(net, [2, 2], padding='SAME', scope='pool1')
        net = slim.repeat(net, 2, slim.conv2d, 128, [3, 3],
                          trainable=False, scope='conv2')
        net = slim.max_pool2d(net, [2, 2], padding='SAME', scope='pool2')
        net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3],
                          trainable=is_training, scope='conv3')
        net = slim.max_pool2d(net, [2, 2], padding='SAME', scope='pool3')
        net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3],
                          trainable=is_training, scope='conv4')
        net = slim.max_pool2d(net, [2, 2], padding='SAME', scope='pool4')
        net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3],
                          trainable=is_training, scope='conv5')

    self._act_summaries.append(net)
    self._layers['head'] = net

    return net
self._anchor_component()生成anchor box
def _anchor_component(self):
    with tf.variable_scope('ANCHOR_' + self._tag) as scope:
        # 獲取圖片的形狀
        height = tf.to_int32(tf.ceil(self._im_info[0] /             
                     np.float32(self._feat_stride[0])))  # 特徵圖的高(原圖的1/16)
        width = tf.to_int32(tf.ceil(self._im_info[1] / 
                     np.float32(self._feat_stride[0])))  # 特徵圖的寬(原圖的1/16)
        # 配置端到端
        if cfg.USE_E2E_TF:
            anchors, anchor_length = generate_anchors_pre_tf(
                    height,
                    width,
                    self._feat_stride,
                    self._anchor_scales,
                    self._anchor_ratios)
        else:
            anchors, anchor_length = tf.py_func(generate_anchors_pre,
                                                    [height, width,
                                                     self._feat_stride, 
                                                     self._anchor_scales, 
                                                     self._anchor_ratios],
                                                    [tf.float32, tf.int32], 
                                                    name="generate_anchors")
        anchors.set_shape([None, 4])
        anchor_length.set_shape([])
        self._anchors = anchors
        self._anchor_length = anchor_length


def generate_anchors_pre_tf(height, width, feat_stride=16, anchor_scales=(8, 16, 32), 
                            anchor_ratios=(0.5, 1, 2)):
    shift_x = tf.range(width) * feat_stride      # width
    shift_y = tf.range(height) * feat_stride     # height
    shift_x, shift_y = tf.meshgrid(shift_x, shift_y)
    sx = tf.reshape(shift_x, shape=(-1,))
    sy = tf.reshape(shift_y, shape=(-1,))
    shifts = tf.transpose(tf.stack([sx, sy, sx, sy]))
    K = tf.multiply(width, height)
    shifts = tf.transpose(tf.reshape(shifts, shape=[1, K, 4]), perm=(1, 0, 2))
    "生成anchor"
    anchors = generate_anchors(ratios=np.array(anchor_ratios), 
                               scales=np.array(anchor_scales))
    A = anchors.shape[0]
    anchor_constant = tf.constant(anchors.reshape((1, A, 4)), dtype=tf.int32)
    length = K * A
    anchors_tf = tf.reshape(tf.add(anchor_constant, shifts), shape=(length, 4))

    return tf.cast(anchors_tf, dtype=tf.float32), length


def generate_anchors(base_size=16, ratios=[0.5, 1, 2],
                     scales=2 ** np.arange(3, 6)):
    """
    在(0, 0, 15, 15) 的基準窗口上,通過不同尺度、比例變換獲得9個anchor boxes.
    """

    base_anchor = np.array([1, 1, base_size, base_size]) - 1
    ratio_anchors = _ratio_enum(base_anchor, ratios)
    anchors = np.vstack([_scale_enum(ratio_anchors[i, :], scales)
                         for i in range(ratio_anchors.shape[0])])

    """
    anchors = array(
    [
        [ -83.,  -39.,  100.,   56.],
        [-175.,  -87.,  192.,  104.],
        [-359., -183.,  376.,  200.],
        [ -55.,  -55.,   72.,   72.],
        [-119., -119.,  136.,  136.],
        [-247., -247.,  264.,  264.],
        [ -35.,  -79.,   52.,   96.],
        [ -79., -167.,   96.,  184.],
        [-167., -343.,  184.,  360.]
    ])
    """
    return anchors
self._region_proposal(net_conv, is_training, initializer)通過RPN網絡產生對應數量分類和第一次迴歸的anchor座標和得分。
def _region_proposal(self, net_conv, is_training, initializer):
    "特徵提取網絡返回的特徵再經歷個3*3的卷積"
    rpn = slim.conv2d(net_conv, cfg.RPN_CHANNELS, [3, 3], 
                      trainable=is_training, 
                      weights_initializer=initializer,
                      scope="rpn_conv/3x3")
    self._act_summaries.append(rpn)
    "1*1的卷積"
    rpn_cls_score = slim.conv2d(rpn, self._num_anchors * 2, [1, 1], 
                                trainable=is_training,
                                weights_initializer=initializer,
                                padding='VALID', activation_fn=None, 
                                scope='rpn_cls_score')
    "重新定義符合caffe數據格式的特徵向量"
    rpn_cls_score_reshape = self._reshape_layer(rpn_cls_score, 2, 
                                                'rpn_cls_score_reshape')
    "softmax二分類,給前景、後景打分"
    rpn_cls_prob_reshape = self._softmax_layer(rpn_cls_score_reshape, 
                                               "rpn_cls_prob_reshape")
    "得到該anchor的預測(屬於前景還是後景)"
    rpn_cls_pred = tf.argmax(tf.reshape(rpn_cls_score_reshape, [-1, 2]), axis=1, 
                             name="rpn_cls_pred")
    "重新改爲原來的數據格式"
    rpn_cls_prob = self._reshape_layer(rpn_cls_prob_reshape, self._num_anchors * 2, 
                                       "rpn_cls_prob")
    "邊框偏移預測"
    rpn_bbox_pred = slim.conv2d(rpn, self._num_anchors * 4, [1, 1],         
                                trainable=is_training,
                                weights_initializer=initializer,
                                padding='VALID', activation_fn=None, 
                                scope='rpn_bbox_pred')
    "訓練生成256個anchor boxes座標信息、類別標籤"
    if is_training:
        """
        通過迴歸預測的偏移,分類的得分和原始座標,得到2000個修正後的anchor box邊框和得分
        """
        rois, roi_scores = self._proposal_layer(rpn_cls_prob, rpn_bbox_pred, "rois")
        "對比真實框判斷圖片中對應的修正後的anchor是正樣本、負樣本、還是不關注"
        rpn_labels = self._anchor_target_layer(rpn_cls_score, "anchor")
        "訓練批次大小是256,該函數產生256個anchor boxes,裏面包含座標信息,類別標籤(前後景)"
        with tf.control_dependencies([rpn_labels]):
            rois, _ = self._proposal_target_layer(rois, roi_scores, "rpn_rois")
    "測試生成300個anchor boxes座標信息、類別標籤"
    else:
        "cfg[TEST].RPN_POST_NMS_TOP_N = 300"
        if cfg.TEST.MODE == 'nms':
            rois, _ = self._proposal_layer(rpn_cls_prob, rpn_bbox_pred, "rois")
        elif cfg.TEST.MODE == 'top':
            rois, _ = self._proposal_top_layer(rpn_cls_prob, rpn_bbox_pred, "rois")
        else:
            raise NotImplementedError

    self._predictions["rpn_cls_score"] = rpn_cls_score      "anchor前後景的得分情況"
    self._predictions["rpn_cls_score_reshape"] = rpn_cls_score_reshape "得分重定義結構"
    self._predictions["rpn_cls_prob"] = rpn_cls_prob        "分類前後景概率"
    self._predictions["rpn_cls_pred"] = rpn_cls_pred        "預測爲前景或者後景"
    self._predictions["rpn_bbox_pred"] = rpn_bbox_pred      "預測迴歸偏移"
    self._predictions["rois"] = rois

    return rois
self._crop_pool_layer(net_conv, rois, "pool5")將特徵向量resize到固定大小
def _crop_pool_layer(self, bottom, rois, name):
    """
     將256個archors從特徵圖中裁剪出來縮放到14*14,並進一步max pool到7*7的固定大小,方便rcnn網 
     絡分類及迴歸座標。
    """
    with tf.variable_scope(name) as scope:
        "類別"
        batch_ids = tf.squeeze(tf.slice(rois, [0, 0], [-1, 1], name="batch_id"), [1])  
        "獲取邊界框的標準化座標"
        bottom_shape = tf.shape(bottom)
        height = (tf.to_float(bottom_shape[1]) - 1.) * np.float32(self._feat_stride[0])
        width = (tf.to_float(bottom_shape[2]) - 1.) * np.float32(self._feat_stride[0])

        x1 = tf.slice(rois, [0, 1], [-1, 1], name="x1") / width
        y1 = tf.slice(rois, [0, 2], [-1, 1], name="y1") / height
        x2 = tf.slice(rois, [0, 3], [-1, 1], name="x2") / width
        y2 = tf.slice(rois, [0, 4], [-1, 1], name="y2") / height
        
        bboxes = tf.stop_gradient(tf.concat([y1, x1, y2, x2], axis=1))
        pre_pool_size = cfg.POOLING_SIZE * 2
        crops = tf.image.crop_and_resize(bottom, bboxes, tf.to_int32(batch_ids), 
                                         [pre_pool_size, pre_pool_size],
                                         name="crops")

    return slim.max_pool2d(crops, [2, 2], padding='SAME')
self._head_to_tail(pool5, is_training)添加fc6、fc7以及防止過擬合的dropout
def _head_to_tail(self, pool5, is_training, reuse=None):
    with tf.variable_scope(self._scope, self._scope, reuse=reuse):
        pool5_flat = slim.flatten(pool5, scope='flatten')
        fc6 = slim.fully_connected(pool5_flat, 4096, scope='fc6')
        if is_training:
            fc6 = slim.dropout(fc6, keep_prob=0.5, is_training=True,
                               scope='dropout6')
        fc7 = slim.fully_connected(fc6, 4096, scope='fc7')
        if is_training:
            fc7 = slim.dropout(fc7, keep_prob=0.5, is_training=True,
                               scope='dropout7')

    return fc7
self._region_classification()分類和迴歸
def _region_classification(self, fc7, is_training, initializer, initializer_bbox):
    "21類分類得分"
    cls_score = slim.fully_connected(fc7, self._num_classes,
                                     weights_initializer=initializer,
                                     trainable=is_training,
                                     activation_fn=None, scope='cls_score')
    "類別概率"
    cls_prob = self._softmax_layer(cls_score, "cls_prob")
    "預測類別"
    cls_pred = tf.argmax(cls_score, axis=1, name="cls_pred")
    "預測邊框偏移,和在rpn網絡裏面一樣都是預測的偏移"
    bbox_pred = slim.fully_connected(fc7, self._num_classes * 4,
                                     weights_initializer=initializer_bbox,
                                     trainable=is_training,
                                     activation_fn=None, scope='bbox_pred')

    self._predictions["cls_score"] = cls_score
    self._predictions["cls_pred"] = cls_pred
    self._predictions["cls_prob"] = cls_prob
    self._predictions["bbox_pred"] = bbox_pred

    return cls_prob, bbox_pred

self._add_losses()損失函數,該模型的損失包括rpn網絡的損失和rcnn網絡的損失,在rpn和rcnn中都包含分類損失和迴歸損失,分類損失用的交叉熵損失,迴歸損失用的是smooth l1 損失。【邊框迴歸的原理及推導過程在前面有提及】

def _add_losses(self, sigma_rpn=3.0):
    with tf.variable_scope('LOSS_' + self._tag) as scope:
        "RPN --> class loss 分類損失"
        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 迴歸損失"
        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']
        "預測偏移和anchor與gt真實偏移的smooth l1 損失"
        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])

        "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=cls_score, 
                                       labels=label))

        "RCNN --> bbox loss 迴歸損失"
        bbox_pred = self._predictions['bbox_pred']
        bbox_targets = self._proposal_targets['bbox_targets']
        bbox_inside_weights = self._proposal_targets['bbox_inside_weights']
        bbox_outside_weights = self._proposal_targets['bbox_outside_weights']
        """
         預測偏移和anchor與gt真實偏移的smooth l1 損失(注意兩次邊框迴歸都是和anchor和gt真實偏移        
         進行迴歸,因爲兩次都是爲了修正anchor使之更接近gt)
        """
        loss_box = self._smooth_l1_loss(bbox_pred, bbox_targets, bbox_inside_weights, 
                                        bbox_outside_weights)

        self._losses['cross_entropy'] = cross_entropy
        self._losses['loss_box'] = loss_box
        self._losses['rpn_cross_entropy'] = rpn_cross_entropy
        self._losses['rpn_loss_box'] = rpn_loss_box
        
        "總損失爲四個損失之和"
        loss = cross_entropy + loss_box + rpn_cross_entropy + rpn_loss_box
        regularization_loss = tf.add_n(tf.losses.get_regularization_losses(), 'regu')
        "總損失添加正則項"
        self._losses['total_loss'] = loss + regularization_loss

        self._event_summaries.update(self._losses)

    return loss

【上面只是介紹了特徵提取、產生anchor、rpn網絡、crop_pooling層、添加fc和dropout、分類和迴歸以及額外添加的loss主體函數,每個主體函數裏面還調用許多主體函數的具體實現過程,Faster RCNN的代碼量比較大,在此就不在闡述,可以參考源碼自己細化理解。也可以參考博客

四、創新與挑戰

1、創新

    Faster RCNN就是RPN + Fast RCNN,RPN內部的分類網絡可以生成高質量的區域提案框,內部的迴歸層可以優化、修正區域提案框。

    在多任務損失訓練添加了RPN網絡裏面的分類和迴歸損失。

2、挑戰

    Faster RCNN一張圖片的處理速度還不是很快。

總結:Faster RCNN是RCNN系列的一個階段性成果,RPN網絡的創新,使得區域提案不再是時間性能瓶頸,邊框偏移的兩次優化提高了整體目標檢測的預測性能。

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