🚗 HorizonDrive:让自动驾驶模拟"不翻车"的炼金术
核心洞见: 生成式模型的"老师"并不天然适合做自回归老师——这个弱点被长期忽视了,而 HorizonDrive 就是为解决这个弱点而生的。
📌 一句话总结
先让老师学会在自己的预测里稳定生存,再把老师的这种稳定性蒸馏进学生。
一、引言——暴露偏差:错误滚雪球的噩梦
在自动驾驶仿真器中,AI 需要自回归地"预测未来":看着当前画面,生成下一帧,再用生成的画面继续往下滚。但这里埋着一个定时炸弹——暴露偏差(Exposure Bias)。
训练时,模型看的是真实路况视频;部署时,它必须把自己上一步生成的(不完美的)画面当作历史继续往下推。第一帧小错,喂给第二帧;第二帧犯了更大的错,再喂给第三帧……像滚雪球一样,最终场景变成一片迷幻的像素泥浆。
这就是为什么现有的驾驶世界模型顶多撑几秒钟——离真正有用的"分钟级别"闭环仿真差得远。
二、相关工作——前人都是怎么"止损"的?
① 条件式视频生成用于自动驾驶(DriveDreamer、Panacea、Vista 等)
- 引入高精度地图、3D边界框、自车动作等控制信号
- 被限制在短片段(8-25帧),无法自回归滚出长序列
② 长视频生成
- 重新设计噪声调度、关键帧锚点、从损坏历史中恢复等
- 在训练时域之外依然会漂移,治标不治本
③ 扩散模型蒸馏加速
- 分布匹配蒸馏(DMD)把慢速"老师"压缩成快速"学生"
- 即便加速,在长时间自回归中错误依然累积
论文的洞见: 上面这些方法都只在学生身上下功夫,没人想到去修好老师!如果老师本身在自回归时就会"漂",那从老师那里学来的东西自然也是歪的。
三、预备知识——扩散模型和蒸馏的底层逻辑
扩散模型(Diffusion Model)——“降噪大师”。给一堆随机噪声,一步步把噪声雕刻成清晰的图像/视频。训练时用"流匹配"(Flow Matching)目标函数,让模型学会预测"噪声变成图像"的方向向量。
分布匹配蒸馏(DMD)——把多步扩散压缩成一步。让快速学生模型去追赶慢速老师的输出分布,通过反向传播梯度对齐,学生就能以极低的推理成本达到接近老师的质量。
四、方法——HorizonDrive 的三级炼金术 🔮
第一阶段:基础驾驶世界模型训练
从通用的文本生成视频(T2V)扩散Transformer出发,基础架构采用 Wan 2.1 1.3B。
注入多路控制信号(即解耦控制策略):
- 🗺️ 高精度地图 + 3D边界框:作为"空间布局令牌",通过零初始化投影层以加性布局Token方式叠加到视觉特征上 → 控制画面里"有什么、在哪儿"
- 🚗 自车动作(Δx, Δy, Δyaw):通过 AdaLN-Zero 门控注入 → 控制整个视野"怎么动"
训练时,每段视频分成历史帧(干净,噪声级别为0)和生成块(加噪监督),得到基础模型 G₀。
G₀的弱点: 它只见过干净的真实历史,从没见过自己生成的有误差历史。如只在晴天练过车的司机——遇到自己不熟悉的"预测帧"就发蒙了。
深入:控制信号注入详解
高精地图 + 3D边界框的注入流程(加性布局Token):
- 画布光栅化(Rasterization):将 3D 坐标数据投影/渲染成与原视频画面尺寸对应的 2D “控制图”
- 特征提取与分块(Patching & Projection):通过小型卷积网络将"控制图"压缩成和视觉特征一模一样的序列长度和维度
- 零初始化注入(Zero-init Addition):通过初始权重全为 0 的线性层后,与视觉 Token 进行元素级相加
自车动作的 AdaLN-Zero 门控注入:
AdaLN(自适应层归一化)的缩放参数 γ 和平移参数 β 不是固定的,而是根据条件(扩散步数 + 自车动作)实时计算。它的核心公式是:
$$x_{modulated} = (1 + \gamma) \cdot \text{LayerNorm}(x) + \beta$$
残差连接部分使用零门控 α:
$$x = x + \alpha \cdot \text{Attn/MLP}(x_{modulated})$$
为什么 Attention 和 MLP 层都要加门控?
- Attention 管"往哪走(Spatial)"——空间交互、Token Mixing
- MLP 管"变成啥样(Semantic)"——语义特征转换、Channel Mixing
- 只给 Attention 加门控会导致"形动而神不动":像素位移了,但光影、透视、运动模糊不跟着变
- 两层都加零门控形成双重保险:训练初始时整个 Block 是恒等映射,不破坏预训练模型
# AdaLN-Zero Block 核心伪代码
class AdaLNZeroBlock:
def __init__(self, hidden_size, cond_size):
self.attn = MultiheadAttention(hidden_size)
self.mlp = MLP(hidden_size)
self.norm1 = LayerNorm(hidden_size, affine=False)
self.norm2 = LayerNorm(hidden_size, affine=False)
# 核心:条件调节器,输出 6 个参数 (gamma1, beta1, alpha1, gamma2, beta2, alpha2)
self.adaLN_modulator = Sequential(SiLU(), Linear(cond_size, 6 * hidden_size))
# 零初始化!训练初期完全不起作用
zeros_init(self.adaLN_modulator[-1])
def forward(self, x, ego_action_and_time_cond):
(gamma1, beta1, alpha1, gamma2, beta2, alpha2) = self.adaLN_modulator(ego_action_and_time_cond).chunk(6)
# Attention 层:先归一化,再用自车动作缩放平移
x_norm1 = self.norm1(x)
x_mod1 = x_norm1 * (1 + gamma1) + beta1 # (1+γ) 技巧:γ初始为0,等价于普通LayerNorm
attn_out = self.attn(x_mod1)
x = x + alpha1 * attn_out # α初始为0,训练初期完全绕过
# MLP 层:同样的调制
x_norm2 = self.norm2(x)
x_mod2 = x_norm2 * (1 + gamma2) + beta2
mlp_out = self.mlp(x_mod2)
x = x + alpha2 * mlp_out
return x
第二阶段:SRR——给老师做"自回归健身" 💪
SRR(Scheduled Rollout Recovery,计划性回滚恢复) 是整篇论文最关键的创新。
核心思路: 让 G₀ 看自己犯错时的样子,然后学会"纠正"。
- 用 G₀ 跑一遍自回归滚动,产生充满累积误差的轨迹
- 训练时,把"被污染的预测帧"当作历史条件,但监督目标仍是真实的未来帧
- 模型学会:哪怕之前预测有误,也能从中恢复并正确预测未来
两个精妙的渐进式课程:
📌 局部"预测→真值"过渡(Local pred-to-GT transition)
在生成边界附近开一个混合窗口,用 $α_i$ 系数线性混合预测帧和真实帧:
$$z_{blended} = \alpha_i \cdot \hat{z}{pred} + (1 - \alpha_i) \cdot z{gt}$$
关键:这个混合发生在Latent特征层面,而非像素层面!
- 像素层面混合会产生"重影"(Ghosting Artifacts)——两辆车变成两辆半透明幽灵车
- Latent层面混合则是语义特征的自然过渡
窗口宽度 $w$ 动态调度:
- 训练初期 $w=0$:硬切换,强迫模型从最大误差中恢复
- 训练后期 $w$ 逐渐增大:平滑过渡,磨练精细修正能力
def local_pred_to_gt_transition(pred_latents, gt_latents, current_epoch, total_epochs, max_w=4):
# 步骤1:动态调度窗口大小 w
progress = current_epoch / total_epochs
w = int(progress * max_w) # 初期w=0,后期逐渐变大
blended_history = pred_latents.clone()
if w > 0:
start_idx = Seq_Len - w
# 生成从 1 线性递减到 0 的 alpha 序列
alphas = linspace(1 - 1/w, 0, w) # 例如w=4: [0.75, 0.5, 0.25, 0.0]
window_pred = pred_latents[:, start_idx:end_idx]
window_gt = gt_latents[:, start_idx:end_idx]
window_blended = alphas * window_pred + (1 - alphas) * window_gt
blended_history[:, start_idx:end_idx] = window_blended
return blended_history
为什么初期用硬切换不会崩? 监督信号(真实未来帧)始终是清晰的,模型有明确的学习方向。突变的边界只是让输入"更丑",但不会让"正确答案"消失。
📌 全局边界衰减采样(Global boundary-decay sampling)
从后往前安排训练——先练后期严重漂移下的恢复(最难),再逐渐聚焦到早期轻微误差的修正(最精细)。
两个维度都是从难到易的课程学习:全局课程从严重漂移到轻微漂移,局部过渡从硬切换到平滑过渡。
经过 SRR,G₀ 变成了 Gᵣₒₗₗ——一个能在自己的预测下保持稳定的"可自回归的老师"。
第三阶段:TRD——把老师的本事"压缩"进学生 🎓
TRD(Teacher Rollout DMD) 把 Gᵣₒₗₗ 的知识提炼进快速学生模型。
不对称设计:
- 老师:每次生成 KT=40 帧的长块,多步去噪,质量高但慢
- 学生:每次只生成 KS=10 帧的短块,仅 4 步去噪,飞速且适合实时交互
训练时,老师和学生同时做自回归。当学生积累了足够长度后,把这40帧整体和老师的40帧对齐——通过分布匹配梯度推动学生学习老师的长视野稳定性,且内存消耗始终固定。
噪声截断的 CFG 策略:
标准 DMD 里 CFG 在所有噪声级别都用,会导致视频"过饱和"。
论文提出延迟衰减 CFG:
- 训练早期(暖机阶段):所有噪声级别都开 CFG(τ_th = 1000),让学生在高噪声阶段快速建立条件感知
- 训练晚期(截断衰减阶段):τ_th 从 1000 逐渐衰减到 0。高噪声阶段(t > τ_th)CFG 关闭——条件感知已内化进权重;低噪声阶段(t ≤ τ_th)CFG 仍开启,维持细节一致性
| 策略 | FVD | 问题 |
|---|---|---|
| 全程开 CFG | 184.06 | 过饱和严重 |
| 全程不开 CFG | 110.81 | 条件感知建立慢 |
| 早期衰减 | 111.99 | 暖机没完成就关掉,和不用差不多 |
| 延迟衰减 | 92.99 | 最优,先喂醒再防吃撑 |
条件约束 ≠ CFG: 模型本身的条件输入始终存在,CFG 只是额外的"放大器"。关掉 CFG 是取消了放大器,条件信号本身一直都在。
标准 DMD 完整流程:
- 学生从随机噪声生成样本 x_fake
- 取真实数据样本 x_real
- 两者都加噪到同一噪声级别 τ
- 老师分别对两者打分(加 CFG 放大)→ s_real 和 s_fake
- 分布差距方向信号 = s_real - s_fake
- 梯度传回学生更新参数
- 反复迭代,直到学生生成分布和老师无法区分
五、实验结果
数据集: nuScenes(700条训练,150条验证)
与三大类方法对比:
| 方法 | FID↓ | FVD↓ |
|---|---|---|
| 通用长视频世界模型框架 | 30~49 | 218~580 |
| 同架构对比(Self-Forcing++) | 28.84 | 147.57 |
| HorizonDrive | 13.82 | 92.99 |
消融实验关键结论:
- 老师的稳定性远比学生的初始化更重要——蒸馏链的质量上限由老师决定
- 训练时自回归步数 N 越多越好——N=20 时 ARE 2.60°、DTW 3.27,远优于 N=1
- 延迟衰减 CFG 最优——先暖机建立条件感知,再逐步收窄防止过饱和
六、结论与局限
核心贡献: “先让老师学会在自己的预测里稳定生存,再把老师的稳定性蒸馏进学生。"——填上了长期被忽视的逻辑漏洞。
局限性: SRR 是离线做的——先用 G₀ 跑一遍产生轨迹,再用轨迹训练。这个过程是固定的,无法随着 Gᵣₒₗₗ 的改进动态更新。未来方向是在线 SRR——让世界模型在不断与自己交互的过程中,持续学习如何从自己的错误中恢复。
一篇把"发现问题→设计课程→系统验证"做得非常扎实的工程论文。核心洞察既简单又深刻。