今日目标 :理解DETR如何用Transformer做端到端检测,掌握Object Query和二分图匹配的原理,对比传统检测器
预计阅读 :12分钟 | 动手操作:40分钟
一、传统检测器 vs DETR:范式革命
传统检测器(YOLO/Faster R-CNN)的"包袱":
1. Anchor设计(大小/比例/数量)
2. NMS后处理(去重)
3. 正负样本分配(IoU阈值)
4. RPN/Region Proposal
这些组件需要大量手工设计和调参
DETR的思想:
把目标检测变成"集合预测"问题
输入:图像
输出:直接输出N个物体的 (类别, 框)
不需要Anchor、不需要NMS、不需要RPN
端到端!一个Transformer搞定一切!
DETR = CNN Backbone + Transformer Encoder + Transformer Decoder + FFN预测头
python
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
from scipy.optimize import linear_sum_assignment
torch.manual_seed(42)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
二、核心知识点
2.1 DETR的检测哲学:Object Query
python
"""
DETR最核心的创新:Object Query(物体查询)
传统检测器:
"在图像的每个位置,用不同大小/比例的Anchor去匹配"
→ 需要Anchor、NMS、手工设计
DETR:
"我有100个Object Query,每个Query问:'这里有没有物体?'"
→ 每个Query学习关注图像的某个区域或某种模式
→ Query通过交叉注意力从图像特征中提取信息
→ 每个Query输出一个预测(类别+框)
→ 如果没有物体,输出"无物体"类别(∅)
Object Query = 可学习的"物体探测器"
Query 0: 学会了关注图像左上角 → 检测左上角的物体
Query 1: 学会了关注图像中心 → 检测中心的物体
Query 5: 学会了关注大物体
Query 20: 学会了关注小物体
...
"""
class ObjectQueryConcept:
def __init__(self):
self.num_queries = 100 # 100个query,最多检测100个物体
self.query_dim = 256 # 每个query是256维向量
def describe(self):
print("DETR Object Query:")
print(f" 数量: {self.num_queries}个")
print(f" 维度: {self.query_dim}维")
print(f" 本质: 可学习的物体位置编码")
print(f"\n 每个Query学习:")
print(f" - 关注图像某个区域")
print(f" - 关注某种大小的物体")
print(f" - 关注某种类别的物体")
print(f"\n 100个Query分工:")
print(f" Query 0-20: 小物体")
print(f" Query 20-60: 中等物体")
print(f" Query 60-100: 大物体")
ObjectQueryConcept().describe()
2.2 DETR架构全景
python
"""
DETR完整架构:
1. CNN Backbone (ResNet-50)
224×224×3 → 7×7×2048
提取图像特征
2. 位置编码 + 特征降维
2048 → 256 (1×1卷积)
7×7=49个位置编码
3. Transformer Encoder
全局建模:每个像素看到所有其他像素
输入: 49个token (图像特征)
输出: 49个token (增强后的特征)
4. Transformer Decoder
交叉注意力:Object Query从图像特征中找物体
输入: 100个Object Query + 49个Encoder输出
输出: 100个增强的Query
5. FFN预测头
每个Query → 类别预测 + 框预测
类别: 91类 (COCO) 或 N+1类 (N类+背景)
框: (cx, cy, w, h) 归一化坐标
"""
class DETRArchitecture:
def __init__(self):
self.components = [
('CNN Backbone', 'ResNet-50', '图像 → 特征图'),
('位置编码', '正弦编码', '告诉Transformer像素位置'),
('特征投影', '1×1 Conv', '2048维 → 256维'),
('Transformer Encoder', '6层', '全局特征增强'),
('Transformer Decoder', '6层', 'Object Query + 交叉注意力'),
('FFN预测头', '3层MLP', '每个Query → 类别+框'),
]
def describe(self):
print("DETR架构:")
for name, config, desc in self.components:
print(f" {name:<20s} {config:<15s} {desc}")
DETRArchitecture().describe()
2.3 二分图匹配损失:训练的关键
python
"""
DETR的损失函数:匈牙利匹配损失
传统检测器:每个Anchor匹配一个GT(IoU阈值)
DETR:100个预测 匹配 N个GT,求最优匹配
为什么需要二分图匹配?
100个预测中,只有N个有物体(N << 100)
需要找到"哪个预测对应哪个GT"
这是典型的二分图匹配问题!
流程:
1. 计算代价矩阵 C(100×N)
C[i][j] = 预测i匹配GT j的代价
代价 = -log(类别概率) + L1_loss(框) + GIoU_loss(框)
2. 匈牙利算法求最优匹配
→ 每个GT分配一个预测
→ 未匹配的预测 → 目标类别 = ∅ (背景)
3. 计算损失
matched: 类别损失 + 框损失
unmatched: 只有类别损失(目标=背景)
"""
class DETRLoss:
def __init__(self, num_classes=91, class_weight=1.0, bbox_weight=5.0, giou_weight=2.0):
self.num_classes = num_classes
self.class_weight = class_weight
self.bbox_weight = bbox_weight
self.giou_weight = giou_weight
def _giou_loss(self, pred_boxes, target_boxes):
"""计算GIoU Loss"""
# 简化版:实际需要完整的GIoU计算
# 这里用L1 Loss作为近似
return F.l1_loss(pred_boxes, target_boxes)
def _compute_cost_matrix(self, pred_logits, pred_boxes, targets):
"""
计算代价矩阵
pred_logits: (100, num_classes)
pred_boxes: (100, 4)
targets: [(class_id, cx, cy, w, h), ...]
"""
num_preds = pred_logits.shape[0]
num_targets = len(targets)
if num_targets == 0:
return torch.zeros(num_preds, 0)
# 构建GT张量
target_classes = torch.tensor([t[0] for t in targets])
target_boxes = torch.tensor([[t[1], t[2], t[3], t[4]] for t in targets])
# 类别代价
class_cost = -pred_logits[:, target_classes] # (100, N)
# 框代价
bbox_cost = torch.cdist(pred_boxes, target_boxes, p=1) # L1距离
# GIoU代价
giou_cost = torch.zeros_like(bbox_cost)
for i in range(num_preds):
for j in range(num_targets):
giou_cost[i, j] = self._giou_loss(
pred_boxes[i].unsqueeze(0), target_boxes[j].unsqueeze(0)
)
# 总代价
total_cost = (
self.class_weight * class_cost +
self.bbox_weight * bbox_cost +
self.giou_weight * giou_cost
)
return total_cost.detach().cpu().numpy()
def forward(self, pred_logits, pred_boxes, targets):
"""
前向计算损失
targets: list of [(class_id, cx, cy, w, h), ...]
"""
num_preds = pred_logits.shape[0]
if len(targets) == 0:
# 没有GT:所有预测都是背景
return {'loss_ce': 0, 'loss_bbox': 0, 'loss_giou': 0}
# 1. 计算代价矩阵
cost_matrix = self._compute_cost_matrix(pred_logits, pred_boxes, targets)
# 2. 匈牙利匹配
row_ind, col_ind = linear_sum_assignment(cost_matrix)
# 3. 构建目标标签
target_classes = torch.ones(num_preds, dtype=torch.long) * (self.num_classes - 1) # 默认背景
target_boxes_all = torch.zeros(num_preds, 4)
for r, c in zip(row_ind, col_ind):
target_classes[r] = targets[c][0]
target_boxes_all[r] = torch.tensor([targets[c][1], targets[c][2], targets[c][3], targets[c][4]])
# 4. 计算损失
loss_ce = F.cross_entropy(pred_logits, target_classes)
# 只对匹配的预测计算框损失
bbox_mask = (target_classes != self.num_classes - 1)
if bbox_mask.sum() > 0:
loss_bbox = F.l1_loss(pred_boxes[bbox_mask], target_boxes_all[bbox_mask])
loss_giou = self._giou_loss(pred_boxes[bbox_mask], target_boxes_all[bbox_mask])
else:
loss_bbox = torch.tensor(0.0)
loss_giou = torch.tensor(0.0)
return {
'loss_ce': loss_ce,
'loss_bbox': loss_bbox * self.bbox_weight,
'loss_giou': loss_giou * self.giou_weight,
}
def detr_loss_demo():
"""演示DETR损失计算"""
loss_fn = DETRLoss()
# 模拟:100个预测,2个GT
pred_logits = torch.randn(100, 91) # 91类
pred_boxes = torch.sigmoid(torch.randn(100, 4)) # 归一化坐标
targets = [
(5, 0.3, 0.4, 0.2, 0.3), # 类别5, 框(0.3, 0.4, 0.2, 0.3)
(12, 0.6, 0.7, 0.15, 0.25), # 类别12
]
losses = loss_fn.forward(pred_logits, pred_boxes, targets)
print("DETR损失计算演示:")
print(f" 预测: 100个query")
print(f" GT: 2个物体")
print(f" 匈牙利匹配: 2个预测匹配GT,98个预测匹配背景")
print(f"\n 损失:")
for k, v in losses.items():
print(f" {k}: {v.item():.4f}")
detr_loss_demo()
2.4 DETR的Transformer Decoder
python
class DETRDecoderConcept:
"""
DETR Decoder的核心:交叉注意力
Self-Attention: Object Query之间的交互
→ 让Query之间互相协调,避免检测同一个物体
Cross-Attention: Object Query 从 Encoder输出中提取信息
→ Query关注图像特征的某个区域
→ 这就是"Query去图像中找物体"的过程
Self-Attention的Q/K/V都来自Object Query
Cross-Attention的Q来自Object Query,K/V来自Encoder输出
Decoder 6层,每层:
1. Self-Attention: Query之间协调
2. Cross-Attention: Query从图像特征中提取信息
3. FFN: 特征变换
"""
def describe(self):
print("DETR Decoder的交叉注意力:")
print(" Q = Object Query (100个)")
print(" K = Encoder输出 (49个像素特征)")
print(" V = Encoder输出 (49个像素特征)")
print("\n 每个Query:")
print(" 1. 计算Q与所有K的相似度")
print(" 2. 找到最相关的像素区域")
print(" 3. 加权聚合那些像素的信息")
print(" 4. 更新自己的表示")
print("\n 6层迭代后,每个Query都找到了自己的物体!")
DETRDecoderConcept().describe()
三、动手实践:DETR vs 传统检测器
python
def detr_vs_traditional():
"""
DETR vs 传统检测器的核心区别
"""
comparison = {
'Anchor': {
'YOLO/Faster R-CNN': '需要手工设计Anchor',
'DETR': '不需要!Object Query替代',
},
'NMS': {
'YOLO/Faster R-CNN': '需要NMS后处理',
'DETR': '不需要!二分图匹配天然去重',
},
'RPN': {
'YOLO/Faster R-CNN': 'Faster R-CNN需要RPN',
'DETR': '不需要!Decoder直接预测',
},
'正负样本': {
'YOLO/Faster R-CNN': 'IoU阈值分配',
'DETR': '匈牙利匹配分配',
},
'预测数量': {
'YOLO/Faster R-CNN': '固定(Anchor数量)',
'DETR': '固定(100个Query)',
},
'训练收敛': {
'YOLO/Faster R-CNN': '快(几小时)',
'DETR': '慢(需要500 epoch)',
},
'小物体检测': {
'YOLO/Faster R-CNN': 'Faster R-CNN较好',
'DETR': '较差(特征图分辨率低)',
},
'端侧部署': {
'YOLO/Faster R-CNN': '成熟(YOLO首选)',
'DETR': '不推荐(参数量大,速度慢)',
},
}
print("DETR vs 传统检测器:")
for aspect, info in comparison.items():
print(f"\n [{aspect}]")
print(f" YOLO/Faster R-CNN: {info['YOLO/Faster R-CNN']}")
print(f" DETR: {info['DETR']}")
detr_vs_traditional()
四、Deformable DETR:改进版
python
"""
Deformable DETR: 解决DETR的三大问题
DETR的三大问题:
1. 训练收敛慢(需要500 epoch,YOLO只需50 epoch)
2. 小物体检测差(特征图分辨率低)
3. 计算量大(自注意力是O(N²))
Deformable DETR的改进:
1. Deformable Attention
不是看所有像素,只关注参考点周围K个采样点
O(N²) → O(N×K),K=4,大幅加速!
2. 多尺度特征
不只用一个尺度的特征图
用FPN式的多尺度特征(类似YOLO)
→ 小物体检测大幅提升!
3. 收敛加速
训练从500 epoch降到50 epoch
→ 和传统检测器一样快
Deformable DETR ≈ DETR + 多尺度 + 可变形注意力
"""
class DeformableDETRConcept:
def __init__(self):
self.improvements = {
'可变形注意力': 'O(N×K) vs O(N²),K=4',
'多尺度特征': '4个尺度,类似FPN',
'收敛速度': '500 epoch → 50 epoch',
'小物体': '大幅提升(多尺度)',
'参数量': '~40M(DETR ~41M)',
}
def describe(self):
print("Deformable DETR改进:")
for k, v in self.improvements.items():
print(f" {k}: {v}")
print("\n 端侧部署: 仍然较重,不推荐")
print(" 学术价值: 检测Transformer的里程碑")
DeformableDETRConcept().describe()
五、端侧AI检测方案对比
python
def edge_detection_guide():
"""
端侧AI目标检测方案全面对比
"""
guide = {
'YOLOv8-nano': {
'params': '3.2M',
'FPS': '200+ (Jetson)',
'mAP': '中',
'端侧推荐': '⭐⭐⭐⭐⭐ 首选',
},
'YOLO-NAS': {
'params': '~5M',
'FPS': '150+',
'mAP': '中高',
'端侧推荐': '⭐⭐⭐⭐',
},
'RT-DETR': {
'params': '~20M',
'FPS': '50-100',
'mAP': '高',
'端侧推荐': '⭐⭐⭐ DETR式端侧方案',
},
'DETR-R50': {
'params': '41M',
'FPS': '10-20',
'mAP': '中',
'端侧推荐': '⭐⭐ 不推荐',
},
}
print("端侧AI检测方案:")
print(f"{'模型':<15s} {'参数':<8s} {'FPS':<12s} {'mAP':<8s} {'推荐'}")
print('-' * 60)
for name, info in guide.items():
print(f"{name:<15s} {info['params']:<8s} {info['FPS']:<12s} {info['mAP']:<8s} {info['端侧推荐']}")
print("\n结论: 端侧AI检测,YOLO仍然是王者")
print(" DETR的价值: 理解为范式,学思想,端侧暂不部署")
edge_detection_guide()
六、常见坑点
坑1:DETR需要大量训练
python
# DETR训练需要500 epoch(COCO上)
# 相比之下YOLO只需要50 epoch
# 原因:二分图匹配的收敛比Anchor匹配慢
# 如果数据少(<1000张),DETR很难收敛
# ✅ 小数据集用YOLO,大数据集可以尝试DETR
坑2:Object Query数量的选择
python
# 默认100个Query → 最多检测100个物体
# 太少 → 检测不到所有物体
# 太多 → 计算量大,收敛慢
# 端侧:推荐50个Query(减少计算量)
# 服务器:推荐100-300个Query
坑3:二分图匹配中"背景"类的处理
python
# 100个Query,只有N个匹配GT(N << 100)
# 其余100-N个Query → 目标类别 = ∅(背景/无物体)
# 背景类必须有专门的类别编号(如 num_classes 作为背景索引)
# ❌ 忘记给背景类留位置 → 训练崩溃
# ✅ 分类头: num_classes+1 (多一个背景类)
坑4:DETR不擅长小物体
python
# DETR的Encoder输出分辨率 = 输入/32
# 224×224 → 7×7 → 49个token
# 30×30的小物体只占1-2个token → 信息不足
# ✅ 解决方案:Deformable DETR的多尺度特征
# ✅ 或放大输入图像
七、今日作业
- 手写二分图匹配:理解匈牙利匹配在DETR中的应用
- DETR架构:画出DETR的完整数据流:CNN → Encoder → Decoder → FFN
- 对比思考:DETR、YOLO、Faster R-CNN各有什么优缺点?
- 打卡 :评论区发你的理解,格式:"Day 35/100 打卡:DETR检测范式已掌握!"
今日小结
今天你学会了:
✅ DETR:端到端检测,去掉Anchor/NMS/RPN
✅ Object Query:可学习的"物体探测器"
✅ 二分图匹配损失:匈牙利算法分配预测→GT
✅ DETR架构:CNN → Encoder → Decoder → FFN
✅ 交叉注意力:Query从图像特征中提取信息
✅ Deformable DETR:多尺度 + 可变形注意力
✅ DETR vs YOLO全面对比
✅ 端侧AI检测方案:YOLO仍是王者
✅ 4个经典坑点
明日预告
Day 36:轻量级Backbone(MobileNet/ShuffleNet/EfficientNet)
深度可分离卷积、通道重排、复合缩放、端侧最佳backbone
🔥 关注我,每天解锁一个端侧AI技能!
微信公众号:xxx | 小红书:xxx | CSDN:xxx
评论区打卡,一起坚持100天!
附:小红书图文版
封面标题建议:DETR | Transformer做检测,不用Anchor不用NMS!🔥
P1 --- 封面
标题:DETR检测Transformer
副标题:Object Query / 二分图匹配 / 端到端检测
关键词:DETR / Transformer / 目标检测 / 端到端
P2 --- 传统检测器的包袱
Anchor设计 → 手工调参
NMS后处理 → 复杂
正负样本分配 → IoU阈值
RPN → 两阶段慢
DETR:全部扔掉!端到端!
P3 --- Object Query魔法
100个可学习的"物体探测器"
每个Query学习关注不同区域
Query 0-20: 小物体
Query 20-60: 中等物体
Query 60-100: 大物体
通过交叉注意力从图像中提取信息
P4 --- 二分图匹配
DETR怎么训练?
100个预测 ↔ 2个GT
匈牙利算法求最优匹配
匹配的:类别损失+框损失
未匹配的:目标=背景 ∅
P5 --- DETR vs YOLO
DETR: 端到端优雅,但训练慢(500 epoch)
YOLO: 需要Anchor/NMS,但收敛快(50 epoch)
端侧:YOLO是王者!
DETR的价值:学思想,理解范式
P6 --- 今日作业
画出DETR架构 + 手写二分图匹配
评论区打卡 Day 35/100
标签:#DETR #Transformer #目标检测 #端到端
CSDN发布提示:CSDN版本建议在DETR架构部分放完整的CNN→Encoder→Decoder→FFN流程图,在Object Query部分放Query和图像特征图交互的可视化,在二分图匹配部分放匈牙利匹配示意图,在DETR vs YOLO部分放对比表。