☰
DETR 端到端目标检测:Transformer 如何取消 Anchor 与 NMS
2026/10/15 10:25:55 网站建设 项目流程

简介:这套PPT是围绕计算机视觉经典论文 DETR(DEtection TRansformer)整理的文献汇报课件,适合需要做组会分享的研究生、目标检测方向初学者及相关从业者。内容从研究背景展开,先对比RCNN系列和YOLO系列依赖 Anchor、NMS 等手工组件的传统检测流程,再引入 DETR 将目标检测视为直接集预测的核心思想;课件详细拆解了 CNN 特征提取、Transformer 编码器-解码器、Object Queries、二分图匹配损失与 FFN 预测等关键环节,也说明了模型可扩展到全景分割任务的应用价值。资源共1个文件,文件类型为 pptx,压缩包大小约3.44MB,精炼完整,适合直接作为组会汇报底稿或论文阅读笔记。目前已有1089人学习/浏览。通过学习该课件,可以系统理解 DETR 的端到端检测思路和训练机制,同时节省整理原论文图表与公式的时间,为后续研究 Transformer 在视觉任务中的应用打下基础。

1. 这篇 End-to-End Object Detection with Transformers 论文汇报,把机器学习里 DETR 最反直觉的一件事讲清楚了

目标检测不一定非要从一张图上铺满先验框开始。这篇 End-to-End Object Detection with Transformers 文献汇报,核心只讲了一件事:DETR 怎么把检测当成集合预测问题,直接从输入到输出端到端地给出框和类别,彻底绕开 Anchor 设计、NMS 这种手工组件。汇报按研究背景、研究目的、研究过程、结论四段展开,每一段都对应原论文的关键章节,读完既能向别人解释 DETR 为什么能省掉那些模块,也能按图索骥进入复现流程。它适合准备组会分享的研究生、刚接触检测方向的新手,也适合从 YOLO 或 Faster R-CNN 迁移过来、想搞清楚 Transformer 派对检测管线到底动了什么的人。这份材料的价值在于把模型结构、训练匹配、损失设计和实验消融连成了一条完整线索,而不是把论文贴一遍。

2. 架构三件套:CNN 特征压缩、Transformer 编解码与 FFN 预测头的配合逻辑

DETR 的模型结构拆开看,只有三部分:CNN backbone、Transformer encoder-decoder、FFN 预测头。很多人第一眼只记住「用了 Transformer」,但真正决定能不能复现的,是 CNN 和 Transformer 之间的数据接口——一张图怎么变成序列、序列长度是多少、通道数压到多少、位置信息靠什么补上。这一章就把这条链路一步步拆开。

2.1 为什么还要 CNN:从 2048 通道特征图到序列化的第一步

先回答一个常见疑问:既然要做端到端,为什么不直接让 Transformer 吃原始像素?因为自注意力的计算量随 token 数量平方增长,原图 800×600 就有 48 万个像素,两两做 attention 在工程上完全跑不动。CNN 的作用是把空间尺寸压下来:常见主干网络输出特征图相对原图缩小 32 倍,800×600 的输入到这里变成 25×19 左右的空间分辨率,token 数量从几十万降到几百,Transformer 才算得动。同时主干网络输出 2048 个通道,每个通道提取了不同的图像特征。

但 Transformer 期望输入是序列,所以要先把四维特征图转成二维矩阵。常见做法是先用 1×1 卷积把通道从 2048 压缩到 d,再把高和宽合并成一个维度。下面这段代码就是这个桥接过程的 PyTorch 示意:

import torch import torch.nn as nn class BackboneToSequence(nn.Module): def __init__(self, in_channels=2048, d_model=256): super().__init__() # 1x1 卷积只做通道混合,不改变空间尺寸 self.proj = nn.Conv2d(in_channels, d_model, kernel_size=1) def forward(self, feat: torch.Tensor): # feat: (B, 2048, H, W) 来自 CNN backbone B, C, H, W = feat.shape x = self.proj(feat) # (B, 256, H, W) x = x.flatten(2) # (B, 256, H*W) x = x.permute(2, 0, 1) # (H*W, B, 256) return x

这里的 flatten(2) 把空间维度 H 和 W 合并成一个 HW 维,顺序是先沿 W 再沿 H 展开,后边加位置编码时要保持这个顺序和原始特征图坐标一一对应。permute 把序列长度放到第一维,是为了贴合 Transformer 常用的 seq_len, batch, d_model 布局。d_model 取 256 是原论文比较常用的配置,显存紧张时也可以降到 192,但 encoder 输出维度会随之变小,最终 AP 会略掉一些。1×1 卷积在这里不做残差、不加激活,只是纯粹的通道压缩。

2.2 Encoder:全局自注意力如何替代滑动窗口先验

Encoder 接收的是加了位置编码的序列。位置编码是必须的,因为 reshape 之后特征图的排列顺序包含空间位置信息,但自注意力本身是顺序无关的,如果不把位置信息显式告诉模型,它会把左上角和右下角的特征当成完全等价的 token。常见做法是加正弦位置编码,也有用可学习位置编码的版本。DETR 里位置编码不只是加在最前面,还会在 encoder 每一层的注意力输入里补充一次,让每一层都能感知空间关系。

有了位置编码以后,encoder 内部每个 token 都会和整张特征图的所有 token 计算注意力权重。这一步就是「全局建模」:某个位置的像素可以直接看到远处另一个目标的像素,而不需要靠滑动窗口一步步扫过去。滑动窗口的问题是感受野受限,先验框只能覆盖局部,目标之间、目标与背景之间的长程依赖很难在一次前向中建立;而全局自注意力相当于把整幅图的所有位置两两建边,实例之间的区分信息在 encoder 阶段就完成了。这也是 DETR 能去掉 Anchor 的底层原因——Anchor 本质上是用密集先验去覆盖目标可能出现的位置,而 Transformer 用全局 attention 直接建立「这个位置和那个位置是不是属于同一个目标」的关系。

2.3 Decoder:object queries 并行解码与 FFN 输出的固定 N 个预测

Decoder 和标准 Transformer decoder 的差别在于:DETR 在每个解码层并行解码 N 个目标,不是自回归式逐个生成。N 在论文里通常取 100,意思是模型最多预测一张图中的 100 个目标。object queries 是 N 个随机初始化然后训练出来的向量,每个 query 通过训练学到一种「职责」:有的负责询问图像左下角区域,有的负责中间区域。它们在 decoder 中和 encoder 输出的特征做 cross-attention,并在 N 个 query 之间做 self-attention,从而让模型在整图上下文里统一推理所有目标。

import torch.nn as nn num_queries = 100 # N,最大预测目标数 d_model = 256 # object queries 是可学习参数,不是人为设计的先验框 object_queries = nn.Parameter(torch.randn(num_queries, d_model)) # 每个 batch 的输入 shape 都是 (num_queries, batch, d_model) tgt = object_queries.unsqueeze(1).repeat(1, batch_size, 1)

object_queries 初始值是随机噪声,训练后每个向量学会去「询问」图像中某类区域。tgt 在 decoder 里作为第一层注意力输入的 query 侧,encoder 的输出作为 key 和 value 侧。这里最需要注意的是:这些 queries 不是 Anchor,它们不含有预设的宽高比或位置偏移,所有框信息都是在 decoder 的 cross-attention 中从图像特征里取出来的。

每个 query 经过 decoder 后得到一个输出 embedding,再送入 FFN 预测头。FFN 由三层组成:一个 ReLU 激活层、一个 d 维隐藏层、一个线性投影层。线性层输出类别概率和框坐标,框坐标是标准化后的中心点位置和宽高。由于 N=100 远大于图像中实际目标数量,FFN 还需要预测一个特殊类 ∅,表示这个槽位没有检测到对象,效果类似传统检测里的 background 类。

3. 训练内核:二分匹配、匈牙利算法与组合损失

推理时 DETR 一次前向就输出 N 个预测框,但训练时不能直接拿这 N 个框去和 GT 算 loss——因为模型不知道第一个框对应的是图像里的哪只猫,也不知道剩下那些「空槽位」该匹配谁。所以训练和推理之间横着一个关键步骤:先用二分匹配把预测和 GT 一一对应,再算损失。

3.1 固定 N 个预测与动态数量 GT 怎么对齐

DETR 的预测集合固定大小是 N,但每张图的 GT 数量是变化的,比如一张图只有 3 个目标,另一张图有 20 个。为了让两个集合能比较,训练时要先把 GT 补齐到 N:真实目标维持原类别和框坐标,不足的部分用 ∅ 类别填充。这样预测集合和 GT 集合大小都是 N,可以做一对一的整体匹配。

这里有一个很多复现者第一次没绕过来的点:匹配是全局最优匹配,不是每个预测单独找最近 GT。如果让每个预测各自贪心地找最近的 GT,可能出现多个预测同时匹配到同一个目标、而另一个目标没人认领的情况。DETR 用二分图匹配保证:每个预测恰好匹配一个 GT,每个 GT 恰好匹配一个预测,目标是让整体匹配代价最小。

二分匹配只在训练阶段使用,推理时没有匹配过程,直接取每个预测的类别概率和框坐标输出。这也是设计上比传统检测「先出大量候选框再做 NMS」更简洁的地方:训练时通过匹配让模型学会每个 query 该负责哪个目标,推理时也就不需要后处理来剔除重复框。

3.2 匈牙利算法:代价矩阵怎么构造、为什么 GT 也要补齐到 N

求解二分图匹配的经典工具是匈牙利算法,Python 里可以直接用 scipy 的 linear_sum_assignment。关键难点在代价矩阵的构造:矩阵行对应 N 个预测,列对应 N 个 GT,每个元素是「预测 i 匹配 GT j 的代价」。代价由两部分组成:类别预测的负对数似然,以及框坐标的 L1 距离与 GIoU 距离。

import numpy as np from scipy.optimize import linear_sum_assignment # 假设 num_pred=100, num_gt=100(GT 已补 ∅ 到同样长度) # cost_matrix[i, j]:预测 i 匹配 GT j 的综合代价,越小越好 cost_matrix = np.zeros((100, 100)) cost_matrix = cls_cost + box_cost # 两者都已经是 (100, 100) pred_idx, gt_idx = linear_sum_assignment(cost_matrix) # pred_idx 与 gt_idx 一一对应,即最优匹配结果

scipy 返回的 linear_sum_assignment 会把行索引和列索引分别返回,一一对应就是最优匹配。需要注意这两个索引等长,且行和列各自不重复。构造代价矩阵时如果 GT 没补齐到 N,矩阵就会变成 100×3 这种非方阵,匈牙利算法虽然能跑,但含义变成「只为部分 GT 分配预测」,剩下的预测既没有匹配目标也没有 ∅ 惩罚,训练就会失去对空槽位的约束。

3.3 三类损失组合:分类负对数似然、L1 与 GIoU 的比例

匹配完成之后,损失就按匹配对来计算。整体损失分三块:分类损失用负对数似然,匹配到 ∅ 的预测作为 background 类计算;框坐标损失用 L1;另外还叠加一个 GIoU 损失。L1 直接计算中心点坐标和宽高的绝对差,但对小目标天然不公平——同样 5 个像素的偏差,在一个 30×30 的小框上相对误差很大,在一个 300×300 的大框上却可以忽略。GIoU 衡量的是两个框的重合程度,对目标尺度不敏感,两者互补。

复现时通常按这种起始配置:

loss = cls_loss + 5.0 * l1_loss + 2.0 * giou_loss

L1 权重取 5、GIoU 权重取 2 是常见起步值,原因是 GIoU 本身数值范围在 [-1, 1] 附近,而 L1 的绝对数值通常更小,不加权重容易让 L1 在总损失里占比过低。如果小目标检测效果差,优先调大 giou_weight;如果类别错分明显,优先调大 cls_loss 权重。消融实验也证明了纯 L1 的效果很差,和 GIoU 结合后中目标和大目标的 AP 改善最明显。

4. 实验设计怎么读:与 Faster R-CNN 对齐和四组关键消融

汇报的实验部分给出了五组信息:COCO 上与 Faster R-CNN 的综合对比、encoder 层数影响、decoder 层数影响、位置编码消融、loss 消融,外加一个全景分割扩展。这五组实验不是平行罗列,而是层层递进地回答三个问题:性能到底行不行、哪个组件贡献最大、模型是不是只适合检测任务。

4.1 COCO 对标 Faster R-CNN:性能相当,训练成本不可忽略

在 COCO 数据集上,DETR 的准确率和检测速度和 Faster R-CNN 基本相当。这里要特别注意的是训练成本:DETR 的完整训练通常需要跑到 500 epoch 左右,而 Faster R-CNN 在标准设置下往往几十个 epoch 就能收敛。很多人在复现时只看到「性能相当」就以为可以直接替换,实际落地时如果用同样算力,DETR 的训练时间会是 Faster R-CNN 的好几倍。汇报里说「准确性和检测速度性能相当」是有前提的——它建立在足够长的训练之上。这个点在做技术选型时比模型结构本身更值得关注。

4.2 encoder 层数、decoder 层数与 NMS 叠加的三组观察

汇报里最容易被复现者直接参考的是三组消融观察:

实验变量汇报中观察到的趋势复现时需要盯住的点
encoder 层数增加AP 逐步提升,更多 encoder 层带来更精细的全局建模encoder 从 3 层加到 6 层时收益最明显,超过 6 层后训练显存压力增大
decoder 层数增加每一层后 AP 和 AP50 都有提升decoder 深度直接决定集合预测的精度,减到 3 层以下掉点明显
在 decoder 输出叠加 NMS没有一致增益,DETR 设计上不需要 NMS如果加了 NMS 发现掉点,说明是重复框误删,不是参数问题

encoder 层数的提升逻辑很直观:更多层意味着更充分的全局信息交换,实例分离得更干净。decoder 每一层的提升则说明集合预测不是一步到位,而是逐层精化:前几层把目标大致分开,后几层逐步细化框的位置和类别。NMS 那组实验是很多人容易忽略的设计佐证——传统检测器需要 NMS 是因为候选框之间互相不知道对方的存在,而 DETR 的 decoder 通过 self-attention 让所有 predictions 两两可见,重复框的抑制是在模型内部学习出来的,不需要外部手工规则。

4.3 位置编码与 loss 消融,以及全景分割的一个小扩展

位置编码消融是四组实验里信号最强的一组:去掉空间位置编码和输出位置编码后,AP 显著下降。这说明 Transformer 在目标检测里不能只依赖 CNN 特征的空间排列,必须显式把位置信息喂进去。复现时常见错误是把位置编码只加在 encoder 输入,decoder 侧的 object queries 完全不带位置信息,结果模型很难定位目标。

loss 消融的结论前面已经提过:只用 L1 效果差,加 GIoU 后中目标和大目标 AP 改善明显。这组实验验证了组合损失的必要性,也给参数调优指了方向。

全景分割是 DETR 的迁移验证:在固定 DETR 模型的基础上,只训练一个小的扩展分支就能做全景分割,效果不错。这里的关键词是「固定」——目标检测训练好的 encoder-decoder 权重没有重新训练,说明模型学到的目标级表示是可迁移的,不是只对框回归有效。这个结论对想拿 DETR 做多任务的人很有参考价值:先训练检测,再挂轻量头做分割,比从头训练一个多任务模型省很多成本。

5. 避坑与常见问题:复现 DETR 与准备汇报时容易翻车的五个点

DETR 的复现难度不在于网络结构本身,而在于训练管线里几个小细节。这些坑我基本都踩过,每条按现象、原因、解决的方式列出来,可以当作复现时的自查清单。

5.1 训练时 GT 补齐到 N 维度对不上,匈牙利算法直接报错

现象:构造代价矩阵时 shape 对不上,报错提示 broadcasting error 或者 linear_sum_assignment 返回值长度不一致。原因是 GT 集合没有补齐到 N,代价矩阵变成非方阵,匹配结果语义完全错误。解决方法是训练数据处理时就把 GT 补齐:真实目标保留类别和框坐标,其余位置类别置为 ∅,框坐标置零,同时在损失计算时记录一个 mask,只对有效位置计算框损失,避免补齐的零框干扰 GIoU。检查形状是最快的验证方式:cost_matrix 一定是 N×N,pred_idx 和 gt_idx 长度都是 N。

5.2 在 DETR 输出上加 NMS,反而掉点

现象:把原有检测流程里的 NMS 后处理加到 DETR 输出上,AP 不升反降。原因是 DETR 的 decoder 已经通过 query 之间的 self-attention 抑制了重复预测,模型内部已经做了类似 NMS 的工作,再加手工 NMS 会把那些置信度略低但确实是正确目标的框误删掉。解决方法是直接去掉 NMS。如果发现输出里出现明显重复框,先检查训练是否收敛、object queries 数量是否充足,而不是急着加后处理模块。

5.3 位置编码加错位置或漏加,AP 明显下滑

现象:训练到一定阶段后验证集 AP 上不去,或者比同配置的参考结果低 10 个点以上。原因是位置编码没有加到每层注意力的 query 和 key 上,或者干脆只在 encoder 输入端加了一次。位置编码的目的是让注意力层知道 token 之间的空间关系,如果它只参与 value 计算而不影响 query-key 的相似度,注意力权重就仍然是空间无关的。解决方法是检查每个 encoder layer 的 self-attention:位置编码需要叠加在 query 和 key 的输入上,而不是只加在 token embedding 上。常见实现里位置编码是一个独立的 tensor,在每个注意力层前分别加到 q 和 k 上。

5.4 把 object queries 当作 Anchor 来调参

现象:复现时想当然去调整 query 的数量和初始化方式,甚至有人按 Anchor 的思路给不同 query 设置不同的宽高比预期,结果训练曲线乱七八糟。原因是 object queries 和 Anchor 本质不同:Anchor 是预设的、固定的一组先验框,而 object queries 是随机初始化后从数据里学出来的「提问向量」,它们不对应任何具体的框形状。解决方法是把 object queries 当成参数而不是先验,保持随机初始化,通过训练让它自己学出分工。想观察每个 query 学到了什么,可以直接可视化 decoder attention map,你会看到不同 query 集中关注图像的不同区域,而不是不同宽高比的框。

5.5 DETR 收敛慢被误判为训练失败

现象:训练到 150 epoch 时 AP 还没赶上 Faster R-CNN,以为模型坏了,提前停掉。原因是 DETR 的二分匹配在训练早期不稳定,匹配关系会随网络参数变化而跳变,模型需要很长时间才能把匹配和特征学习协调好。500 epoch 是常见完整配置,300 epoch 只能算快速验证。解决方法是先跑一个 300 epoch 的小规模验证,观察验证集 AP 是否持续上升且没有明显震荡;如果曲线确实在涨,说明管线是对的,再拉长到 500 epoch。训练时常见配置是两个学习率:主网络用 1e-4,backbone 用 1e-5,配合 warmup 300 步,损失通常会在前几十个 epoch 波动较大,这是匹配跳变的正常现象。

6. 把汇报 PPT 变成自己的复现清单:六个问题检验是否吃透 DETR

拿到这份汇报之后,我通常不只是读一遍,而是把它当成一张自测卷。每一页 PPT 对应一个「不看原论文能不能讲清楚」的问题。我自己的做法是把汇报四个部分转成六个提问,逼自己不看材料现场作答:

汇报部分应该能回答的问题
研究背景Anchor 和 NMS 为什么是手工组件?DETR 靠什么取消了它们
研究目的集合预测和「先出候选框再后处理」的代理任务差异在哪
研究过程CNN 输出通道数是多少、空间尺寸缩小多少倍、Transformer 输入 shape 是什么
研究过程代价矩阵是多少乘多少?GT 怎么补 ∅?匈牙利算法的输入输出分别是什么
研究过程三部分损失各解决什么问题?起始权重怎么设
实验哪组消融最能说明位置编码的必要性?NMS 叠加为什么没有收益

六个问题答完,对 DETR 的理解基本就达到能复现的层次。也可以做一个更硬核的验证:把代价矩阵和匈牙利算法单独摘出来,跑一个 5 行的小脚本确认维度处理正确:

import numpy as np from scipy.optimize import linear_sum_assignment cost = np.zeros((100, 100)) # 预测 100,GT 补 ∅ 到 100 cost[:3, :3] = -2.0 # 前 3 个真实目标附近给低代价 row, col = linear_sum_assignment(cost) assert len(row) == len(col) == 100 # 维度必须严格对齐

这个脚本跑通,说明你对方阵匹配、补齐逻辑、索引对应关系都有了实际手感,而不是停留在 PPT 的文字描述里。从那以后我每次拿到论文汇报 PPT,都强制先过一遍「提问 → 作答 → 找证据」这个流程,答不上来的地方回到原论文对应章节重读,而不是急着复现代码。这样做最大的好处是,复现时遇到问题能快速定位是模型结构的问题还是数据管线的问题,少走很多弯路。希望这份资源的拆解和这套提问方式也能帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询