Files
flykhan 2536c937e3 feat: 完整中文翻译 maths-cs-ai-compendium(数学·计算机科学·AI 知识大全)
翻译自英文原版 maths-cs-ai-compendium,共 20 章全部完成。

第01章 向量 | 第02章 矩阵 | 第03章 微积分
第04章 统计学 | 第05章 概率论 | 第06章 机器学习
第07章 计算语言学 | 第08章 计算机视觉 | 第09章 音频与语音
第10章 多模态学习 | 第11章 自主系统 | 第12章 图神经网络
第13章 计算与操作系统 | 第14章 数据结构与算法
第15章 生产级软件工程 | 第16章 SIMD与GPU编程
第17章 AI推理 | 第18章 ML系统设计
第19章 应用人工智能 | 第20章 前沿人工智能

翻译说明:
- 所有数学公式 $...$ / $$...$$、代码块、图片引用完整保留
- mkdocs.yml 配置中文导航 + language: zh
- README.md 已翻译为中文(兼 docs/index.md)
- docs/ 目录包含指向各章文件的 symlink
- 约 29,000 行中文内容,排除 .cache/ 构建缓存
2026-05-03 10:23:20 +08:00

218 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 视觉-语言-动作模型
*视觉-语言-动作模型(VLA)将视觉理解、语言理解和行动控制统一到单个神经网络中。本章涵盖VLA架构、动作标记化、RT-2、Octo、OpenVLA、预训练策略、泛化能力、与具体形态无关的模型以及基准测试。*
- 在前面的文件中,我们涵盖了感知(感知世界)和机器人学习(控制身体)。传统上,这些是独立的流程:感知模块检测物体,语言模块解释指令,控制模块生成动作。每个模块独立设计、训练和调试。
- **视觉-语言-动作模型(VLA)**将这一流程压缩为单个神经网络。模型接收图像(视觉)和自然语言指令(语言),并输出电机命令(动作)。一个模型,端到端。
- 这沿袭了我们在第10章看到的统一趋势:正如多模态模型将视觉和语言理解合并到一个架构中一样,VLA将这一趋势扩展到物理行动。关键洞见在于,语言为指定任务提供了自然、灵活的接口("拿起红色杯子放到架子上"),而大规模预训练的视觉-语言模型已经理解图像和指令。
## 从视觉-语言到行动
- 回顾第10章,**视觉-语言模型(VLM**如LLaVA和Flamingo接收图像和文本作为输入,并生成文本作为输出。它们理解场景、回答问题、遵循指令——全部通过语言完成。
- VLA提出的问题是:如果输出不是文本而是**机器人动作**呢?模型不再生成"红色杯子在桌子的左侧",而是生成一系列电机命令,驱动手臂去抓取那个杯子。
- 关键的架构洞见是,动作可以像单词一样表示为标记。如果VLM使用下一个标记预测逐个生成语言标记,那么VLA以同样的方式生成动作标记。Transformer从根本上并不关心输出标记表示"杯子"还是"将夹爪向前移动2厘米"。
- 这重新定义了机器人控制为序列建模问题,这正是transformer擅长的(第7章)。模型学习映射:(图像观测,语言指令)$\\to$(动作标记序列)。
## VLA架构
![VLA架构:相机图像和语言指令被编码为标记,由LLM主干网络处理,并解码为机器人动作](../images/vla_architecture.svg)
- 典型的VLA有三个组件:
- **视觉编码器**:将相机图像处理为视觉标记。通常是预训练的ViT(第8章)或SigLIP编码器(第10章)。图像被分割成块,每个块嵌入为一个标记,与标准视觉transformer完全一样。
- **语言模型主干网络**:一个预训练的LLM(例如LLaMA、PaLM),处理交错的视觉标记和语言标记序列。这就是推理发生的地方:模型通过同时关注指令和视觉特征来理解"拿起**红色**杯子"。
- **动作头**:将LLM的输出映射到机器人动作。可以是一个简单的MLP,将最后的隐藏状态映射到连续动作值,或者是一种将动作转换为离散标记的方案,由LLM的现有词汇表来预测。
- 架构看起来像:
$$\\text{图像} \\xrightarrow{\\text{ViT}} \\text{视觉标记} \\quad + \\quad \\text{指令} \\xrightarrow{\\text{分词器}} \\text{语言标记} \\quad \\xrightarrow{\\text{LLM}} \\quad \\text{动作标记}$$
- 视觉标记和语言标记被拼接(或交错)并输入到transformer主干网络,后者自回归地生成动作标记。这与VLM(第10章)的架构相同,但输出模态是动作而非文本。
## 动作标记化
- 机器人动作是连续的:关节速度、末端执行器位置、夹爪宽度。这些必须转换为离散标记才能让LLM生成。
![动作标记化:连续动作值被分箱为离散索引,LLM将其作为标记生成](../images/action_tokenisation.svg)
- 最简单的方法是**均匀离散化**。每个动作维度被划分为$N$个箱,覆盖有效值范围。例如,如果x方向速度范围从-0.1到0.1米/秒,使用256个箱,每个箱代表$\\frac{0.2}{256} \\approx 0.8$毫米/秒。动作值被映射到最近的箱索引,该索引成为一个标记。
- 对于7个动作维度(6自由度+夹爪)和每个维度256个箱,动作词汇表有$7 \\times 256 = 1792$个标记。这些被添加到LLM现有的文本词汇表中。模型每个维度生成一个动作标记,自回归地,就像生成单词一样。
- **动作分块**一次预测多个未来时间步,而不是单个动作。如果块大小为$H$,模型输出$H \\times d$个标记(其中$d$是动作维度)。这对于平滑、时间连贯的运动至关重要。一次预测一步会产生抖动行为,因为每次预测都是独立的。分块迫使模型规划一个短轨迹,捕获时间结构。
- 更复杂的方法使用**学习型标记化**,通过VQ-VAE(第10章)。VQ-VAE编码器将连续动作序列映射到离散码本索引序列,解码器从这些索引重建连续动作。LLM然后生成码本索引,而不是均匀分箱的值。这类似于图像分词器(第10章)如何将视觉信息压缩为紧凑的离散编码。
## 关键VLA模型
- **RT-2**(机器人Transformer 2Google DeepMind)是第一个大规模VLA。它使用预训练的VLM(PaLM-E或PaLI-X,参数高达55B)并在机器人示范数据上微调。动作表示为文本字符串:标记序列"1 128 91 241 5 101 127"编码了一个7维动作(每个数字是箱索引)。
- RT-2展示了一个显著特性:来自VLM主干网络的**涌现能力**迁移到了机器人领域。模型可以遵循涉及从未在机器人数据中见过的概念的指令(例如,"将香蕉移动到以A开头的国家"需要视觉物体识别+世界知识+行动)。VLM的语言理解和视觉推理"免费"获得。
- RT-2的局限性在于它是在单个机器人形态(特定的手臂和特定的夹爪)的数据上训练的。它不能泛化到不同的机器人。
- **Octo**(加州大学伯克利分校)是一个开源的、**与具体形态无关**的VLA,设计用于跨不同机器人平台工作。关键创新包括:
- **扩散动作头**,而不是自回归标记预测。动作头获取transformer的输出,并通过去噪扩散过程(第8章)生成动作。这自然地处理了多模态动作分布(见下图),即存在多个有效的任务完成方式。
![多模态动作分布:回归将两条有效路径平均为一条穿过障碍物的无效路径](../images/multimodal_action_distribution.svg)
- **灵活的观测和动作空间**:Octo为不同的机器人配置使用特定于任务的标记化器。它在Open X-Embodiment数据集上预训练,该数据集包含来自22种不同机器人形态的示范。
- **高效微调**:Octo只需100个示范就可以微调到新机器人,使其适用于数据有限的实验室。
- **OpenVLA**(斯坦福大学、加州大学伯克利分校)采用微调现有开源VLM(基于Llama)用于机器人技术的方法。它使用7B参数主干网络、均匀动作标记化(每个维度256个箱),并在Open X-Embodiment数据上训练。其优势在于简单性:架构是标准的VLM,动作标记被附加到词汇表中,使其易于使用现有的LLM基础设施进行训练和部署。
- **$\\pi_0$**Physical Intelligence)代表了当前最高水平。它使用预训练的VLM主干网络和**流匹配**动作头(第8章)。流匹配通过学习一个速度场将噪声传输到动作分布来生成动作,产生平滑、时间连贯的动作轨迹。$\\pi_0$展示了卓越的通用性,在多种机器人形态(包括双臂操作和灵巧手控制)上执行任务。
## 预训练配方
- VLA极大地受益于预训练的VLM主干网络,这些网络已经理解视觉场景和语言。训练流程通常分为几个阶段:
1. **VLM预训练**:在数十亿来自互联网的图像-文本对(CLIP、SigLIP、LLaVA风格的训练,如第10章所述)上训练(或使用现成的)视觉-语言模型。
2. **机器人数据协同训练**:在互联网数据和机器人示范数据的混合上微调VLM。互联网数据防止视觉和语言理解的灾难性遗忘,而机器人数据教授动作生成。混合比例很重要:机器人数据过多会降低语言理解,过少则无法学习动作。
3. **特定任务微调**:可选地在特定任务或机器人的示范上进行微调,通常使用LoRA(第10章)保持可训练参数数量较少。
- 机器人数据的数量比互联网数据少数个数量级。VLM可能在上数十亿张图像上预训练,但最大的机器人数据集(Open X-Embodiment)在所有形态上只有数百万帧。这种数据稀缺性正是从预训练VLM开始至关重要的原因:视觉和语言表示可以迁移,只有动作映射需要从有限的机器人数据中学习。
## 泛化能力
- VLA的承诺是**泛化**:执行训练中未见的任务,使用未见过的物体,在未见过的环境中,遵循未见过的指令。
- VLA沿多个轴进行泛化:
- **新颖物体**:VLM主干网络从互联网预训练中识别物体。如果模型从网络图像中知道"螺丝刀"长什么样,即使没有机器人示范涉及螺丝刀,它也能操作螺丝刀。
- **新颖指令**:组合语言理解使模型能够遵循已知概念的新组合。"将蓝色方块堆叠在绿色方块上"即使训练只展示了堆叠红色方块也能工作,因为模型从语言预训练中理解了颜色形容词。
- **新颖环境**:在一定程度上,VLA跨视觉域(不同的桌子、光照、背景)迁移,因为视觉编码器在多样化的网络图像上预训练。但这有局限性:在实验室训练的机器人可能在杂乱厨房中遇到困难。
- **新颖形态**:这是最难的轴。不同机器人有不同的动作空间(关节角度 vs. 末端执行器速度)、不同的传感器(腕部相机 vs. 头顶相机)和不同的物理能力。与形态无关的模型如Octo和$\\pi_0$通过灵活的标记化器和跨多种机器人类型的预训练来解决这一问题。
- 泛化能力通过**保留任务**进行评估:机器人被要求执行从未训练过的任务。在新颖任务上50-80%的成功率被认为是强劲的结果,而在分布内任务上成功率通常>90%。随着模型规模扩大和机器人数据集增长,这一差距正在缩小。
## 与形态无关的模型
- 该领域正朝着"一个模型,多种机器人"的方向发展。不再为每个机器人训练单独的策略,而是单个VLA处理多种形态。
- 这需要解决**动作空间不匹配**问题。一个7自由度手臂带平行夹爪有7个动作维度。双臂设置是14个。四足机器人有12个。类人机器人有30个以上。动作标记化必须足够灵活以处理所有这些。
- 解决方案包括:
- **填充动作向量**:使用最大的动作空间,较小的用零填充。
- **每种形态的动作头**:共享的transformer主干网络,每种机器人类型有单独的小型MLP。
- **归一化动作表示**:在共同框架中表示所有动作(如世界坐标系中的末端执行器速度),使产生类似末端执行器运动的不同机器人共享相同的动作标记。
- 共享主干网络学习通用的视觉和语言理解,加上通用的操作策略(从上方接近、对齐物体、闭合夹爪)。特定于形态的组件只需要将这些高层策略转化为具体的电机命令。
## 基准测试与评估
- 评估VLA具有独特的挑战性,因为它需要物理机器人实验(或高保真仿真)。
- **SIMPLER**(机器人学习模拟操作策略评估)提供了标准化的仿真环境,无需物理硬件即可比较VLA性能。它与现实世界的成功率相关性良好,并实现了可复现的基准测试。
- **现实世界评估**仍然是金标准。典型协议:
1. 定义一组具有明确成功标准的任务(物体到达目标位置、选择正确物体、在时限内完成任务)。
2. 每次任务运行$N$次试验(通常10-50次)。
3. 报告成功率及置信区间。
4. 包括保留任务(从未训练过的)以衡量泛化能力。
- **Open X-Embodiment**数据集和基准测试汇总了来自22个机构、跨越多个机器人平台的机器人数据。它提供了共享示范的标准格式和用于跨形态迁移的通用评估套件。
## 编程任务(使用CoLab或notebook
1. 实现动作标记化:将连续动作离散化为箱并重建。观察量化误差随箱数量的变化。
```python
import jax.numpy as jnp
# 连续动作:7个维度(6自由度+夹爪)
action_true = jnp.array([0.023, -0.051, 0.012, 0.1, -0.03, 0.005, 0.8])
action_min = jnp.array([-0.1, -0.1, -0.1, -0.5, -0.5, -0.5, 0.0])
action_max = jnp.array([ 0.1, 0.1, 0.1, 0.5, 0.5, 0.5, 1.0])
for n_bins in [16, 64, 256, 1024]:
# 标记化:将连续值映射为箱索引
normalised = (action_true - action_min) / (action_max - action_min)
tokens = jnp.clip((normalised * n_bins).astype(int), 0, n_bins - 1)
# 去标记化:将箱索引映射回连续值
reconstructed = (tokens + 0.5) / n_bins * (action_max - action_min) + action_min
error = jnp.linalg.norm(action_true - reconstructed)
print(f"箱数={n_bins:4d} 标记={tokens} 误差={error:.6f}")
```
2. 模拟动作分块与单步预测的比较。生成平滑轨迹,向单步预测添加噪声,并与分块预测比较。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 真实平滑轨迹(例如,伸手动作)
t = jnp.linspace(0, 2 * jnp.pi, 100)
gt_x = jnp.sin(t)
gt_y = 1 - jnp.cos(t)
# 单步:每次预测有独立噪声
rng = jax.random.PRNGKey(42)
noise_ss = jax.random.normal(rng, (100, 2)) * 0.05
single_step = jnp.stack([gt_x, gt_y], axis=1) + noise_ss
# 单步误差累积漂移
single_step_cumulative = jnp.cumsum(noise_ss, axis=0) * 0.3 + jnp.stack([gt_x, gt_y], axis=1)
# 分块(块大小=10):块内噪声关联,更平滑
chunk_size = 10
rng2 = jax.random.PRNGKey(7)
chunks = []
for i in range(0, 100, chunk_size):
chunk_noise = jax.random.normal(jax.random.fold_in(rng2, i), (2,)) * 0.05
chunk = jnp.stack([gt_x[i:i+chunk_size], gt_y[i:i+chunk_size]], axis=1)
chunks.append(chunk + chunk_noise)
chunked = jnp.concatenate(chunks, axis=0)
plt.figure(figsize=(8, 4))
plt.plot(gt_x, gt_y, "k-", linewidth=2, label="真实轨迹")
plt.plot(single_step_cumulative[:, 0], single_step_cumulative[:, 1],
"r-", alpha=0.7, label="单步(漂移)")
plt.plot(chunked[:, 0], chunked[:, 1], "b-", alpha=0.7, label="分块(稳定)")
plt.legend(); plt.axis("equal"); plt.grid(True)
plt.title("动作分块 vs 单步预测")
plt.show()
```
3. 可视化VLA动作分布如何是多模态的。使用简单的2D高斯混合来展示为什么扩散/流匹配动作头优于回归。
```python
import jax
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 绕过障碍物的两种有效方式:左边或右边
rng = jax.random.PRNGKey(0)
k1, k2 = jax.random.split(rng)
mode1 = jax.random.normal(k1, (200, 2)) * 0.15 + jnp.array([-1.0, 0.5])
mode2 = jax.random.normal(k2, (200, 2)) * 0.15 + jnp.array([ 1.0, 0.5])
samples = jnp.concatenate([mode1, mode2])
# 回归预测均值 = 模态的均值(无效!)
mean_pred = samples.mean(axis=0)
plt.figure(figsize=(6, 5))
plt.scatter(samples[:, 0], samples[:, 1], s=5, alpha=0.5, label="真实动作分布")
plt.plot(*mean_pred, "rx", markersize=15, markeredgewidth=3, label="回归均值(无效!)")
plt.plot(-1, 0.5, "g^", markersize=12, label="模态1(向左)")
plt.plot(1, 0.5, "b^", markersize=12, label="模态2(向右)")
plt.legend(); plt.grid(True)
plt.title("多模态动作:为什么回归失败")
plt.xlabel("动作维度1"); plt.ylabel("动作维度2")
plt.show()
```