【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?

【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论文精度系列】原文精析
5 Faster R-CNN: Down the rabbit hole of modern object detection

1 Github代码

从Github上搜索faster rcnn代码,可以看到很多版本,star数最多的版本都是论文的作者,对源码框架文件的分析很有必要的,可以弄清楚很多我们无法从论文中读懂的点。

  • RBG大神写的Python版本code:
    https://github.com/rbgirshick/py-faster-rcnn
    【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第1张图片
  • ShaoqingRen大神写的Matlab版本code:
    https://github.com/ShaoqingRen/faster_rcnn
    【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第2张图片

2 框架代码解读

在最初的学习之中,我总是强调对论文的解读,对原文中原理的理解,但是我在今天的学习中我发现,论文与代码同步进行是一个很好的选择,具体的代码可以让你很好的理解这篇文章做了些什么东西,可以加深你对论文的理解,这个学习方法很值得推荐。

2.1 train.prototxt文件解读

找到在目录py-faster-rcnn/models/pascal_voc/VGG16/faster_rcnn_end2end下有一个train.prototxt文件,这是整个论文的框架理解,本文选取经典模型进行解读,ZF的框架可以在源码中找到必看!

阅读代码前必须要理解的参数:

  • lr_mult: 学习率的系数,最终的学习率是这个数乘以solver.prototxt配置文件中的base_lr。如果有两个lr_mult, 则第一个表示权值的学习率,第二个表示偏置项的学习率。一般偏置项的学习率是权值学习率的两倍。
  • num_output: 过滤器(filfter)的个数
  • weight_filler: 权值初始化。 默认为“constant",值全为0,很多时候我们用"xavier"算法来进行初始化,也可以设置为”gaussian"
  • bias_filler: 偏置项的初始化。一般设置为"constant",值全为0
  • bias_term: 是否开启偏置项,默认为true, 开启

Relu激活函数解读

  • 对于修正线性单元(Rectified linear unit,ReLU)优点如下:
    ①单侧抑制 ②相对宽阔的兴奋边界 ③稀疏激活性
  • 资料参考
    https://blog.csdn.net/cherrylvlei/article/details/53149381
    https://www.cnblogs.com/qw12/p/6294430.html

2.2 Code分析

name: "VGG_ILSVRC_16_layers"
layer {
  name: 'input-data'
  type: 'Python'
  top: 'data'
  top: 'im_info'
  top: 'gt_boxes'	# top指输入的数据,包括data、image & gt
  python_param
  {
    module: 'roi_data_layer.layer'
    layer: 'RoIDataLayer'
    param_str: "'num_classes': 21"	# 21个分类
  }
}
# conv1_1
layer {
  name: "conv1_1"
  type: "Convolution"
  bottom: "data"    # bottom是输入
  top: "conv1_1"    # top是输出
  param {
    lr_mult: 0
    decay_mult: 0
  }
  param {
    lr_mult: 0
    decay_mult: 0
  }
  convolution_param {
    num_output: 64
    pad: 1
    kernel_size: 3
  }
}
# relu1_1
layer {
  name: "relu1_1"
  type: "ReLU"
  bottom: "conv1_1"
  top: "conv1_1"
}
# conv1_2
layer {
  name: "conv1_2"
  type: "Convolution"
  bottom: "conv1_1"
  top: "conv1_2"
  param {
    lr_mult: 0
    decay_mult: 0
  }
  param {
    lr_mult: 0
    decay_mult: 0
  }
  convolution_param {
    num_output: 64
    pad: 1
    kernel_size: 3
  }
}
# relu1_2
layer {
  name: "relu1_2"
  type: "ReLU"
  bottom: "conv1_2"
  top: "conv1_2"
}
# pool1
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1_2"
  top: "pool1"
  pooling_param {
    pool: MAX	# max pooling
    kernel_size: 2
    stride: 2
  }
}
# conv2_1
layer {
  name: "conv2_1"
  type: "Convolution"
  bottom: "pool1"
  top: "conv2_1"
  param {
    lr_mult: 0
    decay_mult: 0
  }
  param {
    lr_mult: 0
    decay_mult: 0
  }
  convolution_param {
    num_output: 128
    pad: 1
    kernel_size: 3
  }
}
# relu2_1
layer {
  name: "relu2_1"
  type: "ReLU"
  bottom: "conv2_1"
  top: "conv2_1"
}
# conv2_2
layer {
  name: "conv2_2"
  type: "Convolution"
  bottom: "conv2_1"
  top: "conv2_2"
  param {
    lr_mult: 0
    decay_mult: 0
  }
  param {
    lr_mult: 0
    decay_mult: 0
  }
  convolution_param {
    num_output: 128
    pad: 1
    kernel_size: 3
  }
}
# relu2_2
layer {
  name: "relu2_2"
  type: "ReLU"
  bottom: "conv2_2"
  top: "conv2_2"
}
# pool2
layer {
  name: "pool2"
  type: "Pooling"
  bottom: "conv2_2"
  top: "pool2"
  pooling_param {
    pool: MAX   # max pooling
    kernel_size: 2
    stride: 2
  }
}
# conv3_1
layer {
  name: "conv3_1"
  type: "Convolution"
  bottom: "pool2"
  top: "conv3_1"
  param {
    lr_mult: 1  # weight项的学习率(从这里开始了学习率的调整更新)
  }
  param {
    lr_mult: 2  # bias项的学习率,一般是weight的两倍
  }
  convolution_param {
    num_output: 256
    pad: 1
    kernel_size: 3
  }
}
# relu3_1
layer {
  name: "relu3_1"
  type: "ReLU"
  bottom: "conv3_1"
  top: "conv3_1"
}
# conv3_2
layer {
  name: "conv3_2"
  type: "Convolution"
  bottom: "conv3_1"
  top: "conv3_2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 256
    pad: 1
    kernel_size: 3
  }
}
# relu3_2
layer {
  name: "relu3_2"
  type: "ReLU"
  bottom: "conv3_2"
  top: "conv3_2"
}
# conv3_3
layer {
  name: "conv3_3"
  type: "Convolution"
  bottom: "conv3_2"
  top: "conv3_3"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 256
    pad: 1
    kernel_size: 3
  }
}
layer {
  name: "relu3_3"
  type: "ReLU"
  bottom: "conv3_3"
  top: "conv3_3"
}
layer {
  name: "pool3"
  type: "Pooling"
  bottom: "conv3_3"
  top: "pool3"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
# conv4_1
layer {
  name: "conv4_1"
  type: "Convolution"
  bottom: "pool3"
  top: "conv4_1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 512
    pad: 1
    kernel_size: 3
  }
}
# relu4_1
layer {
  name: "relu4_1"
  type: "ReLU"
  bottom: "conv4_1"
  top: "conv4_1"
}
# conv4_2
layer {
  name: "conv4_2"
  type: "Convolution"
  bottom: "conv4_1"
  top: "conv4_2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 512
    pad: 1
    kernel_size: 3
  }
}
# relu4_2
layer {
  name: "relu4_2"
  type: "ReLU"
  bottom: "conv4_2"
  top: "conv4_2"
}
# conv4_3
layer {
  name: "conv4_3"
  type: "Convolution"
  bottom: "conv4_2"
  top: "conv4_3"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 512
    pad: 1
    kernel_size: 3
  }
}
# relu4_3
layer {
  name: "relu4_3"
  type: "ReLU"
  bottom: "conv4_3"
  top: "conv4_3"
}
# pool4
layer {
  name: "pool4"
  type: "Pooling"
  bottom: "conv4_3"
  top: "pool4"
  pooling_param {
    pool: MAX   # max pooling
    kernel_size: 2
    stride: 2
  }
}
# conv5_1
layer {
  name: "conv5_1"
  type: "Convolution"
  bottom: "pool4"
  top: "conv5_1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 512
    pad: 1
    kernel_size: 3
  }
}
# relu5_1
layer {
  name: "relu5_1"
  type: "ReLU"
  bottom: "conv5_1"
  top: "conv5_1"
}
# conv5_2
layer {
  name: "conv5_2"
  type: "Convolution"
  bottom: "conv5_1"
  top: "conv5_2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 512
    pad: 1
    kernel_size: 3
  }
}
# relu5_2
layer {
  name: "relu5_2"
  type: "ReLU"
  bottom: "conv5_2"
  top: "conv5_2"
}
# conv5_3
layer {
  name: "conv5_3"
  type: "Convolution"
  bottom: "conv5_2"
  top: "conv5_3"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 512
    pad: 1
    kernel_size: 3
  }
}
# relu5_3
layer {
  name: "relu5_3"
  type: "ReLU"
  bottom: "conv5_3"
  top: "conv5_3"
}

# ============ RPN ==============

# rpn_conv/3x3
# 对卷积网络传来的feature map做RPN的第一步操作:卷积
layer {
  name: "rpn_conv/3x3"
  type: "Convolution"
  bottom: "conv5_3" # 接在conv5_3后接了一个RPN-layer
  top: "rpn/output"
  param { lr_mult: 1.0 }
  param { lr_mult: 2.0 }
  convolution_param {
    num_output: 512
    kernel_size: 3 pad: 1 stride: 1 # conv参数设定
    weight_filler { type: "gaussian" std: 0.01 }
    bias_filler { type: "constant" value: 0 }
  }
}
# 接relu激活函数,增加其非线性
layer {
  name: "rpn_relu/3x3"
  type: "ReLU"
  bottom: "rpn/output"
  top: "rpn/output"
}
# # 开始cls和reg
# rpn_cls_score
layer {
  name: "rpn_cls_score"
  type: "Convolution"
  bottom: "rpn/output"
  top: "rpn_cls_score"
  param { lr_mult: 1.0 }
  param { lr_mult: 2.0 }
  convolution_param {
    num_output: 18   # 2(label+prob) * 9(anchors)
    kernel_size: 1 pad: 0 stride: 1
    weight_filler { type: "gaussian" std: 0.01 }
    bias_filler { type: "constant" value: 0 }
  }
}
# rpn_bbox_pred
layer {
  name: "rpn_bbox_pred"
  type: "Convolution"
  bottom: "rpn/output"
  top: "rpn_bbox_pred"
  param { lr_mult: 1.0 }
  param { lr_mult: 2.0 }
  convolution_param {
    num_output: 36   # 4 * 9(anchors)
                     # 含有36个输出,每个anchor有4个坐标值
    kernel_size: 1 pad: 0 stride: 1
    weight_filler { type: "gaussian" std: 0.01 }
    bias_filler { type: "constant" value: 0 }
  }
}

layer {
   bottom: "rpn_cls_score"
   top: "rpn_cls_score_reshape"
   name: "rpn_cls_score_reshape"
   type: "Reshape"
   reshape_param { shape { dim: 0 dim: 2 dim: -1 dim: 0 } }
}

# rpn-data
# 这一块进行了很多的操作,在送入回归之前,对很多框进行筛选
# 比如在边缘上出界的框怎么处理,多个框重叠怎么处理(NMS)
layer {
  name: 'rpn-data'
  type: 'Python'
  # 输入
  bottom: 'rpn_cls_score'   # 分类得分
  bottom: 'gt_boxes'
  bottom: 'im_info'
  bottom: 'data'    # 输入data
  # 输出
  top: 'rpn_labels' # 利用对9个anchor给一个label(>0.7为前景)(<0.3为背景)
  top: 'rpn_bbox_targets'   # 算出原文中3.1.2节中给出的8个参数
  top: 'rpn_bbox_inside_weights'
  top: 'rpn_bbox_outside_weights'   # 俩weight为了计算loss而设置的
  python_param {
    module: 'rpn.anchor_target_layer'
    layer: 'AnchorTargetLayer'
    param_str: "'feat_stride': 16"
  }
}
# rpn_loss_cls
# 分类的loss
layer {
  name: "rpn_loss_cls"
  type: "SoftmaxWithLoss"
  bottom: "rpn_cls_score_reshape"
  bottom: "rpn_labels"
  propagate_down: 1
  propagate_down: 0
  top: "rpn_cls_loss"
  loss_weight: 1
  loss_param {
    ignore_label: -1
    normalize: true
  }
}
# rpn_loss_bbox
# b.box的回归的loss
layer {
  name: "rpn_loss_bbox"
  type: "SmoothL1Loss"
  bottom: "rpn_bbox_pred"
  bottom: "rpn_bbox_targets"
  bottom: 'rpn_bbox_inside_weights'
  bottom: 'rpn_bbox_outside_weights'
  top: "rpn_loss_bbox"
  loss_weight: 1
  smooth_l1_loss_param { sigma: 3.0 }
}

#============ RoI Proposal ===============

layer {
  name: "rpn_cls_prob"
  type: "Softmax"
  bottom: "rpn_cls_score_reshape"
  top: "rpn_cls_prob"
}

layer {
  name: 'rpn_cls_prob_reshape'
  type: 'Reshape'
  bottom: 'rpn_cls_prob'
  top: 'rpn_cls_prob_reshape'
  reshape_param { shape { dim: 0 dim: 18 dim: -1 dim: 0 } }
}

layer {
  name: 'proposal'
  type: 'Python'
  bottom: 'rpn_cls_prob_reshape'
  bottom: 'rpn_bbox_pred'
  bottom: 'im_info' # 输入了三个东西reshape后的分类概率、预测的框和im信息
  top: 'rpn_rois'   # 产生一些region,
#  top: 'rpn_scores'
  python_param {
    module: 'rpn.proposal_layer'
    layer: 'ProposalLayer'
    param_str: "'feat_stride': 16"
  }
}

#layer {
#  name: 'debug-data'
#  type: 'Python'
#  bottom: 'data'
#  bottom: 'rpn_rois'
#  bottom: 'rpn_scores'
#  python_param {
#    module: 'rpn.debug_layer'
#    layer: 'RPNDebugLayer'
#  }
#}

layer {
  name: 'roi-data'
  type: 'Python'
  bottom: 'rpn_rois'
  bottom: 'gt_boxes'
  top: 'rois'
  top: 'labels'
  top: 'bbox_targets'
  top: 'bbox_inside_weights'
  top: 'bbox_outside_weights'
  python_param {
    module: 'rpn.proposal_target_layer'
    layer: 'ProposalTargetLayer'
    param_str: "'num_classes': 21"
  }
}

#========= RCNN ============
# 分类任务层

# 对大小不同的框进行roi-pooling
layer {
  name: "roi_pool5"
  type: "ROIPooling"
  bottom: "conv5_3"
  bottom: "rois"
  top: "pool5"
    
  # 对不同的框pooling到固定的尺度大小(参数设定),便于做最后的分类
  roi_pooling_param {
    pooled_w: 7
    pooled_h: 7
    spatial_scale: 0.0625 # 1/16
  }
}
# fc6
# pooling后当然是接上一些全连接层
layer {
  name: "fc6"
  type: "InnerProduct"
  bottom: "pool5"
  top: "fc6"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 4096
  }
}
layer {
  name: "relu6"
  type: "ReLU"
  bottom: "fc6"
  top: "fc6"
}
# 做了一个dropout
layer {
  name: "drop6"
  type: "Dropout"
  bottom: "fc6"
  top: "fc6"
  dropout_param {
    dropout_ratio: 0.5
  }
}
layer {
  name: "fc7"
  type: "InnerProduct"
  bottom: "fc6"
  top: "fc7"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 4096
  }
}
layer {
  name: "relu7"
  type: "ReLU"
  bottom: "fc7"
  top: "fc7"
}
layer {
  name: "drop7"
  type: "Dropout"
  bottom: "fc7"
  top: "fc7"
  dropout_param {
    dropout_ratio: 0.5
  }
}
layer {
  name: "cls_score"
  type: "InnerProduct"
  bottom: "fc7"
  top: "cls_score"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 21
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layer {
  name: "bbox_pred"
  type: "InnerProduct"
  bottom: "fc7"
  top: "bbox_pred"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 84
    weight_filler {
      type: "gaussian"
      std: 0.001
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layer {
  name: "loss_cls"
  type: "SoftmaxWithLoss"
  bottom: "cls_score"
  bottom: "labels"
  propagate_down: 1
  propagate_down: 0
  top: "loss_cls"
  loss_weight: 1
}
layer {
  name: "loss_bbox"
  type: "SmoothL1Loss"
  bottom: "bbox_pred"
  bottom: "bbox_targets"
  bottom: "bbox_inside_weights"
  bottom: "bbox_outside_weights"
  top: "loss_bbox"
  loss_weight: 1
}

下面给出vgg16网络结构图(使用PPT绘制,不会画的可以留言给我)【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第3张图片

2.3 重要理解——什么是“学习”?

  • 问题提出:为什么在源代码中设置18和36?逻辑关系是如何描述?机器如何学习到anchor是什么?如下代码所示:
# 在rpn_cls_score中给出了:
num_output: 18   # 2(bg/fg) * 9(anchors)
# 在rpn_bbox_pred中给出了
num_output: 36   # 4 * 9(anchors)

但是机器在此时以及此时之前,是不知道anchor是啥,更不会知道anchor背后对应的18和36个数字代表的是啥,对于此时的机器来说,只知道18+36个参数。

  • 学习的本质
    经过以下的代码rpn_loss_cls的分类和rpn_loss_bbox的回归
layer 
{
  name: "rpn_loss_cls"
  type: "SoftmaxWithLoss"
  bottom: "rpn_cls_score_reshape"
  bottom: "rpn_labels"
  propagate_down: 1
  propagate_down: 0
  top: "rpn_cls_loss"
  loss_weight: 1
  loss_param {
    ignore_label: -1
    normalize: true
  }
}
layer 
{
  name: "rpn_loss_bbox"
  type: "SmoothL1Loss"	# 引入Smooth L1 Loss函数
  bottom: "rpn_bbox_pred"
  bottom: "rpn_bbox_targets"
  bottom: 'rpn_bbox_inside_weights'
  bottom: 'rpn_bbox_outside_weights'
  top: "rpn_loss_bbox"
  loss_weight: 1
  smooth_l1_loss_param { sigma: 3.0 }
}

即经过了Loss Function后,机器对其参数值(18个或者36个)计算与label之间的“距离”(即:loss,同理越小越好),这个“距离”越小越好。在这个不断迭代、学习的过程之中,学到了当这18或36个参数带入运算时,能够达到最小的Loss,即可以“学习”到了其内涵。

所以在实际的网络中,这些框不是被截取出来的一块区域,而是在计算loss之前,被送进入的data,而是在loss的过程中学习到的,这是我们的anchor,所以这个anchor以及这些框不是实际存在的,是人为的想象出来而设定的

现在整体来理一下上文中的逻辑关系,机器在Loss步骤之前是不知道anchor是什么的,也不知道为什么会有18/36个参数在面前,当时给了一个Loss后,会进行迭代修正,直到拟合到了True Answer上或者接近于它的时候,会达到一个最小的Loss值,在这个不断迭代(机器学习的本质:迭代=学习)的过程中,机器学习到了一个东西:只有这样调整参数才能使我们预先设定的Loss下降到最小。

也可以结合梯度下降算法来看,目标函数(Loss Function)通过GD算法(SGD、BSGD)下降到一个函数的最优解(可能是局部最优,也可能是全局最优),在机器中这个“下降”的过程就是对应人脑中“学习”的过程,这就是机器“学习”的本质。

  • smooth_l1_loss是啥?https://blog.csdn.net/wfei101/article/details/77778462

3 原文扼要分析

Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks
Shaoqing Ren, Kaiming He, Ross Girshick, and Jian Sun

3.1 为什么选则Faster R-CNN

分析论文,不能拿到手从头开始看起,要有所整体认知,鄙人认为,应给出几个看论文的理由,才会有动力让你看到最后,这也就是我的3.1节不选择分析摘要的原因。

其次对于看论文,得有精度得有泛读,泛读适合读摘要读图片读表格,弄清他的思路;而对于大牛实验室的著作,我们需要从源码入手,解读其内涵,分析其思路,模仿其文体。选择Faster R-CNN,我给出了以下几个理由:

  1. 论文文体优秀,结构分明,适合精度语句并加以模仿
  2. 开源(作者给出了.m & .py)
  3. 公开数据(可获取且可信度高)
  4. 算法代码适合精读

3.2 摘要

【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第4张图片摘要给出了论文的核心凝练内容(黄色部分标出),对于2015年的这篇论文,着实是state-of-the-art的相当高的水准了,文章创造性的提出了一个RPN网络,并在多个数据集上去的了优秀的成绩,且开源。

3.2.1 什么是目标检测?

很简单,目标检测 = 目标 + 检测。对于图像分类、物体检测、物体分割、实例分割、语义分割的区别,在笔者的这篇文章中有所提及,推荐阅读,入门必备:
https://blog.csdn.net/Gerwels_JI/article/details/82990189

3.2.2 什么是real-time object detection

对于人眼的极限是20 FPS算是极限了,一般我们玩的LOL和PUBG游戏是70 FPS左右,而在本文中可以达到5 FPS的成绩。当然这个成绩在2018年的今天不算太优秀:现在two stage方法的有rcnn、Fast rcnn、Faster rcnn、Mask rcnn;One shot 的方法有ssd和yolo。速度快请选yolo大法,精度高,请选择rcnn系列。

3.2.3 Faster R-CNN只能做物体检测?

人脸检测可以通过这个算法做(比如从96%提升到97%,小米在FDDB上得到了第一名的准确率)。也就是Faster R-CNN的应用面非常广,对于车牌识别、人脸检测、以及传统的物体识别,都可以带入Faster RCNN这个框架中来。

3.2.4 region proposal

  • 问题引入
    对于分类任务,无非是对一个类别判0、1,模式识别这门课程讲了很多分类的方法(怎么线性分类,怎么使用高维空间对低维空间不可线性分类的类别进行高维线性分类,以及特征降维等等知识)。在图像理解领域中,对框内的物体进行分类并不难,我们只需要对框内的物体进行线性二分类问题就行了,但我们怎么proposal这个框,是一个值得研究考虑的问题。
  • 任务目标
    难点在于找框,图中有框吗?框在哪?框多大?框是正方形吗?框出去了怎么办?
  • 传统方法
    Selective Research(根据像素、纹理进行框的提议2000个、并对这些框进行固定阈值下的分类任务,耗时耗力)
  • 本文中的新颖方法
    提出了第三代R-CNN系列算法:RPN算法(找框算法),不是对每个框逐个判定分类,而是通过了一个卷积网络提取特征,再进行整体上的操作,实现了卷积内容和参数上的共享,大大提升了整体速度,实现了“Real-time”的功能
  • 最后这篇文章的强大之处在于把图像理解过程中的所有的工作全部糅合在了卷积神经网络上面,整合成了由layer-to-layer组成的end-to-end的结构。

3.2.4 Index Terms

Object Detection, Region Proposal, Convolutional Neural Network

4 Image Pyramid(Figure-1 )

【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第5张图片

  • Different schemes for addressing multiple scales and sizes

4.1 图像金字塔格式(图(a))

4.1.1 什么是Image Pyramid?

【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第6张图片

百度百科给出的定义(不愿意看直接看下面的精简版):图像金字塔是图像多尺度表达的一种,是一种以多分辨率来解释图像的有效但概念简单的结构。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。

注意:上采样和下采样是非线性处理,不可逆,有损的处理!

4.1.2 图1 (a)解读:不同的Image

简单来说一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。图1 (a)中构建了图像和特征映射图金字塔,分类器在各种尺度图上运行。在经过Scale变换操作后,图像大小不同其中框住的内容是不同的(这会导致处理速度变慢:相同的filter在不同像素大小的同一张图片上滑动,其每一个感受野所感受到的内容是不一样的,速度也是不一样的)。总之,事物是发展的,老的事物有其存在的意义,也有其被替代的理由,他的速度太慢了。

4.1.3 图1 (b)解读:不同的Filter

在同一张feature image上做不同的multiple fliter sizes(即:在特征映射图上run具有多个比例/大小的Filter所组成的金字塔),从而得到了不同的感受野的大小。

  • 补充:感受野是什么?
    在卷积神经网络CNN中,决定某一层输出结果中一个元素所对应的输入层的区域大小,被称作感受野receptive field。
    推荐阅读:https://blog.csdn.net/u010725283/article/details/78593410

4.1.4 图1 (c )解读:Anchor机制

以上都是传统算法的表现,本文中在回归函数中使用金字塔边界框参数进行回归操作,并引入了anchor的概念,至于怎么实现,请看下文

5 Anchor(Figure-3)

【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第7张图片

5.1 问题引入

怎么通过引入anchor来提取出我们所需要的不同大小的框?

5.2 预先认知

在feature map中每个特征点(也叫锚点anchor),每个锚点上有k个anchor boxes(论文中k=9=3*3,k个都是基于multiple scales(像素值) and aspect ratios(1:1/1:2/2:1))

5.3 为什么不在conv之前做预测box?

  • 传统方法:产生box,再经过conv,每一个框要被fed into一个神经网络running
  • 但若在feature map上找anchor,从而对应imput image上的9个anchor box,在“image-to-conv”这个过程中的整个卷积层的parameter和feature内容是共享的(省时)
  • 这就是为什么在feature map上的一个点要产生9个不同大小的框(原文:we introduce novel “anchor” boxes that serve as references at multiple scales and aspect ratios.)

5.4 对图3的解读

图3左图是RPN网络,在feature map上的每个点有9个anchor box
默认情况下,我们使用3个尺度和3个长宽比,在每个滑动位置产生 k = 9 k=9 k=9个Anchors boxes。对于大小为 W × H W×H W×H(通常约为2400)的convolutional feature map,总计 W × H × k W×H×k W×H×k个Anchors boxes。
具体实现解读和代码分析请戳此链接:

6 Region Proposal Networks

【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第8张图片

6.1 原文解读

  • RPN的输入与输出
    RPN也以任意大小的图像作为输入(因为input不同),输出一组rectangular object proposals(每个proposals都有一个objectness score)。在本节我们将描述怎么用fully convolutional network [7]对这个过程进行建模。
  • Share computation
    因为我们的最终目标是将RPN与Fast R-CNN object detection network [2] 进行share computation,所以我们假设两个网络共享一组相同的卷积层。
  • 引入ZF-net与VGG-16
    在本文的实验中,分别研究了具有5个shareable convolutional layers的Zeiler and Fergus (ZF) model [32]和具有13个shareable convolutional layers的the Simonyan and Zisserman (VGG-16) model [3]。
  • mini-network降维特征
    为了生成region proposals,我们在the last shared convolutional layer所输出的the convolutional feature map上建立一个mini-network。这个mini-network将输入卷积特征映射图的 n × n n × n n×n的spatial window作为输入。每个滑动窗口会将feature map映射到一个低维特征(ZF为256-d,VGG为512-d,后面再跟一个ReLU)。(在本文中使用n=3,注意输入图像上的有效感受野 (the effective receptive field)很大 (171 and 228 pixels inputs for ZF and VGG, respectively)。)
    所得的features被输入到两个sibling fully connected layers: 分别是box-regression layer(reg-layer)和box-classification layer (cls-layer)。
  • 图3(左)
    Figure 3 (left)显示了这个mini-network的一个位置,注意:因为小网络以Sliding-window方式工作,所有空间位置(all spatial locations)共享全连接层。这种架构通过一个n×n卷积层,后面是两个子1×1卷积层(分别用于reg-layer和cls-layer)来实现。

6.2 细节分析

  • 最终我们需要分类的不是图像,而是通过整个过程生成的框。其中输入的image的大与小是无所谓的(当然实际代码中会有一个判定的限定过程,最长边不能大于1000)
  • image-CONV-RPN-output的过程:输入any size的image,输出是一系列的矩形框(坐标)和框的objectness score(是不是物体的分数,是前景还是背景,置信度多少)
  • This feature is fed into two sibling fullyconnected layers—a box-regression layer (reg) and a box-classification layer (cls).

7 Loss Function

7.1 Positive/Negative Label

为了训练RPN,我们为每个anchor分配一个binary class label (of being an object or not),label的分配规则如下:

  1. Positive label:(i)具有与实际边界框的重叠最高交并比(IoU)的anchor box,或者(ii)具有与实际边界框的重叠超过0.7 的IoU阈值的anchor box。(注意,单个真实边界框可以为多个锚点分配正标签。通常第二个条件足以确定正样本;但我们仍然采用第一个条件,因为在一些极少数情况下,第二个条件可能找不到正样本)
  2. Negative label:如果一个anchor box的IoU比值低于0.3,我们给负anchor box分配一个negative label
  3. Neither positive nor negative label:既不正面也不负面的anchor box不会有助于训练目标函数(这个思想和MTCNN中label思想设置的相一致,如下图==(注意!下图不是Faster R-CNN的label设定!)==)
    【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第9张图片
    总之,看proposal region(box框)与GT(true answer)作比较,看两者之间的重合程度,重合程度高是前景,重合程度低是背景,处于中间IOU值的anchor box的对于训练是没有用的

7.2 回归

b.box regression:首先理解啥是回归?(一点一点的迭代拟合到true answer(GT)),这里通过loss来回归(需要找一个衡量的标准),这里不用欧式距离或者曼哈顿距离来衡量,而是用 t x t_x tx t y t_y ty(坐标差除以w或者h)来表示。

7.3 如何进行回归——损失函数

7.3.1 Multi-task Loss

【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第10张图片

7.3.2 L c l s L_{cls} Lcls L r e g L_{reg} Lreg的定义

【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第11张图片

7.3.3 参数化的坐标

【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第12张图片

8 流程图(Figure-2)

【Faster R-CNN论文精度系列】从Faster R-CNN源码中,我们“学习”到了什么?_第13张图片

8.1 流程

  • Two module (RPN + Fast RCNN detector)(原文描述)
    Our object detection system, called Faster R-CNN, is composed of two modules. The first module is a deep fully convolutional network that proposes regions, and the second module is the Fast R-CNN detector that uses the proposed regions.
  • 背景介绍
    经过R-CNN和Fast RCNN的积淀,Ross B. Girshick在2016年提出了新的Faster RCNN,在原有的结构基础上,Faster R-CNN将特征抽取(feature extraction)、proposal提取、bounding box regression(rect refine)、classification都整合在了卷积神经网络之上,利用卷积神经网络的优异特性(train时间长,test时间短),使得其目标检测领域的综合性能有较大提高,在检测速度方面尤为明显。
  • 框架图分析(Faster RCNN其实可以分为4个主要内容):
  1. Conv Layers
    作为一种CNN网络目标检测方法,Faster RCNN首先使用一组基础的CNN层提取image的feature maps。该feature maps被shared(划重点:卷积层共享)用于后续RPN层和RoI Pooling层。
  2. Region Proposal Networks
    RPN网络用于生成region proposals。该层通过softmax判断anchors box属于foreground或者background,并给出分类分数和box坐标,再利用bounding box regression修正anchors获得精确的proposals。
  3. RoI Pooling
    该层主要作用是将region proposals,pooling到同一个大小尺寸(7*7),收集feature maps和proposals并综合这些信息送入后续全连接层,用于判定目标类别。
  4. Classification & B.box Regression
    利用proposal feature maps计算proposal的类别,同时再次bounding box regression获得检测框最终的精确位置。

8.2 细节分析

8.2.1 框架图核心元素

conv layer(vgg-16或者ZF-net)、RPN、RoI Pooling层,具体分析就是围绕以上展开的

8.2.2 RoI Pooling层的作用

传统的卷积网络中(CONV-FC-Classification):从conv送到到FC中的图像的大小是固定的,但本文中从input就是任意的,anchor box也是任意的,当然最终进行分类的,不是对图像进行分类,而是对框内的东西进行分类。

  • 问题产生:
    产生k=9个框,其大小是不一样的,现在如何与全连接层连接在一起?没有fc-layer如何进行后续的回归与分类?
  • 解决方案:
    对不同的框做一个RoI Pooling,这个RoI Pooling是一个自适应的,使得不同大小的框的resize到同一个大小(7*7),现在就可以做classification了(这个方法也是他人提出的)

8.2.3 具体代码描述

图2展示了python版本中的VGG16模型中的faster_rcnn_test.prototxt的网络结构,可以清晰的看到该网络:

  1. 对于一副任意大小PxQ的图像,首先缩放至固定大小MxN,然后将MxN图像送入网络;
  2. Conv layers中包含了13个conv层+13个relu层+4个pooling层(对输入MxN图像进行特征提取,并便于后面的卷积层参数共享和RoI Pooling)
  3. RPN网络首先经过3x3卷积,再分别生成foreground anchors与bounding box regression偏移量,通过Loss和梯度下降,给出Region Proposals
  4. 而Roi Pooling层则综合region proposal和feature maps成为proposal feature map送入后续的FC-layer和softmax网络作classification并给出得分(即分类proposal到底属于什么类别)

你可能感兴趣的:(MachineLearning,python,目标识别,DeepLearning)