视觉-语言-动作模型


文档摘要

视觉-语言-动作模型 视觉-语言-动作模型(vision-language-action model,VLA)把看、理解语言和行动统一进单个神经网络。本文件涵盖 VLA 架构、动作分词、RT-2、Octo、OpenVLA、预训练策略、泛化、与本体无关的模型以及基准评测。 在前几节中,我们介绍了感知(感知世界)和机器人学习(控制身体)。传统上,这些是分开的流水线:一个感知模块检测物体,一个语言模块解释命令,一个控制模块生成动作。每个模块都被独立地设计、训练和调试。 视觉-语言-动作模型(VLA)把这条流水线压缩进单个神经网络。模型接收图像(视觉)、一条自然语言指令(语言),输出电机命令(动作)。一个模型,端到端。

视觉-语言-动作模型

视觉-语言-动作模型(vision-language-action model,VLA)把看、理解语言和行动统一进单个神经网络。本文件涵盖 VLA 架构、动作分词、RT-2、Octo、OpenVLA、预训练策略、泛化、与本体无关的模型以及基准评测。

  • 在前几节中,我们介绍了感知(感知世界)和机器人学习(控制身体)。传统上,这些是分开的流水线:一个感知模块检测物体,一个语言模块解释命令,一个控制模块生成动作。每个模块都被独立地设计、训练和调试。

  • **视觉-语言-动作模型(VLA)**把这条流水线压缩进单个神经网络。模型接收图像(视觉)、一条自然语言指令(语言),输出电机命令(动作)。一个模型,端到端。

  • 这遵循了我们在第 10 章看到的统一趋势:正如多模态模型把视觉和语言理解合并进一个架构,VLA 把这进一步扩展到物理动作。洞见在于:语言提供了一种自然、灵活的任务描述接口("把那个红色杯子拿起来放到架子上"),而大规模预训练的视觉-语言模型已经同时理解图像和指令。

从视觉-语言到动作

  • 回顾第 10 章,**视觉-语言模型(vision-language model,VLM)**如 LLaVA 和 Flamingo 接收图像和文本作为输入,产生文本作为输出。它们能理解场景、回答问题、遵循指令——全部以语言形式。

  • VLA 则追问:如果输出不是文本,而是机器人动作呢?模型不再生成"红色杯子在桌子左边",而是生成一串移动手臂去抓那个杯子的电机命令。

  • 关键的架构洞见是:动作可以像单词一样被表示为 token。如果 VLM 通过下一个 token 预测来逐个生成语言 token,那么 VLA 可以用同样的方式生成动作 token。transformer 本质上并不在意输出 token 表示的是"杯子"还是"把夹爪向前移动 2cm"。

  • 这就把机器人控制重新定义为序列建模问题,而 transformer 在这方面非常擅长(第 7 章)。模型学习这样的映射:(图像观测,语言指令) \to(一串动作 token)。

VLA 架构

VLA 架构:摄像头图像和语言指令被编码成 token,由 LLM 主干处理,再解码成机器人动作

  • 一个典型的 VLA 有三个组件:

    • 视觉编码器:把摄像头图像处理成视觉 token。通常是预训练的 ViT(第 8 章)或 SigLIP 编码器(第 10 章)。图像被切成 patch,每个 patch 嵌入为一个 token,与标准视觉 transformer 完全一样。

    • 语言模型主干:一个预训练的 LLM(如 LLaMA、PaLM),处理视觉 token 和语言 token 交错的序列。推理就发生在这里:模型通过同时关注指令和视觉特征来理解"拿起那个红色杯子"。

    • 动作头:把 LLM 的输出映射为机器人动作。它可以是一个把最后一个隐状态映射到连续动作值的简单 MLP,也可以是一种把动作转换成离散 token、由 LLM 现有词表来预测的分词方案。

  • 架构看起来是这样:

\text{Image} \xrightarrow{\text{ViT}} \text{visual tokens} \quad + \quad \text{Instruction} \xrightarrow{\text{tokeniser}} \text{language tokens} \quad \xrightarrow{\text{LLM}} \quad \text{action tokens}
  • 视觉 token 和语言 token 被拼接(或交错)并送入 transformer 主干,后者自回归地产生动作 token。这与 VLM(第 10 章)的架构相同,只是输出模态从文本变成了动作。

动作分词

  • 机器人动作是连续的:关节速度、末端执行器位置、夹爪宽度。这些必须被转换成离散 token,LLM 才能生成它们。

动作分词:连续的动作值被分箱成离散索引,LLM 把它们当作 token 来生成

  • 最简单的方法是均匀离散化。每个动作维度被划分成 N 个覆盖有效值范围的箱。例如,如果 x 方向速度范围为 -0.1 到 0.1 m/s,我们用 256 个箱,那么每个箱代表 \frac{0.2}{256} \approx 0.8 mm/s。一个动作值被映射到最近的箱索引,这个索引就成为一个 token。

  • 假设有 7 个动作维度(6 DoF + 夹爪),每个 256 个箱,动作词表有 7 \times 256 = 1792 个 token。这些 token 被加到 LLM 现有的文本词表中。模型逐维度地自回归生成动作 token,就像生成单词一样。

  • **动作分块(action chunking)**一次预测多个未来时间步,而不是单个动作。如果块大小为 H,模型输出 H \times d 个 token(其中 d 是动作维数)。这对平滑、时间连贯的运动至关重要。一次只预测一步会产生顿挫的行为,因为每次预测是独立的。分块迫使模型规划一条短轨迹,从而捕捉时间结构。

  • 更复杂的方法通过 VQ-VAE(第 10 章)使用学习型分词。VQ-VAE 编码器把一串连续动作映射成一串离散码本索引,解码器再从这些索引重建出连续动作。然后 LLM 生成的是码本索引,而不是均匀分箱的值。这类似于图像分词器(第 10 章)把视觉信息压缩成紧凑的离散码。

关键 VLA 模型

  • RT-2(Robotic Transformer 2,Google DeepMind)是第一个大规模 VLA。它取一个预训练的 VLM(PaLM-E 或 PaLI-X,参数多达 55B),并在机器人演示数据上微调。动作被表示成文本字符串:token 序列 "1 128 91 241 5 101 127" 编码了一个 7 维动作(每个数字是一个箱索引)。

  • RT-2 展示了一个显著的特性:来自 VLM 主干的涌现能力可以迁移到机器人领域。模型能遵循涉及它在机器人数据中从未见过的概念的指令(例如"把香蕉移到以 A 开头的国家"需要视觉物体识别 + 世界知识 + 动作)。VLM 的语言理解和视觉推理是"白送"的。

  • RT-2 的局限在于它只在单一机器人本体(一个特定的臂配一个特定的夹爪)的数据上训练。它无法泛化到不同的机器人。

  • Octo(UC Berkeley)是一个开源的、**与本体无关(embodiment-agnostic)**的 VLA,设计用于跨不同机器人平台工作。关键创新包括:

    • 扩散动作头替代自回归 token 预测。动作头接收 transformer 的输出,通过去噪扩散过程(第 8 章)产生动作。这天然地处理了多模态动作分布(见下图),即完成任务存在多种有效方式的情况。

多模态动作分布:回归把两条有效路径平均成一条穿过障碍物的无效路径

- **灵活的观测和动作空间**:Octo 为不同的机器人配置使用任务专属的分词器。它在 Open X-Embodiment 数据集上预训练,该数据集包含来自 22 种不同机器人本体的演示。 - **高效微调**:Octo 可以用少至 100 个演示就微调到一台新机器人上,这对数据有限的实验室非常实用。
  • OpenVLA(Stanford、UC Berkeley)采取的路线是微调一个现有的开源 VLM(基于 Llama)用于机器人。它使用 7B 参数的主干、均匀动作分词(每维 256 个箱),并在 Open X-Embodiment 数据上训练。它的优势在于简单:架构就是一个标准的 VLM,把动作 token 追加到词表里,因此很容易用现有的 LLM 基础设施来训练和部署。

  • \pi_0(Physical Intelligence)代表了当前的最先进水平。它使用预训练的 VLM 主干,配以**流匹配(flow matching)**动作头(第 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 通过灵活的分词器和跨多种机器人类型的预训练来应对这一问题。

  • 泛化在**保留任务(held-out tasks)**上评估:要求机器人执行它从未训练过的任务。在新任务上 50–80% 的成功率被认为是强劲的结果,相比之下分布内任务能超过 90%。随着模型规模扩大和机器人数据集增长,这个差距正在缩小。

与本体无关的模型

  • 这个领域正在走向一个模型,多种机器人。与其为每台机器人单独训练一个策略,不如用单个 VLA 处理多种本体。

  • 这需要解决动作空间不匹配问题。一个 7-DoF 臂配平行爪夹爪有 7 个动作维度。双臂装置有 14 个。四足有 12 个。人形有 30 多个。动作分词必须足够灵活,才能处理所有这些情况。

  • 解决方案包括:

    • 填充动作向量:使用最大的动作空间,把较小的用零填充。
    • 每个本体一个动作头:共享的 transformer 主干,配以每种机器人类型各一个小 MLP。
    • 归一化的动作表示:把所有动作表达在一个公共坐标系中(如世界坐标系下的末端执行器速度),这样产生相似末端执行器运动的不同机器人就共享相同的动作 token。
  • 共享的主干学习通用的视觉和语言理解,以及常见的操作策略(从上方接近、与物体对齐、闭合夹爪)。本体相关的组件只需把这些高层策略翻译成具体的电机命令。

基准与评测

  • 评测 VLA 独特地困难,因为它需要真实机器人实验(或高保真仿真)。

  • SIMPLER(Simulated Manipulation Policy Evaluation for Robot learning)为在不依赖物理硬件的情况下比较 VLA 性能提供了标准化的仿真环境。它与真实世界成功率相关性很好,并支持可复现的基准测试。

  • 真实世界评测仍是黄金标准。典型流程:

    1. 定义一组有明确成功标准的任务(物体到达目标位置、选对物体、在时限内完成任务)。
    2. 每个任务跑 N 次试验(通常 10–50 次)。
    3. 报告带置信区间的成功率。
    4. 包含保留的(从未训练过的)任务以衡量泛化。
  • Open X-Embodiment 数据集和基准聚合了来自 22 家机构、跨多种机器人平台的机器人数据。它提供了共享演示的标准化格式,以及一套用于跨本体迁移的公共评测套件。

编程练习(使用 CoLab 或 notebook)

  1. 实现动作分词:把连续动作离散化到箱中,再重建回来。观察量化误差随箱数的变化。
import jax.numpy as jnp # 连续动作:7 维(6 DoF + 夹爪) 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"bins={n_bins:4d} tokens={tokens} error={error:.6f}")
  1. 模拟动作分块与单步预测的对比。生成一条平滑轨迹,给单步预测加噪声,并与基于块的预测比较。
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="Ground truth") plt.plot(single_step_cumulative[:, 0], single_step_cumulative[:, 1], "r-", alpha=0.7, label="Single-step (drifts)") plt.plot(chunked[:, 0], chunked[:, 1], "b-", alpha=0.7, label="Chunked (stable)") plt.legend(); plt.axis("equal"); plt.grid(True) plt.title("Action Chunking vs Single-Step Prediction") plt.show()
  1. 可视化 VLA 的动作分布如何呈现多模态。用一个简单的二维高斯混合来说明为什么扩散/流匹配动作头比回归更可取。
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="True action distribution") plt.plot(*mean_pred, "rx", markersize=15, markeredgewidth=3, label="Regression mean (invalid!)") plt.plot(-1, 0.5, "g^", markersize=12, label="Mode 1 (go left)") plt.plot(1, 0.5, "b^", markersize=12, label="Mode 2 (go right)") plt.legend(); plt.grid(True) plt.title("Multimodal Actions: Why Regression Fails") plt.xlabel("Action dim 1"); plt.ylabel("Action dim 2") plt.show()

作者与出处
原作者: HenryNdubuaku
来源:HenryNdubuaku
许可证:Apache-2.0
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: HenryNdubuaku 转发
评论区 (0)
U