搞目标检测这两年,我被问得最多的一个问题不是“什么是anchor”,而是“学长,项目/毕设到底该学RCNN还是YOLO?”——每年都有新人卡在这个岔路口。今天这篇就把这两条技术路线从头到尾捋一遍:RCNN系列怎么一步步从“慢吞吞的候选区域+CNN分类”进化到Faster RCNN的端到端检测,YOLO系列又是怎么从v1一路迭代到v8/v11,把“单次回归”这件事做到极致。还会把训练自己数据集、标注工具选择、AMD显卡跑YOLO、mmdetection配置、小目标检测、红外小目标评价参数这些实操内容一并讲清楚。
文章适合三类人看:一是正在做目标检测课设/毕设的学生,二是准备把检测技术落地到实际业务的工程师,三是在“到底该追新版本还是先用老版本”之间反复横跳的纠结症患者。我的建议是:不管最终选哪条线,先把原理吃透,再谈工程效率。
1. 先理清目标检测到底在解一道什么题
1.1 分类、定位、检测的区别
很多新手一上来就陷入算法对比,结果连“检测任务本身的输出是什么”都没想明白。图像分类的输出是一个类别标签,比如“这是猫”;目标检测的输出则复杂得多——它要回答“图像里有哪些目标,每个目标在什么位置”。所以检测模型的输出是一组边界框(bounding box),每个框带一个类别和一个置信度分数。
在工程里,这个框通常用两种方式表示:一种是中心点+宽高,即 (x, y, w, h);另一种是左上角+右下角,即 (x1, y1, x2, y2)。不同框架的标注格式不一样,YOLO系列用归一化的 (x_center, y_center, width, height),COCO数据集用 (x, y, width, height) 的像素坐标。这个差异看似不起眼,但我在实际项目中见过太多次因为坐标格式没转对导致的训练崩溃——模型训练起来loss一直不降,最后发现是标注读错了。
检测任务还有一个容易混淆的兄弟叫实例分割,它不仅要框出目标,还要把每个目标的所有像素都抠出来。如果只做检测,模型只需要回归框的四个值;如果做分割,就要在框的基础上再追加一个像素级的掩膜分支。YOLOv8同时支持检测和分割就是这种多任务设计的典型。
1.2 绕不开的四个概念:IoU、NMS、Anchor、mAP
这四个概念是理解RCNN和YOLO所有演进的前提,必须先把它们钉死在脑子里。
**IoU(交并比)**是衡量两个框重叠程度的指标。假设预测框是A,真实框是B,IoU就是A和B交集面积除以并集面积,取值0到1,越接近1说明预测框越准。很多损失函数直接拿IoU来构造,比如后面要讲的CIoU Loss。在推理阶段,IoU也用来判断“这个预测框算不算定位正确”——比如mAP@0.5的0.5就是指IoU阈值0.5,只有预测框和真实框的IoU超过0.5才被算作命中。
**NMS(非极大值抑制)**解决的是“一个目标出多个框”的问题。模型在推理时会对同一个目标输出很多高度重叠的候选框,NMS的做法是:先按置信度分数从高到低排序,选分数最高的框保留,然后删除所有与它IoU超过阈值的框;重复这个过程直到处理完所有候选。这个后处理是早期YOLO和Faster RCNN都离不开的环节,直到YOLOv10提出NMS-free方法才被逐渐替代。
**Anchor(锚框/先验框)**是检测模型里最容易被忽略但最重要的设计。简单理解,Anchor就是预先定义好的一组不同大小、不同长宽比的参考框。模型不是直接预测目标的绝对坐标,而是预测“目标相对某个Anchor的偏移量”。这个机制最早被Faster RCNN系统化引入,YOLOv2开始采用聚类方式生成更贴合数据分布的Anchor尺寸,而YOLOv8则转向了anchor-free,直接预测目标中心点到四条边的距离——这是一个相当关键的思路变化。
**mAP(平均精度均值)**是目标检测最常用的评价指标。先按置信度把所有预测框排序,逐个计算精确率和召回率,画出PR曲线,曲线下的面积就是AP。把所有类别的AP取平均,就是mAP。COCO比赛标准是mAP@0.5:0.95——IoU从0.5到0.95,步长0.05,共10个阈值分别算AP再求平均,这个指标对小目标的定位精度更敏感。很多人只在报告里写mAP@0.5,这其实是取了松的阈值,数字好看但说服力不足。
2. RCNN系:两阶段检测的思路是怎么演进的
2.1 RCNN:先用候选区域,再用CNN鉴别
2014年的RCNN(Regions with CNN features)是深度学习目标检测的里程碑。它的核心思路很朴素:先用传统图像分割算法(选择性搜索)在整图上生成约2000个候选区域,然后把每个候选区域缩放成固定尺寸,分别送入CNN(当时是AlexNet)提取特征,最后用SVM分类器判断这个区域是什么类别,再用一个回归器修正边界框位置。
这套“两阶段”思路在当时效果惊艳,但缺点也极其明显:每个候选区域都要单独过一遍卷积网络,2000个区域就要做2000次前向计算,一张图在GPU上也要40多秒,训练更是要分三步走——先预训练CNN、再微调网络、最后训练SVM和回归器,流程繁琐不说,每个模块还互相割裂。
但RCNN有一个贡献至今被低估:它证明了CNN不仅能做分类,还能通过“候选区域+回归”的方式完成定位。后来的所有两阶段算法,本质上都是在这套思路上做优化。
2.2 Fast RCNN:把卷积变成全图共享
Fast RCNN的核心改进是“只对整图做一次卷积”。具体做法是:整张图输入CNN得到一张特征图,候选区域不再送到CNN里,而是通过坐标映射关系投影到特征图的对应区域,然后用一个ROI Pooling层把这些不同大小的区域池化成固定尺寸(比如7×7),再送入全连接层。
这个优化把速度提升到了单张图0.3秒左右,比RCNN快了近25倍。更重要的是,它把分类和框回归放进了同一个网络里做联合训练,用多任务损失函数同时优化两个目标,不再需要SVM和独立的回归器。
Fast RCNN的瓶颈变成了候选区域生成——选择性搜索依然是CPU上的传统算法,每次大约耗时2秒,比网络本身还慢。这直接催生了Faster RCNN的关键创新。
2.3 Faster RCNN:让网络自己学会提候选
Faster RCNN在2016年提出了RPN(Region Proposal Network,区域提议网络),把候选区域生成也变成了神经网络的一部分。RPN的做法是:在共享特征图上用一个3×3的滑动窗口扫描,每个位置生成k个锚框(Anchor,原文是3种尺度×3种长宽比,k=9),网络为每个Anchor输出两个东西:一个是“是不是前景”的分数,另一个是锚框坐标的回归偏移量。
因为RPN和后面的检测头共享卷积特征,整个Faster RCNN可以实现真正的端到端训练,速度提升到单张0.2秒左右,精度也大幅提升。Faster RCNN确立了两阶段检测的标准范式,后续的Mask RCNN(加分割分支)、Cascade RCNN(级联多阶段回归)都是在它基础上扩展的。
2.4 实操:用mmdetection快速训练Faster RCNN
如果你想在实际项目中使用Faster RCNN,最推荐的方式是用OpenMMLab的mmdetection。它把数据加载、模型定义、训练流程、评测指标全打包好了,不用自己造轮子。但mmdetection的安装有个著名的坑:mmcv、PyTorch、CUDA三者的版本必须严格对应。最简单的办法是直接拉官方提供的Docker镜像,或者按下面这套流程装:
# 创建虚拟环境 conda create -n mmdet python=3.8 conda activate mmdet # 安装pytorch,注意版本要和CUDA匹配 pip install torch==1.13.1 torchvision==0.14.1 # 安装mmcv,这里要指定cuda和torch版本 pip install mmcv==2.0.0 -f https://download.openmmlab.com/mmcv/dist/cu117/torch1.13/index.html # 拉取mmdetection代码并安装 git clone https://github.com/open-mmlab/mmdetection.git cd mmdetection pip install -v -e .数据准备上,mmdetection默认使用COCO格式。你需要把数据组织成这样的目录结构:
data/custom/ ├── annotations/ │ └── instances_train.json ├── images/ │ └── train/ └── labels/然后修改配置文件,重点改三个地方:data_root指向你的数据路径,num_classes改成你的类别数,data.train.img_prefix改成图片所在目录。模型结构部分可以直接复用faster_rcnn_r50_fpn_1x_coco.py这个配置,只改num_classes:
model = dict( roi_head=dict( bbox_head=dict( type='Shared2FCBBoxHead', num_classes=3, # 改成你的类别数 ) ) )训练命令很简单:
python tools/train.py configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py实测下来,Faster RCNN用ResNet50骨干在单卡上训练,batchsize为2时大约4.5GB显存,普通消费级显卡就能跑。如果显存不够,优先调小batchsize和学习率(线性缩放),不要一上来就换轻量骨干,否则要连带调一堆参数。
3. YOLO系:把检测做成一次回归的路线图
3.1 YOLOv1:最暴力的端到端思路
YOLO(You Only Look Once)从v1开始就走了一条和RCNN完全不同的路:不搞候选区域,直接把整张图划分成S×S个网格(原文是7×7),每个网格负责预测B个边界框(原文B=2),每个框输出中心坐标偏移、宽高以及置信度分数,同时每个网格还要输出C个类别的概率分布。网络一次前向计算,就把所有目标的位置和类别全部输出来了——所以在速度上,YOLOv1能做到45帧每秒的实时检测。
YOLOv1的损失函数是理解这个算法灵魂的关键。它由四部分组成:
- 坐标损失:预测框中心点坐标的均方误差,以及宽高的平方根误差。用平方根而不是直接用宽高,是为了削弱大框和小框之间的尺度差异——同样大小的绝对误差,对小框的影响更大,所以用平方根压缩一下。
- 置信度损失:分两种情况,包含目标的框和不包含目标的框分开算。不包含目标的框数量远多于包含目标的框,所以给一个小的权重λ_noobj=0.5来平衡。
- 分类损失:网格预测的类别分布和真实类别的交叉熵。
这个设计在当年非常激进,但也带来明显缺陷:每个网格最终只能预测一个类别,当两个目标靠得很近或者目标尺寸很小时,效果很差。这个问题在后续版本中被逐步解决。
3.2 从v2到v11:版本迭代到底在改什么
YOLO系列的版本迭代经常被外行当成“换皮”,但实际上每个版本都在解决上一代遗留的关键问题。
**YOLOv2(YOLO9000)**引入了一系列工程化改进:给主干网络加批归一化(实际测试mAP提升明显)、用更高分辨率输入训练、引入Anchor机制(并且用K-means聚类在训练数据集上统计出更合理的Anchor尺寸)、多尺度训练。v2还提出了一个把检测和分类联合训练的策略,可以识别9000类目标,相当惊艳。
YOLOv3是真正让“YOLO也能打”的一代。它把骨干换成了Darknet-53,并在三个不同尺度的特征图上做预测——13×13的大特征图负责大目标,26×26和52×52的小特征图负责中小目标。每个尺度分配3个Anchor,总共9个Anchor。这种多尺度特征金字塔的设计极大改善了小目标和密集目标检测,是YOLO系从“偏科生”变成“全能选手”的关键转折点。
YOLOv4可以说是集大成者。它把CSPDarknet53、SPP空间金字塔池化、PANet路径聚合网络、Mosaic数据增强、CIoU损失、DIoU NMS这些当时最有效的trick全部堆在一起,精度和速度都达到了新的高度。
YOLOv5(Ultralytics团队维护)可能是至今工业界用最广泛的版本。虽然它的官方论文后来才发布,但工程生态做得好:Python接口友好、导出ONNX/TensorRT方便、模型大小从n到x分档,还带数据集标注工具和训练可视化。很多公司的检测服务就是直接用YOLOv5改的。
YOLOv6来自美团,面向工业应用优化了部署推理。YOLOv7在v4基础上做了E-ELAN结构改进,速度更快。YOLOv8(2023年)又转向了anchor-free设计,用C2f模块替代C3,并且支持检测、分割、姿态估计、旋转框等任务。YOLOv9提出PGI(可编程梯度信息)解决信息瓶颈问题。YOLOv10来自清华团队,引入双标签分配和无NMS训练,让推理真正做到了端到端。YOLO11(Ultralytics的新命名)则继续在效率和精度上做平衡,官方还提供了极其轻量的nano版本,模型文件只有5MB左右。网上甚至能看到“YOLO v26”之类的标题,别慌,那多半是营销号在蹭热度——主流社区实际上就到v8/v11这个梯队。
3.3 现代YOLO的损失函数:不止一个loss在打架
很多人看YOLO源码时会懵,因为现代YOLO的损失函数已经不是v1那种一个大公式了,而是拆分成了多个loss的加权求和。
以YOLOv8为例,训练时的总损失大致是:
Loss = λ_cls * L_cls + λ_box * L_box + λ_dfl * L_dfl其中L_cls是分类损失,每个类别做二分类的交叉熵;L_box是边界框回归损失,通常用CIoU Loss或类似变体;L_dfl(Distribution Focal Loss)是让模型对框的四个边学习一个分布,而不是直接回归一个值,这样能预测更精细的位置。
CIoU Loss是近年回归损失的主力,它的改进思路很直观:原始IoU Loss在预测框和真实框完全不重叠时梯度为0,模型无法优化;DIoU在IoU基础上加了中心点距离惩罚,让两个框往一起靠;CIoU又进一步加入宽高比的一致性惩罚,让框的形状也尽量对齐。用我的话说,这就像两个人碰头——先保证都往同一个方向走(DIoU),再保证走到的地方姿势也对得上(CIoU)。
理解损失函数对调bug很重要。比如训练时发现L_box在0.5附近震荡下不去,大概率是Anchor或目标尺度分布没匹配好;如果L_cls降得异常快,可能是类别严重不平衡导致的假象,模型根本没学会定位。
3.4 实操:标注、训练、导出一条龙
YOLO系现在最香的是Ultralytics生态,训练自己的数据集几乎可以无脑操作。
先用标注工具打好数据。单人小项目用LabelImg就够了,它导出的是Pascal VOC格式的XML,需要再转成YOLO的txt格式;团队协作或数据量大推荐CVAT,直接支持在线标注和多人协同,也能直接导出YOLO格式。标注的时候有两点经验:一是框不要紧贴目标边缘,留2到3个像素的余量;二是对小目标尽量放大图像再标注,不然模型学到的框都是缩水版。
数据组织成下面这样:
dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/每个txt文件名和图片名一致,每行是class_id x_center y_center width height,全部归一化到0-1。然后写一个data.yaml:
train: dataset/images/train val: dataset/images/val nc: 2 names: ['person', 'cat']训练一行命令搞定:
yolo detect train data=data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16 device=0这里model参数可以填预训练权重路径,YOLO会自动加载在COCO上学到的特征做迁移学习,几千张数据也能训出不错的效果。训练完用best.pt做验证:
yolo detect val model=runs/detect/train/weights/best.pt data=data.yaml最后导出模型。ONNX是通用格式,基本所有推理框架都支持:
yolo export model=runs/detect/train/weights/best.pt format=onnx导出时如果遇到算子不支持,可以先检查PyTorch版本是不是太旧,或者把opset版本调高。AMD显卡用户也不用死磕CUDA——Windows上可以用ONNX Runtime的DirectML后端调用AMD卡,Linux上可以用ROCm,实在搞不定就用CPU训练小模型跑一跑验证流程,或者直接租云GPU。
4. 两大系列正面PK,项目到底该选哪个
4.1 速度、精度、复杂度三方权衡
RCNN系列和YOLO系列本质上是“精度优先”和“速度优先”的两种设计哲学。两阶段方法先粗后精,候选区域生成+逐区域精修,定位更准但结构复杂;单阶段方法一步到位,把检测当回归直接输出结果,网络简化、推理更快但早期精度偏弱。
我整理了一个粗略的对比表,方便你快速定位:
| 维度 | Faster RCNN系列 | YOLO系列 |
|---|---|---|
| 网络结构 | 两阶段:RPN+检测头 | 单阶段:一次回归输出 |
| 推理速度 | 0.1-0.3秒/张 | 毫秒级到几十毫秒/张 |
| 精度上限 | 高,尤其小目标和密集场景 | 已接近,但部分场景仍略逊 |
| 可解释性 | 中间有候选区域,方便理解 | 端到端黑盒 |
| 工程生态 | mmdetection较全面 | Ultralytics极完善 |
| 部署难度 | 中等 | 低,导出格式齐全 |
| 适合场景 | 高性能计算、离线分析 | 实时监控、嵌入式设备 |
这里有个容易踩的坑:不要只看COCO mAP排行就做选择。精度指标的差异在工程里可能只差几个点,但推理延迟的差距可能是一个“能实时用”和一个“只能离线跑”的差距。
4.2 按业务场景对号入座
如果用一句话总结选型逻辑:先问自己业务允许多大的延迟,再考虑精度需求。
如果是视频监控、自动驾驶、机器人、工业在线质检这类场景,延迟是硬指标,YOLO系列基本是唯一选择。因为一旦帧率上不去,再高的精度也白搭。
如果是医疗影像、遥感解译、卫星图像分析这类离线场景,每张图的处理时间可以放宽到毫秒甚至秒级,这时候Faster RCNN及其变体的精度优势就体现出来了——尤其是病灶、目标物很小且需要准确定位的场景,两阶段方法通常表现更稳。
还有一个容易被忽略的维度是数据规模。数据量大的时候,Faster RCNN和YOLO的差距会缩小;数据量很小(几百张)的时候,YOLO的预训练迁移学习优势更明显。我在一个只有800张工业缺陷图片的项目里,YOLOv8微调后的mAP@0.5达到了0.9以上,而Faster RCNN从零训练则很难收敛。
4.3 轻量化与前沿方向要留意什么
现在目标检测的方向早就不是“RCNN还是YOLO”二选一这么简单了。轻量化模型是当前热点,对应热搜里“macs仅5mb的目标检测模型”——YOLO官方就有nano级版本,参数量很小的同时精度也差不了太多,适合跑在树莓派、手机这类边缘设备上。如果你用的设备资源极其紧张,还可以考虑MobileNet这类轻量骨干+CSP结构的组合,或者用知识蒸馏把大模型压小。
前沿方向上,三维目标检测要处理点云或多视角数据,常用PointPillars、VoxelNet等方法,2D检测通常只是前置一环;开放词汇目标检测(如Grounding DINO)让模型可以通过自然语言提示检测任意类别;多模态目标检测则把文本-图像对齐的思想引入检测框架,让模型学会“看了文字再找目标”。这些方向都是建立在RCNN/YOLO的基本范式之上的,基础扎实了再往前走才不虚。
5. 训练自己数据集时,我踩过的坑和排查技巧
5.1 数据侧的坑
第一个坑是类别不平衡。很多数据集里“人”占了90%,其余类别只有零星几张,训练出来的模型几乎把一切都预测成人。解决方法是按类别重采样,或者用带类别权重的损失函数。如果训练集实在不均衡,我建议人工给少数类复制几份加强度,简单粗暴但有效。
第二个坑是小目标漏检。小目标在特征图里往往只占几个像素,经过多层卷积下采样后就消失了。常规办法有三板斧:一是提高输入分辨率(比如imgsz从640提到1280),分辨率翻倍对小目标检测的增益立竿见影;二是启用多尺度训练,让模型对不同尺寸的目标都有适应性;三是用SAHI这类切片推理工具,把大图切成小块分别检测再合并结果。前两个方法在YOLO里改个参数就行,实测能把小目标mAP从0.35拉高到0.55左右。
第三个坑是标注质量差。标注框偏大偏小、边界歪斜、漏标,这些都会直接损害模型。训练前我强烈建议做一轮标注校验——把标注可视化叠加在图片上看一遍,重点看小目标区域和遮挡区域。租了标注团队也不代表可以完全放手,抽查比例至少要做到10%到20%。
5.2 训练侧的坑
训练不收敛是新手最容易遇到的问题。排查路径是固定的:先看loss曲线是不是陡峭震荡,如果是,大概率是学习率太高,调低一个数量级再试;再看数据集路径是不是对,YOLO训练时如果标注和图片名不匹配,loss会不降反升;最后检查类别ID——YOLO的类别ID必须从0开始连续编号,如果从1开始,模型会把背景当成第0类,整体乱套。
显存不足也很常见。解决办法按优先级排列:减小batch size,这是最直接的手段,但batch太小会引入训练噪声,一般不要小于8;减小输入分辨率imgsz,从640降到512,显存占用大约减少36%;使用混合精度训练,Ultralytics默认开启AMP,如果发现关闭后模型效果更好,可能是数据精度问题导致梯度不稳定。实在不行就换nano版本模型起步。
过拟合在小数据集上很普遍。如果训练loss一直在降、验证mAP却停滞甚至反降,就该上数据增强了。现代YOLO默认开了Mosaic(把四张图拼成一张),还可以再加HSV扰动、随机翻转、平移缩放等。扩数据的同时,把dropout或权重衰减加大,也能有效压制过拟合。
5.3 部署侧的坑
模型导出ONNX后推理结果和PyTorch里不一致,这种问题我在项目里遇到不止一次。最常见的源头是算子在转ONNX时不兼容,特别是动态尺寸输入。解决方法是固定输入尺寸,导出时带dynamic=False;如果还有问题,用ONNX Runtime的onnxruntime.transformers.optimizer对图做一次优化,绝大部分算子兼容问题都能解决。
另一个坑是多类别预测阈值调错。训练时的conf阈值(置信度阈值)和iou阈值(NMS阈值)会影响最终输出框的数量和准度。实际部署时不要照搬训练参数,要单独在验证集上做一次网格搜索。我常用的经验值:通用场景conf=0.25、iou=0.45;遮挡严重的场景把iou降到0.3,让NMS更宽松一些,减少漏检;目标稀少的场景把conf提到0.5,过滤掉误检。
5.4 红外小目标等专业场景的评价参数补充
做工程项目,光看通用mAP往往不够。比如红外小目标检测这类专业场景,目标极小、背景杂波复杂,用的评价参数跟普通检测不太一样:信杂比SCR衡量目标强度与背景杂波的比值,SCR增益评价算法增强前后信杂比的提升倍数,背景抑制因子考察算法对背景的抑制能力,此外还有检测概率和虚警率两个直接反映检测性能的指标。如果项目需要出成果或者写技术报告,这类领域专属指标一定要补上,单纯给一个mAP数字很难说明算法在目标场景里的真实表现。
6. 聊点个人经验:别被版本号绑架
最后分享一个我自己早期栽过的跟头。刚接触目标检测时,我特别执着于“必须用最新版本的YOLO”,结果光环境配置就折腾了好几天,别提跑通实际项目了。后来才明白,把RCNN系列和YOLO系列的演进逻辑理解透,远比追最新版本号重要——因为这些模型的设计思想是连贯的:Anchor怎么来、NMS怎么去、损失函数怎么平衡、多尺度怎么设计,这些底层逻辑搞明白了,无论未来出现v12还是v13,你都能很快上手。
我的建议是:如果你是学生做毕设,直接用YOLOv8或者YOLO11跑通一个完整流程——标注、训练、验证、导出、部署,把这条路走通比纠结用哪个版本有价值得多;如果你是工程师做项目,Faster RCNN和YOLO不是二选一,而是工具箱里各有用武之地的两把扳手,真正决定成败的往往不是模型有多新,而是你对数据的理解有多深。