这是《目标检测学习笔记》第 02 篇。缘起:上一篇把 YOLO 和 DETR 两条路线的分歧讲清了,这一篇顺着读 RT-DETR 的源码(HGNetV2、AIFI、RTDETRDecoder),回答一个问题——DETR 的三笔债(收敛慢、编码器重、小目标差),RT-DETR 分别是怎么还的。
这一篇的核心一句话:RT-DETR 没有推翻 DETR,它只做了三件事——编码器做减法、查询做加法、监督做乘法。
本系列导航
- 01 YOLO 与 DETR:两条路线的分岔与汇合
- 02 从 DETR 到 RT-DETR:实时化改造的三板斧(本篇)
一、DETR 的三笔债
| 问题 | 原因 |
|---|---|
| 收敛慢(原版 500 epoch) | 匈牙利一对一匹配,正样本太少:图里 3 个物体就只有 3 个正样本,训练早期监督信号极度稀疏;且匹配每轮都在震荡 |
| 编码器重 | 原版对所有特征 token 做全局自注意力,O(N²),token 一多推理就慢 |
| 小目标差 | 只用 C5 单尺度特征(1/32 分辨率),小目标在这个尺度上只剩几个像素 |
RT-DETR 的目标:保留「端到端、无 NMS」的优点,把速度做到 YOLO 级别。三笔债对应三板斧。
二、第一板斧:编码器做减法——AIFI + CCFF
AIFI(Attention-based Intra-scale Feature Interaction,尺度内特征交互):只对最高层 S5做自注意力。S5 是 20×20 = 400 个 token,计算量可控;而且高层语义最完整,做全局交互收益最大。低层 P3/P4 分辨率高、token 多,做注意力算不起,而且它们更需要的是定位细节而非全局语义——这个取舍本身就是后续改进的切入点(比如给 P4 加一路轻量 AIFI)。
CCFF(跨尺度特征融合):就是把 YOLO 那套 FPN + PAN 搬了过来——自顶向下把语义传给低层,自底向上把细节传回高层。它和 YOLO 的 PAN/FPN 几乎同构,唯一区别是融合块:
| 结构位置 | YOLOv8 | YOLOv11 | RT-DETR |
|---|---|---|---|
| Concat 后的融合模块 | C2f | C3k2 | RepC3 |
RepC3 是重参数化模块:训练时多分支(类似 RepVGG),推理时把多分支卷积核融合成一条,部署零额外开销——这是 RT-DETR 从 YOLO 工程经验里直接借来的钱。
三、第二板斧:查询做加法——查询选择 + 可变形注意力
1. IoU-aware Query Selection:8400 选 300
原版 DETR 的 query 是随机初始化的可学习 embedding,训练初期两眼一抹黑。RT-DETR 的做法:
三路特征投影到 256 维,展平拼接 → 8400 个 token(80²+40²+20²) ↓ enc_score_head 给每个 token 打分 ↓ 取 top-300 作为初始 query 查询内容 = 被选中 token 的特征(不是随机 embedding) 参考框 = 初始 anchor + enc_bbox_head 预测的修正量查询从「盲人」变成了「带着先验知识的侦探」——训练一开始就知道去哪儿看,收敛自然快。
我卡过的一个问题:「编码器都能给每个 token 打分了,为什么还需要解码器?」答案:打分只是初筛(哪些位置像物体),解码器才是精修和消歧——同一位置可能有多个物体重叠、框需要逐层修正、query 之间还要互相商量「这个物体归谁」。初筛便宜,精修昂贵,分工不同。
2. 可变形注意力:O(N²) → O(N)
原版解码器的交叉注意力要对 8400 个 token 全量计算。可变形注意力的做法:每个 query 以参考框为中心,只采样少数几个点(每头每尺度 4 个点,8 头 × 3 尺度 = 96 个点,而不是 8400 个):
- 偏移量是学出来的——模型自己决定看参考框附近的哪几个位置;
- 用双线性插值取特征,再加权求和(权重也学出来);
- 多尺度天然融合:三个尺度各采 4 个点,相当于问「三个层级的证人」。
3. 逐层精修
解码器 6 层,每层做同一件事:采样 → 预测框偏移 → 更新参考框。第 1 层出粗框,第 6 层出精框;每一层都有辅助 loss,浅层学粗、深层学细,梯度也更充分。
顺带一个小知识:为什么框回归用 MLP(多层非线性)而分类只用一层 Linear?框偏移和特征的关系是非线性的,类别分数近似线性可分——把复杂度花在刀刃上。
四、第三板斧:监督做乘法——CDN 对比去噪训练
一对一匹配的正样本太少(3 个物体 = 3 个正样本),这个债怎么还?RT-DETR 借用 DN-DETR 的思路,人为造一批「已知答案的仿写题」:
取 GT 框 [0.5, 0.5, 0.3, 0.3] ↓ 加两类噪声(类别噪声 + 框噪声)→ [0.52, 0.47, 0.31, 0.28] ↓ 生成 100 个「去噪查询」,拼在 300 个正常查询前面 ↓ 解码器的任务:从噪声框还原出 GT ↓ 匹配预先确定,不需要匈牙利算法我最初的本能反应是:加噪声不是让任务更难吗,怎么会加速收敛?方向想反了——去噪查询带来的全是好处:
| 维度 | 正常查询 | 去噪查询 |
|---|---|---|
| 匹配 | 匈牙利算法,每轮震荡 | 预先确定,稳定 |
| 正样本数 | 3 个 | 100 个 |
| 作用 | 主任务 | 辅助任务:教会模型「框偏移怎么算」 |
类比:一对一是「把 3 道开放式大题」,去噪是「额外给 100 道「改错题」——答案就藏在题目旁边,学生(解码器)先靠改错题学会解题手法,再迁移到大题上」。工程细节:attn_mask 保证正常查询看不见去噪查询、各组去噪查询互不可见,否则等于偷看答案,训练就泄漏了。
五、训练与推理的不对称
| 维度 | 训练 | 推理 |
|---|---|---|
| 查询总数 | 300 + 100 = 400 | 300 |
| 去噪查询 | 有 | 无(dn_meta=None) |
| 解码器输出 | 每层都输出、都算 loss | 只用最后一层 |
| 匈牙利匹配 | 有 | 无 |
| 后处理 | 无 | 展平打分 → top-k → 过滤(无 NMS) |
推理时整条链路没有 NMS——这是 DETR 系和 YOLO 最本质的工程差异。
六、总表:三笔债、三板斧、三家对比
DETR 的债 vs RT-DETR 的还法:
| DETR 的问题 | RT-DETR 的解法 | 对应模块 |
|---|---|---|
| 编码器太重 | 只对 S5 做注意力,其余卷积融合 | AIFI + CCFF |
| 查询质量差、收敛慢 | 从 8400 个 token 里选 top-300 当查询 | IoU-aware Query Selection |
| 解码器注意力太贵 | 参考框附近稀疏采样,O(N²)→O(N) | 可变形注意力 |
| 一对一监督稀疏 | GT 加噪造 100 个已知答案的正样本 | CDN 对比去噪 |
| 小目标差 | C3/C4/C5 多尺度 | CCFF + 多尺度采样 |
YOLO vs RT-DETR 速查(把第一篇的对比推进到 RT-DETR):
| 维度 | YOLO(v8/v11) | RT-DETR |
|---|---|---|
| 后处理 | top-k + NMS | 无 NMS |
| 正样本分配 | TAL(一对多、动态) | 匈牙利一对一 |
| 损失 | BCE + CIoU + DFL | VFL + L1 + GIoU |
| Head | 逐网格密集预测 | 300 个稀疏 query |
| 编码器 | PAN/FPN | AIFI(仅 S5)+ CCFF |
| 骨干 | 自研 CSP 系 | HGNetV2 / ResNet |
最需要记住的几句话
- RT-DETR 的三板斧:编码器做减法(AIFI 只管 S5)、查询做加法(8400 选 300 + 可变形采样)、监督做乘法(去噪造正样本)。
- AIFI 只对 S5 做注意力的理由:token 少算得起、语义强收益大;低层要的是定位细节,交给卷积。
- 查询选择是初筛、解码器是精修——「编码器能打分为什么还要解码器」的答案是分工不同。
- 去噪训练不是把任务变难,而是造了一百道已知答案的改错题:匹配稳定、正样本 3→100+;attn_mask 防偷看答案。
- RepC3 的重参数化和「FPN+PAN 同构」说明:RT-DETR 是 DETR 的骨架 + YOLO 的工程经验。
我的复盘
读源码之前我以为 RT-DETR 是「把 DETR 加速」的一个工程 trick 集合;逐模块对完才发现每个 trick 都精确对应 DETR 的一笔债,改进不是堆料,是对问题的逐条偿还。我踩过最深的坑是去噪训练:直觉上「加噪声 = 更难 = 更慢」,但那是把「去噪」当成了主任务的负担——它是并行的辅助任务,题目自带答案。另一个顿悟是「编码器打分了为什么还要解码器」:打分是 O(N) 的初筛,精修是带交互的 O(K) 深加工,便宜的活和贵的活不能让同一个模块干。
到这里,「YOLO 路线 → DETR 路线 → RT-DETR 实时化」的主线就通了。后续如果继续这个系列,下一个自然是「照着这套逻辑去做自己的改进」——比如 P2 小目标支路、尺度感知 top-k,那就是从「读懂」到「改好」的故事了。