ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

DETR模型结构全解析:端到端目标检测的Transformer革命

DETR模型结构全解析:端到端目标检测的Transformer革命 DETR 这个模型搞目标检测的朋友应该都不陌生。从 2020 年 Facebook AI 团队放出那篇《End-to-End Object Detection with Transformers》开始它就把目标检测从“一摞锚框加 NMS 后处理”的套路里拽了出来直接变成了“一个 Transformer 吃进去特征吐出一组预测”的端到端结构彻底绕开了手工设计的锚框和非极大值抑制。当时我看完论文第一反应是原来检测还能这么玩后来自己复现、调参、魔改踩了不少坑也实打实体会到了这套结构的设计精妙之处。这篇文章就围绕 DETR 的模型结构从设计思路到逐模块拆解再到训练里的实际问题和扩展方向一次性讲透。这篇内容适合正在学目标检测的学生、想切换技术方案的工程师以及所有对 Transformer 如何落地视觉任务感兴趣的人。我会把每个模块的运作机制、尺寸变化、关键参数都写清楚也会补上论文里不会写、但在工程里非常要命的细节。1. 整体设计思路为什么检测任务需要 Transformer1.1 传统检测的痛点锚框、NMS 和一地鸡毛在 DETR 之前主流检测器可以分成两大类两阶段比如 Faster R-CNN和单阶段比如 YOLO、SSD。无论哪一类都逃不开几个设计上的“老顽固”。首先是锚框你得事先给特征图的每个位置铺上不同尺度、不同长宽比的框让模型去判断每个框里有没有物体、框要往哪调。这一步需要手工设计锚框的尺度、比例、数量每个数据集可能还得重新聚类调参。其次是 NMS模型会在一张图上预测出大量高度重叠的候选框你必须通过非极大值抑制把这些框压成最终结果这个后处理的阈值很敏感调高了漏检调低了误检而且 NMS 卡在 GPU 上做不了延迟很头疼。最后是一堆基础设施的耦合或者说需要一堆 nested 的模块去兜底。DETR 的思路是干脆把目标检测当作一个直接集合预测问题让模型自己学习“图上到底有几个物体、分别在哪儿、是什么类别”一步到位不再依赖任何手工设计的几何先验。这个想法看起来简单做起来很难因为它需要模型具备很强的全局建模能力才能在一张图上同时处理好所有物体之间的关系。而这恰好是 Transformer 的主场。1.2 集合预测与二分匹配检测变成了一一对应问题检测可以简化成这样一句话给定一张图输出一个固定大小的集合每个元素是 (类别, 归一化坐标, 归一化宽高) 这样的五元组。关键难点在于模型输出的顺序是任意的你没法像分类任务那样直接用交叉熵算损失因为模型可能把“第一辆车”预测成了“第二个人”的位置单纯按顺序比梯度就乱套了。DETR 把这个问题转化成二分图匹配问题。假设我们设定模型最多预测 N 个物体超参数 num_queries论文里默认 100那模型输出一个长度为 N 的预测序列。真实标签不足 N 个就填充成一个“无物体”类让两边数量对齐。然后通过匈牙利算法在预测序列和真实标签之间找到一个损失最小的一一对应关系。这个匹配只看整体开销最低不管预测顺序所以模型天然学会了“谁离哪个目标最近就负责预测谁”。这步是 DETR 能扔掉 NMS 和锚框的基石。1.3 Transformer 为什么适合做这个事Transformer 的核心是自注意力机制。自注意力会对输入序列中任意两个位置计算相关性等于让每个输出位置都能“看到”全图的上下文。传统 CNN 要靠堆叠卷积层和扩大感受野才能一点点看到更大的区域Transformer 一步到位。对目标检测来说这非常关键比如要检测一个被树干挡了一半的骑行者光看局部特征很难判断但自注意力可以把远处的车把、轮胎和身体部分关联起来组合出完整的物体语义。另外DETR 的输出是一组并行的 object queries每个 query 经过 decoder 的交叉注意力后会专门去特征图上的某个区域“取信息”这个过程等于模型自己学会了分配注意力区域不再需要人为设计“每个位置负责预测哪个框”的规则。1.4 DETR 整体结构一图流从输入到输出DETR 的完整链路是输入图像 → CNN Backbone → 特征图 → 展平并加位置编码 → Transformer Encoder → 记忆特征 → Transformer Decoderobject queries 与记忆特征做交叉注意力 → 预测头分类 FFN 回归 FFN → 匈牙利匹配 → 损失计算整个结构可以分成四个大块CNN Backbone 负责提特征Transformer Encoder 负责在全局范围内做特征交互Transformer Decoder 负责从 object queries 出发“查询”目标信息最后的 FFN 预测头负责输出分类和边框坐标。后面逐个拆。2. 模型结构核心模块逐层拆解2.1 Backbone用 CNN 先把图像变成特征序列DETR 的 Backbone 一般用 ResNet-50 或 ResNet-101也可以换更强的骨干网络。输入图像经过数据增强后统一缩放到 800 × 1333论文里短边 800、长边不超过 1333。图像经过 ResNet 的 stage 1 到 stage 4最终输出一个下采样 32 倍的特征图也就是如果输入是 800 × 1066特征图尺寸是 25 × 34 左右通道数 C 2048ResNet-50 的 stage 4 输出。因为这个特征图是三维的而 Transformer 期望输入是序列形式所以会把空间维度展平。假设特征图是 H × W × C展平后就是 (H×W) × C 的序列序列长度是 H×W。比如 25 × 34 的特征图展平后就是 850 个 token每个 token 的维度是 2048。这里可以直接用 1×1 卷积把通道压缩到 d_model 256一方面降低计算量另一方面和 Transformer 内部的特征维度保持一致。后面所有 Transformer 操作都在 256 维空间里做。2.2 位置编码没有它Transformer 分不清“上下左右”自注意力本身是置换等变的对 token 的出现顺序不敏感。但图像特征是二维的物体位置信息对检测来说根本不能丢所以必须把位置信息编码进特征里。DETR 使用的是一个固定形式的二维正弦位置编码不用学习。具体做法是对特征图上的每个位置 (x, y)分别用不同频率的正弦和余弦函数生成一组向量。比如对 x 坐标生成一串值(\sin(x / 10000^{2i/d}))、(\cos(x / 10000^{2i/d}))其中 i 是维度的索引d 是编码维度。y 坐标同样处理。这样每个位置都有一个独一无二的编码向量并且不同位置之间的编码差异有平滑的几何意义——相邻位置编码相似远处位置编码差异大。DETR 论文里还专门提到尝试过可学习位置编码效果和固定编码接近但固定编码在迁移到更长序列时更稳。最后位置编码和展平后的图像特征相加作为 Encoder 的输入。这里有个容易被忽略的细节位置编码是在每个 attention 层都加的不是只在最前面加一次。Transformer 的 Encoder 是 L 层堆叠的每一层的自注意力输入都要重新加上位置编码重新注入位置信息Decoder 里同样如此query 侧和 key 侧的位置信息都要处理。2.3 Encoder全局特征交互让每个点都“知道”全局DETR 的 Encoder 结构就是标准 Transformer Encoder论文默认堆叠 6 层。每一层包含多头自注意力、前馈网络FFN、残差连接和层归一化。输入是展平后的特征序列加上位置编码。Encoder 的作用是让特征图上的每个位置都能聚合全局信息。传统的 CNN 越往上走每个位置的感受野越大但依然有限。Transformer 的自注意力直接计算所有位置两两之间的关系不管两个位置相隔多远都能直接建立联系。这个特性对检测特别有用。简单场景可能不明显但到拥挤场景、小物体互相遮挡的场景Encoder 能把不同物体的上下文信息分离开帮助后续 Decoder 更精准地定位。另外DETR 论文里有个很有意思的观察Encoder 最后一层的注意力图已经表现出很强的实例分离能力也就是说即使没有经过 DecoderEncoder 已经学会了把注意力集中在不同的物体上。这也说明全局自注意力确实在建模物体级语义而不是简单的像素相关性。实际使用中Encoder 层数可以调节通常 6 层是效果和速度的较好平衡点。2.4 DecoderObject Queries 是怎么“查”出物体的Decoder 是整个 DETR 最核心、也最难理解的部分。它同样堆叠 6 层每层有三个子层自注意力处理 object queries 之间的关系、交叉注意力object queries 去 Encoder 输出的特征中查询信息、前馈网络。输入是一组学习出来的 object queries。object queries 是一组形状为 (N, d_model) 的可学习参数N 是预设的最大检测数量默认 100。你可以把它理解成 100 个“检测器”每个检测器最终负责输出一个预测结果。初始时它们是随机初始化的向量不包含任何图像信息在训练中会逐渐学习到“我的职责是去特征图的某个区域找某种类型的物体”。因此这 100 个 query 会query 之间通过第一层自注意力做信息交互。这里有个关键机制自注意力让不同 query 之间能够相互“避让”避免多个 query 同时瞄准同一个物体这其实是 DETR 能替代 NMS 的一个重要原因。理想情况下训练完毕后 100 个 query 会“各司其职”每个 query 学会关注特征图的不同区域和不同语义类别。交叉注意力是 Decoder 里的重头戏。每个 query 作为 QueryQEncoder 输出的特征作为 KeyK和 ValueV通过 scaled dot-product attention 计算 query 对特征图上所有位置的注意力权重然后按权重加权聚合特征。这个步骤可以理解为每个 query 在特征图上“找自己关心的内容”找到后把信息取回来用于后续的类别预测和边框回归。论文里有个经典的可视化训练好的 DETR 中每个 object query 的注意力图往往聚焦在图像上的某个局部区域而且不同 query 的聚焦区域分布得很均匀。这意味着模型确实学到了“把图像分区管理”的策略属于自组织涌现出来的行为非常有启发性。2.5 预测头与输出一个 FFN 同时输出类别和边框Decoder 的输出是 (N, d_model) 的向量序列最后接一个预测头。预测头非常简单包含两个分支分类分支一个线性层把 d_model 映射到类别数 1加的那个 1 是“无物体”类 background得到每个 query 属于各类别的概率。回归分支一个 3 层 MLP隐藏层维度 256激活函数 ReLU输出 4 个值分别是物体中心点的归一化坐标 (cx, cy) 和归一化宽高 (w, h)。需要注意DETR 预测的边框坐标是经过归一化的数值范围在 0 到 1 之间代表相对于原始图像宽高的比例。训练时计算损失会把这些值乘回原图尺寸计算。由于输出没有经过任何空间先验约束直接预测全局坐标确实比锚框回归难一些这也是 DETR 训练收敛慢的原因之一后面会讲怎么缓解。2.6 损失函数匈牙利匹配后的一一对应损失DETR 的损失设计是整个结构里最精巧的部分。它分为两步第一步用匈牙利算法做最优匹配。把真实标签不足 N 个时用空标签填充和模型预测的 N 个结果构造成一个 N×N 的代价矩阵。代价矩阵的每一项是预测结果和真实标签之间的匹配代价包括分类代价用负概率、L1 边框代价和 GIoU 代价。代价矩阵的每一项 分类代价 L1 代价 GIoU 代价。然后在这个矩阵上求解最小匹配得到一对一的对应关系。第二步对匹配上的预测结果和真实标签计算最终损失损失由三部分组成分类损失交叉熵损失匹配上的就按真实类别算没匹配上的按背景类算。这里有个细节类别不平衡问题很严重因为 100 个 query 里通常大量是背景所以背景分类的损失权重应该降下来论文里的做法是在分类损失中给背景类一个较低的权重或者直接对无物体类的 logits 乘一个小系数。L1 回归损失预测边框和真实边框的 L1 距离但 L1 对大框和小框的误差尺度不同所以不能单独用。GIoU 损失计算预测框和真实框的交并比能弥补 L1 对尺度不敏感的缺陷让回归更关注框的重叠程度。论文中的默认权重是分类损失权重 1、L1 损失权重 5、GIoU 损失权重 2。这个权重配比在实际训练中表现很好可以先照搬再根据任务微调。2.7 整体尺寸变化流程为了帮助理解我把 DETR 里面每一步的张量尺寸变化写出来方便核对代码或者画图步骤输入尺寸输出尺寸说明输入图像3 × H × W3 × H × W归一化到 [0,1]ResNet Backbone3 × 800 × 10662048 × 25 × 34下采样 32 倍1x1 Conv 降维2048 × 25 × 34256 × 25 × 34压缩通道展平256 × 25 × 34850 × 256序列化成 token加上位置编码850 × 256850 × 256逐元素相加Encoder 输出850 × 256850 × 2566 层堆叠Object Queries100 × 256100 × 256可学习参数Decoder 输出100 × 256100 × 2566 层堆叠分类分支100 × 256100 × (C1)线性层回归分支100 × 256100 × 43 层 MLP这组数据是 ResNet-50、输入尺寸 800×1066、num_queries100 的情况。实际中特征图尺寸会随输入变化但流程是固定的。3. 实操要点与关键参数配置解析3.1 num_queries 怎么选num_queries 是一个非常重要的超参数代表模型最多能在一张图上预测多少个物体。论文默认 100这基本覆盖了 COCO 数据集中单张图片物体的绝大多数情况。但实际任务里要谨慎选择如果场景中经常出现 100 个以上的目标比如密集人群计数、航拍图像里的车辆统计100 个 query 会不够用必须调大比如 300 或者 900。反过来如果任务场景很简单比如工业零件检测一张图就三五个目标可以减小 num_queries 到 50 甚至更低能减少内存占用和计算量。有一个常见的坑num_queries 设得太大会导致无物体类的样本急剧增多训练时背景类损失占主导模型可能偏向于保守预测检测召回率下降。解决办法是调整分类损失中背景类的权重或者用 focal loss 类的方法缓解类别不平衡。如果你的任务里目标数量波动极大建议先统计分析数据集的分布再定 num_queries。3.2 Transformer 的 d_model、层数、头数该怎么配d_model 是 Transformer 内部特征维度。论文默认 256配合 ResNet-50 的 2048 维输出做通道压缩。如果你的显存充足、数据集不大但需要强特征表达能力可以把 d_model 调到 384 或 512对应注意力头数也调整。头数一般是让 d_model 能被整除比如 256 用 8 个头每个头维度 32512 用 8 个头每个头 64有些实现用 16 个头。Encoder 和 Decoder 层数默认都是 6。层数越多模型表达能力越强但收敛速度更慢、显存占用更高。实际项目中如果数据量不大可以减少到 3-4 层效果下降不明显但训练速度提升明显。Decoder 层数对性能的影响比 Encoder 更大因为 Decoder 是真正做目标查询的地方。我看过的经验是Decoder 从 6 层减到 3 层AP 掉 1-2 个点Encoder 从 6 层减到 3 层AP 掉 0.5 左右。如果算力紧张优先砍 Encoder。3.3 训练超参数与收敛技巧DETR 最出名的槽点就是收敛慢。原论文里用 500 个 epoch 训练相比 Faster R-CNN 的 12 epoch 简直是天文数字。虽然实际复现时可以用更好的初始化、更强的增强来缩短但依然要做好训练时间较长的心理准备。我这里列几个亲测有效的加速收敛技巧使用更强的数据增强DETR 对数据增强非常敏感。随机裁剪、随机缩放、颜色抖动这些操作能明显提升小样本下的效果和收敛速度。原论文在 COCO 上的训练用了随机裁剪、多尺度缩放等手段。我自己实验发现加了随机裁剪后AP 能提升 2-3 个点而且模型更稳。学习率要低且配合 warmupDETR 使用的优化器是 AdamW基础学习率 1e-4backbone 部分学习率通常是 1e-5因为预训练的 ResNet 不需要大幅度更新。训练初期需要 warmup一般前 5% 的迭代从很小的学习率线性升到目标学习率否则容易因为梯度突变导致训练不稳定。梯度裁剪Transformer 结构容易在训练初期产生较大梯度强烈建议设置 max_norm0.1 的梯度裁剪。这个操作几乎成了 DETR 训练的标配不加容易 NaN。推理时的置信度阈值DETR 输出的背景类概率很高实际推理时通常设置置信度阈值为 0.5 左右。但阈值非常依赖数据集和训练程度建议在验证集上画 PR 曲线后再定。3.4 关键代码实现片段这里给一个简化版的结构定义伪代码方便理解各个模块的连接关系。实际实现可以参考官方代码但核心逻辑基本一致。import torch import torch.nn as nn class DETR(nn.Module): def __init__(self, num_classes, num_queries100, d_model256, nhead8, num_encoder_layers6, num_decoder_layers6): super().__init__() # Backbone: 可以用 torchvision 的 resnet50去掉最后的全连接和 avgpool self.backbone resnet50(pretrainedTrue) self.backbone nn.Sequential(*list(self.backbone.children())[:-2]) # 输出 2048xH/32xW/32 # 通道压缩 self.input_proj nn.Conv2d(2048, d_model, kernel_size1) # Transformer 模块简写实际需要自己实现 Encoder/Decoder 或复用 nn.Transformer self.transformer nn.Transformer( d_modeld_model, nheadnhead, num_encoder_layersnum_encoder_layers, num_decoder_layersnum_decoder_layers, dim_feedforward2048, dropout0.1, batch_firstTrue ) # Object queries self.query_embed nn.Embedding(num_queries, d_model) # 分类头 self.class_embed nn.Linear(d_model, num_classes 1) # 回归头 self.bbox_embed MLP(d_model, d_model, 4, num_layers3) def forward(self, images): # images: (B, 3, H, W) features self.backbone(images) # (B, 2048, H/32, W/32) features self.input_proj(features) # (B, 256, H/32, W/32) B, C, H, W features.shape features features.flatten(2).permute(0, 2, 1) # (B, H*W, 256) pos positional_encoding(H, W, C).repeat(B, 1, 1) # (B, H*W, 256) # Transformer 前向 memory self.transformer.encoder(features pos) queries self.query_embed.weight.unsqueeze(0).repeat(B, 1, 1) # (B, num_queries, 256) hs self.transformer.decoder(queries, memory, tgt_maskgenerate_square_subsequent_mask(num_queries)) # 输出 outputs_class self.class_embed(hs) # (B, num_queries, num_classes1) outputs_coord torch.sigmoid(self.bbox_embed(hs)) # (B, num_queries, 4) return outputs_class, outputs_coord这里有个细节在自回归式 Transformer 里Decoder 通常需要带 mask防止看到未来的 token。但 DETR 的 object queries 是并行输入的它们之间不存在先后顺序关系所以推理时不需要 causal mask。不过有些实现里会在 Decoder 自注意力上额外加一个可以学习的注意力 mask 来促进 query 之间的差异化这是可选的优化不是必须的。3.5 推理阶段没有 NMS 是不是真的干净DETR 推理流程是图像输入经过整个网络输出 100 个预测结果按类别置信度过滤掉低于阈值的框剩下的就是最终结果。理论上不需要 NMS因为每个 query 只负责一个目标并且 Decoder 自注意力已经让 query 之间产生了“避让”行为。但在实际中我发现如果训练不充分或者场景很复杂还是会出现少量重复检测。这种情况下可以加一个轻量级的 NMS阈值设得松一点比如 IoU 0.7用来清理边缘情况。这个操作论文里是谴责的但在工程里为了保交付也无伤大雅。当然如果你的模型训练到位NMS 是完全不需要的这也就是 DETR 相对传统方法最大的工程优势——少一个超参数敏感的后处理环节。4. 常见问题与排查技巧实录4.1 训练不收敛或 loss 震荡这是 DETR 新手最容易碰到的问题。我见过不少人在自定义数据集上跑 DETR发现 loss 一直在抖AP 提不上去。大多数情况下是训练配置问题而不是模型结构有问题。首先检查学习率。DETR 对学习率很敏感基础学习率 1e-4 是 COCO 上的经验值但小数据集上往往需要更低的学习率。如果 loss 出现周期性的大幅跳变建议把学习率降到 5e-5 试试。其次是梯度裁剪这是 DETR 训练稳定性的关键。我见过把 max_norm 从 0.1 改成 1.0 后训练开始出现 NaN所以这个参数不要乱调。最后是数据增强小数据集上如果增强不够模型很容易过拟合训练集表现为训练 loss 下降但验证 AP 上不去。建议在训练集上做随机裁剪和多尺度训练。4.2 Object Queries 坍塌多个 query 总预测同一个物体理论上 Decoder 自注意力会让 query 之间“避让”但实际中如果模型容量不够或者训练不充分会出现多个 query 收敛到同一个物体上的情况。表现形式是一张图里某个目标被预测了 3-4 遍而其他目标漏检。排查思路先看 Decoder 自注意力的权重可视化如果发现某些 query 的注意力分布几乎一致说明 query 没有充分分化。解决办法有几种一是增大 num_queries给模型更多“岗位”减少竞争压力二是加辅助损失在每个 Decoder 层都计算损失并反传让每层都往匹配结果靠拢这个方法在 DETR 官方实现中默认开启三是人为初始化 query 的位置信息比如把 100 个 query 的初始位置均匀铺在特征图上这样它们初始时各管一摊分化更快。第三种方法在一些改进版本里用到了效果很好。4.3 小目标检测效果差DETR 在 COCO 上的小物体 AP 一直不占优势这是它的一个短板。原因是Encoder 在全局自注意力计算中小物体对应的 token 数量少注意力容易被大物体或者背景淹没另外 32 倍下采样的特征图对小物体本身就不友好。实际工程中缓解手段有三个层面。第一层是特征层面用更高分辨率特征图比如把 Backbone 的下采样倍数从 32 降到 16也就是把 ResNet 的 stage 4 输出 stride 改小或者直接融合多尺度特征类似 FPN 的思路这能明显提升小物体召回。第二层是结构层面使用 Deformable DETR 这样的改进版本它把注意力限制在稀疏的采样点上对小目标更友好而且计算量大幅下降。第三层是训练层面加大随机裁剪和尺度扰动让模型见到更多小尺寸目标。如果你必须用原版 DETR 结构做小目标检测建议优先把特征分辨率提上去其余都是次要的。4.4 显存不足怎么办DETR 的显存占用主要在 Encoder 的自注意力序列长度是特征图所有位置的乘积在 800×1333 输入下约 1700 个 token自注意力的中间矩阵是 1700×1700乘以 batch size 和层数显存涨得很快。省显存的方法从易到难有几种降低输入分辨率比如把短边从 800 降到 640序列长度直接降到原来的 0.64 倍缩减 Encoder 层数Encoder 占了大量计算使用梯度累积等效增大 batch size 而不用一次装下整个 batch或者直接换用 Deformable DETR它的注意力只在少量采样点上计算显存占用低一个量级是目前工程实践中的首选方案。4.5 常见问题速查表问题可能原因排查/解决手段训练 loss 不降学习率过高/低、数据增强不足调低学习率至 5e-5加数据增强启用 warmup训练中出现 NaN梯度爆炸设置梯度裁剪 max_norm0.1重复检测多Decoder 层数不够、query 未分化增加 Decoder 层数加辅助损失适当加 NMS 兜底大量漏检num_queries 设置过小统计数据集最大目标数调大 num_queries小目标 AP 低特征图分辨率不足缩小下采样率或多尺度融合或换 Deformable DETR推理速度慢序列太长、自注意力计算量大减小输入尺寸降低 Encoder 层数用可变形注意力5. 从 DETR 到 Deformable DETR一个绕不开的进化方向5.1 原版 DETR 的瓶颈到底在哪原版 DETR 虽然在结构上具有开创性但工程落地有两个硬伤训练收敛慢和小目标检测弱。这两个问题归根结底都指向同一个根源全局自注意力。全局注意力让每个 query 都要和特征图上所有位置做交互信息虽然全但噪声也大而且计算量随序列长度平方增长。另外Transformer 里的交叉注意力需要学习“该看哪里”这个学习过程本身就慢导致收敛周期长。还有一个结构上的隐患小物体在特征图上占据的 token 数量少全局注意力分配到每个小物体上的权重容易被稀释模型很难聚焦到局部细节。5.2 Deformable DETR 的核心改动Deformable DETR论文标题就是《Deformable Transformers for End-to-End Object Detection》做了一个非常优雅的改进借鉴可变形卷积的思想把交叉注意力和自注意力中的密集全局计算替换成基于稀疏采样点的注意力。具体来说每个 query 不再对特征图全图做注意力而是通过一个可学习的偏移网络预测出 K 个参考点论文里默认是 4 个采样点只在这 K 个点附近采样特征再计算加权聚合。这样一来计算量从 O(N^2) 降到了 O(NK)K 通常远小于序列长度。同时因为是稀疏采样特征分辨率可以大幅提升不需要像原版那样为了控制计算量而用低分辨率特征图。Deformable DETR 的 Encoder 采用的是多尺度特征融合直接在 4 个不同分辨率的特征图上采样小目标检测能力明显提升。从工程角度讲Deformable DETR 的收敛速度比原版快 10 倍左右在 COCO 上只需要 50 epoch 左右就能达到原版 500 epoch 的效果。这也是为什么现在真正在工业界落地的项目绝大多数用的是 Deformable DETR 及其后续衍生版本而不是原版。5.3 DETR 家族的其他重要成员DETR 的思想后来衍生了大量改进工作各有侧重这里简单梳理几个代表性的Conditional DETR研究发现 Decoder 交叉注意力的收敛问题主要出在 query 需要同时学习“在哪看”和“看什么”于是把目标中心坐标作为条件信息显式注入交叉注意力收敛速度进一步提升。DAB-DETR把 object queries 从纯向量改进成 4D 锚框形式中心坐标加宽高让 query 的物理意义更明确同时动态更新效果和收敛都有提升。DN-DETR通过加入去噪训练任务把 GT 加噪后作为额外 query 输入稳定匹配过程显著加速收敛也提高了最终精度。DINO结合了 Contrastive Denoising、混合 query 选择等多种技术是当前 DETR 家族里性能和收敛都比较顶的一支常用作强 baseline。RT-DETR百度的实时检测模型在 DETR 结构基础上做了重设计支持无 NMS 的端到端实时推理吞吐很高适合工程部署。如果要从头选型我的建议是快速验证思路用原版 DETR结构干净容易改追求工程落地的精度和速度直接上 Deformable DETR 或 DINO如果是实时视频流场景RT-DETR 值得关注。5.4 关于“模型结构”的迁移思考DETR 的结构设计思路不只适用于目标检测。把图像编码成序列、用 Transformer 做全局建模、用一组可学习 query 去“查询”目标信息这套范式已经被复制到了很多领域。比如分割任务里的 Mask2Former就是借用了 DETR 的 query 思路用一组掩码 query 直接输出分割掩码SAMSegment Anything Model在 prompt 编码器里也用了类似的可学习位置编码和 Transformer 结构CLIP 的 text encoder 用的就是标准 Transformer Encoder 结构更不用说多模态检测、VQA、指代分割这些任务很多都能看到 DETR 的架构影子。DETR 最大的贡献不是某几个模块有多新颖而是证明了“集合预测 二分匹配 Transformer”这套组合拳能统一到非常多视觉任务的框架里把以前需要大量领域知识手工设计的流程标准化、简洁化。这里也顺带提一句很多朋友会把 DETR 和 TCN、TabNet 这类网络结构混淆。TCN 是时序卷积网络处理的是时间序列TabNet 面向表格数据而 DETR 是纯视觉目标检测模型定位完全不同不要被名字里的 Transformer 绕晕。6. 个人的一点实验体会踩过这么多坑之后我对 DETR 结构最大的感受是它把目标检测的问题定义变得更干净了但也正因为干净它对训练的要求其实更高了。以前用锚框和 NMS相当于给了模型一堆“拐杖”很多错误被后处理兜住了DETR 把这些拐杖全撤了模型必须自己学会“一个萝卜一个坑”这对数据质量、训练配置、超参数设计都提出了更高要求。所以如果你正准备上手 DETR我的建议是不要一上来就追求魔改先严格按照官方配置把 baseline 复现出来观察 Encoder 的注意力可视化、Decoder 里每个 query 的聚焦区域理解模型到底在学什么。等你亲手看到了 query 分化、匹配收敛的过程再去调整结构这时候你会对每个模块的作用有真正的体感而不是只会照着论文抄代码。最后分享一个小技巧调试 DETR 时把每个 Decoder 层输出的预测结果都做一次可视化按层排列会非常直观地看到模型从“乱猜”到“聚焦”的整个过程。这个信息比任何指标都更能帮助你判断模型卡在哪一层、哪个环节出了问题。希望这篇文章能帮你把 DETR 的结构吃透少走一些我当初走过的弯路。
返回列表