第6讲:检测大模型——从DETR到RT-DETR/R-DETR
第6讲:检测大模型——从DETR到RT-DETR/R-DETR
一、从一个"找猫"游戏开始
1.1 传统目标检测的痛点
想象你要在一张复杂照片里找到所有的猫:
照片里有:
- 3只猫(大小不一,有的被遮挡)
- 2只狗
- 1辆汽车
- 背景:沙发、窗户、植物
传统方法(Anchor-based)怎么做?
步骤1:在图片上撒"锚框"(Anchor Boxes)
像撒网捕鱼一样,在图片每个位置放几百个不同大小的框
小框(检测小猫)、中框(检测中猫)、大框(检测大猫)
步骤2:每个锚框做两件事
- 分类:这个框里是猫?狗?还是背景?
- 回归:调整框的位置和大小,让它更贴合目标
步骤3:非极大值抑制(NMS)
同一个猫可能被多个框检测到,只保留最好的那个
痛点:
| 痛点 | 说明 | 后果 |
|---|---|---|
| 锚框设计复杂 | 需要预设大小、比例、数量 | 不同数据集要调参 |
| 正负样本不平衡 | 99%的锚框是背景(负样本) | 训练效率低,模型偏向背景 |
| NMS后处理 | 需要手动调阈值 | 实时性差,容易漏检或重复 |
| 非端到端 | 分类、回归、NMS是分开的 | 梯度无法统一优化 |
就像钓鱼时撒了1000个网,99%是空的,还要人工挑哪个网里有鱼。
二、DETR的革命:把检测变成"集合预测"
2.1 核心思想:像翻译一样做检测
机器翻译:
输入:一段中文句子(词序列)
输出:一段英文句子(词序列)
模型:Transformer(Encoder-Decoder)
目标检测:
输入:一张图片(像素网格)
输出:一组边界框 + 类别(框序列)
模型:???
DETR的洞察:
"检测和翻译本质一样:都是序列到序列的转换!"
DETR(DEtection TRansformer):
输入图像 → CNN提取特征 → Transformer编码器 → Transformer解码器
↓
100个"查询框"
↓
每个查询输出:
- 框坐标 (x, y, w, h)
- 类别概率(包括"无对象"类)
2.2 DETR的架构详解
整体流程:
原始图像 [3, H, W]
↓
┌─────────────────────────┐
│ CNN Backbone(ResNet-50)│ ← 提取视觉特征
│ 输出: [2048, H/32, W/32] │
└─────────────────────────┘
↓
┌─────────────────────────┐
│ 1×1卷积降维 │
│ 输出: [256, H/32, W/32] │
└─────────────────────────┘
↓
展平 + 加位置编码
↓
┌─────────────────────────┐
│ Transformer Encoder │ ← 全局特征交互
│ (6层,自注意力) │
└─────────────────────────┘
↓
┌─────────────────────────┐
│ Transformer Decoder │ ← 并行生成100个预测框
│ (6层,交叉注意力) │
│ 输入: 100个可学习查询 + │
│ Encoder输出 │
└─────────────────────────┘
↓
100个预测结果(每个:框坐标 + 类别)
↓
匈牙利匹配 → 与真实框配对 → 计算损失
关键创新点:
| 组件 | 作用 | 类比 |
|---|---|---|
| Object Queries | 100个可学习的"查询向量" | 像100个侦探,各自负责找不同目标 |
| Encoder | 全局理解图像内容 | 侦探们先看完整张地图 |
| Decoder | 每个Query去图像中"查案" | 侦探们分头行动,用交叉注意力找目标 |
| 匈牙利匹配 | 预测框和真实框的最优配对 | 给每个侦探分配最合理的任务 |
2.3 Object Queries:100个"侦探"
传统检测:每个位置预设锚框(被动等待)
DETR:100个可学习的Object Queries(主动查询)
初始化:随机向量 [100, 256]
训练后:
Query 1 → 学会找"大图中央的对象"
Query 2 → 学会找"左上角的小对象"
Query 3 → 学会找"被遮挡的对象"
...
Query 100 → 学会判断"这里没有对象"(背景)
Decoder的工作方式(交叉注意力):
每个Query(侦探):
"我在图像的哪个区域找目标?"
↓
与Encoder输出的全局特征做交叉注意力
↓
聚焦到相关区域
↓
输出:框坐标 + 类别
100个Query是并行的,不像GPT那样自回归。DETR的Decoder一次输出所有预测。
三、匈牙利匹配:给侦探分配任务
3.1 问题:预测框和真实框怎么配对?
真实标签(Ground Truth):
猫1: (x=100, y=200, w=50, h=60)
猫2: (x=300, y=150, w=80, h=70)
猫3: (x=500, y=400, w=40, h=50)
DETR预测(100个框):
框1: (x=105, y=205, w=48, h=58) ← 很像猫1!
框2: (x=500, y=100, w=30, h=30) ← 不太像任何猫
框3: (x=305, y=155, w=75, h=68) ← 很像猫2!
...
框100: (x=0, y=0, w=0, h=0) ← "无对象"类
挑战:
- 预测数(100)≠ 真实数(3)
- 需要找到最优配对,让总损失最小
3.2 匈牙利算法:最优分配的数学
类比:4个工人做4个任务
成本矩阵(工人i做任务j的成本):
任务A 任务B 任务C 任务D
工人1 9 2 7 8
工人2 6 4 3 7
工人3 5 8 1 8
工人4 7 6 9 4
目标:每个工人分配一个任务,总成本最小
匈牙利算法步骤(简化版):
Step 1: 每行减去最小值
Step 2: 每列减去最小值
Step 3: 用最少的线覆盖所有0
Step 4: 如果线数=矩阵维度,找到最优分配
否则,调整矩阵,重复Step 3-4
在DETR中的应用:
成本 = 分类损失 + 边界框损失(L1 + GIoU)
目标:找到预测框和真实框的配对,使总成本最小
多余预测(100 > 真实数)→ 配对给"无对象"类(背景)
代码实现:
from scipy.optimize import linear_sum_assignment
# 成本矩阵:预测框 vs 真实框
# 形状: [num_predictions, num_targets]
cost_matrix = compute_cost(predictions, targets)
# cost = λ_cls * L_cls + λ_bbox * L1 + λ_giou * L_giou
# 匈牙利算法求解最优分配
row_ind, col_ind = linear_sum_assignment(cost_matrix)
# row_ind[i] 的预测框 匹配到 col_ind[i] 的真实框
matched_predictions = predictions[row_ind]
matched_targets = targets[col_ind]
四、RT-DETR:实时检测的优化
4.1 DETR的瓶颈
| 问题 | 原因 | 影响 |
|---|---|---|
| 训练慢 | 匈牙利匹配 + Transformer全局注意力 | 需要500 epoch才能收敛 |
| 推理慢 | Encoder的自注意力O(n²),n=特征图尺寸 | 无法满足实时需求(30FPS) |
| 小目标差 | 高分辨率特征图计算量太大 | 小物体检测精度低 |
4.2 RT-DETR的三大优化
优化1:Hybrid Encoder(混合编码器)
DETR的Encoder:所有尺度特征都过Transformer → 计算量大
RT-DETR的Encoder:
┌─────────────────────────────────────┐
│ AIFI(Attention-based Intra-scale) │ ← 单尺度自注意力(轻量)
│ 处理最高分辨率特征 │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ CCFF(CNN-based Cross-scale) │ ← 跨尺度融合(高效)
│ 用CNN融合多尺度特征 │
└─────────────────────────────────────┘
效果:保留全局建模能力,大幅降低计算量
通俗理解:
像看地图:DETR用放大镜看每个细节(慢),RT-DETR先看整体轮廓(AIFI),再用CNN快速拼接细节(CCFF)。
优化2:IoU-aware Query Selection
DETR的Query:100个随机初始化,训练后学会分工
RT-DETR的Query:从Encoder输出中"精选"高质量候选
步骤:
1. Encoder输出特征图,预测初步的类别和框
2. 计算每个位置的IoU(与真实框的重叠度)
3. 选IoU最高的Top-K个位置 → 作为Decoder的初始Query
效果:Query起点更好,收敛更快,精度更高
类比:
DETR:100个侦探随机分散在城市里找线索
RT-DETR:先根据报警记录(IoU),把侦探派到最可能出事的地方
优化3:高效的Decoder设计
DETR Decoder:6层Transformer,每层都过交叉注意力
RT-DETR Decoder:
- 减少层数(如3层)
- 优化交叉注意力的计算
- 引入多尺度特征融合
效果:在精度几乎不降的情况下,速度提升3-5倍
4.3 性能对比
| 模型 | 骨干网络 | AP(COCO) | FPS(T4 GPU) | 延迟 |
|---|---|---|---|---|
| DETR-R50 | ResNet-50 | 42.0 | 28 | 36ms |
| RT-DETR-R50 | ResNet-50 | 53.1 | 108 | 9.2ms |
| RT-DETR-R101 | ResNet-101 | 54.3 | 74 | 13.5ms |
| YOLOv8-X | - | 53.9 | 60 | 16.7ms |
RT-DETR首次实现了Transformer检测器的实时性,且精度超越YOLO系列!
五、DINO/R-DETR的改进:对比学习与去噪训练
5.1 DINO(DETR with Improved deNoising anchOr boxes)
核心改进:
| 技术 | 作用 | 效果 |
|---|---|---|
| 对比去噪训练(CDN) | 同时训练"干净查询"和"带噪查询" | 加速收敛,提升稳定性 |
| 混合查询选择 | 部分Query用IoU选,部分随机 | 兼顾精度和多样性 |
| Look Forward Twice | 解码器层间信息传递优化 | 提升小目标检测 |
去噪训练详解:
正常训练:
输入图像 → Encoder → Query → Decoder → 预测框
↓
与真实框算损失
去噪训练:
输入图像 → Encoder → 干净Query → Decoder → 预测框1
↓
加噪声Query(故意扰动坐标)→ Decoder → 预测框2
↓
目标:框2也要接近真实框!
效果:
- 模型学会"即使查询不太准,也能修正"
- 类似数据增强,提升鲁棒性
- 收敛速度提升2-3倍
六、动手实验:用Ultralytics训练RT-DETR
6.1 环境准备
# 安装ultralytics(包含RT-DETR)
pip install ultralytics
# 验证安装
yolo checks
6.2 实验1:用预训练RT-DETR做推理
from ultralytics import RTDETR
import cv2
import matplotlib.pyplot as plt
import numpy as np
print("=" * 60)
print("【实验1】RT-DETR预训练模型推理")
print("=" * 60)
# 加载预训练模型(自动下载)
model = RTDETR('rtdetr-l.pt') # 'rtdetr-l' 或 'rtdetr-x'
print("✅ 模型加载完成")
# 用COCO预训练权重检测图片
# 可以替换为你的图片路径
image_path = 'https://ultralytics.com/images/bus.jpg' # 示例图片
print(f"\n检测图片: {image_path}")
results = model(image_path)
# 可视化结果
for result in results:
# 获取带标注的图片
annotated_frame = result.plot()
# 显示
plt.figure(figsize=(12, 8))
plt.imshow(cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB))
plt.title('RT-DETR Detection Result')
plt.axis('off')
plt.tight_layout()
plt.savefig('/mnt/agents/output/rtdetr_detection_result.png', dpi=150)
plt.show()
# 打印检测信息
boxes = result.boxes
print(f"\n检测到 {len(boxes)} 个对象:")
for i, box in enumerate(boxes):
cls_id = int(box.cls)
conf = float(box.conf)
xyxy = box.xyxy[0].tolist() # [x1, y1, x2, y2]
class_name = result.names[cls_id]
print(f" {i+1}. {class_name}: 置信度={conf:.2%}, 位置=({xyxy[0]:.1f}, {xyxy[1]:.1f}, {xyxy[2]:.1f}, {xyxy[3]:.1f})")
print("\n📊 检测结果已保存!")
6.3 实验2:在自定义数据集上训练
from ultralytics import RTDETR
import yaml
print("\n" + "=" * 60)
print("【实验2】RT-DETR自定义数据集训练")
print("=" * 60)
# ============================================
# 步骤1:准备数据集(YOLO格式)
# ============================================
"""
YOLO格式目录结构:
dataset/
├── train/
│ ├── images/
│ │ ├── img001.jpg
│ │ └── ...
│ └── labels/
│ ├── img001.txt
│ └── ...
├── val/
│ ├── images/
│ └── labels/
└── data.yaml
标注文件格式(img001.txt):
0 0.5 0.5 0.3 0.4 ← class x_center y_center width height(归一化0-1)
1 0.2 0.3 0.1 0.2
"""
# 创建示例data.yaml
data_yaml = """
path: ./custom_dataset # 数据集根目录
train: train/images
val: val/images
# 类别
names:
0: cat
1: dog
2: person
nc: 3 # 类别数
"""
# 保存yaml(实际使用时修改路径)
with open('custom_data.yaml', 'w') as f:
f.write(data_yaml.strip())
print("✅ 数据集配置文件已创建: custom_data.yaml")
print("请根据实际数据集修改路径和类别")
# ============================================
# 步骤2:加载模型并训练
# ============================================
# 加载预训练模型(迁移学习)
model = RTDETR('rtdetr-l.pt')
print("\n开始训练...")
print("参数说明:")
print(" data: 数据集配置文件")
print(" epochs: 训练轮数(RT-DETR通常需要100-300轮)")
print(" imgsz: 输入图像尺寸(640)")
print(" batch: 批次大小(根据GPU调整)")
print(" lr0: 初始学习率")
print(" lrf: 最终学习率因子")
# 训练(演示用,实际运行时取消注释)
"""
results = model.train(
data='custom_data.yaml',
epochs=100,
imgsz=640,
batch=8,
lr0=1e-4, # RT-DETR需要较小学习率
lrf=0.01,
optimizer='AdamW',
patience=20, # 早停耐心值
save=True,
device=0 # GPU设备号
)
"""
print("""
训练技巧:
1. 一定要用预训练权重!(rtdetr-l.pt)
RT-DETR从头训练需要大量数据和算力
2. 学习率要小(1e-4到5e-4)
Transformer对学习率敏感
3. 数据增强要充分
RT-DETR没有CNN的归纳偏置,需要更多数据变化
4. 训练轮数要够
DETR系列收敛慢,100轮起步,300轮更好
""")
# ============================================
# 步骤3:验证和导出
# ============================================
print("\n" + "=" * 60)
print("【步骤3】模型验证与导出")
print("=" * 60)
# 验证(训练后)
# metrics = model.val()
# 导出为ONNX(部署用)
# model.export(format='onnx', dynamic=True, simplify=True)
print("""
导出格式选项:
ONNX: 通用格式,支持动态输入
TensorRT: NVIDIA GPU加速(最快)
OpenVINO: Intel CPU优化
CoreML: Apple设备
""")
print("\n✅ 训练流程说明完成!")
6.4 实验3:理解匈牙利匹配(可视化)
import torch
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import linear_sum_assignment
print("\n" + "=" * 60)
print("【实验3】匈牙利匹配算法可视化")
print("=" * 60)
def visualize_hungarian_matching():
"""
可视化匈牙利匹配过程
"""
# 模拟:3个真实框,5个预测框
np.random.seed(42)
# 真实框 [x, y, w, h]
gt_boxes = np.array([
[50, 50, 100, 100], # 猫1
[200, 150, 80, 120], # 猫2
[350, 300, 60, 60], # 猫3
])
# 预测框(有些好,有些差)
pred_boxes = np.array([
[55, 55, 95, 95], # 接近猫1 ✅
[210, 160, 75, 110], # 接近猫2 ✅
[100, 100, 50, 50], # 随机框 ❌
[360, 310, 55, 55], # 接近猫3 ✅
[400, 400, 30, 30], # 随机框 ❌
])
# 计算IoU成本(1 - IoU,越小越好)
def compute_iou(box1, box2):
# 计算交集
x1 = max(box1[0], box2[0])
y1 = max(box1[1], box2[1])
x2 = min(box1[0]+box1[2], box2[0]+box2[2])
y2 = min(box1[1]+box1[3], box2[1]+box2[3])
inter = max(0, x2-x1) * max(0, y2-y1)
area1 = box1[2] * box1[3]
area2 = box2[2] * box2[3]
union = area1 + area2 - inter
return inter / union if union > 0 else 0
# 构建成本矩阵
cost_matrix = np.zeros((len(pred_boxes), len(gt_boxes)))
for i, pred in enumerate(pred_boxes):
for j, gt in enumerate(gt_boxes):
iou = compute_iou(pred, gt)
cost_matrix[i, j] = 1 - iou # 成本 = 1 - IoU
print("成本矩阵(预测框 × 真实框):")
print(" 猫1 猫2 猫3")
for i, row in enumerate(cost_matrix):
print(f"框{i+1}: {row[0]:.3f} {row[1]:.3f} {row[2]:.3f}")
# 匈牙利算法求解
row_ind, col_ind = linear_sum_assignment(cost_matrix)
print(f"\n匈牙利匹配结果:")
print(f"最优配对(总成本最小):")
total_cost = 0
for r, c in zip(row_ind, col_ind):
cost = cost_matrix[r, c]
total_cost += cost
match_type = "✅ 好匹配" if cost < 0.5 else "⚠️ 勉强匹配"
print(f" 预测框{r+1} → 真实框{c+1}(猫{c+1}): 成本={cost:.3f} {match_type}")
print(f"\n总成本: {total_cost:.3f}")
# 未匹配的预测框 → 背景类
unmatched = set(range(len(pred_boxes))) - set(row_ind)
print(f"未匹配的预测框(视为背景): {[i+1 for i in unmatched]}")
# 可视化
fig, ax = plt.subplots(1, 1, figsize=(10, 8))
# 画真实框(绿色)
for i, (x, y, w, h) in enumerate(gt_boxes):
rect = plt.Rectangle((x, y), w, h, fill=False, edgecolor='green', linewidth=2)
ax.add_patch(rect)
ax.text(x, y-5, f'GT: 猫{i+1}', color='green', fontsize=10, weight='bold')
# 画预测框(红色),匹配的有连线
colors = ['red'] * len(pred_boxes)
for r in row_ind:
colors[r] = 'blue' # 匹配的变蓝色
for i, (x, y, w, h) in enumerate(pred_boxes):
rect = plt.Rectangle((x, y), w, h, fill=False, edgecolor=colors[i], linewidth=2)
ax.add_patch(rect)
ax.text(x+w, y+h+5, f'Pred{i+1}', color=colors[i], fontsize=9)
# 画匹配连线
for r, c in zip(row_ind, col_ind):
gt = gt_boxes[c]
pred = pred_boxes[r]
ax.plot([gt[0]+gt[2]/2, pred[0]+pred[2]/2],
[gt[1]+gt[3]/2, pred[1]+pred[3]/2],
'b--', alpha=0.5, linewidth=1)
ax.set_xlim(0, 500)
ax.set_ylim(400, 0) # Y轴翻转,符合图像坐标
ax.set_aspect('equal')
ax.set_title('Hungarian Matching: Predictions vs Ground Truth')
plt.legend(['Matched', 'GT', 'Unmatched Pred', 'Matched Pred'],
loc='upper right')
plt.tight_layout()
plt.savefig('/mnt/agents/output/hungarian_matching_viz.png', dpi=150)
plt.show()
print("\n📊 匹配可视化图已保存!")
visualize_hungarian_matching()
七、核心总结
| 概念 | 一句话解释 |
|---|---|
| Anchor-based | 预设大量候选框,分类+回归+NMS,复杂且慢 |
| DETR | 用Transformer做端到端检测,100个Query直接预测 |
| Object Query | 可学习的"侦探",各自负责找不同目标 |
| 匈牙利匹配 | 最优分配预测框到真实框,最小化总成本 |
| RT-DETR | 实时DETR,Hybrid Encoder + IoU-aware Query |
| 去噪训练 | 给Query加噪声,训练模型修正能力 |
| 端到端 | 不需要NMS后处理,梯度统一优化 |
八、面试高频题
Q1:DETR和传统检测器(YOLO/Faster R-CNN)的本质区别?
答:传统检测器是"两阶段"或"多阶段":先产生候选框(Anchor/RPN),再分类和回归,最后NMS去重。DETR是端到端的:用Transformer直接输出固定数量的预测框,通过匈牙利匹配与真实框配对,无需NMS和Anchor设计。
Q2:为什么DETR训练慢?RT-DETR怎么解决的?
答:DETR慢的原因:1)Transformer全局注意力O(n²)计算量大;2)匈牙利匹配的稀疏监督导致收敛慢;3)小目标检测需要高分辨率特征图。RT-DETR通过Hybrid Encoder(AIFI+CCFF)降低计算量,IoU-aware Query Selection提供更好的初始化,优化Decoder结构,实现实时推理。
Q3:匈牙利匹配的成本函数怎么设计?
答:成本 = 分类成本 + 边界框成本。分类成本常用Focal Loss或交叉熵;边界框成本用L1 Loss(坐标差)+ GIoU Loss(考虑重叠度和尺度)。超参数平衡三者权重,通常 λcls=1,λL1=5,λgiou=2\lambda_{cls}=1, \lambda_{L1}=5, \lambda_{giou}=2λcls=1,λL1=5,λgiou=2。
Q4:RT-DETR适合什么场景?
答:需要高精度和端到端优化的场景:工业质检(小目标检测)、自动驾驶(多尺度目标)、医学影像(需要全局上下文)。如果极致速度优先(如手机端),YOLOv8-nano可能更合适;如果精度优先且算力充足,RT-DETR是更好的选择。
九、课后作业
作业1:对比实验
# 在同一数据集上,对比训练:
# 1. YOLOv8(Anchor-free但非Transformer)
# 2. RT-DETR(Transformer端到端)
# 观察:收敛速度、最终精度、推理速度
作业2:理解Query
# 可视化训练后的Object Query
# 看不同Query是否学会了关注不同位置/尺度
# 提示:提取Decoder交叉注意力的权重,画热力图
作业3:思考多模态检测
问题:如果要把文本描述(如"找到红色的车")加入检测,
DETR架构怎么改?
提示:Query变成"文本条件化的查询",
类似GLIP或Grounding DINO的做法。
十、下讲预告
第7讲:分割大模型——SAM(Segment Anything Model)
我们将:
- 理解"任意分割"的提示机制(点、框、文本、掩码)
- 探索SAM的编码器-解码器架构
- 动手用SAM做自动分割和交互式分割
- 了解SAM 2的视频分割能力
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐




所有评论(0)