ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

DETR深度解析:Transformer如何颠覆目标检测的端到端范式

DETR深度解析:Transformer如何颠覆目标检测的端到端范式 把目标检测走到2020年前后主流方案基本被anchor和NMS统治。Faster R-CNN、YOLO、SSD这几位性能虽然越刷越高但套路还是老套路先密集铺anchor再对正负样本做匹配最后用NMS把重复框压下去。整个流程里手工设计的组件一个接一个训练时一堆超参数要调。直到Facebook AI那篇DETRDetection Transformer发出来整个圈子才意识到检测还能有另一种打开方式。这篇论文后来拿了ECCV 2020的Best Paper标题叫《End-to-End Object Detection with Transformers》核心思想用一句话就能说清把目标检测当成一个集合预测问题用Transformer去掉anchor、RPN、NMS这些手工组件。这篇文章主要面向两类人一类是已经跑过YOLO或Faster R-CNN、想深入理解DETR原理的深度学习者另一类是准备在自有数据集上复现、微调或部署DETR的工程师。读完你至少能弄明白三件事DETR为什么能端到端、它的损失函数为什么绕不开匈牙利匹配、以及它到底有哪些绕不过去的短板。1. 检测范式换了赛道从“滑窗猜”到“直接报数”1.1 传统检测器为什么越做越重在DETR之前主流检测器大致分两条路线。一阶段代表如YOLO和SSD直接在主特征图上做密集预测每个位置铺若干个预设的anchor框模型要学的是“这些框里面有没有物体、物体是什么类别、框要往哪个方向调”。二阶段代表如Faster R-CNN先用RPN提一堆候选框再逐框做ROI Pooling和分类回归。无论哪条路线都离不开anchor先验框的设计离不开对正负样本的筛选策略更离不开推理阶段的NMS去重。这些环节单独拿出来都说得通但串在一起就暴露出一个问题整个pipeline像一个手工拼装的大机器组件之间耦合很强。anchor的尺度和比例要针对数据集调正负样本的IoU阈值会影响训练NMS阈值稍微动一下mAP就可能出现明显波动。做检测的工程师很多时间其实不是在调模型而是在调这些后处理细节。DETR的出发点就是要打破这种局面。它把检测结果直接看成一组集合模型每次输出固定数量的预测通过二分图匹配让预测和真值一一对应。于是anchor没了、RPN没了、NMS也没了所有的检测逻辑都被收进了一个Transformer里真正做到了端到端。1.2 DETR整体流程快速过一遍DETR的完整结构可以切成三块CNN backbone负责提特征Transformer encoder负责全局建模Transformer decoder配合object queries负责查物体。具体流程是这样的输入一张图经过backbone得到特征图默认用ResNet-50输出通道2048空间分辨率是原图的1/32。接着用一个1x1卷积把通道降到256这一步是为了匹配Transformer的embedding维度。然后把特征图展平成序列每个空间位置就是一个token同时把每个token的空间位置编码加进去送进encoder。encoder输出的是经过全局建模的特征表示作者把它叫作memory。decoder这边输入是一组可学习的object queries默认100个每个query都会通过cross-attention去memory里“查询”对应的物体信息。最后每个query接一个FFN输出预测包括类别和归一化的bounding box坐标。整个模型训练完你给一张图它就固定输出100个预测。图片里可能有几个物体剩下的预测都是“背景”。用什么方式让100个预测和真实物体一一对上答案是匈牙利算法做二分图匹配这个在第三节细讲。2. 核心模块拆解每个组件都在解决什么问题2.1 Backbone与位置编码给序列补上空间信息DETR的backbone其实没有太多新东西选ResNet-50是因为它在ImageNet上pretrain效果好、结构成熟。有一个细节值得注意DETR只用backbone最后一层特征图没有像FPN那样做多尺度融合。所以在输入分辨率800x1066的情况下送给Transformer的特征图大概是25x34850个token。空间信息被压缩到很低的分辨率这一点后面会解释为什么小目标检测差。位置编码在这套结构里非常关键。Transformer本身是置换等变的它根本不关心token之间的相对位置。图片的特征图如果被打平成序列而不加位置信息模型就不知道哪个token在上哪个token在下。DETR用的是空间位置编码对x和y方向分别做不同频率的正弦编码然后加到每个token的特征上。它不是学出来的是固定公式生成的好处是能泛化到不同的输入分辨率。这里有个实操容易踩的坑如果你自己实现或后来用DAB-DETR这类变体位置编码的维度、加在query还是key上、以及是否在每层attention前重加都会直接影响最终效果。原始DETR的做法是在encoder和decoder每一层的输入上都加位置编码不是只在最前面加一次。2.2 Encoder与全局建模换个角度理解注意力Transformer encoder在DETR里承担的任务是把backbone输出的局部特征变成有全局上下文信息的特征。CNN的卷积核感受野是有限的高层特征虽然能看到更大的区域但要建模两个距离很远物体之间的关系靠堆卷积始终不自然。self-attention不一样它让每个token都能直接看到序列里的所有token一次建模全局依赖。从计算量来看self-attention复杂度是O(N^2)N是token数量。850个token的注意力矩阵是850x850这个规模对GPU来说压力不大。真正的问题不在于计算量而在于收敛速度。DETR论文里明确说过encoder的全局注意力需要大量训练才能学会“该关注哪里”这也是它500个epochs才能收敛的重要原因之一。那为什么还要用encoder因为在目标检测里一个物体是否成立往往取决于它和其他物体的关系。比如图片里有一辆车你是通过“它下面有路面、周围有行道树”来确认的。全局建模能让模型对遮挡、重叠、语义歧义更鲁棒。DETR的实验也显示去掉encoder或者减少encoder层数AP会明显下降。2.3 Decoder与object queries用“提问”代替“滑窗”Decoder是DETR里最有意思的部分。object queries初始是100个可学习的向量每个向量256维。它们不依赖输入图片训练之初随机初始化然后在训练中被逐步塑造成“不同类型查询器”有的query负责查询大目标、有的负责小目标、有的专门关注图片左上角区域。每一层decoder的cross-attention中query会与encoder输出的memory做注意力运算相当于每个query主动向特征图提问“这里有没有我负责的那种物体”decoder一共有6层每层都把前一层输出送去FFN做一轮预测并参与损失计算这就是auxiliary loss辅助收敛。有个容易混淆的点object queries为什么是100个这个数字代表模型最多预测100个物体不是网络结构必须用100。如果你在自有数据集上训练单张图里目标可能远超100个就要加大query数量。反过来如果目标数量少减到50或30也能跑但一般不推荐贸然改动因为匹配数量会影响匈牙利匹配的分布。2.4 FFN与输出头让每个query给出最终预测Decoder输出一个query向量后后面接的FFN其实是一个很小的多层感知机负责把向量映射成预测结果。预测分两部分一部分是类别logits维度等于类别数加1背景类另一部分是box坐标输出4个值分别是中心点cx、cy、宽度w、高度h全部做了归一化范围在0到1之间。这里的box预测没有用anchor回归也没有边界框偏移约束直接回归绝对坐标。之所以能这么做是因为二分图匹配已经解决了预测和真值的配对问题每个query稳稳对应一个真值回归任务相对干净。但如果box坐标不归一化不同尺寸的物体回归难度差异会很大。所以DETR在损失里同时用L1损失和GIoU损失前者保证坐标精度后者让框的重合度更好优化这一块在下一节展开。3. 二分图匹配与损失函数端到端的关键一步3.1 为什么必须用匈牙利匹配既然模型输出的是100个无序预测真值可能只有几个怎么让模型知道哪个预测对应哪个真值如果沿用传统检测器的做法按IoU阈值匹配那又回到了人工设计规则的老路。DETR的办法是把预测和真值当成两个集合做二分图匹配找到一个最优的一一对应关系。数学上就是匈牙利算法解决的问题。给定一个代价矩阵矩阵第i行第j列表示第i个预测与第j个真值之间的匹配代价算法会找出一个代价最小的匹配方案。重点在于这个匹配是在每个训练batch内动态计算的当前模型的预测结果决定了每个query学哪个物体而不是一开始就固定。这种动态匹配让训练相当灵活但也带来了一个弊端匹配结果不稳定训练前期预测很烂匹配也乱模型容易陷入次优解这是DETR收敛慢的另一个原因。3.2 匹配代价和训练损失怎么设计匹配代价决定了匈牙利匹配的“偏好”。DETR的匹配代价由三部分构成分类负log概率、box的L1距离、box的GIoU负值。实现时有一个细节匹配用的是“分类负log概率”而不是交叉熵loss因为匹配阶段只关心预测置信度排序不希望额外引入不必要的梯度。匹配完成之后真正的训练损失才在匹配好的配对上进行。训练损失同样由分类损失、L1损失和GIoU损失组成但分类用的是标准交叉熵背景类要单独设置权重。DETR实验里把背景类的权重调低到0.1这个值直接影响前景背景的平衡。有一个问题是L1损失对框的尺寸敏感。同样的绝对误差大框和小框的L1数值差异很大。DETR的做法是box坐标归一化用相对于图片尺寸的比例来表示减小尺度影响。但即便归一化L1对微小偏移和大偏移的惩罚是线性的优化GIoU能缓解这部分问题。GIoU的取值在-1到1之间它考虑了框之间的重叠区域、闭合区域和空白区域比单纯IoU更能提供可优化的梯度信号。匹配代价和训练损失为什么保持同构因为如果匹配阶段和训练阶段的标准不一致模型会学偏。比如匹配时用IoU训练时用L1那匹配找出来的最优配对可能在训练损失下并不是最好的整个优化方向就乱了。3.3 训练超参和收敛问题DETR论文里的训练配置值得所有复现者拿小本本记下来。默认ResNet-50骨干embedding维度256encoder和decoder各6层8个注意力头object queries数量100训练500个epochs。优化器用的AdamW初始学习率1e-4backbone部分单独用1e-5weight decay设为1e-4。数据增强包括随机裁剪、随机缩放这一点和传统检测器差异不大。最大的槽点是收敛速度。Faster R-CNN在COCO上训练12个epochs就能达到不错效果DETR需要500个epochs才能到42.0 APResNet-50中间整整差了一个数量级。作者在论文里用16张V100训练了大概3天时间这对个人开发者来说门槛相当高。如果你自己复现我的建议是初期实验用5到10个epochs看loss下降趋势不要一上来就冲500 epochs。另外dropout设为0.1、grad clip设为0.1这两个trick能有效稳定训练论文里没有强调但很多复现实验都验证了它们的重要性。3.4 推理阶段NMS被彻底拿掉DETR推理时直接取100个预测里置信度高于阈值的框不需要NMS。为什么不需要因为object queries经过训练后同一个物体大概率只有一个query能激活。即便有两个query同时预测到同一个物体匈牙利匹配的训练目标会让它们分化。这是set prediction范式的自然结果。没有NMS带来两个直接好处一是推理pipeline更短二是不会有NMS阈值超参需要调。但严格来说DETR输出偶尔还是会出现少量重复框不过重复框的置信度通常差别很大过滤阈值就能解决大部分。实际工程里如果你追求极致精度也可以在输出端加一个轻量的NMS兜底但这不是必须的。4. 实践中的坑与改进方向哪些问题必须正视4.1 DETR收敛慢到底卡在哪很多人复现DETR第一反应是我的loss怎么下降得这么慢这是正常的DETR收敛慢是结构性的不是训练细节问题。根本原因有两个。一个是全局注意力在训练初期不知道关注哪里self-attention需要大量样本才能建立起空间对应关系。另一个是匈牙利匹配的动态性预测差导致匹配差匹配差导致监督信号弱形成恶性循环。匹配关系每步都在变模型很难稳定学到一致的映射。传统检测器用anchor或RPN先把候选位置限制在很小的范围模型只需要做局部精修。DETR把“找物体”这件事完全交给注意力机制自由度更高但学习难度也更大。所以不能用训练YOLO的思维去训DETR必须给它足够的训练预算。缓解收敛慢的几种常见做法加auxiliary loss每个decoder层都算损失、用AdamW而不是SGD、预热学习率、dropout和grad clip配合使用。这些都能让训练更快稳定下来但不会从根本上解决慢的问题。4.2 小目标检测差问题出在特征分辨率DETR在COCO上大中目标的AP还不错小目标AP明显偏低。原因其实很直白它只用backbone最后一层1/32特征图小目标在这个分辨率下可能只占一两个像素信息已经丢失Transformer再强大也无法凭空恢复。再加上self-attention是全局的小目标的注意力容易被大目标稀释两个因素叠加小目标性能自然拉胯。如果你的任务主要是小目标检测直接上原版DETR大概率会很难受。要么换成Deformable DETR这类带多尺度特征的设计要么在输入分辨率上做文章比如测试时用更高分辨率。原版DETR在输入尺寸升高时性能会有提升但计算量和显存消耗也会跟着涨需要自己权衡。4.3 改进方案怎么选从Deformable DETR到DINODeformable DETR是2021年提出来的改进版解决两个痛点收敛慢和多尺度。它借鉴了可变形卷积的思想注意力只在每个query的参考点附近采样少量点而不是全图计算。这种稀疏注意力把计算复杂度从O(N^2)降到了O(N*K)K是采样点数也让多尺度特征融合变得可负担。在COCO上Deformable DETR用10个epochs就能达到DETR用500个epochs的效果收敛速度快了将近50倍。DAB-DETR则是把object queries从“隐向量”改成了“可学习的anchor box”每个query自带位置和尺寸信息让decoder的查询目标更明确。DN-DETR引入去噪训练把带噪声的真值框喂给decoder学重建大大缩短收敛时间。DINO综合了DAB和DN的思路在检测精度上连续霸榜多年。到了RT-DETR百度把实时性也解决了用混合编码器和不确定性最小化让Transformer检测器也能跑到实时水平。如果你想在工程里用DETR系列我的建议是直接跑原版DETR理解原理但落地优先考虑Deformable DETR或DINO。原版DETR更多是学术价值和范式的意义实用层面后面这些变体才是真正能上线的版本。4.4 部署DETR系列的几个注意点部署Transformer检测器和部署YOLO这种CNN模型有不少差异容易踩坑。第一个坑是动态shape。推理时如果输入尺寸可变Transformer的序列长度会变ONNX导出时最好固定输入尺寸或者用多档分辨率预先优化。第二个坑是位置编码。DETR用正弦位置编码导出时要把位置编码的计算保留在模型图内不要在外部拼凑。第三个坑是后处理。虽然不需要NMS但100个预测的过滤和框的解码在CPU上实现也要注意效率如果是TensorRT部署可以把过滤逻辑写在plugin里避免频繁的GPU-CPU数据传输。内存方面Transformer的attention中间变量在推理时占内存不小尤其batch size大的时候。实测中用TensorRT部署Deformable DETRFP16精度下精度损失很小速度比PyTorch原生推理快3到5倍算是一个比较稳的部署方案。5. 在自有数据集上训练DETR一个可参考的流程5.1 数据准备与num_classes修改DETR官方代码支持COCO格式数据集你自己训练最省事的路径是把手头的数据转成COCO标注格式再用官方dataloader读取。如果你用Detectron2做后端把数据集注册成自定义name改掉category数就行。如果数据格式不是COCO你需要自己写一个dataset类返回image和对应的boxes、labels。关键修改点就是model头部的num_classes以及object queries数量是否匹配你的数据。经典COCO是80类你的任务如果是比如5类model里class_head的线性层参数会变这个直接改配置文件即可。object queries数量一般保持100就好除非你确认单张图里可能出现超过100个目标那就加大到150或200代价是推理时多算一些背景预测。5.2 关键超参数设置参考我自己训过自定义数据的DETR一个比较稳的配置是这样的输入分辨率用800x1333batch size和显存挂钩8卡或单卡能放多少放多少但不建议单卡batch小于2否则BN统计都不好算。学习率用1e-4backbone部分降为1e-5预训练权重一定得加载否则收敛更慢。训练轮数按数据量来小数据集几千张用50到100个epochs大数据集可以到300个epochs以上。如果你的数据量大且用的是Deformable DETR训练轮数可以大幅缩减到50个epochs左右。数据增强方面随机裁剪、随机缩放这些增强都能用。特别提醒一点如果你的数据集图片尺寸差异很大建议用固定短边缩放到统一尺寸不要直接resize否则物体形变会被模型学进去。5.3 常见问题速查表问题现象可能原因解决建议训练loss不降学习率过高或过低加载预训练权重失败确认backbone预训练加载lr设为1e-4附近内部层1e-5输出全是背景object queries过少或者分类loss权重失衡增大queries数量检查背景类权重是否设置过低收敛极慢几十epoch还没起色没有auxiliary loss或优化器用错开启auxiliary loss换AdamW设grad clip 0.1小目标基本检不到特征图分辨率低、多尺度缺失换Deformable DETR或提高输入分辨率推理时ONNX导出报错位置编码或动态序列长度固定输入尺寸把位置编码包含在图内计算同一目标多个重复框置信度阈值过低或query训练分化不足提高阈值必要时输出端加轻量NMS兜底6. 我的个人体会DETR最让我佩服的地方不是它精度刷得有多高而是它把检测问题重新定义成了一个纯粹的学习问题。它告诉我们那些年我们精心设计的anchor、NMS其实不是目标检测的必需组件只是当时模型能力不足时用来弥补结构缺陷的拐杖。Transformer凭借强大的建模能力直接把拐杖扔了。当然原版DETR的问题也是实实在在的。我自己用单卡V100复现的时候光是在COCO上跑完500个epochs就不现实更别提调参了。所以我的建议是如果你刚接触DETR先跑通一个小的子集看看效果理解它的loss和匹配机制再决定是否上完整训练。如果你想直接落地一定要研究Deformable DETR和DINO这些变体它们解决了原始DETR最难用的那几个痛点。最后分享一个小技巧分析DETR中间attention图是理解它在学什么的最好方式。把decoder不同层的cross-attention可视化出来你会看到query从最初的均匀分布慢慢收敛到某个物体附近这个过程非常直观地展示了“集合预测”是如何做到的。用好这个可视化方法你对DETR的理解会比单纯读论文深得多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进