☰
VLA 系统学习第 9 课:Loss 到底在告诉模型什么?——从 MSE、L1 到 Cross Entropy
2026/10/8 7:04:55 网站建设 项目流程

第八课标准答案

上一课我们解决的是:Backward 已经算出了梯度以后,参数到底怎么更新。

先把 10 个问题完整收掉,然后直接进入第 9 课。今天下一步要解决的是训练链里一直没有真正展开的:

\[ loss = loss\_fn(pred\_action, action) \]

也就是——Loss 到底在衡量什么。


1. Gradient 和 Learning Rate 分别回答什么问题?

Gradient:

\[ \nabla_\theta L \]

主要回答:

参数朝哪个方向变化,会让 Loss 发生怎样的变化。

而 Learning Rate:

\[ \eta \]

主要回答:

知道方向以后,这次参数到底走多大一步。

所以可以记成:

\[ \boxed{ Gradient\rightarrow方向与局部变化趋势 } \]\[ \boxed{ Learning\ Rate\rightarrow更新步长 } \]

最终二者共同参与:

\[ \theta_{new} = \theta_{old} - \eta\nabla_\theta L \]


2. 已知

\[ w=2 \]\[ \frac{\partial L}{\partial w}=5 \]\[ \eta=0.1 \]

最基础梯度下降:

\[ w_{new} = w_{old} - \eta\frac{\partial L}{\partial w} \]

代入:

\[ w_{new} = 2-0.1\times5 \]

所以:

\[ \boxed{w_{new}=1.5} \]

梯度为正,因此为了降低 Loss,参数向更小的方向更新。


3. 为什么 Learning Rate 太大会导致震荡甚至发散?

因为虽然 Gradient 给出了局部方向,但这个方向只描述:

当前参数附近,Loss 大概怎么变化。

如果:

\[ \eta \]

特别大,那么一次更新:

\[ \Delta\theta=-\eta\nabla_\theta L \]

可能直接跨过较好的区域。

于是可能出现:

最低点在中间 左边 ↓ 一次更新太大 ↓ 直接跳到右边很远 ↓ 下一次又跳回左边

参数不断来回跨越低 Loss 区域。

严重时数值越来越大,最终可能出现:

loss = inf

或者:

loss = nan

4. 为什么 Learning Rate 太小会训练很慢?

因为每次:

\[ \Delta\theta = -\eta\nabla_\theta L \]

都非常小。

即使 Gradient 的方向完全正确:

当前参数 ↓ 移动极小一点 ↓ 再移动极小一点 ↓ 再移动极小一点

需要大量 Training Step 才能到达较好的参数区域。

所以不是:

模型完全没学。

而可能是:

学得太慢。


5. Mini-batch SGD 中为什么不同 Batch 的 Gradient 不一样?

因为不同 Batch 包含的训练样本不同。

例如:

\[ B_1 \]

里的样本更容易,而:

\[ B_2 \]

里可能包含更难的数据。

它们对应的 Loss 不同:

\[ L_1(\theta)\neq L_2(\theta) \]

因此:

\[ \nabla_\theta L_1 \]

和:

\[ \nabla_\theta L_2 \]

也可能不同。

所以 Mini-batch Gradient 可以理解为:

利用一小批数据,对整个 Dataset 的更新方向进行一次估计。

这也是训练 Loss 经常上下轻微波动的原因之一。


6. Momentum 比最基础 SGD 多利用了什么?

最基础 SGD 主要看:

\[ 当前Gradient \]

Momentum 还会考虑:

过去若干次更新形成的方向趋势。

因此不会因为当前某一个 Batch 的 Gradient 稍微偏了一点,就立刻完全改变更新方向。

直觉上:

SGD 只看现在往哪里走 Momentum 现在的方向 + 之前积累的惯性

所以 Momentum 经常能:

  • 减少部分震荡;
  • 加速稳定方向上的移动。

7. Adam 中的 \(m_t\) 和 \(v_t\) 怎么理解?

先用直觉理解即可。

\[ m_t \]

是梯度的一阶移动平均,可以理解为:

最近 Gradient 大致朝哪个方向。

而:

\[ v_t \]

是梯度平方的移动平均,可以理解为:

最近这个参数的 Gradient 通常有多大。

所以 Adam 同时考虑:

往哪里走? + 这一维梯度尺度有多大?

再决定实际更新量。


8. Adam 会自动消除 Learning Rate 吗?

不会。

Adam 虽然会根据历史 Gradient 调节不同参数的有效更新尺度,但仍然需要一个基础:

\[ \eta \]

例如:

optimizer = torch.optim.Adam( model.parameters(), lr=1e-4)

这个:

\[ 10^{-4} \]

仍然非常重要。

所以:

\[ \boxed{ Adam\neq不需要Learning\ Rate } \]

更准确地说:

Adam 在基础 Learning Rate 之上,再根据 Gradient 历史进行自适应调整。


9. 为什么恢复 Adam 训练时还要保存optimizer.state_dict()?

因为 Adam 自己也在学习过程中保存状态,例如:

\[ m_t \]\[ v_t \]

以及当前 Step 等信息。

假设已经训练:

\[ 50000 \]

步。

如果只恢复:

model.state_dict()

模型参数恢复了。

但 Adam 之前积累的历史 Gradient 信息没了。

于是 Optimizer 相当于:

参数已经训练很久,但优化器却从“第一天”重新开始。

所以如果目的是真正继续训练,通常需要同时保存:

model.state_dict()

和:

optimizer.state_dict()

10. 为什么optimizer.step()不能简单解释成“执行梯度下降”?

因为:

optimizer.step()

只是 PyTorch 提供的统一接口。

具体怎么更新,取决于 Optimizer 类型。

例如 SGD:

\[ \theta_{t+1} = \theta_t-\eta g_t \]

Momentum SGD 会加入历史趋势。

Adam 又会维护:

\[ m_t,\quad v_t \]

因此:

optimizer.step()

更准确的解释应该是:

根据当前 Optimizer 的更新规则,利用已经计算好的 Gradient 修改它所管理的 Parameter。


VLA 系统学习第 9 课:Loss 到底在告诉模型什么?——从 MSE、L1 到 Cross Entropy

现在我们已经把训练链走到了:

\[ Observation \rightarrow Policy \rightarrow Predicted\ Action \]

然后:

\[ Predicted\ Action + Expert\ Action \rightarrow Loss \]

再:

\[ Loss \rightarrow Backward \rightarrow Gradient \rightarrow Optimizer \]

但是到目前为止,loss_fn一直是一个黑盒。

例如:

loss = loss_fn(pred_action, action)

这里实际上发生的是一件非常重要的事情:

你通过 Loss 告诉模型:什么叫“错”,以及错多少算严重。

所以 Loss 不是训练代码中随便选的一个数学公式。

它实际上定义了:

\[ \boxed{ 模型到底应该努力变成什么样 } \]


一、先回到 Behavior Cloning

假设专家 Action:

\[ a= [0.10,-0.05] \]

模型预测:

\[ \hat a= [0.07,-0.01] \]

现在模型已经给出了结果。

但它自己不知道:

这个结果究竟好不好?

所以我们必须构造:

\[ L(\hat a,a) \]

把:

\[ 预测值 \]

和:

\[ 正确答案 \]

之间的差距转换成一个数字。

例如:

\[ L=0.0025 \]

之后 Backward 才能回答:

为了让这个数字下降,各 Parameter 应该怎么改变?

因此 Loss 是:

\[ Prediction \]

和:

\[ Gradient \]

之间的桥梁。

完整链是:

\[ \boxed{ Prediction \rightarrow Loss \rightarrow Gradient \rightarrow Parameter\ Update } \]


二、连续 Action 最直接的方法:MSE

假设机器人 Action 是连续值。

例如:

\[ a= [\Delta x,\Delta y] \]

专家:

\[ a=[0.10,-0.05] \]

预测:

\[ \hat a=[0.07,-0.01] \]

一种最自然的想法就是:

每一维预测值离正确答案到底有多远?

差值:

\[ \hat a-a \]

得到:

\[ [-0.03,0.04] \]

但是这里有一个问题。

如果直接相加:

\[ -0.03+0.04=0.01 \]

两个方向的错误可能互相抵消。

明明模型错了两次,最后看起来误差却很小。

所以一种常见方法就是:

把误差平方。

于是:

\[ (-0.03)^2=0.0009 \]\[ (0.04)^2=0.0016 \]

它们都变成正数。

这就是 MSE 的基础。


三、MSE 到底是什么?

MSE:

Mean Squared Error

均方误差。

如果有 \(N\) 个预测数字:

\[ \hat y_1,\hat y_2,\ldots,\hat y_N \]

对应正确答案:

\[ y_1,y_2,\ldots,y_N \]

那么:

\[ L_{MSE} = \frac{1}{N} \sum_{i=1}^{N} (\hat y_i-y_i)^2 \]

这里:

Mean

表示求平均。

Squared

表示误差平方。

Error

就是预测误差。


四、刚才的 Action 算一次 MSE

专家:

\[ a=[0.10,-0.05] \]

预测:

\[ \hat a=[0.07,-0.01] \]

两个平方误差:

\[ 0.0009 \]

和:

\[ 0.0016 \]

求平均:

\[ L_{MSE} = \frac{0.0009+0.0016}{2} \]

得到:

\[ \boxed{0.00125} \]

注意上一些教学例子如果直接把平方误差求和,会得到:

\[ 0.0025 \]

那属于 squared-error sum。

而严格意义上的 MSE 还会进行:

\[ Mean \]

所以这里把二者区分清楚。


五、PyTorch 中的 MSE

对应代码:

loss_fn = nn.MSELoss()loss = loss_fn( pred_action, expert_action)

假设:

\[ pred\_action:[32,7] \]\[ expert\_action:[32,7] \]

默认情况下,PyTorch 会对这些元素的平方误差做 reduction,通常得到一个标量:

\[ loss:[] \]

数据链:

\[ [32,7] + [32,7] \rightarrow 224个平方误差 \rightarrow 求平均 \rightarrow 1个Loss \]

然后:

loss.backward()

就从这个 scalar Loss 开始反向传播。


六、MSE 有一个非常重要的特性:大错误会受到更重惩罚

假设误差:

\[ e=1 \]

平方以后:

\[ e^2=1 \]

如果误差:

\[ e=2 \]

平方以后:

\[ e^2=4 \]

误差只是变成原来的两倍,但惩罚变成:

\[ 4 \]

倍。

如果:

\[ e=10 \]

那么:

\[ e^2=100 \]

所以 MSE 会特别重视:

预测得非常离谱的样本。

这既可能是优点,也可能带来问题。


七、为什么异常值会明显影响 MSE?

假设大部分 Action 误差:

\[ 0.1 \]

平方:

\[ 0.01 \]

但某一个异常样本误差:

\[ 5 \]

平方:

\[ 25 \]

那么这个单独的异常点可能远远超过其他样本的 Loss。

所以:

MSE 对大误差比较敏感。

如果 Dataset 中存在:

  • 错误 Demonstration;
  • 传感器异常;
  • Action 突变;
  • 时间同步错误;

这些异常数据可能对 MSE 产生较大影响。

这也是为什么 Robot Dataset 的质量非常重要。


八、另一种思路:L1 Loss

假设我们不平方。

而是直接取误差绝对值:

\[ L_{L1} = \frac{1}{N} \sum_{i=1}^{N} |\hat y_i-y_i| \]

这叫:

L1 Loss

也经常叫:

Mean Absolute Error,MAE

例如误差:

\[ e=2 \]

MSE 惩罚:

\[ 4 \]

L1:

\[ 2 \]

误差:

\[ e=10 \]

MSE:

\[ 100 \]

L1:

\[ 10 \]

所以 L1 对特别大的异常误差没有 MSE 那么敏感。


九、MSE 和 L1 到底有什么直觉区别?

可以直接比较:

ErrorL1Squared Error
0.10.10.01
111
224
5525
1010100

可以看到:

MSE 对大误差的惩罚增长得更快。

因此:

\[ \boxed{ MSE \rightarrow 更强烈惩罚大误差 } \]

而:

\[ \boxed{ L1 \rightarrow 对异常大误差相对更稳健 } \]

但是这不意味着:

L1 一定比 MSE 好。

具体 Loss 应该根据:

  • Action 表示;
  • 数据噪声;
  • 模型设计;
  • 原论文方法;

来确定。

以后分析真实 Repository 时,不能因为“连续 Action”就自动假设一定使用 MSE。

必须去查代码。


十、为什么连续 Action 常见回归 Loss?

假设 Action:

\[ a= [\Delta x,\Delta y,\Delta z] \]

每个维度都是连续数值。

例如:

\[ \Delta x=0.013 \]

模型可以预测:

\[ 0.012 \]

也可以预测:

\[ 0.018 \]

问题是:

预测值离目标值有多远?

这种问题叫:

Regression

回归。

因此常见:

\[ MSE \]

或者:

\[ L1 \]

等回归 Loss。

所以:

\[ Continuous\ Action \]

经常对应:

\[ Regression \]

但这里只能作为基本思想,不是所有 VLA 的统一规则。


十一、如果 Action 不是连续值呢?

假设模型的 Action 不再是:

\[ 0.013 \]

这种连续数值。

而是从几个动作类别中选一个:

0 → Left 1 → Right 2 → Forward 3 → Backward 4 → Gripper Close

现在正确答案可能是:

\[ class=2 \]

这个问题就不是:

预测数值离 2 有多少距离?

因为类别编号:

\[ 0,1,2,3,4 \]

本身并不代表实际几何距离。

比如:

class 0 = Left class 4 = Gripper Close

不能说:

因为 0 离 4 比较远,所以两个动作“差得特别大”。

类别编号只是标签。

因此这时候需要的是:

Classification Loss

最典型就是:

Cross Entropy

交叉熵。


十二、分类模型真正输出的通常不是一个类别编号

假设有 4 个 Action Class。

模型通常不是直接输出:

class = 2

而是先输出四个数:

\[ z=[2.1,0.3,4.0,-1.2] \]

这些通常叫:

Logits

可以把它们理解成:

模型对各个类别的原始打分。

Shape:

\[ [B,C] \]

其中:

  • \(B\):Batch Size;
  • \(C\):类别数量。

例如:

\[ [32,4] \]

表示:

32 个样本,每一个样本对 4 个类别都产生一个分数。


十三、Softmax 做什么?

假设 Logits:

\[ z=[2.1,0.3,4.0,-1.2] \]

Softmax 会把它们转换成类似:

\[ p=[0.12,0.02,0.85,0.01] \]

满足:

\[ \sum_i p_i=1 \]

于是可以理解:

Class 0:12% Class 1:2% Class 2:85% Class 3:1%

模型当前认为:

\[ Class\ 2 \]

最可能。


十四、Cross Entropy 在惩罚什么?

假设正确答案:

\[ y=2 \]

模型给正确类别的概率:

\[ p_2=0.85 \]

那么 Loss 会比较小。

如果模型只给正确类别:

\[ p_2=0.01 \]

Loss 就会非常大。

对于单个正确类别,一个核心形式可以理解为:

\[ L=-\log p_y \]

其中:

\[ p_y \]

就是:

模型分配给正确类别的概率。


十五、算两个数字就非常直观

如果正确类别概率:

\[ p_y=0.9 \]

那么:

\[ L=-\log(0.9) \]

约:

\[ 0.105 \]

Loss 很小。

如果正确类别概率:

\[ p_y=0.01 \]

那么:

\[ L=-\log(0.01) \]

约:

\[ 4.605 \]

Loss 很大。

所以 Cross Entropy 在告诉模型:

你应该给正确类别更高概率。


十六、PyTorch 里的 CrossEntropyLoss 有一个很容易踩坑的地方

假设:

logits.shape == [32, 10]

表示:

  • 32 个样本;
  • 10 个类别。

Target:

target.shape == [32]

里面可能是:

[3, 1, 7, 0, ...]

每个数字表示正确类别编号。

代码:

loss_fn = nn.CrossEntropyLoss()loss = loss_fn(logits, target)

这里通常:

直接把 logits 交给CrossEntropyLoss。

不需要自己先写:

softmax(logits)

因为 PyTorch 的CrossEntropyLoss已经以数值稳定的方式组合了相关计算。

这是以后写分类代码非常重要的点。


十七、现在连续 Action 和离散 Action 就能区分了

连续 Action

例如:

\[ a= [0.01,-0.02,0.03] \]

重点:

数值差多少?

可能使用:

\[ MSE \]\[ L1 \]

或者其他回归类 Loss。


离散 Action

例如:

Left Right Forward Close

重点:

正确类别是哪一个?

可能使用:

\[ Cross\ Entropy \]

所以:

\[ \boxed{ Regression \rightarrow 数值距离 } \]\[ \boxed{ Classification \rightarrow 类别概率 } \]


十八、这和后面 VLA 的 Action Representation 有直接关系

现在你应该明白为什么我们最早就规定:

阅读任何 VLA,必须先确认 Action 到底是什么。

因为 Action Representation 会直接影响:

\[ Output\ Head \]

以及:

\[ Loss \]

例如:

Continuous Action Regression

和:

Discrete Action Token

模型的训练目标可能完全不同。

所以以后看到一篇 VLA 论文,不能只问:

Backbone 是什么?

还必须问:

Action怎么表示? ↓ 模型输出什么? ↓ Loss怎么定义?

这是同一条链。


十九、机器人 Action 的尺度为什么会影响 Loss?

这是非常重要的一点。

假设 Action:

\[ a= [x,y,z,\theta,g] \]

其中位置单位是米。

例如:

\[ x\in[-0.05,0.05] \]

但某个角度使用度:

\[ \theta\in[-180,180] \]

而夹爪:

\[ g\in[0,1] \]

如果直接对所有维度使用 MSE:

\[ L= \frac1D \sum_i (\hat a_i-a_i)^2 \]

就可能出现:

角度这一维因为数值尺度巨大,主导整个 Loss。

例如位置误差:

\[ 0.01 \]

平方:

\[ 0.0001 \]

角度误差:

\[ 10 \]

平方:

\[ 100 \]

那么 Optimization 几乎都在关心角度。

位置维度的误差在数值上显得非常微小。


二十、这就是 Action Normalization 为什么重要

一种办法是先把不同 Action Dimension 归一化到相近尺度。

例如:

\[ a'_i= \frac{a_i-\mu_i}{\sigma_i} \]

那么原来不同量纲:

米 弧度 夹爪值

进入模型后都处在相对可比的数值范围。

这样 Loss 才不容易被某一个巨大尺度的维度完全支配。

所以:

\[ \boxed{ Normalization \neq 只是让数字好看 } \]

它会真正影响:

\[ Loss \]

进而影响:

\[ Gradient \]

最终影响:

\[ Parameter\ Update \]

完整因果链:

\[ Action\ Scale \rightarrow Loss\ Scale \rightarrow Gradient \rightarrow Training \]


二十一、也可以给不同 Action Dimension 不同权重

假设:

\[ L= \lambda_{pos}L_{pos} + \lambda_{rot}L_{rot} + \lambda_{gripper}L_{gripper} \]

这里:

\[ \lambda \]

表示不同部分的 Loss 权重。

例如开发者可能认为:

Gripper 是否闭合非常关键。

于是让:

\[ \lambda_{gripper} \]

更大。

这意味着模型训练时:

Gripper 错误会受到更强惩罚。

所以 Loss Weight 实际上是在表达:

哪些错误对任务更重要。

以后看到论文里的:

\[ L=L_1+\lambda L_2 \]

不能只背公式。

必须问:

为什么第二个 Loss 要乘 \(\lambda\)?


二十二、一个模型可以同时有多个 Loss

后面 ACT、VLA、VLM 中非常常见。

例如模型可能同时学习:

Action Prediction + Latent Representation + Auxiliary Task

于是:

\[ L_{total} = L_{action} + \lambda L_{aux} \]

Backward 最终针对的是:

\[ L_{total} \]

所以不同 Loss 都可能通过计算图影响 Parameter。

这也是为什么读论文时:

Loss Function 是核心方法的一部分。


二十三、把 Loss 放回完整 BC 训练

现在一个最基础连续 Action BC:

for batch in dataloader: obs = batch["observation"] expert_action = batch["action"] optimizer.zero_grad() pred_action = model(obs) loss = torch.nn.functional.mse_loss( pred_action, expert_action ) loss.backward() optimizer.step()

现在每一行都已经有完整含义了。

假设:

\[ obs:[32,10] \]

进入 Policy:

\[ [32,10] \rightarrow [32,64] \rightarrow [32,7] \]

得到:

\[ pred\_action:[32,7] \]

专家:

\[ expert\_action:[32,7] \]

然后:

\[ MSE \]

比较这:

\[ 32\times7 \]

个预测数字。

得到:

\[ loss:[] \]

Backward:

\[ loss \rightarrow \frac{\partial L}{\partial\theta} \]

Optimizer:

\[ \theta \rightarrow \theta_{new} \]

整个 Behavior Cloning Training Loop 现在已经真正闭合。


二十四、Loss 低是不是代表 Robot Policy 一定好?

仍然不是。

这和第二课 Distribution Shift 联系起来了。

模型可能在 Dataset 上:

\[ MSE=0.001 \]

非常低。

但是实际 Rollout:

第一次动作略有偏差 ↓ 进入Dataset很少出现的Observation ↓ 继续预测错误 ↓ 误差累积 ↓ 任务失败

所以:

\[ \boxed{ Training\ Loss \neq 最终机器人任务表现 } \]

Loss 是非常重要的训练信号。

但最终仍然要做:

Rollout Evaluation

例如真正评估:

\[ Success\ Rate \]

这也是为什么 Robot Learning 不能只看训练曲线。


二十五、本章主线回看

现在loss_fn已经不再是一个黑盒。

整个逻辑是:

\[ \boxed{ Model\ Output + Target \rightarrow Loss \rightarrow 定义“什么叫错误” } \]

如果是连续值预测:

\[ Continuous\ Action \rightarrow Regression \rightarrow MSE/L1/\text{其他方法} \]

如果是离散类别:

\[ Discrete\ Action \rightarrow Classification \rightarrow Cross\ Entropy/\text{其他方法} \]

同时:

\[ Action\ Scale \]

会影响:

\[ Loss \]

继而影响:

\[ Gradient \]

最后影响:

\[ Parameter\ Update \]

所以训练从来不是:

随便挑一个 Loss 然后 Backward。

而是:

Loss 必须与模型输出的语义和训练目标匹配。


第九课自测

  1. 为什么不能直接把正负误差简单相加作为回归 Loss?

  2. MSE 中的三个词 Mean、Squared、Error 分别是什么意思?

  3. 专家:

\[ a=[1,3] \]

预测:

\[ \hat a=[2,5] \]

请计算 MSE。

  1. 为什么 MSE 比 L1 更容易受到异常大误差影响?

  2. 连续 Action 为什么通常属于 Regression 问题?

  3. 离散 Action Classification 为什么不能直接把“类别编号之间的数字距离”当成动作误差?

  4. logits:[32,10]中的 32 和 10 分别是什么?

  5. Cross Entropy 的核心目标为什么可以理解成“提高正确类别的概率”?

  6. 为什么 PyTorch 的nn.CrossEntropyLoss()通常直接接 logits,而不是自己提前做 Softmax?

  7. 如果某个 Action 中位置范围大约:

\[ [-0.05,0.05] \]

而另一个维度范围:

\[ [-180,180] \]

为什么直接使用 MSE 可能产生严重问题?

  1. 为什么 Action Normalization 会直接影响模型训练,而不只是数据格式处理?

  2. 最后请解释这条链:

\[ Action\ Representation \rightarrow Model\ Output \rightarrow Loss \rightarrow Gradient \rightarrow Policy \]

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询