@huanghaian
2020-11-18T13:54:14.000000Z
字数 4933
阅读 2416
mmdetection
做过或者了解过目标检测的朋友,我想第一个接触算法应该就是Faster RCNN了吧,这是一个非常主流的two-stage目标检测算法,深深的影响了目标检测算法的发展。对于Faster RCNN的解读随处可见,故本文主要是结合mmdet代码进行原理性分析,希望大家看完本文能对faster rcnn有深入的理解。
在阅读本文前你需要先阅读RPN解读章节,faster rcnn叫做two-stage检测器,原因是其包括一个区域提取网络RPN和roi refine网络RCNN,同时为了将RPN提取的不同大小的roi特征图组成batch输入到后面的rcnn中,在两者中间还插入了一个roipool层,可以保证任意大小特征图输入都可以变成指定大小输出。 所有anchor-base的one stage算法都可以认为只有第二级RCNN网络(其实要看成第一级的RPN网络也是可以的),并且RPN提取的roi对应的就是常说的anchor,从而将two stage中的roi refine网络变成了one stage中的anchor refine网络。
faster rcnn简要流程是:首先采用二分类RPN网络提取大量前后景roi特征图,然后通过roipool变成统一大小,最后将roi特征图输入到rcnn中进行roi的refine,得到优化后的bbox。
其整体核心结构如下:
详细一点的话,如下所示:
下面对每个部分进行深入分析。
这部分和RPN网络重合,请看RPN算法解读部分文章。
RPN层的作用是基于预设值anchor进行二分类前后景提取和bbox回归,主要目的是为rcnn层输入高质量的后续bbbox,这部分内容请看RPN算法解读部分文章。
RCNN部分和RPN模块非常类似,只不过处理的问题不一样而已。整个RCNN部分的配置如下所示:
roi_head=dict(
type='StandardRoIHead',
bbox_roi_extractor=dict(
type='SingleRoIExtractor',
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
out_channels=256,
featmap_strides=[4, 8, 16, 32]),
bbox_head=dict(
type='Shared2FCBBoxHead',
in_channels=256,
fc_out_channels=1024,
roi_feat_size=7,
num_classes=80,
bbox_coder=dict(
type='DeltaXYWHBBoxCoder',
target_means=[0., 0., 0., 0.],
target_stds=[0.1, 0.1, 0.2, 0.2]),
reg_class_agnostic=False,
loss_cls=dict(
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
loss_bbox=dict(type='L1Loss', loss_weight=1.0))))
(1) rpn和rcnn中间数据转换流程
rpn_proposal=dict(
nms_across_levels=False,
nms_pre=12000,
nms_post=2000,
max_num=2000,
nms_thr=0.7,
min_bbox_size=0),
rcnn需要的输入数据来自rpn预测roi,故在运行rcnn模块前需要对rpn输出进行处理,配置如上,这个步骤其实就等价于rpn自身的前向推理流程。具体操作是可以看RPN算法解读的前向推理部分,其大概流程是:
此时假设可以得到(batch,2000,4)个候选roi了。
(2) roipool操作
上述可以得到(batch,num,4)个候选roi bbox,在特征图上面进行切割即可得到batchxnum个不同大小的roi特征图,但是这些roi特征图对于rcnn来说肯定不能是不同大小的,否则无法组成batch进行训练,故需要roi提取器,其有两个功能:利用roi坐标在特征图上面切割出对应区域特征;然后将该区域特征变成指定固定大小输出。该模块称为roipool,该操作非常简单,通过下面的可视化图即可理解:
假设左上是输入特征图,右上的蓝色框是roi,已经取整了。假设输出shape为2x2的统一大小,则左下是切割的示意图,实际上就是把5x7个块切割为2x2的块,第一个块是wh=(7//2,5//2),第二个块是wh=(7-7//2,5-5//2),后面类似。最后在每个小块内部进行max操作即可。可以明显roi发现其存在两次非常严重的取整操作,第一次是将roi proposal值变成整数;第二次是切割时候。可想而知对于本身就是小物体的特征图而言,两次取整操作特征图会偏差很大,故在后续mask rcnn中提出了roiAlign操作。原论文中设置的指定输出大小是7x7。
(3) RCNN网络结构
将roipool操作得到的(batch,num,256,7,7)个roi特征图作为RCNN模块的输入即可。RCNN的head部分是Shared2FCBBoxHead,假设RPN阶段输出了1000个候选bbox,并且经过了roipool层统一变成了(batchx1000,256,7,7),那么Shared2FCBBoxHead的意思是先把(batchx1000,256,7,7)变成(batchx1000,256x7x7),再经过2次fc层,然后在分成两个fc分支用于分类和roi refine回归,前两个fc层是共享权重的。分类分支shape=(batchx1000,cls_num+1),回归分支shape=(batchx1000,4*num_class)(因为参数reg_class_agnostic=False)
这里有一个细节需要强调下:在原始faster rcnn中RCNN网络还包括了resnet的最后一个stage,也就是说resnet的stage0-stage2做为公共的基础网络部分,而stage3是在RCNN部分。为何作者将stage3作为fast RCNN独有网络,不作为基础网络?作者认为把stage3作为基础网络的话,网络会偏向于分类问题,并且imageNet预训练权重的加载更会加深特征的平移不变性,不利于bbox回归。但是由于考虑到代码通用性以及后续FPN模块的引入,现在的faster rcnn都没有这样做。
(4) 正负样本定义和采样
首先解释下rcnn层的输入参数:
def forward_train(self,
x,
img_metas,
proposal_list,
gt_bboxes,
gt_labels,
gt_bboxes_ignore=None,
gt_masks=None):
x是backbone输出的stride=16的特征图,img_metas是每张图片各自的属性,proposal_list就是RPN输出的roi,假设是(batch,1000,4),后面几个都是关于gt bbox相关的数据。
RCNN算法要完成的任务是:基于RPN输出的proposal_list来回归出更好的bbox。此时可以发现RCNN算法其实和RPN思想非常类似,只不过RPN中学习目标是anchor和gt bbox的变换值,而RCNN模块可以认为proposal_list是稀疏的anchor,其学习目标是proposal_list和gt bbox的变换值。
既然proposal_list可以认为是anchor,那么正负样本定义和采样策略就不可少了
rcnn=dict(
assigner=dict(
type='MaxIoUAssigner',
pos_iou_thr=0.5,
neg_iou_thr=0.5,
min_pos_iou=0.5,
match_low_quality=False,
ignore_iof_thr=-1),
sampler=dict(
type='RandomSampler',
num=512,
pos_fraction=0.25,
neg_pos_ub=-1,
add_gt_as_proposals=True),
pos_weight=-1,
debug=False))
其代码和RPN中的完全一样,只不过阈值不一样而已。其主要区别是:
通过正负样本定义和随机采样策略就可以对每个proposal确定其正负样本属性。
(5) loss计算
rcnn部分的bbox编解码流程和RPN完全相同,就不再描述了。假设得到(batch,1000,256x7x7)的矩阵,后面经过rcnn的fc head即可进行分类和回归了,其中分类采用的是ce loss,回归采用的是L1Loss。
test_cfg = dict(
rpn=dict(
nms_across_levels=False,
nms_pre=6000,
nms_post=1000,
max_num=1000,
nms_thr=0.7,
min_bbox_size=0),
rcnn=dict(
score_thr=0.05,
nms=dict(type='nms', iou_threshold=0.5),
max_per_img=100))
推理流程为:
(1) 对于单张图片输入到resnet中,输出1个特征图stride=16
(2) 对输出图经过rpn head,分别预测前后景和bbox坐标
(3) 采用rpn测试配置对预测结果进行解码,并且经过nms得到proposal_list
(4) 对每个proposal进行roipool操作,变成统一大小
(5) 组成batch输入到rcnn head进行类别分类和proposal refine回归
(6) 对预测结果再次进行nms操作得到优化后的最终bbox
faster rcnn是经典的two-stage目标检测算法,其对rpn层提取的大量roi进行refine操作,可以得到精确的bbox。由于其优雅高效的实现,理解上也通俗易懂,故依然是目前的主流算法。对于初学者而言一开始接触就faster rcnn会被其复杂代码绕晕的,最好先把RPN思想彻底理解后再来看本算法,会轻松很多。当然在理解了faster rcnn后,对于后续的one-stage算法就更加容易了。