Fast R-CNN
Fast R-CNN 是 Ross Girshick 于 2014 年提出的算法。 针对 R-CNN 速度慢的问题,提出了 Roi Pooling 算法,使卷积运算可以在不同的提议框之间进行共享,大幅提高了推理时算法的速度。 Fast R-CNN 还借助多任务学习(multi-task learning)的模式实现了模型的端到端训练,大幅提升了模型训练的速度。 受益于这两个创新点,Fast R-CNN 在训练速度和测试速度上提高了 10-100 倍。
R-CNN 的速度问题
从检测精度的提升来看,RCNN 是非常成功的。但 RCNN 在速度方面有着明显的劣势。 即便使用 GPU 加速,推理一张图片也需要几十秒的时间。
RCNN 中存在大量重复的计算。 在推理过程中,selective search 可以产生 2000 个左右的提议框。 这些提议框无论大小,都需要统一拉伸到 227x227 再送入卷积网络。 这些 proposal 之间必然有大量重叠,但由于图像拉伸操作的存在,卷积运算不能在不同提议框之间共享。 即便在一张图像上进行推理,我们也需要上千次完整的 AlexNet 卷积网络的前传计算。
RCNN 的训练也非常复杂。在 RCNN 算法中,训练一个检测模型需要四个步骤:
在 ImageNet 数据集上预训练一个图像分类网络
使用裁剪下来的图像区域,对 ImageNet 预训练的网络进行微调训练
用训练好的网络提取所有图像区域的特征
用提取好的特征训练 SVM 分类器,和边界框回归模型
上述步骤前后依赖,耗时较长,而且需要大量的存储空间存储中间结果。
卷积共享
Fast RCNN 改进的出发点就是减少重叠区域的卷积计算。在 RCNN 算法中,为了提取不同区域的图像特征, 我们需要将 selective search 产生的提议框全部剪裁下来,再拉伸到同样的大小。由于拉伸尺度不同,重叠的区域在拉伸后就变成不同的图像了。 即便有所重叠,重叠部分也需要分别进行卷积计算。
为了减少重叠区域重复的卷积计算,Fast RCNN 采用了另一种方法,直接用卷积核扫过整个图像,产生整张图像上特征图,再从特征图上裁剪下不同的提议框,作为这些提议框的特征。考虑大小为的图像及图像上位置和大小分别为 的提议框。一个空间采样率为 的卷积网络可以将图像变为 大小的特征图。我们可以按比例提议框映射到特征图上的框 ,再将这个框内的特征图裁剪下来作为提议框的特征。
需要注意的是,先拉伸再计算特征,先计算特征再裁剪,两种方法得到的结果是不同的。但两种方法得到的都是语义级别的特征,可以作为后续分类和边界框回归模型的输入。此外,这种映射关系只是人为定义一种几何关系,与神经网络的感受野在并没有本质上的联系,但针对大多数主流的主干网络(如 ResNet、VGG 等),提议框的感受野的中心是 。
在 RCNN 中,如果有 N 个提议框,我们需要进行 N 次完整的前传计算,以得到每个提议框的特征。 在 Fast RCNN 中,无论有多少提议框,我们都只需要对原图进行一次前传计算,即可得到全部提议框的特征。
RoI Pooling
接下来我们需要考虑另一个问题。不同的提议框大小不同,因而裁剪下来的特征图尺度大小也互不相同。但后续的全连接层要求输入特征具有固定的维度。因此我们就需要将不同大小的提议框特征图处理到一个固定的大小。为了达到这个目的,Faster RCNN 提出了 RoI Pooling 层,通过池化操作将提议框特征图映射到固定的大小。
RoI Pooling 在操作上与普通的池化有所区别。普通的池化层设置固定大小的池子,例如 2x2 大小,再将每个池子内的神经元响应通过平均或者最大化聚合成一个值,输出特征图的大小随输入变化。RoI Pooling 则是设置一个固定的输出尺寸,再根据这个尺寸和确定池子的大小,使得池化的输出恰好为期待的大小。
在下面的示例图中,整幅图像的特征图大小为 8x8,黑框表示提议框在特征图上的映射,目标大小为 2x2。RoI Pooling 将黑框划分成 2x2 个区域,再在每个区域内取最大值,使得池化后的大小为 2x2 。
严格来讲,考虑在特征图上左上右下坐标分别为 的框,如果我们希望将特征图池化到 的大小,那么池化结果的第 行,第 列(, )的单元来自特征图上
范围内的所有响应值。
RoI Pooling 层输出的大小通常设置为所搭配的主干网络在 224x224 图像上输出的特征图的大小,例如对于 VGG 或 ResNet,RoI Pooling 输出大小设置为 7x7,对于 AlexNet 或 ZFNet,输出大小设置为 6x6。这种设置可以更好地利用与训练模型。如果我们把一个 VGG 分类网络最后的池化层直接换成 ROI Pooling,那么这个网络就可以在任意大小的图像及任意大小的框上,产生固定 7x7 大小的特征图,而 7x7 的特征图又恰好可以适配预训练模型中的全连接层输出分类结果。
RoI Pooling 与普通池化层一样,是可以传递梯度的,这就使得端到端训练整个检测模型成为可能。
端到端与多任务学习
Fast RCNN 将 RCNN 中的神经网络、SVM 分类器和边界框回归模型集合到了一起,形成了一个单一的神经网络模型。该模型可以根据单张图像和其上的一系列提议框,直接计算出每个提议框中物体的类别及边界框的修正值。由于输入的图像和输出的检测结果之间只有一个神经网络模型,因而我们说 Fast RCNN 是一个端到端(end-to-end)的模型。又由于一个模型可以同时学习分类和边界框回归两个任务,因而我们说它是一个多任务学习(multi-task learning)的模型。
Fast RCNN 模型的整体结构如下图所示:
在推理阶段,Fast RCNN 以单张图片和 selective search (或其他外部算法)产生的一系列提议框为输入,经由如下步骤预测图中出现的物体:
将图片输入主干网络,产生特征图(即上图中的 conv feature map)。这个过程只需要进行一次。
将全部提议框,按照主干网络的缩放比例映射到特征图上。
对于每一个映射框,通过 RoI Pooling 产生固定尺度(例如 7x7)的提议框特征图。
将每个提议框特征图输入两个共享的全连接层,得到一个特征向量,即上图中的 roi feature vector。
将每个特征向量输入分类和回归模型,得到物体的类别,以及边界框的修正值。
删除分类为背景的提议框,利用其它后处理算法如 NMS 除去重叠的检测框,得到最终检测结果。
由于 Fast RCNN 中所有计算都是可微分的,因而我们可以抛弃 RCNN 中分步骤的训练方法,直接对其进行端到端的训练。训练的整体流程与图像分类网络类似,输入图像、前传计算分类和边界框偏移量,与真值比较计算损失函数并回传,更新网络参数,重复迭代至收敛。但由于检测模型在一张图上有若干个提议框,所以前传的过程比图像分类复杂一些。Fast RCNN 的训练的大体流程如下:
输入一个 batch 的图像以及对应的提议框,使用主干网络前传计算得到一批特征图。
将每张图像上的提议框与真值框进行匹配,将所有的提议框分为正样本、负样本、丢弃样本三类。大体而言,正样本即为与某些真值框重合度较大的提议框,负样本为与所有真值框重合都比较小的提议框,一些不适合用于训练的提议框则丢弃。在 Fast RCNN 的原始论文中,与某个真值框 IoU 大于 0.5 的提议框会被归为正样本,与所有真值框的 IoU 的最大值介于 0.1-0.5 的归为负样本,其余丢弃。需要注意的是,匹配算法并不是单一的,可以根据需要设计或调整。注意,这里的正样本和负样本都是提议框。
对于正样本提议框,用与之 IoU 最大的真值框的类别、以及对应的边界框偏移量,作为这个样本预测的真值。对于负样本,类别标记为背景,不需要边界框偏移量。不难发现,提议框的真值是训练过程中动态生成的。
按照一定的比例(如 1:3)随机选取一部分正样本和负样本,通过 RoI Pooling 得到这些提议框的特征图,并组成 batch,继续前传计算,得到对应的物体类别和边界框偏移量的预测值,与真值比较,计算损失函数,并回传。
对于正样本提议框,我们希望神经网络可以预测出其中物体的类别,以及边界框偏移量。对于负样本,我们只希望神经网络可以将其预测为背景,边界框偏移量对于背景是没有意义的。因而,损失函数可以表达为如下形式:
其中, 为分类损失函数,通常使用交叉熵。 为边界框回归的损失函数,通常使用 smooth L1 损失函数,且仅对于正样本计算这部分损失函数。
通过这种方式,我们实现了端到端的训练,也节省了中间结果需要的存储空间。
模型在 mmdetection 中的实现
mmdetection 在 configs/fast_rcnn/fast_rcnn_r50_fpn_1x_coco.py
中实现了 fast-rcnn 模型训练的配置文件。下面我们对这个配置文件进行简要分析,需要注意的是,mmdetection 中实现的 Fast RCNN 实现了许多 Fast RCNN 之后的技术,如 ResNet、FPN、RoI ALign 等等,许多内容与当年提出的算法有所区别。
数据
首先,Fast RCNN 对数据的加载流程进行了改写。我们可以看到,在定义 data
字段时,我们需要把外部算法已经生成好的提议框从预先生成的 pickle 文件中读取进来。在数据流水线中,需要增加一个 LoadProposals
操作,将图像对应的至多 2000 个提议框增加到数据的字典中。
模型
Fast RCNN 的模型定义继承自 configs/_base_/models/fast_rcnn_r50_fpn.py
。
这个模型使用 ResNet 作为主干网络,并使用 FPN 输出了多尺度的特征图。我们会在后面的小节中讲解 FPN,这里可以先忽略之。不使用 FPN 的情况下,我们可以使用 ResNet 的第三个 block 的输出作为特征图,即指定 out_indices=(2, )
主干网络输出的特征图会输入 StandardRoIHead
模块,该模块会使用 SingleRoIExtractor
子模块,根据数据中提供的提议框和主干网络输出的特征图,通过 RoI Align 算法产生 proposal 框的特征图,这里 RoI Align 算法是 RoI Pooling 算法的升级版。我们可以就把它当作 RoI Pooling 来理解。这里 roi_feat_size=7
表明 RoI Pooling 的目标大小为 7x7 。
接下来的 Shared2FCBBoxHead
以 RoI Pooling 输出的 7x7 大小的特征图为输入,经过两个全连接层,产生一个特征向量,再分别通过各自的全连接层产生类别和边界框的预测。在训练过程中,分别使用CrossEntropyLoss
和 L1Loss
作为分类和边界框回归的损失函数,二者的权重通过loss_weight
指定。
RoI 采样
基础配置 configs/_base_/models/fast_rcnn_r50_fpn.py
中的 train_cfg
字段定义了模型在训练过程中的 RoI 采样规则。这份配置使用 MaxIoUAssigner
对提议框和真值框进行匹配,具体匹配规则为:
与所有真值框 IoU 都小于
neg_iou_thr=0.5
的提议框为负样本与某个真值框 IoU 不低于
pos_iou_thr=0.5
的提议框为正样本
匹配完成后,使用 RandomSampler
从这些正负样本中采样,总共采样 num=512
个提议框,其中 pos_fraction=0.25
比例为正样本。同时,add_gt_as_proposals=True
把真值框也作为提议框加入导正样本中,提高正样本的基数。
Last updated