自动驾驶-第四阶段 4.1-VLA—当前最前沿方向强化学习微调——超越模仿学习

从“学人类开车”到“比人类开得更好”。

在前边的课程中,我们用 VLA 赋予了模型“思考”的能力。但有一个更深层的问题:无论是第 3 章的 ResNet、第 5 章的 VAD、第 6 章的 DiffusionDrive 还是第 7 章的 VLA,它们都在做同一件事——模仿人类驾驶数据。而模仿学习有一个无法突破的天花板:模型最多和人类一样好,不能更好。
本章将引入强化学习(Reinforcement Learning)来突破这个天花板。具体用的是 DeepSeek 提出的 GRPO(Group Relative Policy Optimization)——它比传统的 PPO 更简单、更高效。

模仿学习的天花板在哪

file

上图下半部分清晰地展示了 SFT 和 GRPO 的核心区别:
模仿学习(SFT)的问题:人类数据包含 70% 正常驾驶 + 20% 犹豫不决 + 10% 危险操作。模型学到的是一个“平均”策略,包含犹豫和危险的成分。天花板:模型最多和人类一样好,不能更好。

强化学习(GRPO)的突破:不再模仿人类数据,而是让模型自己探索。生成多条轨迹,用奖励函数评分,强化好的、抑制差的。结果:模型可以学到比人类更优的策略。

💡 类比理解

模仿学习就像学生照抄老师的解题步骤——学生最多和老师一样好。强化学习就像学生自己做试卷,对答案看分数,慢慢探索出比老师更高效的解法。这正是 AlphaGo 超越所有人类围棋手的原理——它不是模仿人类下棋,而是通过自我对弈发现了人类从未想过的棋路。

对比项 SFT (模仿学习) GRPO (强化学习)
训练信号 人类驾驶数据 (GT 轨迹) 奖励函数 (碰撞/进度/舒适度)
学习方式 模仿专家行为 自己探索 + 奖励反馈
性能上限 最多和人类一样好 可以超越人类
需要的数据 大量标注数据 只需要奖励函数
代表方法 VAD/SparseDrive/VLA SFT GRPO/PPO/RLHF
行业案例 特斯拉 FSD V12 特斯拉 FSD V14 + 理想 VLA

GRPO 原理:组内相对策略优化

什么是 GRPO?

GRPO(Group Relative Policy Optimization)是 DeepSeek 提出的强化学习方法。它比 OpenAI 用的 PPO 更简单高效,核心思想是:

  1. 对同一个场景,让模型生成 K 条候选轨迹(一个“组”)
  2. 用奖励函数给每条轨迹打分
  3. 在组内做相对排名(标准化)——第一名奖励最大,最后一名惩罚最大
  4. 用排名结果更新模型参数——增加高分轨迹的生成概率,降低低分轨迹的生成概率

概念解释:为什么叫“Group Relative”?

“Group”是因为它把同一场景下的 K 条轨迹当作一个“组”。“Relative”是因为它不看绝对分数,只看组内相对排名。这有一个巨大的优势:不需要单独训练一个 Critic 网络(PPO 需要),简化了训练流程。DeepSeek 正是用 GRPO 训练出了超越 GPT-4 的推理能力。

GRPO vs PPO

对比项 PPO (OpenAI) GRPO (DeepSeek)
是否需要 Critic 需要训练一个价值网络 不需要,用组内排名替代
基线 (baseline) 价值函数估计 组内均值
训练复杂度 高(两个网络同时训练) 低(只训练策略网络)
显存占用 大(Actor + Critic) 小(只有 Actor)
效果 经典有效 同样有效且更简单

驾驶奖励函数设计

奖励函数是强化学习的“灵魂”——它定义了“什么是好的驾驶”。设计不好,模型会学到奇怪的行为(比如“原地不动就不会碰撞”)。

def compute_driving_reward(trajectory, gt_traj, obstacles, lane_center):
    '''
    驾驶奖励函数: 评估一条轨迹的质量
    奖励 = 进度奖励 - 碰撞惩罚 - 偏离惩罚 - 不舒适惩罚
    '''
    reward = 0.0

    # 1. 进度奖励: 你到底往前开了多少?
    forward_progress = trajectory[-1, 0]  # 最后一个点的x坐标
    reward += forward_progress * 0.5

    # 2. 碰撞惩罚: 撞了就严重扣分
    for obs in obstacles:
        min_dist = torch.min(torch.norm(trajectory - obs['position'], dim=1))
        if min_dist < obs['radius']:
            reward -= 10.0  # 碰撞了!
        elif min_dist < obs['radius'] * 2:
            reward -= 2.0   # 太近了, 警告

    # 3. 偏离车道中心的惩罚
    lateral_dev = torch.mean(torch.abs(trajectory[:, 1] - lane_center))
    reward -= lateral_dev * 2.0

    # 4. 不舒适惩罚: 急转弯、急加速
    acceleration = torch.diff(trajectory, dim=0)
    jerk = torch.diff(acceleration, dim=0)
    comfort = torch.mean(torch.norm(jerk, dim=1))
    reward -= comfort * 0.5

    return reward

⚠️ 奖励函数设计的坑

最常见的坑是“奖励黑客”(Reward Hacking)——模型找到了你没想到的“作弊”方式来获取高分。比如:如果只有碰撞惩罚没有进度奖励,模型会学到“原地不动”;如果进度奖励太大,模型会“横冲直撞”不管障碍物。好的奖励函数需要平衡多个目标。

GRPO 训练流程实操

以下是完整的 GRPO 训练代码,基于第 7 章的 MiniVLA 模型:

# grpo_training.py

import torch
import torch.nn.functional as F

def grpo_step(model, scene_input, obstacles, lane_center,
              num_samples=8, temperature=1.0):
    '''
    GRPO 的一个训练步骤
    '''
    # Step 1: 生成 K 条候选轨迹
    trajectories = []
    log_probs = []
    for _ in range(num_samples):
        traj = model.sample(scene_input, temperature=temperature)
        trajectories.append(traj)
        lp = model.log_probability(scene_input, traj)
        log_probs.append(lp)

    trajectories = torch.stack(trajectories)   # (K, 6, 2)
    log_probs = torch.stack(log_probs)         # (K,)

    # Step 2: 计算每条轨迹的奖励
    rewards = torch.tensor([
        compute_driving_reward(traj, None, obstacles, lane_center)
        for traj in trajectories
    ])

    # Step 3: 组内标准化 (Group Relative 的核心!)
    rewards_norm = (rewards - rewards.mean()) / (rewards.std() + 1e-8)
    # 第一名得到正奖励, 最后一名得到负奖励

    # Step 4: 计算 GRPO loss
    loss = -(log_probs * rewards_norm).mean()
    # 直觉: 好的轨迹(+奖励) -> 增加概率
    #       差的轨迹(-奖励) -> 降低概率

    return loss, rewards.mean().item()

# ===== 完整训练循环 =====
model = MiniVLA(freeze_vlm=False).cuda()   # 这次不冻结VLM!
optimizer = torch.optim.Adam(model.parameters(), lr=1e-5)

for epoch in range(100):
    total_reward = 0
    for scene in dataloader:
        loss, avg_reward = grpo_step(
            model, scene['input'], scene['obstacles'],
            scene['lane_center'], num_samples=8)
        optimizer.zero_grad()
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
        optimizer.step()
        total_reward += avg_reward
    print(f'Epoch {epoch}: avg_reward={total_reward/len(dataloader):.3f}')

关键代码解读
第 3 步的 rewards_norm = (rewards - rewards.mean()) / rewards.std() 是 GRPO 的灵魂。它把绝对分数转换为相对排名——第一名的标准化奖励是正的(增加生成概率),最后一名是负的(降低生成概率)。这样就不需要单独训练 Critic 网络,比 PPO 简单得多。

SFT → GRPO 的完整 Pipeline

在实际应用中,GRPO 不是从零开始训练,而是在 SFT 的基础上微调。完整的三阶段 pipeline 是:

阶段 方法 目的 数据来源
阶段 1: 预训练 VLM 预训练 (Qwen2.5-VL) 学会“看图说话” 互联网图文数据
阶段 2: SFT CoT 监督微调 学会“看图开车” nuScenes 驾驶数据
阶段 3: GRPO 强化学习微调 学会“开得更好” 奖励函数反馈

三阶段之间的关系

  • 阶段 1 → 阶段 2:预训练提供了“看图”和“说话”的基础能力。SFT 在这个基础上让模型学会“看图开车”。如果跳过预训练直接做 SFT,模型连图都看不懂,更别说开车了。
  • 阶段 2 → 阶段 3:SFT 提供了一个“还行”的初始策略。GRPO 在这个基础上微调,让模型“更好”。如果跳过 SFT 直接做 GRPO,模型的初始策略太差,探索效率极低。

🎯 面试答题模板
“为什么不能直接用强化学习,要先做 SFT?”参考回答:“强化学习需要一个“还行”的初始策略作为起点。如果从随机策略开始探索,几乎所有采样都会得到极低分(比如立刻撞墙),模型根本学不到东西。SFT 提供了一个“能走直线但弯道差”的起点,GRPO 在这个基础上微调就很高效了。”

实际应用中的考量

  1. 安全约束:在自动驾驶中,强化学习的探索不能在真车上做(太危险)。所以需要在仿真器(CARLA/NAVSIM)或世界模型中做。这正是华为“世界引擎”的价值——提供安全的探索环境。
  2. 奖励函数调试:奖励函数的权重需要反复调试。建议先用小规模数据快速迭代,观察模型是否学到了奖励黑客行为,及时调整。
  3. KL 散度约束:GRPO 训练时通常加一个 KL 散度项,限制模型不能偏离 SFT 的策略太远,避免“死学”奖励函数的漏洞。

本章小结

本章是第三篇(前沿技术)的最后一章。你学到了:

  1. 模仿学习的天花板——最多和人类一样好,不能更好
  2. GRPO 原理——采样K条轨迹、奖励打分、组内排名、更新策略
  3. 奖励函数设计——进度+碰撞+偏离+舒适度的平衡
  4. GRPO 完整训练代码——基于 MiniVLA 的强化学习微调
  5. SFT → GRPO 三阶段 pipeline——预训练→监督微调→强化学习

最通俗、最直观、自动驾驶专属的方式,给你讲透:

奖励函数到底怎么控制模型,让好轨迹变多、坏轨迹变少?

我完全基于你这份课程内容来讲,不讲复杂公式,只讲直觉 + 原理 + 代码对应


先给你一句最核心的结论(一定要记住)

奖励函数不直接“改轨迹”,它告诉模型:哪条轨迹应该下次多生成,哪条应该少生成。

模型靠“概率”开车,奖励函数靠“调整概率”控制行为。


1. 先理解:你的模型是个“概率生成器”

在 GRPO 里,你的 MiniVLA 模型不是“计算轨迹”,而是:

模型 = 轨迹概率分布发生器

给模型一张图,它不会只输出一条轨迹,
它内部有一个概率分布

  • 轨迹A:概率 10%
  • 轨迹B:概率 25%
  • 轨迹C:概率 5%
  • 轨迹D:概率 60%

模型每次生成轨迹,都是按这个概率随机采样的。


2. GRPO 做的事:给同一帧画面生成 K 条轨迹

就像让司机凭感觉开 8 次

  1. 开得很稳 → 好
  2. 压线了 → 差
  3. 撞了 → 很差
  4. 完美贴车道中心 → 极好
    ...

这就是代码里的:

for _ in range(num_samples):
    traj = model.sample(scene_input)  # 生成一条轨迹
    log_probs.append(model.log_probability(traj))  # 记录这条轨迹的概率

3. 奖励函数做什么?

给每条轨迹打分,告诉模型:哪条好,哪条坏。

你课程里的奖励函数包含:

  • 往前开得越远 → 分越高
  • 离障碍物越近 → 分越低
  • 偏离车道中心 → 扣分
  • 颠簸、急转弯 → 扣分

最终分数 = 这条轨迹的“好坏程度”。


4. 最关键:GRPO 如何用分数调整概率?(核心机制)

一句话:

高分轨迹 → 下次生成概率 ↑

低分轨迹 → 下次生成概率 ↓

代码里的灵魂公式:

loss = -(log_probs * rewards_norm).mean()

这个公式的直觉解释


① 如果一条轨迹 奖励为正(好轨迹)

log_probs * 正数

→ 损失变小
→ 反向传播后
模型会提高这条轨迹的概率


② 如果一条轨迹 奖励为负(坏轨迹)

log_probs * 负数

→ 损失变大
→ 反向传播后
模型会降低这条轨迹的概率


训练的本质:

不断重复:生成 → 打分 → 调概率 → 生成 → 打分 → 调概率

最后模型就学会了:

  • 高概率输出:安全、居中、前进、舒适的轨迹
  • 低概率输出:碰撞、压线、不动、颠簸的轨迹

5. 为什么要“组内相对”(Group Relative)?

因为绝对分数没用,相对排名才有用

例子:

  • 轨迹1得分:10
  • 轨迹2得分:8
  • 轨迹3得分:-50

GRPO 不看绝对值,只看:

  • 第1名 → 奖励+
  • 第2名 → 奖励0
  • 第3名 → 奖励-

好处:模型只需要学“哪条更好”,不用管“好到什么程度”。
这就是 GRPO 比 PPO 简单的原因。


6. 用一个超级简单的比喻让你彻底懂

模型 = 射手

轨迹 = 子弹

奖励函数 = 靶纸

  • 射手(模型)随机打 8 枪(生成8条轨迹)
  • 靶纸(奖励函数)告诉你:哪枪中靶、哪枪脱靶
  • 教练(GRPO)告诉射手:
    • 中靶的动作,下次多用!
    • 脱靶的动作,下次少用!

射手不需要理解原理,只需要根据反馈调整动作概率。


7. 回到你的自动驾驶:最终效果是什么?

训练前:

  • 轨迹随机
  • 乱开、乱拐、碰撞、不动

训练后:

  • 安全轨迹概率最大
  • 居中轨迹概率最大
  • 前进轨迹概率最大
  • 舒适轨迹概率最大

模型不是被编程好的
而是被奖励函数引导成这样的


8. 最终总结(面试/理解都能用)

GRPO 训练的本质 = 调整轨迹生成概率

  1. 模型生成多条轨迹
  2. 奖励函数判断好坏
  3. 好轨迹 → 提高下次生成概率
  4. 坏轨迹 → 降低下次生成概率
  5. 越训练,模型生成好轨迹的概率越大

奖励函数不控制轨迹长什么样,它控制轨迹出现的概率!


如果你愿意,我可以用你课程里的 GRPO 代码
逐行给你讲:
loss = -(log_probs * rewards_norm).mean()
到底是怎么让“好轨迹概率上升、坏轨迹下降”的。

为者常成,行者常至