论文信息:
- 论文标题:Fast R-CNN
- 作者:Ross Girshick
- 会议:ICCV 2015
- arXiv 摘要页:https://arxiv.org/abs/1504.08083
- ICCV 官方页面:https://www.cv-foundation.org/openaccess/content_iccv_2015/html/Girshick_Fast_R-CNN_ICCV_2015_paper.html
- 官方 PDF:https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Girshick_Fast_R-CNN_ICCV_2015_paper.pdf
上一篇我们已经讲过R-CNN。
R-CNN 很重要,因为它第一次真正把“候选区域 + CNN 特征”这条路线跑通了;但它也留下了一个特别明显的问题:
每张图大约有 2000 个 proposal,而每个 proposal 都要单独跑一次 CNN,这实在太慢了。
所以到了Fast R-CNN,作者要解决的核心问题就非常明确:
能不能只对整张图做一次卷积,然后让所有 proposal 共享这次卷积计算?
这篇文章,就专门讲清楚 Fast R-CNN 到底快在哪里,以及它为什么会成为 Faster R-CNN 之前最关键的一步。
这里先纠正一个特别常见的误区:
Fast R-CNN本身不负责生成 proposal。
它做的事情是:先接收外部 proposal,再在共享特征图上用RoI Pooling读取这些 proposal 的特征。
也就是说,Fast R-CNN 的“快”,来自共享卷积特征,而不是来自“自己学会了 proposal 生成”。
一、先用一句话概括 Fast R-CNN
如果只用一句话概括 Fast R-CNN,我会这样说:
Fast R-CNN 的核心思想,是先对整张图做一次卷积得到共享特征图,再通过
RoI Pooling从这张特征图里为每个 proposal 取出固定长度特征,最后同时完成分类和边界框回归。
和 R-CNN 相比,它最大的变化不是 proposal 数量变了,而是:
- CNN 不再对每个 proposal 单独跑一遍
- proposal 之间开始共享卷积特征
- 训练流程也从多阶段变成了单阶段多任务训练
但有一点不要混淆:
Fast R-CNN使用的 proposal 仍然来自Selective Search等外部算法- 它是在特征图上读取 proposal 特征
- 它不是在特征图上生成 proposal
这三点加在一起,Fast R-CNN 才真正“快”了起来。
二、R-CNN 的问题到底出在哪
理解 Fast R-CNN,最好先回头看一眼 R-CNN 的瓶颈。
R-CNN 的流程是这样的:
输入图像 -> Selective Search 生成约 2000 个 proposal -> 每个 proposal 单独 warp -> 每个 proposal 单独送进 CNN -> 提取特征 -> SVM 分类 -> Bounding Box Regression这里真正拖慢系统的,不是 proposal 本身,而是:
同一张图里高度重叠的 2000 个候选框,被反复、重复地做了 2000 次卷积。
这会带来三个问题:
1. 测试速度太慢
R-CNN 需要对每个 proposal 单独做前向传播,所以计算大量重复。
2. 训练流程很繁琐
R-CNN 需要分多步训练:
- 先 fine-tune CNN
- 再提特征
- 再训练 SVM
- 再训练边界框回归器
3. 存储开销很大
为了后续训练 SVM 和回归器,R-CNN 往往要把 proposal 特征提前提取并写入磁盘。
Fast R-CNN 的出发点,就是把这三件事一起改掉。
三、Fast R-CNN 到底改了什么
Fast R-CNN 的整体流程可以压缩成下面这条主线:
输入图像 -> 整图卷积 -> 得到共享 conv feature map -> 对每个 proposal 做 RoI Pooling -> 全连接层 -> 两个输出分支 -> softmax 分类 -> 边界框回归这个流程和 R-CNN 的最大不同,在于卷积计算的位置变了。
在 R-CNN 里:
- 先裁 proposal
- 再对每个 proposal 跑 CNN
在 Fast R-CNN 里:
- 先对整张图跑 CNN
- 再从共享特征图里读取每个 proposal 的区域特征
这一步看起来像只是“顺序调了一下”,但其实几乎改变了整个计算结构。
四、为什么“整张图先卷积一次”这么关键
这一点是 Fast R-CNN 的灵魂。
一张图里的很多 proposal 彼此高度重叠。
比如一只狗的不同候选框,往往只是边界稍微大一点、小一点,或者左边多一点、右边少一点。
如果像 R-CNN 那样每个 proposal 都单独卷积,就会出现:
- 同一块狗脸反复被算很多次
- 同一块背景反复被算很多次
这显然很浪费。
Fast R-CNN 的思路是:
既然这些 proposal 都来自同一张图,那我先把整张图的卷积特征一次性算出来,后面的 proposal 直接共享这张特征图就行。
于是卷积层从“按 proposal 重复计算”,变成了“整图只算一次”。
这也是 Fast R-CNN 速度提升最根本的来源。
五、RoI Pooling 到底是什么
但这里马上会遇到一个新问题。
虽然整张图的卷积特征只算一次了,可 proposal 的大小还是各不相同:
- 有的大
- 有的小
- 有的长
- 有的扁
而后面的全连接层需要固定长度输入。
所以作者引入了一个非常关键的模块:
RoI Pooling你可以把它理解成一句很直白的话:
不管 proposal 在原图里多大,我都先把它映射到共享特征图上,再切成固定数量的小格子,然后每个格子做一次 max pooling,最后得到固定大小的输出。
结合这张图看就很好懂:
- 上面的红色和绿色 proposal,形状并不一样
- 它们都来自同一张共享 feature map
- 经过
RoI Pooling之后,都会被变成固定长度的7 x 7表示
论文里举的例子是把每个 RoI 变成一个固定大小的特征图,比如:
7 x 7这样一来,不同大小的 proposal 就都能被统一变成同样尺寸的表示,后面就可以接相同的全连接层。
所以RoI Pooling的作用可以概括成一句话:
它负责把“大小不一的候选区域”变成“固定大小的特征表示”。
这里还要特别强调一点:这和 R-CNN 里的 warp 不是一回事。
在R-CNN里,proposal 是先从原图里裁出来,再强行warp成固定大小,然后单独送进 CNN。
而在Fast R-CNN里,整张图已经先卷积完了,RoI Pooling做的是:
- 先在共享特征图上找到 proposal 对应的位置
- 再把这块区域池化成固定大小
所以 Fast R-CNN 真正省下来的,就是对每个 proposal 单独做卷积这一步。
六、Fast R-CNN 是怎么分类和回归的
在RoI Pooling之后,每个 proposal 都会得到一个固定大小的特征图。
接着这些特征进入全连接层,再分成两条输出分支:
1. 分类分支
输出K + 1类的 softmax 概率:
K个真实目标类别1个背景类
这和 R-CNN 用 SVM 的思路不一样。
Fast R-CNN 直接在网络里用 softmax 输出类别概率,不再单独训练一套外部 SVM。
2. 回归分支
为每个类别输出 4 个边界框回归参数,也就是:
4K这一步负责进一步微调 proposal 的位置。
所以对每个 RoI 来说,Fast R-CNN 会同时回答两个问题:
1. 它属于哪个类别
2. 它的框应该怎么再调准一点
这也正是后面多任务损失要同时优化的两件事。
七、多任务损失为什么重要
R-CNN 的训练流程很碎:
- CNN 用一套损失
- SVM 单独训练
- 边界框回归再单独训练
Fast R-CNN 则把分类和回归放到了同一个网络里,用一个多任务损失联合训练。
论文中的损失可以写成:
L = Lcls + λ [u >= 1] Lloc它的意思并不复杂:
Lcls:分类损失,判断这个 RoI 属于哪个类别Lloc:定位损失,学习怎么修正边界框- 只有前景 RoI 才参与定位损失
也就是说,背景区域只需要学“我是背景”,不需要学“框怎么调”。
这一设计的重要性在于:
Fast R-CNN 不再像 R-CNN 那样把分类和定位拆成多个独立训练阶段,而是一次 fine-tuning 里联合优化两件事。
这让训练流程变得更简单,也更统一。
八、Fast R-CNN 的训练为什么比 R-CNN 优雅得多
论文里明确强调,Fast R-CNN 的优势不只是测试快,训练也快得多。
1. 不再需要特征缓存到磁盘
R-CNN 为了训练 SVM 和回归器,往往要提前提取特征并存盘。
这会消耗大量时间和存储空间。
Fast R-CNN 直接在网络中完成训练,不需要这一步。
2. 单阶段 fine-tuning
Fast R-CNN 用一次 fine-tuning 就把分类和边界框回归都学了,不再需要:
- softmax 训完再训 SVM
- SVM 训完再训 bbox regressor
3. 可以更新所有网络层
相比 SPPnet,Fast R-CNN 的一个关键改进是:
它可以通过 RoI Pooling 层反向传播,因此能够更新更前面的卷积层。
这点对深层网络尤其重要。
论文里专门做了消融实验,发现对于VGG16这类深网络,仅仅 fine-tune 全连接层是不够的,卷积层也需要跟着学。
九、Fast R-CNN 到底快了多少
这一点是整篇论文最有冲击力的地方之一。
论文摘要里就直接给出结论:
- 相比
R-CNN,训练VGG16时快9x - 测试时快
213x - 在 PASCAL VOC 2012 上精度还更高
论文表 4 给出的更具体结果也很经典。
对于VGG16:
R-CNN测试约47.0 s / imageFast R-CNN测试约0.32 s / image- 使用截断 SVD 后可降到
0.22 s / image
训练时间方面:
R-CNN:约84 hFast R-CNN:约9.5 h
这意味着:
Fast R-CNN 不只是“快一点”,而是从结构上把 R-CNN 那种重复卷积的低效方式彻底改掉了。
十、Fast R-CNN 的精度提升体现在哪
Fast R-CNN 不只是速度上大幅提升,精度也没有牺牲,反而进一步增强了。
1. VOC 2007
在VOC 2007 test上,使用VGG16:
R-CNN BB:66.0% mAPFast R-CNN:66.9% mAP- 使用
07 + 12 trainval训练时可达到70.0% mAP
2. VOC 2010
在VOC 2010 test上,使用07++12训练集时:
68.8% mAP3. VOC 2012
在VOC 2012 test上,使用VGG16:
R-CNN BB:62.4% mAPFast R-CNN:65.7% mAP- 若使用
07++12训练,可达68.4% mAP
所以 Fast R-CNN 很重要的一点是:
它不仅加速了检测,还在更统一的训练方式下把精度也继续往上推了一步。
十一、Fast R-CNN 相比 SPPnet 又强在哪
很多人学到这里会问:
既然已经有SPPnet可以共享卷积特征,Fast R-CNN 为什么还要再来一次?
答案在于:SPPnet虽然已经避免了 R-CNN 那种 proposal 级重复卷积,但它还有训练上的限制。
论文指出,SPPnet 的 fine-tuning 不能有效更新空间金字塔池化之前的卷积层。
这会带来什么问题?
对于像 VGG16 这样更深的网络,前面的卷积层如果不能一起更新,精度会受到限制。
Fast R-CNN 通过RoI Pooling + end-to-end backprop,把这一点补上了。
所以它相对 SPPnet 的优势可以概括为:
- 训练更简单
- 训练更快
- 测试更快
- 可以更新全部层
- 在深网络上更准确
十二、Fast R-CNN 还有什么没解决
Fast R-CNN 已经把 R-CNN 最大的瓶颈之一解决掉了,但它还没有走到终点。
它仍然有一个明显限制:
proposal 还是外部给的。
也就是说,Fast R-CNN 虽然已经做到:
- 整图共享卷积
- RoI Pooling
- 单网络分类和回归
但它还是需要先依赖Selective Search这样的 proposal 算法,才能拿到候选区域。
这意味着整套系统虽然已经比 R-CNN 快很多,但 proposal 本身仍然是外部模块,仍然会成为新的速度瓶颈。
这也正是下一篇Faster R-CNN要出场的原因。
十三、小结:用一句话记住 Fast R-CNN
如果让我用一句话总结 Fast R-CNN,我会这样说:
Fast R-CNN 的关键突破,是把“每个 proposal 单独卷积一次”改成了“整张图卷积一次、所有 proposal 共享特征图”,再借助 RoI Pooling 和多任务损失,把目标检测训练成了一个更快、更统一、也更准确的系统。
如果把三篇论文串起来看,它们的技术演进其实非常清楚:
R-CNN:证明 CNN 特征对目标检测很有效Fast R-CNN:解决 proposal 级重复卷积导致的低效问题Faster R-CNN:进一步把 proposal 本身也并入网络
所以读完 Fast R-CNN 之后,下一步就非常自然了:
既然检测网络已经能共享整图卷积特征,那 proposal 能不能也共享这些特征?
这正是 Faster R-CNN 的核心问题。
参考文献
- Girshick, R. (2015).Fast R-CNN. ICCV 2015.
- Girshick, R., Donahue, J., Darrell, T., & Malik, J. (2014).Rich Feature Hierarchies for Accurate Object Detection and Semantic Segmentation. CVPR 2014.