尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
DETR端到端目标检测:论文笔记与源码逐行拆解
1. 从“论文笔记源码”这个组合说起为什么DETR值得逐行拆第一次看到“论文笔记源码”这种组合我下意识会把它归类成两种东西之一要么是学生交作业式的论文摘要要么是跑通官方脚本后截几张图就完事的“复现记录”。但DETR这个题目不一样它属于那种你光看论文会觉得“思路真优雅”一打开源码又会发现“优雅背后全是工程细节”的典型。所以这篇笔记我打算按“读论文时脑子里冒出的疑问”和“翻源码时被卡住的地方”两条线交叉着写而不是先讲背景再讲方法最后讲实验那种标准三段式。DETR的全称是End-to-End Object Detection with Transformers翻译过来就是“用Transformer做端到端目标检测”。它最核心的卖点是把目标检测里那些手工设计的组件——锚框、非极大值抑制、候选区域生成——全部拿掉换成一个Transformer编码器-解码器结构配合二分图匹配损失直接输出一组固定数量的预测框。你如果做过Faster R-CNN或者YOLO系列第一次看到这个设定会觉得很反直觉检测框数量不应该是动态的吗怎么可以固定成100个多出来的框怎么办这些问题我在读论文时反复出现后来都是在源码里找到答案的。这篇笔记适合两类人一类是已经看过DETR论文但对其中的集合预测损失、匈牙利匹配、位置编码这些概念还停留在“大概知道”层面的读者另一类是想直接读源码但被官方实现里各种张量维度变换绕晕的读者。我会尽量把论文里的公式和源码里的张量操作对应起来讲让你看到某个torch.einsum的时候能反应过来它对应论文里的哪一步。至于需要的前置知识你至少得知道Transformer的基本结构自注意力、多头注意力、位置编码以及目标检测的基本评价指标IoU、mAP。如果这两块还不熟建议先补一下再回来不然读源码会比较痛苦。2. DETR到底把目标检测改成了什么形状的问题2.1 传统检测器的“锚框后处理”链条为什么让作者想推翻要理解DETR的设计动机得先看清楚它想替代的东西到底长什么样。以Faster R-CNN为例它的流程大致是主干网络提特征区域提议网络在特征图上每个位置生成一堆锚框然后对每个锚框做分类和回归最后用非极大值抑制把重叠的框去掉。这条链条里有三个地方是“手工设计”的锚框的尺度和长宽比需要根据数据集调正负样本的划分规则需要根据IoU阈值调NMS的阈值也需要调。这些超参数之间还会相互影响换一个数据集往往就要重新调一轮。更麻烦的是这条链条是串行的区域提议的质量直接影响后续分类回归NMS的结果又依赖前面的框质量。你很难单独说“这个检测器不行是因为分类头不好”因为误差会沿着链条累积。作者在论文里把这种结构称为“间接”的因为模型并不是直接输出最终的检测集合而是输出一堆候选再经过后处理得到结果。DETR想做的事情就是把这个间接过程变成直接过程输入图像输出一个集合集合里每个元素就是一个检测结果没有中间商赚差价。2.2 把检测看成集合预测问题固定100个框的直觉解释DETR的做法是不管图像里有多少个物体模型都输出N个预测论文里N100。这100个预测里有一部分会匹配到真实物体剩下的会匹配到“无物体”类别。你可以把这100个预测想象成100个“查询槽位”每个槽位负责问一句“我这个地方有没有物体如果有它是什么、在哪里”训练的目标就是让这100个槽位学会分工有的槽位专门盯左上角的物体有的专门盯右下角的有的学会在没物体的时候输出“无物体”。这里有个容易误解的点100个槽位并不是预先分配好位置的它们是通过Transformer解码器的自注意力和交叉注意力动态调整的。解码器有100个查询向量query embedding每个查询向量通过交叉注意力去看编码器输出的特征图然后决定自己关注图像的哪个区域。所以这100个查询更像100个“智能探测器”而不是100个固定位置的锚框。这也是DETR和基于锚框的方法最本质的区别锚框的位置是预先定义的查询向量的关注区域是学出来的。2.3 二分图匹配损失为什么不能用普通的交叉熵如果模型输出100个预测真实物体只有3个那怎么定义损失最朴素的想法是对每个真实物体找一个预测框和它算损失剩下的97个预测框都算“无物体”。但问题是哪个预测框对应哪个真实物体如果随便选同一个真实物体可能被多个预测框同时匹配或者一个预测框同时匹配多个真实物体这都会让训练信号变得混乱。DETR用的是二分图匹配匈牙利算法来建立真实物体和预测框之间的一一对应关系。具体来说它把这个问题看成一个二分图匹配问题左边是真实物体集合右边是预测框集合边的权重是匹配代价分类代价框回归代价IoU代价。匈牙利算法会找到一个总代价最小的匹配方案使得每个真实物体恰好匹配一个预测框每个预测框最多匹配一个真实物体。匹配完成之后再对匹配上的预测框算分类损失和回归损失对未匹配的预测框只算“无物体”分类损失。这个设计的关键在于匹配过程本身不产生梯度它只是一个分配方案。梯度只通过匹配后的损失回传。所以匈牙利算法在这里的角色更像一个“调度员”它决定哪个预测框负责哪个物体但不直接参与优化。我在读源码时一开始没想明白这一点后来看到HungarianMatcher类的forward方法里用了scipy.optimize.linear_sum_assignment才意识到匹配是在numpy层面做的和PyTorch的计算图是分开的。3. 源码里那些论文没细说的张量操作3.1 主干网络输出到Transformer输入的维度变换DETR的主干网络用的是ResNet输入图像经过ResNet之后得到一个形状为(batch, 2048, H/32, W/32)的特征图。假设输入是(batch, 3, 800, 1066)那么特征图大概是(batch, 2048, 25, 34)。这个特征图不能直接丢给Transformer因为Transformer期望的输入是序列形式(sequence_length, batch, d_model)。所以需要做两步先用一个1x1卷积把通道数从2048降到256论文里的d_model然后把空间维度展平。展平之后得到(batch, 256, 25*34)再转置成(25*34, batch, 256)这就是编码器的输入序列。这里有个细节位置编码是在展平之后加的而且是固定的正弦位置编码不是可学习的。论文里提到他们试过可学习的位置编码效果差不多但固定版本更简单。源码里position_encoding是一个nn.Module在初始化时预计算好一个(max_len, d_model)的矩阵前向传播时根据输入序列长度切片。我一开始觉得这个位置编码有点奇怪图像是二维的为什么位置编码是一维的后来想明白了因为展平之后序列长度是H/32 * W/32每个位置对应特征图上的一个空间位置一维位置编码实际上是在编码这个展平后的索引。虽然丢失了二维结构信息但Transformer的自注意力本身可以学到空间关系所以效果也还行。后来Deformable DETR改进了这一点用了二维位置编码那是后话。3.2 编码器-解码器结构里那些容易看漏的残差和归一化DETR的Transformer结构基本沿用了标准Transformer但有几个地方和原始Transformer不同。第一个是编码器里没有用nn.MultiheadAttention的batch_first参数因为PyTorch版本更新导致接口变化官方源码里用的是(sequence_length, batch, d_model)的布局。第二个是解码器的输入它接收的是编码器的输出加上100个查询向量。这100个查询向量是nn.Embedding(100, 256)初始化时用正态分布训练时和模型一起更新。解码器的自注意力层有个细节它用的是nn.MultiheadAttention但查询、键、值都来自同一个查询向量序列。这意味着100个查询向量之间会相互通信它们可以协商谁负责哪个区域。交叉注意力层则是查询向量去attend编码器的输出键和值都来自编码器。每个注意力层后面都有残差连接和层归一化前馈网络是两层线性加ReLU中间维度是2048d_model的8倍。我在读源码时被一个地方卡了很久解码器的输出经过一个class_embed和一个bbox_embed分别得到分类logits和框坐标。class_embed是一个nn.Linear(256, num_classes1)bbox_embed是一个三层MLP输出4个值中心点x、y、宽、高。这里有个容易忽略的点框坐标的预测是相对于图像尺寸归一化的范围在0到1之间但源码里没有显式做sigmoid而是在损失函数里用F.l1_loss直接算。我一开始以为漏了激活函数后来发现论文里就是这么设计的因为L1损失对尺度不敏感归一化后的坐标直接回归也没问题。3.3 匈牙利匹配在代码里到底怎么算代价矩阵HungarianMatcher类是DETR源码里最值得逐行读的部分之一。它的forward方法接收两个输入预测结果分类logits和框坐标和真实标签类别和框坐标。然后它要构建一个代价矩阵形状是(batch_size, num_queries, num_targets)其中num_queries100num_targets是当前图像里真实物体的数量。代价矩阵由三部分组成分类代价、L1框代价、GIoU代价。分类代价是-softmax(logits)[target_class]也就是预测为目标类别的概率的负值。L1框代价是预测框和真实框的L1距离。GIoU代价是1 - giou其中giou是广义IoU。这三部分各有权重论文里分类代价权重是1L1代价权重是5GIoU代价权重是2。源码里这些权重是硬编码的没有暴露成超参数。构建完代价矩阵后对每个batch样本调用scipy.optimize.linear_sum_assignment得到匹配索引。这里有个性能上的坑linear_sum_assignment是CPU上的操作而且对每个样本单独调用所以训练时数据加载和匹配会成为瓶颈。官方源码里用了torch.no_grad()包住匹配过程避免计算图膨胀。我在自己的实验里试过把匹配放到GPU上但scipy没有GPU版本后来换成了torchlinear_assignment之类的库速度提升有限因为匹配本身的计算量不大瓶颈在CPU和GPU之间的数据传输。4. 训练DETR时那些论文不会告诉你的参数细节4.1 学习率、权重衰减和梯度裁剪的配合方式DETR的训练配置在论文附录里有表格但有些细节需要结合源码才能理解。主干网络用的是ResNet-50初始学习率是1e-4Transformer部分的学习率是1e-4但主干网络的学习率是1e-5差了10倍。这个设计是因为主干网络是预训练的不需要大改而Transformer是随机初始化的需要更大的学习率。权重衰减是1e-4用的是AdamW优化器。梯度裁剪的阈值是0.1这个值比常见的1.0或5.0小很多。我一开始觉得0.1太激进后来发现DETR的损失里分类损失和框损失的量级差异很大如果不裁剪梯度范数会波动很厉害。源码里用的是torch.nn.utils.clip_grad_norm_在每次反向传播之后、优化器更新之前调用。这里有个经验如果你自己复现时发现loss震荡或者不收敛先检查梯度裁剪有没有加阈值是不是0.1。学习率调度用的是step decay在训练到第100个epoch时降10倍总共训练300个epoch。这个训练时长比YOLO系列长很多YOLOv5大概300个epoch就能收敛但DETR需要更久因为Transformer的注意力机制需要更多数据来学习。我在单卡上跑过DETR300个epoch大概需要一周左右所以如果算力有限建议先用小分辨率或者小模型试。4.2 数据增强里那些影响收敛的隐藏开关DETR的数据增强比较简单主要是随机水平翻转和随机缩放。缩放是把图像短边缩放到480到800之间随机一个值长边不超过1333。这个缩放策略和Faster R-CNN类似但DETR对缩放更敏感因为位置编码是固定的如果图像尺寸变化太大位置编码的分布会偏移。源码里在缩放之后还会做一个normalize把像素值从0-255转到0-1再减均值除标准差。有个容易被忽略的点DETR在训练时用了RandomHorizontalFlip但翻转之后框的坐标也要翻转。源码里是在__getitem__里手动处理的不是用torchvision的transform。如果你用torchvision的RandomHorizontalFlip它只会翻转图像不会翻转框需要自己写一个包装。我在第一次复现时就踩了这个坑模型学出来的框全是反的排查了半天才发现是翻转没同步。另外DETR没有用多尺度训练也没有用mosaic或者mixup这类增强。论文里提到他们试过一些增强但效果不明显反而增加了训练时间。所以如果你想让DETR收敛得快一点不要盲目加增强先把基础配置调对。4.3 损失权重和辅助损失的实际影响DETR的损失函数是L λ_cls * L_cls λ_l1 * L_l1 λ_giou * L_giou论文里λ_cls1λ_l15λ_giou2。这三个权重不是随便定的作者在消融实验里试过不同组合发现L1和GIoU的权重对最终mAP影响很大。如果L1权重太小框的位置会不准如果GIoU权重太小框的尺度会不准。我在自己的实验里试过把λ_l1调到10结果mAP掉了2个点因为模型过度关注框的绝对坐标忽略了分类。辅助损失是DETR的一个特色解码器的每一层都会输出预测每一层都算损失然后加权求和。论文里辅助损失的权重是1和最后一层的损失权重一样。这个设计是为了让每一层的解码器都学到有用的表示而不是只靠最后一层。源码里在SetCriterion类的forward里会遍历所有解码器层的输出分别算损失然后累加。这里有个细节辅助损失只对分类和框回归算不对匹配算因为匹配只在最后一层做一次。我一开始觉得辅助损失会增加计算量后来发现它其实加速了收敛。因为每一层都有梯度回传浅层的解码器不会“偷懒”整个解码器栈的训练更充分。如果你自己实现DETR建议保留辅助损失不要为了省事去掉。5. 从源码跑通到真正理解几个我踩过的坑5.1 环境配置里PyTorch版本和CUDA的兼容问题DETR官方源码是基于PyTorch 1.5写的但现在大家用的都是PyTorch 1.10以上有些API变了。最典型的是nn.MultiheadAttention的batch_first参数在1.5里没有在1.10里有了。如果你直接用官方源码可能会遇到TypeError: forward() got an unexpected keyword argument batch_first。解决办法是把batch_first去掉然后手动调整张量维度。另一个坑是torch.einsum在某些版本上对非连续张量的支持不好需要先调.contiguous()。CUDA版本也要注意DETR用了torch.cuda.amp做混合精度训练但AMP在PyTorch 1.5里还是实验性的在1.10里稳定了。如果你用1.10建议开启AMP能省不少显存。我在2080Ti上跑DETR不开AMP只能跑batch size 2开了AMP能跑到4。但AMP有个坑匈牙利匹配里的scipy操作不支持半精度所以匹配那一步要强制转回float32。5.2 训练不收敛时的排查顺序DETR训练不收敛是常见问题我总结了一个排查顺序。第一步看损失曲线如果分类损失一直不降检查分类头的初始化官方源码里分类头的bias初始化为-4.0这是为了在训练初期让模型倾向于预测“无物体”避免一开始就乱匹配。如果你忘了这个初始化分类损失会震荡很久。第二步看匹配数量如果匹配到的框数量一直是0说明代价矩阵有问题检查分类代价的符号是不是反了。第三步看梯度范数如果梯度范数一直很大检查梯度裁剪有没有生效。还有一个隐蔽的坑DETR的输入图像尺寸必须是32的倍数因为ResNet有5次下采样每次下采样2倍总共32倍。如果你输入的图像尺寸不是32的倍数源码里会做padding但padding的位置会影响位置编码。我在第一次跑的时候用了(800, 1066)1066不是32的倍数结果位置编码错位mAP掉了5个点。后来改成(800, 1088)就正常了。5.3 推理阶段的后处理为什么比训练简单DETR的推理阶段比训练简单很多因为不需要匈牙利匹配也不需要算损失。你只需要把图像过一遍模型得到100个预测然后对每个预测做softmax取最大概率的类别如果概率大于阈值默认0.5就保留否则丢弃。这里没有NMS因为模型已经通过训练学会了让每个查询负责不同的物体理论上不会出现重复框。但实际用的时候我发现还是会有少量重复框尤其是在物体密集的场景。论文里提到可以用一个简单的NMS做后处理但默认不开启。如果你对精度要求高可以加一个IoU阈值0.5的NMS能去掉一些重复。另外DETR对小物体的检测效果一般因为特征图的分辨率是输入图像的1/32小物体在特征图上可能只有几个像素。如果你主要检测小物体建议用Deformable DETR或者加一个FPN。6. 读完源码之后我对DETR的重新认识6.1 端到端的代价训练慢但推理干净DETR最大的优点是推理干净没有锚框、没有NMS、没有复杂的后处理一个模型输入图像输出检测结果部署起来非常方便。但代价是训练慢300个epoch在单卡上要跑一周而且对小物体不友好。这个 trade-off 在实际项目中要仔细权衡如果你做的是离线检测训练慢一点无所谓DETR的简洁性很吸引人如果你做的是实时检测DETR的推理速度虽然比Faster R-CNN快但比YOLO慢而且小物体漏检多可能不适合。我在一个项目里试过用DETR做工业质检检测目标是大尺寸的零件DETR表现很好因为零件尺寸大特征图上的分辨率够用。但后来换了一个检测小缺陷的场景DETR就力不从心了最后换回了YOLOv5。所以选型的时候一定要看你的目标尺寸分布不要因为DETR“优雅”就盲目上。6.2 查询向量的可解释性它们到底学到了什么我做过一个实验把100个查询向量的注意力图可视化出来想看它们到底关注图像的哪些区域。结果发现在训练初期所有查询向量的注意力图都差不多都是均匀分布在整个图像上。训练到中期开始分化有些查询向量专门关注左上角有些关注右下角有些关注图像中心。训练到后期分化更明显而且和图像内容相关如果图像里有一只狗在左边会有几个查询向量专门关注狗的区域。这个实验让我意识到查询向量并不是预先分配好位置的而是通过训练自己学会了分工。这也解释了为什么DETR需要这么长的训练时间它不仅要学分类和回归还要学查询向量的分工策略。这个分工策略是隐式的没有显式的监督信号完全靠二分图匹配的梯度来引导。所以如果你减少查询数量比如从100降到50模型可能会学不过来因为查询不够用分工不够细。6.3 后续改进方向Deformable DETR和DAB-DETR解决了什么DETR之后有几个重要的改进工作我在读源码时也顺带看了一下。Deformable DETR主要解决两个问题一是训练慢二是小物体检测差。它的做法是把交叉注意力改成可变形注意力每个查询只关注特征图上的几个采样点而不是全局注意力。这样计算量从O(N^2)降到O(NK)K是采样点数量通常取4。训练epoch从300降到50小物体mAP也提升了。DAB-DETR则是从查询向量的角度改进它把查询向量显式地解释成4维的框坐标x, y, w, h然后通过迭代细化来更新框。这样查询向量有了明确的物理意义训练更稳定收敛更快。我在自己的实验里试过DAB-DETR确实比原始DETR好训而且mAP更高。如果你现在要复现DETR我建议直接从DAB-DETR或者Deformable DETR入手原始DETR的训练成本太高了。7. 如果你也想读DETR源码我的建议顺序读DETR源码不要从main.py开始那样会被各种参数和日志绕晕。我的建议顺序是先读models/detr.py里的DETR类看清楚主干网络、Transformer、分类头、回归头是怎么串起来的。然后读models/transformer.py理解编码器和解码器的实现特别是位置编码和注意力层的维度变换。接着读models/matcher.py这是匈牙利匹配的核心理解代价矩阵的构建和匹配过程。最后读models/criterion.py理解损失函数的计算特别是辅助损失的处理。读的时候建议配合论文的公式一起看比如看到torch.einsum(qnc,ckc-qnk, ...)的时候回去翻论文里的注意力公式看看对应的是哪一步。另外建议自己动手改几个地方比如把查询数量从100改成50看看mAP怎么变把位置编码从固定改成可学习看看收敛速度怎么变。这种“改一改、跑一跑”的方式比单纯读代码理解得快。最后分享一个我读源码时的小技巧在关键位置加print语句打印张量的形状。DETR的维度变换比较多光看代码容易晕打印出来一目了然。比如在编码器输入之前打印src.shape在解码器输出之后打印hs.shape在匹配之后打印indices这样整个数据流就清晰了。这个技巧虽然笨但很有效尤其是对刚接触Transformer检测器的人。
RELATED

相关推荐

SpringBoot+Vue+MySQL物流管理系统毕设全解析:从数据库设计到部署

SpringBoot+Vue+MySQL物流管理系统毕设全解析:从数据库设计到部署

“【毕业设计】SpringBootVueMySQL 物流信息管理系统平台源码数据库论文部署文档”——这个标题我在各种资源站、毕设辅导群、高校论坛里见过不下几百次,但说实话,真正把系统本地跑起来、把论文逻辑讲清楚、把源码改出自己风格的人,可能不到三…

📅 2026/10/13 13:19:22
考研择校与夏令营:信息战如何决定你的上岸概率

考研择校与夏令营:信息战如何决定你的上岸概率

1. 为什么择校是考研路上第一个分水岭每年三四月份,总有一批学弟学妹来问我同一个问题:“学长,我想考某所学校的某个专业,你觉得我行不行?”说实话,这个问题我没法直接回答,因为“行不行”取决于…

📅 2026/10/13 13:14:21
周易算命源码开源:占卜、八字排盘、姓名测试完整项目解析

周易算命源码开源:占卜、八字排盘、姓名测试完整项目解析

项目标题 : 周易算命源码开源下载 - 周易占卜、八字排盘、姓名测试完整项目 项目正文 : 一个完整的周易文化主题Web应用,集成了占卜起卦、八字排盘、姓名测试三大功能模块,实现纯前端计算引擎与后端服务解耦,源码开源可二次开发。 关键词…

📅 2026/10/13 13:14:21
MORE NEWS

更多资讯

📰

Python手写进度条:print隐藏参数与回车符实现终端实时刷新

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

STM32驱动DS1302实时时钟:从接线到校准的完整实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

2026论文降重工具哪家好用?沁言学术与QuillBot等AI写作工具对比

摘要:这篇盘点围绕 AI 论文降重与写作辅助工具展开,把沁言学术、Writefull、QuillBot、Consensus、SciSpace 放进同一场比较,从改写降重的实际效果、文献支撑、中文适配和数据安全几个维度看差别,最后给出一套按研究阶段搭配工具的参考思路。论文查重和降重几乎每个写过毕业论文…

📰

基础DP模型详解:状态定义、转移方程与背包、区间、树形DP实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Matlab传染病模型实战:从SEIR方程到参数拟合与干预推演

简介:这份资源面向生物数学、传染病动力学方向的学习者与科研人员,提供一套基于Matlab的传染病模型代码与配套分析材料,帮助理解SIR、SEIR等模型的构建思路,并完成无病平衡点与地方病平衡点的稳定性分析。压缩包共13个文件&#x…

📰

商贸企业采购退货财务处理与管家婆系统操作指南

1. 业务场景解析:成本支出后退回的本质在商贸企业的日常经营中,经常会出现已付款采购的商品因质量问题、规格不符等原因需要退货的情况。这类业务在财务处理上涉及三个关键节点:前期已确认采购成本并完成付款商品实物退回供应商资金退回企业账…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬