第十二课标准答案
1. 为什么只看当前一帧 \(o_t\) 有时候无法判断运动趋势?
因为:
\[ o_t \]
只能描述“现在是什么样”,不一定包含“刚才是怎么变过来的”。
例如当前关节位置:
\[ q_t=0.5 \]
仅凭这一点无法判断它正在:
\[ 0.4\rightarrow0.5 \]
还是:
\[ 0.6\rightarrow0.5 \]
所以加入过去 Observation:
\[ [o_{t-1},o_t] \]
就能提供一定时间信息。
2. 什么叫 Observation History?
就是:
Policy 不只看当前 Observation,而是同时看过去若干个 Observation。
例如:
\[ T_o=3 \]
那么:
\[ [o_{t-2},o_{t-1},o_t] \]
就是一个长度为 3 的 Observation History。
3. Tensor
\[ [32,4,10] \]
分别是什么?
\[ 32=B \]
Batch Size,一次 32 个训练样本。
\[ 4=T \]
每个样本包含 4 个时间步。
\[ 10=D \]
每个时间步由 10 个特征描述。
所以应该读成:
32 个样本,每个样本有 4 个时间位置,每个时间位置是 10 维特征。
4. 图像 Tensor
\[ [16,3,3,224,224] \]
如果第一个 3 是时间长度,那么第二个 3 是什么?
第二个:
\[ 3 \]
表示 RGB Channel:
\[ R,G,B \]
所以:
\[ [B,T,C,H,W] \]
这里是:
\[ [16,3,3,224,224] \]
即:
16 个样本,每个样本有 3 帧,每帧 RGB 三通道,图片尺寸 224×224。
5. 什么叫 Single-step Action Prediction?
就是一次只预测:
\[ a_t \]
一个 Action。
形式:
\[ o_t\rightarrow a_t \]
执行后重新观察,再预测:
\[ o_{t+1}\rightarrow a_{t+1} \]
6. 什么叫 Action Chunk?
不是只预测一个 Action,而是一次预测一串未来 Action:
\[ A_t= [a_t,a_{t+1},\ldots,a_{t+K-1}] \]
这一小段连续 Action 就叫:
Action Chunk
7. 如果
\[ B=32,\quad K=20,\quad D_a=7 \]
Action Chunk Shape 是什么?
\[ \boxed{[32,20,7]} \]
含义:
- 32 个 Sample;
- 每个 Sample 预测 20 个未来 Action;
- 每个 Action 是 7 维。
8. Observation Horizon 和 Prediction Horizon 为什么不是一个东西?
Observation Horizon 回答:
模型往过去看多久?
例如:
\[ T_o=3 \]
输入:
\[ [o_{t-2},o_{t-1},o_t] \]
Prediction Horizon 回答:
模型往未来预测多长?
例如:
\[ T_p=16 \]
输出:
\[ [a_t,a_{t+1},\ldots,a_{t+15}] \]
所以完全可以:
\[ T_o=2,\qquad T_p=16 \]
两者没有必须相等的关系。
9. 为什么预测 16 个 Action,不一定执行完 16 个?
因为真实环境会变化。
可以:
观察 ↓ 预测16步 ↓ 只执行前4步 ↓ 重新观察 ↓ 再预测16步这样既保留一定动作连续性,又保持闭环反馈。
10. Episode 只剩 3 个 Action,但要求固定 10 个怎么办?
因为模型通常希望固定 Shape:
\[ [K,D_a] \]
如果:
\[ K=10 \]
但真实只剩:
\[ 3 \]
个 Action,就可能需要用 Padding 补齐剩下 7 个位置。
11. Padding 后为什么还需要 Mask?
因为模型必须知道:
哪些是真数据,哪些只是为了凑长度补出来的。
例如:
\[ mask= [1,1,1,0,0,0,0,0,0,0] \]
前 3 个有效,后面 7 个是 Padding。
否则 Loss 可能错误地把 Padding 当成真实动作训练。
12. 为什么[B,T,D]会自然把我们带向 Transformer?
因为:
\[ T \]
代表一个Sequence。
模型现在不只处理单个向量,而是:
\[ x_1,x_2,\ldots,x_T \]
这样一串位置。
于是问题变成:
第 1 个位置和第 5 个位置有什么关系?
哪个时间步对当前决策更重要?
不同位置之间的信息应该怎样交互?
这正是 Attention / Transformer 最擅长处理的问题。
VLA 系统学习第 13 课:Transformer 到底看到什么?——Sequence、Token、Embedding 和 Position
上一课我们已经自然得到:
\[ [B,T,D] \]
也就是说每个 Sample 不再只是一个向量,而是一个 Sequence:
\[ x_1,x_2,\ldots,x_T \]
接下来真正进入 Transformer 前,必须先解决一个很容易被讲模糊的问题:
Transformer 里的“Token”到底是什么?
因为后面一上来就会出现:
\[ X\in\mathbb R^{B\times T\times D} \]
然后:
\[ Q=XW_Q \]
如果现在连 \(X\) 里面装的是什么都不清楚,Q/K/V 很容易沦为背公式。
一、Sequence 不等于 Language
很多人第一次接触 Transformer,是从语言模型开始的。
于是容易形成:
Token = 单词
这种印象。
但严格来说不是。
Token 更准确的理解是:
Transformer Sequence 中的一个“位置单位”。
语言里,一个 Token 可以来自:
"pick" "up" "the" "block"图像里,一个 Token 可以对应:
一块图像 Patch。
机器人状态里,一个 Token 可以对应:
某一个时间步经过编码后的 Robot State。
所以:
\[ \boxed{ Token不是某一种具体数据 } \]
而是:
Transformer 序列中的一个信息单元。
二、先看最熟悉的 Language Token
假设一句话:
pick up the red blockTokenizer 处理以后,可能得到若干 Token。
为了教学,假设得到:
["pick", "up", "the", "red", "block"]实际上现代 Tokenizer 往往还会进行 subword 切分,所以真实结果不一定正好等于单词。
然后每个 Token 会映射成一个 Token ID。
例如教学示意:
pick → 17 up → 52 the → 4 red → 91 block → 33于是:
\[ tokens= [17,52,4,91,33] \]
Shape:
\[ [T] \]
其中:
\[ T=5 \]
三、为什么不能直接把 Token ID 喂进 Transformer?
因为:
\[ 17 \]
和:
\[ 52 \]
只是编号。
不能理解成:
52 比 17 大,所以
"up"比"pick"多 35 个单位。
Token ID 没有这种连续数值意义。
它更像:
学生1号 学生17号 学生52号编号只是身份标识。
因此需要把:
\[ Token\ ID \]
转换成一个向量:
\[ Embedding \]
四、Embedding 到底是什么?
假设:
\[ D=4 \]
每个 Token 被表示成 4 维向量。
例如:
\[ 17 \rightarrow [0.2,-0.4,0.8,0.1] \]\[ 52 \rightarrow [-0.5,0.3,0.1,0.9] \]
那么一句 5 Token 的话:
\[ [17,52,4,91,33] \]
经过 Embedding 以后就从:
\[ [5] \]
变成:
\[ [5,4] \]
也就是:
\[ [T,D] \]
加入 Batch:
\[ \boxed{ [B,T,D] } \]
看到没有?
这正好和上一课的 Sequence Tensor 接上了。
五、代码里 Embedding 长什么样?
PyTorch:
import torchimport torch.nn as nnembedding = nn.Embedding( num_embeddings=100, embedding_dim=4)这里:
num_embeddings=100意思是:
我的词表中有 100 个不同 Token ID。
而:
embedding_dim=4表示:
每个 Token 最终变成一个 4 维向量。
现在输入:
token_ids = torch.tensor([ [17, 52, 4, 91, 33], [12, 8, 7, 31, 2]])Shape:
\[ [2,5] \]
也就是:
- Batch = 2;
- 每句话 5 个 Token。
执行:
x = embedding(token_ids)输出 Shape:
\[ [2,5,4] \]
所以代码链就是:
Token ID [B,T] ↓ nn.Embedding ↓ Token Embedding [B,T,D]六、nn.Embedding本质上里面是什么?
它内部可以先理解成有一张大表:
\[ E\in\mathbb R^{V\times D} \]
其中:
\[ V \]
是 Vocabulary Size,
\[ D \]
是 Embedding Dimension。
例如:
\[ E:[100,4] \]
每一行对应一个 Token 的向量。
如果 Token ID:
\[ 17 \]
就取:
\[ E_{17} \]
这一行。
因此:
embedding(token_ids)本质上是在:
根据 Token ID,从 Embedding Table 里查对应的向量。
所以 Embedding 很像:
\[ \boxed{ ID\rightarrow Vector } \]
七、Embedding 也是可训练 Parameter
这点很重要。
nn.Embedding(100, 4)内部的 Embedding Table 不是固定死的。
它也是:
\[ Parameter \]
训练时:
loss.backward()可以计算:
\[ \frac{\partial L}{\partial E} \]
Optimizer 再更新它。
所以最开始:
"red"对应的向量可能是随机的。
训练以后,模型会逐渐学习:
哪种向量表示更有利于当前任务。
这和我们前面学习:
\[ W,b \]
完全是一回事。
八、那 Robot State 怎么变成 Token?
语言有 Token ID,可以查 Embedding Table。
Robot State 不一样。
假设某个时间步:
\[ s_t\in\mathbb R^{10} \]
它本身已经是连续数值:
\[ [0.1,-0.3,0.8,\ldots] \]
所以通常不需要:
State → Token ID → Embedding Table更自然的做法是用 Linear Projection:
\[ s_t\in\mathbb R^{10} \]
映射到 Transformer 使用的:
\[ D=64 \]
维度:
\[ e_t=W_ss_t+b_s \]
于是:
\[ [10]\rightarrow[64] \]
这个:
\[ [64] \]
就可以作为一个 Robot State Token。
九、代码里 Robot State Token 怎么做?
例如:
state_encoder = nn.Linear(10, 64)现在 Batch 中有:
\[ state:[32,4,10] \]
其中:
- 32 个 Sample;
- 4 个时间步;
- 每个 State 10 维。
直接:
state_tokens = state_encoder(state)nn.Linear会作用在最后一个维度。
所以:
\[ [32,4,10] \rightarrow [32,4,64] \]
也就是:
\[ [B,T,D_s] \rightarrow [B,T,D_{model}] \]
现在每个时间步:
\[ [10] \]
变成:
\[ [64] \]
于是 Transformer 可以把它理解成:
长度为 4 的 Robot State Token Sequence。
十、为什么nn.Linear(10,64)能直接处理[32,4,10]?
这是非常重要的 PyTorch Shape 规则。
我们以前一直用:
\[ [32,10] \]
输入:
nn.Linear(10,64)得到:
\[ [32,64] \]
实际上nn.Linear主要要求:
输入最后一个维度必须等于
in_features。
所以:
nn.Linear(10, 64)可以处理:
\[ [32,4,10] \]
因为最后一维是:
\[ 10 \]
它会把每一个:
\[ [10] \]
向量分别变成:
\[ [64] \]
所以:
\[ [32,4,10] \rightarrow [32,4,64] \]
前面的:
\[ 32,4 \]
都保留。
以后这一点非常有用。
十一、Language 和 Robot State 为什么最后都能变成[B,T,D]?
语言:
Token IDs ↓ Embedding变成:
\[ [B,T_l,D] \]
Robot State:
Continuous State ↓ Linear Projection变成:
\[ [B,T_s,D] \]
关键在于:
虽然原始数据形式完全不同,但最后都被编码成 D 维 Token。
比如:
\[ D=64 \]
那么:
Language Token → [64] Robot State Token → [64]这就给后面的统一 Sequence Modeling 创造了条件。
十二、那 Image 怎么变成 Token?
现在是第三种模态。
一张图:
\[ I\in\mathbb R^{3\times224\times224} \]
Transformer 不适合直接把:
\[ 224\times224 \]
每个像素都当一个 Token。
一种常见思想是:
把图片切成很多小 Patch。
例如 Patch Size:
\[ 16\times16 \]
那么:
\[ 224/16=14 \]
横向 14 个 Patch,
纵向 14 个 Patch。
一共:
\[ 14\times14=196 \]
个 Patch。
于是图片变成:
\[ 196 \]
个视觉位置。
十三、一个 Patch 里面有多少原始数字?
一个 RGB Patch:
\[ 16\times16 \]
每个像素 3 个 Channel。
所以:
\[ 16\times16\times3=768 \]
个数字。
即一个 Patch 原始可以看成:
\[ [768] \]
然后再通过 Linear Projection:
\[ [768]\rightarrow[D] \]
例如:
\[ [768]\rightarrow[64] \]
于是每个 Patch 就变成一个:
\[ [64] \]
Visual Token。
最终:
\[ 196 \]
个 Patch:
\[ [196,64] \]
加入 Batch:
\[ [B,196,64] \]
这就是:
\[ [B,T,D] \]
结构。
只是这里:
\[ T=196 \]
不是时间步,而是:
Visual Patch Sequence Length。
十四、这里非常关键:\(T\) 不一定表示“时间”
上一课我们用:
\[ T \]
表示时间长度。
进入 Transformer 以后,更广义地应该理解成:
Sequence Length
例如 Language:
\[ T=句子Token数 \]
Robot History:
\[ T=时间步数量 \]
Image:
\[ T=Patch数量 \]
所以以后看到:
\[ [B,T,D] \]
不能机械翻译成:
Batch × Time × Dimension。
更准确的是:
\[ \boxed{ Batch \times Sequence \times Feature/Embedding } \]
至于 Sequence 到底是:
- 时间;
- 文字;
- Patch;
- Action Query;
要根据上下文判断。
十五、图片 Patch 化在代码里可以怎么理解?
教学上可以用 Conv2d 很方便地做 Patch Embedding:
patch_embed = nn.Conv2d( in_channels=3, out_channels=64, kernel_size=16, stride=16)输入:
\[ image:[B,3,224,224] \]
经过:
x = patch_embed(image)得到:
\[ [B,64,14,14] \]
为什么?
因为:
\[ 224/16=14 \]
每个 Patch 被映射成 64 维 Feature。
但 Transformer 想要:
\[ [B,T,D] \]
所以还需要把:
\[ 14\times14 \]
展平。
例如:
x = x.flatten(2)变成:
\[ [B,64,196] \]
再:
x = x.transpose(1, 2)变成:
\[ [B,196,64] \]
于是:
Image [B,3,224,224] ↓ Patch Embedding [B,64,14,14] ↓ Flatten [B,64,196] ↓ Transpose [B,196,64]现在:
\[ 196 \]
就是 Visual Token 数量,
\[ 64 \]
就是 Embedding Dimension。
十六、为什么这里要transpose(1,2)?
因为Conv2d输出习惯是:
\[ [B,C,H,W] \]
Patch Embedding 后:
\[ [B,D,H_p,W_p] \]
展平空间后:
\[ [B,D,T] \]
但是 Transformer 常见输入更希望:
\[ [B,T,D] \]
所以需要把第 1 和第 2 维互换:
\[ [B,D,T] \rightarrow [B,T,D] \]
代码:
x = x.transpose(1, 2)这就是 Tensor Shape 真正服务代码理解的地方。
十七、所以三种完全不同的数据,最后居然能统一
现在我们有:
Language
\[ [B,T_l,D] \]
Robot State
\[ [B,T_s,D] \]
Visual Tokens
\[ [B,T_v,D] \]
注意最后一维全部是:
\[ D \]
于是理论上可以进一步组织成统一 Token Sequence。
例如概念上:
[Visual Token 1] [Visual Token 2] ... [Language Token 1] [Language Token 2] ... [State Token 1] [State Token 2] ...最后形成:
\[ X\in\mathbb R^{B\times T_{total}\times D} \]
其中:
\[ T_{total} = T_v+T_l+T_s \]
实际模型到底是否这样直接拼接,要看具体架构,不能一概而论。
但是这个思想非常重要:
Transformer 不需要原始输入都是同一种模态,它需要的是这些模态最后都能变成某种 Token Representation。
十八、这就是 Multimodal Transformer 的一个基础思想
原始输入:
Image Language Robot State差异巨大。
但经过各自 Encoder / Projection:
Image ↓ Visual Tokens Language ↓ Language Tokens Robot State ↓ State Tokens最后都变成:
\[ D\text{维向量} \]
于是 Transformer 可以对它们进行统一的信息交互。
这也是为什么 VLA 中会频繁看到:
Vision Encoder Language Encoder State Projection Transformer Action Head十九、但是现在出现一个严重问题:Transformer 怎么知道顺序?
假设两个 Token Sequence:
\[ [A,B,C] \]
和:
\[ [C,B,A] \]
如果每个 Token 只有自己的 Embedding:
A → vector_A B → vector_B C → vector_C那么模型还缺一个信息:
A 是第 1 个还是第 3 个?
Sequence 的顺序没有自动写在向量里。
所以需要:
Position Information
位置编码。
二十、为什么 Robot Sequence 更需要 Position?
例如:
\[ [s_{t-2},s_{t-1},s_t] \]
如果模型不知道位置,那么:
最早的状态 当前状态 中间状态之间的时间顺序可能不明确。
但是对 Robot Policy 来说:
\[ s_{t-2}\rightarrow s_{t-1}\rightarrow s_t \]
和:
\[ s_t\rightarrow s_{t-1}\rightarrow s_{t-2} \]
含义完全不同。
所以必须告诉模型:
这是第几个位置。
二十一、最简单的位置 Embedding 思想
假设:
\[ X:[B,T,D] \]
我们准备:
\[ P:[T,D] \]
其中每个位置都有一个 Position Vector:
\[ p_1,p_2,\ldots,p_T \]
然后:
\[ X'=X+P \]
也就是说 Token 自己的内容:
\[ x_t \]
加上:
\[ p_t \]
得到:
\[ x'_t=x_t+p_t \]
于是一个 Token 同时包含:
我是谁? + 我在第几个位置?二十二、代码里最简单可以怎么写?
例如:
class SimpleSequenceEncoder(nn.Module): def __init__(self, input_dim, d_model, max_len): super().__init__() self.input_proj = nn.Linear(input_dim, d_model) self.pos_embedding = nn.Parameter( torch.randn(max_len, d_model) ) def forward(self, x): x = self.input_proj(x) T = x.shape[1] x = x + self.pos_embedding[:T] return x假设输入:
\[ x:[32,4,10] \]
第一步:
x = self.input_proj(x)得到:
\[ [32,4,64] \]
然后:
T = x.shape[1]此时:
\[ T=4 \]
取:
self.pos_embedding[:4]Shape:
\[ [4,64] \]
然后:
x = x + self.pos_embedding[:T]这里利用了 PyTorch Broadcasting:
\[ [32,4,64] + [4,64] \]
得到:
\[ [32,4,64] \]
每个 Batch 都加上同样的位置向量。
二十三、这里第一次自然遇到 Broadcasting
不用展开成 NumPy 大课。
这里只需要理解:
\[ [32,4,64] + [4,64] \]
PyTorch 会把:
\[ [4,64] \]
视为:
对 32 个 Batch 都使用同样的位置编码。
所以不用手工复制 32 份。
最终 Shape 仍然:
\[ [32,4,64] \]
二十四、Position Embedding 是固定的吗?
不一定。
常见方案很多,例如:
- fixed positional encoding;
- learned positional embedding;
- rotary position embedding;
- relative positional information。
现在不要往旁支扩。
我们当前只需要掌握核心原因:
\[ \boxed{ Transformer本身需要额外获得位置/顺序信息 } \]
具体模型采用哪种 Position Encoding,以后读真实代码再查。
二十五、Token 和 Embedding 最容易混淆在哪里?
必须严格区分:
Token
表示一个序列位置上的“信息单位”。
例如:
一个词片段 一个图像Patch 一个时间步StateToken ID
语言中经常存在。
例如:
\[ 17 \]
只是词表索引。
Embedding
Token 被编码后真正送入 Transformer 的向量:
\[ e_t\in\mathbb R^D \]
所以 Language 中:
\[ Token \rightarrow Token\ ID \rightarrow Embedding \]
而 Robot State 可能直接:
\[ Continuous\ State \rightarrow Linear\ Projection \rightarrow Embedding \]
不一定存在 Token ID。
这一点要分清。
二十六、现在终于可以解释后面 Attention 里的 \(X\)
以后会看到:
\[ Q=XW_Q \]\[ K=XW_K \]\[ V=XW_V \]
那么这里:
\[ X \]
不应该再是一个抽象字母。
它通常可以理解成:
一批已经变成统一 Embedding Dimension 的 Token Sequence。
例如:
\[ X:[B,T,D] \]
其中:
\[ B \]
Batch Size,
\[ T \]
Token / Sequence Length,
\[ D \]
Embedding Dimension。
例如:
\[ X:[32,16,64] \]
意思:
32 个 Sample,每个 Sample 有 16 个 Token,每个 Token 是 64 维向量。
下一课的 Q/K/V 全部从这里出发。
二十七、代码里一个最小“进入 Attention 前”的数据流
现在可以组合成:
class RobotTokenEncoder(nn.Module): def __init__(self): super().__init__() self.state_proj = nn.Linear(10, 64) self.pos_embedding = nn.Parameter( torch.randn(16, 64) ) def forward(self, state): x = self.state_proj(state) T = x.shape[1] x = x + self.pos_embedding[:T] return x假设:
state.shape是:
\[ [32,4,10] \]
经过:
x = self.state_proj(state)得到:
\[ [32,4,64] \]
再加 Position:
\[ [32,4,64] \]
Shape 不变。
于是:
x = encoder(state)最终:
\[ X:[32,4,64] \]
这已经是一个非常标准的 Transformer Sequence Representation。
下一步就可以:
\[ X \rightarrow Q,K,V \]
二十八、这一课和 ACT 已经有什么联系?
ACT 后面会处理:
- Robot State;
- Image Feature;
- Action Sequence;
- Transformer;
- Query;
- Position Embedding。
如果现在没有建立:
\[ [B,T,D] \]
以及:
\[ Token \rightarrow Embedding \]
和:
\[ Position \]
这些概念,
以后看到 ACT 代码中的:
query_embed pos_embed encoder decoder就很容易全部混成名词。
所以这一课真正是在给 ACT 搭最后的 Transformer 地基。
第十三课整章链路回看
上一课我们已经得到:
\[ Sequence \]
这一课进一步回答:
Sequence 中每个位置到底用什么形式进入 Transformer?
答案是:
\[ \boxed{ Token\ Representation } \]
语言:
\[ Token\ ID \rightarrow Embedding \]
Robot State:
\[ State \rightarrow Linear\ Projection \]
Image:
\[ Image \rightarrow Patch \rightarrow Visual\ Embedding \]
最终统一得到:
\[ X:[B,T,D] \]
再加入:
\[ Position\ Information \]
告诉模型:
每个 Token 在 Sequence 的哪个位置。
因此下一课终于可以正式开始:
\[ \boxed{ X \rightarrow Q,K,V \rightarrow Attention } \]
第十三课自测
Token 为什么不能简单等同于“单词”?
Language Token ID 为什么不能直接当连续数值输入神经网络?
nn.Embedding(100,64)中的 100 和 64 分别是什么?输入 Token ID Shape:
\[ [32,20] \]
经过nn.Embedding(1000,64)后 Shape 是什么?
- Robot State:
\[ [32,4,10] \]
经过:
nn.Linear(10,64)为什么会得到:
\[ [32,4,64] \]
?
- 一张:
\[ [3,224,224] \]
图片,如果 Patch Size = 16×16,一共有多少 Patch?
为什么 Visual Patch 也可以称为 Token?
[B,T,D]中的 \(T\) 为什么不一定表示时间?为什么 Transformer 需要 Position Information?
下面:
x = x + pos_embedding[:T]为什么通常不会改变:
\[ [B,T,D] \]
的 Shape?
Language、Image、Robot State 原始形式完全不同,为什么最后有可能被同一个 Transformer 处理?
当下一课写:
\[ Q=XW_Q \]
时,这里的:
\[ X \]
现在应该怎样解释?