RT-DETR是百度提出的实时检测Transformer,精度比YOLOv5x高,延迟更低。但Transformer的量化比CNN麻烦,attention层的动态范围大、LayerNorm对精度敏感、decoder的交叉注意力有复杂索引操作。这篇把RT-DETR在征程6M上的完整QAT调优流程和踩的坑记下来。
一、RT-DETR模型结构简析
RT-DETR主要分三部分:
1. Backbone:ResNet50/101,提取多尺度特征
2. Encoder:Transformer encoder,做特征增强和目标查询初始化
3. Decoder:Transformer decoder,用交叉注意力做目标检测
征程6部署的难点在encoder和decoder:
· Encoder的自注意力有Softmax+Q×K^T,数值范围大,int8量化容易截断
· Decoder的交叉注意力有动态索引(根据参考点采样特征),导出ONNX时容易产生ScatterND
二、QAT训练流程
Step 1:准备量化配置
```python
from horizon_plugin_pytorch.qconfig import qconfig
# 默认int8配置,敏感层用int16
qat_config = qconfig(
activation=qconfig.activation.int8,
weight=qconfig.weight.int8,
fallback_to_int16_for_sensitive_layers=True
)
model.qconfig = qat_config
```
Step 2:插入伪量化节点
```python
from horizon_plugin_pytorch.quantization import prepare_qat
model_qat = prepare_qat(model, inplace=False)
```
Step 3:QAT训练
QAT训练的关键参数:
· 学习率:比浮点训练低10倍(如浮点用1e-4,QAT用1e-5)
· epoch数:通常需要50-100个epoch,比浮点训练多一倍
· 批量大小:建议保持和浮点训练一致,不要改小
Step 4:导出ONNX
```python
torch.onnx.export(model_qat, dummy, 'rtdetr_qat.onnx',
opset_version=11,
input_names=['images'],
output_names=['boxes', 'scores', 'labels'],
do_constant_folding=True,
)
```
三、精度调优策略
策略1:分层量化精度
不是所有层都用int8。根据官方QAT调优原则,精度敏感层用int16:
层类型 推荐量化 理由
Backbone Conv int8 CNN对量化鲁棒
Encoder Self-Attn int16 Softmax动态范围大
Encoder FFN int8 FFN主要是Linear+ReLU
Decoder Cross-Attn int16 交叉注意力有动态索引
Decoder FFN int8 同上
输出头 int16 检测框坐标对精度敏感
策略2:校准数据集选择
RT-DETR的decoder有参考点(reference points)机制,校准数据集必须覆盖不同尺度的目标。建议:
· 小目标(
· 中目标(32×32 ~ 96×96):占50%
· 大目标(>96×96):占20%
如果校准集里缺少小目标,量化后小目标检测精度会显著下降。
策略3:固定激活scale
当量化精度/浮点精度 >= 95%时,可以尝试固定校准激活scale的QAT训练:
```python
# 固定scale,减少训练波动
for name, module in model_qat.named_modules():
if hasattr(module, 'activation_post_process'):
module.activation_post_process.fixed_scale = True
```
固定scale后QAT训练的稳定性提升,最终精度通常能再涨1-2%。
四、踩坑记录
· 坑1:RT-DETR的decoder里有torch.gather操作,用于根据参考点索引采样特征。这个操作在ONNX导出时变成Gather算子,某些版本的hb_mapper不支持。解决:把gather改成index_select+concat的组合,或升级到OE 3.5.0+。
· 坑2:Encoder的Softmax在int8量化后,概率分布出现"峰值化"(某些位置的概率被截断到0或1)。解决:Softmax层用int16,或用log-softmax替代。
· 坑3:QAT训练时如果伪量化节点的scale学习率太高,模型训练到后期精度会突然崩掉。解决:把量化参数的learning rate设为weight lr的0.1倍。
· 坑4:RT-DETR的输出是(cx, cy, w, h)格式的检测框,量化后w和h的相对误差比cx/cy大(因为w/h的绝对值通常更大)。解决:输出头用int16,或在后处理时做尺度补偿。
五、性能表现
在J6M上,RT-DETR-L(backbone ResNet50)的部署性能:
· 延迟:约10-15ms(batch=1,int8+int16混合量化)
· 帧率:65-100fps(取决于输入分辨率)
· mAP:COCO val上约52-54%(int8 QAT,比浮点掉2-3%)
作为对比,YOLOv5x在J6M上的延迟约8-10ms,mAP约50-51%。RT-DETR-L精度更高但延迟也略高,选型时根据精度要求权衡。
六、顺便提一嘴
· RT-DETR的encoder比decoder计算量大,优化时优先压榨encoder(通道对齐、算子融合、内存复用)。
· 如果项目对实时性要求极高(>100fps),可以考虑RT-DETR-S(小版本),backbone换成ResNet34,延迟降到5-7ms。
· RT-DETR的QAT训练比YOLOv5慢很多,一方面是Transformer收敛慢,另一方面是伪量化节点增加了计算开销。建议用多卡训练,或把QAT训练拆成两阶段(先浮点训练收敛,再插入伪量化微调)。
数据来源:本文RT-DETR部署及QAT调优参考地平线开发者社区官方博客《【地平线J6工具链进阶教程】J6E/M工具链QAT精度调优-v1.1》及《RT-DETR QAT调优完整案例》。
