HorizonDrive: Self-Corrective Autoregressive World Model for Long-horizon Driving Simulation

🚗 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):

  1. 画布光栅化(Rasterization):将 3D 坐标数据投影/渲染成与原视频画面尺寸对应的 2D “控制图”
  2. 特征提取与分块(Patching & Projection):通过小型卷积网络将"控制图"压缩成和视觉特征一模一样的序列长度和维度
  3. 零初始化注入(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₀ 看自己犯错时的样子,然后学会"纠正"。

  1. 用 G₀ 跑一遍自回归滚动,产生充满累积误差的轨迹
  2. 训练时,把"被污染的预测帧"当作历史条件,但监督目标仍是真实的未来帧
  3. 模型学会:哪怕之前预测有误,也能从中恢复并正确预测未来

两个精妙的渐进式课程:

📌 局部"预测→真值"过渡(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 完整流程:

  1. 学生从随机噪声生成样本 x_fake
  2. 取真实数据样本 x_real
  3. 两者都加噪到同一噪声级别 τ
  4. 老师分别对两者打分(加 CFG 放大)→ s_real 和 s_fake
  5. 分布差距方向信号 = s_real - s_fake
  6. 梯度传回学生更新参数
  7. 反复迭代,直到学生生成分布和老师无法区分

五、实验结果

数据集: nuScenes(700条训练,150条验证)

与三大类方法对比:

方法 FID↓ FVD↓
通用长视频世界模型框架 30~49 218~580
同架构对比(Self-Forcing++) 28.84 147.57
HorizonDrive 13.82 92.99

消融实验关键结论:

  1. 老师的稳定性远比学生的初始化更重要——蒸馏链的质量上限由老师决定
  2. 训练时自回归步数 N 越多越好——N=20 时 ARE 2.60°、DTW 3.27,远优于 N=1
  3. 延迟衰减 CFG 最优——先暖机建立条件感知,再逐步收窄防止过饱和

六、结论与局限

核心贡献: “先让老师学会在自己的预测里稳定生存,再把老师的稳定性蒸馏进学生。"——填上了长期被忽视的逻辑漏洞。

局限性: SRR 是离线做的——先用 G₀ 跑一遍产生轨迹,再用轨迹训练。这个过程是固定的,无法随着 Gᵣₒₗₗ 的改进动态更新。未来方向是在线 SRR——让世界模型在不断与自己交互的过程中,持续学习如何从自己的错误中恢复。


一篇把"发现问题→设计课程→系统验证"做得非常扎实的工程论文。核心洞察既简单又深刻。