Surya OCR 的三合一之谜:一个 650M VLM 怎么同时干布局、识别和表格三件事
【免费下载链接】suryaOCR, layout analysis, reading order, table recognition in 90+ languages项目地址: https://gitcode.com/GitHub_Trending/su/surya
上一批生产扫描件是份金融季报:公式段落输出一串无意义的 math 标签,两栏表格被撕成四块,右下角那栏的行序完全错乱。用传统的"检测→识别→拼装"流水线遇到这种 bug 会很绝望——三个阶段层层叠加误差,你甚至定位不到是哪个模型的问题。这正是 Surya OCR 选择用一个 VLM(视觉语言模型,约 650M 参数)同时做 layout、文本识别和表格识别的核心原因。
一个 prompt 契约:VLM 怎么知道自己该输出什么
Surya 的 layout、OCR、table_rec 共用同一个模型,区别只在 prompt。prompts.py 开头就注明"prompt 措辞是模型训练时的契约,未经重新训练不要改写":layout 任务让模型输出带 label/bbox/count 的 JSON,坐标归一化到 0-1000;区块识别任务则只有一句"OCR this block image to HTML"。
换句话说,同一个模型看 prompt 决定吐 layout JSON 还是整页 HTML,公式也不是独立任务,而是嵌在页面 HTML 里用<math>标签包裹。好处是整页上下文天然完整——模型见过全页,天然理解双栏关系和表格行归属,省掉了流水线里"这块属于哪一栏"的仲裁环节。
推理后端自管理:vllm 还是 llama.cpp
模型不在你的 Python 进程里跑,SuryaInferenceManager 负责托管一个 OpenAI 兼容的推理服务器:构造时自动探测本机硬件——除了查 torch 的 CUDA 状态,还会直接检查 /dev/nvidia0 设备节点,避免驱动版本落后导致 torch 误报"无 GPU"。有 NVIDIA 卡就拉起 vllm,没有就下载 GGUF 权重跑 llama.cpp,CPU 和 Apple Silicon 都能工作。
服务器默认首次使用时启动、进程退出时关闭。如果你连着跑surya_ocr和surya_layout,每次都要付一遍模型加载的代价,加--keep_server可以让服务器常驻,后面的命令直接 attach。已有自己的 vllm 部署时,设SURYA_INFERENCE_URL跳过 spawn 直接接入。
模型输出后的防御层:解析、置信度与退化检测
VLM 的输出永远不是 100% 可靠的,Surya 的后处理很防御。parsers.py 负责剥掉模型偶尔输出的代码围栏、强转 bbox 类型、把 0-1000 归一化坐标反归一化回真实像素。layout 解码还默认开着 JSON schema 约束解码(guided decoding,SURYA_GUIDED_LAYOUT为 true),让模型从一开始就无法输出畸形 JSON。
置信度不是拍脑袋的分数,而是解码时逐 token 概率的均值(走 logprobs 接口拿到),整块概率塌掉的块可以直接标记复核。util.py 里还有一个重复循环检测器,专治"模型开始复读同一串字符"的退化现象,触发了就走回退路径。块级 OCR 失败时该块标记 error,不会拖垮整页。
识别错了先调什么:DPI 与阈值的博弈
如果遇到"吞吐不够、图像质量又正常"→ 优先把IMAGE_DPI_HIGHRES从 192 降到 96,因为输出 token 量随图像面积近似线性增长,这一项的收益远超调并发。
如果遇到"低质量扫描件小字认错"→ 反过来提 DPI,或先做二值化、去倾斜等预处理,比碰模型参数可靠得多。
如果错在检测阶段(文本行黏在一起、空白被判成文字)→ 调DETECTOR_TEXT_THRESHOLD和DETECTOR_BLANK_THRESHOLD。README 给了很实用的判据:看检测器热图调试输出,出现淡淡的"幽灵框"就调低阈值,框被黏连就调高阈值。
| 症状 | 先动的设置 | 原因 |
|---|---|---|
| 吞吐不足、图像正常 | IMAGE_DPI_HIGHRES192→96 | token 量随面积线性涨 |
| 小字/模糊误识别 | 提 DPI 或图像预处理 | 输入信息不足 |
| 检测行黏连/漏行 | 两个 DETECTOR 阈值 | 热图置信度卡点 |
十分钟跑通:一条命令确认链路正常
pip install surya-ocr surya_ocr ./page.pngfrom PIL import Image from surya.inference import SuryaInferenceManager from surya.recognition import RecognitionPredictor rec = RecognitionPredictor(SuryaInferenceManager()) print(rec([Image.open("page.png")])[0].blocks[0].html)⚠️ 首次运行要下载模型权重并拉起服务器,卡几分钟是正常的。判定标准:结果里每页一份 blocks,每块带 label、html、polygon、confidence,且 error 为 false。若整块 error=true,先查后端——vllm 的 docker 是否起来、llama.cpp 权重是否下全;若 html 为空但 skipped=true,说明该块是图片类标签被有意跳过,不算故障。
需要说清楚边界:Surya 专为文档 OCR 设计,README 明确不追求自然场景照片的效果,别把它当通用 OCR 用。仓库里也没有训练脚本——模型微调是官方托管服务,"用自己的数据微调"不是在本仓库跑一条命令能完成的事,选型时要把这点算进去。延伸方向有两个:想交互式调试效果,装 streamlit 后跑surya_gui;想把表格落成 markdown 或 HTML 给下游系统用,官方 marker 仓库的 TableConverter 是下一步。
【免费下载链接】suryaOCR, layout analysis, reading order, table recognition in 90+ languages项目地址: https://gitcode.com/GitHub_Trending/su/surya
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考