第十一课标准答案
上一课真正要建立的是一个判断习惯:
看到 Robot Learning / VLA 的测试结果,先不要急着看 Success Rate,先看它到底怎么切数据、测试了什么"没见过的东西"。
1. 为什么 Robot Dataset 随机按 Frame 划分特别容易造成 Data Leakage?
因为同一个 Episode 中相邻 Frame 高度相关。
例如:
Episode 7
Frame 100 → Train
Frame 101 → Test
Frame 102 → Train
假设控制频率很高,那么 Frame 100 和 Frame 101 可能只相隔几十毫秒。
它们的:
- 图像;
- Robot State;
- Action;
都可能几乎一样。
于是虽然:
\ Frame_{101} \\
名义上属于 Test,但模型训练时已经看过几乎完全相同的:
\ Frame_{100},Frame_{102} \\
这样得到的 Test Performance 会被高估。
这就是一种典型的:
\ \\boxed{\\text{Data Leakage}} \\
2. Episode-level split 解决了什么?
Episode-level split 会把一整次任务轨迹作为最小划分单位。
例如:
\ Episode_{0\\sim79}\\rightarrow Train \\\ Episode_{80\\sim89}\\rightarrow Validation \\\ Episode_{90\\sim99}\\rightarrow Test \\
这样:
一个 Episode 中的相邻 Frame 不会一部分进入 Train、一部分进入 Test。
因此至少解决了:
\ \\boxed{\\text{同一条轨迹内部的时间泄漏}} \\
问题。
3. 为什么按 Episode 划分后仍不能自动说"泛化很好"?
因为不同 Episode 虽然是不同的轨迹,但实验条件可能几乎完全一样。
例如所有 Episode 都是:
同一个物体
同一个背景
同一个任务
同一个相机
相近的初始位置
那么 Test 只是:
在相似条件下重新做一遍任务。
它并没有证明模型能够处理:
- 新物体;
- 新场景;
- 新任务;
- 新语言;
- 新位置分布。
所以必须继续问:
\ \\boxed{\\text{Generalize to what?}} \\
4. Seen Object 和 Unseen Object 有什么区别?
最直接理解:
如果某个物体在训练数据中出现过,那么测试时再次出现,可以称为:
Seen Object
如果训练阶段完全没有出现,而只在测试阶段第一次出现,则可以称为:
Unseen Object
但以后看到论文写 unseen,不能自动理解成"整个任务都没见过"。
必须看作者具体定义的是:
- unseen object;
- unseen task;
- unseen scene;
- unseen instruction;
- 还是其他东西。
5. IID Test 和 OOD Test 怎么直观理解?
IID 可以先理解成:
考试题是新题,但仍然来自和平时练习差不多的出题规律。
例如训练:
\ x\\sim\[-0.1,0.1 \]
测试也从差不多的范围随机采样。
而 OOD 可以理解成:
考试时出现了训练数据没有覆盖好的情况。
例如训练:
\ x\\in\[-0.1,0.1 \]
测试突然变成:
\ x\\in\[0.3,0.4 \]
数据条件发生明显变化。
6. Train 中
\ x\\in\[-0.1,0.1 \]
Test 中:
\ x\\in\[0.35,0.45 \]
更接近什么?
更接近:
\ \\boxed{\\text{OOD}} \\
因为测试位置明显超出了训练主要覆盖范围。
模型需要对训练分布之外的位置进行泛化。
7. 为什么 95% Success Rate 不一定强于 85%?
因为两篇论文可能根本不是在考同一种考试。
例如论文 A:
Seen Object
Seen Scene
Seen Task
成功率:
\ 95\\% \\
论文 B:
Unseen Object
Unseen Scene
成功率:
\ 85\\% \\
B 的测试可能明显更困难。
所以模型比较不能只看:
\ Success\\ Rate \\
还必须一起看:
\ \\boxed{ Dataset + Split + Task + Evaluation\\ Protocol + Metric } \\
8. 阅读 Robot Learning Repository,除了 Dataset Class 还必须检查什么?
至少继续找:
\ \\boxed{\\text{Dataset Split Protocol}} \\
也就是:
Train / Validation / Test 到底按照什么单位划分?
重点看:
- Frame-level?
- Episode-level?
- Object-level?
- Task-level?
- Scene-level?
以及 Test 中哪些条件属于:
- Seen;
- Unseen;
- IID;
- OOD。
这直接决定最终评估结果到底在证明什么。
VLA 系统学习第 12 课:为什么机器人不能只看一帧?------时间序列、Observation History 与 Action Chunk
到目前为止,我们为了把 Behavior Cloning 讲清楚,一直把 Policy 写成:
\ a_t=\\pi(o_t) \\
意思是:
给我当前时刻 \(t\) 的 Observation,我预测当前时刻应该执行的一个 Action。
这个形式很重要,但它把真实机器人中的两个时间问题隐藏掉了。
第一个问题:
只看当前这一帧,真的足够判断机器人现在在干什么吗?
第二个问题:
Policy 为什么一定要每次只预测一个 Action?能不能一次预测未来一串动作?
这两个问题分别会引出:
\ \\boxed{\\text{Observation History}} \\
以及:
\ \\boxed{\\text{Action Chunk}} \\
而 Action Chunk 正是后面 ACT 的核心入口之一。
一、先理解:一张静态图片经常缺少"运动信息"
假设你只看到一张照片:
一个机械臂夹爪在物体左侧
你知道夹爪现在在哪里。
但你未必知道:
它正在往右靠近物体?
还是:
它刚刚从右边往左退回来?
单独看:
\ I_t \\
有时候两种情况看起来几乎一样。
但如果同时看到:
\ I_{t-2},I_{t-1},I_t \\
你就可能发现:
前两帧夹爪更靠左
现在逐渐向右
于是知道:
它正在向右运动。
这就是时间序列信息的价值。
二、Robot State 也有同样的问题
假设当前关节位置:
\ q_t=0.5 \\
只看:
\ q_t \\
你只知道:
当前关节在 0.5。
但不知道:
它正在向正方向运动,还是负方向运动。
如果前一时刻:
\ q_{t-1}=0.4 \\
那么:
\ 0.4\\rightarrow0.5 \\
说明它最近在增大。
如果:
\ q_{t-1}=0.6 \\
那么:
\ 0.6\\rightarrow0.5 \\
说明它最近在减小。
所以:
\ q_t \\
只是位置,
而:
\ (q_{t-1},q_t) \\
还隐含了运动趋势。
三、这就是 Observation History
以前我们使用:
\ o_t \\
现在把过去若干个 Observation 一起给模型:
\ \[o_{t-k+1},\\ldots,o_{t-1},o_t \]
这就是:
Observation History
也可以理解成:
当前时刻之前的一小段历史窗口。
例如 History Length:
\ T_o=3 \\
那么一个输入可能是:
\ \[o_{t-2},o_{t-1},o_t \]
Policy 就变成:
\ a_t = \\pi( o_{t-2}, o_{t-1}, o_t ) \\
而不是:
\ a_t=\\pi(o_t) \\
四、这时候 Tensor 为什么突然多了一个维度?
以前一个 Robot State:
\ s_t:\[D_s \]
例如:
\ D_s=10 \\
那么:
\ s_t:\[10 \]
如果现在给过去 4 个时刻:
\ s_{t-3},s_{t-2},s_{t-1},s_t \\
把它们堆在一起:
\ \[4,10 \]
第一个维度:
\ 4 \\
就是:
Time / Sequence Length
通常用:
\ T \\
表示。
于是:
\ \[T,D_s \]
加入 Batch:
\ \\boxed{ \[B,T,D_s } \]
例如:
\ \[32,4,10 \]
表示:
- 32 个 Sample;
- 每个 Sample 有连续 4 个时间点;
- 每个时间点的 Robot State 是 10 维。
五、一定要区分 \(B\)、\(T\)、\(D\)
以后 Transformer、ACT 中会频繁看到:
\ \[B,T,D \]
这三个维度必须形成条件反射。
\ B \\
是:
Batch Size
表示:
一次训练多少个样本。
\ T \\
是:
Sequence Length / Time Length
表示:
每个样本内部有多少个时间位置。
\ D \\
是:
Feature Dimension
表示:
每一个时间位置用多少个数字表示。
所以:
\ \[32,4,10 \]
绝对不能读成:
32×4×10 的三维数组。
虽然数学上没错,但这没有理解数据语义。
你应该读成:
32 个训练样本,每个样本包含 4 个连续时间步,每个时间步有 10 维特征。
这就是后面读 Transformer Tensor 的基本方式。
六、图像有时间维以后 Shape 怎么变化?
原来一张 RGB 图:
\ \[3,H,W \]
一个 Batch:
\ \[B,3,H,W \]
如果每个 Sample 现在包含 \(T\) 张连续图片:
\ \\boxed{ \[B,T,3,H,W } \]
例如:
\ \[32,4,3,224,224 \]
解释:
32个Sample
↓
每个Sample 4个时间点
↓
每个时间点1张RGB图片
↓
3个颜色通道
↓
224×224
这就是 Observation History 的图像版本。
七、但"历史帧"不是越多越好吗?
不是。
假设使用:
\ T=2 \\
模型只看很短历史。
优点:
- 数据量小;
- 计算量低;
- 延迟小。
但可能缺少更长时间信息。
如果:
\ T=100 \\
模型看到的历史很多,
但:
- Tensor 更大;
- 显存更高;
- 计算更慢;
- 很久以前的信息可能没有价值。
所以:
\ T \\
是一个设计参数。
不是:
越大越先进。
八、到这里我们解决的是"输入看多久"
也就是:
\ \\boxed{\\text{Observation Horizon}} \\
可以粗略理解:
Policy 决策时考虑多少历史 Observation。
例如:
\ T_o=4 \\
表示使用:
\ o_{t-3:t} \\
也就是最近 4 个 Observation。
但是这里又出现第二个时间问题:
输出为什么只能是一个 Action?
九、以前我们一次只预测一个 Action
最基础 BC:
\ o_t \\rightarrow a_t \\
模型输入当前 Observation:
\ o_t \\
输出:
\ a_t \\
然后下一时刻:
\ o_{t+1} \\rightarrow a_{t+1} \\
所以运行:
Observe
↓
Predict one Action
↓
Execute
↓
Observe
↓
Predict one Action
↓
Execute
...
这叫:
single-step action prediction
单步动作预测。
十、单步动作预测有什么问题?
假设一个抓取动作需要连续:
向前
向前
向下
向下
关闭夹爪
向上
如果模型每一步都完全重新预测:
第1帧 → Action1
第2帧 → Action2
第3帧 → Action3
...
那么相邻 Action 之间可能不够一致。
例如:
Action t:
向右 1 cm
Action t+1:
突然向左 0.8 cm
Action t+2:
又向右 1.2 cm
就可能表现为:
动作抖动、不连贯。
尤其机器人控制本身具有明显的时间连续性。
因此一个自然想法是:
能不能一次预测未来一小段连续动作?
答案是可以。
十一、于是从一个 Action 变成一串 Action
原来:
\ a_t \\
现在预测:
\ A_t= \[ a_t, a_{t+1}, a_{t+2}, \\ldots, a_{t+K-1} \]
这里:
\ K \\
表示一次预测多少个 Action。
这串 Action 就可以称为:
Action Chunk
动作块。
如果:
\ K=4 \\
那么模型一次预测:
\ \[a_t,a_{t+1},a_{t+2},a_{t+3} \]
而不是只预测:
\ a_t \\
十二、Action Chunk 的 Shape 怎么来?
假设单个 Action:
\ a_t:\[D_a \]
例如:
\ D_a=7 \\
以前一个 Sample 输出:
\ \[7 \]
现在预测未来:
\ K=16 \\
个 Action。
那么:
\ A_t:\[16,7 \]
加入 Batch:
\ \\boxed{ A:\[B,K,D_a } \]
例如:
\ \[32,16,7 \]
应该读成:
32 个 Sample,每个 Sample 预测未来 16 个 Action,每个 Action 有 7 个维度。
十三、这里的 \(T\) 和 \(K\) 不一定一样
这是一个很重要的点。
输入可能看:
\ T_o=2 \\
个 Observation。
输出却可能预测:
\ K=16 \\
个 Action。
于是:
\ Observation: \[B,2,D_o \]
而:
\ Action: \[B,16,D_a \]
所以:
输入历史长度和输出动作长度是两个独立概念。
不要看到两个时间维就默认它们一样。
十四、为什么 Action Chunk 可能让动作更连贯?
因为模型不是把每一步当成完全孤立的问题。
它直接学习一段:
\ \[a_t,a_{t+1},\\ldots,a_{t+K-1} \]
这意味着:
模型可以同时考虑这一小段动作之间的整体关系。
例如一次抓取动作中:
靠近
↓
下降
↓
闭合
↓
抬起
这些动作在 Demonstration 中本来就是连续发生的。
如果模型一起预测,它有机会学习:
这一小段动作应该作为一个整体保持协调。
这就是 Action Chunking 非常重要的直觉。
十五、但预测 16 个 Action,是不是一定全部执行?
不一定。
这里必须区分两个概念:
\ \\boxed{\\text{Prediction Horizon}} \\
和:
\ \\boxed{\\text{Execution Horizon}} \\
假设模型一次预测:
\ 16 \\
步:
\ \[a_t,\\ldots,a_{t+15} \]
并不意味着机器人必须一口气把 16 步全部执行完。
它可以只执行前:
\ 4 \\
步。
然后重新获取 Observation,再预测新的 Action Chunk。
于是:
Observation at t
↓
Predict 16 actions
↓
Execute first 4
↓
New Observation
↓
Predict another 16
↓
Execute first 4
↓
...
这个思想以后在:
- Diffusion Policy;
- Receding Horizon Control;
中都会非常重要。
十六、为什么不直接把整个 Chunk 全执行完?
假设:
\ K=100 \\
模型预测未来 100 个 Action。
如果全部盲目执行:
Action 1
Action 2
...
Action 100
中间环境稍微变化,
后面的动作可能已经不适合当前状态。
所以一个自然策略是:
预测得长一点,但只执行其中一部分,然后重新观察。
这样兼顾:
- 动作连续性;
- 闭环反馈。
十七、现在出现三个非常容易混淆的 Horizon
以后尤其学 Diffusion Policy 时必须分清。
Observation Horizon
\ T_o \\
表示:
输入看多少历史 Observation。
Prediction Horizon
\ T_p \\
表示:
模型一次预测多长的未来 Action Sequence。
Action / Execution Horizon
\ T_a \\
表示:
预测出来以后,真正执行其中多少步再重新规划。
例如:
\ T_o=2 \\\ T_p=16 \\\ T_a=8 \\
表示:
看过去2个Observation
↓
预测未来16个Action
↓
实际执行前8个
↓
重新观察
↓
再次预测
不过不同论文对命名可能略有不同。
所以以后不能只看到 horizon 就自己默认含义,必须检查作者定义和代码。
十八、ACT 的 Action Chunking 为什么叫"Chunking"?
现在终于可以第一次真正理解 ACT 这个名字中的一个核心部分。
ACT:
Action Chunking with Transformers
先只看:
Action Chunking
它强调的就是:
Policy 不再只预测单个 Action,而是一次预测一段未来动作。
形式上:
\ o_t \\rightarrow \[a_t,a_{t+1},\\ldots,a_{t+K-1} \]
所以 ACT 并不是突然发明一个玄学概念。
它来自一个非常自然的问题:
单步预测机器人动作容易受到时序不稳定和高频决策等问题影响,那么能不能一次学习一段动作?
答案就是:
Action Chunking
十九、但是这里有一个很重要的问题:训练数据怎么得到未来 Action Chunk?
假设 Dataset 中原本存的是:
\ a_0,a_1,a_2,\\ldots,a_T \\
现在训练样本在:
\ t=20 \\
我们希望预测:
\ K=4 \\
个 Action。
那么 Target 就可以从 Dataset 中切:
\ \[a_{20},a_{21},a_{22},a_{23} \]
所以原本:
\ (o_t,a_t) \\
这个 Sample 定义变成:
\ \\boxed{ (o_t, \[a_t,a_{t+1},\\ldots,a_{t+K-1}) } \]
这就是为什么我们前面一直强调:
Dataset 中的时间结构非常重要。
Action Chunk 不是模型凭空创造未来监督信号。
这些 Future Actions 原本就在 Demonstration Trajectory 里。
二十、这时 __getitem__() 的职责也变了
最简单 BC Dataset:
def __getitem__(self, index): obs = ... action = ... return obs, action
Target 只有:
\ a_t \\
但 Action Chunk Dataset 的逻辑可能更接近:
def __getitem__(self, index): obs = ... action_chunk = ... return obs, action_chunk
其中:
\ action\\_chunk = \[a_t,\\ldots,a_{t+K-1} \]
所以现在你应该重新理解:
__getitem__()不只是"读取第几个文件"。
它还可能决定:
一个训练 Sample 的时间窗口到底怎么切。
这就是代码阅读为什么必须看 Dataset 实现。
二十一、Episode 结尾会发生什么?
假设 Episode 总共只有:
\ 100 \\
步。
现在:
\ t=98 \\
而我们要求:
\ K=8 \\
未来需要:
\ a_{98},a_{99},a_{100},\\ldots \\
但 Episode 已经结束。
数据不够。
于是这里才自然出现我们之前暂时没展开的:
Padding
例如有效 Action 只有:
\ a_{98},a_{99} \\
剩下位置需要补齐到固定长度:
\ K=8 \\
可能得到:
\ \[a_{98},a_{99},PAD,PAD,PAD,PAD,PAD,PAD \]
二十二、为什么 Padding 后还需要 Mask?
模型必须知道:
哪些是真实 Action?
哪些只是为了凑 Shape 补出来的?
于是可能有:
\ mask= \[1,1,0,0,0,0,0,0 \]
其中可以约定:
\ 1 \\
表示有效,
\ 0 \\
表示 Padding。
那么计算 Loss 时:
Padding 的那些位置不应该当成真实 Action 参与训练。
否则模型会被迫学习:
"Episode 结束以后应该预测 PAD 伪数据。"
这是错误的。
所以:
\ \\boxed{ Padding \\rightarrow 通常需要配套Mask } \\
这里 Padding 和 Mask 出现就很自然了------因为现在确实遇到了变长时间序列问题。
二十三、现在看一次完整 Tensor
假设:
\ B=32 \\
Observation History:
\ T_o=3 \\
Robot State Dimension:
\ D_s=10 \\
那么:
\ state:\[32,3,10 \]
如果图像也保留时间:
\ image:\[32,3,3,H,W \]
注意这里出现两个 3:
第一个:
\ 3=T_o \\
表示 3 个时间点。
第二个:
\ 3=C \\
表示 RGB 三个 Channel。
所以必须根据位置和语义区分。
Action Chunk:
\ K=16 \\
Action Dimension:
\ D_a=7 \\
于是:
\ action:\[32,16,7 \]
这就是一个典型时间序列 Robot Learning Sample 在 Batch 后可能出现的形态。
二十四、为什么这个 [B,T,D] 会直接把我们带向 Transformer?
现在出现了一个新问题。
以前:
\ \[B,D \]
每个 Sample 只是一个向量。
现在:
\ \[B,T,D \]
每个 Sample 内部变成了一串:
\ x_1,x_2,\\ldots,x_T \\
也就是:
Sequence
序列。
例如:
\ \[x_{t-2},x_{t-1},x_t \]
或者 Action Query:
\ \[q_1,q_2,\\ldots,q_K \]
那么模型需要理解:
序列中不同位置之间是什么关系?
这正是 Attention / Transformer 擅长处理的问题。
所以 Transformer 并不是突然切换到另一个主题。
它是从:
\ \\boxed{ Robot\\ Data开始具有Sequence结构 } \\
自然出现的。
二十五、ACT 的问题已经逐渐显现出来了
现在我们已经能提出 ACT 要解决的一部分问题:
问题 1
Robot Manipulation 是时间连续的。
问题 2
单步 Action Prediction 可能缺少一整段动作协调性。
问题 3
Demonstration 本身就是:
\ a_0,a_1,\\ldots,a_T \\
这样的 Sequence。
于是可以考虑:
\ Observation \\rightarrow Action\\ Sequence \\
也就是:
\ o_t \\rightarrow A_t \\
其中:
\ A_t:\[K,D_a \]
然后用能够处理 Sequence 的 Transformer 建模。
现在"Action Chunking with Transformers"这几个词已经第一次真正连起来了:
\ \\boxed{ Action\\ Chunk + Sequence\\ Modeling \\rightarrow Transformer } \\
但现在还不能直接进入 ACT 源码。
因为我们还缺一个关键基础:
Transformer 到底如何理解 Sequence 中不同位置之间的关系?
这就是 Attention。
第二十六、整章链路回看
这一课从最简单 BC:
\ o_t\\rightarrow a_t \\
进行了两个方向的扩展。
输入端:
\ o_t \\
扩展为:
\ \[o_{t-k+1},\\ldots,o_t \]
也就是:
\ \\boxed{\\text{Observation History}} \\
因此 Tensor 出现:
\ \[B,T,D \]
输出端:
\ a_t \\
扩展为:
\ \[a_t,a_{t+1},\\ldots,a_{t+K-1} \]
也就是:
\ \\boxed{\\text{Action Chunk}} \\
因此 Action Tensor 变成:
\ \[B,K,D_a \]
最终:
\ \\boxed{ Sequence\\ Input \\rightarrow Sequence\\ Modeling \\rightarrow Sequence\\ Output } \\
这就是我们接下来进入 Attention / Transformer 的直接原因。
第十二课自测
-
为什么只看当前一帧 \(o_t\) 有时候无法判断机器人当前的运动趋势?
-
什么叫 Observation History?
-
Tensor:
\ \[32,4,10 \]
中的 32、4、10 分别表示什么?
- 图像 Tensor:
\ \[16,3,3,224,224 \]
如果第一个 3 表示时间长度,那么第二个 3 表示什么?
-
什么叫 Single-step Action Prediction?
-
什么叫 Action Chunk?
-
如果:
\ B=32,\\quad K=20,\\quad D_a=7 \\
那么 Action Chunk Batch 的 Shape 是什么?
-
Observation Horizon 和 Prediction Horizon 为什么不是同一个东西?
-
为什么"预测 16 个 Action"不一定意味着"一口气执行完 16 个 Action"?
-
如果 Episode 只剩 3 个 Action,但模型要求固定预测 10 个 Action,为什么可能需要 Padding?
-
Padding 后为什么通常还需要 Mask?
-
为什么 Robot Data 一旦变成:
\ \[B,T,D \]
这种 Sequence 结构,就会自然把我们带向 Attention / Transformer?
下一课:第 13 课------Transformer 前最后一块真正必要的基础:Sequence、Token 和 Embedding
下一课我们暂时还不会直接写 Q/K/V。
因为如果现在直接写:
\ Q=XW_Q,\\qquad K=XW_K,\\qquad V=XW_V \\
你很可能会知道怎么算,却不知道这里的:
\ X \\
到底是什么。
所以第 13 课只解决这一件事:
Transformer 看到的到底不是"图片""语言""关节状态",而是一串 Token 表示。
我们会把:
\ \[B,T,D \]
进一步解释成:
\ \\boxed{ Batch \\times Token/Sequence \\times Embedding\\ Dimension } \\
并讲清:
- Sequence 和 Token 到底是什么关系;
- 一个数字为什么不能直接代表 Token;
- Embedding 到底是什么;
- Language Token 怎么变成向量;
- Robot State 怎么变成 Token;
- Image 为什么也能变成 Visual Token;
- 为什么 Transformer 最终可以统一处理语言、视觉、机器人状态;
- Position Information 为什么必须补进去。
这一步完成以后,第 14 课就可以正式进入:
\ \\boxed{ Q,\\ K,\\ V,\\ Attention } \\
然后再进入完整 Transformer Block。