ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【自动驾驶】检测中 Denoising Training (去噪训练) 怎么理解?

【自动驾驶】检测中 Denoising Training (去噪训练) 怎么理解?

检测中 Denoising Training (去噪训练) 怎么理解?

1. 什么是 Denoising?

Denoising Training(去噪训练)是 DETR 系列方法中引入的一种训练技巧,用于加速 Transformer 检测器的收敛。

1.1 核心思想

Denoising 的核心思想是:给模型一些"带噪声的 GT(Ground Truth)",让模型学习如何从这些噪声中恢复出正确的 GT。

这类似于:

  • 自监督学习:模型需要从损坏的输入中恢复原始信息
  • 数据增强:通过添加噪声增加训练样本的多样性
  • 正则化:防止模型过拟合

1.2 为什么需要 Denoising?

问题:DETR 系列方法训练收敛慢,需要很多轮训练才能达到好的效果。

原因:

  1. 二分匹配的不稳定性:在训练初期,模型预测很差,Hungarian matching 的结果不稳定
  2. 缺乏明确的监督信号:模型需要从随机初始化的 queries 学习,没有明确的起始点
  3. 正负样本不平衡:大部分 queries 匹配不到 GT,只有少数是正样本

解决方案:Denoising

  • 提供带噪声的 GT作为输入,模型知道这些 noisy anchors 应该回归到哪个 GT
  • 这给模型提供了明确的监督信号,加速收敛
  • 同时训练模型处理有噪声的输入,提高鲁棒性

2. 带噪声的 GT 是什么?

2.1 概念

带噪声的 GT(Noisy Ground Truth)是指:

  • 从真实的 GT boxes开始
  • 添加随机噪声(偏移、缩放等)
  • 得到带噪声的 anchor boxes
  • 这些 noisy anchors 的目标仍然是原始的 GT

2.2 具体操作

# 1. 从 GT boxes 开始gt_boxes=[batch,num_gt,10]# 真实的 GT boxes# 2. 添加随机噪声noise=torch.rand_like(gt_boxes)*2-1# [-1, 1]noise*=noise_scale# 例如: [2.0, 2.0, 2.0, 0.5, 0.5, ...]noisy_anchors=gt_boxes+noise# 3. 这些 noisy anchors 的目标仍然是原始的 GTtarget_boxes=gt_boxes# 目标不变target_labels=gt_labels# 标签不变

2.3 噪声的尺度

不同维度使用不同的噪声尺度:

  • 位置 (x, y, z):较大的噪声(如 2.0),因为位置变化范围大
  • 尺寸 (w, l, h):较小的噪声(如 0.5),因为尺寸变化范围小
  • 角度 (yaw):中等噪声
  • 速度 (vx, vy, vz):较小的噪声
# Sparse4D 中的配置dn_noise_scale=[2.0]*3+[0.5]*7# 位置 尺寸+角度+速度

3. Denoising 的具体操作流程

3.1 生成带噪声的 Anchors

defget_dn_anchors(cls_target,box_target,gt_instance_id=None):""" 生成带噪声的 anchors 用于 denoising training 输入: cls_target: [batch, num_gt] - GT 类别标签 box_target: [batch, num_gt, 10] - GT boxes gt_instance_id: [batch, num_gt] - GT 实例 ID(用于时序) 输出: dn_anchor: [batch, num_dn_groups * num_gt, 10] - 带噪声的 anchors dn_box_target: [batch, num_dn_groups * num_gt, 10] - 对应的 GT boxes dn_cls_target: [batch, num_dn_groups * num_gt] - 对应的 GT 标签 attn_mask: [num_dn, num_dn] - attention mask valid_mask: [batch, num_dn_groups * num_gt] - 有效掩码 dn_id_target: [batch, num_dn_groups * num_gt] - 实例 ID """# 1. 准备 GT(padding 到相同长度)max_dn_gt=max([len(x)forxincls_target])cls_target=pad_to_length(cls_target,max_dn_gt)box_target=pad_to_length(box_target,max_dn_gt)# 2. 复制 num_dn_groups 次(每组使用不同的噪声)ifnum_dn_groups>1:cls_target=cls_target.tile(num_dn_groups,1)box_target=box_target.tile(num_dn_groups,1,1)# 3. 生成随机噪声noise=torch.rand_like(box_target)*2-1# [-1, 1]noise*=dn_noise_scale# 应用噪声尺度dn_anchor=box_target+noise# 带噪声的 anchor# 4. (可选)添加负样本ifadd_neg_dn:noise_neg=torch.rand_like(box_target)+1# [1, 2]noise_neg*=random_sign()*dn_noise_scale dn_anchor=concat([dn_anchor,box_target+noise_neg],dim=1)# 5. 使用 Hungarian matching 匹配 noisy anchors 和 GT# (虽然我们知道对应关系,但使用 matching 可以处理边界情况)cost=compute_box_cost(dn_anchor,box_target)anchor_idx,gt_idx=hungarian_matching(cost)# 6. 分配目标dn_box_target[anchor_idx]=box_target[gt_idx]dn_cls_target[anchor_idx]=cls_target[gt_idx]# 7. 生成 attention mask(同一组内的 anchors 可以互相 attention)attn_mask=create_group_attention_mask(num_dn_groups,num_gt)returndn_anchor,dn_box_target,dn_cls_target,attn_mask,valid_mask,dn_id_target

3.2 在模型中使用

# 在 forward 中defforward(self,feature_maps,metas):# 1. 获取正常的 learnable instancesinstance_feature,anchor=self.instance_bank.get(...)# 2. 生成带噪声的 anchors(仅在训练时)ifself.training:dn_metas=self.sampler.get_dn_anchors(metas["gt_labels_3d"],metas["gt_bboxes_3d"],gt_instance_id)# 3. 拼接 learnable instances 和 noisy instancesanchor=concat([anchor,dn_anchor],dim=1)instance_feature=concat([instance_feature,dn_feature],dim=1)# 4. 设置 attention mask# - learnable instances 之间可以 attention# - noisy instances 在同一组内可以 attention# - 不同组之间不能 attention# 5. 通过 decoderforlayerindecoder_layers:instance_feature=layer(instance_feature,anchor,...)# 6. 分离预测结果prediction=model_output[:,:num_learnable]# 正常预测dn_prediction=model_output[:,num_learnable:]# denoising 预测return{"prediction":prediction,"dn_prediction":dn_prediction,"dn_reg_target":dn_box_target,"dn_cls_target":dn_cls_target,...}

3.3 计算 Loss

defloss(self,model_outs,data):# 1. 正常预测的 lossnormal_loss=compute_loss(prediction,reg_target,cls_target)# 2. Denoising 的 lossdn_loss=compute_loss(dn_prediction,dn_reg_target,# 原始的 GT boxesdn_cls_target,# 原始的 GT 标签weight=dn_loss_weight# 通常权重较大,如 5.0)total_loss=normal_loss+dn_lossreturntotal_loss

4. Sparse4D 中的 Denoising

4.1 配置参数

在sparse4dv3_temporal_r50_1x8_bs6_256x704.py中:

sampler=dict(type="SparseBox3DTarget",num_dn_groups=5,# Denoising 组数num_temp_dn_groups=2,# 时序 denoising 组数dn_noise_scale=[2.0]*3+[0.5]*7,# 噪声尺度max_dn_gt=32,# 最多使用 32 个 GTadd_neg_dn=True,# 是否添加负样本)

4.2 关键参数说明

参数说明默认值
num_dn_groupsDenoising 组数,每组使用不同的噪声5
num_temp_dn_groups时序 denoising 组数(用于时序融合)2
dn_noise_scale噪声尺度,不同维度使用不同尺度[2.0]*3 + [0.5]*7
max_dn_gt最多使用的 GT 数量(限制计算量)32
add_neg_dn是否添加负样本(远离 GT 的 noisy anchors)True

4.3 工作流程

1. 输入 GT └─> gt_boxes: [batch, num_gt, 10] └─> gt_labels: [batch, num_gt] 2. 生成带噪声的 Anchors └─> 复制 num_dn_groups 次 └─> 每组添加不同的随机噪声 └─> dn_anchor: [batch, num_dn_groups * num_gt, 10] 3. 匹配和分配目标 └─> 使用 Hungarian matching └─> 分配对应的 GT boxes 和 labels 4. 拼接到模型输入 └─> learnable instances: [batch, 900, ...] └─> noisy instances: [batch, num_dn_groups * num_gt, ...] └─> 总输入: [batch, 900 + num_dn_groups * num_gt, ...] 5. 通过 Decoder └─> 所有 instances 一起处理 └─> 使用 attention mask 控制交互 6. 分离预测结果 └─> normal_prediction: [batch, 900, ...] └─> dn_prediction: [batch, num_dn_groups * num_gt, ...] 7. 计算 Loss └─> normal_loss: 正常预测的 loss └─> dn_loss: denoising 的 loss (权重 5.0)

4.4 时序 Denoising

Sparse4D 还支持时序 denoising,用于时序融合:

# 在单帧 decoder 后iflen(prediction)==num_single_frame_decoder:# 缓存当前帧的 denoising instancesself.sampler.cache_dn(dn_instance_feature,dn_anchor,dn_cls_target,valid_mask,dn_id_target)# 在时序 decoder 中# 使用缓存的时序 denoising instances# 与当前帧的 denoising instances 进行匹配和融合

5. Attention Mask

Denoising 使用特殊的 attention mask 来控制不同 instances 之间的交互:

# Attention Mask 结构attn_mask=[# Learnable instances (900 个)[0,0,...,1,1,...],# 可以互相 attention[0,0,...,1,1,...],...# Noisy instances (num_dn_groups * num_gt 个)[1,1,...,0,0,...],# 同一组内可以 attention[1,1,...,0,0,...],...]# 规则:# - Learnable instances 之间:可以 attention (mask=0)# - Noisy instances 同一组内:可以 attention (mask=0)# - Noisy instances 不同组间:不能 attention (mask=1)# - Learnable 和 Noisy 之间:不能 attention (mask=1)

6. 为什么 Denoising 有效?

6.1 提供明确的监督信号

  • 正常训练:模型需要从随机初始化的 queries 学习,初期匹配不稳定
  • Denoising:模型知道 noisy anchors 应该回归到哪个 GT,提供明确的监督

6.2 增加正样本数量

  • 正常训练:只有匹配到的 queries 是正样本,数量少
  • Denoising:所有 noisy anchors 都有对应的 GT,都是正样本

6.3 提高鲁棒性

  • 模型学习从有噪声的输入中恢复正确的输出
  • 提高模型对不完美输入的鲁棒性

6.4 加速收敛

  • 实验表明,使用 denoising 可以将训练轮数减少50%以上
  • 同时提高最终的性能

7. 代码示例

7.1 生成带噪声的 Anchors

# 在 SparseBox3DTarget.get_dn_anchors 中# 1. 准备 GTbox_target=encode_reg_target(gt_boxes)# [batch, num_gt, 10]# 2. 复制 num_dn_groups 次ifnum_dn_groups>1:box_target=box_target.tile(num_dn_groups,1,1)# [batch, num_dn_groups * num_gt, 10]# 3. 生成噪声noise=torch.rand_like(box_target)*2-1# [-1, 1]noise*=torch.tensor([2.0,2.0,2.0,0.5,0.5,0.5,0.5,0.5,0.5,0.5])dn_anchor=box_target+noise# 4. 匹配和分配cost=compute_box_cost(dn_anchor,box_target)anchor_idx,gt_idx=hungarian_matching(cost)dn_box_target[anchor_idx]=box_target[gt_idx]

7.2 在 Forward 中使用

# 在 Sparse4DHead.forward 中# 1. 获取正常 instancesinstance_feature,anchor=self.instance_bank.get(...)# 2. 生成 denoising instancesifself.training:dn_metas=self.sampler.get_dn_anchors(metas["gt_labels_3d"],metas["gt_bboxes_3d"])dn_anchor,dn_box_target,dn_cls_target,attn_mask,valid_mask,dn_id_target=dn_metas# 3. 拼接anchor=torch.cat([anchor,dn_anchor],dim=1)instance_feature=torch.cat([instance_feature,torch.zeros_like(instance_feature[:,:dn_anchor.shape[1]])],dim=1)# 4. 设置 attention mask# attn_mask 已经设置好

7.3 计算 Loss

# 在 Sparse4DHead.loss 中# 1. 正常预测的 lossnormal_loss=compute_loss(prediction,reg_target,cls_target)# 2. Denoising 的 lossif"dn_prediction"inmodel_outs:dn_loss=compute_loss(model_outs["dn_prediction"],model_outs["dn_reg_target"],model_outs["dn_cls_target"],weight=self.dn_loss_weight# 5.0)total_loss=normal_loss+dn_loss

8. 总结

Denoising Training 的核心:

  1. 生成带噪声的 GT:从真实 GT 添加随机噪声
  2. 明确的监督信号:这些 noisy anchors 的目标是原始 GT
  3. 增加正样本:所有 noisy anchors 都是正样本
  4. 加速收敛:提供更好的训练信号

关键点:

  • 不是真正的"去噪",而是"从噪声中恢复"
  • 噪声尺度需要仔细设计(位置大,尺寸小)
  • 使用 attention mask 控制不同 instances 的交互
  • 可以显著加速训练并提高性能

类比理解:

  • 就像给模型一些"模糊的答案"(带噪声的 GT)
  • 让模型学习如何"修正"这些模糊答案,得到正确答案
  • 这样模型既能学习检测,又能处理不完美的输入
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进