首页 / 资讯中心 / 文章详情

R-CNN深度拆解:从候选区域到边界框回归的经典目标检测流程

R-CNN深度拆解:从候选区域到边界框回归的经典目标检测流程 ★ FEATURED ARTICLE
最近又把R-CNN的原始实现翻出来跑了一遍。这篇《Rich feature hierarchies for accurate object detection and semantic segmentation》放到今天看依然是理解深度学习目标检测最合适的第一块基石。它在2014年把PASCAL VOC上的检测mAP从传统方法的30%出头直接拉到58.5%以上用的还是一个非常直白的三段式结构先找候选区域再逐区域提取CNN特征最后分类加回归。这篇笔记不想逐段翻译论文重点想聊几个容易被一句话带过的细节为什么要用SVM而不直接用softmax、IoU阈值为什么有0.5和0.3两个版本、边界框回归器到底在回归什么。如果你正准备复现经典检测模型或者想彻底搞懂Fast R-CNN、Faster R-CNN到底改进了哪些痛点这篇笔记应该能帮你省不少时间。1. 为什么还要拆解R-CNN检测问题里的第一个“正确分解”1.1 目标检测不是“分类加定位”的简单加法很多初接触检测的人会本能地觉得检测不就是把图像切成一堆小块然后逐个分类吗这个想法本身没错但代价巨大。传统方法就是这么干的——滑窗遍历整张图每个窗口用一个分类器打分。问题在于尺度一个物体在图像里可能占50像素也可能占500像素你没法用单个固定尺寸的窗口覆盖所有情况只能做金字塔式的多尺度扫描。再加上窗口滑动步长必须足够密搜索空间膨胀得非常快分类器的计算量再低也顶不住。R-CNN之前的经典检测方案基本上被HOG特征和可变形部件模型统治它们能处理的物体类别有限对遮挡、姿态变化、复杂背景的鲁棒性也一般。大家都意识到特征表达力是天花板但没人想到可以直接拿CNN分类模型来干检测。当时的主流观点是CNN需要固定输入尺寸、平移不变性太强定位这种对空间位置敏感的任务不适合它。R-CNN论文最值钱的地方不是“用了CNN”这个动作本身而是把检测问题拆成了几个可以分别优化的子问题然后逐个用当时最成熟的工具去解决。1.2 三段式流水线为什么成立R-CNN的处理流程如果用大白话讲就是输入一张图先用Selective Search产出大约2000个候选区域这些区域大概率框住了目标但位置和尺度不够准然后把每个候选区域缩放成227x227的固定尺寸送进一个在ImageNet上预训练过的AlexNet取全连接层的4096维向量作为该区域的语义特征最后把这些特征分别送给两类头部一类是线性SVM做类别判断另一类是边界框回归器做位置精修。关键设计在于这三个模块可以独立替换和迭代。区域提议不满意可以换一套提议算法特征提取得不够好可以把骨干网络换深分类器不够强可以换更强的分类模型。这种解耦对复现者极其友好因为你可以一步一步验证每个模块的效果而不需要像端到端方案那样一报错就不知道问题出在哪。后来的Fast R-CNN和Faster R-CNN本质上做的事情就是把这个流水线从“分阶段实现”做成了“一体化网络”让梯度可以端到端传播。理解了R-CNN的三段式再看那些改进版本会顺畅很多。1.3 论文标题里的“语义分割”是什么情况标题上写了object detection and semantic segmentation但语义分割在论文里更像是验证CNN特征可迁移性的附加章节不是主线。作者团队做了少量分割实验证明CNN提取的特征不仅对检测有效对像素级任务也有一定泛化能力。对大多数读者来说读这篇论文可以先把分割部分跳过把精力放在检测整体框架、微调策略、SVM训练、边界框回归这四件事上。复现的时候也建议先只跑检测分支把流程跑通再考虑分割那块否则很容易被无关细节拖慢进度。2. 三个关键模块的原理与细节2.1 Selective Search区域提议到底在做什么Selective Search是R-CNN选定的区域提议算法。它基于图像分割用颜色、纹理、尺寸、填充兼容性这几路信号做自底向上的层次化合并最后输出一组可能包含物体的候选框。相比密集滑动窗口它的优势在于候选区域数量少、召回率高而且能自适应物体的尺度和长宽比利用边缘和纹理线索避免纯穷举。原论文默认取2000个候选区域这个数字不是随便拍的。太少召回会掉复杂场景里目标容易被漏掉太多后面的CNN特征提取开销直线上升。VOC这类数据集上2000个Selective Search候选框配合恰当的策略召回率已经足够支撑后续分类器。复现时我第一次只选了1000个候选结果mAP明显偏低后来改成2000就好很多。这个数字应该作为默认参数不要为了省时间盲目调低。另外要注意Selective Search有一种快速模式和一种质量模式论文实验用的是质量模式速度和召回表现更均衡。如果复现时发现区域质量不对先检查是不是选了快速模式。2.2 warp到227x227加padding不是可有可无CNN要求固定输入尺寸所以每个候选区域都要归一化到227x227。这一步看起来简单实际上藏着R-CNN的一个重要实验结论。作者团队对比了几种归一化方式包括各向同性缩放和考虑上下文填充等结论是直接在原始框四周加上16像素的上下文再整体缩放到227x227效果最好。为什么要加上下文因为物体不是孤立存在的周围的背景提供了重要的尺度和空间关系线索。比如一辆车周围略带一些路面和天空的背景分类器更容易判断这是什么如果框刚好紧紧贴住物体边缘一点上下文都没有分类反而容易误判。p16这个值在实现里不是随便加的它是相对原始候选框尺寸的固定像素偏移。复现时很多人直接用OpenCV的resize把原始候选框拉成227x227发现效果和论文差了一截再去查代码才发现漏了这一层padding。另外原论文因为沿用分类任务的习惯也做了水平翻转的数据增强但测试时不做多尺度投票。这些细节直接影响最终mAP但在各种博客和总结里经常被忽略。2.3 特征取哪一层fc7和pool5的区别R-CNN骨干用的是AlexNet五个卷积层加三个全连接层。论文里主要取fc7层的4096维向量做SVM分类特征但边界框回归器用的却是pool5层的特征。这里面的逻辑值得说清楚全连接层靠近输出语义信息更抽象最能体现“这是什么类别”适合做分类决策但语义抽象会丢掉精确定位信息而回归任务恰恰需要位置相关的细节因此pool5这种还保留一定空间结构的特征更合适。这个观察放到今天依然有参考价值。面对一个检测任务如果你在特征图上做位置回归总是不准可以想想是不是用了过于高层、空间分辨率太低的特征。R-CNN的骨干是AlexNet输出特征图已经很小区域在pool5上的空间信息有限所以它在回归上的提升其实不如后来的Fast R-CNN那么大但思路是对的。复现时如果显存紧张可以考虑先保存fc6的特征用来做SVM分类再单独算pool5特征做回归但工程复杂度会高一些。我个人的经验是分两个缓存文件存储分类用一份、回归用一份SVM训练和回归器训练可以随时独立调试不必每次重跑整个CNN。2.4 为什么不直接用softmax非要多训一个SVM这是几乎所有读者都会问的问题。论文的实验结论其实有点微妙在ImageNet预训练得到的CNN特征上直接训softmax分类器的效果和线性SVM差距不大但经过检测数据集上的微调之后softmax的表现会提升一些整体上SVM的优势不再那么明显。那作者为什么还是保留了SVM原因有几个。第一SVM可以做类间独立的难负样本挖掘训练时每个类别单独优化不需要等整个网络一起收敛。第二当时大家普遍认为SVM在小样本高维特征上更稳定检测训练数据本身不多CNN的整体微调容易过拟合而SVM只处理特征参数规模小很多。第三从工程上讲特征已经提前算好了训练SVM的成本很低多训一个模块也不亏。复现的时候我不建议死守SVM不放——如果你想快速验证流水线直接在微调后的网络上加一个全连接分类头测试也能得到不错的结果但如果要贴近原论文指标最好老老实实按论文流程把SVM训练起来尤其是要复现那个58.5%的数字时SVM加难例挖掘是绕不开的。3. 训练过程的几个关键阈值0.5、0.3和难例挖掘3.1 从ImageNet预训练到检测数据集微调R-CNN的骨干网络不是一个随机初始化的CNN而是先在ImageNet这种大规模分类数据集上预训练过的模型。预训练质量直接影响检测精度这一点在复现时体会特别深。直接用随机初始化在VOC这种小规模检测数据集上从头训练AlexNet几乎必死因为VOC每类训练样本只有几百到上千张不足以训练一个深层网络的底层特征。微调阶段的具体做法是把预训练模型的最后一层1000类分类头替换成21类20个VOC类别加1个背景然后只保留包含目标或背景的候选区域参与训练初始学习率一般设为0.001比预训练时低一个数量级。这个阶段不会持续太久论文里的迭代次数也不算多重点在于让网络适应检测任务中“区域可能只包含物体的一部分”的输入分布。一个常见误区是把整个网络的所有层都按同样的学习率更新实际复现时前几层卷积学到的通用边缘纹理特征基本不用怎么变动可以设置更低的学习率甚至冻结。我个人复现时把前三层卷积冻结只微调后面的层既省显存又能保持接近论文的精度。3.2 IoU阈值为什么有0.5和0.3两套标准论文里出现了两个不同的IoU阈值很多新手看到就懵了。微调CNN时候选区域与某个ground truth框的IoU大于0.5就当作前景正样本训练SVM时这个阈值变成0.3。同样一个候选框可能微调时算正样本到了SVM阶段又变成负样本这并不矛盾因为两者的目标不一样。微调时用0.5是相对宽松的目的是让CNN多见到一些和真实目标部分重叠的样本学到更鲁棒的前景背景区分能力。SVM是最终分类决策的模块希望正样本尽量“干净”所以要求IoU大于0.3。这里注意方向0.3比0.5小反而更严格这里必须说清楚IoU 0.3表示需要和真实框有至少30%的重叠率。相比0.50.3允许更低的真实重叠就被当作正样本不是的正样本要求重叠大于阈值阈值越大正样本越靠近真实框越严格。论文里SVM用的是0.3比微调阶段的0.5更低所以SVM阶段的正样本范围其实比微调时更大。这句话很多人记反了。为什么SVM敢用更低的阈值因为SVM训练时可以接受更多正样本并且配合难负样本挖掘对标注框本身位置不极端敏感。复现时这两套阈值最好都按论文原始配置来自己拍脑袋改成0.7之类mAP可能不升反降。3.3 难负样本挖掘负样本太多内存扛不住每个候选区域如果没有和任何真实框重叠足够多就会被当成背景负样本。VOC里的真实标注框并不多但Selective Search一给就是2000个候选框负样本数量轻松到百万级。用几百万个负样本直接训练线性SVM内存直接爆炸而且绝大多数负样本都是那种很“简单”的背景训练出来的分类器根本不关心困难案例比如远处像人的树影、卡车后面露出的人腿。论文的处理方法是标准的难负样本挖掘也叫hard negative mining。先用一部分容易采到的负样本训练一个初始SVM然后用这个SVM去扫描全量负样本挑出所有被误判为正样本的hard negatives把它们加进训练集重新训练。反复迭代两三轮分类器会越来越聚焦在容易混淆的区域上。每类负样本通常会设置一个缓存上限超过上限就丢掉旧样本保证训练集规模可控。这个是R-CNN复现里除了CNN微调以外最影响精度的一环。我记得第一次偷懒没做难例挖掘直接用随机负样本跑完最后mAP掉了将近两个点。负样本质量和数量对SVM分类器的影响就是这么明显。3.4 边界框回归只调位置不重新分类Selective Search的框质量其实一般IoU经常只到0.5到0.6之间远不够准确。边界框回归器的作用就是把这种“大致框住”变成“精确框住”。输出四个量中心点x、y的平移量以及宽和高的缩放量其中宽高是在log空间里预测的避免预测值变成负数。论文里的实现细节至少有三个点需要复现时注意。第一回归器的输入是pool5特征而不是fc7特征理由在上一节说过保留空间细节更有助于定位。第二每个类别单独训练一个回归器因为不同类别的物体形态差别很大一个通用的修框参数不如各类独立的参数准确。第三训练样本只挑与该类别某个ground truth重叠较大的候选框我复现时取的是IoU大于0.6的候选框让回归器学会把相近的错框拉回真实位置而不是随便拿所有候选框一锅炖。我当时用带一点正则化的最小二乘来拟合回归目标参数控制在很小效果比较接近原论文。边界框回归在VOC上的收益非常明显幅度在3到5个点mAP之间属于整个流程里性价比最高的一个步骤。4. 推理流程与速度慢是历史问题不是设计缺陷4.1 特征提取的批量化与缓存技巧推理时对每张图提取2000个候选区域的特征在当时的GPU上也要花十几秒一张图非常慢。但工程上可以优化一块既然所有候选区域都是独立的不必一个个过网络可以把1000个或2000个区域拼成一个大batch一次性跑前向得到特征矩阵再用矩阵乘同时给所有SVM打分。这种向量化方式在当年的Caffe里就能稳定提速好几倍。另外R-CNN的特征是可以缓存复用的。一张图的特征算完之后存到本地之后不管换多少次SVM参数、调多少次回归器都不用重算CNN。我复现时就是把这个思路做到了极致所有训练图像的候选区域特征全部离线算好存到磁盘SVM训练和回归训练都只读特征把整套实验周期从“每次迭代重跑CNN”缩短到“分钟级迭代”。这种思路对杀SVM参数特别有用调一次IoU阈值、调一次负样本缓存大小都能快速看到结果。4.2 NMS后处理逐类别做别跨类别卷SVM打分之后会得到一堆置信度同一类别的多个候选框可能全部得分很高因为它们实际上指向同一个目标。非极大值抑制就是用来合并这类重复检测的把所有框按分数排序保留得分最高的框然后删除掉所有和它重叠超过阈值的其他候选框再在剩余框里重复这个过程。论文实现是每个类别独立做NMS不会把一个类别的框拿来抑制另一个类别的框。我在复现时用IoU阈值0.5做NMS这个值属于常规选择。阈值调大一些保留的候选框更多但容易出重复框调小一些重复框少了但可能会把两个靠很近的真实目标合并掉。VOC这种多目标场景0.5比较稳。一个容易忽略的经验是NMS如果放到边界框回归之后再执行效果比回归前好因为回归后的框更贴近真实位置IoU比较更可靠重复框的辨识度更高。4.3 R-CNN慢在哪里怎么评估它的价值R-CNN慢是公认的主要瓶颈不在CNN本身而在于要处理2000个候选区域每个区域都要过一次完整的网络前向特征计算量很大。有段时间我也想全流程实时跑起来但真没必要。R-CNN的价值不在于工程速度而在于它通过“区域提议CNN特征”的组合证明了深度特征对检测任务的碾压性优势。如果你用今天的GPU去复现把AlexNet换成ResNet之类的更强骨干区域提议数量减到几百个速度会快很多但那就不是论文原貌了。复现的时候不必纠结推理速度重点是训练流程和指标能对齐。下面用一个表总结R-CNN和后续两代模型的主要差异方便对照理解快慢变化的来源。模型区域提议特征提取分类与回归训练方式主要瓶颈R-CNNSelective Search每个区域单独过CNN外部SVM回归器分阶段训练2000次前向Fast R-CNNSelective Search全图一次卷积RoI Pooling网络头部端到端多任务区域提议本身慢Faster R-CNNRPN网络全图一次卷积RoI Pooling网络头部端到端多任务结构复杂但可控5. 从R-CNN到Fast、Faster历史坐标就是一条主线5.1 Fast R-CNN到底改了什么Fast R-CNN的第一个改进是RoI Pooling。它把不同尺寸的候选区域对应的特征图区域池化成固定大小的特征这样整张图只需要算一次卷积所有候选区域的分类和回归特征都在同一份特征图上截取。原先2000次前向变成1次前向加2000次轻量池化训练和推理速度都大幅提升。第二个改进是合并训练目标。R-CNN把CNN、SVM、边界框回归器分开训练Fast R-CNN把它们放在同一个网络里联合训练分类用softmax回归用smooth L1损失一起反向传播。这让特征提取层能同时为分类和定位服务精度不降反升。复现R-CNN之后再去看Fast R-CNN你会明显感受到端到端带来的简化和稳定不需要难例挖掘不需要特征缓存也不需要维护三个相互独立的模块。5.2 Faster R-CNN把最后一块短板也补上了R-CNN和Fast R-CNN都在用Selective Search做区域提议这是一个CPU上的独立算法不能整体训练速度也不可控。Faster R-CNN用Region Proposal Network把提议生成也网络化在特征图上用滑动窗口预测每个位置是不是前景同时输出建议框。这样整个检测器成为一个完全可训练的整体区域提议、特征提取、分类回归全部共享卷积特征。历史角度看Faster R-CNN之后的两阶段检测器框架已经定型后续工作大多在骨干网络、特征金字塔、标签分配策略、后处理方式上做迭代。R-CNN里的人工可替换模块逐渐被网络层替代但最初的问题分解方式仍然清晰可见先粗定位再精细分类和回归。这个“候选精修”的范式到今天也依然是很多检测器设计的主心骨。5.3 R-CNN留下的两笔遗产第一笔遗产是预训练加微调的范式。分类任务上预训练检测任务上微调这个习惯在R-CNN之后成为整个视觉领域的主流。即便今天有了各种自监督预训练思路依然是在大数据上学通用表征再在小数据任务上适配。第二笔遗产是多阶段系统工程的思路。R-CNN每个模块都可评测、可替换、可解释这在科研阶段特别有价值。它不像端到端模型那样给你一个黑盒最终指标而是允许你在区域提议、CNN特征、分类器、回归器每一层都看到自己的改进和退化。对做研究和做工程的人来说这种可解释性甚至比绝对精度更难能可贵。6. 复现避坑记录看着简单细节里全是坑6.1 数据集和候选区域先拿小块数据跑通全流程复现R-CNN的第一建议是别直接上完整VOC。我当时的做法是先取VOC 2007 trainval里一小部分图片把Selective Search、CNN特征提取、SVM训练、边界框回归、NMS这个完整链条跑通确认每个环节都能出数再放大到完整数据。直接上全量数据一旦某个模块有bug排查成本非常高而且训练特征缓存可能就要占掉好几十GB磁盘。另外要检查Selective Search产出的候选框本身质量。如果一张图生成的候选框完全覆盖不到真实目标后面所有步骤都等于白做。先做一次最朴素的检验算算候选框和真实框的平均recall如果Top 2000个候选框连90%的真实目标都没覆盖到说明提议参数有问题不要急着往下走。6.2 特征缓存与内存管理R-CNN训练SVM时需要反复扫描负样本如果每次扫描都重新算CNN特征效率会低到难以接受。把所有候选区域的特征离线缓存是必须的。2000个候选区域每个4096维浮点向量单张图大约几十MB内存看起来不大但整个VOC训练集几万张图累计下来全部放内存不太现实。我当时是直接按图像ID分开存在磁盘上SVM训练时流式读取既不会爆内存也不影响调试。还有一个容易出错的地方是图像缩放。检测数据集里的图像分辨率差别很大Selective Search是在原图还是缩放后的图上跑候选框坐标是否需要同步缩放这些坐标变换最容易出bug。我当时在这个问题上翻过车建议写一个辅助函数把真实标注和候选框绘制到同一张图上做可视化一眼就能发现坐标有没有错位。6.3 结果为什么没有论文里的58.5%很多人复现R-CNN训练流程完整走完最终mAP却比论文低几个点。常见原因无非这几个骨干网络没有用AlexNet而是换了网络结构微调学习率或者迭代次数不对SVM的难负样本挖掘没做或者负样本缓存上限设置不合理边界框回归器使用了没有对齐的坐标空间比如用了resize后的坐标去做回归训练但推理时没换算回去NMS阈值和回归先后顺序不对。这些每一项都可能造成一到两个点的损失放在一起差距就明显了。建议先把v1版本的流程固定在几个关键节点上微调后的CNN特征分别做一次纯分类实验不加回归先和论文对照分类指标然后逐步加入回归器、完整SVM难例挖掘流程每加一块重新看指标变化。这样能定位出是哪个环节导致精度损失而不是最后一锅端发现不对再回头查。我自己的体验是分类部分最容易先对齐加了回归之后指标能明显涨最后再抠SVM训练细节缩近最后那一点差距。6.4 一个适合快速上手的实现路径如果你是第一次接触R-CNN我会建议先读论文里的实验表格把VOC 2007上的几组主要数字记下来baseline微调后分类的mAP、加回归后的mAP、用VOC 2012训练集的mAP。这些数字是你复现过程中的对照坐标。然后拿一个成熟的深度学习框架实现骨干网络和特征提取区域提议可以用现成的Selective Search库不需要自己从零写。整个复现顺序建议是候选区域生成、CNN特征缓存、SVM分类、NMS、边界框回归每走完一层就在小额数据上验证一次。这个路径虽然不是最短的但每一步都可控、可解释非常适合用来建立对检测问题的整体手感。我个人复盘R-CNN复现过程最有收获的其实不是最终跑出多少mAP而是通过亲手拆解三阶段流水线真正理解了“检测任务应该被拆成哪些子问题”。很多人在Fast R-CNN时代入坑检测一上来就是端到端遇到问题只能黑盒调参不知道误差来自区域提议还是特征提取还是分类头。而R-CNN这种结构的价值恰恰在于它逼着你把每个环节一一打开、单独验证。所以如果你也想把目标检测学扎实我还是建议耐着性子把R-CNN这套老流程完整复现一遍哪怕最后精度不是那么重要这个过程本身值回票价。
阅读完成 · 觉得有帮助?
咨询建站