Paper:Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks
faster rcnn是继RCNN,fast RCNN之后又一新作,是何凯明等大神在2015年提出目标检测算法,该算法在2015年的ILSVRV和COCO竞赛中获得多项第一,该算法是目标检测领域的经典算法,值得反复研读;
Faster Rcnn相关链接:
目标检测之正负样本详解
困难样本挖掘
目标检测 — Anchor的生成
Faster_Rcnn误检解决方案—强制负样本策略
Faster R-CNN的结构主要分为四大部分,
第一部分是特征提取部分,包括backbone和neck,用于提取特征。
第二部分是候选区域生成网络-RPN
第三部分是对RPN选取的候选框进行映射的RoI Pooling
第四部分是对候选区域进行分类回归的网络头
概述:
RPN输出region proposal的过程
1.假设经过3x3卷积层后得到特征图大小40×60,共2400个点,每个点对应到输入分辨率上设置9个anchor,所以一共可以得到40×60×9大约20000个候选区域,通过1-1的cls layer计算得到所有候选区域预测的scores;
2.根据预测得到的scores对候选区域进行排序,选取score最大的前12000个候选区域;
3.通过非极大值抑制【nms】,设置IOU阈值为0.7,对步骤2筛选的候选区域进一步处理;再在剩余的候选区域中选出score最大的前2000个候选区域
【以上是候选区域选取的大致流程,实际网络结构的不同,如fpn的引入,可能会有一些额外的操作】
【最终输出到rcnn阶段的候选区域数量最多为2000【有可能少】,输出的结果为各候选区域在输入分辨率上的坐标】
以上超参数的设置可以进行调整
rpn_proposal=dict(
nms_across_levels=False,
nms_pre=12000, #nms前根据score选取的候选区域数量
nms_post=2000, #nms之后选取的候选区域数量
max_num=2000, #最终选取的最大候选区域数量
nms_thr=0.7, #nms操作的阈值
min_bbox_size=0),
基于python实现候选区域的生成过程,其代码如下:
def get_bboxes_(self,
cls_scores,
bbox_preds,
mlvl_anchors,
img_shape,
scale_factor,
cfg,
rescale=False):
mlvl_proposals = []
for idx in range(len(cls_scores)):#遍历特征图的各层[主要考虑fpn等多层输出的结构]
rpn_cls_score = cls_scores[idx]#cls layer预测的scores
rpn_bbox_pred = bbox_preds[idx]#reg layer预测的bbox
assert rpn_cls_score.size()[-2:] == rpn_bbox_pred.size()[-2:]
rpn_cls_score = rpn_cls_score.permute(1, 2, 0)
if self.use_sigmoid_cls:
rpn_cls_score = rpn_cls_score.reshape(-1)
scores = rpn_cls_score.sigmoid()
else:
rpn_cls_score = rpn_cls_score.reshape(-1, 2)
scores = rpn_cls_score.softmax(dim=1)[:, 1]
rpn_bbox_pred = rpn_bbox_pred.permute(1, 2, 0).reshape(-1, 4)
anchors = mlvl_anchors[idx]#预先设置的anchor坐标
if cfg.nms_pre > 0 and scores.shape[0] > cfg.nms_pre:
_, topk_inds = scores.topk(cfg.nms_pre) #12000
rpn_bbox_pred = rpn_bbox_pred[topk_inds, :]
anchors = anchors[topk_inds, :]
scores = scores[topk_inds]
proposals = delta2bbox(anchors, rpn_bbox_pred, self.target_means,
self.target_stds, img_shape) #映射到训练分辨率的大小上
if cfg.min_bbox_size > 0:#对小于特定尺寸的框进行过滤{默认设置为0}
w = proposals[:, 2] - proposals[:, 0] + 1
h = proposals[:, 3] - proposals[:, 1] + 1
valid_inds = torch.nonzero((w >= cfg.min_bbox_size) &
(h >= cfg.min_bbox_size)).squeeze()
proposals = proposals[valid_inds, :]
scores = scores[valid_inds]
proposals = torch.cat([proposals, scores.unsqueeze(-1)], dim=-1)
proposals, _ = nms(proposals, cfg.nms_thr) #nms操作
proposals = proposals[:cfg.nms_post, :] # 2000
mlvl_proposals.append(proposals)
proposals = torch.cat(mlvl_proposals, 0)
if cfg.nms_across_levels:#对于fpn结构,是否将多层的框放到一起进行nms操作
proposals, _ = nms(proposals, cfg.nms_thr)
# proposals = proposals[:cfg.max_num, :]
scores = proposals[:, 4]
num = min(cfg.max_num, proposals.shape[0])
_, topk_inds = scores.topk(num)
proposals = proposals[topk_inds, :]
else:
scores = proposals[:, 4]
num = min(cfg.max_num, proposals.shape[0])#cfg.max_num=2000
_, topk_inds = scores.topk(num)
proposals = proposals[topk_inds, :]
return proposals #返回得到的bbox坐标也是相对于输入[训练]分辨率
RPN的训练过程:
1.假设经过3*3卷积层后得到特征图大小40×60,共2400个点,每个点对应到输入分辨率上设置9个anchor,所以一共可以得到40×60×9大约20000个候选区域;
2.对生成的候选区域进行初步过滤:丢掉部分超出图像边缘过多的候选区域
3.对筛选完的候选区域进一步处理:如果候选区域与某个标定区城【ground truth】重叠比例大于0.7,记为正样本,如果与任意一个标定区城重叠比例都小于0.3,记为负样本。其余区城不作为样本。
4.我们会随机地抽取256个区城来训练(正、负样本中各抽128),正负候选区城比例为1:1,如采正样本数小于128,用负样本填充。正样本的数量一般不够,往往需要负样本来进行填充,负样本的数量往往很多远远超过128,此时负样本的选取可以采取一些策略,如在线困难样例挖掘,使得网络更加关注于困难负样本的学习;
【关于正负样本的定义和筛选参考:目标检测之正负样本详解】
5.计算正样本和标定区城【ground truth】的偏差【相对值】,生成正样本的位置标签【负样本只有类别标签没有位置标签】;根据网络的预测值和标签进行模型参数的迭代
以上训练过程的超参数都可以进行调整
rpn=dict(
assigner=dict(
type='MaxIoUAssigner',
pos_iou_thr=0.7, #正样本阈值
neg_iou_thr=0.3, #负样本阈值
min_pos_iou=0.2, #最小正样本阈值
ignore_iof_thr=0.3),
sampler=dict(
type='RandomSampler',
num=256, #用于训练的正负样本总数
pos_fraction=0.5, #用于训练的正样本比例
neg_pos_ub=-1,
add_gt_as_proposals=False),
),
allowed_border=128,#允许超过的最大边界像素
RoI Pooling
作用:
主要完成两件事:
第一件是为每个RoI选取对应的特征(从输入分辨率区域映射到ROI特征层区域)
第二件事是为了满足全连接层的输入需求,将每个RoI对应的特征的维度转化成某个定值(输出固定大小的特征图,如7-7-512)
RoI Pooling的输入输出
-输入:RPN输出的候选区域【RoI】以及对应的RoI Feature Map
RoI Feature Map可以和RPN共享同一个特征图也可以是在RPN基础进一步处理得到的特征图
ROI pooling具体操作
1.根据输入image,将ROI映射到RoI Feature Map对应位置;
2.将映射后的区域划分为相同大小的sections(sections数量与输出的维度相同,如7);
3.对每个sections进行max pooling操作;
这样我们就可以从不同大小的方框得到固定大小的相应 的feature maps。需要注意的是,输出的feature maps的大小不取决于ROI和feature maps大小;Roi Pooling并没有改变通道数量,因此输出的通道数仍然等于输入的通道数
Roi Pooling操作示例:假设有一个88大小的feature map和一个ROI,要求输出大小为22的特征图,其流程如下:.
RoI Align
在Faster-RCNN中,ROI Pooling的作用是根据预选框的位置坐标在特征图中将相应区域池化为固定尺寸的特征图,以便进行后续的分类和候选框回归操作;但是由于候选框的位置通常是由模型回归得到的,一般来讲是浮点数(小数),而像素都为整数且池化后的特征图要求尺寸固定。故ROI Pooling这一操作存在两次量化的过程:
1.将候选框边界量化为特征图上的整数点坐标值。
2.将量化后的边界区域平均分割成kxk个单元(bin),对每一个单元的边界进行量化
举例说明:
如上图所示:卷积网络实现对原图的32倍下采样。假定原图中有一region proposal,大小为665x665,映射到特征图中的大小:665/32=20.78,即20.78x20.78,由于像素为整数,RoiPool在计算的时候会进行取整操作,于是,进行所谓的第一次量化,即映射的特征图大小为20x20;假定pooling后固定成7x7大小的特征图,所以将上面在 feature map上映射的20x20的 region proposal划分成49个同等大小的小区域,每个小区域的大小20/7=2.86(Rol bin)即2.86x2.86,此时,进行第二次量化,故小区域大小变成2x2(Rol bin),原本在特征图上映射的20x20大小的region proposal,偏差成大小为14x14的,对应到原图的偏差(2.86-2)x32=27.52
两次量化引入了Rol和提取特征之间的非对准;这种误差对于分类任务的影响可能不大,但对于定位和像素级的任务影响不可忽略
RoiAlign和Roipooling的目的是一样的,但为了消除量化误差,其实现的方式有所区别:取消量化操作,使用双线性插值法的方法获得坐标为浮点数的像素点上的图像数值:
针对上图,映射得到特征图中的大小:665/32=20.78,即20.78x20.78,此时,RoiAlign没有像RoiPooling那样就行取整操作,而是保留浮点数; pooling后固定成7x7大小的特征图,所以,将在 feature map上映射的20.78x20.78的region proposal 划分成49个同等大小的小区域,每个小区域的大小20.78/7=2.97,即2.97x2.97(Rol bin,不取整),对于每个2.97x2.97的小区域,平分四份,每一份取其中心点位置,而中心点位置的像素,采用双线性插值法进行计算,这样,就会得到四个点的像素值,如下图,最后采用最大值池化或平均池化得到一个7x7的特征图。
RoiAlign将计算得到的每个Rol bin分成四份,如上图四个红色叉叉‘×’所示,该点我们称为原图中虚拟点(又称双线性插值的网格点,比如20.56这个浮点数);其值由其四周的最近的四个真实存在的像素值通过双线性插值的方式计算得到,(比如(20.56,20.56)是根据(20,20)(20,21)(21,20)(21,21)四个点的像素求出),然后对求得的四个值进行max pooling操作,获得每一个Rol bin的输出结果;整个过程中没有用到量化操作,没有引入误差,即原图中的像素和feature map中的像素是完全对齐的,没有偏差。
Faster Rcnn作为一种经典的Two-Stage算法,已经在学术界和工业界得到了广泛的应用,其精度也得到了证明,其优点主要包括以下几点:
1.模型精度较高:存在两次的定位调整,坐标值的预测更加精细化
2.正负样本的比例控制的好;输入rpn会生成很多的正负样本,但真正用于训练的正负样本数量却并不多,且比例控制的很好
3.RPN阶段会生成很多的先验框,模型训练前期变相的增加的很多随机裁剪的目标,相对于加入了一种额外的数据增强方式
缺点:
1.相对one-stage系列的算法而言,速度要慢一些
2.背景误检率高;只有部分负样本用于训练,背景的学习相对来说不是很充分