从VLA到VLM——VLA-0
·
VLA-0 是一款开创性的视觉 - 语言动作(Vision-Language-Action, VLA)模型,其核心理念是零架构修改适配主流预训练VLM模型,无需改造模型结构、分词器和训练逻辑,即可将纯文本大模型快速升级为具备视觉感知、语言理解与动作执行能力的 SOTA 级 VLA 模型。
———VLA-0: Building State-of-the-Art VLAs with Zero Modification
这篇文章的核心技术是不修改底层视觉语言模型(VLM)的架构,直接将机器人动作表示为文本字符串,利用 VLM 的原生文本生成能力构建视觉 - 语言 - 动作模型(VLA),即 VLA-0。
本文对VLA-0的代码进行整理并且重构,训练速度提升20倍,模型可替换,搭建了一个基础框架方便大家做科研。
支持功能:deepspeed,lora,finetune,multiple-gpus,multiple-datasets
代码仓库:https://github.com/garlic-byte/VLA-0-Speedup.git
https://github.com/garlic-byte/VLA-0-Speedup.git
一、 核心技术内容
- 输入输出设计
- 输入包含系统提示、任务指令文本和多视角图像,无需对 VLM 做任何结构改动。
- 输出将连续的机器人动作(如关节角度、末端执行器坐标)归一化到固定整数范围,再转化为空格分隔的文本字符串。
- 关键训练与推理策略
- 掩码动作增强:训练时随机掩码目标动作字符串中的部分字符,强制模型依赖视觉观察和任务指令推理动作,而非依赖自回归的序列补全。
- 集成预测:推理时融合多个历史时间步对当前动作的预测结果,提升动作输出的稳定性。
二、 核心创新点
- 极简架构设计,零修改 VLM
- 摒弃传统 VLA 的三类复杂方案,包括离散动作令牌化、新增生成式动作头、自定义架构。
- 直接复用 VLM 的文本生成能力,无需改动词汇表、添加新网络层或设计专用分词器,最大程度保留 VLM 原有的语言理解和视觉 grounding 能力。
- 端到端动作文本生成,超越两阶段方案
- 不同于 LLARVA 等两阶段模型,VLA-0 无需先生成 2D 轨迹再预测动作,可直接端到端生成完整的机器人动作文本序列。
- 该设计在保证动作精度的同时,简化了模型流程,降低了训练和部署的复杂度。
- 轻量化方案实现 SOTA 性能,无需大规模预训练
- 在 LIBERO 基准测试中,未经过大规模机器人动作数据预训练的 VLA-0,性能超过了所有同量级训练的模型,还优于多款大规模预训练的 VLA 模型。
- 在真实机器人实验中,VLA-0 相比基于大规模数据集预训练的 SmolVLA,平均成功率提升 12.5 个百分点,验证了方案的实用性和泛化性。
三、重构训练架构
1. 批量对动作进行掩码,提升效率
def __call__(self, features: List[Dict[str, any]]) -> BatchFeature:
"""
Apply random mask to actions from VLM inputs.
:param features: List of VLM inputs which contains text, images, question_length.
:return: Integrate features into a batch.
"""
text_ls = [element['text'] for element in features]
images_ls = [element['image'] for element in features]
qus_len_ls = [element['qus_len'] for element in features]
# Integrate text and images into a batch
vlm_inputs = self.processor(
text=text_ls, images=images_ls, return_tensors="pt", padding=True
)
labels = vlm_inputs["input_ids"].clone()
labels[labels == 151643] = -100
batch_size, seq_len = labels.shape
qus_len_tensor = torch.tensor(qus_len_ls)
# Create mask for actions
pos_id = torch.arange(seq_len).unsqueeze(0).repeat(batch_size, 1)
action_mask = pos_id > qus_len_tensor.unsqueeze(1)
# element equal to -100 will not calculate the loss
labels[~action_mask] = -100
# Create random mask
random_values = torch.rand((batch_size, seq_len))
action_mask &= random_values < self.mask_ratio
# Apply mask into labels and input_ids
labels[action_mask] = -100
vlm_inputs["input_ids"][action_mask] = 30 # element equal to 30 represent '?'
# Reshape vlm inputs to batch shape
vlm_inputs["input_ids"] = vlm_inputs["input_ids"].view(batch_size, 1, seq_len)
vlm_inputs["attention_mask"] = vlm_inputs["attention_mask"].view(batch_size, 1, seq_len)
vlm_inputs["pixel_values"] = vlm_inputs["pixel_values"].view(batch_size, -1, vlm_inputs["pixel_values"].size(1))
vlm_inputs["image_grid_thw"] = vlm_inputs["image_grid_thw"].view(batch_size, -1, vlm_inputs["image_grid_thw"].size(1))
vlm_inputs["labels"] = labels.view(batch_size, 1, seq_len)
return BatchFeature(data={**vlm_inputs})
2. 使用Qwen3-VL作为基座模型,可拆卸
def _setup_model(self):
nvtx.range_push("Load model")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
assert device.type == "cuda", "GPUs is not supported."
model = Qwen3VLForConditionalGeneration.from_pretrained(
self.config.model_path, dtype=self.config.dtype, device_map=device
)
# Freeze all parameters
for param in model.parameters():
param.requires_grad = False
finetune_modules = []
# Activate parameters according configuration
# Use Lora for training model
if self.config.lora_rank > 1:
from peft import LoraConfig, get_peft_model
# Only train part of language module for lora tune as default
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
lora_config = LoraConfig(
r=self.config.lora_rank,
lora_alpha=self.config.lora_alpha,
lora_dropout=self.config.lora_dropout,
bias="none",
target_modules=target_modules,
)
model = get_peft_model(model, lora_config)
finetune_modules.append("Lora: ")
finetune_modules.append(target_modules)
# Only training partly parameters of model
else:
if self.config.tune_llm:
for param in model.model.language_model.parameters():
param.requires_grad = True
finetune_modules.append("Language modules")
if self.config.tune_visual:
for param in model.model.visual.parameters():
param.requires_grad = True
finetune_modules.append("Visual modules")
total_params = sum(p.numel() for p in model.parameters())
total_trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
logging_model_load(
model_path=self.config.model_path,
finetune_modules=finetune_modules,
total_params=total_params,
total_trainable_params=total_trainable_params,
)
nvtx.range_pop()
return model
3. 使用线程提前加载数据集,提升训练速度20倍
def _start_get_shard(self):
"""Get shard from dataset through thread."""
if self.cur_sample_index >= len(self.filtered_sample_indices):
self._reset_environment()
# Sample from filtered_sample_indices
index_dataset, indices_shard = self.filtered_sample_indices[self.cur_sample_index]
self._cache_job = self._executor.submit(
self.datasets[index_dataset].get_shard, indices_shard
)
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)