ModelBaidu (ERNIE)Baidu (ERNIE)published Oct 26, 2026seen 2d

baidu/Unlimited-OCR-2

Open original ↗

Captured source

source ↗
published Oct 26, 2026seen 2dcaptured 2dhttp 200method plaintask image-text-to-textlicense mitlibrary transformersparams 3.3Bdownloads 8.4klikes 350

Unlimited OCR Works 2

Pioneering the OCR-3.0 Era: From Long- to Unlimited-Horizon Parsing.

Release

What's New in 2

  • Sparse Reference Sliding Window Attention (SR-SWA): Compresses text-visual attention interactions from ultra-long sequences down to a fixed-size set of working references (e.g., 512 tokens). This bounds computational complexity, keeping per-step decoding latency and memory footprint nearly constant regardless of document length.
  • From Long- to Unlimited-Horizon Parsing: Powered by SR-SWA, Unlimited-OCR 2 achieves true, unbounded document parsing within a finite context window—effectively eliminating quadratic attention scaling and memory bottlenecks as documents grow.
  • Ultra-Long Document Parsing SOTA: Achieves state-of-the-art performance across both dozens-of-pages benchmarks (e.g., MPDocBench) and extreme hundred-page benchmarks (e.g., MarathonDocBench).

Inference

> Unlimited OCR 2 supports Transformers (native, for quick tests) and SGLang (recommended for serving).

Transformers

Tested with Python 3.10, torch==2.13.0, transformers==4.52.1, huggingface-hub==0.36.0, tokenizers==0.21.4, on an NVIDIA A800-80GB:

pip install torch==2.13.0 torchvision pillow transformers==4.52.1 huggingface-hub==0.36.0 \
tokenizers==0.21.4 addict==2.4.0 einops==0.8.2 easydict==1.13

Load the model once:

import torch
from transformers import AutoModel, AutoTokenizer

model_name = "baidu/Unlimited-OCR-2"
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).eval().cuda()

Multi-page inference (infer_multi). All pages of a document are parsed in one request, so the model can use cross-page context. Pass the page images in page order:

pages = [f"your_doc/page_{i}.jpg" for i in range(1, 8)]

text, num_tokens = model.infer_multi(tokenizer, image_files=pages,
output_path="output/multi", save_results=True)

The prompt is built automatically as "" * num_pages + "Multi page parsing.", and each page is encoded as one global view (base mode). The output contains one ` per input page, in the same order as image_files`.

Single-page inference (infer). A single page is encoded with dynamic tiling (gundam mode) by default:

text = model.infer(tokenizer, image_file="your_doc/page_1.jpg",
output_path="output/single", save_results=True)

SGLang

Set up the environment (uv-managed virtualenv, Python 3.10) and install the customized SGLang wheel sglang-0.5.20.dev10+g8d62f5fb9c-py3-none-any.whl, plus PyMuPDF for PDF-to-image conversion:

uv venv --python 3.10
source .venv/bin/activate

uv pip install wheel/sglang-0.5.20.dev10+g8d62f5fb9c-py3-none-any.whl
uv pip install pymupdf==1.27.2.2
uv pip install fastokens==0.3.2

Start the SGLang server:

# Page-window scheduling for very long documents (the KV tail of the previous window is reused).
export SGLANG_UOCR_EANABLE_DOC_LIMIT=1 # 1 = enable, 0 = whole document in one window
export SGLANG_UOCR_DOC_GUNDAM_PAGE_WINDOW=10 # pages per window (gundam mode)
export SGLANG_UOCR_DOC_BASE_PAGE_WINDOW=10 # pages per window (base mode)
export SGLANG_UOCR_MAX_PAGE_TOKENS=20000 # per-page output cap; must be > 0
# Chunk cap for the SAM attention bias. The built-in default (512) allows a single
# ~462 MiB allocation and can OOM; 64 is equivalent with a lower peak.
export SGLANG_OPT_UOCR_SAM_REL_POS_BIAS_MB=64
# Pass large crop tensors through /dev/shm instead of pickling them into the
# tokenizer -> scheduler frame.
export SGLANG_UOCR_SHM_WRAP_MODEL_DATA=1
export SGLANG_UOCR_SHM_WRAP_MIN_MB=1
export SGLANG_VLM_CACHE_SIZE_MB=0
# Reduces fragmentation failures on the large contiguous allocations.
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True

python -m sglang.launch_server \
--model-path baidu/Unlimited-OCR-2 \
--served-model-name Unlimited-OCR-2 \
--chat-template unlimited-ocr \
--tokenizer-backend fastokens \
--tokenizer-worker-num 8 \
--mem-fraction-static 0.7 \
--max-running-requests 64 \
--max-prefill-tokens 2048 \
--enable-custom-logit-processor \
--enable-streaming-session \
--allow-auto-truncate \
--disable-radix-cache \
--mm-preprocess-cache-size-mb 0 \
--cuda-graph-backend-decode full --cuda-graph-max-bs-decode 64 \
--cuda-graph-backend-prefill full \
--cuda-graph-max-bs-prefill 16384 \
--cuda-graph-bs-prefill 1024 2048 \
--cuda-graph-config '{"prefill": {"backend": "full", "full_prefill_max_req": 64}}' \
--host 0.0.0.0 \
--port 10000

Notes on the knobs that matter for memory:

  • --mem-fraction-static is the share reserved for weights + KV. 0.75 leaves too little headroom at 32+ concurrent requests; 0.7 is stable.
  • --max-prefill-tokens drives a temporary tensor of roughly [tokens, context/32], so memory grows linearly with it: 4096 -> 2048 halves that peak.
  • The largest --cuda-graph-bs-prefill bucket must equal --max-prefill-tokens. A smaller one makes oversized batches fall back to eager; a larger one is never...

Excerpt shown — open the source for the full document.

Notability

notability 7.0/10

Baidu released a new OCR model version.