RCNN与YOLO全解析:目标检测原理、实操与避坑指南
做目标检测项目这几年RCNN和YOLO这两条技术线几乎绕不开。新手入门最先接触的往往是这两个系列团队选型、毕业设计、工业落地讨论来讨论去也还是这两个方向。网上讲目标检测的资料非常多但大部分要么只讲原理不讲实操要么给一堆代码却没有讲清楚为什么这么配置。这篇文章我打算把RCNN系列和YOLO系列放在一起做一个完整拆解从设计思路、核心机制、训练实操到问题排查把“为什么这么做”讲透同时也把可以直接落地的步骤、配置和参数给出来。内容会比较长但我会尽量用大白话把这套东西讲明白适合正在做目标检测课题的学生、刚入门深度学习视觉的开发者以及需要在工程里做检测方案选型的朋友。无论你是要用Faster RCNN做高精度检测还是准备用YOLO做实时推理这篇文章都可以当作一份带避坑指南的参考笔记来用。1. 目标检测的整体设计思路拆解1.1 先搞清楚目标检测到底在做什么目标检测这个任务拆开来看其实就是两个问题的组合这个东西在哪以及它是什么。分类任务只需要回答“是什么”检测则多了一个“在哪”的边界框回归问题。边界框通常用四个值表示常见的有 (x_{center}, y_{center}, width, height) 或者 (x_{min}, y_{min}, x_{max}, y_{max}) 这两种形式。模型要做的事情就是预测出这四个值同时预测出框内物体的类别概率。用人话说检测模型就是一个在图像上“找框”的模型。找到框之后框的坐标准不准、类别对不对就是衡量模型好坏的核心标准。而围绕这两件事学术界和工业界演化出了两条截然不同的技术路线。1.2 两阶段与单阶段RCNN和YOLO最核心的分野RCNN系列和YOLO系列最大的区别不在于网络结构有多花哨而在于它们解决问题的顺序完全不同。RCNN系列走的是“两阶段”路线第一阶段先生成可能包含物体的候选区域第二阶段再对每个候选区域做分类和边界框修正。整个过程可以理解成人先拿一个扫描仪把整个房间扫一遍圈出几个可能是目标的位置然后再走到每个位置跟前仔细辨认。YOLO系列走的是“单阶段”路线一次前向传播直接输出所有目标的类别和位置。它不先找候选区域而是把图像分成网格每个网格直接预测它负责的区域里有没有目标、目标是什么。这个差异直接决定了两者的性能特征。两阶段方法因为做了两次筛选精度通常更高候选区域质量好误检少但一次推理要跑两遍网络或者一个网络跑两个分支速度较慢。单阶段方法把检测当成一个回归问题一步到位速度快得多但早期的YOLO在小目标和密集目标场景下精度明显吃亏。不过这几年两个系列的边界已经越来越模糊了。Faster RCNN引入了RPN而YOLO系列也借鉴了anchor机制、FPN多尺度特征融合这些理念。选型的时候不能只看“两阶段还是单阶段”还得看具体版本、部署平台和任务特点。1.3 项目选型什么时候选RCNN什么时候选YOLO根据我实际做项目的经验选型逻辑大致是这样的如果你的任务对精度要求极高、对速度不太敏感并且目标尺寸相对固定、场景相对简单选Faster RCNN这类两阶段模型会更稳妥。典型场景包括医学影像中的病灶检测、工业质检中的缺陷检测、遥感图像中特定目标识别。如果你的任务需要实时推理比如视频监控、自动驾驶、无人机巡检、嵌入式终端部署那就YOLO系列选最新版还是选老版本就看硬件算力。还有一个很现实的考量点生态和工具链成熟度。YOLO系列的社区生态非常繁盛数据标注、训练、部署都有大量现成工具遇到问题基本都能搜到答案。而RCNN系列通常要借助mmdetection这类框架来跑配置起来更繁琐一些但胜在算法全面、适合做学术对比实验。2. RCNN家族核心细节解析2.1 从RCNN到Fast RCNN把“候选区域”这件事想明白RCNN的思路放在今天看其实挺直白的。先用Selective Search算法在图像上生成大约2000个候选区域然后把每个候选区域裁剪出来并缩放到固定尺寸送入一个CNN网络提取特征最后用SVM分类器判断类别再用一个回归器修正边界框。这个方案在2014年刚出来的时候效果非常惊艳但问题也很明显2000个候选区域都要单独过一遍CNN速度慢得离谱而且每个区域独立计算大量特征其实重复提取了。Fast RCNN做了两个关键改进。第一个是把整张图一次性送入CNN得到特征图而不是每个候选区域都跑一遍第二个是引入了ROI Pooling层把候选区域在特征图上对应的区域统一池化到固定大小。这样原本需要跑2000次的特征提取变成了只跑1次速度一下子提升了几十倍。我自己在复现Fast RCNN的时候有一个体会ROI Pooling这个东西看似简单但它其实是整个两阶段检测思想的核心枢纽——它把“在图像上找框”和“对框做分类”这两件事巧妙地连接了起来。理解了这个层后面看Faster RCNN就容易多了。2.2 Faster RCNN把提议也交给网络Fast RCNN虽然快了但候选区域还是用Selective Search在CPU上生成的这成了一个新的性能瓶颈。Faster RCNN的贡献就是把这个瓶颈也解决掉了它新增了一个RPNRegion Proposal Network网络直接在特征图上生成候选区域整个过程完全可以在GPU上跑。RPN是怎么工作的呢它会在特征图的每个位置上预置一组不同尺寸和长宽比的anchor box比如常见的3种尺寸乘3种长宽比就是9个anchor。RPN要做的就是判断这9个anchor里哪些包含物体哪些是背景同时对包含物体的anchor做一次粗略的位置修正。Faster RCNN的损失函数由四部分组成RPN的分类损失、RPN的回归损失、Fast RCNN部分的分类损失和回归损失。训练的时候是端到端的四个损失一起优化这也是它精度高的一个重要原因。在实际使用中Faster RCNN的anchor参数是需要根据任务调整的。比如检测小目标时默认的anchor尺寸往往偏大需要把anchor scale调小或者加入更多小尺寸anchor。这个调参过程很考验耐心但效果立竿见影。2.3 用mmdetection训练Faster RCNN的实操要点如果你要在自己的数据集上用Faster RCNN我建议直接使用mmdetection这个框架而不是从零写训练代码。原因是Faster RCNN的pipeline细节非常多——anchor匹配、ROI采样、损失权重设置这些在mmdetection里都已经调好了直接用比自己重写靠谱得多。安装好mmdetection之后最关键的三个文件是模型配置文件、数据集配置文件和训练脚本。模型配置文件里需要重点关注num_classes这个参数它要根据自己的数据集类别数修改。需要注意这里填的是num_classes 类别数 1因为背景也算一类。新手在这个地方经常出错训练出来的模型输出维度不对报错或者推理结果异常。数据集配置一般会用CocoDataset格式。如果手头的数据是VOC格式或者YOLO格式需要先转成COCO的JSON格式。转换方法可以写一个简单的Python脚本把XML中每个目标的name、xmin、ymin、xmax、ymax读出来组织成COCO需要的字典结构。训练命令本身不复杂python tools/train.py configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py但mmdetection的配置文件层级比较多继承关系可能让新手蒙圈。建议先跑通官方demo再基于已有的配置文件改不要从空白配置开始写。3. YOLO系列核心机制与实操要点3.1 YOLO的核心思路网格、置信度与边界框YOLO的第一版思路非常激进把图像划分成 S×S 的网格每个网格负责预测固定数量的边界框每个边界框输出5个值四个是位置和尺寸中心坐标x、y宽高w、h第五个是置信度表示这个框里包含物体的概率以及框的准确程度。置信度怎么定义论文里用的公式是 (Pr(Object) \times IoU_{pred}^{truth})——当网格内有目标时(Pr(Object)1)此时置信度就等于预测框和真实框的IoU没有目标时置信度直接为0。这个设计非常巧妙它把“有没有物体”和“框准不准”这两个信息统一到了同一个数值里。每个网格还会预测每个类别的条件概率。注意YOLO早期版本里每个网格只预测一组类别概率不管它预测出几个框所以一个网格最终只能输出一个目标这导致相邻目标很多时检测效果很差。这也是YOLOv1容易漏检密集小目标的根本原因。前面这些理解非常重要因为YOLO后续每个版本其实都是在解决“网格划分和边界框预测”这两个核心问题上的改进网格从固定变成自适应边界框从格子输出变成anchor回归类别预测从每网格一次变成每个anchor都有。3.2 YOLO各代版本演进到底该选第几代YOLO系列发展到现在版本之多确实容易让人迷惑。v3、v5、v8、v11还有网上各种“v26”之类的说法——其实很多是社区玩家自己命名的改进版本或者整合包不能完全按官方版本来理解。我自己在项目里的选型逻辑是这样的如果是做学术实验或者需要稳定复现选YOLOv8。它的架构成熟、文档齐全、ultralytics库封装得很好提供了统一的训练和推理API新手和老手用起来都顺手。现在很多论文复现、竞赛baseline都直接基于v8。如果是做工业落地尤其是要部署到边缘设备YOLOv5依然有很强的生命力。它虽然版本老一些但模型轻量化方案成熟导出ONNX、TensorRT的坑基本都被踩平了社区问答覆盖了你能遇到的大部分问题。如果追求最新最佳精度可以试试YOLOv11或者更新的版本。它们引入了更多结构上的创新比如改进的C3k2模块、更高效的注意力机制精度和速度都有提升。但要注意新版本的工具链、部署方案可能需要自己摸索遇到问题可参考的案例相对少。如果是极端轻量的场景比如只有几MB的模型预算可以关注YOLO系列中的nano版本或者考虑一些专门为移动端设计的检测模型比如热词里提到的“仅5MB的目标检测模型”这类。这种场景下通常要做精度和速度的明显取舍可以先用一个较大的模型做实验确认可行性再压缩成轻量模型。3.3 YOLO损失函数拆解定位、置信度与分类很多新手对着YOLO的代码看损失函数时会一头雾水这里我把核心思路拆开说。YOLO的损失函数本质上由三部分组成定位损失、置信度损失、分类损失。其中定位损失是边界框坐标和宽高的误差分类损失是类别预测和真实类别的误差置信度损失是物体性得分的误差。边界框回归从最早的IoU Loss一路进化到GIoU、DIoU再到CIoU。CIoU是目前YOLO系列里非常常用的方案它比IoU多考虑了边界框中心点距离和长宽比一致性。具体来说CIoU在IoU的基础上加了两项一项惩罚两个框中心点之间的欧氏距离另一项惩罚长宽比的不一致。这样回归的时候不仅要求框“盖住”目标还要求框的中心点和长宽比都对得上。置信度损失分为两部分有目标的地方和无目标的地方。如果直接使用标准二元交叉熵一张图里大量网格都没有目标置信度损失会被负样本主导导致模型倾向于把所有位置都预测为背景。YOLO的解决办法是使用focal loss的思路调整正负样本权重——对有目标的位置更关注对无目标的位置提高“已经学得很好”时的抑制。分类损失比较容易理解多标签、单标签、多分类任务用交叉熵即可。实际需要关注的坑是如果你的数据集类别不均衡需要给少数类别更高的权重否则模型会在类别A上表现很好、在类别B上几乎不检出——我处理过的一个项目中数据里有90%都是某一类模型一开始几乎把另一类全部漏掉了后来通过重采样和数据增强才纠正过来。还有一个训练细节非常重要正负样本的分配策略。YOLO系列从v3开始用“和哪个GT的IoU最大”来决定正样本到v5引入自适应anchor和多正样本分配再到v8用TaskAlignedAssigner分配策略越来越精细。不理解这套机制很容易在分析训练效果时困惑为什么某个框没有被模型学习那可能是因为它的IoU或者对齐度分数不满足分配阈值。4. 实操过程从数据处理到模型训练4.1 数据标注与数据集格式转换目标检测训练的第一步是数据标注。常用的工具是LabelImg、Labelme和CVAT。LabelImg适合本地小规模标注输出VOC格式的XML文件CVAT适合团队协作和大规模项目可以部署在服务器上多人同时标注。标注的时候有几个经验值得分享。第一个是标注框要尽量贴合目标边缘不要留太多背景也不要截断目标主体。背景留太多会干扰模型学习框太紧了又可能丢失一些边缘特征。最理想的情况是框刚好包含目标的最小外接矩形略微留1到2个像素的边距。第二个经验是对于遮挡严重的目标遮挡超过50%的建议不标注或者单独建立一个“被遮挡目标”的类别。否则同一个目标一会儿被标注一会儿不被标注会让模型非常困惑训练收敛会很困难。标注完成后根据你要用的框架转换数据格式。YOLO系列一般用txt格式每行是“类别id 中心点x归一化 中心点y归一化 宽度w归一化 高度h归一化”。COCO格式是JSON包含images、annotations、categories三个字段。VOC格式是XMLPascal目录结构。4.2 准备目录结构和配置文件这里我以YOLOv8为例演示训练自己的数据集需要准备的目录结构和配置。首先建立一个数据集目录推荐按照以下结构组织datasets/mydata/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamltrain和val的划分建议按8:2或者9:1如果数据量很大也可以留一部分做测试集。注意图片和标签的文件名要一一对应否则加载的时候会丢失数据。data.yaml文件内容大体如下train: datasets/mydata/images/train val: datasets/mydata/images/val nc: 3 names: [cat, dog, bird]这个文件里最关键的就是nc和names类别数量和类别名称必须和标注文件里的id严格对应顺序不能错。4.3 训练自己的数据集准备好数据后训练命令很简洁yolo detect train datadatasets/mydata/data.yaml modelyolov8s.pt epochs100 imgsz640 batch16这里的几个参数我说一下我的理解。model参数可以填一个预训练权重路径相当于用COCO上预训练的模型做微调训练速度会快很多最终精度也通常更好。imgsz训练尺寸默认640如果目标相对较小可以尝试把尺寸提高到768或者1024但显存占用也会明显增加。batch大小根据显卡显存调整一般要让显存占用率保持在80%以上才不会浪费算力。训练过程中需要观察的几个指标大家可以留意一下box_loss、cls_loss、dfl_loss 这三条曲线是否稳定下降如果震荡过大说明学习率可能偏大。验证集上的mAP50和mAP50-95是否持续上升如果到训练后期还在缓慢上升可以适当增加训练轮数。如果训练集loss降得很好但验证集loss不降说明过拟合了需要加数据增强或者正则化。4.4 用训练好的模型做推理与部署训练完成后在runs/detect/train目录下会得到best.pt和last.pt这两个权重文件。last.pt是最后一轮的权重best.pt是验证集上表现最好的那一轮的权重。推理的时候默认用best.pt。单张图片检测yolo detect predict modelruns/detect/train/best.pt sourcetest.jpg conf0.25conf是置信度阈值低于这个值的检测结果会被过滤。实际应用的时候阈值怎么定要看场景监控场景误报代价高可以设高一点漏检代价高的场景可以设低一点再配合跟踪算法做后处理。部署到移动端或者嵌入式设备时一般需要把模型导出成ONNX再转成TensorRT或者NCNN。导出命令如下yolo export modelruns/detect/train/best.pt formatonnx imgsz640导出时最好固定输入尺寸避免动态尺寸带来的额外计算开销和不稳定问题。5. 常见问题与排查技巧实录5.1 六个高频问题和排查思路这些问题是平时在社区被问得最多的我自己也踩过类似的坑整理成表格方便大家对照排查问题现象可能原因排查思路和解决方案训练时loss为NaN学习率过大、数据里有异常标注降低学习率检查标注文件是否存在超出图像范围的坐标mAP一直很低数据标注错误、类别不均衡、anchor设置不合理抽样可视化检查标注统计类别分布计算数据集的anchor尺寸小目标完全检测不到下采样倍数过大导致小目标特征丢失采用更高分辨率输入使用更大的特征图调整anchor训练速度很慢batch太小、数据加载是瓶颈调大batch开启amp混合精度训练检查数据读取线程数显卡显存不够用batch太大、输入尺寸太大减小batch或者imgsz开启梯度累积训练完推理结果全是背景框类别数配置错误、推理时未设置正确类别检查nc和names是否与标注一致检查推理代码是否加载了正确的权重5.2 小目标检测和红外小目标的评价参数问题小目标检测是目标检测里一个老大难问题。一般来说COCO定义里目标面积小于32×32像素就算小目标。小目标的特征在下采样过程中特别容易丢失这也是为什么YOLO在检测小目标时往往不如一些带特殊机制的网络。从实践角度出发我推荐几个有效的小目标检测手段使用更高分辨率的输入图像、在FPN中保留更大的特征图比如P2层、针对小目标调整anchor、使用过采样和裁剪的方法把小目标区域放大后训练。红外小目标方面除了通用检测指标外还经常用到信杂比增益SCR Gain和背景抑制因子BSF。信杂比增益衡量的是检测前后目标与背景杂波的信杂比提升倍数背景抑制因子衡量的是算法对背景杂波的抑制程度。这两个指标在红外弱小目标检测的论文中非常常见如果你的项目涉及这个方向建议在评估时把这几个指标和mAP一起报告。5.3 多目标跟踪指标怎么得到很多目标检测项目会进一步扩展成多目标跟踪这时需要评估跟踪效果。多目标跟踪的核心指标是MOTA多目标跟踪准确率、MOTP多目标跟踪精确率、IDF1身份F1分数和HOTA高阶跟踪准确率。MOTA综合了漏检、误检和身份切换三方面的错误计算公式是 (1 - \frac{FP FN IDSW}{GT})其中IDSW是身份切换次数ID Switch。MOTP衡量的是跟踪框和真实框之间的对齐程度。IDF1则侧重ID保持的准确性如果跟踪算法经常给同一目标切换IDIDF1就会非常低。计算这些指标时建议使用TrackEval工具它支持MOT Challenge格式的GT文件可以自动计算上面所有指标。数据准备上的一个关键点是GT框和算法输出的track文件都要包含帧号、ID、边界框坐标、置信度等字段格式一定要对齐。我自己在跑跟踪评估时踩过的最多的坑就是帧号不连续或者ID从0开始还是从1开始的问题这些差异都会导致最终指标完全对不上。建议从一开始就在项目里约定好统一的数据格式规范避免后期返工。5.4 YOLO环境配置的几个坑YOLO环境配置的坑其实集中在两个地方一个是PyTorch和CUDA版本的匹配另一个是依赖库的版本冲突。安装PyTorch时一定要先确认自己的显卡驱动支持的CUDA版本再选择对应的PyTorch安装命令。很多人在import torch时报错“CUDA unavailable”多半是PyTorch版本和CUDA版本不匹配或者安装的是CPU版。如果使用的是AMD显卡驱动和深度学习框架的适配是一个需要认真核对环境的操作通常需要专门的适配层或社区封装版本且版本匹配要求比主流方案更严格。在做深度学习项目时优先使用主流显卡可以节省大量调试时间如果只有AMD显卡的环境建议先确认好框架的兼容版本再动手。依赖库版本冲突的经典解法是使用干净的环境重建不要用base环境。我在实际项目里通常这样操作conda create -n yolo python3.10 -y conda activate yolo pip install ultralytics只需要这几步YOLOv8的日常训练和推理就能跑起来了。如果是在Windows上使用建议把图像路径都设为绝对路径避免反斜杠被当成转义字符。写在最后的一点经验做目标检测这几年我最大的感受是算法理解得再透彻都不如亲手把一套完整流程跑通一遍来得实在。RCNN系列让我理解了“检测本质上是一个由粗到精的搜索问题”YOLO系列让我明白“在精度和速度之间做取舍必须建立在清晰的任务定义之上”。如果你有机会在同一个数据集上分别用Faster RCNN和YOLOv8做训练对比我强烈建议试试。你会直观地看到两阶段和单阶段在收敛速度、最终精度、误检模式上的真实差异这种经验比光看论文来得深刻得多。目标检测这个领域的工具和模型还在快速迭代但核心思想是稳定的——先把数据做扎实把loss看明白把评估指标搞准确无论将来冒出什么新模型你都能够快速上手。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →