第 6 章 · NMS 与端到端检测——去掉最后一个后处理瓶颈
开场:照片去重的烦恼
你出去旅游拍了 500 张照片,回来整理时发现:同一个景点从同一个角度拍了 5-6 张几乎一样的照片。你需要从中挑出最好的一张,删掉其余的。这个”去重”过程就是目标检测中 NMS 的日常类比。
检测模型在 8400 个位置做预测后,同一个目标可能被多个位置检测到——产生了一堆重叠的框。NMS(Non-Maximum Suppression,非极大值抑制)就是负责”从重叠的框中只保留最好的那个”的后处理步骤。
听起来很简单,但这个简单的步骤却是目标检测流水线中最大的”不优雅”之处——它不可微、有超参数、延迟不确定、在边缘场景下表现脆弱。消除 NMS,实现真正的端到端检测,是检测领域过去几年最活跃的研究方向之一。
NMS 到底在做什么?
标准 NMS 算法
输入:N 个检测框,每个有 (x1, y1, x2, y2, score, class)
参数:IoU 阈值 T(通常 0.45-0.7)
输出:过滤后的 M 个框(M << N)
算法:
1. 按分数(score)从高到低排序
2. 取出分数最高的框 B_best,加入最终结果
3. 计算 B_best 和所有剩余框的 IoU
4. 删掉 IoU > T 的框(它们被认为检测的是同一个目标)
5. 重复 2-4,直到没有剩余框
用代码来看:
def nms(boxes, scores, iou_threshold=0.45):
"""标准 NMS 实现"""
# 按分数从高到低排序
order = scores.argsort(descending=True)
keep = [] # 保留的框索引
while order.numel() > 0:
i = order[0] # 取出当前最高分的框
keep.append(i)
if order.numel() == 1:
break
# 计算最高分框和所有剩余框的 IoU
ious = compute_iou(boxes[i], boxes[order[1:]])
# 只保留 IoU < 阈值的框(不同目标的框)
mask = ious < iou_threshold # ← 关键:IoU 超过阈值的被删掉
order = order[1:][mask]
return keep
一个具体的例子
假设图中有一只猫,模型产生了 5 个框:
框 A: score=0.95, IoU(A,GT)=0.88 ← 最好的框
框 B: score=0.87, IoU(B,GT)=0.82 ← 重叠框
框 C: score=0.72, IoU(C,GT)=0.75 ← 重叠框
框 D: score=0.60, IoU(D,GT)=0.30 ← 另一个位置的低分框
框 E: score=0.45, IoU(E,GT)=0.91 ← 高 IoU 但低分
NMS 过程(阈值 0.5):
Step 1: 取 A (0.95)。A 和 B 的 IoU=0.78 > 0.5 → 删 B
A 和 C 的 IoU=0.65 > 0.5 → 删 C
A 和 D 的 IoU=0.20 < 0.5 → 保留 D
A 和 E 的 IoU=0.85 > 0.5 → 删 E
Step 2: 取 D (0.60)。无剩余框。
结果: 保留 [A, D]
NMS 的五大问题
问题一:IoU 阈值是超参数
IoU 阈值需要手动设定,而且对检测质量影响很大。阈值太高(如 0.7):相邻的不同目标可能被误杀(两个紧挨着的人的框 IoU 可能超过 0.7)。阈值太低(如 0.3):同一目标的重复框保留下来太多。没有一个”对所有场景都好”的阈值。
问题二:不可微
NMS 涉及排序、比较、删除等离散操作,无法传播梯度。这意味着NMS 无法和模型一起端到端训练。模型学到的是”怎么产生好的框”,但它无法学到”怎么避免产生重复的框”——因为 NMS 把这个任务从训练中隔离了。
问题三:推理延迟不确定
NMS 的时间复杂度取决于保留的框数——目标越多,NMS 越慢。在密集场景(如一群鸟、拥挤人群)下,NMS 的耗时可能超过模型推理本身。这对实时系统很不友好——你无法保证每一帧的延迟都在限定范围内。
稀疏场景(5 个目标): NMS 耗时 ~0.5ms
中等场景(50 个目标): NMS 耗时 ~5ms
密集场景(500 个目标): NMS 耗时 ~50ms ← 可能超过模型推理时间
问题四:密集遮挡时的误杀
当两个不同目标严重重叠时(比如一个人抱着另一个人),它们的框 IoU 可能很高。NMS 会把其中一个删掉——这是真实的漏检,不是重复框。
问题五:串行计算
标准 NMS 是一个贪心算法,必须串行执行(每一步依赖上一步的结果)。在 GPU 上不好并行化。虽然有 batched NMS 等优化,但本质上仍然是串行逻辑。
NMS 的改进方案
在完全去掉 NMS 之前,学术界尝试了多种 NMS 的改进。
Soft-NMS
Soft-NMS 不直接删掉重叠框,而是根据 IoU 按比例降低它们的分数。IoU 越高,分数衰减越多。这样在密集场景下,被遮挡目标的框不会被完全删掉,只是分数变低——如果它本身分数够高,仍然能保留。
# Soft-NMS: 不删除,只降分
for remaining_box in remaining:
iou = compute_iou(best_box, remaining_box)
# 高斯衰减:IoU 越高,分数衰减越多
remaining_box.score *= exp(-iou^2 / sigma)
DIoU-NMS
标准 NMS 用 IoU 判断两个框是否”指向同一个目标”。DIoU-NMS 改为同时考虑 IoU 和两个框中心点的距离——如果两个框虽然 IoU 高但中心点差很远,可能是不同的目标,不该删掉。
问题依然存在
这些改进缓解了 NMS 的一些问题,但没有从根本上解决:NMS 仍然是一个不可微的后处理步骤,仍然有超参数,仍然无法和模型端到端训练。
方案一:YOLOv10 的 NMS-Free——双头策略
核心思路
YOLOv10 的方案在上一章已经介绍了正样本分配的部分。从 NMS 的视角来看,它的核心思路是:
让模型在训练时就学会”不产生重复框”,这样推理时就不需要 NMS 来去重。
具体做法:用两个检测头(one2many 和 one2one)同时训练。one2many 用 TAL 分配,提供丰富的梯度信号让 backbone 学好特征。one2one 用匈牙利匹配,强制每个目标只有一个预测,学会”自己去重”。
训练时的损失函数:
Total Loss = Loss_one2many (TAL) + Loss_one2one (Hungarian)
↓ ↓
教模型"检测" 教模型"去重"
推理时:
只用 one2one 分支的输出 → 天然无重复 → 不需要 NMS
实际效果
YOLOv10 在 COCO 上的精度和 YOLOv8 相当,但推理延迟更低(省掉了 NMS 的时间)且完全确定(不受目标数量影响)。在目标数量很多的场景下优势更明显。
局限性
双分配策略的训练成本更高(额外的 one2one 分支和匈牙利匹配计算),而且 one2one 分支在极密集场景下的表现仍有待验证。
方案二:RT-DETR 的天然 NMS-Free——Set Prediction
集合预测范式
DETR 系列从一个完全不同的角度解决了这个问题。DETR 不是”先产生大量框再去重”,而是直接”产生恰好数量的框”。
传统 YOLO 思路:
8400 个位置都预测 → 大量重复框 → NMS 去重 → 最终结果
DETR 思路:
300 个 learnable query → 每个 query 负责"找"一个目标 → 匈牙利匹配 → 最终结果
300 个 query 已经比目标数量多很多,无需 NMS
这里的关键概念是 query:DETR 有一组可学习的向量(比如 300 个),每个向量通过 Transformer Decoder 的 cross-attention 去”查询”图像特征,输出一个检测结果。训练时用匈牙利匹配保证一对一分配,模型自然学会”每个 query 只负责一个目标”。
RT-DETR 的具体实现
# RT-DETR Decoder 简化版
class RTDETRDecoder(nn.Module):
def forward(self, memory, query_embed):
# memory: Encoder 输出的图像特征
# query_embed: 300 个查询向量
# IoU-aware Query Selection: 从 memory 中选 Top-300 个质量最好的特征
# 比 learnable query 更好——有了具体的图像信息做初始化
for layer in self.decoder_layers: # 6 层 Decoder
# Self-Attention: query 之间互相通信
# → "你已经检测了那个人,我就不重复检测了"
query = layer.self_attn(query)
# Cross-Attention: query 和图像特征交互
# → "根据图像特征,更新我的检测结果"
query = layer.cross_attn(query, memory)
# 输出 300 个预测,每个包含类别 + 框
return self.class_head(query), self.bbox_head(query)
关键洞察:Decoder 中的 Self-Attention 让 query 之间可以”沟通”——如果 query_5 已经在检测目标 A,query_12 通过 self-attention 会”知道”这件事,从而避免重复检测目标 A。这是 DETR 天然 NMS-Free 的根本原因——去重逻辑被内化到了模型的注意力机制中。
DETR 方案的 Trade-off
优势:
- 架构最优雅——完全端到端,无手动设计的后处理
- 推理延迟完全确定
- 天然适合需要集合输出的任务(全景分割、多目标跟踪)
劣势:
- 训练收敛慢(匈牙利匹配 + 稀疏训练信号)
- 需要多层 Decoder(6 层)才能有效去重
- Transformer 的计算量在小模型场景下不占优势
- Deformable Attention 的 CUDA 实现限制了部署平台
YOLO-Master 的 NMS 现状与未来可能
当前状态
YOLO-Master 目前仍然使用 NMS 后处理——和 YOLOv8 一样。这是因为 YOLO-Master 的核心创新在 MoE 自适应计算,而非后处理。
MoE 为 NMS 替代提供的可能性
YOLO-Master 的 MoE 架构为”自适应去重”提供了一个有趣的可能方向:
设想:MoE 辅助去重
路由器在做"判断目标难度"时,已经在感知空间布局
→ 理论上可以让某些专家专门负责"去重"
→ 或者让路由信息指导 NMS 的阈值选择
→ 或者在 MoE 后面接一个轻量的 one2one 头
这还是一个开放的研究方向——把 MoE 的路由信息用于去重,可能比纯匈牙利匹配的方案更高效(因为不需要 O(N^3) 的匹配)。这是犀牛鸟项目中一个高难度但高价值的潜在贡献方向。
NMS-Free 方案的全景对比
| 维度 | 标准 NMS | Soft-NMS | YOLOv10 双分配 | DETR 集合预测 |
|---|---|---|---|---|
| 方法类别 | 后处理 | 后处理 | 模型内置 | 模型内置 |
| 可微性 | 否 | 部分 | 是 | 是 |
| 超参数 | IoU 阈值 | IoU 阈值 + sigma | 无 | 无 |
| 延迟确定性 | 不确定 | 不确定 | 确定 | 确定 |
| 密集场景表现 | 可能误杀 | 更好 | 待验证 | 良好 |
| 训练复杂度 | — | — | 增加 | 增加(收敛慢) |
| 推理开销 | 额外时间 | 额外时间 | 无额外 | 无额外 |
端到端检测的意义
“去掉 NMS”不仅仅是一个工程优化——它代表了目标检测范式的一次根本性转变。
从”检测 + 后处理”到”端到端检测”,意味着:
模型的训练和推理在同一个计算图中完成,所有步骤都是可微的,可以端到端优化。这就像自动驾驶从”分模块开发(感知→规划→控制各自独立优化)”到”端到端学习(一个模型从像素到方向盘)”的转变——虽然更难训练,但上限更高。
对于犀牛鸟参赛者,理解 NMS 和端到端检测的 trade-off,能帮你更好地定位 YOLO-Master 的改进方向——是在 MoE 框架内实现去 NMS?还是把 NMS 优化留给 Ultralytics 的通用层?这取决于你选择的贡献路线。
初学者常见误区
误区一:”NMS 是一个过时的技术,应该尽快抛弃”
→ 正确理解:NMS 虽然有诸多问题,但在大多数实际场景中仍然工作得很好。YOLOv8 + NMS 在 COCO 上的 mAP 与 YOLOv10(无 NMS)相当。NMS 的问题主要在延迟不确定性和密集遮挡场景——如果你的应用不涉及这些场景,NMS 完全够用。工程中”能用且简单”往往比”优雅但复杂”更有价值。
误区二:”端到端 = 更高精度”
→ 正确理解:端到端检测的优势不在精度,而在架构简洁性和推理延迟确定性。在 COCO 标准 benchmark 上,NMS-free 方案的精度和 NMS 方案基本持平(甚至略低)。端到端的真正价值在于:可以和下游任务(跟踪、分割)无缝衔接、推理延迟完全确定、不需要调 NMS 超参数。
误区三:”DETR 的 Self-Attention 完美解决了去重问题”
→ 正确理解:DETR 需要 6 层 Decoder 才能有效去重,前几层仍然会有重复预测。而且 Self-Attention 的复杂度和 query 数量的平方成正比——query 数量太少会漏检,太多会增加计算量。当前 DETR 系列通常使用 300 个 query,这对 COCO(平均 7 个目标/图)来说绰绰有余,但对极密集场景(如人群计数、卫星图像)可能不够。
读完本章你能做什么
读完这一章,你应该能够:
- 用自己的话描述 NMS 的算法流程
- 列举 NMS 的 5 个核心问题
- 比较 YOLOv10(双分配)和 RT-DETR(集合预测)两种 NMS-free 方案
- 理解 DETR 中 Self-Attention 如何实现”去重”
- 说出 YOLO-Master 在 NMS 方面的当前状态和潜在改进方向
到此为止,我们已经建立了目标检测的完整基础知识。接下来进入 Part 3——逐个深入赛道内的 5 个核心项目。让我们从工业标杆 Ultralytics 开始。
导读目录:README.md 上一章:第 5 章 · 正样本分配 下一章:第 7 章 · Ultralytics (YOLOv8)