为了增加自己的理解,本文会从论文的理解和代码的解析两个方面来阐述。
一、概述
在目标检测领域,faster-RCNN表现出了极强的生命力,是现在很多目标算法的基础,尤其是其核心算法,RPN网络更是很多优秀的检测算法的基础。
网络上有很多优秀的论文解读的博客,本文也是以它们作为一个参考,然后再尽力配合一些编程方面的讲解。在编程方面,本文以VGG-16为例。
1、1目标
从编程的角度的来说,以faster-RCNN 为代表的Object Detection任务,可以描述成:给定一张图片,找出图中有哪些对象,以及这些对象的位置和置信度概率。
1.2 整体架构
上图可以清晰的看到对于我们的faster-RCNN来说,整个的模型包含4个部分:
- Conv layers:作为一种CNN网络目标检测的方法,Faster-RCNN首先使用一组基础的conv+relu+Pooling层来提取image的feature map。该feature maps被共享用于后续RPN层和全连接层。
- Region Proposal Networks:RPN网络作为faster-RCNN的核心,用于生成region proposals。该层会通过softmax判断anchors属于positive还是negative,再利用bounding box regesssion修正anchors获得较精确的proposals。
- Roi Pooling。该层负责收集输入的feature maps和proposals,综合这些信息后提取proposal feature maps,送入后续全连接层判定目标类别。
- Classification。利用proposal feature maps计算proposal的类别,同时再次bounding box regression获得检测框最终的精确位置。
- 所有的conv层都是:kernel_size=3,pad=1,stride=1
- 所有的Pooling层都是:kernel_size=2,pad=0,stride=2
为什么说它重要,在Faster RCNN Conv layers中对所有的卷积都做了扩边处理( pad=1,即填充一圈0),导致原图变为 (M+2)x(N+2)大小,再做3x3卷积后输出MxN 。正是这种设置,导致Conv layers中的conv层不改变输入和输出矩阵大小。如图4:
三、RPN网路
RPN网络作为faster-RCNN最为突出的贡献,将候选区域提取的时间开销几乎降为0(2s->0.01s)。
3.1 多通道图像卷积基础知识介绍
在介绍RPN之前,稍微说一下几个小的基础知识,已经懂得大佬们跳过就好~
1、对于单通道图像+单卷积核做卷积,图4已经展示了;
2、对于多通道图像+多卷积核做卷积,计算方式如下
3.2 anchors
提到RPN网络,就不能不说anchors。所谓anchors,实际上就是一组矩形,是大小和尺寸固定的候选框,论文中用到的 anchor 有三种尺寸和三种比例,如下图所示,三种尺寸分别是小(蓝 128)中(红 256)大(绿 512),三个比例(width:height)分别是 1:1,1:2,2:1。3×3 的组合总共有 9 种 anchor。
借用Faster-RCNN论文中的原图来说明这9个anchors是用来做什么的。用这 9 种 anchor 在特征图(feature map)左右上下移动,每一个特征图上的点都有 9 个 anchor,最终生成了 (H/16)× (W/16)×9 个anchor. 对于一个 512×50×38 的 feature map,有 50×38×9~ 17100个 anchor。 也就是对一张图片,有 17100个左右的 anchor。这种做法很像是暴力穷举,17100个 anchor,哪怕是蒙也能够把绝大多数的 ground truth bounding boxes 蒙中。
1. 在原文中使用的是ZF model中,其Conv Layers中最后的conv5层num_output=256,对应生成256张特征图,所以相当于feature map每个点都是256 dimensions。
2. 在conv5之后,做了rpn_conv/3x3卷积且num_output=256,相当于每个点又融合了周围3x3的空间信息(没有想通这个地方是为什么~~欢迎大佬指教),同时256 d不变。
3. 假设在conv5 feature map中每个点上有k个anchor(默认k=9),而每个anhcor要分positive和negative,所以每个点由256d feature转化为cls=2k scores;而每个anchor都有(x, y, w, h)对应4个偏移量,所以reg=4k coordinates。
4. 补充一点,全部anchors拿去训练太多了,训练程序会在合适的anchors中随机选取128个postive anchors+128个negative anchors进行训练。
注意,在本文讲解中使用的VGG conv5 num_output=512,所以是512d,其他类似。
其实RPN最终就是在原图尺度上,设置了密密麻麻的候选Anchor。然后用cnn去判断哪些Anchor是里面有目标的positive anchor,哪些是没目标的negative anchor。所以,仅仅是个二分类而已!
来一张精华图,
##### 3.3 softmax判定positive与negative 一副MxN大小的矩阵送入Faster RCNN网络后,到RPN网络变为(M/16)x(N/16),不妨设 W=M/16,H=N/16。在进入reshape与softmax之前,先做了1x1卷积,如图11: ![alt](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWM0LnpoaW1nLmNvbS84MC92Mi0xYWI0YjZjM2RkNjA3YTUwMzViNTIwM2M3NmIwNzhmM19oZC5qcGc?x-oss-process=image/format,png#pic_center)
该1x1卷积的caffe prototxt定义如下:
layer {
name: "rpn_cls_score"
type: "Convolution"
bottom: "rpn/output"
top: "rpn_cls_score"
convolution_param {
num_output: 18 # 2(positive/negative) * 9(anchors)
kernel_size: 1 pad: 0 stride: 1
}
}
可以看到其num_output=18,也就是经过该卷积的输出图像为WxHx18大小。这也就刚好对应了feature maps每一个点都有9个anchors,同时每个anchors又有可能是positive和negative,所有这些信息都保存WxHx(9*2)大小的矩阵。为何这样做?后面接softmax分类获得positive anchors,也就相当于初步提取了检测目标候选区域box(一般认为目标在positive anchors中)。
那么为何要在softmax前后都接一个reshape layer?其实只是为了便于softmax分类,至于具体原因这就要从caffe的实现形式说起了。在caffe基本数据结构blob中以如下形式保存数据:
blob=[batch_size, channel,height,width]
对应至上面的保存positive/negative anchors的矩阵,其在caffe blob中的存储形式为[1, 2x9, H, W]。而在softmax分类时需要进行positive/negative二分类,所以reshape layer会将其变为[1, 2, 9xH, W]大小,即单独“腾空”出来一个维度以便softmax分类,之后再reshape回复原状。贴一段caffe softmax_loss_layer.cpp的reshape函数的解释,非常精辟:
"Number of labels must match number of predictions; "
"e.g., if softmax axis == 1 and prediction shape is (N, C, H, W), "
"label count (number of labels) must be N*H*W, "
"with integer values in {0, 1, ..., C-1}.";
综上所述,RPN网络中利用anchors和softmax初步提取出positive anchors作为候选区域(另外也有实现用sigmoid代替softmax,原理类似)。
3.4 对proposals进行bounding box regression
说完RPN网络的第一条线,我们再来看一下它的下面的这条线,如图13
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,即经过该卷积输出图像为WxHx36,在caffe blob存储为[1, 4x9, H, W],这里相当于feature maps每个点都有9个anchors,每个anchors又都有4个用于回归
变换量。
也就是说,假设VGG的输出是50x38x512的特征,那么对应的设置50x38xk个anchors,而RPN输出:
1、大小为50x38x2k的positive or negative softmax分类特征矩阵
2、大小为50x38x4k的regression坐标回归特征矩阵
恰好满足RPN完成positive/negative分类+bounding box regression坐标回归.
3.5 RPN生成Rols
Proposal Layer负责综合所有 变换量和positive anchors,计算出精准的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个输入:
1、positive/negative anchors分类器结果rpn_cls_prob_reshape
2、对应的bbox reg的
变换量rpn_bbox_pred,
3、im_info
另外还有参数feat_stride=16。
首先解释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偏移量。
Proposal Layer forward(caffe layer的前传函数)按照以下顺序依次处理:
- 生成anchors,利用 对所有的anchors做bbox regression回归(这里的anchors生成和训练时完全一致)
- 按照输入的positive softmax scores由大到小排序anchors,提取前pre_nms_topN(e.g. 6000)个anchors,即提取修正位置后的positive anchors。
- 限定超出图像边界的positive anchors为图像边界(防止后续roi pooling时proposal超出图像边界)
- 剔除非常小(width<threshold or height<threshold)的positive anchors
- 进行nonmaximum suppression
- Proposal Layer有3个输入:positive和negative anchors分类器结果rpn_cls_prob_reshape,对应的bbox reg的(e.g. 300)结果作为proposal输出。
这里需要留意的地方是,在第三步中,将anchors映射会原图判断是否超出了边界,所有输出的proposal= 是相对于MxN的输入的图像尺度的,这一点会在后续的网络中使用。原则来说,检测的任务就到此为止啦,后续的内容应该是属于分类任务。
RPN 的使命就结束啦,然后总结起来的话就是:
生成anchors -> softmax分类器提取positvie anchors -> bbox reg回归positive anchors -> Proposal Layer生成proposals
三、Rol Pooling
RoI Pooling层则负责收集proposal,并计算出proposal feature maps,送入后续网络。从图3中可以看到Rol pooling层有2个输入:
- 原始的feature maps
- RPN输出的proposal boxes(大小各不相同)
3.1为何需要Rol Pooling
先来看一个问题:对于传统的CNN(如AlexNet和VGG),当网络训练好后输入的图像尺寸必须是固定值,同时网络输出也是固定大小的vector or matrix。如果输入图像大小不定,这个问题就变得比较麻烦。有2种解决办法:
- 从图像中crop一部分传入网络
- 将图像warp成需要的大小后传入网络
3.2 Rol Pooling原理
先来看看RoI Pooling Layer的caffe prototxt的定义:
layer {
name: "roi_pool5"
type: "ROIPooling"
bottom: "conv5_3"
bottom: "rois"
top: "pool5"
roi_pooling_param {
pooled_w: 7
pooled_h: 7
spatial_scale: 0.0625 # 1/16
}
}
其中有新参数pooled_w和pooled_h,还有一个参数spatial_scale。RoI Pooling layer forward过程:
- 由于proposal是对应MXN尺度的,所以首先使用spatial_scale参数将其映射回(M/16)X(N/16)大小的feature map尺度;
- 再将每个proposal对应的feature map区域水平分为 pooled_w x pooled_h的网格;
- 对网格的每一份都进行max pooling处理。
这样处理后,即使大小不同的proposal输出结果都是 pooled_w x pooled_h固定大小,实现了固定长度输出。
四、Classification
Classification部分利用已经获得的proposal feature maps,通过full connect层与softmax计算每个proposal具体属于那个类别(如人,车,电视等),输出cls_prob概率向量;同时再次利用bounding box regression获得每个proposal的位置偏移量bbox_pred,用于回归更加精确的目标检测框。Classification部分网络结构如图16。
从RoI Pooling获取到7x7=49大小的proposal feature maps后,送入后续网络,可以看到做了如下2件事:
1.通过全连接和softmax对proposals进行分类,这实际上已经是识别的范畴了
2.再次对proposals进行bounding box regression,获取更高精度的rect box
这里来看看全连接层InnerProduct layers,简单的示意图如图15,
其计算公式如下:
其中W和bias B都是预先训练好的,即大小是固定的,当然输入X和输出Y也就是固定大小。所以,这也就印证了之前Roi Pooling的必要性。到这里,我想其他内容已经很容易理解,不在赘述了。
到这里整个的关于faster-RCNN的网络结构就已经阐述完啦,接下来讲一下faster-RCNN的训练,在原论文中也花了大量的篇幅来阐述其训练的过程。
五、Faster-RCNN训练
Faster R-CNN的训练,是在已经训练好的model(如VGG_CNN_M_1024,VGG,ZF)的基础上继续进行训练。实际中训练过程分为6个步骤:
- 在已经训练好的model上,训练RPN网络。
- 利用步骤1中训练好的RPN网络,收集proposals
- 第一次训练Fast RCNN网络
- 第二训练RPN网络
- 再次利用步骤4中训练好的RPN网络,收集proposals
- 第二次训练Fast RCNN网络
可以看到训练过程类似于一种“迭代”的过程,不过只循环了2次。至于只循环了2次的原因是应为作者提到:“A similar alternating training can be run for more iterations, but we have observed negligible mprovements”,即循环更多次没有提升了。接下来本章以上述6个步骤讲解训练过程。
5.1RPN网络训练
RPN的总体的架构如下所示:
对于每个 anchor, gt_label 要么为 1(前景),要么为 0(背景),而 gt_loc 则是由 4 个位置参数 (tx,ty,tw,th) 组成,这样比直接回归座标更好。
计算分类损失用的是交叉熵损失,而计算回归损失用的是 Smooth_l1_loss. 在计算回归损失的时候,只计算正样本(前景)的损失,不计算负样本的位置损失。
- cls loss,即rpn_cls_loss层计算的softmax loss,用于分类anchors为positive与negative的网络训练
- reg loss,即rpn_loss_bbox层计算的soomth L1 loss,用于bounding box regression网络训练。注意在该loss中乘了 ,相当于只关心positive anchors的回归(其实在回归中也完全没必要去关心negative)。
由于在实际过程中,
和
差距过大,用参数
平衡二者(如
,
时设置
),使总的网络Loss计算过程中能够均匀考虑2种Loss。这里比较重要是
使用的soomth L1 loss,计算公式如下:
现在我们再来详细的聊一下图18中的各个模块在损失函数中扮演了什么角色。
- 在RPN训练阶段,rpn-data(python AnchorTargetLayer)层会按照和test阶段Proposal层完全一样的方式生成Anchors用于训练
- 对于rpn_loss_cls,输入的rpn_cls_scors_reshape和rpn_labels分别对应 与 , 参数隐含 与 的caffe blob的大小中
- 对于rpn_loss_bbox,输入的rpn_bbox_pred和rpn_bbox_targets分别对应 与 ,rpn_bbox_inside_weigths对应 ,rpn_bbox_outside_weigths未用到(从soomth_L1_Loss layer代码中可以看到),而 同样隐含在caffe blob大小中
5.2 通过训练好的RPN网络收集proposals
在该步骤中,利用之前的RPN网络,获取proposal rois,同时获取positive softmax probability,如图19,然后将获取的信息保存在python pickle文件中。该网络本质上和检测中的RPN网络一样,没有什么区别。
5.3 训练Faster-RCNN网络
读取之前保存的pickle文件,获取proposals与positive probability。从data层输入网络。然后:
1.将提取的proposals作为rois传入网络,如图20蓝框
2.计算bbox_inside_weights+bbox_outside_weights,作用与RPN一样,传入soomth_L1_loss layer,如图20绿框
最后对于整个网络结构补充一种图~盗用来自于小伙伴的图片
Faster-RCNN
一文读懂Faster-RCNN
从编程实现角度学习 Faster R-CNN(附极简实现)
转载:https://blog.csdn.net/WangKingJ/article/details/102541279