在 VLAVision-Language-Action模型的训练和部署中“L无用”的说法每隔一段时间就会出现一次。现象通常很一致把语言指令从输入里删掉动作预测的成功率没有明显下降甚至个别任务上还会更好。接触过具身操作项目的工程师多数会在某个阶段怀疑语言分支是不是只是占位符。我自己的判断是“L无用”这个结论大概率把因果看反了。真正的问题不是语言模态没有价值而是语言指令没有被放进正确的位置。这里的“位置”既指句子里的空间语义也指它在模型序列、注意力层和训练数据中的角色。当位置指令真正落到视觉区域并把文本 token 注入到决策层能够持续访问的地方任务级泛化和组合泛化会明显拉开差距。在抓取、摆放和排序这类常见拆解任务里这种差距可以达到 20%-40% 的相对提升。这篇文章会把概念、代码、实验和排错串起来讲清楚这条链路到底应该怎么搭。1. VLA 里的「L」到底承担什么为什么会被判无用1.1 “L”是语言条件不是任务标题VLA 模型的输入通常是三部分视觉观测、文本指令和动作标签。视觉观测可以是单帧图像也可以是多帧短视频序列文本指令用自然语言描述当前要完成的目标模型输出的是离散动作 token 或连续动作序列。这里的“L”就是 Language也就是语言条件。很多人会把语言指令理解成任务标题比如“把红色方块放到左侧托盘”。这句话表面是一个标题实际在训练和推理中起到的是条件约束作用。模型面对同一个桌面场景时可能存在多个可执行任务把方块放到左侧托盘、把方块放到右侧托盘、把方块按颜色分类摆好。如果只有图像模型无法确定当前应该执行哪一个意图语言指令的作用就是把多任务空间压缩到当前一个分支上。语言条件真正重要的地方在于抽象性。视觉上“红色方块”可以因为光照、角度、背景发生变化但指令文本“红色方块”是一个稳定的符号视觉上“左侧托盘”会因为相机视角不同而改变像素坐标但语言里“左侧”提供的是相对空间关系。模型如果能学会从语言符号到视觉区域的映射就能在未见过的颜色、物体组合和摆放模式下依然做出正确动作。这也是谈论 VLA 泛化能力时最核心的支撑点。1.2 “无用感”通常来自三个具体机制如果语言真的没用那 VLA 完全可以叫 VA。但项目里出现“删掉语言后成功率不降”的现象往往不是模态没用而是语言信号在传播链路上被截断了。第一个机制是语言 token 被长序列稀释。许多实现会把文本 token 和视觉 token 拼接成一个整体序列文本放在最前面。Transformer 处理长序列时越靠后的 token 携带的信息越强。如果视觉 token 有几十甚至上百个动作 token 在后面生成语言 token 对最终动作的影响会被大幅压缩。第二个机制是视觉捷径在训练时占据主导。若训练数据中同一个物体颜色和同一个目标位置反复出现模型不需要读语言也能从视觉特征推断出动作。例如数据集里红色方块永远只出现在左侧托盘附近那么模型只要看到红色就能猜出要放到左侧。训练损失会把视觉分支推到更优解语言分支的梯度贡献趋近于零最终表现为“语言没用”。第三个机制是位置指令语义模糊。指令写“把红色方块放到托盘里”但桌面上有两个托盘。模型不知道放到哪一个语言条件变成一个无效信息。这种情况下模型只能靠视觉猜测或者干脆忽略语言。它不是在学习语言而是在学习一个没有区分度的噪声标签。这三类问题叠加很容易让评估者得出“L无用”的结论。但拆开看第一类是注入位置问题第二类是训练数据问题第三类是指令内容问题都不是语言本身失效。1.3 语言条件的不同形态决定了泛化上限语言条件在模型里的形态不同最终表现差异很大。下面对比几种常见处理方式。语言处理方式模型实际看到的信息典型风险泛化表现只编码成一个全局 CLS 向量语言被压缩成一条向量信息密度极低决策层基本读不到细节组合泛化差文本 token 与视觉 token 前后拼接开头位置的语言对后续注意力递减长序列稀释语言信号中等但不稳定每层交叉注意力访问语言 token每个动作 token 都能查询语言特征实现成本偏高稳定提升语言 token 与视觉区域显式绑定语言中名词对应具体 bbox需要额外标注或检测器组合泛化上限最高从工程角度看先不要争论“语言有没有用”而是先确认语言条件以什么形态进入决策层。如果连语言特征都无法被后续头部访问那无论模型多大都会得到 L 无用的结论。下一节会把“位置”这个概念拆细因为标题里的“用对位置指令”其实包含三个不同维度。2. 把“位置指令”拆开文本空间、token 序列和时间线都要对齐2.1 文本中的空间位置指令要能落回视觉坐标“位置指令”最容易联想到的是文本里表达空间关系的词左、右、上、下、内侧、外侧、旁边、附近。位置指令的目标是让模型知道物体应该去往哪个空间区域同时把文本里的方位词映射到图像坐标上。举个例子。指令 A 是“把红色方块放到托盘里”视觉画面里有两个托盘。这个指令对模型来说没有提供足够的位置约束它必须在画面里寻找“哪个托盘”。指令 B 是“把红色方块放到左侧托盘”这里多了一个方位词“左侧”模型需要把“左侧”翻译成图像中 x 坐标较小的托盘区域。指令 C 是“以左侧托盘为参照把红色方块放到托盘内侧”这种相对位置表达更复杂需要同时理解参照物和内在方向。位置指令的类型和训练数据形式可以按下面表格整理。指令类型例子需要的视觉锚点理想训练数据形式绝对方位“放到图像右下角”图像四等分边界目标区域 bbox 与方位标签相对方位“放到左侧托盘”托盘 bbox参照物 bbox 左右关系顺序关系“从左到右依次摆放”多个目标 bbox排序序列距离关系“放到离红色方块更近的位置”两个物体 bbox距离比较结果嵌套关系“放到托盘内侧”托盘内外轮廓内外区域 mask如果训练数据里只有“放到托盘里”这类弱位置指令语言的价值就会被抹平。要让语言起作用第一步是保证指令里真的包含位置约束并且这种约束可以被自动化标注成视觉区域。2.2 序列里的 token 位置语言不能只在开头出现第二个“位置”是模型序列中的位置。很多 VLA 实现会把文本 token 放在输入序列开头后面接视觉 token再接动作 token。从因果注意力角度看越靠后的 token 只能看到它前面的信息最终动作 head 在计算时语言 token 已经隔了很长一段距离。这里要区分两种情况。如果视觉 token 数量很少比如只有 16 或 32 个语言放开头的问题还不明显如果视觉 token 有 256 个以上并且动作是逐 token 自回归生成的语言影响很容易被视觉中间特征稀释。更严重的是有些实现只在最后拿语言编码器的 pooler_output 做一次粗粒度条件相当于把整句话压成一个向量所有的方位细节都丢掉了。推荐做法是让语言 token 出现在每个决策点的可访问范围内而不是只出现在开头。一种有效手段是使用交叉注意力把动作 token 或视觉 token 的隐状态当 query把语言 token 的 key/value 引入每一层 Transformer 块。这样每个动作 token 在生成时都能直接查询语言特征语言信号不会因为序列长度而衰减。如果把语言条件放在开头又希望保住信息可以考虑在关键层重复插入语言 token。这种设计虽然增加计算量但能明显提升语言利用率。2.3 时间线上的条件位置每次决策都要能读到语言第三个“位置”是时间维度。机器人操作任务通常是序列决策模型在一个时间窗口内连续输出动作。如果语言指令只在第一帧输入时拼接后续帧决策时语言特征只能依赖初始上下文长程任务很容易出现“把语言忘了”的情况。假设任务要求“先抓红色方块再放到左侧托盘最后把蓝色方块放到右侧托盘”。模型在第一步看到了语言指令但执行到第三步时中间已经过了很多视觉帧和动作步骤。如果语言条件没有持续进入每一帧的编码器模型可能只靠当前视觉信息猜测下一步最后表现就是“语言对最终结果影响有限”。正确做法是把语言指令作为整个轨迹的全局条件每一帧决策时都要保持可读。具体实现可以每帧拼接语言 token也可以在策略模型的解码器内部通过 cross-attention 持续注入语言特征。如果模型是视频级别的联合编解码要确保语言特征参与每一帧的注意力计算而不是只作为视频开头的一个额外 token。3. 最小验证先写一个判断 L 有没有真正参与决策的样例3.1 选定可控任务把物体放到指定位置与其争论 L 有没有用不如先做一个最小验证。建议选一个非常容易控制的桌面操作任务场景里有若干物体和两个托盘模型根据语言指令把指定物体放到指定托盘。这类任务视觉差异清晰、空间关系可标注、动作序列短非常适合排查语言条件是否真正发挥作用。一个样本可以设计成下面这样的 JSON 结构。实际项目中rgb是保存图像路径instruction是文本指令anchors是语言中提到的对象与视觉区域的绑定关系action是与训练环境对齐的动作参数。{ task: place_object, rgb: obs_frame_001.jpg, instruction: 把红色方块放到左侧托盘, anchors: [ { label: 红色方块, bbox: [180, 220, 260, 310] }, { label: 左侧托盘, bbox: [120, 340, 210, 430] } ], target_move: { from: 红色方块, to: 左侧托盘 }, action: [128, 180, 220, 320, 0.0] }要点不是 JSON 本身而是anchors这一层。它把“红色方块”“左侧托盘”这些语言中的名词显式映射到图像的 bbox。有了它模型才有机会学习“语言符号 - 视觉区域”的对应关系。3.2 构造带位置锚点的指令数据接下来要把语言指令和视觉区域绑定起来。训练时锚点可以来自人工标注或现成检测器推理时如果检测器不可用也可以用指代分割模型或 attention 热图回调。关键在于不能只给文本 token就期待模型自动学会复杂的指代关系特别是当训练数据量不够大的时候。下面是区域特征抽取的示意代码。它把视觉特征图上的检测框映射为区域特征供后续与文本 token 拼接。import torch import torchvision.ops as ops def extract_anchor_features(visual_feat, boxes, object_detector): 从视觉特征中提取语言锚点对应区域的特征。 visual_feat: [B, C, H, W] boxes: dict[str, list[int]]如 {红色方块: [180, 220, 260, 310]} anchor_features {} for label, box in boxes.items(): box_tensor torch.tensor([box], dtypetorch.float32) roi_feat ops.roi_align( visual_feat, box_tensor, output_size(1, 1), spatial_scale1.0, ) anchor_features[label] roi_feat.flatten(1) # [B, C] return anchor_features注意roi_align的spatial_scale必须与视觉特征图的缩放比例一致。常见错误是特征图是原图的 1/32但 box 仍然使用原图坐标导致区域特征完全错位。3.3 用两个指标量化语言利用率做完训练后需要评估语言到底有没有被用到。推荐两个指标。第一个是语言利用率。正常条件下得到成功率normal_sr然后把语言特征清零只保留视觉特征得到remove_lang_sr。语言利用率定义为def language_coverage(normal_sr, remove_lang_sr): return 1.0 - (remove_lang_sr / max(normal_sr, 1e-6))如果语言利用率接近 0说明模型不看语言也能完成动作语言分支实际没有参与决策。如果语言利用率为正值说明移除语言后成功率下降模型确实依赖语言信息。第二个是组合泛化分数。单独在“训练中未出现过的新颜色 新位置组合”测试集上计算成功率再和普通测试集成功率相除。组合泛化分数越低说明模型越依赖视觉捷径语言没有提供抽象泛化能力。建议每次训练跑完先记录这两组指标。如果语言利用率小于 0.05就要回去检查语言注入位置而不是继续调模型容量。3.4 为什么必须先做这个最小验证很多项目直接进大规模 VLA 训练结果语言分支对不对都不知道。先跑一个最小验证的收益是能快速定位问题。下面是一个可行的运行流程python train_vla.py --config configs/position_instruct.yaml python eval_refbench.py --split normal --n-trials 50 python eval_refbench.py --split remove-lang --n-trials 50正常成功率、无语言成功率和组合泛化成功率三组数字一出来就能判断“L无用”是真实结论还是工程假象。如果正常成功率和无语言成功率几乎一样说明语言没有进入决策链后面要优先改注入机制而不是继续加数据。4. 让「L」真正进入决策链规范化工程做法4.1 用多层跨模态融合代替开头拼接开头拼接是最简单的实现方式但它只在模型输入层做了一次融合。为了让语言在整个 Transformer 网络中保持影响推荐在多层解码块中加入 cross-attention。下面是一个 PyTorch 风格的简化实现。class VLADecoderLayer(nn.Module): def __init__(self, hidden_size, use_lang_cross_attentionTrue): super().__init__() self.self_attn nn.MultiheadAttention(hidden_size, num_heads8) if use_lang_cross_attention: self.lang_attn nn.MultiheadAttention(hidden_size, num_heads8) else: self.lang_attn None self.ffn nn.Sequential( nn.Linear(hidden_size, hidden_size * 4), nn.GELU(), nn.Linear(hidden_size * 4, hidden_size), ) self.norm1 nn.LayerNorm(hidden_size) self.norm2 nn.LayerNorm(hidden_size) def forward(self, x, lang_feat): # x: [seq_len, B, hidden_size] x x self.self_attn(x, x, x)[0] x self.norm1(x) if self.lang_attn is not None: # 当前 token 作为 query语言 token 作为 key/value x x self.lang_attn(x, lang_feat, lang_feat)[0] x self.norm1(x) x x self.ffn(x) return x这里的关键是lang_feat全程可访问。每个动作 token 在生成时都能查询语言特征语言就不会被前面的视觉 token 遮挡。实际项目中可以对一部分层加lang_attn而不是所有层都加先看语言利用率是否有变化。4.2 训练阶段对语言做随机丢弃如果训练时语言特征一直存在模型可能学到一种捷径视觉信息足够就忽略语言语言信息足够就不看视觉。为了逼模型同时利用两种模态可以在训练时随机丢弃语言条件。def forward_with_lang_dropout(text_embed, visual_embed, lang_dropout0.15): if torch.rand(1) lang_dropout: lang_embed torch.zeros_like(text_embed) else: lang_embed text_embed seq_feat torch.cat([lang_embed, visual_embed], dim-2) return seq_feat语言丢弃的做法参考了多模态 Dropout 的思路。比例不要太高0.1 到 0.2 是常见区间。过高会导致训练不稳定因为模型在测试时看到的语言条件不充分过低又无法形成约束。同时推理时要关闭丢弃否则语言条件会随机被清空。4.3 位置锚定从文本指代到视觉区域如果只做 cross-attention模型仍然可能需要自己学到“左侧托盘”对应哪个视觉区域。更直接的办法是把语言中提到的名词锚定到某个视觉区域特征上替换或增强对应的文本 token。这样可以减小学习难度。在训练数据里位置锚点已经以 bbox 形式存在。推理时可以先用一个轻量级指代分割模型找到“左侧托盘”的 mask 或 bbox再提取区域特征。下面是一个简化流程prompt 把红色方块放到左侧托盘 anchors referring_segmenter(visual_frame, prompt) # anchors 示例: {红色方块: [bbox], 左侧托盘: [bbox]} visual_regions extract_anchor_features(visual_feat, anchors, detectorNone) # 将语言 token 中的对象名区域做替换或额外拼接区域 token lang_token text_encoder(prompt).last_hidden_state region_token torch.stack(list(visual_regions.values()), dim-2) seq_feat torch.cat([lang_token, region_token, visual_feat], dim-2)这里并不要求把所有名词都解析出来。只要位置短语能被定位模型就能得到更强的空间条件。实际项目中可以先从规则开始例如检测到文本中包含“左侧/右侧/上方/下方”再结合检测器找到对应参照物。用规则跑通后再替换成神经网络解析器。4.4 构造位置指令的完整模板与负样本仅仅把“位置指令”理解为“包含左、右”是不够的。训练时需要用不同表达方式覆盖同一个位置关系并且要加入负样本让模型学会拒绝错误指令。类型指令示例说明无位置弱指令“把红色方块放到托盘里”通用指令便于学习任务主流程绝对位置强指令“把红色方块放到右侧托盘”提供明确空间约束相对位置强指令“把红色方块放到红色托盘左侧”需要理解参照物顺序位置指令“先放红色再放蓝色”提供时间顺序信息负样本“把蓝色方块放到左侧托盘”当前动作与指令矛盾同义改写“red block to the left tray”增加语言多样性负样本的主要作用是防止模型把语言当作可忽略标签。如果模型看到“蓝色方块”指令仍然执行红色方块动作说明语言没有影响决策。训练时可按一定比例混入负样本让模型在动作与语言不一致时学会降低对应动作的概率。5. 如何做消融实验理解“20%-40%”这个量级5.1 正确切分组合而不是随机切分很多项目复现不出语言带来的提升原因是测试集切分方式有问题。如果训练集和测试集都来自同一个数据分布颜色组合、位置组合高度重合那么模型靠视觉记忆就能拿到高成功率语言价值很难体现。正确做法是做组合切分。例如训练集里出现“红色方块 - 左侧托盘”和“蓝色方块 - 右侧托盘”测试集里只出现“红色方块 - 右侧托盘”和“蓝色方块 - 左侧托盘”。这样测试集要求模型把“颜色”和“位置”两个属性重新组合而不是记住训练时的固定搭配。切分伪代码如下train_samples [ s for s in samples if (s[color], s[position]) in train_combinations ] test_samples [ s for s in samples if (s[color], s[position]) in test_combinations ]关键是保证test_combinations中的组合在训练集中从未同时出现。否则测试结果只能说明模型记住了场景不能说明语言泛化能力。5.2 典型消融表从无语言到完整位置锚定下面这张表展示了不同条件下可能出现的结果排序。关键不是绝对数值而是不同方案在“普通任务”和“组合泛化任务”上的差距。方案普通任务成功率组合泛化成功率语言利用率结论A. 无语言仅视觉0.820.580视觉捷径强组合能力弱B. 语言压缩成单向量0.810.550.02语言信息几乎丢失C. 语言 token 开头拼接0.850.680.18有提升但不稳定D. 多层交叉注意力 位置锚定0.900.830.33组合泛化差距明显方案 D 相比方案 A在组合泛化成功率上从 0.58 提升到 0.83相对提升大约 43%。如果报告的是普通任务上的相对提升数字会小一些如果报告的是组合泛化任务20%-40% 的量级很常见。这里的关键不是同一个固定数值而是“语言在正确位置时组合泛化能力会显著高于无语言和弱语言基线”。5.3 为什么这个量级不违反直觉20%-40% 不是指所有任务都能提升这么多而是指“语言本该起作用但没用起来”的场景中修正位置指令后的收益。具体幅度受以下因素影响任务本身的歧义程度。任务越复杂语言约束越重要提升越明显。视觉捷径强度。如果物体颜色和目标位置在数据中高度相关视觉分支配得起所有动作语言收益被压缩。指令表达多样性。训练时同一种位置关系如果只有一种写法模型很难把语言泛化到新表达。模型规模。小模型可能没有足够容量学习跨模态绑定语言收益体现不充分。因此如果看论文或项目报告时发现提升只有 5%不一定是语言无用可能是测试集没有做组合切分。如果看到提升超过 40%也要检查是否出现了数据泄漏比如测试集中出现了与训练集相同的位置短语或相同背景。6. 生产环境引入前的排错清单和落地建议6.1 指令在推理时失效按六步排查如果在推理阶段发现 VLA 模型没有执行语言指令不要直接调大模型按下表顺序排查。排查顺序检查内容常见问题处理建议1输入文本tokenizer 截断后位置短语被切掉打印 token 序列确认左右托盘等词存在2位置锚点检测器没有找到“左侧托盘”检查 bbox 坐标和锚点名称是否对齐3注意力依赖语言 token 在 attention 分数中是零权重输出各层 attention观察语言 token 是否被访问4训练推理一致性训练用“left tray”推理用“左侧托盘”统一文本模板或加入同义改写增强5网络结构语言只在开头嵌入后续层不可见增加 cross-attention 或每帧条件注入6采样策略温度过高导致动作随机性掩盖语言影响降低采样温度或先使用贪心解码验证这六步覆盖了从输入、特征、结构到解码的完整链路。大多数“语言指令不生效”的问题都出在第 2 步和第 5 步。6.2 常见坑与对应解决方式第一个坑是训练固定模板、推理换同义表达。训练集里只写“左侧托盘”推理时用户说“左手边那个盘子”模型没见过这种表达语言利用率就会下降。解决方式是训练时做指令改写增强至少用三种不同句式表达同一个位置关系。第二个坑是坐标与视觉特征图尺度不一致。roi_align的spatial_scale配错区域特征取到完全错误的像素位置锚定变成噪声。建议把所有 bbox 先从原图坐标归一化到 0-1再根据特征图尺寸换算。第三个坑是语言丢弃比例过大。0.5 的语言丢弃会让模型长期在缺少语言的条件下训练最终模型学会完全不依赖语言。建议从 0.1 开始调优先检查语言利用率和组合泛化分数而不是只看训练 loss。第四个坑是长任务只把语言放开头。一个 50 步的机器人任务在最初的输入里拼接语言 token后续 40 步每一步都在没有语言约束的情况下决策。长任务场景里应该每帧或每个动作解码步都持有一个语言条件向量。6.3 上架前的可复用检查清单生产环境与学习环境差异很大。模型在开发阶段能跑通不代表部署后语言条件依然有效。下面清单可以直接作为发布前检查项。数据层面每个位置关系至少有 20 组不同坐标每种语言表达至少 3 种不同写法测试集包含训练集未出现的新组合。模型层面确认语言 embedding 在反向传播中有梯度确认 cross-att