一个困惑
看端到端自动驾驶的论文时,你会发现同一个任务在不同的文章里用了完全不同的 Loss:
| 任务 | UniAD | VADv2 | DiffusionDrive |
|---|---|---|---|
| 轨迹规划 | L2 回归 + 碰撞 Loss | KL 散度(分布匹配) | 扩散去噪 Loss + 碰撞/边界/舒适 |
| 检测 | Focal Loss | Focal Loss | — |
| 占用预测 | 二值交叉熵 + Dice | — | — |
这还不是最让人晕的——同一篇文章内部,Loss 也在"打架":UniAD 的 Planner 用 L2 回归,但 MotionFormer 用 L1 回归;VADv2 的分布 Loss 对 4096 条轨迹做分类,而 SparseDrive 对锚点做分类 + 残差回归,而 DiffusionDrive 又完全不用任何分类或回归——它用一个扩散去噪 Loss 隐式地学分布。
这些 Loss 背后遵循什么设计逻辑?为什么从 UniAD 到 VADv2 到 DiffusionDrive,Loss 设计的思路一直在变?
答案其实很简单:Loss 的选择反映了你对"驾驶规划"这个问题的建模方式。你对问题的认知变了,Loss 就会变。这篇文章帮大家把每类 Loss 吃透。
一、损失函数的本质
在端到端自动驾驶中,损失函数的核心作用是把"什么样的驾驶是好的"这个模糊目标,翻译成神经网络能理解的数学信号。
用个比方:你教一个刚拿驾照的新手开车。
你(教练)说:
"刚才那个弯转得太急了,下次慢一点" ← 这是语言反馈
神经网络的 Loss 做的是类似的事:
L2回归 → "你的轨迹和我的轨迹差了2.3米,扣分"
碰撞 Loss → "你刚才离路沿只有10厘米了,扣分"
Focal Loss → "你第三次没认出停止标志了,扣大分"
每种 Loss 都在告诉模型"你哪里做得不够好"。但"不够好"的定义不同,Loss 的数学形式就不同。
二、Loss 是怎么让模型"学会"的?——梯度与反向传播
讲具体 Loss 之前,先搞明白一个更基础的问题:计算了 Loss 之后,模型到底是怎么更新参数的?
很多同学看论文只看 Loss 公式,然后跳过训练过程,结果对"为什么 Loss 长这样"一知半解。这一节用最白话的方式讲清楚。
2.1 先理解这个核心公式
整个深度学习的训练过程,可以用一句话概括:
新参数 = 旧参数 − 学习率 × 梯度
或者写成公式:
\[ \theta_{new} = \theta_{old} - \eta \cdot \frac{\partial \mathcal{L}}{\partial \theta} \]逐词翻译:
| 符号 | 含义 | 大白话 |
|---|---|---|
| \(\theta\) | 模型的参数 | 就是神经网络里所有的权重 W 和偏置 b |
| \(\mathcal{L}\) | Loss 值 | 模型预测得有多差 |
| \(\partial \mathcal{L} / \partial \theta\) | 梯度 | 参数变化一点点,Loss 会变多少 |
| \(\eta\) | 学习率 | 每次更新的步长 |
2.2 梯度是什么?一个例子就懂
假设你只有一个参数 w,Loss 是 w 的函数:ℒ = w²。
w = 3 时:ℒ = 9,梯度 = ∂ℒ/∂w = 2w = 6
解读:w 增加一点点(比如 0.1),Loss 会增加 6 × 0.1 = 0.6
→ Loss 在 w=3 处"往上走",需要往反方向走 → w 应该减小
w = -2 时:ℒ = 4,梯度 = 2w = -4
解读:w 增加一点点,Loss 会减少 4 × 0.1 = 0.4
→ Loss 在 w=-2 处"往下走",继续往这个方向走 → w 应该增加
梯度的方向 = Loss 上升最快的方向。所以参数更新时,我们要沿着梯度的反方向走——这就是"梯度下降"名字的由来。
梯度 > 0 → Loss 随参数增大而增大 → 参数应该减小
梯度 < 0 → Loss 随参数增大而减小 → 参数应该增大
2.3 链式法则:为什么叫"反向传播"?
神经网络有很多层,Loss 在最末端,参数在最前面。怎么把 Loss 信号传回前面的层?
答案是链式法则。
假设一个 3 层的玩具网络:
输入 x → 层1 (W₁) → h₁ → 层2 (W₂) → h₂ → 层3 (W₃) → ŷ → Loss
我们要算 Loss 对 W₁ 的梯度。W₁ 在最前面,Loss 在最后面,中间隔了:
- W₁ → h₁ → h₂ → ŷ → Loss
链式法则说:
\[ \frac{\partial \mathcal{L}}{\partial W_1} = \frac{\partial \mathcal{L}}{\partial \hat{y}} \times \frac{\partial \hat{y}}{\partial h_2} \times \frac{\partial h_2}{\partial h_1} \times \frac{\partial h_1}{\partial W_1} \]逐层相乘,从后往前传。
前向传播(蓝色)→: x → h₁ → h₂ → ŷ → Loss (逐层计算)
反向传播(红色)←: ∂ℒ/∂ŷ → ∂ℒ/∂h₂ → ∂ℒ/∂h₁ → ∂ℒ/∂W₁ (从后往前乘回去)
这就是"反向传播"名字的来源——梯度信号从 Loss 出发,沿着网络的反方向逐层传回去。
2.4 一个完整的训练步骤(Step-by-Step)
假设 UniAD 的 Planner 输出了一条轨迹 ŷ,GT 轨迹是 y。
第 1 步:前向传播(Forward)
→ 图像进 BEVFormer → 输出 BEV 特征 B
→ B 进 TrackFormer → 输出 agent query Q_A
→ Q_A 进 MotionFormer → 输出 motion feature
→ motion + B 进 Planner → 输出轨迹 ŷ
第 2 步:算 Loss
ℒ_plan = (ŷ₁ - y₁)² + (ŷ₂ - y₂)² + ... + (ŷ₁₂ - y₁₂)²
得到:ℒ = 2.34(一个具体的 Loss 值)
第 3 步:反向传播(Backward)
∂ℒ/∂W_planner = ? ← Planner 参数的梯度
∂ℒ/∂W_motion = ? ← MotionFormer 参数的梯度
∂ℒ/∂W_track = ? ← TrackFormer 参数的梯度
∂ℒ/∂W_backbone = ? ← BEVFormer 参数的梯度
这些梯度通过链式法则逐层算出来。
注意:UniAD 是端到端的,规划 Loss 的梯度可以一路回传到 backbone!
第 4 步:更新参数
W_planner_new = W_planner - 0.001 × ∂ℒ/∂W_planner
W_motion_new = W_motion - 0.001 × ∂ℒ/∂W_motion
... 所有参数都按这个公式更新
第 5 步:下一轮
用新的参数再跑一次前向传播 → 算 Loss → 反向传播 → 更新
重复几千几万次 → Loss 逐渐下降 → 模型越开越好
2.5 为什么 Loss 的数学形式这么重要?
回到 Loss 设计——现在你应该能理解为什么 Loss 的数学形式直接决定了训练效果:
L2 Loss 的梯度:∂ℒ₂/∂ŷ = 2(ŷ - y)
误差越大,梯度越大 → 大误差样本被优先修正
问题:异常值主导训练
L1 Loss 的梯度:∂ℒ₁/∂ŷ = sign(ŷ - y)
误差不管多大,梯度都是 ±1 → 大误差不会崩
问题:小误差处梯度不变,收敛慢
Smooth L1 的梯度:
|ŷ-y| < 1 时:梯度 = ŷ - y(像 L2,小误差平滑收敛)
|ŷ-y| ≥ 1 时:梯度 = ±1(像 L1,大误差鲁棒)
不同 Loss 的梯度行为不同,这才是选择 Loss 时真正要考虑的——不是哪个 Loss 分数高,而是哪个 Loss 的梯度行为符合你的任务需求。
2.6 学习率 η 怎么定?
学习率是训练中最关键的超参数之一。
η 太大(比如 0.1):
θ_new = θ_old − 0.1 × 梯度
步子太大 → 在 Loss 最低点附近来回震荡,永远不收敛
η 太小(比如 0.00001):
θ_new = θ_old − 0.00001 × 梯度
步子太小 → 要跑几百万步才能到最低点,训练极慢
η 适中(比如 0.001):
刚好每一步都稳定下降 → 又快又好地收敛
实际训练中学习率通常是动态的——学习率衰减(learning rate decay):前几步大(快速接近最优),后面小(精调不震荡)。
2.7 实际训练中常见的"翻车"场景
场景 1:Loss 完全不下降
原因:学习率太小 / 梯度消失 / 参数初始化不对
解决:调大学习率 / 检查激活函数(别用 sigmoid 在深层)
/ 用 Xavier/Glorot 初始化
场景 2:Loss 先下降然后突然变 NaN
原因:梯度爆炸 → 参数一下变得极大 → NaN
解决:梯度裁剪(gradient clipping)→ 限制梯度最大值
UniAD 等所有端到端方法都会做梯度裁剪
场景 3:Loss 下降但验证集不降
原因:过拟合
解决:加正则化(weight decay)/ dropout / 数据增强
场景 4:Loss 震荡不降
原因:学习率太大 / batch size 太小
解决:调小学习率 / 增大 batch size
2.8 这就够了:训练的核心流程
while 没收敛:
1. 取一批数据(图像 + GT 轨迹)
2. 前向传播 → 模型输出预测轨迹 ŷ
3. 算 Loss:ℒ = f(ŷ, y) ← Loss 函数在起作用
4. 反向传播:∂ℒ/∂θ ← 链式法则 ← 算出每个参数的梯度
5. 更新:θ = θ - η × ∂ℒ/∂θ ← 参数沿着梯度反方向走一步
6. 如果 Loss 降不下去了 → 收敛
这个流程对 L1、L2、Focal、KL、扩散、碰撞 Loss——对所有 Loss 都完全一样。区别只在第 3 步的 f(ŷ, y) 不同,导致第 4 步的梯度不同,进而导致第 5 步的参数更新方向不同。
这就是为什么 Loss 设计如此重要:它决定了参数更新的方向,而参数更新的方向决定了模型能学到什么。
三、回归损失:找"最优答案"
回归损失是最直观的 Loss——给你一个场景,你认为有一条"正确答案"轨迹,让模型的预测尽量靠近它。
2.1 L1 Loss / MAE(平均绝对误差)
\[ \mathcal{L}_1 = \frac{1}{N} \sum_{i=1}^N |y_i - \hat{y}_i| \]直觉理解:你和正确答案的距离,绝对差值。预测偏了 1 米,扣 1 分;偏了 3 米,扣 3 分。扣分和偏差成线性关系。
梯度特点:梯度始终是 ±1。误差 0.1 米和误差 10 米,梯度大小一样——不会因为某个样本误差特别大就疯狂更新参数。
谁在用:UniAD 的 MotionFormer(轨迹预测)、MapFormer(地图点回归)、SparseDrive(锚点残差回归)
举个例子:UniAD 的 MotionFormer 预测每个智能体未来 3 秒的轨迹。3 秒后的位置本来就很难准确预测,即使同一场景让两个人来标,他们标的轨迹也会有差异——因为未来是不确定的。用 L1 的好处是:即使某个时间点的预测误差很大,它也不会主导整个梯度更新。L1 对"不确定的未来"很宽容。
2.2 L2 Loss / MSE(均方误差)
\[ \mathcal{L}_2 = \frac{1}{N} \sum_{i=1}^N (y_i - \hat{y}_i)^2 \]直觉理解:偏 1 米扣 1 分,偏 3 米扣 9 分。误差越大的样本,惩罚力度呈平方级增长。
梯度特点:梯度 = 2 × (y−ŷ)。误差越大,梯度越大——模型会优先"修正"那些大误差样本。
谁在用:UniAD Planner(规划轨迹回归)、Diffusion Planner(扩散去噪)、ST-P3(端到端规划)
L1 vs L2 的一个经典例子——UniAD 内部的"分裂":
UniAD 的 MotionFormer(运动预测)→ 用 L1
原因:预测其他车辆的轨迹,3秒后的位置不确定性大
效果:即使预测错了,也不会因为一个样本就崩掉整个训练
UniAD 的 Planner(规划)→ 用 L2
原因:自车轨迹是执行信号,需要精确控制
效果:偏差稍大就受到强烈惩罚,迫使模型输出高精度轨迹
同一个模型、同一个作者,却在两个相邻的模块里用了不同的回归 Loss。这不是失误,是任务目标不同导致的有意识设计。
2.3 Smooth L1
ℒ_smooth = 0.5 × x² 当 |x| < 1 (小误差:像 L2 一样平滑收敛)
ℒ_smooth = |x| - 0.5 当 |x| ≥ 1 (大误差:像 L1 一样对异常值鲁棒)
直觉理解:误差小的时候用 L2(平滑),误差大的时候切到 L1(不被异常值带偏)。这是"既要又要"的聪明折中。
谁在用:Faster R-CNN(框回归)、Sparse4D、几乎所有现代 3D 检测器
为什么检测器爱用 Smooth L1? 因为框的标注有系统误差——两个标注员标的 3D 框可能差几厘米,L2 会把这种标注噪声放大,L1 在大误差处梯度不够平滑。Smooth L1 综合了两者优点。
2.4 三张 Loss 的对比图
图示解读:
- L1(蓝线):V 形折线,底部尖锐,两侧斜率为 ±1。梯度处处一样大。
- L2(红线):抛物线,底部平坦、越往外越陡。误差越大,梯度越大。
- Smooth L1(绿线):底部像 L2(圆滑),两侧像 L1(直线)。兼顾两者优点。
2.5 怎么选?
| 场景 | 推荐 Loss | 原因 |
|---|---|---|
| 训练数据干净,需要快速收敛 | L2 / MSE | 大误差梯度大,收敛快 |
| 数据有离群值 / 标注噪声 | L1 | 对异常值不敏感 |
| 框回归(检测) | Smooth L1 | 兼顾小误差平滑 + 大误差鲁棒 |
| 轨迹回归(规划) | L1 为主 | 轨迹标注本身噪声大 |
四、分类损失:选"哪个答案"
4.1 交叉熵
\[ \mathcal{L}_{CE} = -\sum_{c=1}^C y_c \log(\hat{y}_c) \]直觉理解:模型给正确答案的概率越高,Loss 越低。如果正确答案的概率是 0.9,Loss 很小;如果是 0.1,Loss 很大。
一个直观的例子:假设你要模型判断交通灯——红/黄/绿。正确答案是"红"。
情况A:模型输出 [红:0.9, 黄:0.05, 绿:0.05] → 交叉熵 = -log(0.9) ≈ 0.1 ← Loss 低
情况B:模型输出 [红:0.3, 黄:0.3, 绿:0.4] → 交叉熵 = -log(0.3) ≈ 1.2 ← Loss 高
情况C:模型输出 [红:0.01, 黄:0.01, 绿:0.98] → 交叉熵 = -log(0.01) ≈ 4.6 ← Loss 极高
谁在用:几乎所有分类任务——目标检测类别、交通灯状态、驾驶意图
4.2 Focal Loss
\[ \mathcal{L}_{focal} = -\alpha (1 - \hat{y}_c)^\gamma \log(\hat{y}_c) \]和交叉熵的唯一区别:加了一个调制因子 \((1 - \hat{y}_c)^\gamma\)。
直觉理解:自动驾驶的目标检测有一个棘手的问题——正负样本极度不平衡。一张图像里有几十万个锚点/像素,但其中只有几个是真正的车/人。99.99% 都是背景。如果用普通交叉熵,模型会学会"把全部检测为背景,Loss 就很低"——因为 99.99% 的样本都对了。
Focal Loss 的解法:模型已经分得很好的样本(比如背景概率 0.999),权重被大幅压低;分类错误的难样本(比如车被分类成背景),权重保持不变。
Focal Loss 的两个参数:
γ(聚焦参数):γ=0 → 退化为标准交叉熵;γ=2 → 常用值,效果显著
α(平衡参数):正样本权重,处理正负样本数量差异
举个例子(γ=2):
模型对某个背景预测 0.999:调制因子 = (1-0.999)² = 0.000001,几乎忽略
模型对某辆车预测 0.6:调制因子 = (1-0.6)² = 0.16,保留大部分梯度
谁在用:UniAD(检测 + 地图)、VAD(检测 + 地图)、SparseDrive(检测 + 地图)、几乎所有自动驾驶检测头
论文原文: “Our novel Focal Loss focuses training on a sparse set of hard examples and prevents the vast number of easy negatives from overwhelming the detector during training.” (Lin et al., ICCV 2017)
3.3 BCE 二值交叉熵
\[ \mathcal{L}_{BCE} = -y \log(\sigma(s)) - (1-y) \log(1-\sigma(s)) \]直觉理解:BCE 和普通交叉熵的区别在于——不用 softmax 做归一化,每个类别独立用 sigmoid 判断"是还是不是"。
关键区别:
softmax(多分类 CE):
输出 [0.9, 0.05, 0.05] → 概率和为 1
"三个类别中我选第一个"
sigmoid(多标签 BCE):
输出 [0.9, 0.8, 0.1] → 各维度独立
"第一个很可能,第二个也可能,第三个不可能"
谁在用:
- VADv2 规划词表——4096 条轨迹各自独立打分,多个合理动作可以同时高概率
- OccFormer 占用预测——BEV 中每个格子独立判断"是否被占用"
- DiffusionDriveV2 两阶段选择器中的粗筛——对每条轨迹做"是否可通行"二分类
4.4 交叉熵 vs Focal Loss vs BCE 总结
| 损失 | 适用场景 | 特点 |
|---|---|---|
| 交叉熵 | 多分类(N选1) | 标准分类 Loss |
| Focal Loss | 类别极度不平衡 | 压住易分样本,聚焦难样本 |
| BCE | 多标签分类(可同时选多个) | 各维度独立,互不排斥 |
五、分布损失:学"正确答案的分布"
分类损失要求动作之间互斥,回归损失在非凸空间会取平均。当这两种 Loss 都面临根本缺陷时——分布损失出现了。
4.1 为什么需要分布损失?
先看三个 Loss 面对同一个问题时的表现差异:
场景:自车在路口,前方有对向来车
合理动作:减速让行(概率 0.6),加速抢道(概率 0.3),急刹(概率 0.1)
回归 Loss:
模型学"正确答案" → 取所有合理轨迹的平均 → 输出一条"半减速半加速"的危险轨迹
这就像让你回答"3和7的平均是什么"——答案是5,但5根本不在选项里
分类 Loss(softmax):
强制概率和为 1 → 加速和让行的概率互相挤压
但问题在于:两条都是合理轨迹,概率不应该互相排斥
BCE + sigmoid:
每条轨迹独立打分 → 加速0.6、让行0.3、急刹0.1
这才是"多模态"的正确建模方式
这就是 VADv2 采用 BCE + sigmoid 而不是 softmax 的根本原因。
5.2 KL 散度
\[ D_{KL}(P \parallel Q) = \sum_x P(x) \log \frac{P(x)}{Q(x)} \]直觉理解:KL 散度衡量两个分布的距离——你预测的分布 Q 和真实分布 P 有多像。
VADv2 怎么算数据分布 P_data?
VADv2 的数据分布统计方法:
1. 对训练集中每一帧数据,从规划词表中找到与人类轨迹最匹配的词条
2. 该词条标记为"最佳匹配"
3. 统计所有帧中每个词条成为最佳匹配的频次
4. 归一化 → p_data(a):每条轨迹被人类选择的概率
然后让模型的预测分布 p_pred(a) 尽量接近 p_data(a):
ℒ_distribution = D_KL(p_data || p_pred) = -∑ p_data(a) · log p_pred(a) + 常数
(常数是 p_data 的熵,训练中不变,所以等价于最小化交叉熵)
谁在用:VADv2(规划分布)、知识蒸馏(教师分布 → 学生分布)
5.3 扩散 Loss
\[ \mathcal{L}_{simple} = \mathbb{E}_{t, x_0, \epsilon} \left[ \|\epsilon - \epsilon_\theta(x_t, t)\|^2 \right] \]扩散模型的特殊之处:它的 Loss 既不是回归也不是分类——它预测的是"当前噪声图像里的噪声是什么",而不是"正确答案在哪"。
直觉理解:
训练过程:
1. 拿一条真实轨迹 x₀
2. 加噪声到时间 t,得到 x_t
3. 告诉模型:"现在给你 x_t 和时间 t,请猜我加的是什么噪声"
4. Loss = 预测的噪声和实际加的噪声之间的 MSE
5. 模型本质上学会了:给定一个"损坏版本",如何一步步恢复原样
推理过程:
1. 从纯噪声 x_T 开始
2. 用模型预测当前步该去多少噪声
3. 去掉一点噪声 → x_{T-1}
4. 重复直到 x₀(干净轨迹)
关键 Insight:扩散 Loss 隐式地建模了整个数据分布,而不只是"一个正确答案"。因为模型学会的不是"场景→输出"的映射,而是"噪声→数据"的去噪过程。在这个过程中,多样的训练数据使模型学会了多样的轨迹分布。
谁在用:
- DiffusionDrive — 锚定截断扩散 + 安全 Loss 组合
- Diffusion Planner — 联合预测-规划扩散
- Gen-Drive — 扩散生成未来场景 + RL 微调
5.4 Flow Matching Loss
\[ \mathcal{L}_{FM} = \mathbb{E}_{t, x_0, x_1} \left[ \|v_\theta(x_t, t) - (x_1 - x_0)\|^2 \right] \]和扩散 Loss 的区别:
扩散:走一条弯曲的随机路径从噪声到数据(SDE)
Flow Matching:走一条从噪声到数据的直线路径(ODE)
扩散路径 → 步数多(50-1000步),质量高
FM 路径 → 步数少(2-10步),速度快
谁在用:π0(机器人 VLA)、GoalFlow(自动驾驶规划)
5.5 分布损失总结
| 方法 | Loss | 本质 | 步数 | 谁在用 |
|---|---|---|---|---|
| VADv2 | KL 散度(分布匹配) | 离散词表分类 | 1步 | VADv2 |
| 扩散 | 噪声预测 MSE | 连续去噪 | 10-1000步 | DiffusionDrive |
| Flow Matching | 矢量场预测 | 直线去噪 | 1-10步 | π0, GoalFlow |
六、安全约束 Loss:让模型学会"绝对不能做什么"
这是端到端自动驾驶最有特色的 Loss 类别——纯模仿学习不管安全。
5.1 碰撞 Loss(UniAD)
\[ \mathcal{L}_{collision} = \sum_t \max(0, \text{dist}(\tau_t, O_t) - \text{margin}) \]直觉理解:模型产出一条规划轨迹 τ,把它和 OccFormer 预测的未来占用 O_t 对比。如果轨迹太靠近占用区域(小于安全距离 margin),就被惩罚。
为什么需要 margin? 你不需要轨迹离障碍物无限远——那会开成"老奶奶开车",安全但没法走。margin = 0.5 米意味着只要轨迹和障碍物保持 0.5 米以上就不扣分,只有小于 0.5 米才被惩罚。
UniAD 消融实验:只有加上了碰撞 Loss,3 秒碰撞率才从 1.58 降到 1.39(降 12%)。再加占用优化,降到 1.05(降 36%)。
6.2 冲突 Loss(VADv2)
\[ \mathcal{L}_{conflict} = \sum_{a \in V} \mathbb{1}_{\text{conflict}}(a) \cdot \log p_{\text{pred}}(a) \]直觉理解:VADv2 对规划词表里的 4096 条轨迹做两件事:
- 统计每条轨迹被人类选的频率 → 分布 Loss
- 判断每条轨迹是否碰撞/偏离道路 → 冲突 Loss
冲突的轨迹被标记为正样本,Log Loss 让它们的概率被压低。
谁在用:VADv2、Hydra-MDP
6.3 DiffusionDrive 的安全 Loss 组合
ℒ_total = ℒ_diff ← 学得像人类(扩散 Loss)
+ λ_collision ℒ_collision ← 别撞上
+ λ_boundary ℒ_boundary ← 别出车道
+ λ_comfort ℒ_comfort ← 别急刹急转(jerk 约束)
工程经验:不能只靠扩散 Loss。纯生成目标只关心"分布像不像数据",但驾驶是安全攸关任务。必须把碰撞、合规等硬约束作为辅助 Loss 显式注入。
6.4 碰撞 Loss 的演进
| 论文 | 安全 Loss | 方式 | 特点 |
|---|---|---|---|
| UniAD | 碰撞 Loss + 占用优化 | 可微 Loss | 用 OccFormer 的占用预测做碰撞检查 |
| VAD | 向量化规划约束 | 可微几何约束 | 把矢量地图变成可微的"安全距离场" |
| VADv2 | 冲突 Loss | 词表负采样 | 对词表中冲突轨迹压低概率 |
| DiffusionDrive | 碰撞 + 边界 + 舒适 | 多 Loss 加权 | 扩散 Loss 做生成 + 三个安全约束 |
七、多任务 Loss 平衡
7.1 问题:Loss 在打架
端到端模型有 5+ 个 Loss 同时优化。它们天然会打架:
UniAD 的 Loss 构成:
ℒ = ℒ_track + ℒ_map + ℒ_motion + ℒ_occ + ℒ_plan
每个 Loss 的典型量级:
ℒ_plan(L2 回归):≈ 1.0 量级
ℒ_track(Focal + L1):分类 ≈ 0.01,回归 ≈ 0.1
ℒ_occ(BCE + Dice):≈ 0.5
如果直接相加 → ℒ_plan 的梯度把 ℒ_track 的梯度淹没了
模型学到的全是规划,感知部分几乎不更新
7.2 解决方式 1:手动加权(最简单)
给每个 Loss 手动配权重:
\[ \mathcal{L} = \lambda_1 \mathcal{L}_{track} + \lambda_2 \mathcal{L}_{map} + \lambda_3 \mathcal{L}_{motion} + \lambda_4 \mathcal{L}_{occ} + \lambda_5 \mathcal{L}_{plan} \]UniAD 就这么干的。缺点是需要反复试,每个权重都是超参数。
7.3 解决方式 2:不确定性加权(最常用)
\[ \mathcal{L} = \sum_i \frac{1}{2\sigma_i^2} \mathcal{L}_i + \log \sigma_i \]每个任务有一个可学习的 \(\sigma_i\)。任务噪声大 → 权重自动低;任务噪声小 → 权重自动高。
直觉理解:模型的 Loss 自动学会"什么时候该相信哪个任务"。如果跟踪模块当前不确定性高,跟踪 Loss 的权重自动降低,不让它干扰其他任务。
谁在用:多任务分割、Multi-Task Learning Using Uncertainty to Weigh Losses (Kendall et al., 2018) 系列方法
7.4 解决方式 3:两阶段训练(最可靠)
阶段一:各模块分别预训练
1. 训练 BEVFormer(骨干)
2. 训练 TrackFormer + MapFormer(感知)
3. 固定感知,训练 MotionFormer(预测)
4. 固定 MotionFormer,训练 OccFormer(占用)
5. 固定所有上游,训练 Planner(规划)
阶段二:端到端联合微调
1. 打开所有梯度
2. 规划 Loss 的梯度回传至感知
3. 各模块在规划目标下做精调
关键经验:直接从头端到端训练一定崩。梯度尺度差异大一两个量级。先让每个模块各自学个差不多,再联合微调。
谁在用:UniAD(经典两阶段)、SparseDrive(逐步端到端)、几乎所有多模块融合的端到端方法
八、强化学习 Loss:从"模仿"到"探索"
监督学习的学生永远只能学到老师做过的。RL 让模型自己探索"怎么做更好"。
7.1 PPO-Clip(Proximal Policy Optimization)
\[ \mathcal{L}_{PPO} = -\mathbb{E} \left[ \min\left( \frac{\pi_\theta(a|s)}{\pi_{\theta_{old}}(a|s)} A, \text{clip}\left( \frac{\pi_\theta(a|s)}{\pi_{\theta_{old}}(a|s)}, 1-\epsilon, 1+\epsilon \right) A \right) \right] \]用大白话翻译:
1. 旧策略在场景 s 下选了动作 a,获得了"相对优势"A
(A > 0 表示这个动作比平均水平好,A < 0 表示比平均水平差)
2. 新策略现在要更新自己
- 对好动作(A > 0):提高选这个动作的概率
- 对坏动作(A < 0):降低选这个动作的概率
3. 但不准一次改太多
- clip(ratio, 1-ε, 1+ε):新旧策略的比率不能超过 1±ε
- 这就像"每次只迈一小步",保证训练的稳定性
谁在用:多智能体交互策略、传统 RL 驾驶
7.2 GRPO(Group Relative Policy Optimization)
GRPO 是 PPO 在端到端自动驾驶中的"特化版本"。
PPO 的痛点:需要训练一个价值网络(critic)来估计"这个状态有多好"。价值网络和策略网络一起训,不稳定且显存翻倍。
GRPO 的解法:不要价值网络了。模型自己生成一组候选轨迹,在这组内部算相对优势。
GRPO 的组内优势计算:
场景 → 模型生成 8 条候选轨迹 → 每条打分
候选 A:85 分 候选 B:92 分 候选 C:60 分 ...
平均分:78 分,标准差:12
A 的优势 = (85 - 78) / 12 = +0.58 (比平均好)
B 的优势 = (92 - 78) / 12 = +1.17 (明显好)
C 的优势 = (60 - 78) / 12 = -1.50 (明显差)
为什么 GRPO 在自动驾驶里尤其流行?因为端到端自动驾驶天然产出一组候选轨迹——你不需要额外采样,模型的前向传播本身就在产出多条候选。组内比较比绝对评分更稳、更简单。
谁在用:
- AlphaDrive-GRPO — 元动作分类 + GRPO 精调
- AutoVLA — 连续轨迹 + GRPO 后训练
- DiffusionDriveV2 — 截断扩散 + 锚内 GRPO
- Flow-GRPO — Flow Matching + GRPO
7.3 DiffusionDriveV2 的截断 GRPO(一个具体例子)
DiffusionDriveV2 把扩散和 GRPO 做了个聪明的结合:
1. 截断扩散:
- 正常扩散去噪要 100 步
- 截断扩散只保留最后 2 步的梯度
- 前面 98 步完全忽略梯度
2. 为何能截断?
- 扩散的前期去噪是全体的、粗粒度的
- 后期去噪是局部的、精调的
- RL 的目标是"精调轨迹质量",所以只对最后几步做 RL
3. 锚内比较:
- 直行锚的候选和直行锚的候选比
- 转弯锚的候选和转弯锚的候选比
- 不同锚之间不直接比较——直行和转弯没有优劣之分
7.4 RL Loss 总结
| Loss | 需要价值网络? | 计算量 | 谁在用 |
|---|---|---|---|
| PPO | ✅ 是 | 高 | 传统 RL 驾驶 |
| GRPO | ❌ 否 | 中 | AlphaDrive, AutoVLA, Flow-GRPO |
| REINFORCE | ❌ 否 | 低 | 策略梯度基础方法 |
| 截断 GRPO | ❌ 否 | 低 | DiffusionDriveV2 |
九、排序 / 对比 Loss
8.1 Margin-Rank Loss
\[ \mathcal{L}_{rank} = \sum_{i,j} \max(0, -(\hat{s}_i - \hat{s}_j) + m) \]直觉理解:好轨迹的分数要比坏轨迹高至少 m 分。只关注"相对排序"而不关心绝对分数。
谁在用:DiffusionDriveV2 的两阶段选择器——先用 BCE 粗筛,再用 Margin-Rank 精排。
和 MSE / 交叉熵的区别:
MSE:让预测分数 = 真实分数 0.95(精确拟合)
Margin-Rank:让好轨迹分数 > 坏轨迹分数 + m(只关心排序)
交叉熵:正确答案概率高,其他低(硬分类)
8.2 对比 Loss
\[ \mathcal{L}_{contrast} = -\log \frac{\exp(\text{sim}(q, k_+) / \tau)}{\sum \exp(\text{sim}(q, k_i) / \tau)} \]直觉理解:把相似的东西拉近,把不同的推远。
谁在用:
- JEPA-DRIVE(自监督表征学习)
- VLA-World(跨模态对齐)
- ReWorld(世界模型表征学习)
十、全景总结
演进路线:Loss 设计的"三代"
第一代:确定论
Loss:L1 / L2 / Smooth L1
哲学:"每条轨迹有唯一正确答案"
局限:多模态场景下输出"平均"
代表:ST-P3、TCP
第二代:分布论
Loss:交叉熵 / Focal / BCE / KL / 扩散
哲学:"动作空间有分布,不应只取一点"
局限:安全只能软约束
代表:VADv2、DiffusionDrive
第三代:策论
Loss:PPO / GRPO / Rank
哲学:"让它自己探索更好"
局限:奖励设计难、训练不稳定
代表:AlphaDrive、DiffusionDriveV2
十条设计原则
- 回归适合精确控制,不适合多模态 — 轨迹回归用 L1,框回归用 Smooth L1
- 分类解决多模态,但受限于词表覆盖 — Focal Loss 解决正负样本不平衡
- 分布匹配是"不丢信息"的回归替代方案 — KL 散度允许分布呈任意形状
- 扩散 Loss 同时做生成 + 分布建模 — 简单 Loss 即可学复杂分布
- 安全约束不能靠学,要硬编码进 Loss — collision / conflict loss 是必需的
- 多任务 Loss 需要显式平衡 — 两阶段训练是最可靠的工程经验
- RL Loss 让模型超越人类 — GRPO 是当前端到端规划的主流选择
- 不同 Loss 服务于不同的优化目标 — 理解目标比理解公式更重要
- 没有万能 Loss — 每个 Loss 都是对驾驶某个维度的建模
- Loss 进化的方向:从"找答案"到"学分布"再到"定策略"
十一、各论文 Loss 配置速查表
| 论文 | 检测 | 建图 | 运动预测 | 占用预测 | 规划 |
|---|---|---|---|---|---|
| UniAD | Focal + L1 | Focal + L1 | L1 + Focal | BCE + Dice | L2 + 碰撞 Loss |
| VAD | Focal + L1 | Focal + L1 | L1 + 终点 Loss | — | L1 + 向量约束 ×3 |
| VADv2 | Focal + L1 | Focal + L1 | — | — | KL + Conflict + Token |
| SparseDrive | Focal + L1 | Focal + L1 | L1 + 分类 | — | 锚点分类 + 残差 L1 |
| DiffusionDrive | — | — | — | — | 扩散 + 碰撞 + 边界 + 舒适 |
| DiffusionDriverV2 | — | — | — | — | 扩散 + 碰撞 + RL(GRPO) |
| SparseDriveV2 | Focal + L1 | Focal + L1 | L1 + 分类 | — | 锚分类 + 残差 L1 + 碰撞 |
| AlphaDrive | — | — | — | — | SFT(CE) + GRPO |
十二、写在最后
端到端自动驾驶是一个多目标优化问题——要像人(模仿)、要安全(碰撞)、要舒适(jerk)、要合法(车道)、还要能处理不确定性(多模态)。每个目标都是一个 Loss,每个 Loss 都有适合它的数学形式。
一个好的 Loss 设计,不是在选"哪个 Loss 最好",而是在搞清楚这个 Loss 让模型把注意力放在哪里——是让模型"猜得准"(L2),还是"选得对"(分类),还是"不犯错"(碰撞 Loss),还是"敢于尝试"(GRPO)。
这篇文章覆盖了所有主流 Loss,但实际中你可能还需要根据具体场景做组合和改动。如果有特定的 Loss 或论文配置想了解,欢迎继续问。