☰
Unlimited-OCR 实战指南:One-shot Long-horizon 单次长文档解析与 Transformers / vLLM / SGLang 三条推理路径
2026/9/30 10:52:44 网站建设 项目流程
  • 人工智能
  • 大模型
  • 计算机视觉
  • 模型推理服务

【免费下载链接】Unlimited-OCR

Unlimited OCR Works: Welcome the Era of One-shot Long-horizon Parsing.

项目地址:https://gitcode.com/gh_mirrors/un/Unlimited-OCR
点击查看免费下载

本文以百度开源的 Unlimited-OCR(仓库 README.md)为主线,围绕其"一次推理完成长篇幅文档解析(One-shot Long-horizon Parsing)"的核心能力,系统讲解环境搭建、Transformers 直接推理、vLLM Docker 部署与 SGLang 服务化推理的完整实操,并结合仓库 infer.py 源码解读批量并发推理的实现细节。读完本文,你将掌握单图、多页、PDF 三类输入的解析方法,理解 gundam / base 两种图像配置的选型依据,并能把模型快速接入自有服务或批处理流水线。

一、项目定位:把 OCR 从"单页识别"推进到"一次吃下整份文档"

Unlimited-OCR 的核心主张在 README 的标语中写得很明确:Welcome the Era of One-shot Long-horizon Parsing——"欢迎进入一次性长时程(long-horizon)解析时代"。它定位为把 DeepSeek-OCR 再向前推进一步的方案:不再局限于单张图片的版面识别,而是面向整份文档(多页扫描件、长 PDF)在单次推理内完成端到端解析。从架构示意图可以看到,模型在骨干网络与 KV 缓存机制上做了面向长文档的专门设计。

从 README 的 Release 小节可以还原项目的时间线:

  • 2026/06/22 项目正式发布,目标是把 DeepSeek-OCR 往前推进一步;
  • 2026/06/23 论文《Unlimited OCR Works》在 arXiv 公开(编号 2606.23050),模型同期上线 ModelScope;
  • 2026/06/24 社区在 Hugging Face Spaces 提供了在线 Demo;
  • 2026/06/28 在 vLLM 社区支持下,模型可支持 vLLM 推理;
  • 2026/07/03 模型上线百度智能云 OCR 服务。

二、环境准备与依赖清单

2.1 硬件与基础软件

README 明确说明 Transformers 推理运行于 NVIDIA GPU,官方测试环境为 python 3.12.3 + CUDA12.9,依赖清单如下(建议按此版本对齐):

torch==2.10.0 torchvision==0.25.0 transformers==4.57.1 Pillow==12.1.1 matplotlib==3.10.8 einops==0.8.2 addict==2.4.0 easydict==1.13 pymupdf==1.27.2.2 psutil==7.2.2

其中pymupdf(fitz)用于 PDF 转图,是 PDF 解析链路的前置依赖。

2.2 SGLang 路径的独立环境

如果走 SGLang 服务化路线,README 建议用 uv 管理独立虚拟环境,先安装仓库自带的本地 wheel(wheel/sglang-0.0.0.dev11416+g92e8bb79e-py3-none-any.whl),再固定 kernels 版本并安装 PyMuPDF:

uv venv --python 3.12 source .venv/bin/activate uv pip install wheel/sglang-0.0.0.dev11416+g92e8bb79e-py3-none-any.whl uv pip install kernels==0.11.7 uv pip install pymupdf==1.27.2.2

一点提示:README 正文提到固定kernels==0.9.0,而实际给出的安装命令是kernels==0.11.7,以命令行为准即可。

三、路径一:基于 Transformers 的即插即用推理

Transformers 路径适合单机快速验证与脚本化调用,模型通过trust_remote_code=True加载远程代码。

3.1 加载模型

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()

3.2 单图推理:gundam 与 base 两种配置

README 明确给出:单张图片支持两种配置。

配置base_sizeimage_sizecrop_mode适用场景
gundam1024640True单图,聚焦裁剪
base10241024False单图 / 多页 / PDF
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, )

从参数名与取值可以推断:base_size控制基础分辨率;image_size为送入模型的输入尺寸,gundam 配置下 640 配合crop_mode=True,即先裁剪出文档主体区域再解析,在较低分辨率下获得更聚焦的版面细节;max_length=32768是单次推理的最大生成长度,也是 "long-horizon" 覆盖长文档的容量保证;no_repeat_ngram_size=35与ngram_window=128用于抑制长文本生成中的重复(详见第五节 SGLang 的 logit 处理器);save_results=True表示把解析结果写入output_path目录。

3.3 多页图片 / PDF:infer_multi

多页输入只使用 base 配置(image_size=1024),并换用infer_multi接口:

model.infer_multi( tokenizer, prompt='<image>Multi page parsing.', image_files=['page1.png', 'page2.png', 'page3.png'], output_path='your/output/dir', image_size=1024, max_length=32768, no_repeat_ngram_size=35, ngram_window=1024, save_results=True, )

PDF 需要先转成页面图片再走多页解析。README 给出了基于 PyMuPDF 的转换函数(dpi=300):

import tempfile, fitz # PyMuPDF 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, )

注意:多页/PDF 场景下ngram_window从单图的 128 提高到 1024,no_repeat_ngram_size保持 35。

四、路径二:vLLM 推理部署

vLLM 路径面向服务化部署。README 说明官方提供了对应的部署配方(Recipe),可在 vLLM 官方 Recipe 站点检索baidu/Unlimited-OCR获取完整部署步骤。仓库内同时给出了 Docker 镜像,按 GPU 平台二选一:

默认镜像(CUDA 13.0):

docker pull vllm/vllm-openai:unlimited-ocr

Hopper GPU(CUDA 12.9):

docker pull vllm/vllm-openai:unlimited-ocr-cu129

即:常规平台使用默认镜像,Hopper(H 系列)GPU 使用带-cu129后缀的镜像。

五、路径三:SGLang 推理(OpenAI 兼容 API)

SGLang 路径适合需要并发与流式输出的生产场景,且与仓库提供的 infer.py 批处理脚本深度绑定。

5.1 启动服务端

按 2.2 节完成环境搭建后,用以下命令启动服务(README 原命令):

python -m sglang.launch_server \ --model baidu/Unlimited-OCR \ --served-model-name Unlimited-OCR \ --attention-backend fa3 \ --page-size 1 \ --mem-fraction-static 0.8 \ --context-length 32768 \ --enable-custom-logit-processor \ --disable-overlap-schedule \ --skip-server-warmup \ --host 0.0.0.0 \ --port 10000

关键参数含义(与 infer.py 中的同参常量可相互印证):

参数取值说明
--modelbaidu/Unlimited-OCRHF 模型 ID 或本地模型目录
--served-model-nameUnlimited-OCR对外服务名,客户端请求体中的 model 字段需一致
--attention-backendfa3FlashAttention 3 后端(infer.py 中ATTENTION_BACKEND同为 fa3)
--page-size1分页大小(infer.py 中PAGE_SIZE=1)
--mem-fraction-static0.8显存静态占用比例(infer.py 中MEM_FRACTION_STATIC=0.8)
--context-length32768上下文长度,与 Transformers 路径的max_length=32768对齐
--enable-custom-logit-processor-启用自定义 logit 处理器,支撑 no-repeat n-gram 去重
--host/--port0.0.0.0 / 10000监听地址与端口(infer.py 中HOST/PORT一致)

5.2 OpenAI 兼容流式客户端

README 提供了完整的流式客户端示例:把图片以 base64 的data:image/...;base64,形式组装进多模态消息,通过POST /v1/chat/completions发起请求,并启用自定义 logit 处理器抑制重复 n-gram:

import base64 import json import os import tempfile import fitz import requests from sglang.srt.sampling.custom_logit_processor import DeepseekOCRNoRepeatNGramLogitProcessor server_url = "http://127.0.0.1:10000" session = requests.Session() session.trust_env = False 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) image_paths = [] for i, page in enumerate(doc): image_path = os.path.join(tmp_dir, f"page_{i + 1:04d}.png") page.get_pixmap(matrix=mat).save(image_path) image_paths.append(image_path) doc.close() return image_paths def encode_image(image_path): ext = os.path.splitext(image_path)[1].lower() mime = "image/jpeg" if ext in (".jpg", ".jpeg") else f"image/{ext.lstrip('.')}" with open(image_path, "rb") as f: data = base64.b64encode(f.read()).decode("utf-8") return {"type": "image_url", "image_url": {"url": f"data:{mime};base64,{data}"}} def build_content(prompt, image_paths): return [{"type": "text", "text": prompt}] + [encode_image(path) for path in image_paths] def generate(prompt, image_paths, image_mode, ngram_window): payload = { "model": "Unlimited-OCR", "messages": [{"role": "user", "content": build_content(prompt, image_paths)}], "temperature": 0, "skip_special_tokens": False, "images_config": {"image_mode": image_mode}, "custom_logit_processor": DeepseekOCRNoRepeatNGramLogitProcessor.to_str(), "custom_params": { "ngram_size": 35, "window_size": ngram_window, }, "stream": True, } response = session.post( f"{server_url}/v1/chat/completions", headers={"Content-Type": "application/json"}, data=json.dumps(payload), timeout=1200, stream=True, ) response.raise_for_status() chunks = [] for line in response.iter_lines(chunk_size=1, decode_unicode=True): if not line or not line.startswith("data: "): continue data = line[len("data: "):] if data == "[DONE]": break event = json.loads(data) delta = event["choices"][0].get("delta", {}).get("content", "") if delta: print(delta, end="", flush=True) chunks.append(delta) print() return "".join(chunks) # 单图:gundam 或 base 均可,示例使用 gundam generate("document parsing.", ["your_image.jpg"], image_mode="gundam", ngram_window=128) # 多图(仅 base) generate("Multi page parsing.", ["page1.png", "page2.png"], image_mode="base", ngram_window=1024) # PDF(仅 base) generate("Multi page parsing.", pdf_to_images("your_doc.pdf", dpi=300), image_mode="base", ngram_window=1024)

请求体中的images_config.image_mode对应 gundam / base 两种图像配置,语义与 Transformers 路径完全一致:单图可用 gundam(640 + crop)或 base(1024),多图/PDF 仅支持 base。custom_logit_processor使用 SGLang 提供的DeepseekOCRNoRepeatNGramLogitProcessor,其custom_params中的ngram_size=35、window_size(单图 128 / 多页 1024)与 Transformers 路径的no_repeat_ngram_size/ngram_window一一对应,用于长文本生成中抑制 n-gram 重复,保证长文档解析输出的可读性。

六、批处理推理:infer.py 源码级解读

infer.py 是仓库自带的 SGLang 并发批处理入口,会自动拉起(或复用)SGLang 服务端,再对图片目录或 PDF 逐页并发推理。以下结合源码逐层展开。

6.1 两种输入模式与输出命名

  • --image_dir:递归遍历目录,支持.png / .jpg / .jpeg / .webp / .bmp(见collect_dataset_images,infer.py),并按文件大小降序排列(大图优先,利于负载均衡)。
  • --pdf:每页按 dpi=300 转成 PNG(见pdf_to_images,infer.py),再逐页作为独立请求。

输出命名规则(见build_jobs,infer.py):

  • PDF 模式:{pdf文件名}_page_{序号:04d}.md,如document_page_0001.md;
  • 图片目录模式:保留相对路径,目录层级以__连接,如chapter1__fig2.md。
# 图片目录 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

6.2 服务端自动拉起与复用

start_server(infer.py)的调度逻辑很实用:

  1. 先探测http://127.0.0.1:10000/health,若已有可用服务则直接复用,并打印 "Reuse existing SGLang server";
  2. 否则以subprocess.Popen拉起python -m sglang.launch_server,并把--gpu参数写入环境变量CUDA_VISIBLE_DEVICES;
  3. 启动后每 3 秒轮询健康接口,最长等待 300 秒(SERVER_TIMEOUT),超时或进程提前退出都会在--server_log指向的日志中留下排查线索。

服务端参数在源码中以常量固定:--attention-backend fa3、--page-size 1、--mem-fraction-static 0.8、--context-length 32768,与 README 手动启动命令保持一致。

6.3 并发调度与失败重试

run(infer.py)用ThreadPoolExecutor(max_workers=args.concurrency)并发提交任务,默认并发 8。单请求固定temperature=0、stream=True,提示词固定为document parsing.(PROMPT常量)。请求失败时最多重试 5 次(MAX_RETRIES=5),其中 502 等瞬时错误按3×(attempt+1)秒退避重试(见infer_one,infer.py);单请求超时上限 1200 秒(REQUEST_TIMEOUT)。

6.4 结果汇总与性能统计

每完成一个请求会打印该图的 token 数与解码耗时;全部完成后输出汇总:

Concurrent Results: Requests: 8/8 Total tokens: 12345 Wall time: 12.34s System TPS: 1000.41 tokens/s Avg tokens/request: 1543 Avg decode_time/request: 1.23s

从源码看,System TPS定义为"总 token 数 / 墙钟耗时",是评估并发吞吐的直观指标。

6.5 完整 CLI 参数

对应parse_args(infer.py):

参数默认值说明
--image_dir""图片目录(数据集并发模式)
--pdf""PDF 文件,逐页转图后并发
--output_dir./outputs结果输出目录,每图/每页一个 .md
--concurrency8并发请求数
--gpu0CUDA_VISIBLE_DEVICES取值
--model_dirbaidu/Unlimited-OCR本地路径或 Hugging Face 模型 ID
--image_modegundamgundam / base 二选一
--server_log./log/sglang_server.logSGLang 服务端日志文件

七、可视化演示

README 的 Visualization 小节展示了模型的交互式解析演示(输入文档 → 解析输出 → 版面可视化)。你可以跑通上述任意一条推理路径后,把输出目录中的 .md 结果与演示效果对照验证。

八、实战要点速查

  1. 图像配置选型:单图选 gundam(640 + crop_mode)可获得更聚焦的版面细节;多页/PDF 必须用 base(1024),README 明确 "Multi page / PDF only uses base"。
  2. 长文档容量:三条路径的上下文/最大生成长度均为 32768;SGLang 需--context-length 32768,与 Transformers 的max_length=32768保持一致。
  3. 去重参数:no_repeat_ngram_size=35固定;ngram_window单图 128、多页/PDF 1024(注意 infer.py 当前把NGRAM_WINDOW固定为 128,手动客户端则按场景分别传 128/1024)。
  4. 推理确定性:SGLang 客户端与 infer.py 均使用temperature=0,便于复现与质检。
  5. PDF 前置转换:统一走 PyMuPDF(dpi=300)转 PNG 后再多页解析,pymupdf是必需的依赖。

引用与致谢

仓库 README 提供了论文《Unlimited OCR Works》的 BibTeX 引用信息(arXiv 编号 2606.23050,cs.CV 方向)。项目在致谢中说明其构建于 DeepSeek-OCR、DeepSeek-OCR-2 与 PaddleOCR 的模型与思路之上,Unlimited-OCR 正是在这些工作的基础上向"一次性长时程解析"推进的开源实现。

  • 人工智能
  • 大模型
  • 计算机视觉
  • 模型推理服务

【免费下载链接】Unlimited-OCR

Unlimited OCR Works: Welcome the Era of One-shot Long-horizon Parsing.

项目地址:https://gitcode.com/gh_mirrors/un/Unlimited-OCR
点击查看免费下载

相关推荐

上一篇:DeepSeek Coder 1.3B Base 企业级部署方案:安全、高效的团队协作配置
下一篇:tokio-modbus 项目推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询