OpenVLA 概述:开源版的通用 VLA 模型
随着机器人动作预测策略日趋成熟(如 ACT、Diffusion Policy),为了提升泛化能力,利用预训练大语言模型的广泛知识并增加 Policy Head 成为典型路径。随后出现了更多成熟的专用 VLA 模型,例如 OpenVLA。
通用机器人的核心发展方向在于「预训练 - 微调」模式,即通过同一套策略算法操作不同机器人。虽然 π0 等模型代表了这一方向,但 OpenVLA 作为早期开源方案,其架构思路依然极具参考价值。它相当于 RT-2 的开源版,支持开箱即用控制多个机器人,并能通过参数高效微调适应新环境。
基于 Open-X Embodiment 数据的 7B 模型微调
2024 年 6 月,来自斯坦福大学、UC Berkeley、Google DeepMind 等机构的研究者推出了 OpenVLA。其论文《OpenVLA: An Open-Source Vision-Language-Action Model》详细阐述了模型细节。
这是一个具有 70 亿参数的开源视觉 - 语言 - 动作模型(VLA)。它由一个预训练的视觉条件语言模型骨干组成,在 Open-X Embodiment 数据集中的 97 万条机器人操作轨迹上进行了微调。该数据集涵盖了广泛的机器人形态、任务和场景。
背景与动机
当前机器人策略存在关键弱点:无法超越训练数据进行泛化。虽然现有策略能在物体位置或光照变化下外推行为,但在面对场景干扰物或新颖物体时缺乏鲁棒性。
相比之下,CLIP、SigLIP 和 Llama 2 等基础模型具备强大的泛化能力。尽管在机器人领域复现大规模预训练仍是难题,但这带来了机遇:可以利用现有的视觉和语言基础模型作为核心构建模块。
现有研究已探索将预训练模型整合用于机器人表征学习。最近,这些模型被直接用于学习视觉 - 语言 - 动作模型(VLAs)以进行控制。依托于互联网规模数据训练的基础模型,诸如 RT-2 等 VLA 展现出令人印象深刻的鲁棒性。
然而,现有 VLA 尚未广泛应用,主要原因有二:
- 闭源模型透明度有限;
- 缺乏部署适配至新型机器人及消费级硬件的最佳实践。
为此,作者推出了 OpenVLA,采用端到端方法,直接将机器人动作视为语言模型词汇表中的 token 来生成控制指令。
模型架构:Prismatic-7B VLM
大多数最新 VLM 的架构一般由三部分组成:视觉编码器、投影器、大型语言模型(LLM)骨干。
OpenVLA 基于 Prismatic-7B VLM 构建,包含以下组件:
- 视觉编码器:600M 参数,由预训练的 SigLIP 和 DinoV2 模型组成。输入图像块分别通过这两个编码器,随后特征向量按通道拼接。添加 DinoV2 特征有助于改善空间推理,这对机器人控制尤为重要。
- 注:后续更高效的模型如 π0 仅使用 SigLIP,而 OpenVLA 早期版本 openvla-v01-7b 也证明了单一 SigLIP 骨干仍能取得强劲性能。
- 投影器:两层小型 MLP。
- 语言模型骨干:70 亿参数的 Llama 2。
选择 Prismatic 的原因在于其融合了 SigLIP-DinoV2 主干提供的改进空间推理能力,且提供了模块化且易于使用的代码库。
关于训练细节:
- 分辨率:最终选择 224×224 像素,因为 384×384 虽计算量大三倍,但评估中未发现性能差异。
- 轮次:VLA 训练需显著增加迭代次数,实际性能直到动作 token 准确率超过 95% 才趋于稳定,最终完成了 27 个轮次。
- 学习率:固定学习率 2e-5 效果最佳,无需预热。
训练机制:离散化动作预测
为了训练 OpenVLA,作者对预训练的 Prismatic-7B VLM 主干进行微调,以实现机器人动作预测。
作者将动作预测问题表述为'视觉 - 语言'任务,输入的观测图像和自然语言任务指令被映射为一串预测的机器人动作。
- 动作离散化:为了使 VLM 能够预测动作,作者将连续的机器人动作映射到语言模型的分词器使用的离散 token。类似 RT-2 的方法,将每个动作维度分别离散化为 256 个区间中的一个。
- Token 覆盖:Llama 分词器仅为微调预留了 100 个特殊 token,不足以满足 256 个动作 token 的需求。因此,作者简单地用动作 token 覆盖 Llama 分词器词表中频率最低的 256 个 token(即最后 256 个 token)。
- 损失计算:一旦动作处理为 token 序列,OpenVLA 就采用标准的下一个 token 预测目标进行训练,仅对预测的动作 token 计算交叉熵损失。
对于每个动作维度,设置区间宽度使其在训练数据动作的第 1st 和第 99th 分位数之间均匀划分。使用分位数而非最小 - 最大边界,可以忽略异常动作,避免扩展离散化区间从而降低有效精度。
训练数据与基础设施
OpenVLA 使用 Open X-Embodiment 数据集(OpenX)作为基础。完整的 OpenX 数据集包含超过 70 个单独的机器人数据集,超过 200 万条轨迹。
数据整理步骤包括:
- 输入输出空间一致:限制仅包含至少有一个第三人称摄像机的操作数据集,并使用单臂末端执行器控制。
- 平衡 Embodiments:利用 Octo 的数据混合权重处理筛选后的数据集,降低多样性较低的数据集权重,增大任务多样性大的数据集权重。
此外,作者尝试整合 DROID 数据集,但发现其动作 token 准确率较低,故在最后三分之一训练中将 DROID 移除以保证模型质量。
最终的 OpenVLA 模型在一个由 64 个 A100 GPU 组成的集群上训练了 14 天,总计 21,500 A100 小时,批量大小为 2048。
推理方面,OpenVLA 在加载为 bfloat16 精度时需要 15GB 显存,在一块 NVIDIA RTX 4090 GPU 上以大约 6Hz 的速度运行。通过量化可进一步减少内存占用而不影响性能。
实验对比与微调策略
与 RT-2 的横向对比
作者在 BridgeData V2 评估的 WidowX 机器人和 Google Robot 平台上测试了 OpenVLA。
- Google 机器人:RT-2-X 和 OpenVLA 表现相当,均优于 RT-1-X 和 Octo。
- BridgeData V2:OpenVLA 在大多数任务中表现最强,总体成功率最高。尽管 OpenVLA 规模小了一个数量级(7B vs 55B),但其融合视觉编码器和更仔细的清理(如过滤全零动作)弥补了差距。
值得注意的是,RT-2-X 在语义泛化任务中取得了更高的性能,这与其使用了更大规模的互联网预训练数据有关。
与 Diffusion Policy 对比
在针对特定任务的微调实验中:
- 单一指令任务:Diffusion Policy 显示出更平滑和精确的轨迹,适合狭窄但高度灵活的任务。
- 多对象/语言调节任务:预训练的通用策略(Octo、OpenVLA)表现更佳,得益于 OpenX 预训练带来的适应能力。
参数高效微调:LoRA
实验比较了全量微调、冻结视觉、Sandwich Fine-tuning 和 LoRA。
- 结果:仅微调最后一层或冻结视觉导致性能不佳。LoRA 在性能和训练内存消耗之间实现了最佳平衡,优于 Sandwich Fine-tuning。
- 效率:使用 LoRA,可以在单个 A100 GPU 上在 10-15 小时内微调 OpenVLA 到新任务上,计算减少了 8 倍。
Prismatic VLM 架构与设计空间
设计轴探索
Prismatic VLMs 工作探索了开发 VLM 的四个关键设计轴:优化过程、图像处理与视觉表示、语言模型、以及扩展属性。
- 优化过程:发现多阶段训练程序可以在不影响性能的情况下消除,减少 20-25% 的计算成本。
- 视觉表示:融合来自不同骨干的特征(如 CLIP 和 DINOv2)能导致更高性能的 VLM。
- 语言模型:基础语言模型(如 Llama-2)的性能与指令调优语言模型相当或更好。
- 扩展属性:增加多样化的数据和延长训练时间显著提升性能。
关键设计考量
多阶段训练
许多 VLM 采用两阶段训练流程(对齐阶段 + 微调阶段)。实验发现,微调包括视觉骨干在内的完整模型反而降低了性能,尤其是在需要细粒度空间推理的任务上。因此,保持视觉骨干不变是更优选择。
图像处理与视觉表示
- 骨干选择:使用视觉 - 语言对比目标训练的骨干网络(CLIP, SigLIP)性能显著优于其他选择。
- 图像缩放:对于 CLIP,"naive resize"(拉伸)方案表现最佳;对于 SigLIP,"naive resize" 和 "letterbox padding" 表现相似。总体而言,倾向于 "naive resizing"。
- 分辨率:缩放到 336px 或 384px 带来显著改进,代价是计算复杂度提升。
- 特征融合:融合 DINOv2 和 SigLIP 特征在整体上提供了显著提升,DINOv2 提供了低级空间属性,增强了高级语义属性。
语言模型选择
指令微调语言模型导致的 VLM 更加冗长且易出现幻觉。基础语言模型(如 Llama-2)在性能上并未劣于指令微调变体。
训练时间与数据
单轮训练存在严重欠拟合,两轮训练比一轮有显著改进。增加多样化数据和延长训练时间是提升性能的关键。
核心源码剖析:ActionTokenizer
整个代码仓库主要包含 vla-scripts(训练脚本)、prismatic(核心工具包)等部分。
ActionTokenizer 类逻辑
prismatic/vla/action_tokenizer.py 中的 ActionTokenizer 类负责将连续的机器人动作离散化为 N 个区间,并映射到最少使用的 token 上。
初始化
构造函数接受基础 Tokenizer、区间数量、最小/最大动作值。默认假设使用 BPE 风格 tokenizer,其中最少使用的 token 出现在词汇表末尾。
class ActionTokenizer:
def __init__(self, tokenizer: PreTrainedTokenizerBase, bins: int = 256, min_action: int = -1, max_action: int = 1) -> None:
self.tokenizer, self.n_bins, self.min_action, self.max_action = tokenizer, bins, min_action, max_action
# 创建均匀分布的区间并计算中心
self.bins = np.linspace(min_action, max_action, self.n_bins)
self.bin_centers = (self.bins[:-1] + self.bins[1:]) / 2.0
# 设定 action_token_begin_idx,覆盖词汇表最后的 n_bins 个 token
self.action_token_begin_idx = int(self.tokenizer.vocab_size - (self.n_bins + 1))
离散化调用
__call__ 方法将动作裁剪并离散化为词汇表中最后的 n_bins 个 token。
def __call__(self, action: np.ndarray) -> Union[str, List[str]]:
# 裁剪动作到指定范围
action = np.clip(action, a_min=float(self.min_action), a_max=float(self.max_action))
# 离散化
discretized_action = np.digitize(action, self.bins)
# 解码为 token 字符串
if len(discretized_action.shape) == 1:
return self.tokenizer.decode(list(self.tokenizer.vocab_size - discretized_action))
else:
return self.tokenizer.batch_decode((self.tokenizer.vocab_size - discretized_action).tolist())
这里需要注意,由于覆盖了词汇表末尾的 token,解码时需要用 vocab_size - discretized_action 来还原索引。
反序列化
decode_token_ids_to_actions 方法将离散的动作 token ID 转换回连续的动作值。
def decode_token_ids_to_actions(self, action_token_ids: np.ndarray) -> np.ndarray:
# 计算离散化动作值
discretized_actions = self.tokenizer.vocab_size - action_token_ids
# 限制在有效区间范围内
discretized_actions = np.clip(discretized_actions - 1, a_min=0, a_max=self.bin_centers.shape[0] - 1)
# 返回对应的 bin centers
return self.bin_centers[discretized_actions]
这种设计有效地将离散的 token ID 转换回了连续的动作值,便于机器人控制器直接使用。

