恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
目标检测基石:R-CNN到Faster R-CNN进化详解
首页
资讯中心
/
目标检测基石:R-CNN到Faster R-CNN进化详解
目标检测基石:R-CNN到Faster R-CNN进化详解
发布时间:2026/10/13 13:16:34
目标检测这个方向这几年迭代很快但要说真正的“奠基性”工作绕不开R-CNN、Fast R-CNN、Faster R-CNN这一条线。现在不少做检测的工程师包括我自己最初就是靠啃这三篇论文上手的。哪怕今天工业界用的很多检测框架骨架里依然能看到这三代算法的影子。这篇就把它们的前因后果、核心思路和演进逻辑一次说清楚如果你正准备入坑目标检测或者想复盘检测框架的底层设计这篇应该能帮你省不少时间。先说个总体的判断R-CNN系列解决的本质上是一个“检测该怎么做”的问题。检测跟分类的最大区别是你不但要知道图像里有什么还要知道它在哪。这就带来了两个核心子任务——定位和分类。早期做法是穷举式滑窗把一张图缩放到不同尺度然后用不同大小的窗口扫一遍每个窗口都送进分类器判断。思路是对的但实际跑起来效率极低因为窗口数量是几万甚至几十万的量级每个窗口都要单独过一遍卷积网络算力根本扛不住。R-CNN系列的高明之处就是一步步把“候选区域卷积特征分类回归”这套体系建立起来并且不断把计算往共享方向推。下面按时间线拆开讲每一代解决什么问题、用了什么招、留下了什么坑都给你捋清楚。1. 检测任务的底层逻辑为什么R-CNN之前都慢1.1 检测为什么比分类难一个量级图像分类任务里一张图就是一个输入网络输出它的类别就行。但目标检测要处理的是一个“集合”问题图里有几个目标每个目标是什么类别每个目标在哪难点在于目标的尺寸和位置都是未知的。一张图像里可能一个大象占了半边角落里还有一只小鸟只有几十个像素。你没法直接用一个固定大小的输入去框住所有可能的目标所以“搜索”就成了绕不开的第一步。在深度学习之前主流方案是DPM那一套用滑动窗口加手工特征。到深度学习兴起后有人尝试把CNN当作滑窗分类器用但窗口稍微多一些卷积就要一遍遍地重算。我当时跑过一个类似的实验输入一张500×500的图设了三个尺度、几十种长宽比最后出来的候选框超过十万个哪怕网络本身很小单张图也要跑几十秒完全没法用。1.2 区域提议先猜“哪里可能有物体”于是有人想到另一条路不需要把所有窗口都遍历一遍而是先用一个轻量级的算法快速挑出“看起来像物体”的区域再只对这些区域做精细分类。这就是“区域提议”Region Proposal的由来。早期的代表性方法是Selective Search中文一般叫选择性搜索。它的做法是把图像超像素分割成很多小块然后根据颜色、纹理、尺寸、形状等相似度不断把相邻的小块合并成更大的区域。合并的过程会生成一大堆不同尺度的候选框这些框就是“可能包含物体”的位置假设。Selective Search有两个特点值得注意一是它生成的候选框数量远小于滑窗通常一张图几千个但召回率很高大部分真实目标都能被覆盖到二是它完全不用训练是纯图像处理的手段速度也还凑合。R-CNN的“R”指的就是Region Proposal整个思路就是建立在“先提议、再分类”这个框架上的。1.3 先粗后细的思路为什么有效这其实是个很朴素的工程思想先把全部计算量集中在小部分“高概率区域”上而不是把算力均匀撒在全图上。用一个生活化类比——你要在一堆书里找某本特定封面的书不会一本本从头翻到尾而是先看书脊的颜色、厚度挑出几个嫌疑书架再仔细核对。R-CNN能跑起来根基就是这个“猜验”的思路。后面的Fast和Faster版本虽然不断优化“猜”和“验”的具体方式但核心框架从未改变。理解了这一点后面三代算法在做什么就很容易串起来了。2. R-CNN把CNN引入检测的破冰之作2.1 R-CNN的完整流程2014年提出的R-CNN整个流程分四个阶段现在看挺繁琐但每一步都够扎实输入一张图像用Selective Search生成约2000个候选区域。把每个候选区域裁剪出来缩放到固定尺寸论文里是227×227送进一个预训练的CNN提取特征。特征向量输入一系列SVM分类器判断这个区域属于哪个类别每个类别一个SVM。如果SVM判断为正类再用一个Bounding Box Regression模型对候选框的位置做精细修正。注意第三步和第四步是独立训练的这也是后来被诟病最多的“多阶段训练问题”但在当时这套方案的效果是碾压性的。在PASCAL VOC数据集上R-CNN把mAP从传统方法的35%左右直接推到了53%以上几乎翻了一倍。这个提升幅度放在今天都是令人震惊的等于告诉整个CV圈子CNN不光能做分类检测也能靠它起飞。2.2 每个模块的细节设计先说CNN特征。R-CNN用的是在ImageNet上预训练好的分类网络然后在检测数据集上做微调。微调时把原来的1000类分类层换成检测的类别数比如VOC是21类20个目标类别加1个背景。这里有一个容易忽略的细节预训练模型输入是227×227的正方形图但Selective Search生成的框是任意形状的所以R-CNN的做法是直接把框内的图像拉伸到227×227不管原来的长宽比。这个操作在今天看起来有点粗暴但在当时是不可避免的因为全连接层要求输入维度固定。再说SVM。为什么要用SVM而不是直接用微调后的softmax层这背后有个有趣的历史原因。作者发现CNN最后一层特征也就是fc7层输出的4096维向量在SVM上的表现比在softmax上更好。主要是因为SVM的训练方式更灵活正样本和负样本的定义可以跟CNN微调时不一样。微调时只要候选框与真实框的IoU大于0.5就算正样本而训练SVM时正样本的IoU阈值被提高到了0.7负样本是IoU小于0.3的那些框。这个差别很微妙。IoU阈值越高正样本的质量越高但数量也越少。SVM阶段用更严格的阈值是为了让分类器最大限度学到目标的完整特征而不是半截车身、半个脸这种模棱两可的样本。最后是Bounding Box Regression。它的目标是学习一种映射把候选框的坐标修正到离真实框更近的位置。具体做法是对每个类别训练一个线性回归模型输入是候选框对应的CNN特征同样来自fc7层输出是四个变换参数分别是中心点偏移和宽高缩放。这个模块的效果提升很明显在VOC上能让mAP再涨3到4个点。2.3 R-CNN的致命伤重复计算与存储爆炸R-CNN效果好但它的缺点也极其致命。2000个候选框每个都要独立过一遍CNN这意味着每张图要进行2000次前向传播。而候选框之间大量重叠低层特征被反复计算这是极大的浪费。另一个坑是存储。既然每个候选框都提取了一个4096维的特征向量为了不重复跑网络当时的标准做法是预先提取并缓存到硬盘上。VOC训练集大约5000张图每张图2000个框算下来要存储上千万条特征。我当时照论文复现这一步时300GB的硬盘直接被塞满了训练还得靠几个T的移动硬盘轮着用。这种存储开销在实验环境里还能忍放到工业场景根本没法落地。还有一点是推理速度。一张图跑完全流程在当时的GPU上要40到50秒。这个数字放到今天看已经慢得离谱了但在当年它依然是精度上限的最高水平。只能说慢是慢至少它证明了大方向是对的。2.4 R-CNN时代踩过的坑实操中有一个细节很多人会忽略Selective Search生成的候选框经常会包含一些重复度极高的框如果直接全部拿去提取特征不但浪费算力还会让训练样本的分布失衡。我当时的做法是加了一个简单的NMS非极大值抑制预处理按Selective Search给的分数先做一轮去重能把候选框数量从2000压到1500左右精度几乎不变训练速度却快了不少。另外正负样本的平衡也是大问题。2000个候选框里真正跟目标重叠的正样本可能只有几十个剩下全是背景。如果直接拿来训练SVM负样本数量碾压正样本模型会严重偏向背景最后什么也检测不出来。所以当时训练SVM时一般会做难负样本挖掘先用初始模型挑出那些“看起来像物体”的背景框把它们加入负样本池再重新训练。这一步在R-CNN里是必须的忘了它测试时的误检率会飙升。3. Fast R-CNN砍掉重复计算实现单阶段训练3.1 核心思路让卷积在整张图上只算一次Fast R-CNN在2015年提出它要解决的核心问题就是R-CNN的重复计算。思路其实很直接既然候选框都是从同一张图来的为什么不让整张图只通过一次CNN得到完整的特征图然后把每个候选框对应的特征区域从特征图上抠出来用这样整张图只需要一次前向传播卷积计算量直接从2000倍降到了1倍。这个设计的关键是RoI Pooling。它的作用是把任意大小的候选框区域映射到特征图上然后通过分块池化输出固定尺寸的特征。具体做法是先把候选框在原始图上的坐标按比例缩放到特征图的坐标一般是1/16或1/32的比例取决于网络卷积层的stride然后把框内的特征区域划分成固定数量的网格比如7×7在每个网格内做最大池化最终输出一个7×7×C的固定尺寸特征图。RoI Pooling解决了两个问题一是特征提取共用二是允许输入尺寸不固定。网络前面的卷积层不要求输入固定尺寸只要到了RoI Pooling这一层统一输出7×7×C的特征后面接全连接层就不会报维度错误。3.2 多任务损失分类和回归一次搞定Fast R-CNN做的第二个重大改变是把分类和边框回归放进了同一个网络里用多任务损失联合训练。具体是在RoI Pooling之后特征分别送进两个并列的全连接分支一个分支输出类别概率包括背景类另一个分支输出每个类别的边框修正参数。损失函数由两部分组成分类用softmax交叉熵损失回归用Smooth L1损失。Smooth L1这个函数值得单独说一下。它是L1和L2损失的结合当误差较小时用二次函数保证平滑当误差较大时用线性函数避免梯度爆炸。相比R-CNN里SVM和回归器分开训练的方式多任务损失的优点是分类和回归可以共享前面的特征彼此互相促进训练时也不再需要分阶段而是整个网络一起反向传播端到端优化。当时跑下来的效果训练速度相比R-CNN提升了约3倍推理速度提升了约9倍。在VOC数据集上mAP基本保持在R-CNN的水平甚至略高。训练流程也从“预训练CNN → 微调 → 训SVM → 训回归器”的四步走简化成了“预训练CNN → 端到端微调”的两步走实验成本大幅下降。3.3 为什么速度能提升这么多很多初学者会以为Fast R-CNN变快主要是GPU变好了。其实不是算法本身的优化才是主因。速度提升有两个来源。第一是大规模共享计算。R-CNN里2000个候选框各自过一遍网络低层卷积特征重复计算2000次Fast R-CNN只算一次这部分节省是几千倍的系数。第二是RoI Pooling把不同大小的候选框统一成了固定维度使得后续的全连接层可以批量处理充分利用GPU的并行能力。注意RoI Pooling本身并不是什么复杂操作它就是把一个不规则区域通过网格池化变成规则特征。但恰恰是这个小操作打破了“全连接层必须吃固定尺寸输入”的限制让检测网络可以自由处理任意尺寸的输入图像。3.4 RoI Pooling的实际问题与应对RoI Pooling有一个明显的不足候选框坐标映射到特征图时需要除以stride的倍数结果会出现小数实现时一般会做四舍五入。同理7×7网格划分时网格边界也不会正好落在整数像素上也要舍入。这两次量化误差会导致候选框的位置有轻微偏移对精确定位的小目标很不友好。这个问题在当时的实验里其实已经能感觉到。检测大目标时几乎无影响但检测行人、车辆这类长宽比明显或小尺寸的目标时回归误差会比预期大。后来出了RoI Align用双线性插值解决量化问题那就是另一个故事了。不过Fast R-CNN的RoI Pooling作为一代经典方案在当年的硬件条件下已经是最优解。3.5 还是慢Selective Search成为新的瓶颈Fast R-CNN把CNN部分的计算量压了下来但整个推理流程远没有达到实时。原因很简单Selective Search太慢了。Selective Search处理一张普通图像大约需要1到2秒。这对于一个纯CPU的图像处理算法来说不算慢但Fast R-CNN整个CNN前向计算在GPU上也只需要零点几秒。于是原本不起眼的候选框生成阶段成了整个pipeline的最大短板。这就好比一个工厂的生产线之前是每个环节都慢现在机器快了结果原材料供应跟不上拖了后腿。我当时测过各步骤耗时Selective Search约1.5秒CNN前向约0.2秒后处理约0.1秒。整个流程里有超过80%的时间花在候选框生成上。如果想让检测速度再上一个台阶必须把候选框生成也并到GPU管线里或者干脆用一个更快的网络来生成。这个观察直接引出了Faster R-CNN的核心变革。4. Faster R-CNN把候选框生成也交给网络4.1 RPN的诞生从Selective Search到网络预测Faster R-CNN在2015年底提出核心贡献是一个全新的模块RPN也就是区域提议网络。RPN的本质是用一个轻量级的卷积网络直接在特征图上预测候选框。它不再依赖任何外部算法候选框生成和后续检测完全共享同一套卷积特征全部跑在GPU上。这样一来之前耗时1.5秒的Selective Search被替换成了几乎可以忽略不计的前向计算整个检测流程才真正接近实时。RPN的设计思路是在特征图上每一个位置预先设定一组不同尺寸和长宽比的锚点框通常叫Anchor。每个锚点框在原始图像上对应一个区域。RPN要做两件事一是判断这个锚点框内是不是有物体二分类前景/背景二是预测锚点框到真实目标的偏移修正。这两件事分别通过两个并行的1×1卷积层完成。默认情况下RPN在特征图的每个位置生成9个锚点框分别是3种尺度和3种长宽比的组合。之所以选9是因为这个组合覆盖了大多数目标的自然尺度范围比如人通常偏高、车通常偏宽、密集小目标通常用更小的尺度。这个参数不是固定的遇到特殊数据集时比如全是超大目标的遥感图可以对锚点框的尺度和比例做重新聚类。4.2 RPN的训练细节正负样本与损失RPN的训练要解决一个关键问题锚点框那么多哪些是正样本哪些是负样本Faster R-CNN论文里的规则是与任意真实框的IoU最高的那个锚点框视为正样本另外与任意真实框的IoU大于0.7的锚点框也视为正样本。负样本是IoU小于0.3的所有锚点框。IoU在0.3到0.7之间的直接丢弃不参与训练。为什么这样设计因为太高阈值导致正样本太少网络学不到东西太低阈值又会让大量只有一部分重叠的框进入训练干扰分类器的判断。0.3和0.7这个区间可以看作是一个模糊地带——它们既不够“像目标”也不够“像背景”硬拿去训练只会让模型的分界面变得混乱。RPN的损失函数同样是多任务分类加回归的形式。分类用二分类交叉熵回归用Smooth L1。训练时还有一个重要的工程细节minibatch内正负样本的比例要控制一般取1:1。具体做法是从一张图的锚点框中随机采样256个框其中正样本128个、负样本128个不足的话用其余的框来补。这个比例控制很关键它避免了负样本过多导致模型完全退化成背景分类器。4.3 共享特征与联合训练Faster R-CNN在结构上是一个整体前面是共享的卷积骨干网络中间分出一条RPN分支产生候选框同时主分支对候选框做分类和回归。两条分支共用骨干网络的特征这一点是Faster R-CNN能够实现高速度的关键。但共享特征在训练时会遇到一个鸡生蛋的问题RPN需要骨干网络的紧特征来产生好候选框而骨干网络又需要有候选框才能训练检测分支。论文给出的解法是分步训练先用ImageNet预训练的权重初始化骨干网络单独训练RPN。用训练好的RPN生成候选框作为Fast R-CNN检测分支的输入单独训练检测分支。再次训练RPN但这次固定骨干网络的参数只微调RPN自己的层。保持骨干网络和RPN不变只微调检测分支的层。这四步走完整个网络基本收敛。后来也有人逐步把四步合并成一步联合训练基础就是把这套交替训练的过程展开成一个大计算图同时更新所有层的梯度。但我个人的经验是交替训练虽然繁琐但每一步的检查点都很清晰出了问题能快速定位是RPN的问题还是检测分支的问题排错效率更高。4.4 Faster R-CNN的性能表现在当时的GPU硬件上Faster R-CNN的推理速度大约是5帧每秒相比Fast R-CNN的0.5帧每秒左右提升了近一个数量级。同时由于RPN本身是可微分的模块它产生的候选框质量比Selective Search更稳定mAP在VOC上又涨了几个点达到了70%以上。5fps这个速度在今天的标准下不算快但放在当时已经是“准实时”的水平。更重要的是它证明了目标检测完全可以打造成一个端到端的网络输入图像输出检测结果中间所有步骤都在GPU上完成。这个思路直接影响了后来所有的检测框架包括one-stage系列。虽然one-stage算法后来在速度上超越了Faster R-CNN但两阶段检测在精度上限上的优势至今仍在很多重视准确率的场景里被保留。4.5 Anchor的坑与调参经验Anchor机制是Faster R-CNN里最需要手工调的部分也是最容易翻车的地方。我自己的经验是拿到一个陌生数据集时千万不要直接沿用默认的9个锚点框尤其是遥感、医疗影像这类目标尺度和自然图像差异很大的场景。一个行之有效的办法是把训练集所有标注框的宽高统计出来做一个聚类分析看看真实目标的长宽比集中分布在哪些区间。比如自然图像里行人多锚点框的比例可以偏细长航拍图里车辆和建筑多比例则接近1:1或者略宽。聚类结果可以直接替代默认的{0.5, 1, 2}三个长宽比。还有一个常见的坑RPN输出的候选框很多都重叠如果不做后处理后续检测分支要处理大量重复框。标准做法是NMSIoU阈值取0.7。阈值调大保留框更多召回率更高但计算量变大调小计算量下降但可能漏检。具体取多少跟目标的分布密度有关系。密集小目标场景我一般会调到0.75到0.8之间稀疏场景0.7完全够用。5. 三代算法的演进逻辑一条“计算下沉”的路线5.1 同一个问题的一步步解决把三代算法摆在一起就会发现一条非常清晰的演进线索计算一步步从“重复”变成“共享”候选框生成从“外部工具”变成“网络模块”。R-CNN阶段每一处都是独立的候选框外部生成特征重复提取分类器和回归器也都是独立训练的小模型。Fast R-CNN把特征提取和分类回归打通共享了卷积特征但候选框生成还是外部的。Faster R-CNN把最后一块拼图补齐候选框生成也被网络化整个检测pipeline变成一张完整的计算图。这个过程本质上是在回答一个问题检测流程中哪些环节可以共享哪些环节可以并入网络哪些环节可以由GPU加速。每一次“计算下沉”都让前一个阶段的计算结果更好地传递给后一个阶段减少了冗余也减少了信息损失。5.2 三代算法速查对比模型候选框生成特征计算次数训练流程推理速度当年GPU核心瓶颈R-CNNSelective Search独立每框一次约2000次四阶段独立训练几十秒/张重复计算与存储开销Fast R-CNNSelective Search独立整图一次端到端微调约0.5秒/张候选框生成耗时占比高Faster R-CNNRPN网络内生成整图一次交替联合训练约0.2秒/张锚点框超参敏感这个表能帮你直观看到进化点。速度提升是表象本质是把原本割裂的子任务逐步融合到一张网络里。5.3 对后来检测算法的影响Faster R-CNN的锚点框思路直接影响了后面众多的one-stage检测器。比如SSD把锚点框铺在多层特征图上YOLOv2从聚类中得到先验框本质上都是Anchor思想的不同形态。即使到后来的Anchor-Free流派也是针对Anchor本身的不足去提出替代方案但它们要解决的问题——怎么在特征图上定义位置和尺度——依然是R-CNN系列留下的命题。如果你现在要去读检测相关的新论文我会建议你把R-CNN系列当成一个“坐标系”。新论文里那些复杂名词比如特征金字塔、可变形卷积、注意力机制绝大多数都能映射到这个坐标系里。理解了这一条主线读新论文的阻力会小很多。6. 从零复现那些论文里没写的实操经验6.1 数据准备与预处理细节复现R-CNN系列前数据准备阶段就有很多坑。先说标注格式。VOC格式的标注是按XML文件存储的包含目标和目标框坐标。但实际数据集里经常会出现坐标越界、宽高为0、目标极小等脏数据。如果你不做清洗直接进训练流程轻则loss出现NaN重则模型直接不收敛。我当时写过一个校验脚本专门处理这类问题检查目标框坐标是否在图像范围内超出就裁剪到边界剔除宽或高小于5像素的极小板检查标注类别是否在类别列表内。这一步虽然简单但能省下后面至少一天的排错时间。另一个容易被忽略的是图像缩放策略。Fast和Faster R-CNN训练时通常会把图像的短边缩放到600像素左右长边不超过1000像素。这个尺度不是随便定的它直接影响检测小目标的性能。如果你把短边设得太小比如400小目标的特征在特征图上可能只占一两个像素RPN很难覆盖设得太大GPU显存又顶不住。600这个值是在当年的硬件条件和模型容量下折中出来的。6.2 训练超参该怎么调学习率是复现时最常出问题的地方。R-CNN系列检测模型的Backbone一般用ImageNet预训练权重初始化所以初始学习率可以比较大常用的是0.001到0.01之间后期再乘0.1进行衰减。但要注意如果你的数据集很小比如只有几千张图学习率太大很容易在微调阶段把预训练特征破坏掉导致模型反而比预训练权重还差。我自己有个习惯小数据集初始学习率降到0.0005左右并且在前几个epoch用一个warmup机制让学习率从0缓慢爬升。batch size方面Fast R-CNN和Faster R-CNN在训练时一般每张图采样128到256个RoI。R-CNN的SVM训练则完全不同它需要把每张图的候选特征全部提取出来单独存成文件再做子采样训练。在这个环节磁盘IO会成为瓶颈建议用SSD存特征文件并且把特征提取和数据加载分离成两个阶段。6.3 踩过的几个典型坑复现过程中有几个问题属于“论文里不会写但迟早会遇见”的典型。第一个是RoI Pooling越界。候选框经过坐标缩放后可能会越出特征图的边界如果不加裁剪池化操作会访问到非法地址导致崩溃。实现时一定记得加clip操作把坐标限制在特征图范围内。第二个是正负样本采样的随机性问题。RPN训练时如果每次都用同一个随机种子模型可能会在特定数据上过拟合如果不固定随机种子每次训练结果波动又很大。我的做法是固定一个常用种子方便复现等最终确定超参后再撤掉种子跑几次看方差。第三个是NMS的时机。Faster R-CNN里RPN输出后和检测分支输出后都要做一次NMS两次NMS的阈值是不同的。RPN阶段用0.7保留2000个框给检测分支检测分支输出后再用更低的阈值比如0.5做最终去重。有人只做一次NMS结果会产生大量重叠检测框看起来像模型没收敛其实是后处理没做对。6.4 从源码中学到了什么当年啃完Fast R-CNN和Faster R-CNN的源码最大的收获不是某个具体实现而是一种工程思维。源码里大量细节体现了作者对“边界情况”的谨慎处理特征图坐标对齐时全用float而不是int就是为了减少量化误差累积采样时显式地保证正负样本数量就是为了稳定训练loss除以batch size时保留一个小数点精度就是为了数值稳定。这些经验放到现在依然不过时。后来我自己写检测模块时凡是涉及坐标换算的地方一律统一用float运算到了最终可视化或者写盘时才转int。这个习惯帮我省掉了太多莫名其妙的检测框偏移问题。6.5 给后来者的建议如果你打算从零复现Faster R-CNN我的建议是不要直接上现成的目标检测库比如mmdetection里的一行调用。先试着在一个小数据集上按论文的步骤把RPN和检测分支分开实现一遍哪怕最终的代码没有那些库高效但这个过程中你对每个模块的理解会完全不同。选数据集也有讲究。第一个复现项目建议用VOC或者类似的自然图像数据集不要一上来就挑战工业质检、医疗影像这些难度高的场景。自然图像的目标尺度多样、类别清晰、标注质量高是最适合验证算法正确性的环境。等你在标准数据集上跑通了再去迁移到自己的业务数据那时候才谈得上调优Anchor、改特征融合之类的事。个人体会是R-CNN系列最值得学的不是网络结构本身而是作者们拆解问题的方式先发现瓶颈再设计针对性方案每一个改动都对应一个明确的计算瓶颈。今天做大模型也好、做端侧部署也好这种“把流程往下沉、把重复变共享”的思路依然是最有效的优化方法论。