1. 三大目标检测框架的江湖格局
目标检测这个领域,过去十年基本就是一部“改朝换代”的历史。2014年RCNN横空出世之前,大家还在用人工特征加滑窗的笨办法,一张图跑几十秒是常态。RCNN把卷积神经网络引进来之后,整个赛道被彻底点燃,从Fast RCNN到Faster RCNN,再到后来YOLO掀桌子搞单阶段检测,以及DETR用Transformer把锚框和NMS全部干掉。到今天,工业界落地最多的还是YOLO系列,学术界刷点最猛的往往是DETR变体,而RCNN家族则更多作为教学和基线存在。
这篇文章我打算把这三个框架从设计哲学到实操细节全部拆一遍。不管你是刚入门想搞明白“为什么YOLO比Faster RCNN快那么多”,还是已经上手训练过模型但遇到BN崩溃、混淆矩阵对不上、TensorRT部署路数算不清楚这些具体问题,都能在这里找到可以直接抄的答案。我会尽量少讲空泛的概念,多讲“为什么这么设计”和“实际跑起来会碰到什么”。
先给一个全局对比,让你心里有个谱:
| 维度 | RCNN系列(以Faster RCNN为代表) | DETR系列 | YOLO系列(以v8为代表) |
|---|---|---|---|
| 检测范式 | 两阶段(提议+分类回归) | 端到端集合预测 | 单阶段密集预测 |
| 核心组件 | RPN + RoI Pooling | Transformer编解码器 + 二分匹配 | CSP主干 + PANet + 解耦头 |
| 是否需要NMS | 需要 | 不需要 | 需要 |
| 是否需要锚框 | 需要 | 不需要 | 需要(v8仍用anchor) |
| 训练收敛速度 | 中等 | 极慢 | 快 |
| 小目标表现 | 较好 | 较弱 | 好(多尺度特征) |
| 部署友好度 | 一般 | 较差 | 极好 |
| 典型推理速度 | 5-15 FPS | 10-20 FPS | 100+ FPS |
这张表不是让你背的,而是让你在选型的时候有个判断依据。下面我逐个展开。
2. RCNN家族:两阶段检测的开山鼻祖
2.1 从RCNN到Faster RCNN的演进逻辑
RCNN的思路其实很朴素:既然分类网络已经很强了,那我就先用某种方法找出可能包含物体的区域,再把这些区域一个个送进分类网络判断是什么。具体做法是用Selective Search从图像中提取约2000个候选区域,每个区域缩放到固定尺寸,送进AlexNet提取特征,最后用SVM分类。这个流程的问题非常明显——2000个区域意味着2000次前向传播,一张图推理要47秒,训练更是要分好几步走,存储特征就要几百GB。
Fast RCNN做了两个关键改进。第一,不再对每个区域单独跑CNN,而是整张图跑一次卷积得到特征图,然后通过RoI Pooling从特征图上“抠”出每个区域对应的特征。第二,把分类和边界框回归合并到一个网络里多任务训练,不再需要单独的SVM。这一步把推理时间从47秒降到了2秒左右,但候选区域的生成还是靠Selective Search,这是CPU上的操作,成了新的瓶颈。
Faster RCNN的贡献就是用一个可学习的RPN(Region Proposal Network)替换掉了Selective Search。RPN本身是一个小卷积网络,在特征图的每个位置预设9个锚框(3种尺度×3种长宽比),输出每个锚框的前景/背景得分和位置偏移量。这样候选区域的生成也变成了GPU上的矩阵运算,整个网络可以端到端训练。推理速度进一步提升到5-15 FPS,精度也上了一个台阶。
2.2 RPN的工作原理与锚框设计
RPN的核心是一组锚框。假设特征图大小为H×W,每个位置有k个锚框,那么总共就有H×W×k个锚框。以VGG16为骨干、输入600×800的图像为例,最后的特征图大约是38×50,每个位置9个锚框,总共约17100个锚框。RPN对每个锚框输出两个东西:一个是2维的前景/背景分类得分,一个是4维的位置偏移量(dx, dy, dw, dh)。
训练RPN的时候,需要给每个锚框打标签。正样本的判定规则通常是:与某个真实框的IoU大于0.7的锚框标为正,或者与某个真实框IoU最大的锚框标为正。负样本是IoU小于0.3的锚框。介于0.3和0.7之间的锚框不参与训练。这里有个细节:一张图里正样本通常远少于负样本,所以会随机采样256个锚框,保持正负比例大约1:1。如果正样本不够128个,就用负样本补齐。
锚框的尺度和长宽比是需要根据数据集调整的超参数。COCO数据集上常用的设置是尺度[128, 256, 512],长宽比[0.5, 1, 2]。但如果你的数据集里全是小目标,比如电力红外数据集里的绝缘子缺陷,那这个尺度设置就完全不对了,需要改成[16, 32, 64]这种量级。我见过太多人直接拿COCO的配置去训自己的数据,结果RPN一个正样本都匹配不上,loss直接不降。
2.3 RoI Pooling与RoI Align的区别
RoI Pooling的作用是把任意大小的候选区域特征图变成固定大小的输出(比如7×7)。做法是把候选区域划分成7×7的网格,每个网格内做最大池化。但问题在于,候选区域的坐标是浮点数,量化到整数会引入误差。对于大目标这个误差可以忽略,但对于小目标,几个像素的偏移可能就让特征完全对不上了。
RoI Align就是来解决这个问题的。它不做量化,而是用双线性插值在浮点坐标上采样。具体来说,每个网格里采样4个点(2×2),每个点的值通过周围4个整数像素双线性插值得到,然后对这4个点取最大或平均。这个改动在COCO上带来了约3个点的AP提升,对小目标的提升尤其明显。如果你现在还在用RoI Pooling,强烈建议换成RoI Align,代码改动量极小但收益很大。
2.4 Faster RCNN训练自己数据的实操要点
训练自己的数据,第一步是格式转换。Faster RCNN通常需要COCO格式或VOC格式的标注。如果你用的是LabelImg标注的XML(VOC格式),可以用现成的脚本转成COCO JSON。关键字段是images、annotations、categories三个数组,其中annotations里的bbox是[x, y, width, height]格式,注意不是[x1, y1, x2, y2]。
第二步是修改配置文件。以Detectron2为例,需要改的主要有:类别数(NUM_CLASSES)、锚框尺度和长宽比、学习率、batch size。类别数一定要改对,否则最后分类头维度不对会直接报错。锚框尺度前面说了,要根据数据集中目标的大小分布来定。可以先跑一遍统计脚本,看看所有标注框的面积分布,然后取25%、50%、75%分位数作为三个尺度的参考。
第三步是学习率设置。Faster RCNN对学习率比较敏感,太大容易梯度爆炸,太小收敛太慢。Detectron2的默认配置是0.02(8卡×2张图),如果你只有单卡,需要按线性缩放规则调整,比如单卡batch size为2的话,学习率设为0.02×2/16=0.0025。warmup也很重要,前1000步从0.001线性增加到设定值,可以避免训练初期loss震荡。
注意:Faster RCNN训练时如果遇到loss变成NaN,八成是学习率太大或者数据里有非法标注(比如宽高为0的框)。先检查数据,再降学习率。
3. DETR:用Transformer重新定义检测
3.1 DETR的核心思想与论文要点
DETR是2020年Facebook提出的,全称是DEtection TRansformer。它的核心思想非常激进:把目标检测当成一个集合预测问题,直接用Transformer编解码器从图像特征预测一组固定数量的检测结果,不需要锚框,不需要NMS,完全端到端。
具体流程是这样的:先用CNN骨干(ResNet-50)提取图像特征,得到一张H×W×C的特征图。然后用1×1卷积降维到d维(通常是256),拉平成一个长度为H×W的序列,加上位置编码,送进Transformer编码器。编码器输出再送进解码器,解码器的输入是一组N个可学习的查询向量(object queries,N通常设为100)。每个查询向量经过解码器后输出一个检测结果,包括类别和边界框坐标。最后用匈牙利算法把预测结果和真实标注做二分匹配,计算损失。
这个设计最妙的地方在于,它把“哪些预测对应哪些真实框”这个问题交给匈牙利算法在训练时解决,推理时直接输出100个结果,其中大部分是“无物体”类别。这样就彻底绕开了NMS这个后处理步骤,也就避免了NMS带来的超参数调优和推理延迟。
3.2 二分匹配与匈牙利算法
匈牙利算法的目标是在N个预测和M个真实框之间找到一个最优的一对一匹配,使得总的匹配代价最小。代价函数通常由三部分组成:分类代价(预测类别与真实类别的交叉熵)、L1边界框代价、GIoU代价。三者的权重需要调,论文里用的是1:5:2。
为什么用匈牙利算法而不是简单的IoU匹配?因为DETR要的是一对一匹配,每个真实框只能匹配一个预测,每个预测也只能匹配一个真实框。如果像Faster RCNN那样用IoU阈值匹配,会出现多个预测匹配同一个真实框的情况,那就还是需要NMS来去重。匈牙利算法从全局最优的角度解决这个问题,保证匹配是一对一的。
实际训练中,匈牙利算法的计算量不大,因为N=100,M通常也就几十个,代价矩阵是100×M,用scipy的linear_sum_assignment几毫秒就解完了。但要注意,如果一张图里真实框数量超过100个,DETR就处理不了了,因为查询向量只有100个。所以DETR不适合密集场景,比如人群计数或者细胞检测。
3.3 DETR训练自己数据的踩坑记录
DETR的训练收敛非常慢,这是它最大的问题。原论文在COCO上要训500个epoch才能达到Faster RCNN训30个epoch的效果。如果你用自己的小数据集训DETR,大概率会遇到loss长时间不降的情况。
第一个坑是学习率。DETR的Transformer部分学习率要设得很小,通常是1e-4,而CNN骨干部分可以设1e-5。如果统一用1e-4,骨干部分的预训练权重会被破坏,反而拖慢收敛。Detectron2和HuggingFace的实现里都支持分组学习率,记得用上。
第二个坑是位置编码。DETR用的是正弦位置编码,固定在特征图上。如果你的输入分辨率跟预训练不一致,位置编码需要重新生成。有些实现会自动处理,有些不会,需要手动检查。
第三个坑是数据增强。DETR对数据增强很敏感,尤其是随机裁剪和缩放。因为DETR没有锚框,它依赖位置编码来定位物体,如果图像被裁剪后物体位置变化太大,模型很难学。建议初期先用简单的翻转和颜色抖动,等loss降下来再加复杂的增强。
实操心得:如果你要用DETR训自己的数据,建议先用预训练模型在COCO上微调,而不是从头训。即使你的数据集跟COCO差异很大,微调也比从头训快3-5倍。另外,batch size尽量大,DETR的二分匹配需要足够的样本才能稳定。
3.4 DETR的变体与改进方向
DETR之后出现了很多改进工作,主要围绕三个方向:加速收敛、提升小目标性能、降低计算量。Deformable DETR用可变形注意力替换了全局注意力,只关注参考点周围的少量采样点,收敛速度提升了10倍,小目标AP也明显提升。DAB-DETR把查询向量显式地建模成4D锚框(x, y, w, h),让解码器的注意力更有针对性。DN-DETR引入了去噪训练,在解码器输入里加入噪声框,让模型学会从噪声中恢复真实框,进一步加速收敛。
这些改进里,Deformable DETR是最值得关注的,它在保持端到端优势的同时,把训练epoch降到了50左右,已经接近实用水平。如果你要上DETR系列,建议直接从Deformable DETR或者DINO(DETR with Improved deNoising anchOr boxes)开始,不要从原版DETR入手。
4. YOLO系列:工业落地的首选
4.1 YOLO的设计哲学与版本演进
YOLO的核心思想跟Faster RCNN完全相反:既然两阶段慢,那我就一步到位,把检测当成回归问题,直接从图像像素预测边界框和类别。YOLOv1把图像分成7×7的网格,每个网格预测2个边界框和类别概率。这个设计非常快,但精度不行,尤其是小目标和密集场景。
YOLOv2引入了锚框机制,用k-means聚类得到先验框尺寸,精度大幅提升。YOLOv3引入了多尺度预测(FPN结构)和残差连接,成为经典。YOLOv4和v5主要是工程优化,把各种trick(Mosaic增强、CIoU损失、PANet等)集成进来。YOLOv8是Ultralytics推出的,把anchor-free、解耦头、C2f模块等设计融合在一起,目前是工业界最常用的版本。
YOLOv8的几个关键设计值得说一下。第一,它改成了anchor-free,直接预测边界框中心点和宽高,不再需要聚类先验框。第二,分类头和回归头解耦,各自用独立的卷积分支,避免任务冲突。第三,用了Task-Aligned Assigner做正负样本匹配,根据分类得分和回归IoU的加权结果来分配正样本,比简单的IoU阈值更合理。第四,损失函数用了VFL(Varifocal Loss)做分类,CIoU做回归,DFL(Distribution Focal Loss)做框的分布学习。
4.2 YOLO损失函数的细节拆解
YOLOv8的损失由三部分组成:分类损失、回归损失、DFL损失。分类损失用的是BCE(二值交叉熵),但加了VFL的加权。VFL的核心思想是,对正样本的损失按IoU加权,对负样本的损失按得分加权。这样高质量的正样本贡献更大,而容易分类的负样本贡献更小。
回归损失用的是CIoU。CIoU在IoU的基础上加了两项:中心点距离和长宽比一致性。公式是CIoU = IoU - (中心点距离² / 对角线距离²) - α×v,其中v衡量长宽比的一致性,α是权重。这样梯度不仅考虑重叠面积,还考虑位置和形状,收敛更快。
DFL损失是YOLOv8的一个特色。它不直接回归框的宽高,而是预测一个分布,然后取期望作为最终值。具体来说,把宽高的范围离散成16个bin,网络输出每个bin的概率,然后加权求和得到预测值。这样做的好处是,模型可以表达不确定性,对模糊边界的物体更鲁棒。DFL的损失就是让真实值落在对应bin上的交叉熵。
注意:YOLOv8的损失函数里有个超参数box和cls的权重,默认是7.5和0.5。如果你的数据集里正负样本极度不平衡(比如缺陷检测),可以适当调高cls权重,让模型更关注分类。
4.3 YOLO训练中BN崩溃的原因与解决
BN崩溃是YOLO训练里最常见的问题之一,表现是loss突然变成NaN,或者mAP骤降到0。根本原因是batch size太小,导致batch统计量不稳定。BN层在训练时用当前batch的均值和方差做归一化,如果batch size只有2或4,统计量波动很大,running mean和running var会跑偏,最终导致推理时输出异常。
解决办法有几个。第一,增大batch size,至少到8以上,最好16。如果显存不够,可以用梯度累积,比如batch size=4,累积4次,等效batch size=16。第二,用SyncBN,多卡同步统计量,但单卡没用。第三,冻结BN层,用预训练模型的running stats,但这会限制模型适应新数据。第四,换用GroupNorm或LayerNorm,但YOLO的官方实现默认是BN,改动较大。
我自己的经验是,如果显存实在不够,优先用梯度累积,其次是把模型缩小(比如用yolov8n而不是yolov8l),最后才考虑冻结BN。冻结BN只适合你的数据集跟COCO非常相似的情况,否则精度会掉很多。
4.4 YOLO混淆矩阵总合不唯一的排查
混淆矩阵总合不唯一这个问题,通常出现在验证阶段。你可能会发现,混淆矩阵里所有元素加起来不等于验证集的总标注数。原因一般有三个。
第一个原因是置信度阈值和IoU阈值。YOLO在验证时会先做NMS,NMS会过滤掉一部分预测框。如果你的置信度阈值设得高,很多低分预测被过滤了,混淆矩阵的总数就少了。另外,如果一个预测框跟真实框的IoU低于阈值,它会被归为背景,但如果它跟另一个预测框重叠被NMS删了,那它就不出现在混淆矩阵里。
第二个原因是多标签问题。YOLO的每个预测框可以同时属于多个类别(多标签分类),但混淆矩阵通常假设每个框只有一个类别。如果一个框被预测为“猫”和“狗”两个类别,它在混淆矩阵里会被算两次,导致总数偏大。
第三个原因是数据加载器的bug。有些实现里,验证集的标注在增强后没有正确同步,导致部分标注丢失。建议先用小数据集(比如10张图)手动核对一遍,确认标注数量和预测数量对得上。
排查步骤:先把置信度阈值设为0.001,IoU阈值设为0.99,关掉NMS,看混淆矩阵总数是否等于标注数。如果等于,说明是阈值问题;如果不等于,检查数据加载和多标签设置。
5. 部署与性能优化实战
5.1 TensorRT加速YOLO的完整流程
TensorRT是NVIDIA的推理加速库,能把YOLO的推理速度提升2-5倍。流程分四步:导出ONNX、构建TensorRT引擎、序列化引擎、推理。
导出ONNX的时候,注意opset版本要选对。YOLOv8建议用opset 11或12,太新的opset可能不被TensorRT支持。导出命令是yolo export model=yolov8s.pt format=onnx opset=12。导出后可以用Netron看一下图结构,确认没有奇怪的算子。
构建TensorRT引擎的时候,关键参数是精度模式。FP32最准但最慢,FP16速度翻倍精度几乎无损,INT8速度再翻倍但需要校准数据集。对于大多数场景,FP16就够了。构建命令可以用trtexec:trtexec --onnx=yolov8s.onnx --fp16 --saveEngine=yolov8s.engine。如果是INT8,需要加--int8 --calib=calibration.cache。
推理的时候,TensorRT引擎需要绑定输入输出buffer。输入通常是1×3×640×640的float32数组,输出是1×84×8400(84=4个框坐标+80个类别,8400=80×80+40×40+20×20)。后处理包括解码框坐标、置信度过滤、NMS。这些操作可以在GPU上用CUDA kernel做,也可以拉回CPU用numpy做。如果追求极致速度,建议用CUDA做后处理。
5.2 T4显卡1080p 25fps场景下的路数计算
这个问题很具体:T4显卡,1080p视频,25帧每秒,YOLO 640分辨率检测,能支持多少路?
先算单路推理耗时。T4的FP16算力是65 TFLOPS,YOLOv8s在640×640输入下的FLOPs大约是28.6 GFLOPs。理论推理时间=28.6/65000≈0.44ms。但实际推理远不止理论值,因为还有内存带宽、kernel启动开销、后处理等。实测YOLOv8s在T4上用TensorRT FP16推理,单帧耗时约3-5ms。
然后算解码和拉流。1080p视频解码用NVDEC,T4有两个NVDEC,每个可以解多路H.264。解码1080p 25fps大约占用5%的NVDEC资源。拉流如果是RTSP,CPU开销主要在协议解析,每路大约占用0.5个CPU核心。
假设单帧推理5ms,那么单路25fps需要125ms/s,即12.5%的GPU时间。理论上可以支持8路。但实际要考虑batch推理的效率。如果把8路的帧拼成一个batch=8,推理时间不是8×5=40ms,而是可能只要15-20ms,因为GPU并行度更高。这样8路的总GPU占用可能只有60-80ms/s,即6-8%。
但还要留余量给解码、后处理和系统调度。保守估计,T4上YOLOv8s 640分辨率,1080p 25fps,支持8-12路是合理的。如果换成YOLOv8n,可以支持15-20路。如果换成YOLOv8m,可能只能支持4-6路。
实操建议:先用单路测出实际推理耗时,再乘以路数,留30%余量。不要只看理论算力,内存带宽和PCIe传输往往是瓶颈。
5.3 一键部署脚本的设计思路
一键部署脚本的核心是把环境安装、模型下载、配置修改、服务启动全部自动化。我一般用bash脚本,结构分四部分。
第一部分是环境检查。检查CUDA版本、cuDNN版本、Python版本、TensorRT版本。如果版本不匹配,脚本直接退出并提示。比如TensorRT 8.5需要CUDA 11.8,如果检测到CUDA 12.0就报错。
第二部分是依赖安装。用pip安装ultralytics、opencv-python、tensorrt等。注意tensorrt的pip包版本要和系统安装的TensorRT版本一致,否则会报错。可以用pip install tensorrt==8.5.3.1指定版本。
第三部分是模型准备。从Ultralytics的release页面下载预训练权重,或者从自己的服务器拉取。下载后校验MD5,确保文件完整。
第四部分是服务启动。用systemd或supervisor管理进程,配置开机自启。日志输出到指定目录,方便排查。
脚本里要加错误处理,每一步都检查返回值。比如pip install失败后要打印错误日志并退出,而不是继续往下跑。另外,脚本要支持参数,比如--model yolov8s --device 0 --port 8080,方便不同场景复用。
5.4 监控视频拉流与YOLO集成的注意事项
RTSP拉流用OpenCV的VideoCapture就可以,但有几个坑。第一,OpenCV默认用FFmpeg解码,如果网络抖动,会丢帧甚至断流。建议设置cv2.CAP_PROP_BUFFERSIZE为1,减少缓冲。第二,RTSP的URL里通常包含用户名密码,注意不要硬编码在代码里,用环境变量或配置文件。第三,断流重连要加机制,检测到连续多帧读取失败就重新连接。
如果路数多,OpenCV的拉流会成为瓶颈。可以考虑用GStreamer,它支持硬件解码,CPU占用更低。GStreamer的pipeline大概是rtspsrc location=... ! rtph264depay ! h264parse ! nvv4l2decoder ! nvvidconv ! video/x-raw,format=BGRx ! videoconvert ! appsink。这样解码在GPU上做,CPU只负责协议解析。
推理结果的可视化也要注意。如果每帧都画框并显示,会占用大量CPU。建议只在需要的时候画框,比如检测到目标才画,或者降低显示帧率。保存视频的话,用cv2.VideoWriter,编码器选MJPG或H264,注意帧率和分辨率要跟输入一致。
6. 数据集与训练技巧
6.1 电力红外数据集(FIRC-Dataset)的转换与训练
电力红外数据集通常包含绝缘子、变压器、套管等设备的红外图像,标注格式可能是VOC XML或COCO JSON。转成YOLO格式需要把bbox从[x1, y1, x2, y2]转成[class_id, x_center, y_center, width, height],并且归一化到0-1。
红外图像的特点是对比度低、纹理少,但目标的热辐射特征明显。训练的时候,建议在数据增强里加一些对比度调整和直方图均衡化,让目标更突出。另外,红外图像通常是灰度图,但YOLO需要3通道输入,可以把灰度图复制成3通道,或者在加载时用cv2.cvtColor转成伪彩色。
类别不平衡是电力数据集常见的问题。绝缘子可能几百个,但某些缺陷只有几个。解决办法有:过采样少数类、在损失里给少数类更高权重、用focal loss。YOLOv8的VFL本身就有一定的类别平衡能力,但如果差距太大,还是需要手动干预。
6.2 YOLO预训练模型下载与微调策略
YOLOv8的预训练模型可以从Ultralytics的GitHub release页面下载,命名规则是yolov8n.pt、yolov8s.pt、yolov8m.pt、yolov8l.pt、yolov8x.pt,n到x表示模型从小到大。下载后直接用yolo train model=yolov8s.pt data=my_data.yaml就可以微调。
微调的时候,学习率要设小一点,通常是从头训练的1/10。YOLOv8默认的初始学习率是0.01,微调建议用0.001。另外,冻结骨干网络的前几层可以加速收敛,尤其是你的数据集跟COCO差异不大的时候。YOLOv8支持freeze参数,比如freeze=10表示冻结前10层。
如果数据集很小(比如几百张图),建议用更强的数据增强,比如Mosaic、MixUp、随机旋转、随机裁剪。YOLOv8默认开启了Mosaic,但MixUp需要手动开。另外,可以用预训练模型做知识蒸馏,用大模型(yolov8x)的输出作为软标签训练小模型(yolov8n),精度能提升2-3个点。
6.3 训练中常见问题速查表
| 问题 | 可能原因 | 解决办法 |
|---|---|---|
| loss不降 | 学习率太小、数据标注错误、锚框不匹配 | 调大学习率、检查标注、重新聚类锚框 |
| loss变NaN | 学习率太大、BN崩溃、数据有非法值 | 降学习率、增大batch、检查数据 |
| mAP低 | 过拟合、欠拟合、类别不平衡 | 加数据增强、换更大模型、调损失权重 |
| 推理速度慢 | 模型太大、输入分辨率高、后处理慢 | 换小模型、降分辨率、用TensorRT |
| 小目标漏检 | 特征图分辨率不够、锚框太小 | 加高分辨率特征层、调小锚框 |
| 混淆矩阵对不上 | 阈值设置、多标签、数据加载bug | 调阈值、检查多标签、核对数据 |
避坑技巧:训练前一定要用可视化工具检查标注框是否准确。我见过太多人训了半天发现标注框偏移了几十个像素,白白浪费算力。用
cv2.rectangle把标注画在图上,随机抽100张看一眼,五分钟的事能省几天时间。
7. 模型改进与前沿方向
7.1 YOLO加CLIP做开放词汇检测
YOLO是闭集检测器,只能检测训练时见过的类别。如果要检测新类别,必须重新标注和训练。CLIP是开放词汇的视觉语言模型,可以把文本和图像映射到同一个特征空间。把YOLO和CLIP结合,可以实现零样本检测新类别。
具体做法是:YOLO负责生成候选框,CLIP负责给每个候选框分类。分类的时候,把候选框裁剪出来送进CLIP的视觉编码器,同时把类别名称送进文本编码器,计算相似度,取最高的作为类别。这样不需要重新训练YOLO,只需要换文本提示词就能检测新类别。
这个方案的瓶颈在速度。CLIP的视觉编码器推理不便宜,如果每个候选框都跑一次,延迟会很大。优化方法有:只对高分候选框跑CLIP、用轻量级CLIP变体、把CLIP分类做成batch。另外,CLIP对细粒度分类(比如不同型号的绝缘子)效果一般,需要针对领域微调。
7.2 YOLO蒸馏提升小模型精度
知识蒸馏是用大模型(教师)的输出指导小模型(学生)训练。YOLO蒸馏通常蒸馏三个部分:分类logits、回归输出、特征图。分类蒸馏用KL散度,回归蒸馏用MSE,特征蒸馏用注意力转移。
具体实现的时候,教师模型用yolov8x,学生模型用yolov8n。训练时同时跑两个模型,计算蒸馏损失和学生自身的检测损失,加权求和。蒸馏损失的权重需要调,太大反而会拖累学生。一般分类蒸馏权重0.5,回归蒸馏权重0.1,特征蒸馏权重0.01。
蒸馏的收益在小模型上比较明显,yolov8n蒸馏后mAP能提升2-4个点,接近yolov8s的水平,但推理速度还是n的速度。如果部署环境算力有限,蒸馏是性价比很高的方案。
7.3 YOLO实例分割与检测的联合训练
YOLOv8支持实例分割,输出除了边界框还有每个实例的mask。分割头的结构是在检测头的基础上加一个原型mask分支和系数分支,每个实例的mask是原型mask的线性组合。
联合训练检测和分割的时候,损失是检测损失加分割损失。分割损失用BCE,只对正样本计算。需要注意的是,分割的标注成本比检测高很多,如果只有检测标注,可以用检测框生成粗糙的mask(比如用GrabCut),但精度有限。
实例分割在工业质检里很有用,比如检测PCB板上的缺陷,不仅要知道缺陷在哪,还要知道缺陷的形状和面积。YOLOv8的分割模型(yolov8s-seg)在COCO上mask AP能到44左右,速度比Mask RCNN快很多,是工业落地的首选。
7.4 火灾实时监控手机摄像头的实现思路
用手机摄像头做火灾实时监控,核心是把YOLO部署到手机端。方案有两种:一是用TFLite或NCNN把YOLO转成手机可用的格式,在手机上推理;二是手机只负责采集和传输,推理在服务器做。
手机端推理的话,YOLOv8n转TFLite后模型大小约6MB,在骁龙8 Gen 2上推理一帧640×640大约30-50ms,能跑到20-30 FPS。但手机发热和耗电是问题,长时间跑会降频。建议用INT8量化,模型缩到3MB,速度提升一倍。
服务器推理的话,手机用RTMP或WebRTC推流到服务器,服务器跑YOLO,检测到火灾再推通知回手机。这个方案对手机要求低,但依赖网络。如果网络不稳定,可以在手机端做轻量级检测(比如颜色和运动检测),只在可疑时才推流到服务器确认。
火灾检测的数据集比较特殊,火焰和烟雾的形态变化很大,而且负样本(比如夕阳、灯光)很容易误检。建议收集多样化的数据,包括不同光照、不同距离、不同火焰形态。另外,可以用时序信息,连续多帧检测到才报警,降低误报率。
8. 学习路径与工具推荐
8.1 从零入门YOLO的学习路线
如果你刚接触目标检测,建议按这个顺序走。第一周,搞明白卷积、池化、BN、激活函数这些基础,能手动算一个简单CNN的输出尺寸。第二周,跑通YOLOv8的官方demo,用预训练模型检测一张图片,理解输入输出格式。第三周,标注自己的小数据集(100张图就够了),训练一个YOLOv8n,看loss曲线和mAP。第四周,尝试导出ONNX和TensorRT,测推理速度。第五周,读YOLOv8的源码,重点看损失函数和数据加载。第六周,尝试改进,比如换骨干网络、加注意力模块、调损失权重。
这个路线大概一个半月,每天投入2-3小时。关键是动手,不要只看视频。我见过太多人收藏了几十个教程,但自己一行代码没跑过。目标检测是工程学科,跑通一次比看十遍强。
8.2 常用工具与资源清单
- 标注工具:LabelImg(简单)、CVAT(功能强)、Roboflow(在线)
- 训练框架:Ultralytics YOLOv8(推荐)、MMDetection(灵活)、Detectron2(Facebook)
- 可视化:Netron(看模型结构)、TensorBoard(看训练曲线)、WandB(实验管理)
- 部署:TensorRT(NVIDIA)、OpenVINO(Intel)、NCNN(手机)、ONNX Runtime(通用)
- 数据集:COCO、VOC、Open Images、Roboflow Universe
8.3 我个人的一些经验体会
搞目标检测这些年,最大的体会是:数据和标注的质量比模型结构重要得多。我见过用YOLOv3在干净数据集上跑出比YOLOv8在脏数据上高10个点的mAP。所以,与其花时间调模型,不如先花时间清洗数据、修正标注、平衡类别。
第二个体会是,不要迷信SOTA。论文里的指标都是在特定数据集上调出来的,换到你的场景可能完全不一样。选模型的时候,先看推理速度能不能满足需求,再看精度。如果速度不够,再好的精度也没用。
第三个体会是,部署和训练是两回事。训练的时候可以用大模型、大batch、复杂增强,但部署的时候要考虑延迟、内存、功耗。很多时候,你需要训练一个大的教师模型,然后蒸馏一个小的学生模型去部署。
最后分享一个小技巧:如果你不确定用哪个模型,先用YOLOv8n跑一个baseline,记录mAP和速度。然后换YOLOv8s,看mAP提升多少,速度降多少。如果mAP提升不到3个点但速度降了一半,那就不值得换。这个简单的对比能帮你快速做决策。