多模态模型答错图里的小字时先别换模型——用 PerceptionBench 单独测感知这一层

文章导读
先做一个隔离判断:同一张图,分别问“整体场景是什么”和“右下角那行小字写了什么”。如果整体描述稳定正确,换成小字就稳定出错,那么更可能是感知层没有看清,理解层未必有问题。此时直接换更大的模型,成本高,而且未必针对真正的原因。下面的做法把感知这一层单独测一次:用一小批可人工核对的题目、统一的判分口径和可复现的记录,先把“模型没看清”和“模型不聪明”分开。前提是你能拿到原图,能固定模型和请求参数,能保
📋 目录
  1. A 先确认错的是细节而不是整体
  2. B 构造只考感知的最小题目集
  3. C 与基准的感知类题目对齐判分口径
  4. D 跑一次并保存可复现的结果
  5. E 顺着结果决定补图源、加预处理还是换模型
A A

先做一个隔离判断:同一张图,分别问“整体场景是什么”和“右下角那行小字写了什么”。如果整体描述稳定正确,换成小字就稳定出错,那么更可能是感知层没有看清,理解层未必有问题。此时直接换更大的模型,成本高,而且未必针对真正的原因。下面的做法把感知这一层单独测一次:用一小批可人工核对的题目、统一的判分口径和可复现的记录,先把“模型没看清”和“模型不聪明”分开。前提是你能拿到原图,能固定模型和请求参数,能保存每次回答;边界是这套测法只能说明感知层是否可疑,不能替代对业务整体效果的评估。

当模型漏掉小字、密集表格、低对比文字时,先做隔离测试再换模型:同一张图分别问整体和局部,确认错误集中在具体区域;用只问“图上写了什么”的最小题目集排除推理干扰;判分按统一规则,与基准中同类感知题对齐;记录逐题结果并重跑验证一致性。补图源、加预处理都无效,且错误集中在同一类视觉特征时,再考虑换模型。

先确认错的是细节而不是整体

这一步的目的是把问题限定在感知层。取一张真实出错的图,用两条提示词分别问,不要混在同一次对话里,避免上文影响回答。提示词可以这样写(把括号内的内容替换成你图上的实际内容):

# 整体问法
请描述这张图的整体内容:画面里有哪些主要对象、大致在做什么。

# 局部问法
只看画面(右下角/表格第二行第三列)这一小块,逐字写出上面的文字,不要解释含义。

对照两组回答时,不要只看“哪句错了”,要指出差异出现的具体位置:是整段文字被漏掉、个别字被替换成形近字、数字和小数点错位,还是行列对应关系错乱。形近字替换和数字错位通常指向分辨率或对比度不足;行列错乱更可能是布局解析的问题,属于感知与结构解析的交界处。如果整体问法也错,比如把主体对象认错,那么问题不止在细节,这时先别继续往下套用这套小题。

构造只考感知的最小题目集

目的是让测试不受推理难度干扰。每道题只问“图上写了什么”,答案必须能在原图上一字一字核对出来。建议选取三类图,每类先取几道,够用就行,不必凑数量:

  • 小字图:字号明显偏小、缩放后笔画粘连的标签、票据小字、截图里的注释。
  • 密集表格:多行多列、有无框线都行,重点是有单元格文字密集相邻。
  • 低对比文字:浅灰字压在浅色底上、深色底上的暗色字、带水印或背景纹理的文字。

选题标准:文字在原图中真实存在且清晰可辨,答案唯一,出题位置可以明确指认(给出坐标或行列号)。排除标准:需要计算、需要外部知识、答案有歧义、以及人眼放大后也读不出来的图,因为人眼读不出就不能算模型答错。也不要把“请判断这张表说明了什么趋势”这类推理题放进这套小题,否则无法区分是看不清还是想不明白。

与基准的感知类题目对齐判分口径

目的是让自己的小题和基准结果能直接对照。做法是统一判分方式,而不是改题去迁就结果。判分采用二值判对错加错误类型标注:答对记 1,答错记 0,并记录错误属于漏字、错字、数字错位、行列错位中的哪一类。文字比对前先做归一化,保证两边规则一致:

def normalize(s: str) -> str:
    # 统一全角半角、去首尾空白、合并连续空格、统一大小写
    s = unicodedata.normalize("NFKC", s)
    s = re.sub(r"\s+", "", s)
    return s.strip().lower()

def judge(pred: str, gold: str) -> int:
    return 1 if normalize(pred) == normalize(gold) else 0

如果基准里同一类感知题是人工判对错的,那么你的小题也用同一批人、同一份判分说明来判,不要一边用脚本一边用人判。基准的具体子集名称和题目数量,以你手上实际能跑的那份为准,用前先在本地确认它包含哪类感知题、答案格式是什么;不要凭印象写子集名。两边都归一化之后,逐题结果才能放在一起看。

多模态模型答错图里的小字时先别换模型——用 PerceptionBench 单独测感知这一层

跑一次并保存可复现的结果

目的是让结论能被别人复核。下面是一个通用命令骨架,脚本名、模型名和路径按你的环境替换,参数建议固定 temperature 并写进输出,否则重跑结果不一致时说不清是模型随机还是代码问题。

python run_perception.py \
  `--cases` cases.jsonl \
  `--model` <你的模型标识> \
  `--temperature` 0 \
  `--out` runs/$(date +%Y%m%d-%H%M%S).jsonl

# 复核:同一输入重跑一次,逐题比对
diff <(jq -r '.case_id + "\t" + (.verdict|tostring)' runs/a.jsonl) \
     <(jq -r '.case_id + "\t" + (.verdict|tostring)' runs/b.jsonl)

每条记录建议包含这些字段:case_id、image_path、region(坐标或行列号)、question、model_answer、gold、verdict、error_type、model、temperature、run_id。同一输入重跑两次,如果 verdict 一致,说明流程本身可复现,可以直接拿结果做判断;如果两次不一致,先查是不是 temperature、图片预处理或缓存没固定,把不稳定的题单独标出来,不要用一次结果下结论。

结果记录表可以直接照这个结构填,逐题看,不要只看总数:

case_id图片类型位置goldmodel_answerverdicterror_type
c-001小字右下角标签批号 A7批号 A10错字
c-002密集表格第2行第3列12.5012500数字错位
c-003低对比页脚灰色说明仅供内部使用(空)0漏字

顺着结果决定补图源、加预处理还是换模型

判定口径按三条线走。整体答对、局部小字错,按感知问题处理;整体和小字都错,先修图源再看模型;小字能答对,但回答含义或计算错,那属于理解层,换模型的收益要重新算。确认是感知问题后,三种动作按成本从低到高依次试,每次都只重跑感知小题,看对应位置有没有改善:

  • 补图源:重新导出原分辨率截图,而不是从压缩过的聊天记录里二次保存。验证方式是同一题集、同一模型只换图片,逐题对比同一 region 的 verdict;如果小字题从 0 变 1,说明之前是图源损耗。
  • 加预处理:对出错区域做裁剪放大、提高对比度或灰度二值化,只对指定 region 生效。验证方式是同时看两类题:目标小题是否改善,其余整体题是否被破坏。若预处理后整体描述变差,说明裁剪掉了上下文,需要缩小裁剪范围。
  • 换模型或换输入分辨率档位:只在补图源和预处理都无效、且错题集中在同一类视觉特征(比如全部是低对比)时才考虑。验证方式是用同一题集跑新模型,对比逐题差异,而不是看平均分,重点看原来错的那几道有没有针对性改善、原来对的有没有变错。

每一步改动都保留 run_id 和改动说明,这样下次再遇到小字答错时,可以直接翻记录判断是图的问题、前处理的问题,还是模型本身的能力边界,不必从头再猜一遍。