Faster R-CNN
Last updated
Last updated
Faster R-CNN 是一个非常主流的两阶段目标检测算法,它的出现深深的影响了目标检测的发展以及工业应用。 Faster R-CNN 叫做两阶段检测器,原因是其包括一个区域提取网络 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 R-CNN 简要流程是:首先采用二分类 RPN 网络提取大量前后景 roi 特征图,然后通过 roipool 变成统一大小,最后将 roi 特征图输入到 rcnn 中进行 roi 的 refine,得到优化后的 bbox。
其整体核心结构如下:
详细一点的话,如下所示:
下面对每个部分进行深入分析。
这部分和 RPN 网络重合,请看 RPN 算法解读部分文章。
RPN 层的作用是基于预设值 anchor 进行二分类前后景提取和 bbox 回归,主要目的是为 rcnn 层输入高质量的后续 bbbox,这部分内容请看 RPN 算法解读部分文章。
RCNN 部分和 RPN 模块非常类似,只不过处理的问题不一样而已。整个 RCNN 部分的配置如下所示:
(1) rpn 和 rcnn 中间数据转换流程
rcnn 需要的输入数据来自 rpn 预测 roi,故在运行 rcnn 模块前需要对 rpn 输出进行处理,配置如上,这个步骤其实就等价于 rpn 自身的前向推理流程。具体操作是可以看 RPN 算法解读的前向推理部分,其大概流程是:
遍历每个 RPN 输出层(其实就是 1 个),对每一层的分类分值 scores 进行降序排列,保留 topk=nms_pre 个样本,分值越大越可能是正样本
此时就可以收集到所有层的所有样本,先过滤掉不满足 min_bbox_size 预测结果,然后统一进行 nms 即可,最好保留最多前 nms_post 个检测结果
此时假设可以得到(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 层的输入参数:
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,那么正负样本定义和采样策略就不可少了
其代码和 RPN 中的完全一样,只不过阈值不一样而已。其主要区别是:
由于 rcnn head 预测值是 rpn head 输出 roi 的 refine,故 rcnn head 面对的 anchor 和 gt bbox 的 iou 会高于 rpn head 部分,anchor 质量更高,neg_iou_thr 阈值设置的就可以高一些
由于 pos_iou_thr 和 neg_iou_thr 设置都是 0.5,那么忽略区域就没有了,因为 rcnn head 面对的都是高质量样本,可以不需要忽略区域
由于 rcnn 在网络训练前期,rpn 无法输出大量高质量样本,故为了平衡和稳定 rcnn 训练过程,通常会对 rcnn head 部分添加 gt bbox 作为 proposal
rcnn 部分 match_low_quality=False,原因是 rcnn 面对的正样本都是比较高质量的,没必要 match_low_quality 设置为 True,因为其可能会导致低质量 anchor 匹配
通过正负样本定义和随机采样策略就可以对每个 proposal 确定其正负样本属性。
(5) loss 计算
rcnn 部分的 bbox 编解码流程和 RPN 完全相同,就不再描述了。假设得到(batch,1000,256x7x7)的矩阵,后面经过 rcnn 的 fc head 即可进行分类和回归了,其中分类采用的是 ce loss,回归采用的是 L1Loss。
推理流程为:
对于单张图片输入到 resnet 中,输出 1 个特征图 stride=16
对输出图经过 rpn head,分别预测前后景和 bbox 坐标
采用 rpn 测试配置对预测结果进行解码,并且经过 nms 得到 proposal_list
对每个 proposal 进行 roipool 操作,变成统一大小
组成 batch 输入到 rcnn head 进行类别分类和 proposal refine 回归
对预测结果再次进行 nms 操作得到优化后的最终 bbox
faster rcnn 是经典的 two-stage 目标检测算法,其对 rpn 层提取的大量 roi 进行 refine 操作,可以得到精确的 bbox。由于其优雅高效的实现,理解上也通俗易懂,故依然是目前的主流算法。对于初学者而言一开始接触就 faster rcnn 会被其复杂代码绕晕的,最好先把 RPN 思想彻底理解后再来看本算法,会轻松很多。当然在理解了 faster rcnn 后,对于后续的 one-stage 算法就更加容易了。