如果你在2020年之前接触过目标检测,那么你一定对“两阶段”和“一阶段”这两个词印象深刻。从R-CNN系列到YOLO系列,整个领域似乎都在这两条技术路径上做“选择题”:是先找候选框再分类,还是直接回归出框和类别?无论怎么选,都绕不开一个核心组件——非极大值抑制(NMS)。这个后处理步骤,像一道无法绕开的“人工关卡”,负责剔除冗余的检测框,但它带来的计算开销、调参敏感性和端到端训练的障碍,一直是工程师们心中的一根刺。
直到2020年,Facebook AI Research(FAIR)扔出了一颗“炸弹”:DETR(Detection Transformer)。它用一句话宣告了自己的不同:“我们不需要NMS,也不需要手工设计的锚框(Anchor),我们直接用Transformer做端到端的目标检测。”这个想法在当时听起来有些“离经叛道”,因为Transformer那时还是NLP领域的霸主,在视觉任务中大规模应用才刚刚开始。
如今,DETR及其衍生模型(如Deformable DETR)已经成为目标检测领域不可忽视的一股力量。但很多开发者初次接触时,往往会被其“端到端”的宣传所吸引,却在实际部署和调优时感到困惑:为什么训练这么慢?为什么小目标检测效果不好?Transformer的注意力机制到底是怎么用在目标检测上的?
本文将从实战和源码的角度,彻底拆解DETR。我们不只讲“是什么”,更要讲清楚“为什么”要这样设计,以及在实际项目中“怎么用”和“怎么调”。你将看到DETR如何用一套简洁的编码器-解码器架构,将目标检测任务转化为一个集合预测问题,并理解其背后的设计哲学与工程权衡。
1. DETR解决了什么根本问题?—— 告别手工设计组件
在深入代码之前,我们必须先理解DETR要革谁的命。传统目标检测模型,无论是Faster R-CNN还是YOLO,其 pipeline 中都包含大量手工设计(hand-crafted)的归纳偏置(inductive bias):
- 锚框(Anchor Boxes):需要预先定义一系列不同尺度、长宽比的候选框。这些超参数对数据集非常敏感,换一个场景可能就需要重新调整。
- 非极大值抑制(NMS):后处理必备,用于去除高度重叠的冗余框。NMS本身有一个阈值需要调节,这个阈值会影响模型的召回率和精度,且其不可微的性质阻碍了真正的端到端训练。
- 区域提议网络(RPN)或特征金字塔网络(FPN):这些结构的设计初衷是为了处理多尺度目标,但其本身也是复杂的模块。
DETR的核心主张是:将这些手工设计的组件全部拿掉,用一套统一、可学习、端到端的架构来替代。它把目标检测视为一个**集合预测(Set Prediction)**问题。给定一张图片,模型直接输出一个固定长度的无序集合,集合中的每个元素就是一个预测目标(包含类别和边界框)。
这种设计带来了几个直观的好处:
- 架构极度简洁:没有NMS,没有Anchor, pipeline 清晰。
- 真正的端到端:从图像输入到预测结果,所有部分都可微,可以联合优化。
- 全局推理能力:得益于Transformer的自注意力机制,模型在做预测时能够“看到”图像的所有位置,理论上能更好地处理被遮挡物体或需要上下文理解的目标。
然而,这种“简洁”并非没有代价,最突出的就是训练收敛慢和小目标检测性能相对较弱。这也是后续Deformable DETR等改进模型重点优化的方向。理解原始DETR,是理解这一切改进的基石。
2. 核心思想:如何用Transformer做目标检测?
DETR的 pipeline 可以概括为四个步骤,下图清晰地展示了其数据流向:
flowchart TD A[输入图像] --> B[CNN Backbone<br>(如ResNet-50)] B --> C[特征图<br>+ 位置编码] C --> D[Transformer 编码器<br>(自注意力)] subgraph E [Transformer 解码器] direction LR F[对象查询<br>(可学习参数)] --> G[交叉注意力<br>(查询与特征交互)] end D --> G G --> H[前馈网络 FFN] H --> I[预测头<br>(类别 + 框坐标)] I --> J[输出: 无序预测集合<br>(Set of Predictions)] J --> K[二分图匹配<br>(匈牙利算法)] K --> L[计算损失<br>(监督训练)]下面,我们来逐一拆解图中的每个关键模块。
2.1 Backbone:从图像到特征序列
和大多数视觉模型一样,DETR需要一个CNN主干网络(如ResNet-50)来提取图像的视觉特征。输入一张3xH0xW0的图像,Backbone会输出一个更低分辨率的特征图,例如2048xHxW(其中H=H0/32,W=W0/32)。
关键的一步是“展平”:DETR将这张2D特征图在空间维度上展平,变成一个1D的序列。特征图的每个“像素点”(实际上是特征图上的一个位置)对应序列中的一个元素。因此,我们得到一个长度为H*W的序列,每个元素是一个2048维的特征向量。
为了保留位置信息,DETR为这个序列的每一个位置(即原特征图上的每个坐标(x, y))计算了一个位置编码(Positional Encoding),并将其加到对应的特征向量上。这样,Transformer才能知道特征来自图像中的哪个位置。
# 伪代码示意:特征提取与位置编码 import torch import torch.nn as nn # 假设 backbone 输出特征图 feat_map: [batch_size, d_model=2048, H, W] batch_size, d_model, H, W = feat_map.shape # 1. 将特征图展平为序列 feat_sequence = feat_map.flatten(2).permute(0, 2, 1) # 形状: [batch_size, H*W, d_model] # 2. 生成对应的2D位置编码 (sin/cos形式) pos_encoding = generate_2d_sincos_pos_encoding(H, W, d_model) # 形状: [H*W, d_model] pos_encoding = pos_encoding.unsqueeze(0).expand(batch_size, -1, -1) # 扩展到batch维度 # 3. 将位置编码加到特征序列上 feat_sequence_with_pos = feat_sequence + pos_encoding2.2 Transformer 编码器-解码器
加上位置编码的特征序列,被送入一个标准的Transformer编码器。编码器由多层自注意力(Self-Attention)和前馈网络(FFN)组成。自注意力机制让序列中的每个特征都能与所有其他特征进行交互,从而捕获图像的全局上下文信息。这对于理解物体之间的关系、解决遮挡问题至关重要。
解码器的输入有两部分:
- 来自编码器的内存(Memory):即编码器输出的特征序列。
- 对象查询(Object Queries):这是一组可学习的参数,可以理解为模型预先定义好的“提问”。DETR默认有100个对象查询(
num_queries=100),这意味着模型最多预测100个物体。每个查询都是一个d_model维的向量。
在解码器的每一层,对象查询会通过交叉注意力(Cross-Attention)机制与编码器输出的内存进行交互。这个过程可以理解为:每个对象查询(“提问”)在整个编码后的特征图(“知识库”)中寻找与自己最相关的信息,从而“解码”出一个具体的物体。
2.3 预测头与集合预测
解码器输出的每个对象查询(现在已经是富含信息的向量),会分别通过两个前馈网络(FFN):
- 一个FFN用于分类:输出
(num_classes + 1)个分数,其中+1代表“无物体”(no object)类。 - 一个FFN用于边界框回归:输出一个4维向量
(cx, cy, w, h),表示边界框的中心坐标和宽高(通常归一化到[0, 1]区间)。
因此,DETR的最终输出是一个固定大小的集合:100个预测结果,每个结果包含一个类别概率分布和一个边界框。
2.4 二分图匹配与损失函数
这是DETR训练中最精妙也最关键的一环。如何为这100个无序的预测分配监督信号(即图像中真实存在的物体标签)?
DETR使用了匈牙利算法(Hungarian Algorithm)来解决这个二分图匹配问题。它寻找一个最优的“配对”方式,将预测集合与真实物体集合进行一一对应,使得总体的配对代价最小。这个代价由两部分组成:
- 分类代价:配对的预测类别与真实类别之间的负对数概率。
- 边界框代价:配对的预测框与真实框之间的L1损失和广义IoU(GIoU)损失。
匹配完成后,只有成功配对的预测才计算分类和框回归损失,未配对的预测则被鼓励预测为“无物体”类。
# 伪代码示意:匈牙利匹配的核心思想 from scipy.optimize import linear_sum_assignment import torch def hungarian_matching(pred_logits, pred_boxes, gt_labels, gt_boxes): """ pred_logits: [batch_size, num_queries, num_classes+1] pred_boxes: [batch_size, num_queries, 4] gt_labels: list of tensor (每个图像的真实标签) gt_boxes: list of tensor (每个图像的真实框) """ batch_size = pred_logits.shape[0] indices = [] # 存储每个样本的匹配结果 for i in range(batch_size): num_gt = gt_boxes[i].shape[0] # 1. 计算代价矩阵 cost_matrix: [num_queries, num_gt] cost_class = -pred_logits[i, :, gt_labels[i]] # 分类代价 cost_bbox = torch.cdist(pred_boxes[i], gt_boxes[i], p=1) # L1距离 cost_giou = 1 - generalized_box_iou(pred_boxes[i], gt_boxes[i]) # GIoU代价 cost_matrix = cost_class + cost_bbox + cost_giou # 加权和 # 2. 填充:如果num_queries > num_gt,用零填充代价矩阵 if num_gt < pred_logits.shape[1]: padding = torch.zeros(pred_logits.shape[1], pred_logits.shape[1] - num_gt) cost_matrix = torch.cat([cost_matrix, padding], dim=1) # 3. 执行匈牙利算法,找到最优匹配 row_ind, col_ind = linear_sum_assignment(cost_matrix.cpu().numpy()) indices.append((row_ind, col_ind)) return indices通过这种匹配方式,DETR在训练中动态地学习如何将对象查询“分配”给不同的物体,甚至学习到一些空间先验(例如,某个查询倾向于检测图像左下角的物体)。
3. 环境搭建与源码获取
理解了原理,我们开始动手实践。DETR的官方实现基于PyTorch,代码清晰,是学习的绝佳材料。
3.1 环境准备
建议使用Python 3.8+和PyTorch 1.9+。以下命令创建了一个基础的conda环境:
# 创建并激活环境 conda create -n detr python=3.8 -y conda activate detr # 安装PyTorch (请根据你的CUDA版本到PyTorch官网选择对应命令) # 例如,对于CUDA 11.3: conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch # 安装DETR所需的其他依赖 pip install cython scipy pip install -U 'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI' pip install opencv-python3.2 获取官方源码
直接从Facebook Research的GitHub仓库克隆:
git clone https://github.com/facebookresearch/detr.git cd detr项目结构非常清晰:
models/: 包含DETR模型的核心定义(transformer.py,detr.py)。hubconf.py: 用于TorchHub的模型加载配置。main.py: 训练脚本。datasets/: 数据准备相关代码(COCO和Panoptic)。util/: 工具函数,包括框操作、分布式训练等。
4. 模型构建核心代码拆解
我们深入到models/detr.py和models/transformer.py,看看关键模块是如何实现的。
4.1 DETR主类 (Detr)
Detr类集成了所有组件。其forward函数清晰地展示了数据流:
# 文件:models/detr.py (简化版) class Detr(nn.Module): def __init__(self, backbone, transformer, num_classes, num_queries, aux_loss=False): super().__init__() self.num_queries = num_queries self.transformer = transformer hidden_dim = transformer.d_model # 分类头:输出 num_classes + 1 (背景类) self.class_embed = nn.Linear(hidden_dim, num_classes + 1) # 边界框回归头:输出4个坐标 (cx, cy, w, h) self.bbox_embed = MLP(hidden_dim, hidden_dim, 4, 3) # 对象查询:可学习的参数 self.query_embed = nn.Embedding(num_queries, hidden_dim) # 将backbone输出的通道数映射到transformer的隐藏维度 self.input_proj = nn.Conv2d(backbone.num_channels, hidden_dim, kernel_size=1) self.backbone = backbone self.aux_loss = aux_loss def forward(self, samples: NestedTensor): # 1. 通过Backbone提取特征 features, pos = self.backbone(samples) # 2. 将特征投影到隐藏维度,并展平 src, mask = features[-1].decompose() assert mask is not None hs = self.transformer(self.input_proj(src), mask, self.query_embed.weight, pos[-1])[0] # 3. 通过预测头得到输出 outputs_class = self.class_embed(hs) outputs_coord = self.bbox_embed(hs).sigmoid() # 使用sigmoid将坐标限制在[0,1] out = {'pred_logits': outputs_class[-1], 'pred_boxes': outputs_coord[-1]} if self.aux_loss: out['aux_outputs'] = self._set_aux_loss(outputs_class, outputs_coord) return out4.2 Transformer编码器 (TransformerEncoder)
编码器层就是标准的Transformer编码器层,包含多头自注意力和前馈网络。
# 文件:models/transformer.py (编码器层) class TransformerEncoderLayer(nn.Module): def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1, activation="relu", normalize_before=False): super().__init__() self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout) # 前馈网络 self.linear1 = nn.Linear(d_model, dim_feedforward) self.dropout = nn.Dropout(dropout) self.linear2 = nn.Linear(dim_feedforward, d_model) self.norm1 = nn.LayerNorm(d_model) self.norm2 = nn.LayerNorm(d_model) self.dropout1 = nn.Dropout(dropout) self.dropout2 = nn.Dropout(dropout) self.activation = _get_activation_fn(activation) self.normalize_before = normalize_before def forward(self, src, src_mask=None, src_key_padding_mask=None, pos=None): # 添加位置编码 if pos is not None: src2 = src + pos q = k = src2 # 自注意力 src2 = self.self_attn(q, k, value=src2, attn_mask=src_mask, key_padding_mask=src_key_padding_mask)[0] src = src + self.dropout1(src2) src = self.norm1(src) # 前馈网络 src2 = self.linear2(self.dropout(self.activation(self.linear1(src)))) src = src + self.dropout2(src2) src = self.norm2(src) return src4.3 Transformer解码器与交叉注意力 (TransformerDecoder)
解码器层是DETR的灵魂,其核心是交叉注意力机制。
# 文件:models/transformer.py (解码器层,关键部分) class TransformerDecoderLayer(nn.Module): def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1, activation="relu", normalize_before=False): super().__init__() # 自注意力:对象查询之间的交互 self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout) # 交叉注意力:对象查询与编码器输出的交互 self.multihead_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout) # 前馈网络 self.linear1 = nn.Linear(d_model, dim_feedforward) self.linear2 = nn.Linear(dim_feedforward, d_model) self.norm1 = nn.LayerNorm(d_model) self.norm2 = nn.LayerNorm(d_model) self.norm3 = nn.LayerNorm(d_model) self.dropout1 = nn.Dropout(dropout) self.dropout2 = nn.Dropout(dropout) self.dropout3 = nn.Dropout(dropout) self.activation = _get_activation_fn(activation) def forward(self, tgt, memory, tgt_mask=None, memory_mask=None, tgt_key_padding_mask=None, memory_key_padding_mask=None, pos=None, query_pos=None): # tgt: 对象查询 [num_queries, batch_size, d_model] # memory: 编码器输出 [H*W, batch_size, d_model] # query_pos: 对象查询的位置编码(就是可学习的query_embed) # pos: 编码器特征的位置编码 # 第一步:对象查询的自注意力 (带查询位置编码) q = k = tgt + query_pos tgt2 = self.self_attn(q, k, value=tgt, attn_mask=tgt_mask, key_padding_mask=tgt_key_padding_mask)[0] tgt = tgt + self.dropout1(tgt2) tgt = self.norm1(tgt) # 第二步:交叉注意力 (查询与内存交互) # 查询侧加入query_pos,键侧加入pos tgt2 = self.multihead_attn(query=tgt + query_pos, key=memory + pos, value=memory, attn_mask=memory_mask, key_padding_mask=memory_key_padding_mask)[0] tgt = tgt + self.dropout2(tgt2) tgt = self.norm2(tgt) # 第三步:前馈网络 tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt)))) tgt = tgt + self.dropout3(tgt2) tgt = self.norm3(tgt) return tgt关键理解:在交叉注意力中,query是来自解码器的对象查询(加上其位置编码query_pos),而key和value都来自编码器的输出memory(加上其空间位置编码pos)。这使得每个对象查询能够从全局特征中聚合与自身最相关的信息。
5. 训练与推理实战
5.1 使用官方脚本训练
DETR官方提供了在COCO数据集上训练的脚本。这是一个多GPU训练示例:
# 在detr目录下 python -m torch.distributed.launch --nproc_per_node=8 --use_env main.py \ --coco_path /path/to/coco \ --output_dir /path/to/save/checkpoints \ --batch_size 2 \ --epochs 300 \ --lr_drop 200 \ --num_queries 100重要参数解析:
--coco_path: COCO数据集的根目录。--batch_size: 这是每个GPU的batch size。如果你有8张GPU,batch_size=2,则总batch size为16。--epochs: DETR需要长时间训练,论文中在COCO上训练了300个epoch。--lr_drop: 学习率在第200个epoch时下降10倍。--num_queries: 默认100,决定了模型最多预测的物体数量。对于一般场景足够,如果图像中物体非常密集,可以考虑增加。
5.2 加载预训练模型进行推理
DETR提供了在COCO上预训练的模型,可以通过TorchHub轻松加载:
import torch import matplotlib.pyplot as plt import torchvision.transforms as T from PIL import Image import requests from io import BytesIO # 加载预训练模型 model = torch.hub.load('facebookresearch/detr', 'detr_resnet50', pretrained=True) model.eval() # 定义图像预处理变换 transform = T.Compose([ T.Resize(800), # 将短边缩放到800像素 T.ToTensor(), T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) # 加载并预处理图像 url = 'http://images.cocodataset.org/val2017/000000039769.jpg' im = Image.open(requests.get(url, stream=True).raw) img_tensor = transform(im).unsqueeze(0) # 增加batch维度 # 模型推理 with torch.no_grad(): outputs = model(img_tensor) # 后处理:过滤低置信度预测 probas = outputs['pred_logits'].softmax(-1)[0, :, :-1] # 去掉背景类 keep = probas.max(-1).values > 0.7 # 置信度阈值 # 绘制结果 def plot_results(pil_img, prob, boxes): plt.figure(figsize=(16,10)) plt.imshow(pil_img) ax = plt.gca() for p, (xmin, ymin, xmax, ymax) in zip(prob, boxes.tolist()): ax.add_patch(plt.Rectangle((xmin, ymin), xmax-xmin, ymax-ymin, fill=False, color='red', linewidth=3)) cl = p.argmax() text = f'{model.config.id2label[cl.item()]}: {p[cl]:0.2f}' ax.text(xmin, ymin, text, fontsize=15, bbox=dict(facecolor='yellow', alpha=0.5)) plt.axis('off') plt.show() # 转换框坐标 (cx, cy, w, h) -> (xmin, ymin, xmax, ymax) bboxes_scaled = outputs['pred_boxes'][0, keep] bboxes_scaled[:, [0, 2]] = bboxes_scaled[:, [0, 2]] * im.size[0] bboxes_scaled[:, [1, 3]] = bboxes_scaled[:, [1, 3]] * im.size[1] plot_results(im, probas[keep], bboxes_scaled)运行这段代码,你将看到模型对图像中物体的检测结果,完全不需要NMS后处理,输出已经是过滤后的最终结果。
6. 深入分析:DETR的优势与挑战
6.1 优势总结
- 设计简洁优雅:移除了Anchor和NMS, pipeline 清晰,减少了大量超参数调优工作。
- 全局推理能力:Transformer的自注意力机制使模型能够利用图像中所有位置的上下文信息,对于处理遮挡、理解物体关系有潜在优势。
- 易于扩展:DETR的框架很容易扩展到其他视觉任务,如全景分割(Panoptic Segmentation),只需在解码器后添加一个掩码头即可。
6.2 面临的挑战与改进方向
DETR并非完美,其原始版本存在明显短板,这也催生了一系列改进工作:
- 训练收敛慢:这是最被诟病的一点。DETR在COCO上需要训练500个epoch才能达到最佳效果(后续工作优化到108个epoch)。原因在于:Transformer解码器中的对象查询是随机初始化的,它们需要很长时间才能学会关注图像中不同的、有意义的区域。
- 小目标检测性能弱:DETR使用CNN backbone下采样32倍后的特征图,对于小目标来说,其特征信息已经非常微弱。同时,Transformer在处理高分辨率特征图时,自注意力的计算复杂度是序列长度的平方(O(N²)),直接使用高分辨率特征会导致计算量爆炸。
- 计算资源需求大:Transformer编码器对长序列(H*W)做自注意力,当输入图像较大时,内存和计算消耗很高。
针对这些挑战,社区提出了许多改进方案:
- Deformable DETR:引入了可变形注意力(Deformable Attention),让每个查询只关注特征图上的一小部分关键采样点,而不是全部位置。这大幅降低了计算量,并加速了训练收敛(约10倍),同时通过多尺度特征融合显著提升了对小目标的检测能力。
- Conditional DETR:改进了解码器的交叉注意力机制,让对象查询能够更早、更准确地聚焦到目标区域,加速训练。
- DAB-DETR:将对象查询显式地解码为动态锚框(Dynamic Anchor Boxes),提供了更好的空间先验。
7. 常见问题与排查指南
在实际使用DETR时,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 训练Loss不下降或NaN | 学习率设置过高;梯度爆炸;数据中存在异常标注(如框坐标超出图像)。 | 1. 监控训练初期的几个batch的loss值。 2. 使用 torch.nn.utils.clip_grad_norm_裁剪梯度。3. 检查数据预处理,确保框坐标被正确归一化到[0,1]。 | 1. 降低初始学习率(如从1e-4开始)。 2. 添加梯度裁剪( max_norm=0.1)。3. 在数据加载器中添加对非法标注的过滤。 |
| 显存不足(OOM) | 输入图像尺寸过大;Batch size过大;Transformer层数或隐藏维度太大。 | 1. 使用torch.cuda.empty_cache()清理缓存。2. 使用 nvidia-smi监控显存使用。3. 尝试减小 --resize参数(如从800调到600)。 | 1. 减小输入图像尺寸。 2. 使用梯度累积( --accumulate_grad_batches)来模拟大batch。3. 考虑使用混合精度训练(AMP)。 |
| 推理速度慢 | 未使用model.eval()和torch.no_grad();图像预处理和后处理耗时。 | 1. 使用Python的cProfile或PyTorch的torch.utils.bottleneck分析代码瓶颈。2. 对比开启/关闭eval模式的速度。 | 1. 确保推理时调用model.eval()并置于torch.no_grad()上下文。2. 对预处理和后处理进行优化或向量化。 3. 考虑使用TensorRT或ONNX进行模型加速。 |
| 小目标漏检严重 | Backbone下采样率过高,小目标特征丢失;原始DETR架构缺陷。 | 1. 可视化特征图,看小目标区域是否有响应。 2. 在验证集上统计不同尺度目标的AP。 | 1. 使用特征金字塔网络(FPN)或类似结构的多尺度特征。 2.强烈建议使用改进模型,如Deformable DETR,它原生支持多尺度特征。 |
| 预测框数量不足100个 | 这是正常现象。模型输出的100个查询中,大部分会预测为“背景”。只有置信度高于阈值的才会被保留。 | 检查outputs['pred_logits'].softmax(-1)[:, :, :-1].max(-1).values,看有多少查询的置信度高于你的阈值(如0.7)。 | 调整置信度阈值。如果希望召回更多目标,可以降低阈值(如0.5),但可能会引入更多误检。 |
8. 最佳实践与工程建议
- 从预训练模型开始:除非有海量数据和计算资源,否则不要从头开始训练DETR。优先使用在COCO上预训练的模型进行微调(Fine-tuning)。
- 针对自己的数据集微调:
将你的数据集转换为COCO格式,并修改python main.py \ --coco_path /path/to/your/coco_format_dataset \ --output_dir /path/to/output \ --resume /path/to/pretrained/detr-r50.pth \ --epochs 50 \ --lr 1e-5 \ --batch_size 4 \ --num_queries 100datasets/coco.py中的类别映射。 - 使用改进版本:对于新项目,优先考虑 Deformable DETR。它在收敛速度和小目标检测上都有巨大提升,是更实用的选择。其GitHub仓库同样来自FAIR。
- 注意对象查询数量:
num_queries决定了模型最大检测数量。对于交通监控等密集场景,可以适当增加(如150或200)。但增加查询数会线性增加解码器的计算量。 - 理解训练时间:即使使用Deformable DETR,在中等规模数据集上微调也需要数十个epoch。请预留足够的训练时间和GPU资源。
- 部署优化:Transformer的解码器在推理时是串行执行的,不利于并行化。可以考虑将解码器层进行融合或使用更高效的注意力实现来提升推理速度。
DETR的出现,标志着目标检测领域从大量手工设计时代向更简洁、更统一的端到端学习时代迈出了坚实的一步。它可能不是所有场景下的最优解,但其思想深刻影响了后续的许多工作。通过本文对原理和源码的拆解,希望你能不仅学会如何使用DETR,更能理解其设计精髓,从而在面对新的视觉任务时,能够思考是否可以用“集合预测”和“Transformer”的视角来优雅地解决问题。真正的创新,往往始于对固有范式的重新审视。