1. VLA模型基础架构搭建
任务说明
构建一个最小可运行的VLA(Vision-Language-Action)模型基础框架,包含视觉编码器、语言编码器和动作解码器三个核心模块,并实现完整的前向传播流程。输入为图像和文本指令,输出为连续动作向量。
环境依赖
需要 Python 3.9+、PyTorch 2.0+、torchvision、transformers。若使用预训练权重需联网下载,本案例为演示自包含实现,不依赖外部预训练模型。
核心实现
import torch
import torch.nn as nn
import torch.nn.functional as F
from transformers import AutoTokenizer
class VisionEncoder(nn.Module):
"""视觉编码器:将图像转换为视觉序列"""
def __init__(self, img_size=224, patch_size=16, in_ch=3, embed_dim=512):
super().__init__()
self.patch_size = patch_size
self.n_patches = (img_size // patch_size) ** 2
self.proj = nn.Conv2d(in_ch, embed_dim, kernel_size=patch_size, stride=patch_size)
self.cls_ = nn.Parameter(torch.randn(1, 1, embed_dim))
self.pos_embed = nn.Parameter(torch.randn(1, self.n_patches + 1, embed_dim))
self.norm = nn.LayerNorm(embed_dim)
def forward(self, x):
# x: (B, C, H, W)
B = x.shape[0]
x = self.proj(x) # (B, D, H/P, W/P)
x = x.flatten(2).transpose(1, 2) # (B, N, D)
x = torch.cat([self.cls_.expand(B, -1, -1), x], dim=1)
x = x + self.pos_embed
return self.norm(x) # (B, N+1, D)
class LanguageEncoder(nn.Module):
"""语言编码器:将文本指令编码为语言序列"""
def __init__(self, vocab_size=30522, embed_dim=512, max_len=64):
super().__init__()
self.embed = nn.Embedding(vocab_size, embed_dim)
self.pos_embed = nn.Parameter(torch.randn(1, max_len, embed_dim))
self.norm = nn.LayerNorm(embed_dim)
def forward(self, input_ids, attention_mask=None):
# input_ids: (B, L)
B, L = input_ids.shape
x = self.embed(input_ids) # (B, L, D)
x = x + self.pos_embed[:, :L, :]
x = self.norm(x)
if attention_mask is not None:
x = x * attention_mask.unsqueeze(-1)
return x # (B, L, D)
class ActionDecoder(nn.Module):
"""动作解码器:融合视觉与语言特征,输出连续动作"""
def __init__(self, embed_dim=512, action_dim=7, nhead=8, num_layers=2):
super().__init__()
self.action_dim = action_dim
# 可学习的动作查询向量
self.query = nn.Parameter(torch.randn(1, 1, embed_dim))
decoder_layer = nn.TransformerDecoderLayer(d_model=embed_dim, nhead=nhead, batch_first=True)
self.decoder = nn.TransformerDecoder(decoder_layer, num_layers=num_layers)
self.fc_out = nn.Linear(embed_dim, action_dim)
def forward(self, vis_feat, lang_feat):
# vis_feat: (B, Nv, D), lang_feat: (B, Nl, D)
B = vis_feat.shape[0]
query = self.query.expand(B, -1, -1) # (B, 1, D)
# 将视觉和语言特征拼接作为decoder的memory
memory = torch.cat([vis_feat, lang_feat], dim=1) # (B, Nv+Nl, D)
out = self.decoder(query, memory) # (B, 1, D)
action = self.fc_out(out).squeeze(1) # (B, action_dim)
return action
class VLA(nn.Module):
"""VLA模型:视觉编码器 + 语言编码器 + 动作解码器"""
def __init__(self, img_size=224, patch_size=16, embed_dim=512,
vocab_size=30522, action_dim=7, max_len=64):
super().__init__()
self.vision_encoder = VisionEncoder(img_size, patch_size, 3, embed_dim)
self.language_encoder = LanguageEncoder(vocab_size, embed_dim, max_len)
self.action_decoder = ActionDecoder(embed_dim, action_dim)
def forward(self, image, input_ids, attention_mask=None):
vis_feat = self.vision_encoder(image) # (B, Nv, D)
lang_feat = self.language_encoder(input_ids, attention_mask) # (B, Nl, D)
action = self.action_decoder(vis_feat, lang_feat) # (B, action_dim)
return action
# ========== 最小可运行示例 ==========
if __name__ == "__main__":
# 初始化模型
model = VLA(img_size=224, patch_size=16, embed_dim=512,
vocab_size=30522, action_dim=7, max_len=64)
# 构造输入:随机图像 (B=2, C=3, H=224, W=224)
image = torch.randn(2, 3, 224, 224)
# 构造文本输入:使用transformers的tokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
texts = ["move the arm forward", "grasp the red block"]
encodings = tokenizer(texts, padding=True, truncation=True, max_length=64, return_tensors="pt")
input_ids = encodings["input_ids"]
attention_mask = encodings["attention_mask"]
# 前向传播
with torch.no_grad():
action = model(image, input_ids, attention_mask)
print(f"输入图像形状: {image.shape}")
print(f"输入文本形状: {input_ids.shape}")
print(f"输出动作形状: {action.shape}")
print(f"输出动作值范围: [{action.min().item():.3f}, {action.max().item():.3f}]")
运行方式
pip install torch torchvision transformers
python vla_model.py
验证方法
运行后观察输出:
- 输出动作张量形状应为
(2, 7),对应2个样本各7维动作向量(如机械臂的关节角度或末端位姿); - 前向传播无报错,且输出值在合理数值范围内(非NaN/Inf);
- 修改输入文本或图像,输出动作应随之变化,说明模型成功融合了视觉与语言信息。
关键说明
- 本案例为教学演示,使用随机初始化的编码器,未加载预训练权重,因此输出动作无实际物理意义,仅验证框架正确性;
- 实际应用中,需将视觉编码器替换为预训练ViT/ResNet,语言编码器替换为预训练BERT,并在机器人数据集上训练动作解码器;
- 动作维度
action_dim=7对应常见机械臂的7自由度关节角度,可根据实际机器人调整。