一个开源 OCR 模型,发布不到两个月,GitHub 星标冲到 23.5K,复刻数超过 2400,连 vLLM、SGLang 都主动出适配镜像。在多数人眼里,OCR 是早就卷到头的成熟赛道:扫描件转文字、票据识别、证件提取,传统方案跑了十几年,手机系统甚至自带。可百度 2026 年 6 月开源的 Unlimited-OCR,硬是在这条赛道上又撕开了一个口子,官方口号叫 one-shot long-horizon parsing,一次推理啃完一整份长文档。这篇文章从它解决什么问题讲起,把本地推理、PDF 批处理、服务化部署和踩坑点一次说清。
为什么 OCR 还要再做一个模型
传统 OCR 工具链处理一份几十页的扫描 PDF,走的是分页识别加后处理拼接的路子:每页先做版面分析,找出文字、表格、图片区域,再逐块识别,最后按坐标重排。这套流程在单页票据、身份证上表现稳定,一旦遇到整本论文、技术手册、财报年报,问题就暴露出来。一是分页之间没有上下文,前一页的表格栏位和下一页的内容对不上;二是双栏排版、跨页表格、公式混排这类复杂版式,规则引擎很难还原原始阅读顺序;三是输出只有孤立的文本块,丢掉了文档结构,后续做知识库入库还要自己写一堆清洗逻辑。
Unlimited-OCR 换了一条思路:把整页甚至整份文档作为输入交给大模型,让模型自己理解版面、表格、公式和阅读顺序,直接输出接近原始排版的 markdown 结构。这个方向的代表作是 DeepSeek-OCR,而 Unlimited-OCR 的目标就是把它再推进一步:上下文窗口做到 32768,支持多页图片连续解析,还内置了 ngram 去重机制压制长文档里最容易出现的重复输出。
本地跑起来:transformers 是最快路径
先不用管服务化,本地验证效果最直接的方式是 transformers 加载模型。官方 README 给了完整的依赖清单,核心就几个:torch、transformers、Pillow、pymupdf 负责 PDF 转图,einops 和 addict 是模型内部依赖。环境建议 Python 3.12 加 CUDA 12.9,显存至少 16G,因为默认用 bfloat16 加载。
python
import torch
from transformers import AutoModel, AutoTokenizer
model_name = 'baidu/Unlimited-OCR'
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModel.from_pretrained(
model_name,
trust_remote_code=True,
use_safetensors=True,
torch_dtype=torch.bfloat16,
)
model = model.eval().cuda()
model.infer(
tokenizer,
prompt='<image>document parsing.',
image_file='your_image.jpg',
output_path='your/output/dir',
base_size=1024, image_size=640, crop_mode=True,
max_length=32768,
no_repeat_ngram_size=35, ngram_window=128,
save_results=True,
)
这里有个细节值得注意:infer 内部会自动处理图片缩放和裁剪,返回的结果会保存到 output_path 目录,包含识别出的 markdown 文本。第一次运行会从 Hugging Face 下载模型权重,几个 G 的体量,建议提前配好镜像加速。
两种图像配置:gundam 还是 base
单张图片推理时,官方提供了两套图像配置,参数不同,适用场景也不同。
| 配置 | 适用场景 | 图片尺寸 | 裁剪模式 | ngram 窗口 |
|---|---|---|---|---|
| gundam | 单页文档、清晰扫描件 | 640 | 开启 | 128 |
| base | 多页 PDF、整份文档 | 1024 | 关闭 | 1024 |
选择逻辑很简单:只处理一张图,用 gundam,速度快、省显存;要处理多页或者整本 PDF,必须切到 base,否则长文本容易丢上下文。官方明确说明多页解析只支持 base 配置,这是硬约束,不是建议。
多页 PDF:先转图,再一次性解析
处理 PDF 时,模型不直接读 PDF 文件,需要先用 PyMuPDF 把每一页渲染成图片,再交给 infer_multi 做多页连续解析。官方示例里把这一步封装成了函数,300 dpi 是推荐的渲染精度,太低会丢细节,太高只是白白增加内存。
python
import os, tempfile, fitz
def pdf_to_images(pdf_path, dpi=300):
doc = fitz.open(pdf_path)
tmp_dir = tempfile.mkdtemp(prefix='pdf_ocr_')
mat = fitz.Matrix(dpi / 72, dpi / 72)
paths = []
for i, page in enumerate(doc):
out = os.path.join(tmp_dir, f'page_{i+1:04d}.png')
page.get_pixmap(matrix=mat).save(out)
paths.append(out)
doc.close()
return paths
model.infer_multi(
tokenizer,
prompt='<image>Multi page parsing.',
image_files=pdf_to_images('your_doc.pdf', dpi=300),
output_path='your/output/dir',
image_size=1024,
max_length=32768,
no_repeat_ngram_size=35, ngram_window=1024,
save_results=True,
)
infer_multi 和 infer 的差别在于输入从单张图变成图片列表,模型会把多页内容当作一个连续序列解析,页码之间的表格、脚注、章节标题都能保持连贯。这是传统分页 OCR 做不到的。
服务化部署:vLLM 与 SGLang
本地验证通过之后,要接入业务就要起服务。官方同时给了 vLLM 和 SGLang 两条路线,都是 OpenAI 兼容接口,切换成本很低。
| 部署方式 | 镜像/安装 | 接口 | 适合场景 |
|---|---|---|---|
| vLLM | docker pull vllm/vllm-openai:unlimited-ocr | OpenAI 兼容 | 生产环境、GPU 集群 |
| SGLang | 本地 wheel + uv 安装 | OpenAI 兼容、流式 | 高吞吐批处理 |
vLLM 路线最简单,拉官方镜像就能起服务,Hopper 显卡用 cu129 版本,其余用默认版本。SGLang 路线更灵活,支持流式输出,适合做批量任务,但需要先安装本地 wheel 再固定 kernels 版本,环境要求更高。两条路线都要求显存能装下模型,16G 起步比较稳。
批量处理:一行命令跑整个目录
仓库里自带的 infer.py 把 SGLang 服务启动和并发请求封装到了一起,处理图片目录或者整本 PDF 都只要一条命令。
shell
# 处理整个图片目录,8 并发
python infer.py \
--image_dir ./examples/images \
--output_dir ./outputs \
--concurrency 8 \
--image_mode gundam
# 处理整本 PDF
python infer.py \
--pdf ./examples/document.pdf \
--output_dir ./outputs \
--concurrency 8 \
--image_mode gundam
concurrency 参数控制并发请求数,显存够的情况下调高能明显提速。注意 infer.py 默认假设模型在 Hugging Face 上可以访问,离线环境要先用 --model_dir 指定本地模型路径。
评估长文档解析:OmniDocBench 的坑
官方用 OmniDocBench 做基准评估,但直接拿原始输出跑分数会偏低,因为输出里带着坐标标记。模型解析时会输出类似 det 标记的结构化标签,包含每个文本块的类别和坐标,评估前必须用官方提供的 remove_det 函数把这些标记剥掉,把属于同一块的行合并、不同块之间用空行分隔,才能得到干净的文本去比分数。
这个后处理细节很容易被忽略,社区里不少复现分数对不上,一半原因就是少了这步清洗。
踩坑清单与使用建议
把实践中的几个关键点汇总一下。
| 坑 | 现象 | 解法 |
|---|---|---|
| 依赖版本不匹配 | 导入报错或推理崩溃 | 严格按 README 的版本装:torch 2.10、transformers 4.57 |
| 显存不足 | 加载即 OOM | 换 gundam 配置、降低并发,或改 bf16 加载 |
| PDF 转图太糊 | 识别率明显下降 | dpi 提到 300,关闭压缩渲染 |
| 输出重复文本 | 长文档出现整段重复 | 调大 no_repeat_ngram_size 或 ngram_window |
| 评估分数偏低 | 与官方数字对不上 | 先跑 remove_det 后处理再比对 |
| 离线部署失败 | 拉不到模型权重 | 先手动下载,--model_dir 指向本地目录 |
整体判断:如果你只需要偶尔识别几张票据,传统 OCR 和在线 API 足够;如果业务里是整本论文、合同、技术手册这类长文档,并且需要保留表格结构和阅读顺序,Unlimited-OCR 是目前开源方案里最值得试的那一个。它把长文档解析从流水线工程变成了一个模型调用,接入成本远低于自己拼一套版面分析加表格重建的链路。
最后提醒一句:模型还在快速迭代,官方仓库的 Release 更新很频繁,vLLM、SGLang 的适配也在跟进,上手前先看一遍 README 的 Release 列表,别拿旧教程的依赖版本直接套。