Personal Knowledge Base

A long-term research and learning notebook for posts, notes, papers, projects, and research directions.

Skip to content
Back to RiskAffordance overview

阶段③:以 H′ 为条件的占用扩散生成

前两个阶段产出的是 H′——"如果发生某类事故,危险分布长什么样"。但 H′ 本身不是最终交付物。阶段③要回答:给定过去 5 帧占用 + 激活后的危险场 H′,生成未来 5 帧的三维占用会怎么演化——而且要能生成 K=3~4 种不同的可能(多模态)。

76 min read

目的:从"危险场"到"未来的多种可能"

前两个阶段产出的是 H′——"如果发生某类事故,危险分布长什么样"。但 H′ 本身不是最终交付物。阶段③要回答:给定过去 5 帧占用 + 激活后的危险场 H′,生成未来 5 帧的三维占用会怎么演化——而且要能生成 K=3~4 种不同的可能(多模态)

为什么用扩散模型而不是普通回归网络?因为未来是一对多的:同一句"左侧有车切入",车可以快点切、慢点切、贴近切。回归网络(MSE 训练)在一对多数据上只能学到"各种可能的平均"——平均出来的未来谁都不像,这是数学必然(后面 C0 试金石会实地演示这个失败模式)。扩散模型是生成模型,每次采样从不同的随机噪声出发,能走出不同的未来——多模态是它的本能。

扩散模型五分钟扫盲

前向过程(训练时):拿一张干净的未来占用图 x₀,逐步加噪声,T=1000 步后变成纯噪声:

x₀(干净的未来)→ 加一点噪声 → x₁ → 再加 → ... → x₁₀₀₀(纯噪声)

反向过程(生成时):训练一个 U-Net 学会"从噪声里把干净图还原出来",生成时从纯噪声出发,一步步去噪:

纯噪声 →(U-Net 预测干净图)→ 稍干净 → 再预测 → ... → x₀(生成的未来)
   ↑ 每一步都看着条件(历史占用 + H′)来预测——条件就是这样注入的

三个后面会反复出现的技术名词:

  • x0 参数化 vs eps 参数化:U-Net 每一步可以直接预测"干净图 x₀"(x0 参数化),也可以预测"当前噪声里的噪声成分 ε"(eps 参数化)。数学上等价,但优化行为不同——这个差异后来在 litmus 上制造了一个大坑(0.5 灰场吸引子,下面讲);
  • DDIM 采样:DDPM 要去噪 1000 步太慢,DDIM 是它的加速版(eta=0 时确定性采样,50 步完成)——本项目采样统一用 DDIM 50 步;
  • 教师强制(teacher forcing)/ oracle:评估时的一种作弊模式——每一步去噪时不喂模型自己的上一步输出,而是喂"加了正确噪声的 GT"。它能分离一个关键问题:模型单步预测准不准(oracle)vs 连走 50 步会不会跑偏(free 自由轨迹)。这把手术刀后面救了整个诊断链。

架构设计:2D U-Net + 通道折叠

输入输出的账本:

生成目标 x₀:未来 5 帧占用 = (5帧, 2通道, 200×200, 20层)
条件:o_past(过去 5 帧占用)+ H′(o′/ρ′/v′ 激活场)

关键设计决策:不直接上 3D U-Net,而是把 T×C×Z 折叠进通道维度,在 BEV 平面上用 2D U-Net:

x₀ fold:5×2×20 = 200 通道,空间维度只留 200×200(BEV 鸟瞰)
条件 fold:o_past 200 通道 + H′ 100 通道(o′20 + ρ′20 + v′60,v 的 3 个分量全保留)
输入总计 500 通道 → 2D U-Net(47M 参数)→ 输出 200 通道 → 展开回 (5,2,200,200,20)

为什么折叠?算力现实:3D 卷积在 200×200×20 上做 1000 步扩散,显存和时间都不可行;而驾驶场景的空间结构主要在水平面,Z 轴 20 层折叠成通道损失可控。条件注入走最朴素的 concat(与扩散 U-Net 输入拼接)——因为扩散 U-Net 本身可训练(阶段③训练的只有它,CLIP/先验/阶段②全冻结),concat 的"需要后续层消化"前提在这里成立。

数据与目标:反事实"幻影"设计

阶段③的训练目标不是普通的"预测下一帧",而是反事实生成:场景里本来没有事故,我们要求模型生成"如果发生事故"的未来。做法叫 phantom_primary

在 o_future(未来真值)里按规则注入一个"幻影"(phantom)——
一辆按运动学规则运动、最终与 ego 相撞的虚拟车辆轨迹

为什么不编辑真实车辆(edit 档)?锚点-agent 匹配率探针一锤定音:H′ 的锚点位置与真实 agent 的匹配率只有 17.3%——事故语义锚点处往往根本没有真实车辆可编辑。所以主档 = 凭空生成幻影。

cut-in 类的幻影规则(rule-v2)是打出来的,两个根因和修复:

  • v0=0 零初速 → 幻影不动,变成 ego 自己撞静止靶(ego_driven 占 87%)→ 修复:v0=approach 初速 + intercept 瞄准 ego 未来位置;
  • "起点=锚点"几何死结 → cut-in 锚点横向 ±1m,落在 ego 车身足迹内(车长 4.6m),幻影从那里出发必然首帧即碰撞(首帧停止 89%)→ 修复:锚点语义修正为"风险落点"(碰撞在哪发生)而非"生成点",幻影起点从锚点后撤。

修复后正式全量:首帧停止 0.5%/0.2%、phantom_driven 94%/97.3%,全闸通过。

评估指标体系

指标含义
free IoU自由轨迹:模型自己连走 50 步 DDIM,最终占用与 GT 的交并比——真实能力
oracle IoU教师强制轨迹的 IoU——单步能力上限,分离"单步不会"vs"轨迹跑偏"
copy 基线"直接复制最后一帧过去"当预测——未来的强平凡基线(正常驾驶语境 0.1345,反事实语境重算 0.188/0.213)。跑赢它才算学会了东西
承诺(commitment)/ p̂生成结果里"敢下结论"的体素(p̂ > τ_commit=0.5)——测模型是否输出了确定的结构而非 0.5 灰场
swap SNR把左臂条件换成右臂,输出变化有多大——可控性的直接度量

攻关史(上):扩散力学 —— "单步会、轨迹废"

C0 无条件试金石:无条件生成 2 个样本,IoU≈0,看起来惨败——但诊断发现这是预期失败:无条件+MSE 的数学最优解就是"模态叠加平均",阈值化后两不靠。教训:试金石的形态必须对齐真实使用形态(条件生成),无条件 IoU 闸作废。

C1~C5 排除链(每一步排除一类嫌疑):

  • C1 预算:2000 步不够?→ 不是预算问题;
  • C2 学习率:lr 有单调改善但暴露诡异现象——loss 降了 95.97% 而 IoU 只有 0.0158(损失与轨迹脱钩首证);
  • C3 稀疏塌缩:L1 把质量比救回 0.555 但 IoU 反降——"量对"≠"位置对",塌缩证伪;
  • C4 采样解剖:oracle IoU 0.8614!单步预测很好、schedule 系数零误差、采样器清白——矛头只剩"自由轨迹";
  • C5 定案:exposure bias + 晚期训练不稳定——训练时模型总见"正确的上一步",生成时只能见"自己的上一步",误差逐步累积;且 6000 步后过拟合退化(峰值@6000 free 0.2559 → 终态@10000 掉到 0.10)。

解法:资格掩码峰值选模(oracle≥0.8 才配进候选,argmax 选峰)+ trainer 加固(grad clip / lr decay / 漂移监测)。B0/B1 双臂过闸,3-2 关闭,力学验证完毕。

攻关史(中):可控性试金石 —— "说了左边,它听吗"

这是阶段③的灵魂实验:同一场景,只把条件从 cut_in_left 换成 cut_in_right,生成的幻影应该换边。16 场景 × 左右双臂。

  • r1 前置闸翻车:双臂条件差异度量(质心分离度)只有 0.06,远低于闸门——因为 ±1m 的锚点差被 ~20m 的高斯核展宽淹没。但诊断发现可分信息其实在 v′ 通道(v̄ 夹角 76°~109°)——改前置闸判据后 16/16 过闸。这直接实证了设计决策 5:"v 是 bbox 表达不了的信息"。
  • r2~r5 深坑:free IoU 0.059 全军覆没,且出现灵异现象——五组独立实验的 IoU 逐位相同(0.059448)。排查链:先查缓存复用(P0 对拍,清白)→ 再发现真相:全场输出饱和 0.5 灰场,IoU 退化为目标面积比例(数据常数)。根因两层:① 条件信噪比 0.127~0.395 <1(条件太弱听不见);② eps 参数化的数学吸引子——eps=0 稳态恰好对应 0.5 灰场,"不作为"成了动力学上的舒适区。
  • r6~r7 修复:换 x0 参数化(拆除吸引子,p̂ 0.5→0.07)+ 条件尺度 adapter(SNR 0.26→6.1)→ 从"听不见"变成"听见了不敢写"。
  • C1 判乙 + mask-weighted MSE(突破口):教师强制逐 t 桶探针判定瓶颈是稀疏场 MSE 对幅度的压制(阶段① ρ 塌缩的同构病第三次复发)。先解析验证(w_pos=20 时理论最优幅度提升 18 倍)再授权训练——free IoU 0.869 ≈ oracle 0.874,首次闭合。
  • 终审:46 个承诺体素集中在 3/16 场景,统计不够 → 扩到 128 个零重叠独立场景的 256 个 L/R 配对样本,预注册判定线(先写死规则再跑):McNemar mask-only=9 / B1-only=0,p=0.0039 达标,swap SNR 126.3 vs 基线 0.22——可控性成立,HOLD 解除。同时登记诚实口径:泛化分片 free IoU 0.187≈queue 基线,可控性 claim 的证据是承诺分离 + swap SNR,不是 IoU 压制——两条线分开陈述,防过 claim。

攻关史(下):full-v2 诊断与 provenance 封存闸(当前战线)

可控性过了,进入正式 verdict 评审,877 个验证样本全量诊断,预注册四分支全部落判坏消息:

A. degradation:raw free IoU 0.093 < 同语境 copy 基线 0.213(全量上打不过复制基线)
B. overcommitment:承诺占比是 GT 的 3.276 倍(过度承诺,到处乱亮)
C. midcourse_drift:扩散轨迹 IoU 从 t999 的 0.238 单调劣化到 t0 的 0.093(越生成越偏)
D. selection bias:类别间选择偏差 TV 0.124(对不同类型偏心眼)

要归因,先审计——结果审计掀了桌:v2 数据的 sample_token 指向 frame 9(target),v3 数据指向 frame 4(condition),389 条样本的"身份"对不上;且 v2/v3 的参考系变换证据、p-hat 的逐样本 sidecar 全部缺失。于是落下归因封存闸(attribution gate):

389 条身份全部判 unresolved(注意:不判 different——"证明不同"和"无法证明相同"是两回事)
→ 禁止任何全量归因结论
→ w_pos 悬挂:禁止一切 w_pos/precision 侧修改(怕在没归因清楚时乱动损失)

三线收口(已完成):

  1. closure:389 条不可变 sidecar 落成,身份三态 0 proven / 266 different / 123 unresolved;
  2. 归因考古:266 条"different"被证明是假阳性——时间戳闸门用刚性 1ms 容差,而 nuScenes 采样有 ~125ms 物理抖动(jitter),窗口本身零位移。教训:身份校验以 token/窗口位移为主证据,时间戳只做 jitter-aware 次级校验(容差 250ms)。p-hat 旧产物零元数据,永久封存,事后考古补救不了;
  3. v3_regen(provenance-by-construction):唯一出路是重生成时原生落盘证据——389/389 窗口以 v2 身份锚定、九门 sidecar 全字段非空、运行时 hash 实测自验、生成规则与旧版 389/389 逐字节一致(证明语义零变更)。

当前位置:

下一步 = p-hat GPU 重采样(冻结 checkpoint 仅推理)→ closure 复跑 → commit
→ v3_regen 的 389 条合法样本重跑 full-v2 诊断
→ 归因坐实 → w_pos 解封评估 → 可控性 verdict 签发 → 3-3 全量训练

阶段③小结

阶段③ = 用 2D U-Net 扩散(通道折叠、x0 参数化、DDIM 50 步)以"过去 5 帧 + H′"为条件生成多模态未来占用;它继承了前两阶段的全部方法论并贡献了新的硬核经验——试金石形态要对齐使用形态、oracle/free 双轨是诊断手术刀、eps 参数化的 0.5 吸引子、稀疏场 MSE 压制跨阶段复发、"身份证据不闭合就不许归因"的封存闸制度。它目前是全项目唯一未关闭的阶段,战线停在 provenance 闭合的最后一公里。