
一个简单但容易翻车的常识题Walking to the car wash, is it reasonable?如果把这个问句交给常见的生成式大语言模型LLM很多模型会给出类似“Yes, walking is healthy”或“You can walk to the car wash”的答案。但仔细想想这个判断并不符合常识如果一个人要去洗车店通常需要开车把车送过去而不是步行走到洗车店。这个例子本身就暴露了 LLM 在常识推理Commonsense Reasoning中的一种典型失败模式显著性偏差Salience Bias。显著性偏差是认知心理学里的一个概念指人在判断时更容易被那些突出、显眼、容易获取的信息影响而忽略背景中同样重要的约束条件。LLM 在生成文本时也有类似表现。它会被问题里的高频词、动作词或场景词汇带走沿着训练数据中最常见的搭配路径输出而不是先建立完整的常识模型再回答问题。这篇文章从Walking to the Car Wash这个最小案例出发拆解显著性偏差的表现、复现方式、评估方法和缓解手段。你可以把它当作一次实验设计练习也可以在搭建 LLM 评测集、优化 Prompt、调试模型输出时直接复用里面的思路和代码。1. 先看一个把 LLM 带偏的常识问题Walking to the Car Wash1.1 这个题目为什么能测出显著性偏差先还原一下直觉判断过程。看到car wash人会自然联想到“车”。看到walking人会自然联想到“步行”。这两件事放在一起会形成一种语义冲突如果你没有开车你为什么要去洗车店你要洗的是什么车反过来如果走路去洗车店是为了散步那任务对象就不再是“洗车”而是“走路去某个地点”。人类会把这种隐含约束拆开主语是“人”动作是“走路”目标是“洗车店”而“洗车”需要“车”作为服务对象。缺了“车”动作和目标之间就没有合理连接。LLM 的问题在于它常常抓住walking这个高显著性动词把它当成主要线索然后寻找walking to ...的高频补全比如park、store、school结果就没有识别出car wash隐含的“车里装满车才能洗”这个语义前提。所以这个题目很适合作为显著性偏差的探针。它语句短、冲突明显、不需要复杂专业知识普通人和评测者一眼就能看出标准答案。如果模型在这么短的题目上都会偏离常识那么面对更长、更模糊的上文时偏离风险会更高。1.2 从心理学到 LLMsalience bias 是什么用一句话概括salience bias 就是“被最抢眼的信息带跑”。在人类决策中它表现为高估显著信息的重要性。例如新闻频繁报道飞机失事人们会高估坐飞机的风险因为这种事件更“抢眼”。在 LLM 里pipeline 不存在真正的“注意什么”但它的自注意力机制和训练数据概率分布会共同制造类似效果。从技术机制上看LLM 的每一层 Transformer 都会计算 token 之间的注意力权重。某个 token 如果和训练语料中大量高频上下文绑定它就可能在解码阶段获得更高概率。walking to后面接地点名词是语料里非常常见的结构所以当模型生成答案时它会倾向于把car wash当作“一个步行可达的地点”而不是“需要开车过去完成洗车服务的场所”。这就是一种统计层面上的显著性偏差。它和传统规则系统有本质区别。传统规则系统如果设计了“车必须跟随人到达洗车店”这样的约束就不会犯这个错。LLM 没有显式约束它只能靠参数里的隐性知识来平衡多组线索。当线索强弱不均时较强的语言模式会压制较弱的常识约束最终输出明显反直觉的结果。1.3 为什么常识推理任务特别容易暴露这种偏差常识推理任务通常要求模型把多个背景事实组合起来。这些事实往往没有全部写进输入文本而是藏在模型参数里。例如“车是洗车店的服务对象”“步行意味着没有汽车”“洗车店通常服务于开来的汽车”这些信息都是常识但模型需要先想起来然后用它们去约束生成。问题是显式输入的词频特征往往远强于隐式常识特征。walking to这种连续结构在原始文本里出现频率高而“到达洗车店的交通工具必须是汽车”这种陈述在训练语料里很少以同样的句子形式出现。所以模型在解码时更容易被已经写出来的强关联词引导而不是去回忆那条不常出现的常识约束。这个机制不仅出现在生成式任务中也会影响多项选择、完形填空和对话回复。2. 复现显著性偏差最小实验需要准备的环境与数据2.1 准备一个能用脚本反复调用的推理环境要观察显著性偏差不需要很大的模型。实际上一些参数量更小的模型更容易暴露这种偏差因为它的常识存储更弱词频依赖更强。实验环境可以分三种本地推理框架、OpenAI 兼容 API、Hugging Face Transformers。建议从本地推理框架开始这样不需要把测试集传出环境也方便固定模型版本。下表是一个最小环境参考具体版本可以根据实际机器调整组件推荐选择说明Python3.10 或 3.11兼容当前主流推理框架模型推理vLLM 或 Ollama 或 Transformers三者都提供文本生成接口模型一个 7B 左右的指令微调模型即可不需要追求最强模型调用方式HTTP 接口或本地 pipeline脚本循环测试更方便显存/内存至少 8GB 显存或纯 CPU 使用小模型小模型也能看到趋势依赖torch, transformers, requests, pandas记录和统计实验数据如果需要快速验证也可以不部署本地模型直接使用一个 OpenAI 兼容的接口。本地部署的话可以参考下面的命令启动一个推理服务# 以 vLLM 为例具体参数需要按模型路径和 GPU 显存调整 python -m vllm.entrypoints.openai.api_server \ --model /path/to/your-model \ --served-model-name test-model \ --port 8000代码里统一使用 HTTP 请求方式这样无论后面换成哪个模型脚本逻辑都不会变。下面是一个最简调用函数import requests def query_llm(prompt, endpointhttp://127.0.0.1:8000/v1/completions, modeltest-model, temperature0.0, max_tokens128): payload { model: model, prompt: prompt, temperature: temperature, max_tokens: max_tokens } resp requests.post(endpoint, jsonpayload, timeout60) resp.raise_for_status() return resp.json()[choices][0][text]这里的temperature0.0是刻意设置的。低温度会减少采样随机性更容易让模型选择最高概率路径从而稳定复现词频主导的预测。如果你希望在多次运行中看到波动可以把温度调到 0.7 以上。2.2 设计包含正反例的测试样本只用一个题目不足以说明问题所以实验样本应该包含“基础题干 干扰词 隐含约束”三部分。以Walking to the Car Wash为例可以用 JSON 结构化记录[ { id: walking_to_car_wash_01, question: Does it make sense to walk to the car wash?, choices: [ Yes, walking is healthy., No, you usually drive a car to a car wash. ], label: No, salient_trigger: walk, missing_object: car, expected_reason: A car wash serves cars, so the person needs to bring a car. }, { id: walking_to_park_02, question: Does it make sense to walk to the park?, choices: [ Yes, walking to the park is normal., No, you usually drive to the park. ], label: Yes, salient_trigger: walk, missing_object: , expected_reason: Parks are walkable destinations. } ]第一题用来测试显著性偏差第二题是控制组。控制组非常重要如果模型连walking to the park都判错说明问题不在显著性偏差而可能在指令理解或常识缺失上。样本还可以继续拆分。例如把car wash换成laundromat、restaurant、gas station等地点把动作换成driving、riding、flying就能组成一个多类型的小型评测集。每个样本都要记录“干扰项是什么”“隐含约束是什么”“期望答案是什么”。2.3 编写批量调用和输出记录脚本批量实验需要把“提示词构造、模型调用、结果解析、统计”四件事分开。先写一个提示词构造函数def build_prompt(sample, modedirect): if mode direct: return fQuestion: {sample[question]}\nAnswer: elif mode choice: choices_text \n.join(f- {c} for c in sample[choices]) return ( fQuestion: {sample[question]}\n fChoices:\n{choices_text}\n Answer the letter of the correct choice. ) elif mode constraint: return ( fQuestion: {sample[question]}\n Hint: A car wash provides washing service for cars. Think about what object must be present for the action to make sense.\n Answer: ) else: raise ValueError(funknown mode: {mode})构造好 prompt 后循环调用query_llm并把结果写入一个 DataFrame方便后续统计import pandas as pd import json samples json.load(open(test_samples.json)) records [] for sample in samples: for mode in [direct, choice, constraint]: prompt build_prompt(sample, mode) output query_llm(prompt) records.append({ id: sample[id], mode: mode, prompt: prompt, output: output.strip(), label: sample[label], salient_trigger: sample.get(salient_trigger, ), }) df pd.DataFrame(records) df.to_csv(llm_salience_outputs.csv, indexFalse)注意temperature0.0时模型输出通常是确定性的但不同模型、不同推理框架对temperature的处理可能有细微差异。如果希望得到稳定结果可以每个条件重复多次再取多数结果。3. 实验变量与控制怎样让偏差更明显或更隐蔽3.1 提示词层面的变量显著性偏差不是非黑即白它会随着提示词结构变化。下面这几类变量会直接影响模型的判断路径。变量作用机制示例直接问答模型自由生成最容易被强线索带跑Question: Does it make sense to walk to the car wash?多选题选项会提供显式对比降低一部分生成压力Which choice is more reasonable? A) yes B) no加入约束提示提示模型关注隐含条件减少词频主导Think about what object a car wash needs to serve.反事实改写让模型先想象异常场景再判断合理性If someone walked to a car wash, what cant they do afterward?强制分步推理先要求列出前提再给结论First list the prerequisites, then answer.这些变量适合做对照实验。每组对照只改一个变量其余保持一致。例如一组用direct一组用constraint比较两组准确率差异就能知道提示词中的约束有没有帮助模型恢复常识判断。3.2 解码参数与模型层面的变量除了提示词解码参数也会改变偏差的显现程度。参数调低的效果调高的效果建议temperature输出更集中容易稳定到高频词路径输出更分散可能随机跳到更合理的答案初始实验设为 0.0 或 0.2top_p限制候选词范围更保守允许更多低概率词增加多样性使用默认值 0.9 即可max_tokens强制模型短答便于解析允许长推理可能产生中间修正设为 128 到 256 之间frequency_penalty惩罚重复词不太影响本题过高会改变正常词序实验阶段保持默认模型层面的差异更明显。同一个题目在 1B、7B、70B 不同规模模型上错误率可能完全不同。小模型更容易依赖词频大模型因为记忆了更多常识可能一眼就看出问题。但大模型也可能在更复杂的显著性干扰下失败。所以实验时要锁定模型版本不能跨版本混着统计。3.3 评估指标和重复实验规则生成式模型的输出不会严格按预期格式返回所以需要一套简单的解析规则。最稳妥的方式是定义标准答案关键词映射def parse_output(text, positive_words(yes, makes sense, reasonable), negative_words(no, does not make sense, unreasonable)): text text.lower().strip() for word in positive_words: if text.startswith(word): return yes for word in negative_words: if text.startswith(word): return no return unknown注意这里的startswith可能过于严格。更复杂的解析可以用规则引擎但小实验里可以人工抽查一批结果再根据常见输出形态补充关键词。评估指标建议同时看三个准确率预测结果与标准答案一致的占比。偏差率在错误答案中答案与salient_trigger相关的占比。例如模型答“Yes因为走路更健康”就属于被walk带偏。稳定性同一条件下重复 5 次或 10 次结果一致的比例。重复实验时要固定随机种子如果框架支持或固定温度。如果是采样模式可以在脚本外层跑循环for round_idx in range(5): output query_llm(prompt, temperature0.8) # 记录 round_idx, output这样统计出来的准确性才有意义。不能只跑一次就下结论。4. 一个假设性结果为什么模型会更容易答错4.1 推测性实验结果下面给出一组基于文本生成模型行为特征的假设性数据。不同模型、不同推理框架下数值会有明显差异这里的目的只是展示“如何分析结果”而不是替某个具体模型下结论。提示词模式控制组walk to park目标组walk to car wash目标组错误率direct高准确率偏低准确率约 65%choice高准确率中等准确率约 45%constraint高准确率中等偏高准确率约 28%cot要求先列前提高准确率中等偏高准确率约 22%从这个假设结果可以看出两个规律第一控制组不容易错说明模型不是完全缺失“步行可到达”的常识第二一旦加入显式约束或分步推理目标组的错误率会明显下降说明显著性偏差并不是不可修复而是生成路径更容易被高显著 token 占据。4.2 从注意力和概率分布看偏差成因从注意力机制看walking和car wash之间的连接并不一定强。真正强的是walking to这个动作词组和地点名词的组合模式。模型在解码下一个 token 时会依据上文的语言概率分布而“地点可步行性”是一种隐含语义很难通过表面 token 表达。所以Yes会成为高概率选项。从训练数据看walk to the park、walk to the store、walk to the school这类句子远比“洗车店通常需要开车去”更常见。模型学到了这个文本规律却没有在推理阶段显式调用“洗车店需要车辆”的背景知识。结果就是它在形式上回答得通顺在语义上却不符合常识。如果还想进一步定位问题可以使用模型的可解释性工具查看注意力权重。例如transformers的output_attentionsTrue可以导出每一层的注意力矩阵观察在car、wash、walking这些 token 之间的注意力强度。这能帮助区分是注意力分配问题还是后续语言头的决策问题。4.3 错误案例分析假设模型对目标题返回了下面两段输出Prompt: Does it make sense to walk to the car wash? Output 1: Yes, walking to the car wash is a great way to get some exercise and fresh air.这个回答典型地抓住了walking的“健康、锻炼”含义忽略了洗车店的服务对象。Prompt: Does it make sense to walk to the car wash? Output 2: No, you should drive because you need to bring your car there.这个回答正确识别了隐含约束逻辑完整。它没有急着从walking延伸而是先定义了“car wash 用来洗车”这个前提。两种输出可以放在同一个分析表中用于人工评估模型是“理解约束”还是“语言模式匹配”。输出是否合理判断依据是否属于显著性偏差走路健康不合理忽略了 car wash 的服务对象是需要开车带车过去合理识别汽车与洗车服务的关联否走路比开车环保不合理仍然没有回应洗车对象是5. 缓解显著性偏差提示、验证和训练三个层面5.1 提示词工程先拆前提再作推理缓解偏差的第一层是提示词工程。核心思路是让模型在作答前先显式建立前提而不是直接跳到高频回答。可以在 prompt 中加入“拆解前提”指令Question: Does it make sense to walk to the car wash? Before answering, list the key objects and actions: - A car wash is a facility for washing cars. - Walking means the person is not driving a car. - If there is no car, there is nothing to wash. Then decide whether the original statement is reasonable.这种“先拆前提再判断”的写法本质上是在增加推理深度让模型在生成最终答案之前先激活更多背景知识。它不能确保所有模型都正确但对显著性偏差的抑制效果通常比直接提问更稳定。还有一类做法是反事实改写Imagine someone walks to a car wash but does not bring a car. Is that a normal way to use a car wash? Explain why.反事实改写把“缺失的关键对象”直接摆到显式文本里让模型不再需要猜测隐含条件。很多原本会犯错的模型在被告知“没有带车”之后反而能给出合理判断。5.2 引入外部验证器或约束规则提示词层面的缓解不保证可靠。如果生产环境对判断准确性有要求可以在模型之外加一个约束规则校验器。比如先让模型输出结论然后用一段简单的 Python 函数检查是否与常量知识冲突def validate_walking_reasoning(sample, model_answer): # 如果目标是 car wash且模型答案为 yes就触发“缺失对象”校验 if sample[id] walking_to_car_wash_01: if yes in model_answer.lower(): return { valid: False, reason: Car wash requires a car; walking does not provide a car. } return {valid: True, reason: }这种方法不能覆盖所有常识场景但很适合真实业务里需要强约束的少数场景。比如在客服系统、推荐系统或内容审核系统中只要存在少量像“洗车必须带车”这类清晰约束就可以把它们沉淀成规则库在模型输出后做一次安检。5.3 训练增强与评测基准建设如果目标是改进模型自身能力提示词和后处理只是临时的。更根本的做法是在微调阶段加入反事实样本和逻辑一致性样本。一个可行的数据增强方向是构造“前提 - 异常组合”样本- 前提洗车店提供服务对象是汽车。 - 正常组合开车到洗车店。 - 异常组合走路到洗车店。 - 目标判断异常组合是否合理。通过让模型在训练阶段同时看到正常组合和异常组合它可以学习到“发现缺失对象”的推理模式而不只是记忆“走路到公园合理”这类单点知识。同时对于做 LLM 评测的团队建议把显著性偏差样本加入基准集。评测集不能只包含高难知识和复杂逻辑也应该包含这种“看一眼就发现矛盾”的常识反例。这类题目虽然句子短但非常能区分模型是否真正理解语义约束还是只是在做词频匹配。6. 常见问题排查与实验落地建议6.1 排查表格做这类实验时可能遇到的问题比较集中下面这张表可以作为排查参考。问题现象常见原因检查方式处理建议模型所有题目都答 yes指令理解弱或选项引导不足查看输出原文检查 prompt 里是否有偏向增加选项、把“No”放在前面、要求先拆前提输出格式五花八门模型生成过长解释查看 max_tokens 和 temperature降低 max_tokens增加解析规则同一题多次运行结果不同温度过高或采样开启记录 temperature、seed、sample id固定温度或固定随机种子换一个模型后偏差趋势消失大模型常识存储更强在多个模型上重复实验统一模型版本不要跨版本汇总自己无法判断答案对不对题目设计存在歧义请多人标注确认标准答案只保留无争议样本显存不足无法加载模型模型规模超出 GPU 显存查看进程显存占用用量化版本或升级硬件6.2 学习环境和生产环境差异学习环境里跑通一个脚本很容易但进入生产环境或正规评测流程还需要考虑几个细节。模型版本要锁定不能悄悄升级。大模型版本升级可能导致同一 prompt 的输出语义大改。提示词版本要记录。建议给每个 prompt 模式编号例如direct_v1、cot_v2否则复盘时很难定位结论来自哪份配置。要做并发控制。循环调用模型服务时如果并发过高可能压垮本地推理服务导致超时和假失败。要保存原始输出。不要只保存“正确/错误”因为以后还需要分析错误模式。如果用 API要设置超时和重试机制避免网络不稳定影响实验结果。6.3 尝试的边界不要把一个例子推广成唯一结论Walking to the Car Wash是一个很好的教学案例但它只是一个样例。单独的样例不能证明某个模型整体上存在显著性偏差。真正的结论需要基于多组题目、多种提示词、多个模型和多次重复实验。原因是生成式模型输出高度依赖上下文。一个模型在这个题目上答错不代表它在所有常识推理题上都弱一个模型在这个题目上答对也不代表它真正理解了约束。所以每次分析结果时都要注明实验条件模型版本、temperature、prompt 模式、样本数量、重复次数。没有这些信息准确率数字没有太多参考价值。7. 延伸从“洗车”例子到通用常识推理评估7.1 如何构建显著性偏差基准测试集如果你想把单个例子扩展成一个小型基准可以按照下面的清单设计题目。每个样本至少包含以下字段question有歧义或存在显著性干扰的问题。choices至少一个直观答案和一个常识正确答案。label标准答案。salient_trigger最容易被模型抓住的显著词。hidden_constraint模型需要调用的背景知识。category冲突类型。冲突类型可以是冲突类型示例动作与目的地冲突走路去洗车店、开车去海滩边步行大道对象缺失用勺子喝汤、用笔刀切西瓜角色与场景冲突医生在法庭上做手术、厨师在工地炒菜时间顺序冲突先吃饭再买菜做饭空间关系冲突把公园放在冰箱里每一类至少准备 10 到 20 条才能形成有统计意义的评测集。7.2 适用于哪些后续场景这套方法可以迁移到几个常见场景。第一Prompt 评测在迭代 prompt 时加入反直觉样本观察新版 prompt 是否会引入显著性偏差。第二模型选型对比多个候选模型时除了看综合 benchmark 分数还要看它们在显著性偏差子集上的表现。第三内容风控一些安全过滤规则也会被显著词带偏用同样思路构造测试数据可以提前发现规则漏洞。不过要注意反直觉样本不能无限制增加。如果一个评测集里全是反常识陷阱模型可能倾向于怀疑所有输入反而降低正常场景的准确率。合理比例是显著偏差样本占总样本的 10% 到 20%既能暴露问题又不会让模型过度“警惕”。7.3 给团队或新手的落地建议如果团队刚开始做 LLM 应用可以从这个案例出发做一次小范围“偏差巡检”。方法很简单从业务里找出 20 到 50 个常见问题再手动给每个问题增加一个“显著性干扰项”让测试者判断模型有没有被干扰项带偏。比如客服系统里用户常问“退款怎么操作”干扰项可以是“退款怎么投诉”看看模型会不会因为“投诉”这个强情绪词改变回答风格。对于新手建议不要一上来就用 70B 级别的大模型做实验。先用本地小模型跑通脚本理解输出格式和判断逻辑再切换到业务目标模型。这样既能节省成本又能快速理解显著性偏差的本质。最终要记住的一点是模型输出自然流畅不等于模型理解语义。一个表面上完美通顺的句子可能在常识层面是矛盾的。把这个矛盾作为测试用例是检验 LLM 常识推理能力最便宜也最有效的方法之一。