VLA 系统学习第 3 课:从一次机器人示范,到真正送进神经网络的 Batch

一、第二课标准答案:Behavior Cloning

1. Behavior Cloning 为什么属于监督学习?

因为训练数据里已经有"输入"和"正确答案"。

对 BC 来说,一个最基本训练样本就是:

\ (o_t,a_t) \\

其中:

  • \(o_t\):Observation;
  • \(a_t\):专家实际执行的 Action。

模型根据 \(o_t\) 预测:

\ \\hat a_t=\\pi_\\theta(o_t) \\

再把预测值:

\ \\hat a_t \\

和专家答案:

\ a_t \\

比较。

所以本质就是:

\ \\boxed{\\text{输入}\\rightarrow\\text{正确答案}} \\

和图像分类:

\ \\text{Image}\\rightarrow\\text{Class Label} \\

本质上属于同一种监督学习逻辑。

区别只是 BC 的标签不是"猫/狗",而是机器人动作。


2. \(a_t\) 和 \(\hat a_t\) 有什么区别?

\ a_t \\

是:

数据集中由专家真正执行过的 Action。

而:

\ \\hat a_t \\

是:

当前神经网络根据 Observation 预测出来的 Action。

例如专家动作是:

\ a_t=\[0.10,-0.05 \]

模型预测:

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

训练的目标就是让:

\ \\hat a_t \\

越来越接近:

\ a_t \\


3. 下面这条式子是什么意思?

\ \\tau=(o_0,a_0,o_1,a_1,\\ldots,o_T,a_T) \\

它表示一条完整的:

Trajectory

也就是:

从任务开始到结束,按时间顺序记录下来的一整段 Observation 和 Action。

例如:

复制代码
t = 0
o0 → a0

t = 1
o1 → a1

t = 2
o2 → a2

...

t = T
oT → aT

它不是一个样本,而是一串连续样本。


4. 为什么 Training Loss 很低,但 Rollout 可能很差?

因为训练时模型看到的主要是:

\ D_{\\text{expert}} \\

也就是专家操作产生的数据。

但真正运行时,机器人执行的是:

\ \\hat a_t \\

也就是模型自己的预测。

一旦模型出现一点点误差,机器人下一时刻所处的位置就可能和专家示范不同。

于是:

\ o_{t+1} \\

开始偏离训练数据中的情况。

再继续预测,可能进一步偏离。

所以可能出现:

复制代码
训练数据上预测很准
↓
第一次执行稍微有误差
↓
进入训练数据很少出现的状态
↓
模型预测变差
↓
进一步偏离

因此:

\ \\boxed{\\text{Training Loss低}\\not\\Rightarrow\\text{Rollout一定成功}} \\


5. Distribution Shift 怎么理解?

最直观地说:

模型自己的动作,把自己带到了训练时没见过的位置。

例如专家示范永远非常稳定:

复制代码
夹爪正对物体
↓
向前移动

但模型第一次偏了 1 cm。

于是下一帧变成:

复制代码
夹爪已经偏了 1 cm

如果训练集中几乎没有这种情况,模型就不知道应该如何纠正。

可能继续偏:

复制代码
1 cm
↓
2 cm
↓
4 cm
↓
最终失败

这就是:

Distribution Shift


6. Observation、Predicted Action、Expert Action、Loss 的关系是什么?

真正的数据流应该理解成:

复制代码
Observation
↓
Policy
↓
Predicted Action
      ↘
       与 Expert Action 比较
              ↓
             Loss

数学上:

\ o_t \\rightarrow \\pi_\\theta \\rightarrow \\hat a_t \\

然后:

\ L(\\hat a_t,a_t) \\

注意:

Expert Action 不是预测以后才生成的。

它本来就在 Dataset 里,只是在计算 Loss 时拿出来作为正确答案。


7. 如果

\ obs.shape=\[32,10 \]\ action.shape=\[32,7 \]

分别是什么意思?

这里:

\ 32 \\

表示:

Batch Size

一次训练 32 个样本。


\ 10 \\

表示每个 Observation 用 10 个数表示。

所以单个 Observation:

\ o_i\\in\\mathbb R\^{10} \\


\ 7 \\

表示每个 Action 有 7 个数。

所以单个 Action:

\ a_i\\in\\mathbb R\^7 \\

整个 Batch 就是:

\ O\\in\\mathbb R\^{32\\times10} \\\ A\\in\\mathbb R\^{32\\times7} \\


二、插一个必须彻底弄懂的 Python 点:定义类、继承以后,__getitem__() 为什么会"自己被调用"?

这个问题非常重要,因为你以后看 PyTorch、Dataset、模型代码,会遇到大量:

复制代码
__init____len____getitem____call____iter__

这种名字。

它们不是普通函数名。

Python 把这一类:

复制代码
__xxx__

称为:

Special Method

常被中文叫:

  • 特殊方法;
  • 魔术方法;
  • 双下划线方法;
  • dunder method。

其中 dunder 就是:

double underscore


1. 先从普通类开始

例如:

复制代码
class Dog:    def bark(self):        print("wang")

这里我们只是:

定义了一个类。

还没有真正创建对象。

真正创建对象:

复制代码
dog = Dog()

这里:

复制代码
dog

才是一个真正的对象。

然后:

复制代码
dog.bark()

Python 会执行:

复制代码
Dog 类里的 bark()

这是普通方法调用。


2. __init__() 特殊在哪里?

例如:

复制代码
class Dog:    def __init__(self, name):        self.name = name

我们并不会通常这样写:

复制代码
dog.__init__("Tom")

而是:

复制代码
dog = Dog("Tom")

你写:

复制代码
Dog("Tom")

以后,Python 在对象创建过程中会自动调用初始化逻辑。

所以你可以先简单理解成:

复制代码
Dog("Tom")
↓
Python创建Dog对象
↓
自动触发 __init__()
↓
把 name 初始化进去

这就是特殊方法的核心思想:

你通常不是直接调用它,而是进行某种 Python 语法操作,由 Python 自动寻找并调用对应的特殊方法。


3. __len__() 也是一样

假设:

复制代码
class MyDataset:    def __len__(self):        return 100

我们一般不会写:

复制代码
dataset.__len__()

虽然这样通常也能执行。

更自然的是:

复制代码
len(dataset)

Python看到:

复制代码
len(dataset)

以后,会去找这个对象对应的:

复制代码
__len__()

所以可以粗略理解:

复制代码
len(dataset)

相当于触发:

复制代码
dataset.__len__()

于是:

复制代码
len(dataset)
↓
Python检查对象
↓
发现类定义了 __len__
↓
调用 __len__
↓
返回100

4. 那 __getitem__() 呢?

这就是 Robot Dataset 最重要的一个。

假设:

复制代码
class MyDataset:    def __getitem__(self, index):        return index * 10

创建对象:

复制代码
dataset = MyDataset()

现在写:

复制代码
x = dataset[3]

你看起来只是用了:

复制代码
[3]

但 Python 会把这种:

复制代码
对象[index]

语法解释为:

去调用这个对象的 __getitem__()。

也就是大致等价于:

复制代码
x = dataset.__getitem__(3)

因此:

复制代码
dataset[3]

会得到:

复制代码
30

完整调用关系:

复制代码
dataset[3]
↓
Python发现你在对对象使用 []
↓
寻找 dataset 所属类中的 __getitem__
↓
调用 __getitem__(self, 3)
↓
返回结果

5. 为什么 PyTorch Dataset 要这样设计?

因为如果没有 __getitem__(),你可能只能写:

复制代码
dataset.get_sample(10)

但定义 __getitem__() 后,就可以像操作普通列表一样:

复制代码
dataset[10]

这非常自然。

普通 Python list 也是:

复制代码
numbers = [10, 20, 30]numbers[1]

得到:

复制代码
20

所以 PyTorch Dataset 实际上是在让你的自定义 Dataset 具备:

"像一个数据容器一样按索引访问"

的能力。


6. 这和"继承"是什么关系?

PyTorch 中我们通常写:

复制代码
from torch.utils.data import Datasetclass RobotDataset(Dataset):    ...

这里不是:

RobotDataset 里面创建了一个 Dataset 对象。

而是:

我们定义了一个新的类 RobotDataset,它继承 Dataset。

也就是说:

复制代码
Dataset
   ↑
RobotDataset

RobotDataset 可以继承父类已有的一些行为和约定,同时自己实现:

复制代码
__len__()

和:

复制代码
__getitem__()

例如:

复制代码
class RobotDataset(Dataset):    def __len__(self):        return ...    def __getitem__(self, index):        return ...

这意味着:

我们按照 PyTorch Dataset 所期望的接口,告诉它"长度怎么算"和"某个索引的数据怎么取"。


7. DataLoader 怎么利用 __getitem__()?

假设:

复制代码
dataloader = DataLoader(    dataset,    batch_size=4)

DataLoader 需要获取类似:

复制代码
第7条
第18条
第2条
第31条

数据。

于是内部逻辑可以先粗略理解成:

复制代码
sample1 = dataset[7]sample2 = dataset[18]sample3 = dataset[2]sample4 = dataset[31]

而每一个:

复制代码
dataset[index]

又会触发:

复制代码
dataset.__getitem__(index)

然后 DataLoader 再把这些 Sample 拼成一个 Batch。

所以整个关系是:

复制代码
DataLoader
↓
需要某几个索引的数据
↓
dataset[index]
↓
__getitem__(index)
↓
返回一个Sample
↓
多个Sample组合成Batch

这就是为什么以后看机器人 Dataset,我会让你重点找:

复制代码
__getitem__()

因为它经常直接决定:

模型训练时到底吃到了什么。


8. 再看 self

例如:

复制代码
class RobotDataset(Dataset):    def __getitem__(self, index):        obs = self.observations[index]        return obs

假设:

复制代码
dataset = RobotDataset(...)

再执行:

复制代码
dataset[10]

Python 实际上会把:

复制代码
dataset

这个对象本身传给:

复制代码
self

所以此时:

复制代码
self

就是:

复制代码
dataset

因此:

复制代码
self.observations

就是:

这个 dataset 对象自己保存的 observations。

你可以暂时理解:

复制代码
dataset[10]

触发:

复制代码
RobotDataset.__getitem__(dataset, 10)

这样你会更容易理解 self。


9. 常见特殊方法先认识这几个

现在只需要掌握:

你写的语法 Python 会触发
obj = MyClass(...) 对象构造/初始化过程,常见 __init__()
len(obj) obj.__len__()
obj[index] obj.__getitem__(index)
obj(...) obj.__call__(...)
str(obj) obj.__str__()

以后还会遇到:

复制代码
__iter__()__next__()__enter__()__exit__()

但现在不用一次学完。

我们的原则还是:

真正遇到的时候,再结合代码讲。

VLA 系统学习第 3 课:从一次机器人示范,到真正送进神经网络的 Batch

前两课我们已经知道,Behavior Cloning 做的事情可以写成:

\ o_t \\rightarrow \\pi_\\theta \\rightarrow \\hat a_t \\

训练时,再把模型预测的 \(\hat a_t\) 和专家真正执行的 \(a_t\) 比较:

\ L(\\hat a_t,a_t) \\

但这里还有一个关键问题没有解决:

训练代码中的 \(o_t\) 和 \(a_t\),到底是怎么得到的?

它们不是凭空出现在 model(obs) 里的。

真正的数据链条是:

\ \\boxed{ \\text{机器人示范} \\rightarrow \\text{Episode} \\rightarrow \\text{时间步数据} \\rightarrow \\text{Dataset} \\rightarrow \\text{Sample} \\rightarrow \\text{DataLoader} \\rightarrow \\text{Batch} \\rightarrow \\text{Policy} } \\

这一课只把这条链弄明白。


一、先从"一次完整操作"开始,而不是从 Dataset 类开始

假设人类遥操作机器人完成一次任务:

从桌面上拿起一个物体,再放到目标位置。

整个过程持续 5 秒。

假设控制系统每秒记录 10 次数据,也就是:

\ f=10\\text{ Hz} \\

那么 5 秒大约会得到:

\ 5\\times10=50 \\

个时间点。

可以把它想象成:

\ t=0,1,2,\\ldots,49 \\

在每一个时间点,系统都可能记录当前相机画面、机器人自身状态,以及操作者此时给出的动作。

于是某一个时间点 \(t\) 的数据可以写成:

\ (I_t,s_t,a_t) \\

其中:

\ I_t \\

表示这一时刻看到的图像,

\ s_t \\

表示机器人这一时刻自己的状态,

\ a_t \\

表示专家此时真正执行的动作。

所以,一个时间点实际上就是:

\ \\boxed{ \\text{当前看到了什么} + \\text{机器人现在是什么状态} \\rightarrow \\text{专家现在怎么操作} } \\

这正是 Behavior Cloning 需要的监督数据。


二、Episode 本质上就是这一整段时间序列

刚才那 5 秒从任务开始到任务结束的完整记录,可以称为一个:

Episode

也可以把它理解为一次完整任务示范。

如果这一次任务包含 50 个时间点,那么:

\ \\text{Episode}_0 = \\{ (o_0,a_0), (o_1,a_1), \\ldots, (o_{49},a_{49}) \\} \\

这里为了简化,把:

\ o_t \\

理解成整个 Observation,例如:

\ o_t=(I_t,s_t) \\

于是:

\ \\text{Episode}_0 = \\{ (o_0,a_0), (o_1,a_1), \\ldots \\} \\

如果操作者重新把环境复位,再完成一次任务,就是:

\ \\text{Episode}_1 \\

再做一次:

\ \\text{Episode}_2 \\

最终我们得到:

\ \\mathcal D = \\{ \\text{Episode}_0, \\text{Episode}_1, \\ldots, \\text{Episode}_{N-1} \\} \\

这整个集合就是机器人训练数据集。

这里要特别注意:

100 个 Episode,并不等于只有 100 个训练样本。

因为每个 Episode 里面还有几十、几百甚至更多个时间点。

例如每个 Episode 平均有 100 帧,那么:

\ 100\\text{ episodes}\\times100\\text{ frames} = 10000\\text{ time steps} \\

最简单的 BC 完全可以把这些时间点分别作为训练样本。

所以真正的训练单位通常不是:

\ \\text{一个 Episode} \\

而可能是:

\ (o_t,a_t) \\


三、为什么 Observation 和 Action 必须严格对应同一个时间点?

现在假设:

\ t=17 \\

时,相机看到物体在夹爪右侧,同时机器人当前关节状态为 \(s_{17}\)。

专家看到这个情况之后,执行:

\ a_{17} \\

那么正确的监督学习样本就是:

\ (o_{17},a_{17}) \\

模型学习的是:

当我看到 \(o_{17}\) 这种情况时,专家会执行 \(a_{17}\)。

如果数据错位,变成:

\ (o_{17},a_{30}) \\

模型学到的东西就彻底错了。

因为 \(a_{30}\) 是机器人后来处于另一个位置时执行的动作。

所以机器人数据不是简单的:

有图片,有动作就行。

而是必须保证:

\ \\boxed{ \\text{Observation}_t \\leftrightarrow \\text{Action}_t } \\

这种时间对应关系非常重要。

这也是为什么机器人数据中经常能看到:

复制代码
timestamp
frame_index
episode_index

这些信息。

它们不是装饰性的元数据,而是在告诉程序:

这条数据属于哪一次任务、哪一个时间点。


四、现在才轮到 Dataset:它负责把磁盘数据变成"一个训练样本"

前面讲的都是数据在概念上的组织方式。

现在进入代码。

磁盘里的真实数据可能存成:

复制代码
images/
video/
data.parquet
metadata.json
...

不同项目存法不同。

但是神经网络不能直接拿:

复制代码
.jpg
.mp4
.json

去计算。

神经网络真正需要的是:

\ \\text{Tensor} \\

所以程序中通常需要一个 Dataset 类,负责完成:

\ \\boxed{ \\text{磁盘中的原始数据} \\rightarrow \\text{读取} \\rightarrow \\text{预处理} \\rightarrow \\text{Tensor} \\rightarrow \\text{返回一个 Sample} } \\

先看最简单的教学版本:

复制代码
import torchfrom torch.utils.data import Datasetclass RobotDataset(Dataset):    def __init__(self, observations, actions):        self.observations = observations        self.actions = actions    def __len__(self):        return len(self.observations)    def __getitem__(self, index):        obs = self.observations[index]        action = self.actions[index]        return {            "observation": obs,            "action": action        }

这段代码现在不要孤立地背。

它实际上就是把前面的数学数据:

\ \\{(o_i,a_i)\\}_{i=1}\^{N} \\

包装成了一个 Python 对象。


dataset[index] 到底发生了什么?

假设执行:

复制代码
sample = dataset[10]

Python 会调用:

复制代码
dataset.__getitem__(10)

然后拿出第 10 个样本:

\ (o_{10},a_{10}) \\

最终返回:

复制代码
{    "observation": obs,    "action": action}

所以:

复制代码
__getitem__()

以后是我们看真实 Robot Dataset 时非常关键的地方。

因为它能回答:

模型训练时真正拿到的到底是什么?

比如真实代码中可能返回:

复制代码
{    "image": image,    "state": state,    "action": action}

那么你就已经知道模型的数据入口至少包含:

\ I_t,\\quad s_t,\\quad a_t \\


五、为什么原始图片还不能直接成为模型输入?

假设磁盘里有:

复制代码
image_001.jpg

它只是一个图片文件。

模型真正需要的可能是:

\ I_t\\in\\mathbb R\^{3\\times H\\times W} \\

所以 Dataset 读取图片以后,还要做预处理。

数据流可能是:

复制代码
JPEG
↓
Decode
↓
Image Array
↓
Resize
↓
Normalize
↓
Tensor

例如最后得到:

\ I_t\\in\\mathbb R\^{3\\times224\\times224} \\

这里的 224 只是教学示例,不代表所有 VLA 都是这个尺寸。

Robot State 也是一样。

原始状态可能是:

\ s_t= \[0.32,-0.41,1.08,\\ldots \]

代码可能对它做归一化:

\ s'_t= \\frac{s_t-\\mu}{\\sigma} \\

然后模型真正吃进去的是:

\ s'_t \\

而不一定是原始 \(s_t\)。

Action 也可能做同样处理。

这件事情为什么重要?

因为训练时如果模型学习的是归一化 Action:

\ a'_t \\

那么模型推理时输出的:

\ \\hat a'_t \\

也还是归一化空间里的数值。

真实执行之前可能必须再变回:

\ \\hat a_t \\

也就是:

\ \\hat a_t = \\hat a'_t\\sigma+\\mu \\

所以以后不能看到模型输出:

复制代码
0.42

就直接说:

机器人移动 0.42 米。

必须先确认:

这个 Action 有没有经过 normalization?


六、一个 Sample 和一个 Batch 是两回事

假设 Dataset 中取出一个样本:

复制代码
sample = dataset[10]

它可能包含:

\ image:\[3,H,W \]\ state:\[D_s \]\ action:\[D_a \]

注意这里没有:

\ B \\

因为现在只有一个样本。

比如:

复制代码
image.shape  = [3, 224, 224]
state.shape  = [10]
action.shape = [7]

这代表:

一张图片,一份机器人状态,一个专家动作。

但是训练神经网络时,一般不会每次只训练一个样本。

比如我们希望一次拿:

\ 32 \\

个样本。

这就需要:

DataLoader


七、DataLoader 的作用,是把多个 Sample 拼成一个 Batch

代码可能是:

复制代码
from torch.utils.data import DataLoaderdataloader = DataLoader(    dataset,    batch_size=32,    shuffle=True)

这里:

复制代码
batch_size=32

表示:

每次取 32 个 Sample。

于是原来一个样本的:

\ \[3,H,W \]

现在会变成:

\ \[32,3,H,W \]

Robot State:

\ \[D_s \]

变成:

\ \[32,D_s \]

Action:

\ \[D_a \]

变成:

\ \[32,D_a \]

所以 Batch Dimension 并不是模型突然创造出来的。

它来源于:

把很多个独立 Sample 堆在一起。

也就是:

\ \\boxed{ \\text{Single Sample} \\rightarrow \\text{Stack B Samples} \\rightarrow \\text{Batch} } \\


八、现在终于能看懂训练循环的数据是从哪里来的了

我们前面曾经看过:

复制代码
for batch in dataloader:    obs = batch["observation"]    action = batch["action"]    pred_action = model(obs)    loss = loss_fn(pred_action, action)

现在这几行不应该再像"突然出现的 PyTorch 代码"。

因为它前面实际上有完整的数据链:

复制代码
专家操作机器人
↓
产生很多 Episode
↓
Episode 中记录 Observation 和 Action
↓
数据保存到磁盘
↓
Dataset 读取一条数据
↓
__getitem__ 返回一个 Sample
↓
DataLoader 取 32 个 Sample
↓
组成一个 Batch
↓
送入 Policy

于是:

复制代码
obs = batch["observation"]

实际上就是:

\ O= \\begin{bmatrix} o_1\\\\ o_2\\\\ \\vdots\\\\ o_{32} \\end{bmatrix} \\

而:

复制代码
action = batch["action"]

就是:

\ A= \\begin{bmatrix} a_1\\\\ a_2\\\\ \\vdots\\\\ a_{32} \\end{bmatrix} \\

模型一次预测:

\ \\hat A=\\pi_\\theta(O) \\

然后:

\ L=L(\\hat A,A) \\

这就是一整个 Batch 的 BC 训练。


九、把 Image 和 Robot State 都放进来

前面的 obs 还是一个抽象概念。

实际机器人 Policy 可能拿到:

\ I_t \\

和:

\ s_t \\

那么一个 Sample 可以写成:

\ \\text{Sample}_t= \\{ I_t,s_t,a_t \\} \\

例如一个样本:

\ I_t:\[3,H,W \]\ s_t:\[D_s \]\ a_t:\[D_a \]

DataLoader 组成 Batch:

\ I:\[B,3,H,W \]\ S:\[B,D_s \]\ A:\[B,D_a \]

然后模型:

\ \\hat A=\\pi_\\theta(I,S) \\

输出:

\ \\hat A:\[B,D_a \]

再和专家动作:

\ A:\[B,D_a \]

计算 Loss。

于是 Shape 并不是一张独立的表格,而是自然来自数据流:

\ \\boxed{ \\text{磁盘中的一个时间点} \\rightarrow \\text{Sample} \\rightarrow \\text{Batch} \\rightarrow \\text{Model Input} \\rightarrow \\text{Model Output} } \\

这才是以后真正应该使用的 Tensor Shape 思维。


十、为什么训练和真正运行时的数据来源不同?

这是这一课最后一个必须弄清的问题。

训练时:

\ o_t \\

来自已经保存好的 Dataset。

也就是:

复制代码
Disk Dataset
↓
Dataset
↓
DataLoader
↓
Policy

而真正运行时:

\ o_t \\

不再来自磁盘中的专家示范。

而是来自实时环境:

复制代码
Camera / Robot Sensors
↓
Preprocessing
↓
Policy
↓
Predicted Action
↓
Execute

所以训练阶段有:

\ (o_t,a_t) \\

其中 \(a_t\) 是专家答案。

但推理阶段只有:

\ o_t \\

然后模型自己产生:

\ \\hat a_t \\

这一点也解释了为什么部署不能简单写一句:

复制代码
action = model(obs)

就认为结束了。

因为真正部署时,你还必须重新实现训练阶段 Dataset 曾经帮你完成的那些事情:

复制代码
读取图像
↓
Resize
↓
Normalize
↓
读取 Robot State
↓
Normalize
↓
构造 Tensor
↓
Policy

如果训练时用了:

\ x'=\\frac{x-\\mu}{\\sigma} \\

部署时却直接把原始 \(x\) 输入模型,

模型看到的数据分布就和训练阶段不同。

即使网络权重完全没变,结果也可能明显变差。

所以:

\ \\boxed{ \\text{Training Preprocessing} \\approx \\text{Inference Preprocessing} } \\

这是一条非常重要的工程原则。


十一、这一课先不要继续扩展时间序列

现在你可能会继续想到:

如果模型不是只看当前一帧呢?
如果一次输出未来很多个 Action 呢?

这时候确实会出现:

\ \[B,T,3,H,W \]

以及:

\ \[B,K,D_a \]

还会逐渐遇到:

  • Observation History;
  • Action Chunk;
  • Padding;
  • Mask。

但这些概念这节课先不展开。

因为现在更重要的是先把最简单的:

\ (o_t,a_t) \\

数据链彻底弄稳。

等进入 ACT 时,我们再从:

\ a_t \\

自然扩展到:

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

那时候 Action Chunk 就不会显得突然。


十二、整章链路回看

现在把这一课压成一条连续链:

\ \\boxed{ \\text{Human Demonstration} \\rightarrow \\text{Episode} \\rightarrow (o_t,a_t) \\rightarrow \\text{Save to Disk} \\rightarrow \\text{Dataset} \\rightarrow \\_\\_getitem\\_\\_() \\rightarrow \\text{Sample} \\rightarrow \\text{DataLoader} \\rightarrow \\text{Batch} \\rightarrow \\text{Policy} } \\

如果一个 Sample 是:

\ I_t:\[3,H,W \]\ s_t:\[D_s \]\ a_t:\[D_a \]

那么经过 DataLoader 组成 Batch 后:

\ I:\[B,3,H,W \]\ S:\[B,D_s \]\ A:\[B,D_a \]

Policy 接收:

\ (I,S) \\

输出:

\ \\hat A:\[B,D_a \]

然后计算:

\ L(\\hat A,A) \\

这就是 Behavior Cloning 中,从真实机器人示范一直走到神经网络训练输入的完整数据链。


第三课自测

1. 为什么 100 个 Episode 不代表 Dataset 只有 100 个训练样本?

2. dataset[10] 和 dataloader 分别负责什么?

3. 如果单个图像 Shape 是:

\ \[3,224,224 \]

并且:

\ B=32 \\

为什么进入模型以后会变成:

\ \[32,3,224,224 \]

?

4. 为什么训练时 Action 可以从 Dataset 里直接获得,而真正 Rollout 时却没有 Expert Action?

5. 为什么训练时做过 normalization,推理时通常也必须使用同样的 normalization?

6. 请尝试自己从头说一遍:

\ \\text{机器人示范} \\

究竟是怎样一步步变成:

复制代码
pred_action = model(obs)

中的 obs 的?

相关推荐
Dawson Zhu7 小时前
《Agentic Design Patterns》第 9 章导读:学习与适应(Learning and Adaptation)
人工智能·语言模型·架构·aigc·agi
IT研究所7 小时前
AI-ITR平台如何减少客户问题反复升级?
大数据·运维·人工智能·低代码·自然语言处理·安全架构·企微
SEO_juper7 小时前
用 Python 写一个 GEO 可见性检查脚本:你的网站现在能被 AI 引用吗
开发语言·人工智能·爬虫·python·seo·外贸独立站
小易老师AI实战7 小时前
RLHF深度详解(超通俗+原理+工程+对比):大模型对齐的核心基石
人工智能·大模型·sft·rlhf·ppo·人类反馈强化学习·llm 对齐
资深电气设计7 小时前
高压直流母线系统测试是什么?宜迈思液冷直流负载方案技术说明
人工智能
数智工坊7 小时前
视觉SLAM第12讲|地图构建:单目稠密重建、RGB-D点云与八叉树地图全解析
人工智能·深度学习·矩阵·机器人
GPU实战笔记7 小时前
云端 GPU 训练的账户余额提醒:它能说明什么,不能说明什么?
深度学习·算法·成本管理·gpu云计算·云端训练
回眸&啤酒鸭7 小时前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能
博图光电7 小时前
Libra 27105相关技术参数
人工智能·数码相机
IT_陈寒8 小时前
SpringBoot自动配置差点让我加班到凌晨
前端·人工智能·后端