检测中 Denoising Training (去噪训练) 怎么理解1. 什么是 DenoisingDenoising Training去噪训练是 DETR 系列方法中引入的一种训练技巧用于加速 Transformer 检测器的收敛。1.1 核心思想Denoising 的核心思想是给模型一些带噪声的 GTGround Truth让模型学习如何从这些噪声中恢复出正确的 GT。这类似于自监督学习模型需要从损坏的输入中恢复原始信息数据增强通过添加噪声增加训练样本的多样性正则化防止模型过拟合1.2 为什么需要 Denoising问题DETR 系列方法训练收敛慢需要很多轮训练才能达到好的效果。原因二分匹配的不稳定性在训练初期模型预测很差Hungarian matching 的结果不稳定缺乏明确的监督信号模型需要从随机初始化的 queries 学习没有明确的起始点正负样本不平衡大部分 queries 匹配不到 GT只有少数是正样本解决方案Denoising提供带噪声的 GT作为输入模型知道这些 noisy anchors 应该回归到哪个 GT这给模型提供了明确的监督信号加速收敛同时训练模型处理有噪声的输入提高鲁棒性2. 带噪声的 GT 是什么2.1 概念带噪声的 GTNoisy Ground Truth是指从真实的 GT boxes开始添加随机噪声偏移、缩放等得到带噪声的 anchor boxes这些 noisy anchors 的目标仍然是原始的 GT2.2 具体操作# 1. 从 GT boxes 开始gt_boxes[batch,num_gt,10]# 真实的 GT boxes# 2. 添加随机噪声noisetorch.rand_like(gt_boxes)*2-1# [-1, 1]noise*noise_scale# 例如: [2.0, 2.0, 2.0, 0.5, 0.5, ...]noisy_anchorsgt_boxesnoise# 3. 这些 noisy anchors 的目标仍然是原始的 GTtarget_boxesgt_boxes# 目标不变target_labelsgt_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 生成带噪声的 Anchorsdefget_dn_anchors(cls_target,box_target,gt_instance_idNone): 生成带噪声的 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. 准备 GTpadding 到相同长度max_dn_gtmax([len(x)forxincls_target])cls_targetpad_to_length(cls_target,max_dn_gt)box_targetpad_to_length(box_target,max_dn_gt)# 2. 复制 num_dn_groups 次每组使用不同的噪声ifnum_dn_groups1:cls_targetcls_target.tile(num_dn_groups,1)box_targetbox_target.tile(num_dn_groups,1,1)# 3. 生成随机噪声noisetorch.rand_like(box_target)*2-1# [-1, 1]noise*dn_noise_scale# 应用噪声尺度dn_anchorbox_targetnoise# 带噪声的 anchor# 4. 可选添加负样本ifadd_neg_dn:noise_negtorch.rand_like(box_target)1# [1, 2]noise_neg*random_sign()*dn_noise_scale dn_anchorconcat([dn_anchor,box_targetnoise_neg],dim1)# 5. 使用 Hungarian matching 匹配 noisy anchors 和 GT# 虽然我们知道对应关系但使用 matching 可以处理边界情况costcompute_box_cost(dn_anchor,box_target)anchor_idx,gt_idxhungarian_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 可以互相 attentionattn_maskcreate_group_attention_mask(num_dn_groups,num_gt)returndn_anchor,dn_box_target,dn_cls_target,attn_mask,valid_mask,dn_id_target3.2 在模型中使用# 在 forward 中defforward(self,feature_maps,metas):# 1. 获取正常的 learnable instancesinstance_feature,anchorself.instance_bank.get(...)# 2. 生成带噪声的 anchors仅在训练时ifself.training:dn_metasself.sampler.get_dn_anchors(metas[gt_labels_3d],metas[gt_bboxes_3d],gt_instance_id)# 3. 拼接 learnable instances 和 noisy instancesanchorconcat([anchor,dn_anchor],dim1)instance_featureconcat([instance_feature,dn_feature],dim1)# 4. 设置 attention mask# - learnable instances 之间可以 attention# - noisy instances 在同一组内可以 attention# - 不同组之间不能 attention# 5. 通过 decoderforlayerindecoder_layers:instance_featurelayer(instance_feature,anchor,...)# 6. 分离预测结果predictionmodel_output[:,:num_learnable]# 正常预测dn_predictionmodel_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 计算 Lossdefloss(self,model_outs,data):# 1. 正常预测的 lossnormal_losscompute_loss(prediction,reg_target,cls_target)# 2. Denoising 的 lossdn_losscompute_loss(dn_prediction,dn_reg_target,# 原始的 GT boxesdn_cls_target,# 原始的 GT 标签weightdn_loss_weight# 通常权重较大如 5.0)total_lossnormal_lossdn_lossreturntotal_loss4. Sparse4D 中的 Denoising4.1 配置参数在sparse4dv3_temporal_r50_1x8_bs6_256x704.py中samplerdict(typeSparseBox3DTarget,num_dn_groups5,# Denoising 组数num_temp_dn_groups2,# 时序 denoising 组数dn_noise_scale[2.0]*3[0.5]*7,# 噪声尺度max_dn_gt32,# 最多使用 32 个 GTadd_neg_dnTrue,# 是否添加负样本)4.2 关键参数说明参数说明默认值num_dn_groupsDenoising 组数每组使用不同的噪声5num_temp_dn_groups时序 denoising 组数用于时序融合2dn_noise_scale噪声尺度不同维度使用不同尺度[2.0]*3 [0.5]*7max_dn_gt最多使用的 GT 数量限制计算量32add_neg_dn是否添加负样本远离 GT 的 noisy anchorsTrue4.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 时序 DenoisingSparse4D 还支持时序 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 MaskDenoising 使用特殊的 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 (mask0)# - Noisy instances 同一组内可以 attention (mask0)# - Noisy instances 不同组间不能 attention (mask1)# - Learnable 和 Noisy 之间不能 attention (mask1)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_targetencode_reg_target(gt_boxes)# [batch, num_gt, 10]# 2. 复制 num_dn_groups 次ifnum_dn_groups1:box_targetbox_target.tile(num_dn_groups,1,1)# [batch, num_dn_groups * num_gt, 10]# 3. 生成噪声noisetorch.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_anchorbox_targetnoise# 4. 匹配和分配costcompute_box_cost(dn_anchor,box_target)anchor_idx,gt_idxhungarian_matching(cost)dn_box_target[anchor_idx]box_target[gt_idx]7.2 在 Forward 中使用# 在 Sparse4DHead.forward 中# 1. 获取正常 instancesinstance_feature,anchorself.instance_bank.get(...)# 2. 生成 denoising instancesifself.training:dn_metasself.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_targetdn_metas# 3. 拼接anchortorch.cat([anchor,dn_anchor],dim1)instance_featuretorch.cat([instance_feature,torch.zeros_like(instance_feature[:,:dn_anchor.shape[1]])],dim1)# 4. 设置 attention mask# attn_mask 已经设置好7.3 计算 Loss# 在 Sparse4DHead.loss 中# 1. 正常预测的 lossnormal_losscompute_loss(prediction,reg_target,cls_target)# 2. Denoising 的 lossifdn_predictioninmodel_outs:dn_losscompute_loss(model_outs[dn_prediction],model_outs[dn_reg_target],model_outs[dn_cls_target],weightself.dn_loss_weight# 5.0)total_lossnormal_lossdn_loss8. 总结Denoising Training 的核心生成带噪声的 GT从真实 GT 添加随机噪声明确的监督信号这些 noisy anchors 的目标是原始 GT增加正样本所有 noisy anchors 都是正样本加速收敛提供更好的训练信号关键点不是真正的去噪而是从噪声中恢复噪声尺度需要仔细设计位置大尺寸小使用 attention mask 控制不同 instances 的交互可以显著加速训练并提高性能类比理解就像给模型一些模糊的答案带噪声的 GT让模型学习如何修正这些模糊答案得到正确答案这样模型既能学习检测又能处理不完美的输入