第十课标准答案

  1. Generalization 是模型把训练数据里学到的规律,用到没参与训练的新数据上的能力。不是“把训练集做得很好”,而是面对新的 Observation、Episode 或任务条件仍然有效。

  2. Train、Validation、Test 的核心职责分别是:

\[ Train\rightarrow更新模型参数 \]\[ Validation\rightarrow训练过程中选模型、调超参数 \]\[ Test\rightarrow开发完成后的最终独立评价 \]

  1. Validation 虽然通常不参与 backward(),但我们会根据 Validation 结果决定网络结构、Learning Rate、Epoch、Checkpoint 等。因此开发者已经间接利用了 Validation 信息,所以还需要 Test 做最后独立评价。

  2. Overfitting,过拟合,典型现象是:

\[ Train\ Loss\downarrow \]

但是到某个阶段:

\[ Validation\ Loss\uparrow \]

模型越来越适合训练数据,却越来越不适合新数据。

  1. Underfitting,欠拟合,就是连训练集本身都没有学好。通常表现为 Train Loss 和 Validation Loss 都比较高。

  2. Robot Dataset 如果随机按 Frame 划分,容易出现:

Episode 7 Frame 100 → Train
Episode 7 Frame 101 → Test

而相邻帧几乎一样。这样 Test 看似“没参与训练”,实际上模型已经见过几乎完全相同的数据,造成 Data Leakage。

  1. Episode-level split 更合理,因为一整个 Episode 要么属于 Train,要么属于 Validation/Test,避免同一条轨迹的相邻帧跨集合泄漏。

  2. 即使按 Episode 划分,也不能自动说明泛化强。因为 Train 和 Test Episode 仍可能具有相同物体、相同背景、相同任务、相同初始位置。真正的泛化要看你究竟改变了什么条件。

  3. Test Loss 很低但 Rollout 仍可能失败,因为 Test Loss 是在专家 Dataset 提供的 Observation 上离线预测 Action;Rollout 时模型自己的 Action 会改变后续 Observation,因此会发生 Distribution Shift 和误差累积。

  4. model.eval() 和 torch.no_grad() 不一样。model.eval() 把 Module 切换到评估模式,影响 Dropout、BatchNorm 等层的行为;torch.no_grad() 是关闭梯度跟踪,减少显存和计算开销。二者经常一起用,但作用完全不同。

  5. Validation 阶段如果执行普通的:

val_loss.backward()optimizer.step()

就等于让 Validation 数据参与 Parameter Update,它就失去了“未参与训练数据”的意义。

  1. 如果 Epoch 60 的 Validation 最好,而 Epoch 100 只是 Train Loss 更低,通常更倾向保存 Epoch 60。因为我们关心的是泛化,而不是单纯把 Train Set 拟合到最低。

VLA 系统学习第 11 课:机器人 Dataset 到底应该怎么划?——从 Episode Split 到真正的 Generalization

上一课我们知道:

\[ Train\neq Validation\neq Test \]

但在普通图片分类里,“随机把图片分成 80% / 10% / 10%”有时还能工作。

Robot Learning 没这么简单。

因为机器人数据不是彼此独立的一堆图片,而是一条条具有强时间相关性的轨迹:

\[ \text{Episode} = (o_0,a_0,o_1,a_1,\ldots,o_T,a_T) \]

所以真正的问题不是:

“Train 占多少比例?”

而是:

什么东西允许同时出现在 Train 和 Test?什么东西必须彻底隔离?

这会直接决定你最后测出来的结果到底有没有意义。


一、为什么 Frame-level Random Split 很危险?

假设我们有一个抓取 Episode:

Frame 100
Frame 101
Frame 102
Frame 103

控制频率比较高,因此相邻两帧可能只相隔:

\[ 0.05s \]

于是:

\[ I_{100} \]

和:

\[ I_{101} \]

几乎一样。

Robot State 也可能只是:

\[ q_{101}=q_{100}+\Delta q \]

其中 \(\Delta q\) 非常小。

现在如果把所有 Frame 打乱:

random.shuffle(all_frames)

然后随机切分,可能得到:

Frame 100 → Train
Frame 101 → Test
Frame 102 → Train

模型在 Test 看到 Frame 101 时,实际上已经在 Train 中见过几乎一样的 Frame 100 和 102。

于是 Test Loss 很漂亮。

但它真正证明的可能只是:

模型会对已经见过附近状态进行插值。

它并没有证明:

模型能面对一条全新的任务轨迹。

所以这里产生:

\[ \boxed{Data\ Leakage} \]

数据泄漏。


二、Episode-level Split 如何解决这个问题?

更合理的一种做法是:

假设:

\[ 100\ Episodes \]

直接按 Episode 编号划分:

\[ Episode\ 0\sim79\rightarrow Train \]\[ Episode\ 80\sim89\rightarrow Validation \]\[ Episode\ 90\sim99\rightarrow Test \]

那么:

Episode 91 Frame 0
Episode 91 Frame 1
...
Episode 91 Frame 200

全部属于 Test。

Train 中不会出现 Episode 91 的前一帧、后一帧。

这样测试变成:

给模型一条训练阶段没有出现过的新轨迹,它还能不能预测合理 Action?

这比随机 Frame split 严格得多。

因此在 Robot Learning 中,一看到 Dataset Split,我们就应该先问:

\[ \boxed{ Split\ unit到底是什么? } \]

是:

\[ Frame? \]

还是:

\[ Episode? \]

这个问题往往比“80/20 还是 90/10”重要得多。


三、但 Episode-level Split 仍然只是第一层泛化

假设所有 100 个 Episode 都这样采集:

同一个桌子
同一个红色方块
同一个目标盒
方块几乎总在同一位置
相机位置不变
任务始终相同

你确实按 Episode 划分了:

\[ 80/10/10 \]

但是 Test Episode 和 Train Episode 的实际条件仍然极其相似。

于是模型 Test 表现很好,只能说明:

它对“同类条件下的新轨迹”有一定泛化能力。

却不能说明它能处理:

新物体
新位置
新场景
新任务
新语言

所以:

\[ \text{Generalization} \]

不是只有“泛化/不泛化”两个状态。

我们必须明确:

到底对什么东西泛化?


四、从 Episode Generalization 继续往外扩

假设机器人任务是:

把物体拿起来放进容器。

我们可以设计不同难度的 Test。

最简单的测试:

Train:
Episode 0~79

Test:
Episode 80~99

物体、场景、任务都一样,只是具体轨迹不同。

这是比较弱的一层泛化。

再严格一点,可以让:

\[ Train \]

中的物体位置主要在:

\[ x\in[-0.1,0.1] \]

而 Test 使用一些没有见过的初始位置。

那么在问:

Policy 能不能泛化到新的初始状态?

再进一步:

Train:
红色方块
蓝色方块

Test:
绿色方块

这时测试的是:

新物体条件下是否有效。

再比如:

Train:
桌面场景 A

Test:
桌面场景 B

是在测 Scene Generalization。

如果进一步:

Train:
pick up the red block

Test:
put the block into the box

那任务本身都发生变化,测试难度又不同。

所以以后看到论文写:

“Our model generalizes well.”

第一反应不能是:

好厉害。

而应该问:

\[ \boxed{ Generalize\ to\ what? } \]


五、Seen 和 Unseen 到底是什么意思?

以后论文和 Benchmark 中会频繁看到:

Seen

和:

Unseen

先从最简单例子理解。

如果训练数据里出现过:

red block

测试还是:

red block

可以称某种意义上的:

Seen Object

如果训练数据完全没有:

green mug

测试第一次出现:

green mug

那么可能称:

Unseen Object

但“Unseen”一定要看论文具体定义。

因为它可能表示:

  • unseen object;
  • unseen scene;
  • unseen instruction;
  • unseen task;
  • unseen environment configuration。

不能看到:

unseen

就自己默认是“模型从未见过这个任务”。


六、现在引入 IID:训练和测试来自“同一种数据规律”

这里需要一个以后非常常见的概念:

IID

全称:

Independent and Identically Distributed

先不深挖概率论。

当前只抓住其中最重要的:

Identically Distributed

同分布。

可以粗略理解:

Train 和 Test 虽然不是同一批数据,但来自大致相同的数据生成规律。

例如:

Train:
物体位置在桌面上随机采样

Test:
物体位置仍按同样规则随机采样

具体位置不一样。

但是数据生成方式差不多。

这就比较接近:

\[ IID\ Evaluation \]

它回答的是:

在和训练环境相似的新样本上,模型怎么样?


七、OOD 又是什么?

OOD:

Out-of-Distribution

分布外。

意思是:

Test 数据在某些关键方面已经超出了训练数据主要覆盖的范围。

例如 Train:

\[ x\in[-0.1,0.1] \]

而 Test:

\[ x\in[0.3,0.4] \]

模型训练阶段从来没见过这么偏的位置。

或者:

Train:
方块

Test:
杯子

再或者:

Train:
白色背景

Test:
复杂厨房背景

这些都可能形成某种 OOD 情况。

所以:

\[ IID \]

更像:

同类条件的新数据。

而:

\[ OOD \]

更像:

数据条件本身发生了明显变化。


八、为什么不能简单说“OOD 一定比 IID 高级”?

因为 OOD 有很多不同种类。

比如:

新颜色

可能很容易。

而:

新任务逻辑

可能非常难。

所以不能只看:

OOD

三个字母。

必须继续问:

\[ \boxed{ 到底哪个Distribution发生了变化? } \]

例如:

Object OOD
Scene OOD
Task OOD
Language OOD
Pose OOD

这些难度和意义完全不同。


九、Dataset Split 实际上是在定义“考试题”

可以用考试类比。

训练集:

平时给你的练习题。

Validation:

模拟考试。

Test:

最终考试。

但真正决定考试难度的,并不是:

考试有多少道题。

而是:

最终考试和练习题到底有多像。

例如 Train:

1 + 1
2 + 2
3 + 3

Test:

4 + 4

属于同一种规律。

但如果 Test 突然变成:

求微积分

就是完全不同的 Generalization。

Robot Dataset 也是一样。

所以评估一个模型之前,必须先看:

\[ \boxed{Split\ Protocol} \]

也就是:

数据到底按照什么规则被划成 Train / Validation / Test。


十、为什么论文之间的 Success Rate 不能只看数字?

假设论文 A:

\[ Success\ Rate=95\% \]

论文 B:

\[ Success\ Rate=82\% \]

不能立刻说:

A 比 B 强。

因为 A 可能测试的是:

Seen Object
Seen Scene
Seen Task
New Episode

而 B 测试:

Unseen Object
Unseen Scene

后者显然更困难。

甚至两篇论文可能使用完全不同:

  • Dataset;
  • Robot;
  • Task;
  • Split;
  • Evaluation Protocol。

所以以后比较 Robot Learning / VLA 模型必须同时看:

\[ \boxed{ Metric + Dataset + Split + Evaluation\ Protocol } \]

单独一个 Success Rate 没有足够意义。


十一、代码层面 Dataset Split 会在哪里出现?

真实代码里可能看到:

train_dataset = ...val_dataset = ...test_dataset = ...

也可能看到:

train_episodes = [...]val_episodes = [...]

或者配置:

train_episodes:
  - ...

val_episodes:
  - ...

再或者 Dataset 类内部通过:

split="train"

加载不同集合。

所以以后进入真实仓库时,不能只找到:

Dataset

然后就结束。

还要继续问:

Dataset 的 Train / Val / Test 到底在哪里定义?

以及:

是按 Frame、Episode、Task 还是其他单位切分?

这将成为我们以后代码审读的固定检查项。


十二、把这一课重新接回模型训练

现在完整流程已经不是简单:

\[ Dataset \rightarrow Training \]

而应该变成:

\[ Dataset \rightarrow Split\ Protocol \rightarrow \begin{cases} Train\\ Validation\\ Test \end{cases} \]

Train:

\[ Observation \rightarrow Policy \rightarrow Loss \rightarrow Backward \rightarrow Update \]

Validation:

\[ Observation \rightarrow Policy \rightarrow Metric \]

但:

\[ \text{No Parameter Update} \]

Test:

\[ \text{Final Evaluation} \]

最终如果是 Robot Policy,还要继续:

\[ Checkpoint \rightarrow Rollout \rightarrow Task\ Success \]

所以真正意义上的模型评价链现在变成:

\[ \boxed{ Train\ Loss \rightarrow Validation \rightarrow Test \rightarrow Rollout } \]

越往后,越接近真正任务表现。


第十一课自测

  1. 为什么 Robot Dataset 随机按 Frame 划分 Train/Test 特别容易造成 Data Leakage?

  2. Episode-level split 解决了什么问题?

  3. 为什么 Episode-level split 做完以后仍然不能自动宣称模型“泛化很好”?

  4. Seen Object 和 Unseen Object 的核心区别是什么?

  5. IID Test 和 OOD Test 可以分别怎样直观理解?

  6. 如果 Train 中物体位置始终:

\[ x\in[-0.1,0.1] \]

Test 使用:

\[ x\in[0.35,0.45] \]

这更接近 IID 还是 OOD?为什么?

  1. 为什么论文 A 的 Success Rate = 95%,不能仅凭这个数字认为它一定强于 Success Rate = 85% 的论文 B?

  2. 以后阅读 Robot Learning Repository 时,除了 Dataset Class 本身,还必须继续检查哪个信息?


下一课:第 12 课——为什么机器人不能只看“一帧”?从时间序列到 Observation History 和 Action Chunk

这一课之后我们就会明显向 ACT 靠近。

因为现在的数据我们一直写成:

\[ (o_t,a_t) \]

也就是:

看当前 Observation,预测当前 Action。

但真实动作具有时间连续性。

下一课我们会自然把它扩展成:

\[ [o_{t-k},\ldots,o_t] \]

以及:

\[ [a_t,a_{t+1},\ldots,a_{t+K-1}] \]

真正讲清:

  • 为什么需要时间信息;
  • Observation History 是什么;
  • Sequence Tensor 为什么会出现;
  • 为什么 Shape 开始变成 \[ [B,T,D] \]
  • 单步 Action 和 Action Sequence 有什么区别;
  • 为什么“一次预测一串动作”会自然导向 Action Chunking。

这一步完成后,ACT 里的第一个核心词 Action Chunking 就不会再是突然冒出来的名词;之后我们就可以开始为 Attention / Transformer 做最后一段必要准备。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐