CER仅1.72%却暗藏幻觉:VLM转OCR的指标失效与评测重建
说起来有点讽刺我最近在做一个表单类文档的 OCR 评测初版结果漂亮得让人发慌字符错误率 CER 只有 1.72%。换算一下相当于每 100 个字符里只错 1.72 个这种精度放到商用场景里已经属于可以大张旗鼓宣传的数字了。但我把抽检样本翻出来逐条核对的时候发现了很不安的东西——几个供应商名称、经办人姓名、银行卡号的后四位全都错了。尤其是人名几乎从“张三丰”变成了“张四丰”汉字被悄悄替换成了形近字视觉上人眼不仔细看根本发现不了但数据入库以后就是完全不同的两个人。这就是我这次想聊的核心问题当你把 VLM视觉语言模型从“看图说话”的场景挪到 OCR 的严肃识别场景里你会发现传统的 CER / ACC 指标根本看不见幻觉。指标好看不代表结果正确。CER 是一个字符级别的编辑距离指标它计算的是把识别文本转成真值需要多少次“插入、删除、替换”。它对于单词级的拼写错误很敏感但它完全无法回答一个问题这个错错得有多严重把“有限公司”识别成“有限责任公司”CER 会很难看但这在业务上几乎无所谓反过来把人名、编号这类不可纠错的实体字段识别成一个语义完全不同的合法字符串CER 可能只涨零点几个百分点但业务上这是重大事故。这篇文章我想把 VLM 转 OCR 这条路踩过的坑、指标失效的机制、以及补救的评测方案完整讲一遍。1. VLM 转 OCR 的整体设计与方案选型1.1 为什么要把 VLM 用在 OCR 上先说清楚我为什么选这条路。传统的 OCR 系统比如 PaddleOCR、Tesseract、RapidOCR它们的工作方式本质上是一个多阶段流水线检测文本区域逐块裁剪再过识别模型最后拼接。这套体系很成熟识别印刷体、固定模板票据的时候又快又稳。但它在三类场景下明显吃力版面极度复杂、元素有嵌套关系的表格票据图文混排中文字和视觉元素相互解释的部分以及需要语义理解才能正确输出的内容比如“总金额大写”后面跟的到底是“人民币壹仟元整”还是对方手写了“1000元”然后旁边有个印章压住了。VLM 是一条完全不同的路线。端到端输入图片、输出文字模型自己决定看哪里、读什么、怎么组织语言。好处是它对版面干扰的容忍度极高不需要单独训练检测头也不需要做复杂的后处理排序。坏处是它本质上是“根据图像生成文本”换句话说是在做条件语言模型的采样。于是所有大语言模型会犯的病它都会犯幻觉、自由发挥、对不确定区域强行编一个合理内容尤其是当图片质量不高、字段模糊或者被遮挡的时候VLM 会选择“圆过去”。使用 VLM 做 OCR 的典型架构有两种。第一种是轻量接入用 Qwen2-VL、MiniCPM-V 这类模型直接给一句“请把这页票据上的所有文字转写出来”然后把输出文本和真值做比对。第二种是重一点的 Agent 式方案VLM 只是阅读模块前面挂版面分析、字段定位后面挂校验纠错规则模型的输出只是一个中间结果。我一开始图省事用的第一种评测指标也是第一种模式下算出来的结果被 1.72% 这个数字狠狠骗了一次。1.2 2. 指标失效的机制CER 不是万能的CER 的计算是逐字符的编辑距离归一化它天然有好几个盲区。盲区一它不区分错误类型。替换、缺失、插入统统等价地拉高错误计数但业务上它们的杀伤力完全不同。身份证号的最后一位校验码识别错了CER 可能只加 1/n但这一位错了整条数据就废了甚至可能匹配到另一个人。盲区二它对“合法但错误”的字符串毫无惩罚。也就是说模型把“李慕白”识别成“李慕青”两次替换CER 依然很低但这两个人毫无关系。模型并没有产生一个不像话的输出它非常自信地给出了一个像模像样的人名。CER 只知道“不像”不知道“不对”。我后来把错误样本做了个分类发现了一个规律传统 OCR 的错误大多是“视觉层面的变形”比如0和O1和l8和B混了而 VLM 的错误是“语义层面的编造”它会把一个讹字补成一个通顺的词会跨行重组文本甚至会在印章把数字盖住的时候自己脑补一个逻辑上说得通的金额。这就是模型幻觉的典型特征。用一个特别糙的类比传统 OCR 错是抄写员眼神不好VLM 错是抄写员理解力太强看不懂还非要补全。你拿“错字数量”这个标准去考核后者等于只看作文有没有错别字不看他有没有谎报军情。1.3 为啥这项评测卡住了我整整一周一开始我怀疑是某个模型的权重加载有问题甚至怀疑是不是推理参数里的 temperature 调太高导致采样随机性过强。排查了很久最后发现 inference 参数完全正常temperature 0.1top_p 0.8输出就是模型真实水平。然后我怀疑是 prompt 写得不清楚于是换了几种措辞比如强调“逐字转写不要增删改”CER 确实下降了零点几个点但人名字段的错误率纹丝不动。真正卡住我的问题是认知上的我一直把注意力放在“降低 CER”上所有的优化动作都在围绕字符级差异做文章而没有意识到核心矛盾是“评测维度选错了”。后来我换了个思路把样本里的字段拆出来按字段类型分别算准确率。这一拆立刻真相大白。公司名、地址这类长文本字段VLM 表现得近乎完美人名、证件号、金额数字这类短实体字段错得一塌糊涂。因为长文本有上下文约束前后文可以互相纠正短字符串没有冗余一旦模型开始沿着错误的视觉特征推理就得靠瞎猜。猜得准不准完全取决于训练数据里见过多少个类似字形。2. 核心细节解析VLM 做 OCR 的幻觉重灾区2.1 三类最典型的人名替换错误模式我把错误样本整理了一遍归纳出三类高频模式。第一类形近字替换占大约六成。比如“侯”变成“候”“管”变成“菅”“未”变成“末”“干”变成“千”。视觉上用 VLM 的中间层表征来看这些 token 的视觉embedding确实非常接近替换之后输出的句子依然通顺。第二类同音字替换占两成。比如“张伟”变成“张苇”“晓明”变成“小明”。这类错误在纯视觉特征层面解释不通更像是模型在解码过程中把听觉通道的语言先验也带进来了。第三类多字省略或扩展。比如“欧阳娜娜”变成“欧阳娜”“爱新觉罗”变成“爱新罗”。这种常见于印章、手写体遮挡导致部分笔画确实不可见模型选择“跳过忐忑的字符输出一个有信心的子串”。最阴间的是第一类中的一个小分支模型输出的汉字不仅字形相似而且生成了训练语料里出现频率更高的常见字。我曾经遇到过“徐”被识别成“余”当时我一度怀疑是检测框漏了半个偏旁但后来检查原图“双人旁”在图像里非常清晰。这就是典型的语言先验压过了视觉证据模型觉得“余”在这里出现概率更高。2.2 表格、印章、低分辨率都是助燃剂并不是说 VLM 在所有图片上都会疯。我用完全相同的模型跑清晰印刷体、打印体、扫描件和手机拍摄件发现场景因素对幻觉概率的影响极大。表格是最明显的幻觉助燃剂。表格单元格里的文本天然缺乏上下文尤其是数字、简称这类短内容。模型看到一列表格很容易聪明地根据列名推断单元格内容而不是老老实实去读图。举个例子一张采购清单列头是“供应商名称”某一行盖了半个章模型就直接把上一行的供应商名抄了下来。CER 照样低因为只是两个字的差异但上下游数据对账的时候这就是一笔错账。印章是第二大类问题来源。公章压住文字的时候传统 OCR 往往是识别不出来或者乱码这种错误是显性的后处理能拦截VLM 不同它会主动把印章下的内容猜出来而且输出的时候完全不加任何“不确定”的信号结果可信度和普通字段没有任何区别。等你在下游校验规则里发现发票代码对不上已经晚了。低分辨率和字体生僻同样是大麻烦。长尾字体、艺术字、异体字这些样本在训练集里本来就少模型拿不准的时候就会往常见字上靠。你给它一张像素不高的伞兵绳照片它敢给你读成“余兵绳”。我会在后文给出一套防住这些幻觉的工程方案但在此之前评测体系必须先把这些错误暴露出来。2.3 为什么 CER 看不见这些错误说到底CER 是字符串距离不是语义距离。它对惩罚“数字变成相似数字”和“人名变成另一个人名”是一视同仁的。我们作一个简单的推演设某行文本总字符数 50模型把人名 3 个字全换成了另一个合法名字。CER 大约是 6%3 次替换除以 50。如果你用的真值只有 20 个字符CER 就是 15%。看绝对数值这似乎已经不难看了不问题的关键在于在没有专门针对实体字段的评测维度的前提下这个 15% 会被平均分配到所有样本的 CER 里。你看到的 1.72% 是全测试集 500 张图的平均一个高价值字段的错误被稀释在几百个低风险字段中完全隐形。再举一个更直观的例子如果测试集里 98% 的样本都是印刷体公司名和地址2% 的样本人名区域被故意擦掉。模型在 98% 的样本上 CER 能做到 1%在 2% 的样本上人在字段上大面积出错CER 高达 40%平均一下总体仅为 1.78%。这个数字没有任何实际意义但它会让你误判整个系统的健康度。3. 实操过程从 1.72% 到定位幻觉源的完整评测实验3.1 测试集与数据划分的重新设计我开始重新设计实验时先做了一件小事把真值表从“整行文本字符串”改成“字段级 JSON”。一张票据不再是一行话而是拆成vendor_name、vendor_id、amount、date、bank_card_no等字段。这一步很苦因为标注量翻了好几倍但这是整个评测体系重建的地基。字段级的真值表带来的第一个直接好处是我可以分别计算每个字段的准确率。这时候之前那个好看的数字立刻现出原形公司名字段识别准确率 98.6%而姓名字段只有 91.2%银行卡号字段 93.5%。注意这里我说的准确率是整字段精确匹配的准确率不是字符级指标。整字段精确匹配的意思是一个字都不能错错一个字就算这个字段失败。这才是业务视角的指标。数据集划分上也特别讲究。如果想把幻觉问题暴露出来测试集必须包含大量对抗样本。我重新构建了四类子集清晰印刷体 300 张、模拟盖章遮挡 100 张、低分辨率扫描件 100 张、手写叠加印刷 50 张。前两类模型表现都不错真正拉低成绩的是盖章遮挡和低分辨率集。在这里幻觉率直接干到了 20% 以上。3.2 用逐字段排查定位幻觉分布拿到字段级结果之后我把所有错误样本重新拉出来写了一个jsonl格式的错误审计文件每一行是这样子的{ image_id: ocr_11234, field: customer_name, ground_truth: 詹慕白, prediction: 詹慕青, cer_contribution: 0.02, error_type: similar_character_hallucination, confidence: 0.93 }注意看最后一行的 confidence模型对这个错误输出给出的置信度是 0.93。这是最恐怖的它在高置信地产生幻觉而不是犹豫不决地猜测。我后来又统计了一下所有字段错误里置信度超过 0.85 的占了 76%。这意味着单纯用概率阈值做后处理过滤几乎无效你没法通过“置信度低的输出不可信”这个规则来兜底。之后我做了两次人工分类。第一次按字段归类第二次按图片状况归类。交叉统计后发现盖章遮挡场景在“人名、金额”字段上的幻觉集中度是清晰印刷体的 8 倍。这就定位到了需要重点防御的具体场景。3.3 设定新的基准评测体系重建评测体系之后我不再单看 CER 了而是设定了四个维度同时汇报字符级 CER可以和行业基线横向对比、字段级精确匹配率、实体级语义错误率、幻觉样本占比。实体级语义错误率是我自己定的一个指标把预测值里的实体字段单独抽取出来和真值做比较若两个字符串代表的是不同实体则视为一次实体级语义错误。用人名举例“张伟”和“张苇”代表的是两个不同的人算错“张伟”和“张伟”一模一样算对。这个指标最大的作用是倒逼模型让它不能靠“读音相同”蒙混过关。幻觉样本占比指的是所有样本里“输出内容完整但存在语义性编造”的比例。这个指标不一定要压到 0毕竟印章遮挡等场景下强行猜测难免但它是衡量系统可信度的核心风向标。我实测下来一个好的 OCR 系统工程化部署时这个比例至少要压到 1% 以下否则下游数据业务根本兜不住。4. 常见问题与排查技巧实录4.1 为什么我换了更大更强的 VLM幻觉反而更明显我试过把 4B 参数级别的小模型换成 72B 级别的大模型得到的结论是整体 CER 确实下降了但实体幻觉不但没有消失在某些场景下反而更顽固。原因在于模型容量变大以后它的“世界知识”更丰富语言先验更强对残缺图像的自作聪明程度只会水涨船高。尤其是当推理时采样贪婪度拉满、只取最高概率 token 的时候模型会坚决地输出一个“更像话”但错误的结果连丝毫不确定性都不会表现出来。这和直觉是相反的。直觉会告诉你大模型更聪明应该更可靠实际是大模型更善于编造“合理的假话”。从工程角度我把这条经验总结为一个选择标准如果业务场景是整篇文档的语义理解、摘要、结构化抽取VLM 越大越好如果业务场景是精确转写、录入校验、字段归档模型大小不重要重要的是你如何限制它的自由发挥空间。大模型在这种场景下不一定是资产甚至可能是负担。4.2 如何设计 Prompt 来压制幻觉Prompt 设计确实有影响但影响幅度有限。我把多次实验下来最好用的 Prompt 模板写在这里请逐字转写这张图片中的所有可见文字。 要求 1. 只输出图片中真实存在的字符不要补全看不到的内容 2. 如果某个区域被遮挡、模糊或不可辨识用 UNKNOWN 标记 3. 不要根据上下文推测不存在的词 4. 保留原始换行顺序不要重新组织排版。其中最关键的是第二条和第四条。加不加这份约束实测下来在遮挡场景下的幻觉占比从 23% 降到 17%有效果但根治不了。因为即便你让它输出UNKNOWN很多开源模型的指令遵循能力本来就有限稍微复杂一点的版面它就回到“自由发挥”的老路。我更推荐的是配合约束解码来用把输出空间限定在预设的字段范围内。比如你预先知道某个字段是数字那就在后处理阶段用正则强制清洗你预先知道是姓名就建立一个常见姓氏表预测结果不在表内且与真值形态差别细微时保守拒识。约束解码是工程兜底Prompt 只是软引导两者叠加效果远大于单用任何一个。4.3 从困扰到突破一个被我忽略的细节整个过程里最典型的思维死角是我默认“评测指标是客观的模型是固定的”于是遇到异常指标只会在模型或指标之间来回怀疑。直到我放弃总指标、回到逐字段样本审计才真正找到问题所在。这个习惯我保留到了后续所有项目里现在做评测第一件事不是看分而是先挑 20 张错得最离谱的图亲眼看一遍。还有一个小细节值得留意推理时的temperature参数OCR 任务里应该设置成 0 或接近 0。这个我从一开始就设置了但不是所有人都会做这件事。有不少 VLM 在部署时保留文本生成任务的默认参数比如temperature0.7这在对话场景没问题在 OCR 场景就是灾难性随机源同一张图每次跑结果还不一样。你可以做个简单实验把 temperature 调到 0.9 跑同一张单据十次会看到不同的人名版本这就是把 OCR 任务做成了抽卡游戏。4.4 常见问题速查表问题现象根因排查思路我的默认处置总指标 CER 低但字段经常错语义级错误被长文本稀释拆分字段级评测重建字段级真值表大模型幻觉比小模型严重语言先验过强视觉证据被覆盖逐样本比对模型输出与真值用约束解码替代纯自由生成同图多次推理结果不一致temperature 过高查看推理参数配置temperature 置 0关闭随机采样盖章区域出现无中生有内容模型在补全视觉缺失区域检测盖章区域并加大遮挡测试占比区域检测 强规则后校验输出高置信度但错误模型对幻觉毫无自知力置信度阈值过滤无用用二次校验模型或规则兜底真值错也会打高模型错误率标注本身存在错别字抽检标注质量双人复核不一致样本剔除提示如果你的项目已经跑完了 OCR 评测建议回头检查测试集中是否有整张图出错率极低、但个别字段连环错的“带上毒的甜蜜样本”。这种样本会像糖衣炮弹一样把整体指标抬得非常好看却对你的业务毫无贡献。5. 工程侧的必要防线评测之外的事评测体系重建之后工程侧也必须同步补位。否则你只是“看见了问题”还没有“解决问题”。这一节专门讲我把幻觉从“指标问题”变成“已解决问题”时用到的几种工程手段。第一道防线是字段级后处理校验。这个方案不需要改动模型成本最低。对所有预测字段做约束数字字段走正则日期字段格式校验金额字段范围校验身份证、银行卡号加校验位。一旦校验失败强制转人工复核。这套方案能拦截大概 40% 的明显幻觉尤其是数字类的因为数字幻觉往往会产生非法格式或非法校验值。人名这类开放集合字段拦不住因为“张苇”本身合法。第二道防线是区域锁定。对于固定版面的票据先用目标检测模型定位出人名区域、金额区域再把整张图裁剪成若干子区域分别独立交给 VLM。这个方案的效果立竿见影因为它割裂了全局上下文的“圆谎空间”。模型不能借助整张图的语义去脑补被遮挡的字段只能基于一小块子图做转写。实测下来盖章遮挡场景下的实体级错误率从 20% 降到了 7% 左右。第三道防线是“转录 比对”的二次校验。我用一个更小的专用 OCR 模型比如 PaddleOCR在相同区域做一次独立识别拿两路结果做交叉比对。一致则通过冲突则回退给人工。VLM 擅长语义理解传统 OCR 擅长图形匹配两者恰好互补。这个方法能在成本可控的前提下把人工复核的量压到极低同时也为人工标注攒下一批高质量难例后续可以做模型微调的数据池。从成本侧看第一道防线几乎零成本第二道要准备目标检测模型第三道要引入第二个 OCR 引擎。我的建议是从第一道开始逐级叠加按实际情况评估投入产出。我在实际项目里最后保留的是第二道和第三道叠加整体推理成本上升约 30%但实体级错误率从 23.5% 压到了 2.1%。这也是我特别想在结尾再强调一次的原因把 1.72% 当作成功项目上线半年后所有脏数据都会变成定时炸弹但用上面这套方案把“看起来很好的指标”修正成“真实可信的指标”之后整个人就踏实了。技术圈现在都在谈模型能力但真正决定一个 OCR 系统能不能落地的从来都是这些“看不见的可靠工程细节”。