3、目标检测基础理论:任务定义、IoU与NMS、Anchor机制、损失函数
各位同学,欢迎来到第三章。从这章开始,我们正式进入目标检测的核心地带。
很多人一上来就撸YOLO代码,结果调参调到怀疑人生。为什么?说白了,就是基础理论没吃透。今天这章,我带你把这四个地基打牢:任务定义、IoU与NMS、Anchor机制、损失函数。这些东西搞明白了,后面写代码你会觉得“哦,原来如此”。
3.1 目标检测任务定义:不只是“找东西”
图像分类,就是给一张图打标签,比如“这是一只猫”。但目标检测要干两件事:分类 + 定位。你得告诉我图里有什么,还得用框把它圈出来。
我习惯把检测任务拆成两个子问题:
- 分类子任务:判断框里是猫、狗还是背景。
- 定位子任务:回归出框的四个坐标 (x, y, w, h)。
你想想看,一张监控画面里可能有行人、车辆、自行车。模型得同时输出每个目标的类别和位置。这就是检测和分类最本质的区别。
3.2 IoU与NMS:检测的“质检员”和“清道夫”
3.2.1 IoU:交并比
IoU,全称Intersection over Union。说白了,就是两个框的重叠程度。
计算公式很简单:
IoU = (A ∩ B) / (A ∪ B)
分子是两个框的交集面积,分母是并集面积。IoU值在0到1之间,越接近1,说明两个框重合得越好。
在项目中,我常用IoU做三件事:
- 评估检测质量:预测框和真实框的IoU > 0.5,就算检测正确。
- 正负样本分配:IoU > 0.7的Anchor算正样本,< 0.3的算负样本。
- NMS去重:IoU > 阈值就干掉一个。
3.2.2 NMS:非极大值抑制
模型预测时,同一个目标会输出一堆候选框。NMS的作用就是去重,只保留最好的那个。
算法流程其实很直观:
- 把所有框按置信度从高到低排序。
- 选置信度最高的框,保留它。
- 计算它和其他框的IoU,干掉IoU大于阈值(比如0.5)的框。
- 重复步骤2-3,直到没有框剩下。
嗯,这里要注意:阈值设得太小,容易漏检;设得太大,又会有重复框。我一般从0.5开始调,根据实际效果微调。
3.3 Anchor机制:给模型“打个样”
Anchor,也叫先验框。为什么需要它?
你想想看,如果让模型直接回归任意形状的框,太难了。模型不知道“框大概长什么样”。Anchor就是预设好的一组框,模型只需要在Anchor的基础上做微调。
YOLOv3/v5用的Anchor是怎么来的?
- K-means聚类:在训练集上,对所有真实框的宽高做聚类。
- 9个Anchor:3个尺度 × 3个长宽比,覆盖大、中、小目标。
- 每个特征图格子:预测3个框,每个框对应一个Anchor。
| 特征图尺度 | 感受野 | Anchor尺寸(举例) | 适合目标 |
|---|---|---|---|
| 大(13×13) | 大 | (116,90), (156,198), (373,326) | 大目标(车辆) |
| 中(26×26) | 中 | (30,61), (62,45), (59,119) | 中目标(行人) |
| 小(52×52) | 小 | (10,13), (16,30), (33,23) | 小目标(远距离) |
3.4 损失函数:模型学习的“指挥棒”
损失函数告诉模型:你错在哪,错得有多严重。YOLO的损失函数由三部分组成:
Loss = λcoord * Lossbox + Lossobj + Losscls
3.4.1 定位损失(Loss_box)
早期YOLO用MSE(均方误差),后来换成GIoU、CIoU。为什么?
- MSE:对尺度敏感,大框和小框的误差量级不一样。
- GIoU:在IoU基础上,考虑了框不相交时的梯度问题。
- CIoU:进一步加入中心点距离和长宽比,收敛更快。
我个人推荐用CIoU。在安防场景下,行人框的长宽比变化很大,CIoU能更好地处理这种情况。
3.4.2 置信度损失(Loss_obj)
判断格子内是否有目标。用二值交叉熵(BCE Loss)。
这里有个坑:正负样本极度不平衡。一张图里可能只有几个目标,但背景格子成千上万。YOLO用了Focal Loss的思想,给难分样本更大的权重。
3.4.3 分类损失(Loss_cls)
多标签分类,也是BCE Loss。注意,YOLO对每个类别独立做二分类,而不是Softmax多分类。为什么?因为一个框可能同时属于“行人”和“骑自行车的人”,Softmax会强制互斥。
3.5 本章小结
这一章我们聊了四个核心概念:
- 任务定义:检测 = 分类 + 定位。
- IoU与NMS:评估重叠度,去重保最优。
- Anchor机制:预设先验框,降低回归难度。
- 损失函数:三部分损失,指挥模型学习。
这些理论,是后面所有实战章节的基石。你可能会觉得有点抽象,没关系,等我们开始写代码、跑模型时,你会反复和它们打交道。到时候再回来看这章,会有更深的理解。