DETR实战:从YOLO到端到端目标检测的迁移指南

发布时间:2026/10/21 4:37:28
DETR实战:从YOLO到端到端目标检测的迁移指南 几年前我拿到一个安全帽小样本检测项目最初照着惯性用了YOLO结果在密集遮挡和远处小目标上反复调锚框、调NMS阈值调试成本高得离谱。后来把一批业务数据切换到基于Transformer的DETR上才第一次体会到“端到端”三个字的分量。这篇内容就是那段时间的实战记录包括环境搭建、训练、预测全流程以及我踩过的几个印象深刻的坑。如果你正准备从YOLO切到DETR或想弄明白Transformer怎么做目标检测这篇文章应该能帮你省下不少时间。1. 告别YOLO之前DETR把检测问题简化成了什么1.1 从锚框到集合预测DETR的设计思路YOLO系列依赖网格和锚框先预置一堆候选框再做分类和回归。这个思路本身没问题但锚框尺寸、正负样本分配策略、NMS后处理这些手工设计都需要针对数据集调整。DETR把目标检测重新定义成了一个集合预测问题模型直接输出一组预测结果类别坐标然后用匈牙利算法把这组预测和真实标注做最优匹配匹配后计算损失。整个过程不需要锚框也不需要NMS。Transformer承担了全局关系建模的工作。图像先经过CNN骨干提取特征展平后加上位置编码送入编码器编码器让每个位置都能看到全图其它位置。解码器侧有一组可学习的object queries它们就像“我要找目标”的查询向量通过交叉注意力从编码器特征里“捞出”每个目标的信息最终输出类别概率和归一化坐标。听起来抽象但你可以把它理解为YOLO是先铺一大堆候选框再筛DETR则是直接问Transformer“图上到底有哪几个目标各自在哪”Transformer回答一次就够了。1.2 DETR与YOLO的对照少了哪些手工设计多了哪些计算量很多初学者以为DETR只是换了个 backbone实际上整个检测流程都变了。下面这张表是我经常拿来给团队新同学讲的对比维度YOLOv5经典DETR锚框需要聚类生成不需要正负样本分配IoU分配/动态分配匈牙利算法一对一匹配后处理NMS不需要上下文建模局部卷积感受野Transformer全局注意力训练收敛速度快慢通常需要数百epoch小目标表现相对稳定默认配置偏弱显存占用较低较高少了NMS和锚框这是DETR最吸引人的地方。很多项目里我调NMS阈值调到怀疑人生换到DETR之后这些参数全消失了代码路径肉眼可见地变短。代价也很真实收敛速度比YOLO慢得多显存占用也更高。经典DETR在小目标上并不占优后来出现的Deformable DETR、DAB-DETR才逐步改进这些短板。但入门理解Transformer检测范式经典DETR依然是最好的教材——结构清晰代码量少改起来方便。2. 环境搭建实操从驱动检查到依赖安装2.1 GPU与CUDA版本检查环境搭建这一步看似简单但版本不对后面全是问题。我的建议是先检查驱动再决定PyTorch版本。终端里依次执行nvidia-smi nvcc --version第一条看的是驱动支持的CUDA版本比如输出里显示“CUDA Version: 12.1”说明驱动最高支持到12.1。第二条看的是当前是否装了CUDA工具包。如果你的机器从来没有单独装过CUDAnvcc可能显示找不到命令这种情况下完全可以用PyTorch自带的CUDA运行时。PyTorch的预编译包会绑定一份CUDA只要GPU驱动版本够新就行。一个容易踩的坑是nvidia-smi显示的CUDA版本高不代表你能直接用还要看显存是否够大。DETR默认backbone是ResNet50输入约800分辨率batch size为2时显存占用大概在8GB左右。我手上有一张显存只有6GB的卡跑默认参数直接OOM。先确认显存容量再决定模型尺寸比纠结驱动版本更优先。2.2 conda环境与PyTorch安装我习惯用conda隔离环境避免依赖互相污染conda create -n detr python3.8 -y conda activate detrPyTorch安装命令根据CUDA版本选择。比如本机驱动支持CUDA 11.8实测用如下命令最稳pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118这里不要照搬官网上最新的版本号。DETR官方仓库当年是在PyTorch较早期版本上验证的但更老的版本缺省参数可能与新版不兼容。我的建议是选择PyTorch 1.8到2.x之间的稳定版本torchvision版本要跟torch对应。装完后一定要验证GPU可用import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果cuda.is_available()是False优先检查驱动版本和PyTorch CUDA版本是否匹配不要先怀疑代码。2.3 源码下载与依赖安装DETR官方开源仓库名为detr结构很清爽直接拉取到本地git clone https://github.com/facebookresearch/detr.git cd detr依赖很少核心是pycocotools、scipy、pandas、tqdm、matplotlib。requirements.txt里都有安装即可pip install -r requirements.txt这里有两个常见问题。第一pycocotools在Windows上需要VC编译环境如果没有直接装pycocotools-windows替代。第二新版scipy要求Python版本不能太老如果你按3.8创建环境建议用scipy 1.10系列。我最初在Python 3.8环境里直接装最新scipy出现编译错误降到指定版本就正常了。还有一个小细节util/misc.py里用到了torchvision.ops.boxes相关API如果你装了很新的torchvision个别函数签名可能有变化。不过经典DETR代码的整体依赖面非常窄大多数情况下clone下来就能直接跑通这也是我推荐用它入门的原因。3. 数据集准备与输入输出范式3.1 自建数据转成COCO格式DETR原生读取COCO格式标注。如果你用的是VOC格式或YOLO格式数据第一步就得转成COCO的JSON结构。我之前处理的模拟项目X有几千张图标注的是安全帽和人员两类目标。YOLO的txt标注转COCO时要注意COCO的bbox格式是[x_min, y_min, width, height]而YOLO的txt是归一化的[x_center, y_center, width, height]转换时需要先将中心点形式换算成左上角坐标再乘以图像宽高。最终数据集目录结构如下data/ coco/ annotations/ instances_train2017.json instances_val2017.json train2017/ xxx.jpg val2017/ xxx.jpg如果只是跑通流程不需要完整下载COCO全量数据。可以只准备几百张图按上述结构放好。DETR的datasets/coco.py会读取annotations/instances_*.json并与对应图片目录关联。JSON里的categories字段需要与你的类别列表一致注意DETR内部会把类别索引直接映射到模型输出通道上类别排序不要随意改动。3.2 DETR的输入管线图像resize、mask与位置编码DETR的输入处理有两个关键点图像归一化和mask生成。数据加载时图像被resize到短边至少800、长边最大1333然后除以255并做均值方差归一化。同时会生成一个padding mask标记哪些区域是填充的无效区域。这个mask在Transformer编码器里用来屏蔽无效位置的注意力计算非常重要——我见过有人图省事直接去掉mask结果模型完全不收敛。位置编码采用的是三角函数固定编码加到每个token上让Transformer知道特征在空间上的位置。这里不用手工调整但你要理解如果图像的原始分辨率变化很大resize之后的实际尺寸会影响编码器的计算量。在我测试时把--resize相关参数保持默认对大多数情况已经够用只有在目标非常小或非常密集时才需要考虑提高输入分辨率。3.3 第一次跑通前务必检查的几件事第一次训练前我建议花五分钟做三个检查能避免后面浪费大量时间检查类别数。DETR模型的num_classes需要设置为“实际类别数1”最后一个通道代表“无目标”背景类。这个细节极容易出错我见过有人直接填2导致模型把背景也当成了一个类别。检查数据加载。可以单独运行一句python -c from datasets import build_dataset; ...或者在main.py里打断点确认每张图的target字段包含boxes和labels且boxes坐标在0-1之间归一化。检查一次前向传播。拿一张图手动调用模型观察输出logits的shape是否为[batch, 100, num_classes]、pred_boxes的shape是否为[batch, 100, 4]。这里的100对应100个object queries也就是模型最多预测100个目标。4. 训练过程配置参数、观察loss曲线与收敛判断4.1 训练命令与关键参数解析先给一条完整训练命令我实测下来比较稳定python main.py --batch_size 2 --epochs 300 --lr 1e-4 --lr_backbone 1e-5 --weight_decay 1e-4 --coco_path data/coco --output_dir logs/detr --num_classes 3关键参数含义如下参数名作用我的建议值batch_size单卡batch大小显存不够时从2降到1epochs训练轮数小数据集300起步lrTransformer部分学习率1e-4lr_backbone骨干网络学习率1e-5避免破坏预训练特征num_classes类别数真实类别1coco_path数据根目录按COCO结构组织官方默认用8卡训练500个epoch单卡跑小数据集时不需要这么悲观但也不要指望几十个epoch就出好效果。我自己的经验是哪怕只有几百张图300个epoch依然有必要因为匈牙利匹配机制下的损失收敛是渐进的。如果你显存紧张还有一个实用技巧先冻结backbone训练几个epoch让Transformer部分先学到基本匹配规律再解冻整个网络训练。具体做法是遍历模型参数将对backbone参数的requires_grad设为False训完一轮后再置为True。4.2 损失函数的三个组成部分怎么看训练日志DETR的总损失由三部分组成分类损失交叉熵衡量预测类别和真实类别是否匹配。L1框回归损失衡量预测框和真实框在归一化坐标上的绝对差异。GIoU损失衡量预测框和真实框的交并比弥补L1对小偏移不敏感的问题。训练日志中会依次输出loss_ce、loss_bbox、loss_giou和加权总和loss。权重系数里loss_ce对应1.0loss_bbox对应5.0loss_giou对应2.0。我每次训练都会重点看两个信息loss_ce是否持续下降、class_error是否接近0。如果分类损失一直抖动不降大概率是匹配逻辑或者类别数设置出了问题而不是单纯的训练轮次不够。4.3 我训练时看到的真实收敛过程我用安全帽数据集训练时前50个epoch的loss_ce在1.8附近缓慢下降直观上看预测框是乱飞的完全没有章法。到了80个epoch左右分类损失降到1.2边界框开始有意识地去贴合目标位置。150个epoch之后验证集上的mAP才开始明显爬升。这个过程和YOLO“30个epoch就有点样子”完全不同初次接触DETR的人很容易在100个epoch内误判模型不行而放弃。观察到什么程度可以提前停我一般参考validation部分输出的loss_ce和bbox AP。如果连续20个epoch验证损失不再下降就早停如果还在降哪怕速度慢也继续训。另外日志里的test部分默认会在最后一个epoch计算COCO指标数据集小的时候这个计算会占用一点时间但建议保留因为它输出的Average Precision是判断模型质量的最直接依据。5. 推理预测与可视化让模型真正跑起来5.1 用postprocessor把输出变成坐标DETR的训练代码里自带后处理工具推理时可以直接调用。核心流程是模型输出原始logits和归一化坐标经过softmax变成概率再从Prediction对象里取出top-k个预测。示例代码大致长这样import torch from torchvision.transforms import Compose, Resize, ToTensor, Normalize from models import build_model from util.misc import nested_tensor_from_tensor_list model, criterion, postprocessors build_model(args) state_dict torch.load(checkpoint.pth, map_locationcpu)[model] model.load_state_dict(state_dict) model.eval() img Image.open(test.jpg).convert(RGB) transform Compose([ Resize((800, 800)), ToTensor(), Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) inputs transform(img).unsqueeze(0) outputs model(inputs) post_out postprocessors[bbox](outputs, torch.zeros_like(inputs[:, 0, 0]))这里postprocessors[bbox]会做两件事把输出的归一化cx, cy, w, h转成x1, y1, x2, y2的绝对像素坐标并过滤掉低置信度的预测。过滤的置信度阈值在postprocessors构建时由--eos_coef等参数间接影响实际操作时我建议自己再设一个0.7的阈值只保留概率大于0.7的结果。5.2 画框与置信度筛选拿到坐标之后用OpenCV或者PIL画框都很简单。我的做法是先按类别读取置信度再决定是否画出。代码片段scores, labels, boxes post_out[0][scores], post_out[0][labels], post_out[0][boxes] keep scores 0.7 for label, box, score in zip(labels[keep], boxes[keep], scores[keep]): x0, y0, x1, y1 box.tolist() cv2.rectangle(img, (int(x0), int(y0)), (int(x1), int(y1)), (0, 255, 0), 2) cv2.putText(img, f{class_names[label]}: {score:.2f}, (int(x0), int(y0) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)有个容易被忽略的点DETR的boxes是cx, cy, w, h格式且已经除以了原始图像尺寸归一化到0-1。postprocessors返回的坐标是x1, y1, x2, y2的绝对像素值直接画即可不需要再乘以(img_w, img_h)。5.3 小样本数据上预测效果的直观感受我在小样本测试集上的直观感受是DETR对遮挡场景的鲁棒性确实比YOLO好。YOLO在两个人重叠时经常输出两个几乎重叠的框或者把两个人合并成一个框DETR因为全局注意力能看到完整上下文在中等遮挡下依然能区分个体。代价是推理速度明显慢于YOLO在没有GPU加速的CPU上一次预测可能要几百毫秒到一两秒。如果项目对实时性要求高DETR的改进版本或蒸馏方案会更合适。6. 实战中反复遇到的三个坑及完整排查链路6.1 显存溢出从batch_size到梯度累积的解决路径我最早在8GB显存的卡上跑DETRbatch_size2直接OOM。先尝试把batch_size降到1问题还在因为单张800分辨率的图加上Transformer多头注意力占用的中间激活值就已经很大。再往后我把图像输入分辨率降到600显存占用降了不少但小目标效果也明显变差。最终我选择的组合是batch_size1 梯度累积4步等效batch_size4并保持分辨率默认。梯度累积通过accum_iter参数控制具体做法是每4个小batch才执行一次optimizer.step()代码里只需在loss.backward()后面加一个判断达到累积步数再清零梯度。这个办法比降低分辨率更保护检测精度。6.2 训练几十个epoch loss不降学习率与数据问题的判定顺序如果你发现loss完全不动先不要急着调模型结构按这个顺序排查检查数据加载是否正常。把--num_workers设为0排除多线程死锁。检查预训练权重是否加载成功。打印模型backbone的统计量或者直接对比训练前后的权重张量。检查学习率是否过大。DETR对学习率比较敏感我用1e-4正常有人调到1e-3后训练初期loss疯狂震荡降回1e-4才稳定。检查类别数是否多加了背景。若num_classes填成实际类别数而忘了1输出通道和标签索引错位模型永远学不对。我遇到过最诡异的一次是loss前20个epoch在1.9附近波动后来发现是数据集JSON的annotation里出现了空标注图片模型反复学习“图上没有东西”的映射。把空标注图片过滤后loss立刻开始正常下降。6.3 预测时类别错乱num_classes与背景通道的坑DETR推理时最常见的诡异现象是模型把背景当成一个目标预测出来或者类别索引全部偏了1。根因基本都在类别数设置。训练时num_classes要设成实际类别1但在推理画框时要把背景通道剔除只显示实际类别。我写过一版代码训练时num_classes3推理时class_names[helmet, person]结果真实类别2被显示成背景。排查后发现模型的输出索引0和1对应两个真实类索引2是背景而我画框时没有过滤背景。定位过程用了一个很笨但有效的办法把测试图片里所有被预测的框连同类别编号直接打印出来再对照图上真实目标位置一眼就看出是索引偏移。修正方式就是推理画框时加一行if label.item() num_real_classes: continue问题立刻消除。这三个坑一个比一个隐蔽尤其是类别索引偏移它不会导致loss异常只会让结果看起来“模型学错了”。建议你在自己的推理脚本里始终把背景通道过滤逻辑放在最前面不要依赖后处理器的自动处理。最后分享一个我个人的习惯初次跑DETR时先用官方预训练权重跑一次推理确认环境、代码、数据链路都正常再开始训练自己的数据。否则你很难分辨是环境问题、数据问题还是模型训练策略问题。DETR这套范式学起来曲线比YOLO陡但一旦理解端到端集合预测的思路回头看YOLO繁琐的锚框和NMS设计你会有一种“原来检测可以这么干净”的感觉。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询