【目標檢測系列】RPN(區域生成網絡)流程詳細解釋

    看RPN網絡基本是從faster-RCNN來的。看的有點迷糊。後來看了幾篇博客感覺有點懂了。記錄下來防止自己忘了吧。儘量用大白話。瞭解RPN網絡的整個應用流程。

1.明確輸入輸出:輸入是CNN卷積後的特徵圖 輸出是候選框圖  如下圖。知乎上盜了張圖。 感覺這個是最形象的

                        

                                                                                                          圖1

2.先形象解釋一下:左側是特徵圖 ,立方體厚度決定於特徵圖維度。右側紅框就是經過該網絡後提取出的感興趣ROI區域。  這個特徵圖生成方法很多了,例如 ZF,VGG ,Resnet 什麼的都可以。這裏就不細說了。RPN不關注具體目標是啥,是車,是人或者狗,樓房都可以。它只是將框內認爲是目標,框外認爲是背景,做了個二分類。至於框內目標具體是啥,最終是交給分類網絡去做。結果會略顯潦草,後面會對邊框進行提煉。

3.具體做法:以ZF網絡計算的特徵圖作爲輸入來說吧(不同網絡體現在輸入上是特徵圖的尺寸不同,RPN實現都是一致的),當然不同網絡的檢測結果差異是網絡本身決定的了。

3.1.計算RPN的輸入特徵圖(又見無恥的盜圖....) 對應於圖1的最左側

                                                                                                                   圖2

首先輸入圖像大小是224*224*3。 然後開始各種卷積 池化。卷積核大小,個數,步長,填充參數都在圖上可見。最終,rpn是選擇conv5的結果作爲其輸入,也就是13*13*256這個特徵圖是RPN網絡的輸入。具體這個卷積流程去參考其他文章即可。本文重點介紹RPN的處理流程。

3.2.生成Anchors   對應於圖1的中間部分

    首先牢記:特徵圖上的每個點生成一個Anchors,特徵圖大小與Anchors對應。文中是用9個錨點。所謂Anchors 就理解成9個不同的框好了(框的屬性自然就是長寬了,再進一步就是四個點的座標嘍),特徵圖上的每個點對應於一個Anchors。這九個不同的框按大小,比例生成的,論文中有介紹。這個框的數據(框的左上角座標(x0,y0),右下角座標(x1,y1)這四個值都是對應於原圖的)對應於原圖,就是圖1右側那個紅框。下面這個就是原圖,紅色點就是特徵圖上的點對應於原圖的位置。(接着盜圖)。它部署 9 個錨點框:3 個不同寬高比 * 3 個不同大小的錨點框 = 9個錨點框。每一個位置使用 9 個錨點,每個位置會生成 2×9 個目標分數和 4×9 個座標分數。顯然,通過一箇中心定義9個不同的框,就是爲實現多尺度這個想法。當然這樣做獲得檢測框很不準確,後面會做2次bounding box regression修正檢測框位置。

                   

                                                                                                                   圖3

下面是卷積的具體過程。

先上總圖                

                                                                                                             圖4

a.conv fetaure map到 intermediate layer的卷積過程:

    在特徵圖上用sliding window 3*3的窗口滑動。這個窗口提出來的圖 conv feature map大小是3*3*256,上面那個圖只顯示了1通道的,在它下面還有255層。那麼對於特徵圖上某個點來說,就要用256個大小爲3*3*256的卷積覈對這個窗口進行卷積,卷積過程顯然是對應通道(256channel)卷積,獲得1*256維的向量,然後累加獲得1*1向量,卷積核有256個,這樣的向量就有256個最終結果就是1*256(這個256是卷積核個數)。結果對應於下圖intermediate layer。整張特徵圖卷積結果如下圖

                

                            

b.intermediate layer的256維向量後面對應兩條分支:

    cls layer分支是目標和背景的二分類(classification),因爲k等於9,所以通過1×1×256×18的卷積核得到 2*9 = 18個分數,分別是目標和背景的評分。這次卷積總的流程如下圖。最右側的18層中,每兩層代表了某個anchor的特徵圖上所有點的前景背景概率值。

            

    reg layer分支。如果候選框是目標區域,就去判斷該目標區域的候選框位置在哪,這個時候另一條分支就過1×1×256×36的卷積得到4*9個值,每個框包含4個值(x,y,w,h),就是9個候選區域對應的框應該偏移的具體位置Δxcenter,Δycenter,Δwidth,Δheight。如果候選框不是目標區域,就直接將該候選框去除掉,不再進行後續位置信息的判斷操作。這裏預測的值都是通過模型不斷訓練得到的。顯然最右側的18層中,每4層代表了某個anchor的特徵圖上所有點的偏移量的值。

                       

                                                                                                                  

3.3獲得最終結果: 對應於圖1的右側部分

    先上個圖。裏面綠色框就是標註框,是我們樣本中框好的,代表了飛機的真實存在框範圍,紅色框可以看成是候選框的初值,針對特徵圖上的某個點,這樣的框有9個。

                    

                                                                                                                   圖5

 

首先是分類問題,簡而言之就是存在與否問題。上面紅色就是此次認爲紅色框中存在灰機了。對應於cls layer層的某個錨點的某種形狀的框,其判斷爲目標的概率非常大。我們訓練模型,讓cls layer更好的區分前景(灰機),背景(非灰機)。在訓練之初我們先對前景和遠景分別用正樣本(1)和負樣本(0)進行標籤工作

 正樣本標籤: 與某一個GT有最高的IOU重疊的Anchor box。與任意一個GT的IOU重疊部分大於0.7的Anchor box 。

 負樣本標籤: 給與所有GT包圍盒的IOU重疊部分都小於0.3的Anchor box。

    然後通過損失函數softmax loss(cross entropy loss)進行計算,這裏的監督標籤就是GT(1,0),它不斷告訴訓練出的結果對還是不對。就這樣訓練到最後我們的RPN分類訓練完成。這個步驟獲得了紅色框

然後是迴歸問題,簡而言之就是修正邊框而已。上面的藍色就是在紅色框基礎上去修正得來的,對應於reg layer層。我們通過不斷訓練它,讓其得到的四個值,x,y,w,h能與真實的GT座標不斷靠攏,能更接近真實目標的座標。看上面那個飛機圖,Region Porposal 是我們RPN給出的一個候選區域初值。把上面紅色框圖的信息給下一次最終的分類模塊,它可能識別不出飛機。因爲框的太少了。 所以在訓練的時候,就需要儘可能的使我們的Anchor box的框與真實的GT的框接近。但是我們知道想讓A-box=GT,是不太現實的,因爲我們沒法做到100%。

                                                   

                                                                                                           圖6

所以如上圖所示,我們的目標就是希望從現有的原始紅色的Region  Proposal最終得到藍色的預測GT,而這個藍色GT其實與綠色的真實的標定GT非常接近了已經。總而言之,我們如果得到了預測GT,那麼目標就達成了。這個步驟對紅色框估計獲得了藍色框、這個框顯然就是圖1中那幾個紅色的框框了。

多邊框迴歸的參考文獻列在下面了。詳情見它。簡而言之就是已經有個初始框,但是它不準,不準的因素有4個, x,y方向的偏移,x,y方向的縮放比例。然後就用神經網絡去訓練。讓紅色框與綠色框儘可能接近,那麼對應的這4個參數就是需要訓練的值唄

RPN具體的pytorch代碼如下。可對照代碼和上面理論去對應。

from __future__ import absolute_import
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable

from model.utils.config import cfg
from .proposal_layer import _ProposalLayer
from .anchor_target_layer import _AnchorTargetLayer
from model.utils.net_utils import _smooth_l1_loss

import numpy as np
import math
import pdb
import time

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., 256 FT
        self.anchor_scales = cfg.ANCHOR_SCALES
        self.anchor_ratios = cfg.ANCHOR_RATIOS
        self.feat_stride = cfg.FEAT_STRIDE[0]

        # define the convrelu layers processing input feature map
        self.RPN_Conv = nn.Conv2d(self.din, 256, 3, 1, 1, bias=True)

        # 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(256, 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(256, 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
        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):
        input_shape = x.size()
        x = x.view(
            input_shape[0],
            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):

        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)
        rpn_cls_prob_reshape = F.softmax(rpn_cls_score_reshape, 1)
        rpn_cls_prob = self.reshape(rpn_cls_prob_reshape, self.nc_score_out)

        # get rpn offsets to the anchor boxes
        rpn_bbox_pred = self.RPN_bbox_pred(rpn_conv1)

        # proposal layer
        cfg_key = 'TRAIN' if self.training else 'TEST'

        rois = self.RPN_proposal((rpn_cls_prob.data, rpn_bbox_pred.data,
                                 im_info, cfg_key))

        self.rpn_loss_cls = 0
        self.rpn_loss_box = 0

        # generating training labels and build the rpn loss
        if self.training:
            assert gt_boxes is not None

            rpn_data = self.RPN_anchor_target((rpn_cls_score.data, gt_boxes, im_info, num_boxes))

            # compute classification loss
            rpn_cls_score = rpn_cls_score_reshape.permute(0, 2, 3, 1).contiguous().view(batch_size, -1, 2)
            rpn_label = rpn_data[0].view(batch_size, -1)

            rpn_keep = Variable(rpn_label.view(-1).ne(-1).nonzero().view(-1))
            rpn_cls_score = torch.index_select(rpn_cls_score.view(-1,2), 0, rpn_keep)
            rpn_label = torch.index_select(rpn_label.view(-1), 0, rpn_keep.data)
            rpn_label = Variable(rpn_label.long())
            self.rpn_loss_cls = F.cross_entropy(rpn_cls_score, rpn_label)
            fg_cnt = torch.sum(rpn_label.data.ne(0))

            rpn_bbox_targets, rpn_bbox_inside_weights, rpn_bbox_outside_weights = rpn_data[1:]

            # compute bbox regression loss
            rpn_bbox_inside_weights = Variable(rpn_bbox_inside_weights)
            rpn_bbox_outside_weights = Variable(rpn_bbox_outside_weights)
            rpn_bbox_targets = Variable(rpn_bbox_targets)

            self.rpn_loss_box = _smooth_l1_loss(rpn_bbox_pred, rpn_bbox_targets, rpn_bbox_inside_weights,
                                                            rpn_bbox_outside_weights, sigma=3, dim=[1,2,3])

        return rois, self.rpn_loss_cls, self.rpn_loss_box

參考的文章太多了,哎   就厚着臉皮寫個原創吧 哈哈。
參考文獻:

1.https://blog.csdn.net/w437684664/article/details/104238521

2.https://www.jianshu.com/p/468e08f739bd

3.https://www.jianshu.com/p/643cdcf674fc

4.https://blog.csdn.net/YZXnuaa/article/details/79221189

5.https://blog.csdn.net/qq_36269513/article/details/80421990

6.https://blog.csdn.net/zijin0802034/article/details/77685438/ 邊框迴歸的

7.https://blog.csdn.net/wangwei19871103/article/details/100929543 VGG主幹網絡與RPN的結合 基於keras的

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