OCR 方案对比:本地多模态可行吗?
〇、结论先行
本地多模态不仅可行,而且识别质量已经超过传统 OCR------但不适合用来"跑量"。 实测:
- qwen3.5-9b(LM Studio 本地):CER 0.5%,质量最高,还能补残缺字;
- Apple Vision(系统自带):CER 3.2%,质量够用、速度快 15 倍、不占模型槽位;
- gemma-4-e4b(本地 VLM):CER 40.1%,中文不可用。
建议架构:Vision 做全量主力(一夜跑完 1.2 万页),qwen-VLM 做抽样质检、疑难页升级和"改字可审计"的二校;gemma 淘汰。
一、测试设计
- 样本:《剑桥中国秦汉史》3 页------p121(含《过秦论》古籍引文与脚注)、p401(曹丕代汉叙事)、p701(后汉官制专名);300 DPI 渲染。
- 基准真值:由 ark-code-latest 逐页读图人工转录,两处疑难字形(「千乘」「刺吏」)裁 400 DPI 局部图放大确认。
- 指标:字错率 CER = 编辑距离 / 基准字数;统计前统一括号异体、全角标点、空白。
- 另设一张金丝雀页:用脚本现排一页任何出版物都不可能存在的文字(随机编号 QX-90427、公元 3057 年、"鹩哥衔来半块鼍龙鼓,麈尾敲落鼻涕"、暗记 74103/龘-42),专测模型是真看图还是凭记忆背书。
二、总体结果
| 引擎 | 3 页平均 CER | 健康单页耗时 | 11,936 页推算 | 占 LM Studio 槽位 | 错误性质 |
|---|---|---|---|---|---|
| qwen3.5-9b(本地 VLM) | 0.5% | 78--97 秒(关思考) | 单槽约 12 天,双槽约 6 天且饿死 m5/automation | 是 | 标点体例统一 + 极少量语义"纠正" |
| Apple Vision | 3.2% | 12 秒 | 串行 40 小时,3--4 进程并行约一夜 | 否,走神经引擎,与一切管线并行 | 形近字替换,绝不编造 |
| gemma-4-e4b(本地 VLM) | 40.1% | 112 秒 | 单槽 15.5 天 | 是 | 多语种乱码、专名数字崩坏、自行删节 |
| ark-code-latest | 0(基准) | --- | 不外传扫描书页 | --- | --- |
分页 CER:
| 页 | Vision | qwen | gemma |
|---|---|---|---|
| p121(《过秦论》引文) | 3.0% | 0.3% | 36.7% |
| p401(曹丕代汉) | 4.5% | 0.9%(7 处全是标点异体) | 44.7% |
| p701(后汉官制) | 2.0% | 0.3% | 38.5% |
三、一个关键反转:qwen 到底看没看图?
实验中途出现过强迷惑性现象:qwen 对 p121 的输出近乎满分,但 API 返回 prompt_tokens=58------图像 token 似乎没进模型,第一反应是"它在凭训练语料默写《过秦论》"。p401 首次调用又返回 0 字,更像闭眼瞎编失败。
金丝雀页一票定音:这页文字生成于测试前 10 分钟,qwen 仍逐字复现了「QX-90427」「公元3057年」「鹩哥衔来半块鼍龙鼓,麈尾敲落鼻涕」「七四一零三」「74103」。随机编号和荒诞内容只有一条来源------像素。
结论:
- qwen3.5-9b 在 LM Studio 里确实接收并理解了图片 ;
prompt_tokens=58是该服务端的计量怪癖(图像 token 不计入该字段),不能用它判断模型是否多模态。判断"看没看图"只能用金丝雀。 - p401 的 0 字是长思考(1467 reasoning tokens)在槽位争抢下吃光预算导致,与视觉无关;加
reasoning_effort=none+max_tokens后 78 秒正常出全文。这与项目既有教训一致:批量 JSON/转录任务必须关思考、封顶 token、校验非空。
四、三个引擎的错误画像
qwen3.5-9b:0.5%,但每一处错都是"有意的"
全部差异只有 11 处,且没有一个是普通形近误识:
- 标点/体例规范化 :破折号
---→-、间隔号·→・、脚注书名号〈〉→《》------对读者更友好,但对"逐字档案"是篡改; - 语义级"纠错" :扫描页印的是「太守或刺吏 下面供职」(通行本作"刺史"),qwen 输出「刺史 」;同页「在郡里 当下级官吏」被改成「在那里」;
- 补残缺字:金丝雀页因字体缺字,正文"齑"字位置是空白方块,qwen 凭上下文正确补出「齑粉」(Vision 老老实实留白/误识为"前");同时也把「荜拨」看错成「荪拨」。
对阅读和事件抽取,这些特性几乎全是优点;对史料存档,它意味着模型会在 0.3% 的位置无声地替原文做主,而档案的立身之本是 quote 与原图逐字对应。
Apple Vision:3.2%,错得"诚实"
- 「的」系统性误识:的→韵/約/钓/哟(单页多达 5 处);
- 形近专名替换:秦→泰/素、郡→都、迫→追、千乘→干乘、梯→样;
- 脚注方括号与数字粘连(
[105]→[1053)、偶发漏「的」; - 页眉、页码、脚注、换行全保留,图上没有的字一个不添,缺字位置留白。 金丝雀页上 QX-90427、3057、七四一零三、74103 全部读出。
它是"识别器"不是"生成器":宁可认错一个字,绝不发明一句话。错误模式高频、可枚举,且全部落在 bge-m3 语义检索不敏感、下游 qwen 抽取有上下文可纠偏的范围内。
gemma-4-e4b:40%,淘汰
- 中文正文里混排泰语(ไปด้วย)、阿拉伯语、俄语(войска)、法语(urètre)、LaTeX(
+|\text{); - 曹丕→曹王(三页一致地错)、武荣约死于公元 168 年→163 年、第5章 汉代的灭亡→次代的灭亡;
- 流畅地改写史实,输出仅原图约 40% 篇幅(自行删节)。
- 金丝雀页上反而能读对 QX-90427/3057/74103------说明视觉通路本身工作,问题是中文语言模型太弱。该模型不适合中文 OCR。
五、可行性判断与落地方案
- 本地多模态在 9B 量级已经过了"能用"线 (0.5% CER,传统 OCR 的 1/6 错误率),真正的约束不是质量而是吞吐与争用:单页 80--100 秒、必须独占 LM Studio 槽位,而槽位已被 m5 二十四史抽取和 automation 复核占满;全书单槽要跑约 12 天。
- 全量 OCR 用 Apple Vision:12 秒/页、神经引擎与 LM Studio 互不干扰,3--4 进程并行约一夜跑完 1.2 万页;3.2% CER 对下游闭环无害------quote 校验是"抽取结果 ↔ 同一 OCR 文本"自洽比对,bge-m3 对单字替换基本免疫。
- qwen-VLM 用在三个高价值位置(不跑量) :
- 抽样质检(每本书抽 10--20 页双跑,监控 Vision CER 是否漂移);
- 疑难页升级(Vision 输出 CJK 字符数异常少、版面复杂的页);
- Vision 文本的纯文本二校(输出 diff,仅允许最小改动,专名/引文锁定,人工抽查);
- 调用一律关思考、
max_tokens封顶、空结果重试。
- 原始扫描页不参与上传云端;ark-code-latest 只做抽样基准。
- gemma-4-e4b 不用于中文管线。
六、实施步骤(Vision 通道 + qwen 抽检)
chronicle/import_cambridge.py:PyMuPDF 按页渲染(200 DPI)→ 多进程调已编译的vision_ocr→ 按目录/章节聚合成 page(仿 import_hist:title=书名+章节,url 用稳定伪链cambridge://剑桥中国秦汉史#p0121),新增source_type='cambridge';OCR 原文按页落本地 JSON,断点续跑。- 先跑《秦汉史》一本(1,039 页,约 1--3 小时并行),抽 10 页用 qwen-VLM 双跑核对。
- 入库走
insert_page()自带 jieba + bge-m3 双写,事件抽取复用 m5 prompt、复核复用 automation,全链路零新机制。 - 前端
SRC_KIND_NAME增加cambridge: 剑桥中国史,事件详情出处闭环自动生效。
附:测试产物在 /tmp/chronicle-src/camb_ocr/(样页图、金丝雀源图、四方原始输出、基准文本、cer.py);Vision 工具源码 chronicle/vision_ocr.swift(已编译,可直接复用)。