
1. 项目缘起当3D大模型“看见”了不存在的东西最近在折腾3D场景下的具身智能体说白了就是让一个AI驱动的虚拟机器人能在三维空间里“看”懂环境然后执行任务比如“去厨房把桌上的杯子拿过来”。听起来很酷对吧但实际干起来你会发现一个让人头疼的“幻觉”问题。这个幻觉不是指AI有了自我意识而是指它经常“脑补”出一些场景里压根不存在的东西。想象一下你让机器人去卧室拿本书结果它对着空无一物的床头柜信誓旦旦地告诉你“书已经拿在手里了”。或者你问它客厅里有什么家具它可能会描述出一个根本不存在的壁炉。这种“无中生有”的现象在基于大语言模型的3D具身智能体3D-LLM Embodied Agents中尤为突出我们称之为“幻觉”Hallucination。它直接导致任务失败、指令误解让整个系统变得不可靠。为什么3D场景下的幻觉这么难搞这背后有几个深层原因。首先3D数据本身是稀疏且视角受限的。一个3D场景通常由点云或网格构成不像2D图像那样有丰富的纹理和连续的像素信息。智能体从一个固定视角“看”过去可能只能捕捉到物体的一个侧面大量信息是缺失的。其次大语言模型LLM本身是基于海量文本训练的它拥有强大的“常识”和推理能力但这也是一把双刃剑。当3D视觉信息模糊或缺失时LLM会过度依赖其内部的知识库进行“补全”而这种补全往往脱离了当前场景的真实视觉证据从而产生幻觉。最后传统的解码策略比如贪婪搜索或束搜索在生成文本时倾向于选择概率最高的词元这进一步放大了模型内部先验知识的影响压制了来自当前3D视觉输入的微弱但正确的信号。所以我们面临的核心挑战是如何让3D具身智能体在生成对环境的描述或规划行动时更“忠实”于它实际“看到”的3D视觉信息而不是被它脑子里“想象”的常识带偏这就是“3D-VCD: Hallucination Mitigation in 3D-LLM Embodied Agents through Visual Contrastive Decoding”这个工作要解决的核心问题。VCD代表“视觉对比解码”它不是训练一个新模型而是一种在模型推理生成阶段使用的策略旨在从源头抑制幻觉。2. 幻觉的根源拆解3D-LLM生成过程中的信号冲突要理解VCD为什么有效我们得先钻进3D-LLM的“脑子”里看看它在生成每一个词的时候到底在经历什么。一个典型的3D-LLM比如一个接收3D点云特征和文本指令然后输出回答或行动规划的模型其生成过程可以简化理解为一个概率计算游戏。对于每一步要生成的下一个词模型会计算一个庞大的概率分布覆盖了整个词表。这个概率可以看作是两个“声音”在博弈后的结果视觉证据信号源于当前输入的3D场景特征。例如智能体的摄像头传回了点云经过编码器提取特征后这个特征会提示模型“根据你看到的接下来描述‘椅子’的概率应该高一点。”先验知识信号源于LLM在海量文本上训练得到的内部知识。它会说“根据我的常识客厅里通常有‘沙发’、‘电视’、‘茶几’所以接下来出现‘茶几’的概率也很高。”在理想情况下如果视觉信息清晰明确比如确实有一把造型独特的椅子视觉信号会很强主导生成过程。但问题在于3D视觉信号往往是微弱、有噪声、不完整的。点云可能稀疏物体可能被遮挡视角可能不好。此时强大的先验知识信号就会占据上风。更糟糕的是传统的解码方法如贪婪解码会直接选择概率最高的那个词。这个“最高概率”往往是先验知识信号的胜利因为它基于更普遍、更平滑的统计规律。而代表当前特定场景真相的视觉证据其对应的概率可能只是稍微高了一点但不足以成为“最高”。于是模型就“忽略”了真实所见输出了符合常识但不符合事实的幻觉内容。这就好比一个经验丰富的侦探LLM的先验到了一个案发现场3D场景。如果现场线索视觉证据模糊不清他可能会下意识地根据自己过去办过的类似案子常识来推断凶手而不是严格遵循现有线索。VCD要做的就是给这位侦探一个“聚焦放大镜”强迫他更仔细地审视现场独有的、微弱的线索并降低他依赖过去经验进行脑补的倾向。3. VCD核心机制用“对比”凸显视觉证据视觉对比解码Visual Contrastive Decoding, VCD的核心思想非常巧妙它不是直接使用原始的概率分布而是通过构造一个“对比”来放大视觉证据信号抑制先验知识信号。具体来说在生成每一个词的时候VCD会同时计算两个概率分布P_visual(x | context): 这是我们想要的目标模型在给定完整上下文包括3D视觉输入和之前的文本后生成词x的概率。P_prior(x | context_text_only): 这是一个“退化”的参考模型在相同的文本上下文下但屏蔽或移除了3D视觉输入后生成词x的概率。这个分布近似代表了模型纯靠先验知识而无视觉证据时会说什么。VCD的最终生成概率并不是简单的P_visual而是经过调整的P_vcd(x) ∝ max(0, log P_visual(x) - β * log P_prior(x))这里的β是一个大于0的缩放因子用来控制抑制先验的强度。让我们拆解一下这个公式log P_visual(x) - log P_prior(x)这一步是关键。它计算了“有视觉输入时的对数概率”减去“没有视觉输入时的对数概率”。如果一个词x的出现主要是由视觉证据驱动的比如场景里有个独特的红色花瓶那么有视觉时它的概率会比没有视觉时高很多这个差值就很大。反之如果一个词x的出现主要是由先验知识驱动的比如认为客厅“应该”有电视那么有没有视觉输入它的概率都差不多这个差值就很小甚至为负。max(0, ...)这个操作确保了调整后的概率为非负。如果差值为负说明没有视觉证据时这个词的概率反而更高那它很可能就是纯粹的幻觉直接将其概率置零从候选词中剔除。β因子这是一个超参数你可以把它理解为“去幻觉强度”。β越大对先验知识的惩罚就越大模型就会变得越“保守”只说出有强烈视觉证据支持的内容β越小则允许更多的先验知识参与。在实际应用中β需要在一个验证集上进行调整以在“忠实度”和“流畅性/丰富性”之间取得平衡。注意实际操作中计算P_prior并不需要真的训练一个“瞎了”的模型。一个常见的技巧是使用同一个模型但在前向传播计算P_prior时将3D视觉特征的输入置零或用一个可学习的空向量替代从而模拟“无视视觉输入”的行为。这大大降低了实现成本。通过这种对比机制VCD巧妙地将生成概率的“绝对值”竞争转变为了“相对增益”的竞争。一个词只要能证明“我的出现特别依赖于你刚才看到的那个3D场景”它就能胜出。这迫使模型在生成时必须为每个候选词找到视觉上的依据从而有效过滤掉那些仅凭常识“脑补”出来的幻觉词。4. 实战部署将VCD集成到你的3D-LLM推理管线中理论很美好但怎么用起来呢下面我以一个假设的、基于Transformer架构的3D-LLM为例手把手拆解集成VCD的步骤和代码逻辑。假设我们有一个基础模型它由3D编码器、LLM骨干和解码器构成。4.1 模型与数据准备首先你需要一个已经训练好的3D-LLM模型。这个模型应该能够处理你的3D数据格式如点云、多视角图像特征等并将其与文本指令融合。同时你需要一个验证集用于调试VCD的超参数β。import torch import torch.nn.functional as F class Simple3DLLM(torch.nn.Module): def __init__(self, visual_encoder, llm_backbone): super().__init__() self.visual_encoder visual_encoder # 输出视觉特征 V self.llm llm_backbone # 标准的预训练LLM如LLaMA结构 # 假设我们通过一个投影层将视觉特征对齐到LLM的文本嵌入空间 self.visual_proj torch.nn.Linear(visual_encoder.output_dim, llm_backbone.config.hidden_size) def forward(self, input_ids, attention_mask, visual_input, use_visualTrue): Args: input_ids: 文本token ids [B, L] attention_mask: 文本注意力掩码 [B, L] visual_input: 3D输入数据 [B, ...] use_visual: 是否使用视觉输入。为False时用于计算P_prior。 # 1. 处理视觉输入 if use_visual: visual_features self.visual_encoder(visual_input) # [B, D_v] visual_embeds self.visual_proj(visual_features) # [B, D] # 通常将视觉特征作为特殊的“视觉token”添加到输入序列开头 # 这里简化为将视觉嵌入与文本嵌入相加需根据实际模型结构调整 # 实际中可能使用cross-attention或prefix tuning等方式融合 else: # 计算P_prior时视觉部分置零 batch_size input_ids.size(0) visual_embeds torch.zeros(batch_size, self.llm.config.hidden_size, deviceinput_ids.device) # 2. 获取文本嵌入 text_embeds self.llm.get_input_embeddings()(input_ids) # [B, L, D] # 3. 融合视觉与文本嵌入简化示例拼接或相加 # 假设我们将视觉特征作为第一个token combined_embeds torch.cat([visual_embeds.unsqueeze(1), text_embeds], dim1) # 相应地扩展attention_mask为视觉token添加1 if use_visual: visual_mask torch.ones(batch_size, 1, deviceattention_mask.device) combined_mask torch.cat([visual_mask, attention_mask], dim1) else: combined_mask attention_mask # P_prior时没有额外的视觉token # 4. 通过LLM骨干网络 outputs self.llm(inputs_embedscombined_embeds, attention_maskcombined_mask) logits outputs.logits # [B, L1(如果拼接), Vocab_size] # 我们只关心最后一个文本位置的logits用于预测下一个词 next_token_logits logits[:, -1, :] # [B, Vocab_size] return next_token_logits4.2 实现VCD解码函数接下来核心是实现VCD的采样函数。这里我们实现一个最简单的基于VCD的贪婪解码。def vcd_greedy_decode(model, input_ids, attention_mask, visual_input, beta0.5, max_new_tokens50): 使用VCD进行贪婪解码。 Args: beta: 对比强度超参数。 generated input_ids.clone() for _ in range(max_new_tokens): # 计算 P_visual 的 logits with torch.no_grad(): logits_visual model(generated, attention_mask, visual_input, use_visualTrue) # [B, Vocab] log_visual F.log_softmax(logits_visual, dim-1) # 计算 P_prior 的 logits (屏蔽视觉) logits_prior model(generated, attention_mask, visual_input, use_visualFalse) # [B, Vocab] log_prior F.log_softmax(logits_prior, dim-1) # 应用VCD公式: log P_vcd max(0, log P_visual - beta * log P_prior) adjusted_logits torch.clamp(log_visual - beta * log_prior, min0) # 贪婪选择概率最大的token注意adjusted_logits此时不是标准的概率对数但argmax依然有效 next_token torch.argmax(adjusted_logits, dim-1, keepdimTrue) # 将新token添加到生成序列中 generated torch.cat([generated, next_token], dim-1) # 更新attention_mask对新token位置设为1 attention_mask torch.cat( [attention_mask, torch.ones((attention_mask.size(0), 1), deviceattention_mask.device)], dim-1 ) # 简单判断是否生成结束符如EOS if next_token.item() model.llm.config.eos_token_id: break return generated4.3 超参数β的调优策略β的选择至关重要它没有固定值需要根据你的具体任务和模型进行调整。构建评估集准备一个小的验证集其中包含已知的、容易引发幻觉的3D场景和指令。例如场景中只有椅子和桌子但你问“沙发是什么颜色的”正确答案应该是“没有沙发”或“我不知道”而不是幻觉出一个颜色。定义评估指标幻觉率模型输出中包含不存在于场景中的实体或属性的比例。这需要人工标注或使用一个强大的视觉问答VQA模型作为裁判来评估生成文本与场景的匹配度。任务成功率对于具身任务如导航、操作最终能否正确完成。流畅性/相关性虽然抑制了幻觉但生成的内容不能变得语无伦次或完全无关。可以用困惑度Perplexity或与人类参考文本的相似度如BLEU作为辅助指标。网格搜索在合理的范围内例如0.1到2.0之间以一定步长如0.2尝试不同的β值。分析权衡曲线绘制“幻觉率-任务成功率”或“幻觉率-流畅性”的曲线。你的目标是找到那个拐点——在幻觉率显著下降的同时任务成功率和流畅性没有急剧恶化。这个点对应的β值通常就是较优的选择。实操心得在调β时我发现一个现象当β设置得过大时模型会变得过于“沉默”或只输出非常安全、通用的描述如“这是一个房间”任务成功率反而会下降。因此β的调优本质上是寻找“忠实”与“有用”之间的最佳平衡点而不是一味追求零幻觉。4.4 进阶与不同解码策略结合上面的例子使用了贪婪解码但VCD是一种概率调整策略它可以与任何基于概率的解码方法结合比如束搜索Beam Search或核采样Nucleus Sampling。VCD 束搜索在束搜索的每一步对每个候选序列计算其下一个词的VCD调整后概率然后基于此概率进行剪枝和扩展。这能在保持多样性的同时抑制幻觉。VCD 核采样先根据VCD调整后的概率分布进行筛选top-p然后再从筛选后的分布中采样。这样既能保证生成内容不偏离视觉证据又能有一定的创造性。实现时只需将上述vcd_greedy_decode函数中选择next_token的部分替换为束搜索或核采样的逻辑即可核心的adjusted_logits计算方式不变。5. 效果评估与边界案例讨论在实际测试中VCD方法能显著降低3D-LLM的幻觉率。例如在3D视觉问答3D-VQA任务上基线模型可能会因为常识而错误地回答“卧室里有什么”为“一张床和一个衣柜”即使场景中只有一张床。使用VCD后模型更可能回答“有一张床”或者谨慎地补充“可能还有一个衣柜但当前视角看不到”。然而没有任何方法是银弹VCD也有其局限性和需要小心处理的边界情况视觉模糊与合理推断的冲突有些时候基于不完整视觉信息进行合理推断是必要的而不是幻觉。例如只看到椅子的背面模型推断“这是一把椅子”是合理的但推断“这是一把红色的木质椅子”可能就是幻觉如果颜色和材质不可见。VCD中的β因子可以帮助调节这个界限但无法完美区分。一个实用的技巧是在指令中明确模型的角色例如“仅根据你看到的回答”或“你可以进行合理的推断”。对“否定”或“不存在”表述的挑战让模型生成“这里没有X”这类语句本身就很反直觉。因为“没有X”在视觉上对应的证据是一种“缺失”而模型很难从“缺失”中学习到强烈的信号。VCD在处理这类生成时可能效果不佳。可以在训练数据中刻意加入针对“不存在”实体的问答对帮助模型建立这种映射。计算开销VCD需要在每一步生成时做两次前向传播一次有视觉一次无视觉理论上使推理速度翻倍。这对于实时性要求高的具身智能应用是一个挑战。优化策略包括使用更小的模型计算P_prior知识蒸馏一个小型模型、缓存P_prior的计算结果如果文本上下文相同仅视觉输入不同时、或研究近似的单次前向传播算法。多模态对齐的粒度VCD在词级别token-level进行对比。但有时候幻觉发生在短语或概念级别。例如模型可能正确提到了“桌子”但却幻觉了“桌子上有一台笔记本电脑”。虽然“笔记本电脑”这个词本身可能被抑制但整个幻觉概念仍然被输出了。未来的改进方向可能是探索短语级或概念级的对比策略。在我自己的实验里VCD就像一个高效的“事实检查员”它能将明显违背视觉证据的天马行空的幻觉扼杀在摇篮里。但它也需要使用者理解其工作原理像一个精细的旋钮一样去调节β并根据具体任务场景判断哪些推断是可接受的“补全”哪些是不可接受的“捏造”。将VCD集成到推理管线后最直观的感受是智能体的输出变得更“靠谱”了虽然偶尔会显得有点“谨小慎微”但对于需要高可靠性的实际应用如家庭服务机器人、工业巡检来说这种确定性远比天马行空的创造性更重要。