1. 项目概述
第一次接触DETR(Detection Transformer)时,我被这个将Transformer引入目标检测领域的创新思路深深吸引。作为一个长期使用传统CNN架构(如Faster R-CNN、YOLO系列)的计算机视觉工程师,我决定从零开始完整跑通DETR的训练流程。这个过程中遇到了数据格式转换、显存爆炸、收敛困难等一系列典型问题,最终通过系统性的调试和优化成功训练出可用的模型。
DETR的核心价值在于它用Transformer的注意力机制完全替代了传统目标检测中的anchor生成和NMS(非极大值抑制)后处理步骤,实现了端到端的目标检测。这种架构特别适合需要处理大量重叠目标的场景(如密集人群检测),因为传统NMS在这种场景下容易误删真实目标。
2. 环境准备与数据转换
2.1 基础环境配置
我选择PyTorch 1.10 + CUDA 11.3的组合,这是经过验证的稳定搭配。安装核心依赖时特别注意版本匹配:
pip install torch==1.10.0+cu113 torchvision==0.11.1+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html pip install pycocotools matplotlib scipy注意:DETR对PyTorch版本敏感,最新版可能出现API不兼容。建议严格按上述版本安装。
2.2 数据格式转换实战
大多数目标检测项目使用的是YOLO格式的txt标注文件,而DETR需要COCO格式的json文件。我开发了一个通用转换脚本,关键步骤如下:
- 解析YOLO标注文件时要注意坐标转换:
def yolo_to_coco(x_center, y_center, width, height, img_width, img_height): x_min = (x_center - width/2) * img_width y_min = (y_center - height/2) * img_height w = width * img_width h = height * img_height return [x_min, y_min, w, h]- 构建COCO JSON的结构体时需要完整包含以下字段:
coco_format = { "info": {...}, "licenses": [...], "categories": [{"id": 1, "name": "person"}, ...], "images": [{"id": 1, "file_name": "001.jpg", "width": 640, "height": 480}, ...], "annotations": [{ "id": 1, "image_id": 1, "category_id": 1, "bbox": [x,y,w,h], "area": w*h, "iscrowd": 0 }, ...] }- 文件目录组织必须严格符合:
dataset/ ├── annotations/ │ ├── instances_train2017.json │ └── instances_val2017.json ├── train2017/ │ ├── 000001.jpg │ └── ... └── val2017/ ├── 000002.jpg └── ...踩坑实录:曾经因为漏写"iscrowd"字段导致验证时AP计算异常。COCO评估工具会默认iscrowd=1的标注不参与计算。
3. 模型训练关键技巧
3.1 预训练模型选择
DETR官方提供ResNet50和ResNet101两种backbone的预训练模型。经过对比测试:
- ResNet50:训练速度更快(约快40%),显存占用少(约12GB),适合快速验证
- ResNet101:mAP提升约2-3个百分点,但需要16GB以上显存
对于自定义数据集,我推荐以下加载方式:
model = torch.hub.load('facebookresearch/detr', 'detr_resnet50', pretrained=True, num_classes=你的类别数) model.class_embed.weight.data = pretrained_class_embed # 迁移分类头参数 model.class_embed.bias.data = pretrained_class_bias3.2 训练参数调优
经过多次实验,得出以下黄金参数组合:
lr = 1e-4 # 初始学习率 weight_decay = 1e-4 # 权重衰减 batch_size = 4 # 11GB显存可承受的最大batch epochs = 300 # DETR需要长时间训练 lr_drop = 200 # 第200epoch时学习率下降10倍 optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=weight_decay) lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=lr_drop, gamma=0.1)关键发现:
- AdamW比Adam更稳定,特别是配合weight_decay使用
- 学习率过高(>5e-4)会导致loss震荡不收敛
- batch_size小于4时,BN层统计量估计不准影响性能
3.3 损失函数配置
DETR使用匈牙利算法进行预测框和真实框的匹配,其损失函数包含三部分:
loss = 0.1 * loss_ce + loss_bbox + loss_giou其中:
- loss_ce:分类交叉熵损失(系数0.1避免主导优化)
- loss_bbox:L1框坐标损失
- loss_giou:广义IoU损失,对框形状更敏感
调试技巧:初期可以单独监控这三项损失的下降曲线。正常情况应该是giou loss最先快速下降,接着是bbox loss,最后是ce loss。
4. 典型问题与解决方案
4.1 显存爆炸问题
现象:训练开始时显存突然占满然后崩溃
解决方法:
- 减小输入图像尺寸(默认800x1333可降至600x1000)
- 使用梯度累积:
for i, (images, targets) in enumerate(dataloader): outputs = model(images) loss = criterion(outputs, targets) loss.backward() if (i+1) % 4 == 0: # 每4个batch更新一次 optimizer.step() optimizer.zero_grad()4.2 模型不收敛问题
可能原因及对策:
- 学习率过高 → 尝试1e-5到1e-4范围
- 数据标注错误 → 用可视化工具检查:
from detr.datasets.coco import make_coco_transforms transform = make_coco_transforms('train') dataset = CocoDetection(img_folder, ann_file, transforms=transform) visualize_dataset(dataset, n_samples=10)- 类别不平衡 → 在Criterion中设置类别权重:
weight_dict = {'loss_ce': 1, 'loss_bbox': 1, 'loss_giou': 1} losses = ['labels', 'boxes', 'cardinality'] criterion = SetCriterion(num_classes, weight_dict=weight_dict, ...)4.3 验证指标波动大
解决方案:
- 增加验证频率(默认每epoch一次可改为每1000iter一次)
- 使用EMA(指数移动平均)平滑模型参数:
from torch_utils import ModelEMA ema = ModelEMA(model, decay=0.9999) # 训练循环中 outputs = model(images) loss.backward() optimizer.step() ema.update(model)5. 模型部署优化
5.1 导出ONNX格式
DETR的动态attention机制导致直接导出失败,需要修改forward逻辑:
class ExportableDETR(nn.Module): def __init__(self, model): super().__init__() self.model = model def forward(self, x): # 将动态decoder改为固定100个query outputs = self.model(x) return outputs['pred_logits'], outputs['pred_boxes'] torch.onnx.export(ExportableDETR(model), dummy_input, "detr.onnx", opset_version=12)5.2 TensorRT加速
使用TensorRT部署时需要特殊处理:
- 处理自定义的MultiHeadAttention层
- 设置优化profile:
profile = builder.create_optimization_profile() profile.set_shape("input", min=(1,3,600,600), opt=(1,3,800,800), max=(1,3,1333,1333)) config.add_optimization_profile(profile)实测加速效果(Tesla T4):
- FP32: 45ms → FP16: 28ms → INT8: 19ms
6. 进阶改进方向
6.1 使用Deformable DETR
原始DETR收敛慢的问题可以通过Deformable Attention改进:
from models.deformable_detr import DeformableDETR model = DeformableDETR( num_classes=num_classes, num_feature_levels=4, with_box_refine=True, two_stage=True )优势:
- 训练epoch减少到50即可收敛
- mAP提升2-3个点
- 对小目标检测效果更好
6.2 自定义Query设计
默认的100个learned query可以替换为:
# 基于图像内容生成query content_queries = backbone_feature.mean(dim=[2,3]).unsqueeze(1) model.transformer.decoder.query_embed.weight.data = content_queries这种改进在特定场景(如文字检测)中可提升5%以上的召回率。
在实际部署中发现,DETR的推理速度虽然不及YOLO系列,但在处理密集目标场景时,其避免NMS的特性带来了更稳定的检测效果。特别是在交通监控场景中,对重叠车辆的检测准确率比YOLOv5提高了15%。训练过程中最大的教训是一定要耐心等待——DETR需要300epoch才能充分收敛,但在后期会出现明显的性能跃升。建议至少准备3天以上的连续训练时间,并配合自动恢复机制(如使用checkpoint)。