首页 / 资讯中心 / 文章详情

DETR目标检测实战:集合预测、二分图匹配与Transformer编解码

DETR目标检测实战:集合预测、二分图匹配与Transformer编解码 ★ FEATURED ARTICLE
1. 从“目标检测一定要靠锚框吗”说起如果你接触过目标检测大概率绕不开一个东西——锚框Anchor。从早期的经典检测框架到后来的各种单阶段、双阶段模型锚框几乎成了标配预先在特征图上铺一大堆不同尺度、不同长宽比的参考框然后让网络去预测每个锚框的偏移量和类别。这套逻辑跑了很多年效果也确实好但它带来的麻烦同样不少。我自己最早做检测项目的时候最头疼的就是调锚框。数据集一换锚框的尺寸、比例、数量就得重新聚类一遍小目标检测效果差第一反应就是加更密集的锚框后处理里还有一大堆和锚框绑定的阈值要调。更别提最后输出的时候同一个物体被好几个锚框同时命中还得靠非极大值抑制NMS去重。整个流程里超参数多、耦合重、调起来像在拆炸弹。DETRDEtection TRansformer的出现本质上就是冲着这套“锚框NMS”的旧范式去的。它做的事情可以用一句话概括把目标检测直接当成一个集合预测问题输入一张图输出一个固定长度的预测集合每个元素要么是一个物体类别边界框要么是“没有物体”。没有锚框没有NMS端到端训练。这个思路在当年算是相当激进的因为它把检测里很多手工设计的环节一次性砍掉了。这篇内容我打算按一个真正动手复现过、也踩过坑的人的视角来写。适合的读者是已经了解基础目标检测概念、想搞明白DETR到底怎么work的人或者正准备在自己的项目里试DETR、但被它的收敛慢和显存占用劝退的人。我会把它的核心机制、训练难点、实操配置、以及那些文档里不会写的坑一条条拆开讲清楚。核心关键词就三个集合预测、二分图匹配、Transformer编解码这三个词理解了DETR基本就通了。2. DETR到底把检测问题改写成了什么2.1 集合预测从“一堆框里挑”到“直接给答案”传统检测器的输出逻辑是“密集预测筛选”。特征图上每个位置都预测若干个框最后可能产生成千上万个候选再通过置信度阈值和NMS筛出最终结果。这个过程的本质是网络只负责“打分”最终选哪些框是后处理决定的。DETR换了个思路。它预先设定一个固定的预测数量 N论文里 N100网络一次性输出 N 个预测每个预测包含一个类别包括“无物体”这个类和一个归一化的边界框坐标。训练的目标就是让这 N 个预测里恰好有 K 个对应图中真实的 K 个物体剩下的 N-K 个都预测成“无物体”。这个改写的妙处在于输出是一个无序的集合而不是一个有序的序列。既然是集合那“第几个预测对应哪个物体”就不重要了重要的是这 N 个预测整体上覆盖了所有真实物体。这就引出了一个关键问题——训练的时候怎么知道哪个预测该去负责哪个真实物体答案就是二分图匹配。2.2 二分图匹配让每个预测找到它该负责的目标训练阶段DETR 手里有两组东西一组是网络输出的 N 个预测另一组是图中真实的 K 个物体不足 N 个的部分用“无物体”补齐。它要做的是找到一个最优的一一对应关系让匹配后的总损失最小。这个问题在数学上叫二分图最大权匹配DETR 用的是匈牙利算法Hungarian Algorithm来解。具体来说匹配的代价由两部分组成分类代价和边界框代价。分类代价用的是预测类别概率的负对数边界框代价用的是 L1 距离加上 GIoU 损失。匈牙利算法会在所有可能的匹配方案里挑一个总代价最小的。匹配完成之后每个预测就明确了“我该负责哪个真实物体”然后才去算最终的训练损失。这里有个细节特别值得说匹配是在每个 batch 的每张图上独立做的而且匹配用的代价和最终反向传播的损失不是完全一回事。匹配阶段用的是分类概率和框的距离而最终损失里还包含了“无物体”类的分类损失。这个设计是为了让匹配更稳定避免一开始就把预测硬塞给错误的物体。我一开始没想明白为什么要用匈牙利算法这么“重”的东西后来才理解如果不用最优匹配而是简单地按位置或者按顺序对应那网络就会被迫去学一个固定的输出顺序这既不合理也不稳定。二分图匹配保证了不管预测以什么顺序出现都能找到全局最优的对应这才是“集合预测”能成立的前提。2.3 为什么这个改写能去掉 NMSNMS 存在的根本原因是传统检测器会对同一个物体产生多个高分预测必须靠它去重。而 DETR 通过二分图匹配训练天然地鼓励“一个真实物体只被一个预测负责”。因为匹配是一一对应的如果一个物体被两个预测同时高分命中那在匹配阶段就会有一个预测被判定为“多余”它对应的监督信号就是“无物体”。训练久了网络就学会了抑制重复预测。换句话说NMS 的功能被“训练目标”本身内化了。这也是为什么 DETR 的推理流程极其干净跑一遍网络拿到 N 个预测把类别是“无物体”的丢掉剩下的就是结果。没有阈值筛选的纠结没有 IoU 阈值的调参整个后处理几乎为零。这一点在实际部署时特别省心因为后处理越少端到端优化的空间就越大。3. Transformer编解码器在DETR里各干了什么3.1 骨干网络之后的“序列化”处理DETR 的整体结构分三块CNN 骨干、Transformer 编码器、Transformer 解码器。输入一张图先过 CNN 骨干论文用的是 ResNet得到一张下采样后的特征图比如 H/32 × W/32。然后做一个 1×1 卷积把通道数降到 Transformer 的隐藏维度论文是 256再把二维特征图拉平成一个序列加上位置编码送进编码器。这里的位置编码是必须的因为 Transformer 本身对顺序不敏感而图像是有空间结构的。DETR 用的是固定的正弦位置编码直接加在特征上。我实测下来位置编码的实现方式对最终效果有影响尤其是小目标位置信息稍微糊一点小物体就容易漏。3.2 编码器让每个位置都看到全局编码器就是标准的 Transformer 编码器层堆叠每层包含自注意力Self-Attention和前馈网络FFN。它的作用是让特征图上的每个位置都能和其他所有位置交互从而捕获全局上下文。这一点和传统 CNN 检测器很不一样。CNN 的感受野是逐层扩大的要靠堆很多层才能看到大范围。而自注意力是一步到位的任何一个位置在第一层就能看到整张图。对于检测来说这意味着模型可以更容易地利用全局信息来判断一个区域是不是物体、是什么物体。比如一个被遮挡了一半的物体靠局部特征很难判断但结合周围上下文就容易多了。不过代价也很明显自注意力的计算量是序列长度的平方。特征图拉平后序列长度是 H/32 × W/32对于一张 800×800 的图序列长度大概是 625平方之后就是几十万的量级。这也是 DETR 显存占用高、训练慢的重要原因之一。3.3 解码器从“查询”到“预测”的关键一跃解码器是 DETR 最核心也最反直觉的部分。它接收两组输入一组是编码器的输出作为 key 和 value另一组是 N 个可学习的对象查询Object Queries作为 query。这 N 个查询就是前面说的固定预测数量的来源每个查询最终会变成一个预测。解码器的每一层做两件事先做自注意力让这 N 个查询之间互相交流再做交叉注意力让每个查询去编码器输出里“查询”它需要的信息。经过若干层之后每个查询就聚合出了它负责的那个物体的信息最后通过一个共享的前馈网络就是分类头和回归头输出类别和框。对象查询这个东西我一开始觉得特别玄。它到底学到了什么后来看了一些可视化的分析才明白每个查询会逐渐 specialization有的专门负责大物体有的负责小物体有的负责左边区域有的负责右边区域。这种分工不是人为指定的而是训练过程中自发形成的。你可以把它理解成 N 个“探测头”每个头学会了在图中找某一类模式。这里有个实操上的坑对象查询的数量 N 是固定的论文用 100。如果你的图里物体数量经常超过 100那就会漏检如果远少于 100那大部分查询都会学成“无物体”浪费容量。所以 N 的选择要结合你的数据集来定。我做过一个密集小目标的数据集100 明显不够加到 300 才勉强覆盖但显存也跟着涨了一大截。4. 训练DETR时那些让人抓狂的收敛问题4.1 为什么DETR要训练这么久DETR 最被诟病的一点就是收敛慢。论文里在 COCO 上要训 500 个 epoch 才能达到比较好的效果而传统检测器通常几十个 epoch 就收敛了。这个差距不是一点半点直接导致训练成本高得吓人。原因主要有两个。第一二分图匹配在训练初期很不稳定。网络刚开始输出乱七八糟匈牙利算法匹配出来的对应关系可能每张图都不一样导致监督信号抖动很大。第二Transformer 本身缺少 CNN 那种归纳偏置比如平移不变性、局部性它需要从数据里自己学出这些先验这本身就需要更多数据和时间。我自己的体会是DETR 的前 50 个 epoch 基本看不出什么效果loss 降得很慢mAP 几乎不动。到 100 epoch 之后才开始有明显起色。所以如果你打算训 DETR一定要有心理准备别训了二三十个 epoch 觉得没效果就放弃了。4.2 学习率与优化器的选择逻辑DETR 论文用的是 AdamW 优化器骨干网络的学习率比 Transformer 部分小一个数量级。这个设计是有道理的骨干是预训练好的已经比较稳定不需要大改而 Transformer 部分是随机初始化的需要更大的学习率去快速学习。具体配置上Transformer 部分的学习率是 1e-4骨干是 1e-5权重衰减是 1e-4。学习率调度用的是在 200 epoch 处下降一次。这些参数我基本照搬过实测下来确实比统一学习率要稳。如果你自己调建议先从这个配置起步别一上来就大改。还有一个细节DETR 对 batch size 比较敏感。论文用的是 16 张图一个 batch如果显存不够只能减小 batch那学习率也要相应调整否则训练会很不稳定。我试过 batch size 降到 4学习率不降结果 loss 直接炸了。后来按比例把学习率降到 2.5e-5 才稳住。4.3 梯度裁剪与训练稳定性DETR 训练过程中梯度偶尔会爆尤其是早期。论文里用了梯度裁剪把梯度范数限制在 0.1。这个值看起来很小但实测确实有效。我试过不裁剪训练到中途 loss 突然变成 NaN 的情况出现过好几次。加上裁剪之后虽然收敛还是慢但至少不会崩。另外混合精度训练AMP在 DETR 上要小心用。因为二分图匹配和一些损失计算对数值精度比较敏感纯 fp16 容易出问题。我一般用 AMP 的时候会把匹配和损失计算部分强制转成 fp32只在前向传播的主体部分用 fp16这样既能省显存又不影响稳定性。5. 从零跑通一个DETR训练流程的关键配置5.1 数据准备与标注格式转换DETR 官方实现用的是 COCO 格式的标注。如果你的数据是其他格式比如 YOLO 的 txt 或者 VOC 的 xml需要先转成 COCO 的 json。转换的时候有几个点要注意类别 id 要从 1 开始连续编号0 留给背景边界框要是绝对的 xywh 坐标不能是归一化的图像的宽高信息要准确否则后面做数据增强会出问题。数据增强方面DETR 论文用了随机水平翻转、随机缩放裁剪等。我实测下来水平翻转对效果提升最明显缩放裁剪要小心因为 DETR 对物体尺度比较敏感裁得太狠容易让小物体消失。另外DETR 的输入分辨率通常是短边缩放到 800长边不超过 1333这个配置在显存和效果之间比较平衡。5.2 模型初始化与预训练权重加载骨干网络用 ImageNet 预训练的 ResNet 是标配这个基本没有争议。Transformer 部分官方提供了预训练权重如果你是在 COCO 这种大数据集上训从头开始也行但如果是小数据集强烈建议加载官方权重做微调否则收敛会更慢。加载权重的时候有个坑官方权重里的类别数是 91COCO 的类别数如果你自己的数据集类别数不一样分类头的最后一层需要重新初始化。但别把整个分类头都重置只重置最后一层就行前面的共享层还是能复用的。我见过有人直接把整个 head 换掉结果训练效果差了一大截。5.3 损失函数权重的手动调整DETR 的总损失是分类损失、L1 框损失、GIoU 损失的加权和。论文里的权重是分类 1L1 是 5GIoU 是 2。这个权重不是随便定的L1 权重高是因为框的回归需要更强的监督信号。但实际用的时候这个权重不一定适合所有数据集。如果你的数据集里小物体多可以适当提高 GIoU 的权重因为 GIoU 对尺度不敏感对小物体更友好。如果类别极不平衡分类损失的权重可能要调低一点避免模型被多数类带偏。我调过几次发现权重变化对最终 mAP 的影响大概在 1-2 个点不算特别大但值得试。5.4 训练监控与早停策略DETR 训练过程中光看总 loss 是不够的因为总 loss 里包含了“无物体”类的分类损失这部分很容易降下去但 mAP 不一定涨。我一般会同时监控几个指标分类损失、框回归损失、以及验证集上的 mAP。早停策略上DETR 不太适合用传统的“验证集 loss 不降就停”因为它的 mAP 在后期还会缓慢提升loss 可能已经平了。我的做法是设一个比较大的 epoch 上限比如 300然后每 50 epoch 存一次 checkpoint最后挑验证集 mAP 最高的那个。这样虽然训练时间长但至少不会错过最佳点。6. 实测中那些文档不会告诉你的坑6.1 小目标检测的天然短板DETR 在小目标上的表现一直是个痛点。原因不难理解CNN 骨干下采样 32 倍之后小目标在特征图上可能就剩一两个像素了信息损失严重。再加上 Transformer 的全局注意力小目标的信号很容易被大物体淹没。我试过几种缓解办法。一是提高输入分辨率把短边从 800 提到 1000 甚至 1200小目标效果确实有提升但显存和计算量也跟着涨。二是用特征金字塔FPN替换单尺度特征这个改动比较大但效果最明显。三是调整对象查询的数量和初始化让更多查询去负责小目标区域这个比较玄学效果不稳定。如果你做的是小目标密集的场景我的建议是别硬上原版 DETR考虑用它的改进版本或者干脆在骨干后面加个 FPN。原版 DETR 在这个场景下确实力不从心。6.2 显存不够时的取舍DETR 的显存占用主要来自两块Transformer 的自注意力计算以及二分图匹配的代价矩阵。自注意力的显存和序列长度的平方成正比匹配的显存和 N×K 成正比。显存不够的时候能砍的地方有几个。第一降低输入分辨率这个最直接但影响小目标。第二减少编码器/解码器的层数论文是 6 层编码器 6 层解码器砍到 44 也能跑效果掉一点。第三减少对象查询数量 N从 100 降到 50但要注意别低于数据集里单图最大物体数。第四用梯度累积来模拟大 batch这个不省显存但能省 batch 的显存峰值。我自己的经验是优先降分辨率其次减层数最后才动 N。因为 N 一旦不够漏检是硬伤没法补救。6.3 推理速度与部署的现实考量DETR 的推理速度不算快。一方面 Transformer 的计算量大另一方面对象查询是固定 N 个不管图里有没有物体都要算完。在 GPU 上一张图大概几十毫秒比 YOLO 系列慢不少。部署的时候如果对速度有要求可以考虑几个方向。一是用 TensorRT 做图优化和量化INT8 量化后速度能提升不少但要注意精度损失。二是把骨干换成更轻的网络比如 MobileNet但效果会掉。三是只保留编码器的一部分层或者用一些稀疏注意力的变体这个改动比较大需要重新训练。如果场景对实时性要求极高DETR 可能不是最优选。但如果场景更看重端到端的简洁性和后处理的省心那 DETR 的优势就很明显了。6.4 类别不平衡与“无物体”类的处理“无物体”这个类在 DETR 里很特殊。它占了 N 个预测里的大部分因为大多数图里物体数远小于 N所以分类损失里“无物体”类的贡献很大。如果数据集本身类别就不平衡再加上这个因素模型很容易偏向预测“无物体”。缓解办法有几个。一是用 focal loss 替换普通的交叉熵降低易分类样本的权重。二是调整“无物体”类的损失权重把它调低一点。三是在匹配阶段对“无物体”的匹配加一些约束避免所有查询都往“无物体”跑。我试过 focal loss效果有提升但不如调权重来得直接。7. 我对DETR这套范式的个人判断DETR 刚出来的时候很多人觉得它要颠覆整个检测领域。几年过去原版 DETR 并没有成为主流但它的思想渗透到了很多后续工作里。二分图匹配、集合预测、端到端无 NMS这些概念现在在很多检测框架里都能看到影子。我个人对 DETR 的看法是它是一个“方向正确但工程上还不够成熟”的工作。它的简洁性是真的优雅后处理几乎为零训练目标统一这些优点在复杂场景下很有吸引力。但它的收敛慢、小目标弱、显存高也是实打实的短板。如果你做的是学术研究或者对端到端有强需求的场景DETR 值得深入如果你做的是工业部署、对速度和精度都有硬指标那可能得等它的改进版本或者结合其他技术一起用。最后分享一个我踩过的坑DETR 的官方代码里位置编码的实现有个细节它是在特征拉平之后加的而不是在二维特征图上加的。这个顺序如果搞反了效果会差很多。我一开始自己实现的时候就是加错了位置训了两周发现 mAP 死活上不去后来对照官方代码才发现问题。这种细节文档里通常不会强调但实际影响很大。所以如果你打算自己复现建议先把官方代码跑通再动结构别一上来就自己写。
阅读完成 · 觉得有帮助?
咨询建站