【Faster R-CNN论文精度系列】原文精析

本文凝练了CSDN和知乎大佬的思想,并加入自己的理解和思考,仅做总结和参考。

经过R-CNN和Fast RCNN的积淀,Ross B. Girshick在2016年提出了新的Faster RCNN,在结构上,Faster RCNN已经将特征抽取feature extraction、 proposal,bounding box regression(rect refine)和classification都整合在神经网络上,使得综合性能有较大提高,在检测速度方面尤为明显。
【Faster R-CNN论文精度系列】原文精析_第1张图片
如框架图1,可以看到Faster RCNN其实可以分为4个主体部分:

  1. Conv Layers
    作为一种CNN网络目标检测方法,对于任意输入,然后reshape成800x600,Faster RCNN首先使用一组基础的CNN层提取image的feature maps。该feature maps被shared用于后续RPN层和全连接层(特征共享)。
  2. Region Proposal Networks
    RPN网络用于生成region proposals。该层通过softmax判断anchors属于foreground或者background,并给出标签[-1,0,1],再利用bounding box regression修正anchors获得精确的proposals。
  3. RoI Pooling
    该层收集输入的feature maps和proposals,综合这些信息后提取proposal feature maps,送入后续全连接层判定目标类别。
  4. Classification
    利用proposal feature maps计算proposal的类别,同时再次bounding box regression获得检测框最终的精确位置。
    【Faster R-CNN论文精度系列】原文精析_第2张图片
    图2展示了python版本中的VGG16模型中的faster_rcnn_test.pt的网络结构,可以清晰的看到该网络对于一副任意大小PxQ的图像,首先缩放至固定大小MxN,然后将MxN图像送入网络;而Conv layers中包含了13个conv层+13个relu层+4个pooling层;RPN网络首先经过3x3卷积,再分别生成foreground anchors与bounding box regression偏移量,然后计算出proposals;而Roi Pooling层则利用proposals从feature maps中提取proposal feature送入后续全连接和softmax网络作classification(即分类proposal到底是什么object)。

1 Conv layers

【Faster R-CNN论文精度系列】原文精析_第3张图片

  • Conv layers包含了Conv、Pooling、Relu三种层。以python版本代码中的VGG-16模型中的faster_rcnn_test.pt的网络结构为例,如图2,Conv layers部分共有13个conv层,13个relu层,4个pooling层。
  • 这里有一个无比重要的细节,在Conv layers中:
    所有的conv层都是: kernel_size=3,pad=1,stride=1
    所有的pooling层都是: kernel_size=2,pad=0,stride=2
  • 为何重要?在Faster RCNN Conv layers中对所有的卷积都做了扩边处理( pad=1,即周围padding一圈0像素值),导致原图变为 (M+2)*(N+2)大小,再做3x3卷积后输出MxN 。正是这种设置,导致Conv layers中的conv层不改变输入和输出矩阵大小(Conv层保持图片大小不变性)。
  • 但Pooling会改变其大小,即这种情况下多少层pooling就会让图像变小多少倍。Conv layers中的pooling层kernel_size=2,stride=2。这样每个经过pooling层的M x N矩阵,都会变为(M/2) x (N/2)大小。综上所述,在整个Conv layers中,Conv和Relu层不改变输入输出大小,只有pooling层使输出长宽都变为输入的1/2。那么,一个MxN大小的矩阵经过Conv layers固定变为(M/16)x(N/16)!这样Conv layers生成的featuure map中都可以和原图对应起来(分析见下一点)。
  • 重要理解:分析上点的最后一句,有了这样的对应关系,如下图所示,也就是 80 ∗ 80 80*80 8080的input经过network后会生成一张 5 ∗ 5 5*5 55大小的特征图,特征图中的一个cell(或叫特征点)对应原图中的一个 16 ∗ 16 16*16 1616大小的block。有了这样的对应关系后,对于后面对Anchor的理解会有很大的帮助

2 Region Proposal Networks(RPN)

经典的检测方法生成检测框都非常耗时,如OpenCV adaboost使用滑动窗口+图像金字塔生成检测框;或如R-CNN使用SS(Selective Search)方法生成检测框。而Faster RCNN则抛弃了传统的滑动窗口和SS方法,直接使用一个神经网络(RPN)生成proposal,这也是Faster R-CNN的巨大优势,能极大提升检测框的生成速度(注意:在RCNN和Fast RCNN中耗时的主要部分是proposal部分)。
【Faster R-CNN论文精度系列】原文精析_第4张图片
上图中展示了RPN网络的具体结构。可以看到RPN网络实际分为2条线:

  1. 上面一条给出foreground和background的标签(计算IoU,前景label=1,背景label=0),并通过Softmax函数给出分类。(共9个anchor,所以有18个参数(9个label+9个分类分数))
  2. 下面一条用于计算对于anchors的bounding box regression偏移量,以获得精确的proposal。
  3. 最后的Proposal层则负责综合foreground anchors和bounding box regression偏移量获取proposals,同时剔除太小和超出边界的proposals。

其实整个网络到了Proposal Layer这里,就完成了相当于目标定位(定位box)的功能,RoI Pooling的主要工作是对Feature map和Proposal的信息进行综合,然后对不同大小的anchor box归一化到同一个尺寸大小( 7 ∗ 7 7*7 77),便于后面使用全连接层对其进行分类。

2.1 Anchor

Anchor应该放在RPN中讲,但因为其重要性,我决定另开一节对其原理进行阐述。所谓Anchor(锚点),实际上就是一组由rpn/generate_anchors.py生成的不同大小的矩形。直接运行作者demo中的generate_anchors.py可以得到以下输出:

# 运行时间
0.0009987354278564453
# 输出结果
[[ -84.  -40.   99.   55.]
 [-176.  -88.  191.  103.]
 [-360. -184.  375.  199.]
 [ -56.  -56.   71.   71.]
 [-120. -120.  135.  135.]
 [-248. -248.  263.  263.]
 [ -36.  -80.   51.   95.]
 [ -80. -168.   95.  183.]
 [-168. -344.  183.  359.]]
  • 其中每行的4个值 ( x 1 , y 1 , x 2 , y 2 ) (x_1, y_1, x_2, y_2) (x1,y1,x2,y2)表矩形左上和右下角点坐标。9个矩形共有3种形状,aspect ratio为大约为with:height∈{1:1, 1:2, 2:1}三种,scale共三种,[8,16,32]。如下图所示有9种规格的anchor box,实际上通过anchors就引入了检测中常用到的多尺度方法。 【Faster R-CNN论文精度系列】原文精析_第5张图片
  • k=9个Anchor box有什么作用?借用Faster RCNN论文中的原图,遍历Conv layers计算获得的feature maps中的每一个cell(特征点),并为每一个点都配备这9种anchors作为初始的检测框。这样做获得检测框虽说很不准确,但不用担心,后面还有2次bounding box regression可以修正检测框位置。
    【Faster R-CNN论文精度系列】原文精析_第6张图片
  • 解释一下上面这张图中数字的含义:
    在原文中使用的是ZF model中,其Conv Layers中最后的conv5,num_output=256,对应生成256张特征图,所以相当于feature map每个点都是256-dimensions(见以下代码)
layer {
  name: "rpn_conv/3x3"
  type: "Convolution"
  bottom: "conv5"	# 输入是Conv layer的最后一层(conv5)
  top: "rpn/output"	# 输出是rpn
  param { lr_mult: 1.0 }
  param { lr_mult: 2.0 }	# 更新学习率和偏置
  convolution_param {
    num_output: 256	# 共256个filter
    kernel_size: 3 pad: 1 stride: 1	# 这种设定会让
    weight_filler { type: "gaussian" std: 0.01 }
    bias_filler { type: "constant" value: 0 }
  }
}
  • 为什么在conv5之后,又做了rpn_conv/3x3的卷积?
    这样的卷积是不影响特征图的大小的,即:输入为ZF-net的最后一层输出(256-d),其经过rpn_conv后的输出依旧为num_output=256(256-d)(这个过程中256-d的维度是不改变的),这样相当于每个点又融合了周围3x3的空间信息(这样会让模型更加鲁棒)。
  • 参数量(2k的cls与4k的reg)怎么来的?
    假设在conv5生成的feature map中每个cell上有k个anchor(k=9),而每个anhcor要分foreground(label=1)和background(label=0),所以每个点由256-d feature转化为cls=2k scores(k个标签值+k个分数值);而每个anchor都有[x, y, w, h]对应4个偏移量,所以reg=4k coordinates(注意这里不是给出的四个坐标参数,而是通过参数化后的坐标值)
    【Faster R-CNN论文精度系列】原文精析_第7张图片
    补充一点,全部anchors拿去训练太多了,训练程序会在合适的anchors中随机选取128个postive anchors+128个negative anchors进行训练(什么是合适的anchors,在下文中有解释
  • 计算示例:Anchor box共有多少个?
    给一张input并resize到800x600像素大小,VGG16下采样16倍(4个pooling层),生成的feature map中的每个cell(特征点)安插k=9个Anchor。所以: c e i l ( 800 / 16 ) × c e i l ( 600 / 16 ) × 9 = 50 × 38 × 9 = 17100 ceil(800/16) \times ceil(600/16) \times 9=50\times38\times9=17100 ceil(800/16)×ceil(600/16)×9=50×38×9=17100,其中ceil()表示向上取整,是因为VGG输出的Feature map size= 50*38。综上述,会产生对于 800 × 600 800\times600 800×600的输入会产生17100个Anchor box。示意图如下图所示:
    【Faster R-CNN论文精度系列】原文精析_第8张图片
  • 总结:其实RPN最终就是映射到原图尺度上,设置了密密麻麻的候选Anchor。然后用CNN去判断哪些Anchor是里面有目标的foreground anchor box,哪些是没目标的backgroud anchor box(二分类问题)。

2.2 Softmax前后为什么要接Reshape?

【Faster R-CNN论文精度系列】原文精析_第9张图片
处于rpn_conv与proposal之间的softmax层用于判定foreground与background。现给一张 M × N M\times N M×N大小的矩阵送入Faster RCNN网络后,到RPN网络变为 ( M / 16 ) × ( N / 16 ) (M/16) \times (N/16) (M/16)×(N/16),不妨设 W = M / 16 , H = N / 16 W=M/16,H=N/16 W=M/16H=N/16。在进入reshape与softmax之前,先做了18个1x1x256卷积,生成18个分数的雏形(图中的18就是维度)。
其中该1x1卷积的.prototxt文件定义如下:(完整定义请看【Faster R-CNN论文精度系列】代码解读并深入理解Region Proposal Network)

layer {
  name: "rpn_cls_score"
  type: "Convolution"
  bottom: "rpn/output"
  top: "rpn_cls_score"
  convolution_param {
    num_output: 18   # 2(bg/fg) * 9(anchors)
    kernel_size: 1 pad: 0 stride: 1
  }
}

可以看到其num_output=18,也就是经过该卷积的输出图像为 W × H × 18 W\times H\times 18 W×H×18大小(这是一种多通道+多卷积核的卷积形式)。这也就刚好对应了Feature maps中每一个点的9个anchors,同时每个anchors又有可能是foreground和background,所有这些信息都保存 ( W × H × 9 ) × 2 (W\times H\times 9)\times 2 (W×H×9)×2大小的矩阵。

  • 为何这样设置?
    256-d的特征图经过 3 ∗ 3 3*3 33卷积核卷积后,大小不变,再经过 1 ∗ 1 1*1 11卷积核卷积后得到18-d的特征图,这个不能用于softmax进行分类,需要经过reshape后才能给softmax分类获得anchor box的分数。具体实现过程如下:
    那么为何要在softmax前后都接一个reshape layer?其实只是为了便于softmax分类,至于具体原因这就要从caffe的实现形式说起了。在caffe基本数据结构blob中以如下形式保存数据:
blob=[batch_size, channel,height,width]
  • 具体过程和代码分析:
    在第一个reshape之前,对应至上面的保存的(bg/fg) anchors的矩阵:
  1. 对于rpn_cls_score: (n,W,H,18)进行两次transpose和一次reshape操作
    (n,18,W,H) (n,2,9W,H) (n,9W,H,2)
  2. 然后送入softmax层得到rpn_cls_prob:
    (n,9W,H,2) => get rpn_cls_prob
  3. 进行一次反向的两次transpose和一次reshape操作
    (n,2,9W,H) (n,18,W,H) (n,W,H,18)
  4. return
    return rpn_cls_prob

说明:其在blob中的存储形式为[1, 2x9, H, W],即:18通道的特征图。而在softmax分类时需要进行fg/bg二分类,所以reshape layer会将其变为[1, 2, 9xH, W]大小,即单独“腾空”出来一个维度以便softmax分类,之后再reshape回复原状。以下给出tf-Faster-RCNN/Lib/rpn_softmax.py文件中给出的softmax和reshape具体实现代码块

# -*- coding: utf-8 -*-
"""
Created on Wed Jan 11 02:54:21 2017
@author: Kevin Liang
"""

import tensorflow as tf

def rpn_softmax(rpn_cls_score):
    '''
    Reshape the rpn_cls_score (n,W,H,2k) to take a softmax. Converts scores to probabilities
    
    ex. 9 anchors, n samples minibatch, convolutional feature maps of dims WxH
    
    rpn_cls_score:  (n,W,H,18)
         (n,18,W,H)
           (n,2,9W,H)
         (n,9W,H,2)
    
           (n,9W,H,2) => get rpn_cls_prob
    
         (n,2,9W,H)
           (n,18,W,H)
         (n,W,H,18)
    
    return rpn_cls_prob
    
    TODO: Can probably just take the softmax of a specific index and get rid of two tranpsoses
    '''
    with tf.variable_scope('rpn_softmax'):
        # input shape dimensions
        shape = tf.shape(rpn_cls_score)
        
        # Reshape rpn_cls_score to prepare for softmax
        rpn_cls_score = tf.transpose(rpn_cls_score,[0,3,1,2])
        rpn_cls_score = tf.reshape(rpn_cls_score,[shape[0],2,shape[3]//2*shape[1],shape[2]])
        rpn_cls_score = tf.transpose(rpn_cls_score,[0,2,3,1])
        
        # Softmax
        rpn_cls_prob = tf.nn.softmax(rpn_cls_score)
        
        # Reshape back to the original
        rpn_cls_prob = tf.transpose(rpn_cls_prob,[0,3,1,2])
        rpn_cls_prob = tf.reshape(rpn_cls_prob,[shape[0],shape[3],shape[1],shape[2]])
        rpn_cls_prob = tf.transpose(rpn_cls_prob,[0,2,3,1])

    return rpn_cls_prob

综上所述,RPN网络中利用anchors和softmax初步提取出foreground anchors box作为候选区域。该部分的整体图如下:
【Faster R-CNN论文精度系列】原文精析_第10张图片

2.4 Bounding Box Regression

如下图所示green box为飞机的Ground Truth(True Answer),red box为proposal的foreground anchors box,即便红色的框被分类器识别为飞机,但是由于红色的框定位不准,这张图相当于没有正确的检测出飞机。所以我们希望采用一种方法对红色的框进行tuning,使得foreground anchors和GT更加接近。
【Faster R-CNN论文精度系列】原文精析_第11张图片
对于B.box一般使用四维向量 (x, y, w, h)表示,分别表示窗口的中心点坐标和宽高。如下图中,红色的框A代表原始的Foreground Anchors,绿色的框G代表真是答案Ground Truth。

  • 我们的目标是寻找一种关系,使得输入原始的 A A A经过映射得到一个跟真实窗口 G G G更接近的回归窗口 G ′ G' G,即:
    给定:anchor A = ( A x , A y , A w , A h ) A=(A_{x}, A_{y}, A_{w}, A_{h}) A=(Ax,Ay,Aw,Ah) G T = [ G x , G y , G w , G h ] GT=[G_{x}, G_{y}, G_{w}, G_{h}] GT=[Gx,Gy,Gw,Gh],并寻找一种变换F,使得: F ( A x , A y , A w , A h ) = ( G x ′ , G y ′ , G w ′ , G h ′ ) F(A_{x}, A_{y}, A_{w}, A_{h})=(G_{x}^{'}, G_{y}^{'}, G_{w}^{'}, G_{h}^{'}) F(Ax,Ay,Aw,Ah)=(Gx,Gy,Gw,Gh),其中 ( G x ′ , G y ′ , G w ′ , G h ′ ) ≈ ( G x , G y , G w , G h ) (G_{x}^{'}, G_{y}^{'}, G_{w}^{'}, G_{h}^{'})≈(G_{x}, G_{y}, G_{w}, G_{h}) (Gx,Gy,Gw,Gh)(Gx,Gy,Gw,Gh),如下图:
    【Faster R-CNN论文精度系列】原文精析_第12张图片
  • 那么经过何种变换F才能从图10中的anchor A变为G’呢? 比较简单的思路就是:
    先做平移: G x ′ = A w ⋅ d x ( A ) + A x G^{'}_{x}=A_{w} \cdot d_{x}(A)+A_{x} Gx=Awdx(A)+Ax G y ′ = A h ⋅ d y ( A ) + A y G^{'}_{y}=A_{h} \cdot d_{y}(A)+A_{y} Gy=Ahdy(A)+Ay
    再做缩放: G w ′ = A w ⋅ e x p ( d w ( A ) ) G^{'}_{w}=A_{w} \cdot exp(d_{w}(A)) Gw=Awexp(dw(A)) G h ′ = A w ⋅ e x p ( d h ( A ) ) G^{'}_{h}=A_{w} \cdot exp(d_{h}(A)) Gh=Awexp(dh(A))
    观察上面4个公式发现,需要学习的是 d x ( A ) , d y ( A ) , d w ( A ) , d h ( A ) d_{x}(A),d_{y}(A),d_{w}(A),d_{h}(A) dx(A),dy(A),dw(A),dh(A) 这四个变换
  • 近似到线性回归模型
    当输入的Anchor A与GT相差较小时,可以认为这种变换是一种线性变换, 那么就可以用线性回归来建模对窗口进行微调(注意:只有当anchors A和GT比较接近时,才能使用线性回归模型,否则就是复杂的非线性问题了)。接下来的问题就是如何通过线性回归获得: d x ( A ) , d y ( A ) , d w ( A ) , d h ( A ) d_{x}(A),d_{y}(A),d_{w}(A),d_{h}(A) dx(A),dy(A),dw(A),dh(A)
  • 再谈什么是“学习”?
    首先你得对“机器”和“学习”以及“机器学习”有着充足的理解:笔者在这篇文章的2.3节(重要理解——什么是“学习”?)(【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?)中举了非常多详实的例子对“学习”二字进行阐述,理解“学习”二字,对于后期的学习必然会有很大的帮助。这里简单描述一下,学习就是不断迭代修正参数以期获得最佳目标的过程(对于机器来说这个过程就是不断对函数进行修正的过程),也可以结合SGD的过程来看,不断的左右随机找方向,但大致的方向是向着最低点——局部最优值收敛的,这个过程也是一个学习的过程。
  • 怎么使用线性回归实现“学习”四个参数?
    线性回归就是给定输入的特征向量X, 学习一组参数W, 使得经过线性回归后的值跟真实值Y非常接近,即Y=WX。对于该问题,输入X是CNN的feature map,定义为 Φ Φ Φ;同时还有训练传入 A A A G T GT GT之间的变换量,即 ( t x , t y , t w , t h ) (t_{x}, t_{y}, t_{w}, t_{h}) (tx,ty,tw,th)。输出是 d x ( A ) , d y ( A ) , d w ( A ) , d h ( A ) d_{x}(A),d_{y}(A),d_{w}(A),d_{h}(A) dx(A),dy(A),dw(A),dh(A)四个变换函数。那么目标函数可以表示为:
    d ∗ ( A ) = w ∗ T ⋅ Φ ( A ) d_{*}(A)=w^{T}_{*} \cdot \Phi(A) d(A)=wTΦ(A)
    其中 Φ ( A ) Φ(A) Φ(A)是对应anchor的feature map组成的特征向量,w是需要学习的参数,d(A)是得到的预测值(*表示 x,y,w,h,也就是每一个变换对应一个上述目标函数)。为了让预测值 ( t x , t y , t w , t h ) (t_{x}, t_{y}, t_{w}, t_{h}) (tx,ty,tw,th)与真实值差距最小,设计损失函数:
    L o s s = ∑ i n ( t i ∗ − w ^ T ∗ ⋅ Φ ( A i ) ) 2 Loss=\sum_{i}^{n}(t^{*}_{i}-\widehat{w}^{*}_{T}\cdot \Phi(A^{i}))^{2} Loss=in(tiw TΦ(Ai))2
    函数优化目标为:
    w ∗ = a r g m i n w ^ ∗ ∑ i n ( t i ∗ − w ^ T ∗ ⋅ Φ ( A i ) ) 2 + λ ∥ w ^ ∗ ∥ 2 w_{*}=argmin_{\widehat{w}_{*}}\sum_{i}^{n}(t^{*}_{i}-\widehat{w}^{*}_{T}\cdot \Phi(A^{i}))^{2}+\lambda \parallel \widehat{w}_{*}\parallel ^{2} w=argminw in(tiw TΦ(Ai))2+λw 2
    需要说明,只有在GT与需要回归框位置比较接近时,才可近似认为上述线性变换成立。说完原理,对应于Faster RCNN原文,foreground anchor与ground truth之间的平移量 ( t x , t y ) (t_x, t_y) (tx,ty)与尺度因子 ( t w , t h ) (t_w, t_h) (tw,th)如下:
    【Faster R-CNN论文精度系列】原文精析_第13张图片

对于训练b.box reg分支,输入是CNN的feature map Φ Φ Φ,监督信号是Anchor A与GT的差距 ( t x , t y , t w , t h ) (t_x, t_y, t_w, t_h) (tx,ty,tw,th),即训练目标是:输入 Φ Φ Φ的情况下使网络输出与监督信号尽可能接近。那么当bouding box regression工作时,再输入Φ时,回归网络分支的输出就是每个Anchor的平移量和变换尺度组成的 ( t x , t y , t w , t h ) (t_x, t_y, t_w, t_h) (tx,ty,tw,th),这样就能修正Anchor位置了。

  • 回归到.prototxt文件看框架中的第二条路线
    在了解bounding box regression后,再回头来看RPN网络第二条线路,如图所示RPN中的bbox reg:
    【Faster R-CNN论文精度系列】原文精析_第14张图片
    先来看一看上图中1x1卷积的.prototxt定义:
layer {
  name: "rpn_bbox_pred"
  type: "Convolution"
  bottom: "rpn/output"
  top: "rpn_bbox_pred"
  convolution_param {
    num_output: 36   # 4 * 9(anchors)
    kernel_size: 1 pad: 0 stride: 1
  }
}

可以看到其 num_output=36,即经过该卷积输出图像为 W × H × 36 W\times H \times 36 W×H×36,在caffe blob存储为[1, 4x9, H, W],这里相当于feature maps每个点都有9个anchors,每个anchors又都有4个用于回归函数的 [ d x ( A ) , d y ( A ) , d w ( A ) , d h ( A ) ] [d_{x}(A),d_{y}(A),d_{w}(A),d_{h}(A)] [dx(A),dy(A),dw(A),dh(A)]变换参数。

2.5 Proposal Layer

Proposal Layer负责综合所有 [ d x ( A ) , d y ( A ) , d w ( A ) , d h ( A ) ] [d_{x}(A),d_{y}(A),d_{w}(A),d_{h}(A)] [dx(A),dy(A),dw(A),dh(A)]变换量和foreground anchors box的信息(三种信息:rpn_cls_prob_reshape、rpn_bbox_pred、im_info),计算出精准的proposal,送入后续RoI Pooling Layer。还是先来看看Proposal Layer的caffe.prototxt定义:

layer {
  name: 'proposal'
  type: 'Python'
  bottom: 'rpn_cls_prob_reshape'
  bottom: 'rpn_bbox_pred'
  bottom: 'im_info'	# 可以看到其输入的信息有三种
  top: 'rois'
  python_param {
    module: 'rpn.proposal_layer'
    layer: 'ProposalLayer'
    param_str: "'feat_stride': 16"
  }
}

Proposal Layer有3个输入:fg/bg anchors cls分类器路线结果rpn_cls_prob_reshape、对应的bbox reg路线的 [ d x ( A ) , d y ( A ) , d w ( A ) , d h ( A ) ] [d_{x}(A),d_{y}(A),d_{w}(A),d_{h}(A)] [dx(A),dy(A),dw(A),dh(A)]变换量rpn_bbox_pred、以及im_info;另外还有参数feat_stride=16,这和图4是对应的。

  • 首先解释im_info:对于一副任意大小PxQ图像,传入Faster RCNN前首先reshape到固定MxN,im_info=[M, N, scale_factor]则保存了此次缩放的所有信息。然后经过Conv Layers,经过4次pooling变为WxH=(M/16)x(N/16)大小,其中feature_stride=16则保存了该信息,用于计算anchor偏移量。
  • 怎么选Anchor?
    Proposal Layer forward(layer的前传函数)按照以下顺序依次处理:
  1. 生成anchors:利用 [ d x ( A ) , d y ( A ) , d w ( A ) , d h ( A ) ] [d_{x}(A),d_{y}(A),d_{w}(A),d_{h}(A)] [dx(A),dy(A),dw(A),dh(A)]对所有的anchors做bbox regression回归(这里的anchors生成和训练时完全一致)
  2. 排序anchors:按照输入的foreground softmax scores由大到小排序anchors,提取前pre_nms_topN(e.g. 6000)个anchors,即提取修正位置后的foreground anchors。
  3. 修边剔除:限定超出图像边界的foreground anchors为图像边界(防止后续roi pooling时proposal超出图像边界);剔除非常小(width
  4. NMS选框
  5. 再排序:再次按照nms后的foreground softmax scores由大到小排序f/g anchors,提取前post_nms_topN(e.g. 300)结果作为proposal输出。
  6. 输出proposal=[x1, y1, x2, y2]
  • 注意:
    由于在第三步中将anchors映射回原图判断是否超出边界,所以这里输出的proposal是对应 M × N M\times N M×N输入图像尺度的,这点在后续网络中有用。另外我认为,严格意义上的检测应该到此就结束了,后续部分应该属于识别了
  • RPN网络结构就介绍到这里,总结起来就是
    生成anchors => softmax分类器提取f/g anchors => bbox reg回归f/g anchors => Proposal Layer生成proposals

3 RoI Pooling Layer

  • 输入是什么?
    对于RoI Pooling层则负责收集Proposal anchor和Feature map,并计算出Proposal Feature Maps(即:ROI),送入后续网络进行fixed操作并进行回归和分类。单从下图中可以看到Rol pooling层有两个输入:原始的Feature Maps 和 RPN输出的proposal boxes(大小各不相同)。
    【Faster R-CNN论文精度系列】原文精析_第15张图片
  • RoI是什么?
    ROI是Region of Interest的简写,指的是在“特征图上的框”;
    1)在Fast RCNN中, RoI是指Selective Search完成后得到的“候选框”在特征图上的映射得到RoIs。
    2)在Faster RCNN中,候选框是经过RPN产生的“候选框”在特征图上的映射,得到RoIs。【Faster R-CNN论文精度系列】原文精析_第16张图片
  • 一个细节:
    注意到上图中,从原图的Anchor box映射到Feature map后,其维度是有变化的,变成了 ( w , h , c ) (w, h, c) (w,h,c)

3.1 为何添加RoI Pooling层

  • 使用传统方法(Crop和Warp)解决Resize问题
    对于传统的CNN(如:Alexnet、VGG-net、Resnet等),其训练好后的Feature Map是固定的大小,然后用一个固定的filter送入全连接层,这样的作用是为了便于Fully-connection Layer进行判定分类。但在本文中的图片大小是不一致的,如何对不同的图片resize到同一个大小成为了一个难点。一般地,有两种方法:
  1. 从图像中crop一部分传入网络
  2. 将图像warp成需要的大小后传入网络
    【Faster R-CNN论文精度系列】原文精析_第17张图片
    两种办法(Crop和Warp)的示意图如上图所示,可以明显看到两种方法都不是最优的:Crop破坏了图像的结构信息,Warp破坏了图片的形状信息。
  • 使用RoI Pooling层解决Resize问题
    回忆我们在上一节中提到的利用RPN生成的Proposals的方法:对Foreground anchors进行Bounding box regression,那么这样获得的proposals也是大小形状各不相同,即也存在上述问题。所以Faster R-CNN中提出了RoI Pooling解决这个问题。不过RoI Pooling确实是从Spatial Pyramid Pooling(SPP)发展而来。

3.2 RoI Pooling原理

  • 推荐可以先看这篇CSDN,通过对SPPnet的讲解,来对RoI Pooling有一个初步的认知:3分钟理解ROI Pooling层
  • 进行分析之前先来看看RoI Pooling Layer的caffe.prototxt的定义:
    也就是从整体看,RoI的输入是任意图片,输出成了7*7固定大小的feature map,然后送入全连接层做分类回归
layer {
  name: "roi_pool5"
  type: "ROIPooling"
  bottom: "conv5_3"
  bottom: "rois"
  top: "pool5"
  roi_pooling_param {
    pooled_w: 7
    pooled_h: 7	# 将不同大小的proposal转换成7*7大小的图片
    spatial_scale: 0.0625 # 1/16
  }
}
  • 再举一个简单的例子,使大家对这个过程有一个更为形象的理解:【Faster R-CNN论文精度系列】原文精析_第18张图片
    -【Faster R-CNN论文精度系列】原文精析_第19张图片
  • 给出动图,方便理解其过程
    【Faster R-CNN论文精度系列】原文精析_第20张图片
  • RoI Pooling layer forward过程
    在之前有明确提到,是对应MxN尺度的,所以首先使用spatial_scale参数将其映射回 ( M / 16 ) × ( N / 16 ) (M/16)\times (N/16) (M/16)×(N/16)大小的feature maps尺度;之后将每个proposal水平和竖直分为pooled_w和pooled_h份,对每一份都进行max pooling处理。这样处理后,即使大小不同的proposal,输出结果都是大小,实现了Fixed-length Output(固定长度输出)。
    【Faster R-CNN论文精度系列】原文精析_第21张图片
  • tf代码实现
    Convolutional feature maps are cropped to a constant size of (14,14) and then maxpooled to (7x7)
    推荐阅读:详细的Faster R-CNN源码解析之ROI-Pooling逐行代码解析

4 Classification

  • 给出cls_prob和bbox_pred
    Classification部分利用已经获得的proposal feature maps(固定大小),通过Full connect层与softmax计算每个proposal具体属于那个类别(如人,车,电视等),输出cls_prob概率向量;同时再次利用bounding box regression获得每个proposal的位置偏移量bbox_pred,用于回归更加精确的目标检测框。【Faster R-CNN论文精度系列】原文精析_第22张图片
  • 从PoI Pooling获取到7x7=49大小的proposal feature maps后,送入后续网络,可以看到做了如下2件事:
  1. 通过全连接和softmax对proposals进行分类,这实际上已经是识别的范畴了
  2. 再次对proposals进行bounding box regression,获取更高精度的rect_box
  • 这里来看看全连接层InnerProduct layers,示意图如下:
    【Faster R-CNN论文精度系列】原文精析_第23张图片
    其中W和bias B都是预先训练好的,即大小是固定的,当然输入X和输出Y也就是固定大小。所以,这也就印证了之前Roi Pooling的必要性。到这里,我想其他内容已经很容易理解,不在赘述了。

5 Faster R-CNN训练

(注:这部分参考的知乎大佬的文章,没有做改动,只是搬运而已,侵删谢谢!)

  • Faster R-CNN的训练,是在已经训练好的model(如VGG_CNN_M_1024,VGG,ZF)的基础上继续进行训练。实际中训练过程分为6个步骤:
  1. 在已经训练好的model上,训练RPN网络,对应stage1_rpn_train.pt
  2. 利用步骤1中训练好的RPN网络,收集proposals,对应rpn_test.pt
  3. 第一次训练Fast RCNN网络,对应stage1_fast_rcnn_train.pt
  4. 第二训练RPN网络,对应stage2_rpn_train.pt
  5. 再次利用步骤4中训练好的RPN网络,收集proposals,对应rpn_test.pt
  6. 第二次训练Fast RCNN网络,对应stage2_fast_rcnn_train.pt
  7. 可以看到训练过程类似于一种“迭代”的过程,不过只循环了2次。至于只循环了2次的原因是应为作者提到:“A similar alternating training can be run for more iterations, but we have observed negligible improvements”,即循环更多次没有提升了。接下来本章以上述6个步骤讲解训练过程。

【Faster R-CNN论文精度系列】原文精析_第24张图片

图18 Faster RCNN训练步骤(引用自参考文章[1])

5.1 训练RPN网络

在该步骤中,首先读取RBG提供的预训练好的model(本文使用VGG),开始迭代训练。来看看stage1_rpn_train.pt网络结构
【Faster R-CNN论文精度系列】原文精析_第25张图片
(考虑图片大小,Conv Layers中所有的层都画在一起了,如红圈所示,后续图都如此处理)
与检测网络类似的是,依然使用Conv Layers提取feature maps。整个网络使用的Loss如下:
【Faster R-CNN论文精度系列】原文精析_第26张图片

上述公式中, i 表示anchors index, p i p_{i} pi 表示foreground softmax probability, p i ∗ p_{i}^{*} pi代表对应的GT predict概率(即当第i个anchor与GT间 IoU>0.7,认为是该anchor是foreground, p i ∗ = 1 p_{i}^{*}=1 pi=1;反之 IoU<0.3 时,认为是该anchor是background, p i ∗ = 0 p_{i}^{*}=0 pi=0;至于那些 0.3 t ∗ t^{*} t代表对应foreground anchor对应的GT box。可以看到,整个Loss分为2部分:

  1. cls loss,即rpn_cls_loss层计算的softmax loss,用于分类anchors为forground与background的网络训练
  2. reg loss,即rpn_loss_bbox层计算的soomth L1 loss,用于bounding box regression网络训练。注意在该loss中乘了 p i ∗ p_{i}^{*} pi ,相当于只关心foreground anchors的回归(其实在回归中也完全没必要去关心background)。
    由于在实际过程中, N c l s N_{cls} Ncls N r e g N_{reg} Nreg差距过大,用参数λ平衡二者(如 N c l s = 256 N_{cls}=256 Ncls=256 N r e g = 2400 N_{reg}=2400 Nreg=2400时设置 λ = 10 λ=10 λ=10 ),使总的网络Loss计算过程中能够均匀考虑2种Loss。这里比较重要是 L r e g L_{reg} Lreg使用的soomth L1 loss,计算公式如下:
    【Faster R-CNN论文精度系列】原文精析_第27张图片
    在这里插入图片描述

了解数学原理后,反过来看流程图
3. 在RPN训练阶段,rpn-data(python Anchor_Target_Layer)层会按照和test阶段Proposal层完全一样的方式生成Anchors用于训练
4. 对于rpn_loss_cls,输入的rpn_cls_scors_reshape和rpn_labels分别对应p与 p ∗ p^{*} p N c l s N_{cls} Ncls 参数隐含在p与 p ∗ p^{*} p的caffe blob的大小中
5. 对于rpn_loss_bbox,输入的rpn_bbox_pred和rpn_bbox_targets分别对应t于 t ∗ t^{*} t,rpn_bbox_inside_weigths对应 p ∗ p^{*} p,rpn_bbox_outside_weigths未用到(从soomth_L1_Loss layer代码中可以看到),而 N r e g N_{reg} Nreg 同样隐含在caffe blob大小中

这样,公式与代码就完全对应了。特别需要注意的是,在训练和检测阶段生成和存储anchors的顺序完全一样,这样训练结果才能被用于检测!

5.2 通过训练好的RPN网络收集proposals

在该步骤中,利用之前的RPN网络,获取proposal rois,同时获取foreground softmax probability,如图20,然后将获取的信息保存在python pickle文件中。该网络本质上和检测中的RPN网络一样,没有什么区别。
【Faster R-CNN论文精度系列】原文精析_第28张图片
(rpn_test.pt)

5.3 训练Faster RCNN网络

读取之前保存的pickle文件,获取proposals与foreground probability。从data层输入网络。然后:

  1. 将提取的proposals作为rois传入网络,如图19蓝框
  2. 计算bbox_inside_weights+bbox_outside_weights,作用与RPN一样,传入soomth_L1_loss layer,如图20绿框

这样就可以训练最后的识别softmax与最终的bounding box regression了。
【Faster R-CNN论文精度系列】原文精析_第29张图片
(stage1_fast_rcnn_train.pt)
之后的stage2训练都是大同小异,不再赘述了。Faster R-CNN还有一种end-to-end的训练方式,可以一次完成train,有兴趣请自己看作者GitHub吧。

【Faster R-CNN论文精度系列】

(如下为建议阅读顺序)
1【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?
2【Faster R-CNN论文精度系列】代码解读并深入理解Region Proposal Network
3【Faster R-CNN论文精度系列】代码解读并深入理解Anchor和Anchor Box
4【Faster R-CNN论文精度系列】原文精析

Reference

https://zhuanlan.zhihu.com/p/31426458
https://blog.csdn.net/jiongnima/article/details/80016683
https://zhuanlan.zhihu.com/p/24916624

你可能感兴趣的:(Faster,RCNN,TensorFlow,DeepLearning)