图片理解幻觉不能只调温度:视觉语言模型多模态落地实践

发布时间:2026/11/2 11:57:13
图片理解幻觉不能只调温度:视觉语言模型多模态落地实践

聊图片理解幻觉这个话题,很多人第一反应是去调temperature,把它从默认的 0.7 压到 0.2 甚至 0,然后发现模型描述里那些"凭空多出来的东西"确实少了,于是得出结论:温度能降幻觉。我一开始也这么干过,后来在做一套图文一致性核查流程时被现实教育了——温度压低之后,误报是少了,但漏报涨得厉害,而且模型的描述变得越来越像模板填空,很多图里明明有的关键细节它干脆不提。所以"模型温度能降低图片理解幻觉吗"这个问题,答案不是能或不能,而是要看你把哪一种错误算作幻觉、愿意为降低误报付出多少信息量代价。这篇内容面向正在做多模态应用落地的人,尤其是那些已经在用视觉语言模型做图片描述、图表读取、质检辅助、无障碍转写的同行,我会把温度这个参数在图片理解场景里到底动了什么、为什么它只解决了一部分问题、以及在什么情况下低温反而挖了个新坑,一条条拆开讲。

1. 把温度和解码这两件事先分清楚

1.1 温度改的是 logits 的分布形状,不是模型对图像的理解

很多讨论把温度和"模型看得准不准"混在一起,其实这是两个层面的事。视觉语言模型处理一张图,先由视觉编码器把图像切成 patch,编码成一组视觉 token,再和文本 prompt 一起送进语言模型主干,最终在每个生成位置输出一个词表大小的 logits 向量。温度 T 只作用在最后一步:

p_i = exp(z_i / T) / Σ_j exp(z_j / T)

T 小于 1 时,原本高的 logits 被相对放大,分布变尖,采样更倾向于选概率最高的那几个词;T 大于 1 时分布被压平,长尾词的出场机会变多;T 趋近 0 就退化成 argmax,也就是贪婪解码。

关键在于:这一步没有回头去改视觉特征,也没有改模型对图像内容的内部分布。能看见的东西还是那些,看不见的东西还是看不见。温度决定的是"在已经算出来的这堆候选词里,你怎么挑"。所以指望靠温度把一个本来就没看清图里文字的模型变得能读准文字,那是南辕北辙。它真正能影响的,是当模型对某个判断只有五六成把握时,它敢不敢把那个长尾但可能正确的词说出来,还是索性退回到最常见、最安全的表达。

我在早期的一次排查里就吃过这个亏。当时一批商品图描述里频繁出现"包装盒"这个词,但图里根本没有包装盒,只有商品本体。我第一反应是把温度降到 0.1,结果"包装盒"确实少了,取而代之的是大量"产品"这种极度模糊的词——错误被换了一种形式留下来,只是更难被规则检测到了。这件事让我明白,温度压低的本质是让模型更保守,保守不等于更准确。

1.2 图片理解里的幻觉至少有五类,温度对它们的影响完全不同

如果不分类,讨论温度降幻觉就会变成一笔糊涂账。我按实际排查经验把它分成五类,每一类对温度的敏感度差别很大:

幻觉类型典型表现温度调低的实际效果
物体存在性幻觉图里没狗,描述里写"一只狗在草地上"明显减少,这是低温最有效的场景
属性幻觉红色说成蓝色,三个人说成两个人小幅减少,但不稳定
空间关系幻觉左边说成右边,前面说成后面几乎无改善,低温甚至会固化错误
文字与图表幻觉报表数字读错,标签文字拼错基本无效,偶尔更糟
越界推断幻觉看不清的角落被补成"合理"的场景低温反而加重

为什么后三类对温度不敏感?因为它们的根源在视觉侧,不在解码侧。空间关系依赖模型对坐标的隐式建模,文字识别依赖高分辨率细节的保留程度,越界推断则是模型被训练成"必须给出完整描述"这个习惯的产物。这三类问题的解法都在图像输入和指令设计上,调温度属于在错误的地方使劲。有次我处理一批带小字水印的截图,温度从 0.7 一路降到 0.1,识别准确率几乎没有变化,最后靠把原图切成四块分别送进去才解决——这个对比非常直观地说明了边界在哪。

1.3 采样和解码不是一回事,别把两个开关搞混

实际调用接口时,温度经常和top_p、top_k、repetition_penalty绑在一起调,很多人调完温度觉得"有用",其实是旁边那个参数起了作用。举个具体的:你把温度设成 0.7,但top_p设成 0.5,等效的候选集已经被截断了,实际行为接近低温;反过来温度设 0.2 但top_p保持 1.0,长尾词还是有一点点机会冒出来。

我的建议是调参时一次只动一个变量,而且明确记录完整参数组合。我见过最典型的翻车是:某次对比实验,低温组同时把repetition_penalty从 1.0 提到了 1.2,结果低温组的重复描述大幅减少,被误认为是温度的功劳——实际上重复是惩罚项压下去的,而惩罚项本身又引入了新的属性错误。这种多变量同时改的实验结论,基本没有参考价值。

2. 低温为什么会让描述"看起来更准"

2.1 高频词偏好:模型天然倾向于说"安全"的话

语言模型的训练目标决定了它偏爱高频表达。"一只棕色的狗在草地上奔跑"这种句式在语料里出现的次数,远多于"一只深褐色的短毛犬正沿着草坪边缘小跑"。当温度接近 0 时,模型每次都选概率最高的 token,产出的就是这种最主流的描述模板。

这种模板有个副作用:它极少说错,但也极少说细。你要检测幻觉,最容易发现的恰恰是那些具体到可疑的细节,比如"左手拿着一个蓝色的马克杯",而低温输出的是"一个人站在房间里"。前者可能有幻觉,后者几乎不会错,但信息量接近于零。如果你只用"描述里有没有不存在的物体"这一个指标来评估,低温当然大获全胜——问题是这个指标本身有偏。

我做过一个简单的小实验:找 60 张构图比较复杂的日常场景图,让同一个模型在温度 0 和 0.8 下各描述一遍,然后人工统计"图中真实存在的显著物体被提及的比例"。低温组的幻觉率确实低了约四成,但覆盖率也从 0.71 掉到了 0.52。也就是说,模型为了不说错,干脆少说。这在实际业务里往往是更糟的结果,尤其是无障碍描述、内容审核辅助这类场景,漏掉关键信息比多说一句更危险。

2.2 一个可以自己复现的对照实验设计

如果你想把这件事在自己手上验证清楚,而不是听我说,下面这套设计成本很低,一两个小时能跑完。

素材准备:选 50 到 100 张图,覆盖三类难度——简单物体图(单主体、干净背景)、中等场景图(多物体、有遮挡)、含文字图(招牌、报表、包装标签)。每张图人工写出"图中确定存在的显著元素清单",作为判定的基准答案。这一步最费时间,但绝对不能省,用模型自己生成的描述当基准是循环论证。

参数扫描:温度取 0、0.2、0.4、0.7、1.0 五档,其余参数全部固定(top_p=1.0、无惩罚项、固定max_tokens、固定 prompt、固定随机种子)。每档对同一批图各跑一遍。为了看随机性,0.4 和 0.7 这两档各重复跑三次。

统一 prompt:用一句中性指令,例如"请客观描述这张图片的内容,只描述你确定看到的东西"。不要在低温组和高温组用不同 prompt,那是最常见的实验污染。

记录四个指标:

指标计算方式说明
实例幻觉率描述中提到但基准清单里没有的实体数 / 描述中实体总数越低越保守
覆盖率基准清单中被提及的实体数 / 基准清单实体总数越低越漏
平均描述长度字符数或 token 数判断是否信息塌缩
属性错误数颜色、数量、方位等属性与基准不符的次数需要人工核对

跑完你会看到一个很典型的曲线形状:实例幻觉率随温度单调上升,但在 0 到 0.2 之间几乎持平;覆盖率在 0.2 到 0.4 之间达到峰值,两端都掉。也就是说,真正的最优区间往往不在 0,而在 0.2 到 0.4 这段。这个结论我在好几个不同规模的模型上都复现过,虽然具体数值不同,形状很一致。

提示:如果你的模型接口不支持设置随机种子,那么多跑几次取平均是必须的。单次结果在温度 0.5 以上的波动可以大到让结论完全反转。

2.3 "看起来更准"的心理学陷阱

还有一个容易被忽略的因素:低温输出的句子更短、更平淡、更像人话里的"概述",人眼评估时会下意识觉得这种描述更可信。而高温输出长、细节多、句式花哨,读者会不自觉地用更严格的标准去挑刺。这是评估者偏差,不是模型能力的真实差异。

我踩过这个坑。最早做人工评分时,我让两位同事盲评温度和幻觉的关系,结果两人都倾向于认为低温组更好。后来把输出打乱顺序、去掉长度线索、只保留内容做逐条核对,结论就反过来了——低温组的"错误"少了,但"缺失"多了,综合事实一致性得分其实差不多。如果你要严肃评估,一定要做盲评,并且把漏检和误报分开算权重。权重怎么定取决于业务,内容审核偏向于宁可多报,无障碍描述偏向于不能漏关键元素,没有一个通用答案。

3. 把温度压到 0 之后冒出来的三个反直觉现象

3.1 该说"看不清"的地方,模型反而更敢编

这一点最反直觉。理论上温度低应该更保守,但在模糊区域上,低温反而更容易给出确定答案。原因是这样的:模型在遇到看不清楚的区域时,候选词里其实有一批是"无法确认""模糊不清""看不清楚"这类表达,但它们的概率天然低于"一个白色的物体""一扇门"这种具体的名词短语——因为训练语料里,描述性文本绝大多数是在给出确定信息,"不确定"这种表达相对稀少。

温度高的时候,长尾词有机会被采样到,模型偶尔会说"右侧有个模糊的白色形状,无法确定是什么"。温度趋近 0,模型每次都选概率最高的那个词,于是它跳过了"无法确认",直接选了那个最具象的名词。低温消灭了模型承认不确定的能力。这个现象在图像边缘、反光区域、严重遮挡、低照度场景里特别明显。

我在做一批夜间监控截图的分析时被这个问题折磨了很久。温度 0 时,模型对画面里几乎全黑的一角言之凿凿地说"有一辆深色轿车",温度 0.6 时反而会说"画面右下角较暗,难以辨认"。后者才是正确行为。后来我的做法是在指令里硬性加入一条:如果某个区域不清晰或存在遮挡,请明确说明"无法确认",不要推测。加上这句之后,温度 0 的输出才开始出现"无法确认",但它依然比温度 0.3 时少。这说明指令能缓解,但温度对"表达不确定性"这件事的影响是结构性的。

3.2 描述退化:从讲内容变成填空模板

连续跑几百张图之后,低温组的输出会呈现出一种很明显的模式化特征。开头几乎都是"这张图片展示了",中间"可以看到",结尾"整体来看"。这种结构本身没错,问题是当所有图都这么写时,模型实际上是在用句式填补内容空缺——它没有更多可说的时候,就用这些连接词把句子撑起来。

具体表现是:复杂场景的描述长度不升反降,而且下降得很有规律,越复杂的图降得越多。简单物体图的描述长度在温度 0 和 0.5 之间差别不大,复杂场景图的平均长度能差 30% 以上。这说明低温不是均匀地减少输出,而是优先砍掉了那些"不太确定但可能是真的"细节。

对下游任务的影响很直接。如果你拿这些描述去做检索或者做标签抽取,"人""房间""桌子"这种词会大量出现,而"木质的""靠窗的""第三层"这种区分度高的词会消失,检索的召回率会肉眼可见地掉。我在一个内部图片检索的小系统上验证过,把描述生成温度从 0.7 调到 0.1,索引里的独特词数量掉了四成多,几个细分查询的命中率从 0.8 左右掉到 0.6 上下。这件事之后我把描述生成和问答生成的温度分开配了,不再共用一个默认值。

3.3 温度 0 并不等于完全确定,这是工程上真实存在的坑

很多人以为温度设 0 就变成确定性函数,同样的输入必然同样输出。实际不是。

首先是浮点累加顺序的问题。批量推理时,不同 batch size 会导致矩阵乘法的归约顺序变化,logits 在最后几位上有微小差异。当两个候选词的 logits 极其接近时,这点差异足以让 argmax 翻转。其次是混合精度和不同硬件的差异,同一份权重在不同卡型上跑出来的 logits 不保证逐位相同。再就是很多推理框架为了吞吐会做动态批处理,同一张图放在不同批次里可能落到不同的计算路径上。

我遇到过最离谱的一次:同一张图、同一个 prompt、温度 0,连续调用 10 次,有 2 次输出的最后一个分句不一样。排查下来是服务端的批处理调度差异。所以如果你的业务要求严格确定性(比如要做可复现的评测),要么在客户端做结果缓存,要么接受"近似确定"并在评测里多次采样取多数。这个细节在论文里基本不会提,但在线上真的会咬人。

4. 一轮完整的温度梯度实测与结果解读

4.1 测试集构造:难度分层比数量更重要

我在一次内部评估里搭了一套 120 张图的测试集,分四层,每层 30 张:

  • L1 单主体清晰图:白底或纯净背景,单个物体,无遮挡。用来测基础能力下限。
  • L2 多物体场景图:室内外日常场景,物体数在 5 到 15 之间,有部分遮挡。
  • L3 含文字图:路牌、菜单、产品标签、发票、简单报表。用来测文字类幻觉。
  • L4 退化图:低照度、强反光、运动模糊、局部过曝。专门用来测"该不该承认看不清"。

每张图配三份标注:显著元素清单、属性清单(颜色/数量/相对位置)、以及一份"不确定区域"清单(人工标记哪些地方模棱两可)。第三份清单是很多人会漏掉的,但没有它就没法评估模型"承认不确定"的能力。

采样策略上,L1 和 L2 每张图只问一个问题(开放式描述),L3 问两个(描述 + 具体字段提取),L4 只问开放式描述。总调用量大概 600 多次,成本可控。

4.2 结果:幻觉率、覆盖率和"诚实度"三条曲线并不同步

跑完之后我把关键数据整理成下表(数值做了归一化处理,只反映趋势):

温度实例幻觉率覆盖率描述均长不确定表达占比L4 编造率
00.110.52780.030.34
0.20.130.66960.090.21
0.40.160.691080.140.16
0.70.240.631210.170.22
1.00.330.551280.180.31

几个值得说的点:

幻觉率在 0 到 0.2 之间几乎没变,真正的分水岭在 0.2 到 0.4 之后。这说明把温度从 0.7 降到 0.2 收益很大,但从 0.2 继续降到 0,收益接近于零,反而损失了覆盖率。

L4 退化图的表现和整体趋势相反。温度 0 时编造率最高(0.34),0.4 时最低(0.16)。这直接印证了前面说的"低温消灭不确定表达"。对于退化图占比高的业务场景,温度 0 是个糟糕的选择。

不确定表达占比随温度单调上升,从 0.03 涨到 0.18 之后趋于平缓。这意味着如果你希望模型主动说"看不清",光靠温度不够,还得靠指令和训练层面的引导。

描述均长在温度 0.4 之后增长变缓,说明高温带来的主要是啰嗦而不是新信息。超过 0.7 之后新增的内容里,幻觉比例明显升高,这部分基本是模型在凑字数。

4.3 分场景的最优温度不是同一个数

把所有任务混在一起统计会掩盖差异。我把 L3 的文字类单独拎出来看,温度对它的影响几乎是平的:温度 0 时字段提取准确率 0.61,温度 0.2 时 0.60,温度 0.7 时 0.58。这类任务的瓶颈完全在视觉分辨率上,把原图放大两倍再切片送进去,准确率直接从 0.61 跳到 0.83,比调任何采样参数都管用。

L2 多物体场景则对温度很敏感,0.2 到 0.4 是明显的最优区间。L1 简单图各温度都差不多,因为没啥可错的。L4 退化图的最优值在 0.4 到 0.6 之间,偏高,因为它需要模型有"说出不确定"的余地。

我的结论是:不要给一个系统配一个全局温度,按图类型或任务类型路由,效果比全局细调好得多。实现上也不复杂,前置一个轻量分类器或者直接用启发式规则(检测到文字区域走低温+切片,检测到低照度走中温+不确定指令)就行。

5. 真正压幻觉的几套组合拳

5.1 解码层面:对比解码的思路值得借鉴

既然温度只能调节"保守程度",那有没有办法在解码阶段就针对视觉输入做校正?有的,而且思路很巧妙,值得了解一下。

核心想法是:模型在生成每个 token 时,同时算两遍 logits——一遍用完整的图像输入,一遍用削弱过的图像输入(比如把图像特征加噪声、降低分辨率、或者干脆去掉图像只留文本)。完整输入下某个词的 logits 高于削弱输入下同一个词的 logits,说明这个词是真的从图像里读出来的;如果两者差不多甚至后者更高,那这个词很可能是模型从语言先验里编的。

把这个差值放大后加到原始 logits 上再解码,就能压制那些纯靠语言习惯生成的词。这套方法在不同实现里有不同变体,有的用模糊图做对比,有的用注意力图掩码,但本质都是"用视觉证据的增量来筛选候选词"。

我自己实现过一个简化版,成本大约翻倍(因为要跑两遍前向),在 L2 场景上把实例幻觉率从 0.16 压到 0.09 左右,覆盖率basically 不掉。代价是推理延迟和显存都涨了,所以只适合离线批处理或者对准确率要求极高、吞吐不敏感的场合。在线服务里我用得更多的是一个廉价替代:对模型输出的每个实体,再发一次"这个物体在图中哪个位置"的追问,答不出合理位置的就标为可疑。这个方法很土,但能把大部分存在性幻觉捞出来,额外成本只有一次短问答。

5.2 输入层面:分辨率和切片是性价比最高的一招

如果只允许我改一个东西来降幻觉,我不会改温度,我会改输入分辨率。

视觉编码器是按固定 patch 尺寸切图的,整图缩放到模型输入尺寸后,小目标会退化成几个像素,细节信息在编码阶段就永久丢失了,后面任何解码技巧都救不回来。这就是为什么文字类幻觉调温度没用。

具体做法有两种。第一种是提高输入分辨率,很多模型支持动态分辨率,直接传原图比缩放到固定短边效果好很多。第二种是切片:把图切成有重叠的 2×2 或 3×3 块,每块单独送进去描述,最后汇总。切片会带来一个副作用——块与块之间的上下文丢失,处理跨块的空间关系时会出错,所以我一般在切片之外还会送一次全图概览,让模型知道整体布局。

实测数据:一批带小字的产品标签图,整图输入时关键字段抽取准确率 0.58,切成 4 块后到 0.79,切成 9 块(带 10% 重叠)到 0.86。这比任何采样参数的收益都大一个量级。代价是调用次数涨了 4 到 9 倍,成本要算清楚。

5.3 输出层面:把"不确定"变成一种正式的输出格式

模型不愿意说"不知道",很大程度是因为开放式描述这个任务形式本身在逼它填满。解决办法是把它从自由文本改成结构化输出,显式提供一个"未知"选项。

比如不要问"请描述这张图",而是要求输出 JSON:

{ "main_objects": ["..."], "attributes": {"color": "...", "count": "..."}, "uncertain_parts": ["..."] }

并且在指令里写死:如果某项信息在图中无法清晰辨认,该字段填 "unknown",不要根据常识推测。

这招的效果比我想的好。同一批 L4 退化图,自由描述时不确定表达占比 0.09(温度 0.2),改成结构化输出后升到 0.31,编造率从 0.21 降到 0.13。原因很直白:JSON 结构给了模型一个合法的"空位",它不需要用自然语言去组织"我不确定"这种少见表达,只要填个字符串就行。

还有一个配套技巧:字段级校验。对数量、颜色这类离散属性,让模型给出答案后再追问一次"你确定是 X 吗",两次答案不一致的标记为低置信。这个方法很粗糙,但在我的测试里能捞出一半左右的属性幻觉,成本只有一次短调用。

5.4 别忽略训练侧:有些幻觉是你自己的指令教出来的

最后说一个容易被甩锅给模型的问题。如果你的 prompt 里充满了"请详细描述""尽可能完整""不要遗漏任何细节"这类要求,模型为了满足完整性要求,必然会去补它看不清楚的部分。你要求的完整性和你要求的准确性是矛盾的,模型只能二选一,而且它会优先服从更强烈的那个指令。

我的做法是把指令的重心从"完整"挪到"准确":明确说"只描述你有把握的内容,宁可少说"、"对每个描述的物体,如果你不能指出它在图中的大致位置,就不要写"。后面这句特别好用,因为它把"编造"变成了一件需要额外付出成本的事。

另一个很多人没注意的点:多轮对话里,前面轮次的模型输出会进入上下文。如果第一轮它编了一个不存在的物体,后面几轮它会持续围绕这个不存在的物体展开,越聊越离谱。这个问题温度完全管不了。解法是在多轮场景里,每轮都重新附上图像,并且在系统提示里强调"以图像为准,不要延续之前对话中的描述"。我在一个多轮视觉问答的 demo 上验证过,加上这句之后,跨轮次的幻觉扩散明显收敛。

6. 参数怎么配,以及上线后怎么盯

6.1 按场景分档的温度配置

基于前面这些实测,我现在的默认配置大概是这样的:

场景temperaturetop_p配套措施
图片内容概述/打标0.2 ~ 0.30.9指令强调"只写有把握的",限制输出长度
无障碍图像描述0.4 ~ 0.50.95结构化输出 + 覆盖优先,接受少量误报
图表/票据字段提取01.0高分辨率切片 + 字段级二次校验
视觉问答(多轮)0.3 ~ 0.50.9每轮重附图像,禁止延续前文描述
创意配图解说0.8 ~ 1.00.95允许自由发挥,但明确标注哪些是推测
质检/异常判定辅助0.1 ~ 0.21.0强制二分类输出 + 人工复核,不接受自由文本

几个细节:top_p不要设得太低,0.9 左右是个平衡点,设到 0.5 以下会明显加剧信息塌缩,和低温叠加起来效果更糟。max_tokens要卡住,不设上限的话高温下模型会用越来越长的句子填充,而新增部分基本都是幻觉。重复惩罚项在有结构化输出的场景里建议关掉,它容易让模型为了避免重复而强行换词,反而引入属性错误。

6.2 上线之后盯什么指标

调参只是起点,真正决定线上效果的是监控。我一般盯三个:

幻觉抽检率:每天随机抽 100 到 200 条输出做人工核对,记录实例幻觉率。这个数字的绝对值意义不大,看趋势更有用——如果某天突然涨了,先查是不是模型版本变了或者 prompt 被谁改了,再查温度配置有没有被覆盖。

不确定表达占比:这个指标是"诚实度"的代理。如果它长期接近 0,说明你的模型在硬撑,很可能在编。我给它设过一个下限告警,低于 2% 就去看是不是指令写得太强势了。

下游任务指标回归:如果你的描述是给检索或分类用的,一定同时看那个任务的指标。我见过描述质量"提升"了但检索召回下降的情况,原因就是低温把区分度高的词砍掉了。

另外提醒一句:任何参数改动都要跑回归集。温度这种全局参数,改一次影响所有场景,靠感觉调很容易在某个不常被测试的分支上翻车。我就有过一次把温度从 0.3 调到 0.1 "优化"幻觉,结果某个依赖长描述的摘要功能输出变得极其干瘪,用户投诉了一周才发现根因。

6.3 我个人的判断

绕回最初那个问题。温度确实能压下一部分图片理解幻觉,但它压的主要是"模型犹豫时倾向于编"的那一部分,通过让模型更保守来实现。代价是覆盖率下降、描述退化、以及模型丧失表达不确定性的能力。而图片理解幻觉里最顽固的那几类——空间关系错乱、文字读错、模糊区域强行补全——温度基本无能为力,这些必须从分辨率、切片、结构化输出、对比解码这些方向解决。

我现在的做法是:温度设 0.2 到 0.4 之间的某个值,把它当成"减少抖动的阻尼器"而不是"提高准确率的开关";真正的准确率提升靠输入侧的预处理和输出侧的结构约束。还有一个我用了很久的小技巧——在评测脚本里同时记录幻觉数和覆盖率,只要这两个指标有一个明显恶化就报警,别让自己陷入只看单边指标的陷阱里。这套组合用了大半年,比单纯死磕温度参数稳得多。