上一章《数据增强与迁移学习》讲"没数据也能练", 这一章接着问"在哪":分类网络的输出是"这是猫",检测网络的输出是一串框: "左上角 (12, 40)、右下角 (89, 156) 的地方有一只猫,置信度 0.94" (置信度 = 模型认为"这里真有目标"的把握,0 到 1)。
| 对比项 | 分类 | 检测 |
|---|---|---|
| 输出 | 一个类别(1000 选 1) | 4 个坐标 + 1 个类别 + 1 个置信度 |
| 输出空间 | 离散、有限 | 连续、无限(坐标可以是任意实数) |
| 核心难题 | 学一道分界线(决策边界) | 框的数量、位置、大小都未知 |
| 交叉熵 | 分类损失 + 回归损失(坐标差多少,常用 Smooth L1:误差小时像平方 MSE,大时像绝对值 MAE,和《损失函数》里的 Huber 是一回事;两项要联合优化,常见做法是直接相加) | |
| 主要指标 | 准确率 | mAP(既要框对、又要类别对、还要不多报) |
静态图 · 分类从有限个格子里挑一个,检测要吐一串任意实数
核心矛盾:回归坐标,训练时要能逐格对答案;但一张图里的目标数量是变化的—— 有的图有 1 个目标,有的有 30 个。而网络最后一层的输出个数是写死的,它没法吐出变长的列表。 所有检测框架的差别,都是"怎么把变长问题变成定长问题"的不同答案。
最直接的想法:在每个位置预先放几种固定形状的框(锚框 anchor), 让网络对每个框回答两个问题——"这里面有东西吗?"和"需要挪多少才能对准?"
这样输出就变成定长的了:如果撒了 20 万个锚框,网络就输出 20 万个"有无 + 偏移量"。 这些框的尺度和长宽比是人预先定的一串经验值(也可以在训练集真框上聚类——把相似的框自动分成几堆,每堆取一个代表形状; YOLOv2,2017 年 YOLO 的第二版,就这么做)。
互动 · 锚框与 IoU
IoU(交并比)= 两个框重叠的面积 ÷ 它们合起来的面积。
· 训练时怎么贴标签:不同方法阈值不同——Faster R-CNN 的 RPN 用 0.7 / 0.3,RetinaNet 用 0.5 / 0.4;
本页互动取 0.5 / 0.3(过 0.5 正样本、低于 0.3 负样本、中间忽略)。
一个真框没有锚框过 0.5 时,与它 IoU 最高的那个也转正。
· 评估时:预测框和真实框的 IoU 决定它算不算命中(常用门槛 0.5,严格的用 0.75)。
拖圆点盯着右边那个数:跨过 0.50 的一瞬间,框只挪几个像素,身份就从背景翻成正样本;
往回拖先掉进 0.3~0.5 的灰色地带,再变回背景。
一句话:IoU 是把"框得准不准"变成一个可以计算、可以比较的数字。
锚框撒出去之后,同一个目标周围会有几百个框都预测"这里有猫"。 必须去掉重复的。NMS 的做法极其简单粗暴:
互动 · 看 NMS 一步步清理
互动 · 把那个阈值从 0.9 拖到 0.2,看两只挨着的猫怎么被误杀
① 拥挤场景会误杀。如果有两只猫紧挨在一起,它们各自的框 IoU 很高,
NMS 会把其中一只删掉——这是"密集小目标"检测难的主要原因。
② 阈值很敏感。设太高(0.8)会留下大量重复框;设太低(0.2)会把不同目标也误删。
改进方案:Soft-NMS(不删,只降低邻居的置信度)、
DETR(用匈牙利匹配一次性配对,从根本上不需要 NMS)。
网络要学的不是「框在哪」,而是「从锚框改到目标要挪多少」。 这一节把它画成一张能拖的图:公式里每个符号,图上都有一块。
互动 · 把四个数凑出来,让锚框盖住目标(每一步都当场算)
公式 · 从锚框到预测框的这四个数,每一个在图上都有一块
先把 t_x、t_y 拖到能看到预测框(实线)压住真实框(暖色实线),
再把 t_w 拖回来——你会看到位置不用动,只要缩放变了,IoU 就又掉下去。
然后去第 2 节,把那个锚框拖到目标上:你刚才手拖的,就是这四个数在动。
网络做的不是「猜一个坐标」,而是把四个偏移量一点点逼近它们该有的值——
所以它最终能对多准,是被「锚框撒得够不够密」卡住的。
| 对比项 | 两阶段(Two-Stage) | 单阶段(One-Stage) |
|---|---|---|
| 第一步 | 先粗筛出几千个"可能含目标"的区域(RPN:专门干这件事的小网络) | 一次前向直接输出所有框 |
| 第二步 | 对每个候选区域做精细分类和回归 | 无 |
| 速度 | 慢(Faster R-CNN 约 5 FPS,即每秒 5 张图) | 快(YOLO 可到 100+ FPS) |
| 精度 | 小目标、密集目标更好(2017 年前后的对比结论) | 在 2017 年同期的对比里,单阶段在大、中目标上追平了两阶段;后来两边各有新版本互有胜负 |
| 代表 | R-CNN → Fast → Faster → Mask R-CNN | YOLO 系列、SSD、RetinaNet、FCOS |
| 今天主流 | 特定高精度场景 | 绝大多数实际部署 |
互动 · 把容易样本的权重压到多小,才是「单阶段逆袭」的关键一步
单阶段一开始精度差,主要原因是正负样本极度不平衡:
20 万个锚框里可能只有 10 个真框,也就是 99.995% 都是"背景"。
全猜"背景"就有 99.995% 准确率,于是学不到东西。
Focal Loss 的做法:给每个样本的损失乘一个系数:预测越有把握,系数越小。
系数就是 (1 − p)^γ,p 是模型给正确答案的概率,γ 控制压得多狠;
p = 0.9、γ = 2 时系数是 0.01,所以 0.105 × 0.01 ≈ 0.0011。
这就把训练注意力从"一万个明显的背景"挪到了"几个真正难的框"上,
单阶段也就是靠它追上了两阶段。这是 RetinaNet 论文的核心贡献。
两条路线谁更强,要靠一个分数来判。检测的分数就是 mAP。
| 概念 | 含义 |
|---|---|
| IoU 阈值 | 预测框和真实框的 IoU 超过这个值,才算"检测到了" |
| 混淆矩阵里的三个格子:TP = 框对且类别对;FP = 多出来的框或类别错;FN = 漏掉的真框 | |
| TP / (TP + FP) —— 你报出来的框里有多少是对的 | |
| Recall 召回率 | TP / (TP + FN) —— 真实目标里你找出了多少 |
| AP 平均精度 | 对某一个类别,画出 Precision-Recall 曲线,取曲线下面积 |
| mAP | 所有类别的 AP 求平均。这是检测任务最常用的综合指标 |
AP 这样量出来:把预测框按置信度从高到低排好,一个一个往下数。 数到第 k 个时,「报出的框里对了几个」占 k 的比例是准确率(Precision), 「找回的真目标」占总数的比例是召回率(Recall)。 把这些点连起来就是 PR 曲线,曲线下面积就是 AP。
怎么算「配上对」:预测框按置信度从高到低,每个去找 IoU 最高、过了阈值、还没被配过的真框。 配上的算命中(TP),配不上的算误报(FP),没人配的真框算漏掉(FN)。 下面这批 12 个预测对 12 个真目标,阈值 0.5 时命中 11、误报 1——量出面积就是 AP = 0.882。
互动 · 同一个模型的同一批预测,换一个交并比阈值,平均精度就变一个数
论文里的 mAP@0.5 和 mAP@0.75 差别可能非常大。
早期论文习惯报 @0.5(框住大致位置就算对),但太宽松——只框住目标一半也能拿到 0.5。
所以现在更看重 mAP@[.5:.95](COCO 数据集的主指标,10 个阈值平均)。
看论文先确认用的是哪个阈值。
| 难点 | 为什么难 | 什么时候真的会痛 → 换什么 |
|---|---|---|
| 标注成本极高 | 分类只要一个标签,检测要画框(真框由人工标,存成 COCO JSON 这类格式;标错一个就教模型框错一个)。一张图可能有上百个框,画框比点标签慢得多 | 要攒几十万张图 → 半自动预标注(先用现成模型框一遍,人只改错的),或先分类后检测(先判断图里有没有目标,再对有的图做检测) |
| 尺度变化 | 同一张图里,小目标和占满画面的大目标混在一起;特征金字塔(FPN:把不同深度的特征图叠起来用)就是为这个 | 小目标被漏掉 → 提分辨率 / 加 FPN / 大图切片检测 |
| 密集场景 | NMS 在拥挤场景会误杀,这是结构性缺陷 | 人群、货架、细胞这类场景 → 调 NMS 阈值、换 Soft-NMS,或直接用 DETR 这类无 NMS 的模型 |
| 长尾类别 | 常见类别(人、车)数据多,罕见类别(某种工具)数据极少,mAP 被长尾拖累 | 要报一个平均分 → 分别看每一类 AP,别只看 mAP |
| 评估本身很绕 | 要同时权衡"框的准确度""类别正确""不多报""不漏报",任何单一数字都会误导 | 要用一个数下结论 → 同时报不同 IoU 阈值下的数,看清它怎么算的 |
互动 · 一张图里到底撒了多少个锚框,以及为什么九成九是背景
另一边是 DETR:2020 年的它把检测当成集合预测,用匈牙利匹配(一次性给每个预测框配一个真框的算法) 直接把两边配对,从头到尾不需要锚框和 NMS。刚出来时收敛慢、小目标差,后来 DINO(2022)、RT-DETR 追上了 YOLO。 它是检测里第一个端到端用 Transformer 的方法,比分类那边的 ViT 早五个月(Transformer 后面章节讲)。
回到第 2 节那张锚框图,把圆点拖到真实目标上,看右边的 IoU 从 0.05 涨过 0.5—— 跨过那条线的一瞬间,右下角那段话从“通常会被忽略”换成“会被标为正样本”, 网络开始为这个框算损失。
你看到的就是“对错不是事实,是阈值”。框的位置只挪了几个像素, 它的身份却从背景变成了正样本。再挪回去一点,又变回背景。
| 暗线 | 这一章的回答 |
|---|---|
| A 信息流动 | 输入 (H, W, 3) → 骨架网络(提特征的主干,常是 ResNet)每下采样一次尺寸减半,给出 H/8~H/32 大小的特征图(最后几层分别是原图的 1/8 到 1/32) → 每个位置撒 A 个锚框(A 是每个位置撒几个) →
输出 A×(1+类别数+4) 个数。出口是变长框列表(NMS 后 1~100 个),全站唯一「单次前向张量长度」不固定的任务。 定长化靠两步:撒锚框,把「有几个目标」变成固定数量的提问;再用 NMS 收成变长列表 |
| B 什么被牺牲了 | 牺牲了可导性和定位精度的上限。坐标从“绝对位置”改成“相对锚框的偏移量”,
精度被锚框形状和数量卡住;NMS 是纯几何规则,训练时不在计算图里,两个目标只能分开处理。 这笔债直到 DETR 用匈牙利匹配把“去重”变成训练的一部分才还上(回看第 6 节) |
| C 参数账本 | 检测头(输出框和类别的那几层)很轻,骨架是绝对大头:
ResNet-50 骨架 23.5M 参数,检测头只有几 M。锚框本身是 0 个参数——写死在代码里的几何数字。 张量账更贵:20 万个锚框在 COCO 的 80 类下要输出 20万 × (1+80+4) ≈ 1700 万个数,fp32 就是 68 MB; FPN 通常用 3~5 个尺度,账单还要乘 3~5。检测的显存大头是密集预测,不是参数 |
| D 跑在什么上 | 前向是骨架卷积,算术强度高、GPU 吃得很香;可末尾的 NMS 要全局排序 + 逐个比 IoU,难并行,
实时检测最贵的动作常常是“把几万个框排序再比较”,不是卷积——嵌入式设备上先卡住的常常是后处理。 另一半:锚框让正负样本在 |
| E 它假设了什么 | 假设目标能被一组预定义好的形状覆盖(锚框的尺度与长宽比)。
这条假设在自然图像上大致成立,换成“长条形的缺陷”“极细的血管”就不成立——
因为长宽比分布根本不在那几个预设里。 第二层假设更隐蔽:NMS 假设“同一个目标上那个框得更好的预测,置信度也更高”。 拥挤场景下这条假设会直接失效,这才是 Soft-NMS 存在的理由 |
| F 违背了哪个直觉 | 两条。 ① 99.995% 准确率的模型毫无用处——把 20 万个锚框全判成“背景”就能拿到这个数 (20 万个里只有约 10 个真框),但它一个目标也没找到。这就是“类别极不平衡”的算术真相。 ② “框得准不准”不是一个事实,而是一个阈值。同一个预测框, 在 mAP@0.5 下算命中,在 mAP@0.75 下算漏检。看检测论文时最大的误导就来自这里 |
它接住了上一章的什么:《数据增强与迁移学习》开头那句 “ImageNet 花了两年多、几万人、上百万次人工标注”——检测就是那个“贵”的具体形状: 分类只要点一下,检测要画框(本页第 6 节那张表)。
它给后面留了什么:检测给出来的是一个粗糙的矩形。 如果连轮廓都要,就得进到像素级——那是 《语义分割》。 而 NMS 那个不可导的环节,最终被 DETR 的匈牙利匹配绕开了。
检测比分类多的那件事是"在哪",而"在哪"是连续的、数量可变的——这就是全部难点的来源。
锚框把变长问题变成定长问题;IoU 把"准不准"变成可计算的数字;
NMS 去重(但在拥挤场景会误杀);Focal Loss 解决了正负样本极度不平衡,
让单阶段追上两阶段。
看论文时注意 mAP 用的是哪个 IoU 阈值——@0.5 和 @0.75 差别巨大。
上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。