modeling_prismatic.py — 这是 OpenVLA-OFT 模型的核心实现文件(约 2000 行)¶
文件路径:
verl/experimental/vla/models/openvla_oft/modeling_prismatic.py模块路径:verl.experimental.vla.models.openvla_oft.modeling_prismatic
文件概述¶
这是 OpenVLA-OFT 模型的核心实现文件(约 2000 行),包含视觉骨干、多模态投影器、条件生成模型和动作预测模型。这是整个 VLA 系统中最复杂的文件。
架构概览¶
输入: 图像 + 文本提示 ("What action should the robot take to ...?")
↓
┌─────────────────────────────────────────┐
│ PrismaticVisionBackbone (SigLIP/DINOv2)│ ← 视觉特征提取
│ ↓ │
│ PrismaticProjector (MLP) │ ← 投影到语言空间
│ ↓ │
│ LLM (Llama/Mistral) │ ← 多模态推理
│ ↓ │
│ 动作 Token Logits → 离散化 → 连续动作 │ ← 动作预测
└─────────────────────────────────────────┘
一、PrismaticVisionBackbone - 视觉骨干¶
支持单骨干和双骨干融合(SigLIP + DINOv2):
class PrismaticVisionBackbone(nn.Module):
def __init__(self, use_fused_vision_backbone, image_sizes,
timm_model_ids, timm_override_act_layers):
# 主骨干(SigLIP)
self.featurizer = timm.create_model(timm_model_ids[0], ...)
self.embed_dim = self.featurizer.embed_dim
# 融合骨干(DINOv2,可选)
if use_fused_vision_backbone:
self.fused_featurizer = timm.create_model(timm_model_ids[1], ...)
self.embed_dim += self.fused_featurizer.embed_dim
def forward(self, pixel_values):
if not self.use_fused_vision_backbone:
return self.featurizer(pixel_values)
# 融合模式:分割通道 -> 分别提取 -> 拼接
img, img_fused = torch.split(pixel_values, [3, 3], dim=1)
patches = self.featurizer(img)
patches_fused = self.fused_featurizer(img_fused)
return torch.cat([patches, patches_fused], dim=2)
二、PrismaticProjector - 多模态投影器¶
将视觉特征投影到语言模型的嵌入空间:
class PrismaticProjector(nn.Module):
def __init__(self, use_fused_vision_backbone, vision_dim, llm_dim):
if not use_fused_vision_backbone:
# 简单 MLP: vision_dim -> llm_dim
self.fc1 = nn.Linear(vision_dim, llm_dim)
self.fc2 = nn.Linear(llm_dim, llm_dim)
else:
# 更大的 MLP: vision_dim -> 4*vision_dim -> llm_dim -> llm_dim
self.fc1 = nn.Linear(vision_dim, 4 * vision_dim)
self.fc2 = nn.Linear(4 * vision_dim, llm_dim)
self.fc3 = nn.Linear(llm_dim, llm_dim)
三、OpenVLAForActionPrediction - 动作预测模型¶
这是最终的用户接口类,继承自 PrismaticForConditionalGeneration。
动作离散化¶
OpenVLA 将连续动作离散化为 256 个 bin:
class OpenVLAForActionPrediction(PrismaticForConditionalGeneration):
def __init__(self, config):
super().__init__(config)
# 创建 256 个均匀分布的 bin,范围 [-1, 1]
self.bins = np.linspace(-1, 1, config.n_action_bins)
self.bin_centers = (self.bins[:-1] + self.bins[1:]) / 2.0
# 词表大小(去掉填充部分)
self.vocab_size = config.text_config.vocab_size - config.pad_to_multiple_of
verl 推理方法 generate_action_verl¶
这是 verl 框架使用的主要推理方法,支持批量和采样:
def generate_action_verl(self, input_ids, pixel_values, attention_mask,
padding_idx, do_sample, unnorm_key, temperature):
# 1. 创建 labels(用于动作 mask 计算)
labels = input_ids.clone()
labels[:] = IGNORE_INDEX
# 2. 添加占位动作 token 和停止 token
input_ids, attention_mask = self._prepare_input_for_action_prediction(
input_ids, attention_mask)
# 3. 处理左填充
# ... (重新排列 padding 到左侧)
# 4. 提取视觉特征并投影
patch_features = self.vision_backbone(pixel_values)
projected_patch_embeddings = self.projector(patch_features)
# 5. 构建多模态嵌入
# [BOS] + [视觉 patches] + [文本 tokens] + [动作 tokens] + [STOP]
# 6. 前向传播获取 logits
language_model_output = self.language_model(inputs_embeds=...)
# 7. 从 logits 中提取动作预测
# 关键切片:只取词表末尾 256+64 中的 256 个 token
action_logits = logits[..., -256-64:-64]
动作反归一化¶
def _unnormalize_actions(self, normalized_actions, unnorm_key):
"""将 [-1, 1] 范围的归一化动作转换回原始尺度"""
action_stats = self.get_action_stats(unnorm_key)
action_high = np.array(action_stats["q99"])
action_low = np.array(action_stats["q01"])
actions = 0.5 * (normalized_actions + 1) * (action_high - action_low) + action_low
return actions
四、多模态嵌入构建流程¶
输入序列: [BOS] [text_tokens...] [action_placeholders] [STOP]
↓
嵌入层: [BOS_emb] [text_embs...] [action_embs(清零)] [stop_emb]
↓ 插入视觉嵌入(在 BOS 后面)
最终: [BOS_emb] [patch_embs...] [text_embs...] [action_embs] [stop_emb]
↓
attention_mask 也相应扩展
核心类列表¶
| 名称 | 说明 |
|---|---|
PrismaticVisionBackbone |
视觉骨干(TIMM,支持融合) |
PrismaticProjector |
视觉到语言的投影器 |
PrismaticPreTrainedModel |
HF 预训练模型基类 |
PrismaticForConditionalGeneration |
条件生成模型 |
OpenVLAForActionPrediction |
动作预测模型(最终接口) |
与其他模块的关系¶
- 使用
constants.py的动作常量 - 使用
train_utils.py的 mask 计算函数 - 使用
configuration_prismatic.py的配置类 - 被
naive_rollout_rob.py调用做推理 - 被
register_vla_models.py注册到 HuggingFace
小结¶
这个文件实现了从图像+文本到机器人动作的完整推理链。核心创新是将连续动作离散化为词表中的 token,使得可以用标准的语言模型架构来预测机器人动作。理解 vocab_size-256-64:-64 这个切片范围是理解整个模型的关键。