百度 Unlimited OCR 深度实战:30亿参数仅激活5亿、R-SWA注意力革命——长文档OCR端到端SOTA完全指南(2026)
作者按:2026年6月22日,百度开源发布了 Unlimited OCR 模型,总参数量30亿,推理时仅激活5亿参数,在 OmniDocBench v1.6 基准测试中拿下93.92%的综合成绩,直接刷新端到端 OCR 的 SOTA。更重要的是,它通过对 Reference Sliding Window Attention(R-SWA)的巧妙设计,把解码器的 KV cache 从线性增长压成了常数——这意味着"一次前向推理转录几十页文档"不再是梦话,而是可以 run 起来的真实代码。
目录
- 背景介绍:OCR 的长文档困境与端到端革命
- 核心概念:Unlimited OCR 架构设计与 R-SWA 机制深度解析
- 架构分析:从 DeepEncoder 到 MoE 解码器的完整技术栈
- 代码实战:环境搭建、SGLang 部署与 Python API 全栈实战
- 性能优化:KV Cache 压缩、推理加速与生产级调优
- 总结与展望:长文档 OCR 的时代来了
1. 背景介绍:OCR 的长文档困境与端到端革命
1.1 传统 OCR 的两阶段之痛
OCR(Optical Character Recognition,光学字符识别)技术发展到今天,已经走过了半个多世纪的路程。从最早的模式匹配、到机器学习分类器、再到深度学习时代的神经网络,OCR 的精度和提升一直未曾停止。
然而,传统 OCR 系统始终面临一个根本性的架构问题:两阶段流水线。
典型的传统 OCR 流程是这样的:
输入图像
↓
[阶段一:文本检测] → 检测出图像中的文字区域(文本框、文本行、单词边界)
↓
[阶段二:文本识别] → 对每个检测到的文本区域分别进行字符识别
↓
输出文本序列
这个架构在理论上没什么问题,甚至在很多场景下表现还不错。但当你把它放到长文档解析的场景里,问题就一个个冒出来了:
问题一:误差累积。 检测阶段的误差会直接传递到识别阶段。如果检测框偏了、漏了、或者把两个单词框在了一起,识别结果必然受影响。两个阶段的误差是"串联"的,整体精度上限被锁死在 精度_检测 × 精度_识别。
问题二:计算冗余。 检测和识别是两个独立的模型,各自需要单独的前向推理。对于几十页甚至上百页的长文档,这个"各自为政"的计算方式意味着大量的重复计算和显存浪费。
问题三:上下文割裂。 传统 OCR 是"逐框识别"的,每个文本区域是独立处理的,模型看不到全局上下文。遇到模糊、遮挡、字体特殊的情况,单靠一个小小的文本区域,识别率自然上不去。
问题四:长文档的"记忆力"瓶颈。 这是最核心的问题。当文档从一页变成十页、百页,模型需要"记住"前面页面的内容来辅助后续识别(比如一致的术语、格式、排版风格)。但传统的两阶段架构根本没有设计"跨页记忆"的能力,只能 for-loop 一页一页地跑,前面的内容识别完就扔掉了,后面的识别完全从头开始。
1.2 端到端 OCR:把检测和识别融进一个模型
端到端 OCR(End-to-End OCR)的核心思路非常直接:抛弃两阶段流水线,用一个统一的神经网络架构,直接从输入图像映射到输出文本序列。
这听起来像是把两个模型"拼"在一起,但实际上端到端 OCR 做的是更激进的事——它重新设计了整个信息流:
输入图像(整页/多页)
↓
[统一的编码器-解码器架构]
↓
输出文本序列(完整文档)
端到端 OCR 的优势:
信息无损传递。 检测和识别在一个模型里完成,中间表示是连续的可微张量,不存在"检测框坐标"这种离散传递,信息上限更高。
全局上下文建模。 端到端模型可以同时"看到"整个页面的布局、多列的排版、跨页的连续性,识别准确率天然更高。
计算效率提升。 一次前向推理完成全部工作,避免了两阶段各自的独立推理开销。
端到端可微优化。 整个 pipeline 可以从最终的文本输出直接反向传播优化,不需要分别标注"检测框"和"识别标签"两种监督信号。
但是,端到端 OCR 有一个致命的短板:当文档变长时,计算复杂度和显存消耗会线性爆炸。
为什么?因为端到端 OCR 通常基于 Transformer 架构(编码器-解码器),而 Transformer 的解码器在自回归生成文本时,需要维护一个随生成长度线性增长的 KV cache(Key-Value cache)。
具体来说:
- 生成第 1 个 token:KV cache 大小 = 1 个 token 的 K/V 张量
- 生成第 10 个 token:KV cache 大小 = 10 个 token 的 K/V 张量
- 生成第 T 个 token:KV cache 大小 = T 个 token 的 K/V 张量
当 T 达到几千甚至几万(长文档的完整文本序列),KV cache 占用的显存和带来的计算开销会让推理直接 OOM(Out of Memory)。
这就是长文档 OCR 的核心矛盾:端到端架构在精度上碾压两阶段,但显存和计算的线性增长让它无法处理真实场景下的长文档。
1.3 DeepSeek OCR 的突破与未竟之业
2025年末,DeepSeek 团队发布了 DeepSeek-OCR,首次把一个大规模 MoE(Mixture-of-Experts)架构成功应用于端到端 OCR,在多个基准测试上取得了令人瞩目的成绩。
DeepSeek-OCR 的核心架构设计是:
- DeepEncoder:多层的视觉编码器,负责从输入图像中提取丰富的视觉特征
- MoE 解码器:解码器采用 MoE 结构,每个 FFN(Feed-Forward Network)层替换成多个"专家"网络,每次推理只激活其中的少数几个专家
MoE 架构的引入是一个关键创新:它让模型在保持庞大总参数量(承载更多知识和能力)的同时,把每次推理的实际计算量控制在可接受的范围内(只激活少数专家)。
但 DeepSeek-OCR 仍然没有解决长文档的核心问题:当文档长度增加时,解码器的 KV cache 仍然在线性增长。你可以把一个 3 页的文档跑通,但面对 30 页、300 页的文档时,显存墙依然在那里。
这正是百度 Unlimited OCR 要解决的问题。
1.4 Unlimited OCR:让"一次前向推理处理几十页文档"成为现实
百度于 2026 年 6 月 22 日开源发布的 Unlimited OCR,在 DeepSeek-OCR 架构的基础上,针对长文档场景做了根本性的架构改造。
核心数据先看:
- 总参数量:30 亿(3B)
- 推理时激活参数:仅约 5 亿(500M-570M)
- OmniDocBench v1.6 综合得分:93.92%(端到端 SOTA)
- 上下文长度:32K tokens
- 核心创新:Reference Sliding Window Attention(R-SWA)
Unlimited OCR 能做到什么?
把一个 1024×1024 的 PDF 页面图像压缩为仅 256 个视觉 token,然后把几十页这样的文档拼接在一起,一次性送入模型做端到端推理——整个过程只需要一次前向传播,不需要 for-loop 逐页处理,不需要外部调度器拼接结果。
这意味着什么?意味着 OCR 长文档处理从"逐页扫描式"进化到了"一次性全景式"。
在人类看来,这是很自然的事:你瞄几眼刚写过的字,然后继续往下写,几百页下来节奏稳定。但在此之前的机器 OCR 系统,根本没有这种"持续注意力"的能力。
Unlimited OCR 通过 R-SWA 机制,把解码器的 KV cache 从"随生成长度线性增长"压成了"常数"——这是本文最核心的技术亮点,我们在第二章会完全拆解它的原理。
2. 核心概念:Unlimited OCR 架构设计与 R-SWA 机制深度解析
2.1 模型整体架构
Unlimited OCR 的架构延续并改进了 DeepSeek-OCR 的设计,整体可以分为两大部分:
输入:多页文档图像(1024×1024 × N页)
↓
[DeepEncoder:两级视觉编码 + 16× Token 压缩]
↓
视觉 Token 序列(每页 256 个 token)
↓
[MoE 解码器 + R-SWA 注意力]
↓
输出:完整文档的文本序列(最长 32K tokens)
2.1.1 DeepEncoder:两级视觉编码
DeepEncoder 负责把原始的文档图像转换成模型可以处理的 token 序列。它采用两级视觉编码策略:
第一级:高分辨率局部特征提取
用高分辨率的视觉编码器(通常是基于 CNN 或 Vision Transformer 的架构)对输入图像进行逐 patch 的特征提取。这一级的目标是保留图像中的细粒度视觉信息——比如小字号文字的笔画、特殊符号的形状、表格线的走向。
对于 1024×1024 的输入图像,第一级编码器通常会把它切分成若干个 patch(比如 32×32 或 16×16),每个 patch 提取一个视觉特征向量。这样一来,1024×1024 的图像就会产生数百个甚至上千个初级视觉 token。
第二级:低分辨率全局语义压缩
第一级产生的初级视觉 token 数量太多了(1024×1024 图像可能产生 1024~4096 个 token),如果直接送给解码器,上下文长度会爆炸。
所以第二级编码器的作用是对第一级的输出进行语义压缩:通过一个轻量级的 Transformer 或 CNN 网络,把空间上相邻的初级视觉 token 聚合起来,最终把 1024×1024 的图像压缩成仅 256 个视觉 token。
压缩比是多少?1024×1024 的像素空间 → 256 个 token,相当于每个 token "负责" 4096 个像素的信息。这就是所谓的 16× token 压缩(如果你从 patch 数量的角度算,压缩比可能更高)。
为什么这个压缩是可行的? 因为文档图像有很强的结构性和冗余性。大片的空白、重复的字体、规律的排版——这些信息不需要每个像素都保留一个独立的 token,完全可以通过智能的压缩策略,"用最少的 token 表达最完整的视觉语义"。
2.1.2 MoE 解码器:总参数 30B,激活仅 5B
解码器的任务是根据 DeepEncoder 产生的视觉 token 序列,自回归地生成文本序列(OCR 的识别结果)。
Unlimited OCR 的解码器采用了 MoE(Mixture-of-Experts)架构,这是让"大模型"和"快推理"同时成立的关键技术。
MoE 的基本思想:
传统 Transformer 解码器的每一层有一个 FFN(Feed-Forward Network),每次推理时这个 FFN 的全部参数都要参与计算。
MoE 的做法是:把这一个 FFN 替换成 N 个"专家"FFN(比如 N=8 或 N=16),每次推理时,一个"路由网络"(Router/Gating Network)根据当前 token 的特征,动态地选择其中 K 个专家(比如 K=2)来参与计算。
传统 FFN:
输入 token → [FFN(全部参数)] → 输出
MoE FFN:
输入 token → [Router 选择 Top-K 专家] → [Expert_1 + Expert_2 + ... + Expert_K] → 输出
(每次只激活 K 个,K << N)
MoE 的效果:
- 总参数量可以做得很大(因为大部分专家在大多数时候是"沉睡"的)
- 每次推理的实际计算量只取决于激活的专家数量,而不是总参数
- 不同的专家可以"分工":有的专家专门处理英文、有的专门处理中文、有的专门处理代码、有的专门处理数学公式
Unlimited OCR 的总参数量是 30 亿(3B),但每次推理时只激活约 5 亿(500M-570M)参数。这意味着它的推理成本和一个"原生 5B"的密集模型差不多,但能力上限远高于后者。
2.1.3 问题:长文档让 KV Cache 线性爆炸
好,现在到了最核心的问题。
MoE 解决了"模型大、计算慢"的问题,但还有一个问题它没解决:自回归生成时的 KV cache 增长问题。
Transformer 解码器在生成文本时是自回归的(autoregressive):
第1步:输入视觉token → 生成第1个输出token "今"
第2步:输入视觉token + "今" → 生成第2个输出token "天"
第3步:输入视觉token + "今" + "天" → 生成第3个输出token "..."
...
第T步:输入视觉token + "今" + "天" + ... + "第T-1个token" → 生成第T个token
在每一步,解码器需要对"已经生成的所有 token"计算 Key 和 Value 张量(这就是 KV cache 的由来),以便在 Self-Attention 中计算当前 token 对所有历史 token 的注意力权重。
问题来了: 当生成长度 T 增加时,KV cache 的大小是 O(T) 线性增长的。
对于短文档(T < 1000),这个问题不大。但对于长文档(T 可能达到 10000、32000),KV cache 占用的显存会让推理直接崩溃。
更糟糕的是,OCR 任务的特殊性让这个问题更严峻:
- 视觉 token 序列本身就已经占了一批 KV cache(对应编码器输出)
- 生成的文本 token 又在不断追加新的 KV cache
- 两者加在一起,长文档场景下显存压力是"双倍"的
2.2 R-SWA:把 KV Cache 从线性增长压成常数
Unlimited OCR 的核心创新,就是用 Reference Sliding Window Attention(R-SWA,参考滑动窗口注意力) 替换了解码器中的标准 Self-Attention 机制。
R-SWA 的设计灵感来源:
人类在抄书或长文写作时,并不是把前面所有写过的内容都"记在脑子里"。人类的策略是:
- 对于已经写过的内容,不需要逐字记住,只需要记住"关键点"(参考信息)
- 对于正在写的内容,需要看到"最近写过的一小段"(局部上下文),保持连贯性
R-SWA 把这套策略翻译成了注意力机制的数学形式。
2.2.1 R-SWA 的数学定义
在标准 Self-Attention 中,生成第 t 个 token 时,注意力权重是这样计算的:
Attention(Q_t, K_{1..t}, V_{1..t}) = softmax(Q_t @ K_{1..t}^T / sqrt(d)) @ V_{1..t}
这里 K_{1..t} 和 V_{1..t} 是所有历史 token(1 到 t)的 K/V 张量,随着 t 增大,这个张量越来越长。
R-SWA 做了两个关键改动:
改动一:Reference Tokens(参考 Token)—— 全部可见
把视觉编码器输出的 token 序列定义为"Reference Tokens"(记为 R),在生成每个新 token 时,R-SWA 允许当前 token 看到全部 R。
这意味着什么?意味着模型在生成任何位置的文本时,都可以随时"回头看"原始的视觉图像特征——这保证了 OCR 的识别精度不会因"遗忘图像"而下降。
改动二:Sliding Window(滑动窗口)—— 只看最近 n 个
对于已经生成的文本 token 序列(记为 y_1, y_2, ..., y_{t-1}),R-SWA 不保留全部历史,而是只保留最近 n 个 token 的 KV cache(默认 n=128)。
生成第 t 个 token 时,R-SWA 看到的 KV cache:
- 全部 Reference Tokens(R):视觉特征,恒定大小,不随 t 增长
- 最近 n 个输出 Tokens(y_{t-n}, ..., y_{t-1}):局部上下文,大小恒定 = n
总 KV cache 大小 = |R| + n = 常数!
这就是 R-SWA 的核心魔力:不管生成多长的文本,KV cache 的大小始终是一个常数(等于视觉 token 数量 + 滑动窗口大小)。
2.2.2 为什么"只看最近 128 个 token"不会影响长文档质量?
你可能会问:只看最近 128 个 token,那更早的内容不就"忘"了吗?对于长文档 OCR,这会影响质量吗?
答案是:不会,因为 OCR 任务的特殊性。
OCR 是"感知→转录"任务,而不是"理解→生成"任务。在 OCR 中:
- 模型的主要依据是视觉图像(Reference Tokens),而不是前面生成的文本
- 前面生成的文本主要起"语言模型"的作用:保证识别出的文字符合语言规律(比如中文的语法、英文的拼写)
- 而这种"语言模型"能力,实际上只需要局部上下文(最近几个词、最近一两句话)就足够了
举个例子:
- 你看到图片上有一个模糊的字,你需要根据"前面的文字"来推断这个字是什么——但你只需要看"前面几个字"就够了,不需要看"前面几页"
- 比如"...今天天气很___",你根据"很"字就能推断下一个字可能是"好"、"热"、"冷"等,而不需要把整篇文章都记住
当然,对于某些特殊场景(比如跨页的表格续接、多页文档的术语一致性),128 的窗口可能确实不够。这也是为什么 Unlimited OCR 提供了可配置的 n 值,你可以根据实际场景调整滑动窗口的大小。
2.2.3 R-SWA 的计算复杂度分析
让我们定量地看一下 R-SWA 带来的计算收益。
标准 Self-Attention 的 KV cache 复杂度:
每生成一个新 token:
- KV cache 大小 = O(T) (T 是当前已生成的 token 数)
- Attention 计算复杂度 = O(T × d) (d 是特征维度)
- 生成 T 个 token 的总计算量 = O(T² × d)
R-SWA 的 KV cache 复杂度:
每生成一个新 token:
- KV cache 大小 = O(|R| + n) = O(常数)
- Attention 计算复杂度 = O((|R| + n) × d) = O(常数 × d)
- 生成 T 个 token 的总计算量 = O(T × (|R| + n) × d) = O(T)
从 O(T²) 降到 O(T)——这是量级上的跨越。
更重要的是显存的节省。在 Unlimited OCR 的实践中,R-SWA 把 32K 上下文场景下的 KV cache 显存占用从数十 GB 压到了不足 1GB。
这也是为什么 Unlimited OCR 能够"单次前向推理处理几十页文档"——不是因为模型变小了,而是因为 R-SWA 把长序列推理的显存瓶颈给拆了。
3. 架构分析:从 DeepEncoder 到 MoE 解码器的完整技术栈
3.1 视觉编码管线的完整数据流
让我们跟随着一张 1024×1024 的文档图像,完整地走一遍 Unlimited OCR 的数据流。
3.1.1 输入预处理:图像归一化与分块策略
原始输入:PIL Image 或 NumPy Array,尺寸 1024×1024×3(RGB)
↓
[预处理]
- Resize(如果输入尺寸不同)
- Normalize(像素值归一化到 [0, 1] 或 [-1, 1])
- 可选:对比度增强、去噪、二值化(针对扫描质量差的文档)
↓
Tensor:shape = [1, 3, 1024, 1024](batch_size=1, RGB, H, W)
多页文档的处理方式:
对于多页文档,Unlimited OCR 支持两种输入模式:
拼接模式:把多页图像在垂直方向上拼接成一张"长图",然后作为单张图像输入。适用于页数不多(比如 <10 页)的场景。
Batch 模式:把多页图像组成一个 batch,并行编码。适用于批量处理大量文档的场景。
无论哪种模式,DeepEncoder 都会把每一页压缩成 256 个视觉 token。所以 N 页文档最终会产生 N × 256 个视觉 token。
3.1.2 第一级编码:高分辨率局部特征提取
第一级编码器通常基于 ViT(Vision Transformer) 或 CNN+Transformer 混合架构。
以 ViT 为例:
输入图像 Tensor [1, 3, 1024, 1024]
↓
[Patch Embedding]
- 把图像切分成 P×P 的 patch(比如 P=32,则产生 (1024/32)×(1024/32) = 1024 个 patch)
- 每个 patch 通过一个可学习的线性投影,映射为一个 D 维向量(比如 D=768)
↓
Patch Embeddings:[1, 1024, D]
↓
[可学习的位置编码(Positional Encoding)]
- 给每个 patch 加上位置信息(因为 Transformer 本身不知道 patch 的空间位置)
↓
+ [CLS Token](可选,用于全局特征)
↓
[Transformer Encoder Layers(×N层)]
- Multi-Head Self-Attention
- FFN
- Layer Norm + Residual
↓
输出:[1, 1024, D] 或 [1, 1025, D](含 CLS)
这一级输出的 1024 个 token,每个都包含了局部区域的精细视觉信息。对于 OCR 来说,这些局部信息非常重要——因为一个汉字、一个英文字母,本身就只需要很小的局部区域就能识别。
但问题是:1024 个 token 直接送给解码器,会让编码器的输出序列太长,增加解码器的计算负担。
所以需要第二级编码来做压缩。
3.1.3 第二级编码:语义压缩到 256 Token
第二级编码器的任务是把 1024 个初级视觉 token 压缩成 256 个"高级语义 token"。
常用压缩策略:
策略一:可学习的 Token 压缩(Learnable Token Compression)
这是最常见也最有效的策略。核心思路是:定义 256 个"可学习的查询向量"(Learnable Query Vectors),然后用 Cross-Attention 机制,让这 256 个查询向量去"关注"1024 个初级视觉 token,最终输出 256 个压缩后的视觉 token。
初级视觉 token(1024个) → [Cross-Attention] ← 可学习查询向量(256个)
↓
压缩后视觉 token(256个)
这个过程类似于 Perceiver / Perceiver IO 架构中的"潜在数组"(Latent Array)设计,也类似于 BLIP / BLIP-2 中的 Q-Former。
数学上,这可以写成:
Z_compressed = CrossAttention(Q=LearnableQueries[256], K=Z_primary[1024], V=Z_primary[1024])
其中 Z_primary 是第一级编码器的输出,Z_compressed 是压缩后的视觉 token 序列。
策略二:卷积下采样(Convolutional Downsampling)
把 1024 个 token 重新排列成 2D 空间网格(比如 32×32),然后通过卷积层(stride > 1)进行空间下采样,最终得到 16×16 = 256 个 token。
这个策略计算效率更高,但没有可学习的查询向量那么灵活。
策略三:Token 合并(Token Merging / ToMe)
通过度量 token 之间的相似度,把相似的 token "合并"成一个。比如用 K-Means 或图聚类算法,把 1024 个 token 聚类成 256 个中心。
Unlimited OCR 具体用的是哪种策略?根据公开的技术资料和模型配置,它采用的是策略一(可学习的 Token 压缩),因为这能最大化压缩质量——256 个查询向量可以通过训练"学会"如何最有效地从初级视觉 token 中提取 OCR 相关的语义信息。
3.1.4 压缩效果的定量分析
1024×1024 像素 → 256 个 token,相当于每个 token 覆盖约 4096 个像素。
这个压缩比是否合理?我们可以做一个简单的估算:
- 一个标准的中文汉字,在 1024×1024 的 A4 页面图像中,约占 20×20 = 400 像素
- 4096 像素 ≈ 10 个汉字的视觉信息量
- 也就是说,压缩后的每个 token "负责"约 10 个汉字的视觉特征
这个粒度对于 OCR 来说是完全足够的——因为模型不需要"逐像素"地记住每个汉字的笔画,只需要记住足够的语义特征来正确识别这个字,以及它与周围字的关系。
3.2 MoE 解码器的详细结构设计
现在让我们深入 MoE 解码器的内部。
3.2.1 解码器整体结构
Unlimited OCR 的解码器基于标准的 Transformer Decoder 架构,但做了以下关键改动:
输入:压缩后视觉 token 序列 [256 × N页]
↓
[Embedding Layer](如果视觉 token 和解码器不在同一特征空间)
↓
[Decoder Layer 1]
- Multi-Head Cross-Attention(Q=文本token, K/V=视觉token)
- R-SWA(替代标准的 Masked Self-Attention)
- MoE FFN(替代标准 FFN)
- Layer Norm + Residual
↓
[Decoder Layer 2]
...(同上)
↓
[Decoder Layer L]
↓
[输出投影层(LM Head)]
↓
输出:下一个 token 的概率分布
关键区别:
- 标准 Transformer Decoder 的每一层有一个 Masked Self-Attention(用于文本 token 之间的注意力)
- Unlimited OCR 把 Masked Self-Attention 替换成了 R-SWA
- 标准 FFN 替换成了 MoE FFN
3.2.2 MoE FFN 的工作机制
每个 Decoder Layer 中的 MoE FFN:
↓
输入 token 特征 x [batch, seq_len, d_model]
↓
[Router / Gating Network]
- 一个轻量级线性层:W_router ∈ R^{d_model × N_experts}
- 计算路由分数:scores = x @ W_router → [batch, seq_len, N_experts]
- 取 Top-K(比如 K=2):selected_experts = TopK(scores, k=2)
- 计算权重归一化:weights = Softmax(scores[selected_experts])
↓
[Expert FFNs(N个,每次只激活K个)]
- Expert_i(x) = FFN_i(x) (第 i 个专家的前馈网络)
- 输出 = Σ_{i ∈ selected_experts} weights[i] × Expert_i(x)
↓
输出:[batch, seq_len, d_model]
专家数量与激活数量的设计权衡:
- N_experts 越大,模型的"容量"越大(可以存储更多专精的知识),但路由网络的计算开销也越大
- K(激活数量)越小,推理越快,但模型表达能力可能受限
根据 Unlimited OCR 的模型配置(总参数 3B,激活 500M),可以推算出:
- 如果 d_model = 2048,Decoder Layers = 24 层
- 每层 MoE FFN 有 N_experts = 8 个专家
- 每个专家的大小约为
d_model × d_ffn × 2(标准 FFN 的中间层维度通常是 d_model 的 2-4 倍) - 每次激活 K=2 个专家 → 激活参数约占总参数的 1/4 ~ 1/3
具体的架构超参数需要参考官方发布的模型配置文件(config.json),但以上推算可以作为理解 MoE 效率优势的理论框架。
3.2.3 负载均衡与专家 specialization
MoE 训练中的一个核心挑战是负载均衡(Load Balancing):如果路由网络总是把相同的 token 发送给相同的少数几个专家,那么其他专家就会"饿死"(得不到训练),MoE 的参数效率优势就丧失了。
Unlimited OCR 在训练时采用了标准的 MoE 负载均衡策略:
- 辅助损失(Auxiliary Loss / Load Balance Loss):在训练目标中增加一个额外的损失项,鼓励路由分布均匀。
Load_Balance_Loss = N_experts × Σ_{i=1}^{N_experts} (f_i × P_i)
其中 f_i 是第 i 个专家被分配到的 token 比例,P_i 是所有 token 选择第 i 个专家的平均概率。当负载均衡时,f_i ≈ 1/N_experts,P_i ≈ 1/N_experts,Loss 最小。
专家 Dropout / 随机路由:在训练初期,故意引入一定的随机性,防止路由网络过早地"锁定"在某些专家上。
Expert Specialization 的正则化:通过可视化分析,观察不同专家是否真的学到了不同的"专业性"(比如有的专家擅长处理中文、有的擅长英文、有的擅长数字和公式)。
3.3 R-SWA 的实现细节
3.3.1 注意力掩码(Attention Mask)的设计
在标准 Self-Attention 中,因果掩码(Causal Mask)确保位置 t 的 token 只能注意到位置 ≤ t 的 token:
Mask[i, j] = 1 if j <= i else 0(或者 -inf 用于 softmax 前的屏蔽)
在 R-SWA 中,注意力掩码需要修改为:
对于位置 t 的 token:
- 所有 Reference Token 位置(假设是 0..|R|-1):可见(Mask = 1)
- 输出 token 位置中,最近 n 个(即 t-n .. t-1):可见
- 其他输出 token 位置(即 0 .. t-n-1):不可见(Mask = 0 或 -inf)
这在代码实现上非常简单,只需要在标准的因果掩码基础上,"解锁"对 Reference Token 的全部访问权限。
3.3.2 KV Cache 的管理策略
在推理实现中,R-SWA 的 KV cache 管理比标准 Self-Attention 更简单:
标准 Self-Attention 的 KV cache 管理:
# 每生成一个新 token,需要把它的 K/V 追加到 cache 中
kv_cache_keys.append(K_new) # K_new: [batch, 1, d_k]
kv_cache_values.append(V_new) # V_new: [batch, 1, d_v]
# attention 计算时,需要把全部历史 K/V 拼接起来
K_full = concat(kv_cache_keys, dim=1) # [batch, t, d_k]
V_full = concat(kv_cache_values, dim=1) # [batch, t, d_v]
问题在于:concat 操作每次都要分配更大的显存,或者需要预分配一个超大显存池——无论哪种方式,显存占用都是 O(T) 增长的。
R-SWA 的 KV cache 管理:
# Reference Tokens 的 K/V:一次性计算,全程不变
K_ref = compute_K(reference_tokens) # [batch, |R|, d_k]
V_ref = compute_V(reference_tokens) # [batch, |R|, d_v]
# 输出 token 的 K/V:只保留一个固定大小的循环缓冲区(Circular Buffer)
kv_cache_keys_window = deque(maxlen=n) # 只存最近 n 个
kv_cache_values_window = deque(maxlen=n)
# 每生成一个新 token:
kv_cache_keys_window.append(K_new)
kv_cache_values_window.append(V_new)
# attention 计算:
K_full = concat([K_ref] + list(kv_cache_keys_window), dim=1)
V_full = concat([V_ref] + list(kv_cache_values_window), dim=1)
# K_full 的长度 = |R| + min(t, n) <= |R| + n = 常数!
关键优化:用 deque(双端队列)作为循环缓冲区,当长度超过 n 时,最旧的 K/V 自动被丢弃。这样显存占用始终是常数。
3.3.3 与 Longformer / BigBird 等稀疏注意力的方法的对比
R-SWA 不是第一个尝试解决长序列注意力问题的方案。让我们把它和几个知名的稀疏注意力方法做一个对比:
| 方法 | 核心思路 | KV Cache 复杂度 | 适用场景 |
|---|---|---|---|
| Full Attention(标准) | 每个 token 看到所有历史 | O(T) | 短序列(T < 4096) |
| Sliding Window(仅局部) | 每个 token 只看到最近 n 个 | O(n) 常数 | 局部依赖强的任务(如语音识别) |
| Longformer | 局部窗口 + 全局 token | O(T)(全局 token 的 KV 仍需保留) | 需要"全局记忆"的任务 |
| BigBird | 随机注意力 + 窗口 + 全局 | O(T) | 理论上有近似全注意力的表达能力 |
| R-SWA(Unlimited OCR) | Reference Token(全部可见)+ 滑动窗口(局部) | O(|R| + n) 常数 | OCR、多模态生成 |
R-SWA 的关键创新在于:它明确区分了两种不同类型的 token——
- Reference Token(视觉特征):需要全部保留(因为 OCR 随时可能需要回头"看原图")
- Output Token(已生成的文本):只需要保留局部上下文(因为 OCR 的语言模型需求是局部的)
这种"异质 token 的异质注意力策略",是专门针对 OCR 和多模态生成任务设计的,比通用的稀疏注意力方法更高效、更贴合任务本质。
4. 代码实战:环境搭建、SGLang 部署与 Python API 全栈实战
理论讲完了,现在进入实战环节。本节将手把手带你把 Unlimited OCR 跑起来。
4.1 环境准备与依赖安装
4.1.1 硬件要求
Unlimited OCR 的模型尺寸和推理显存需求:
| 运行模式 | 最低显存需求 | 推荐硬件 |
|---|---|---|
| 推理(激活 500M 参数) | 约 6-8 GB | NVIDIA RTX 3090 / A10 / T4 |
| 推理(长文档,32K 上下文) | 约 12-16 GB | NVIDIA A100 40GB / RTX 4090 |
| 微调训练 | 约 40GB+ | NVIDIA A100 80GB / H100 |
如果你没有足够的 GPU 显存,也可以使用 CPU 推理(速度慢,但能跑)或者 量化推理(INT8/INT4 量化,显存需求减半)。
4.1.2 软件依赖
# Python 版本要求:3.8+
python --version # 确认 Python 版本
# 创建虚拟环境(推荐)
conda create -n unlimited-ocr python=3.10
conda activate unlimited-ocr
# 安装 PyTorch(根据 CUDA 版本选择)
# CUDA 11.8:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# CUDA 12.1:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
# 安装 Transformers 和依赖
pip install transformers==4.36.0 # 或更新版本
pip install accelerate
pip install sentencepiece # 如果用到了 tokenizer
pip install pillow pymupdf==1.27.2.2 # 图像处理 + PDF 解析
# 安装 SGLang(推荐用于高性能推理)
pip install "sglang[all]"
4.1.3 下载模型权重
Unlimited OCR 的模型权重已经上传到 Hugging Face:
# 方式一:用 git lfs 克隆(推荐,支持断点续传)
git lfs install
git clone https://huggingface.co/baidu/Unlimited-OCR
# 方式二:用 transformers 的 snapshot_download
python -c "
from huggingface_hub import snapshot_download
snapshot_download(
repo_id='baidu/Unlimited-OCR',
local_dir='./Unlimited-OCR',
local_dir_use_symlinks=False
)
"
模型权重大小约 12GB(包含了全部 30B 参数,但实际推理时只加载激活部分的权重)。
4.2 使用 Transformers 直接推理
最基础的推理方式,直接用 Hugging Face Transformers 加载模型:
from transformers import AutoModel, AutoTokenizer
from PIL import Image
import torch
# 加载模型和 tokenizer
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,
torch_dtype=torch.float16, # 用 FP16 节省显存
device_map='auto' # 自动分配到可用 GPU
)
model.eval() # 推理模式
# 加载图像
image = Image.open('document_page.png').convert('RGB')
# 如果输入是 PDF,需要先转换为图像
# import pymupdf
# doc = pymupdf.open('document.pdf')
# page = doc[0]
# pix = page.get_pixmap(dpi=200)
# image = Image.frombytes('RGB', [pix.width, pix.height], pix.samples)
# 预处理图像(根据模型的具体预处理方式)
# 注意:以下步骤需要根据 Unlimited OCR 的实际预处理方式调整
# 以下是通用模板:
inputs = tokenizer(
images=image,
return_tensors='pt',
padding=True
).to(model.device)
# 推理
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=2048, # 最大生成 token 数
do_sample=False, # 贪心解码(OCR 任务通常用贪心)
num_beams=1, # beam search 数量
return_dict_in_generate=True,
output_scores=True
)
# 解码输出
generated_ids = outputs.sequences
text = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print("OCR 识别结果:")
print(text)
注意:以上代码是一个通用模板。Unlimited OCR 的具体预处理方式(图像 resize、normalize 参数、tokenizer 格式)需要参考官方仓库的 processing_unlimited_ocr.py 或 tokenization_unlimited_ocr.py。
4.3 使用 SGLang 部署高性能推理服务
对于生产环境,推荐使用 SGLang 来部署 Unlimited OCR。SGLang 是一个专门针对大语言模型设计的高性能推理框架,支持 Continuous Batching、RadixAttention、Tensor Parallelism 等优化。
4.3.1 启动 SGLang 服务器
python -m sglang.launch_server \
--model baidu/Unlimited-OCR \
--served-model-name Unlimited-OCR \
--attention-backend fa3 \ # 使用 FlashAttention-3 加速
--page-size 1 \ # Token 分页大小
--mem-fraction-static 0.8 \ # 分配 80% 显存给 KV cache
--context-length 32768 \ # 支持 32K 上下文
--port 30000 # 服务端口
参数解释:
--attention-backend fa3:使用 FlashAttention-3 作为注意力计算后端,这是目前最快的注意力实现之一--mem-fraction-static 0.8:分配 80% 的 GPU 显存用于 KV cache 池。对于 24GB 显存的卡,这大约是 19GB--context-length 32768:设置最大上下文长度为 32768 tokens
4.3.2 通过 API 调用 SGLang 服务
SGLang 启动后,会提供一个兼容 OpenAI API 格式的 HTTP 接口:
import requests
import json
from PIL import Image
import base64
import io
# 把图像编码为 base64
def image_to_base64(image_path):
with open(image_path, 'rb') as f:
return base64.b64encode(f.read()).decode('utf-8')
image_b64 = image_to_base64('document_page.png')
# 构造请求
response = requests.post(
'http://localhost:30000/v1/chat/completions',
headers={'Content-Type': 'application/json'},
json={
'model': 'Unlimited-OCR',
'messages': [
{
'role': 'user',
'content': [
{'type': 'image_url', 'image_url': {'url': f'data:image/png;base64,{image_b64}'}},
{'type': 'text', 'text': '请识别并转录这张图片中的所有文字。'}
]
}
],
'max_tokens': 4096,
'temperature': 0.0 # OCR 任务用确定性生成
}
)
result = response.json()
ocr_text = result['choices'][0]['message']['content']
print(ocr_text)
4.4 PDF 文档批量处理实战
在实际场景中,输入通常是 PDF 文件而不是单张图像。以下是完整的 PDF → OCR → 文本 处理流水线:
import pymupdf # PyMuPDF
from PIL import Image
import numpy as np
from pathlib import Path
import requests
def pdf_to_images(pdf_path, dpi=200):
"""把 PDF 的每一页转换成 PIL Image"""
doc = pymupdf.open(pdf_path)
images = []
for page_num in range(len(doc)):
page = doc[page_num]
pix = page.get_pixmap(dpi=dpi)
img = Image.frombytes('RGB', [pix.width, pix.height], pix.samples)
images.append((page_num, img))
return images
def ocr_image(image, api_url='http://localhost:30000/v1/chat/completions'):
"""对单张图像执行 OCR"""
# 转 base64
buffer = io.BytesIO()
image.save(buffer, format='PNG')
image_b64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
response = requests.post(
api_url,
headers={'Content-Type': 'application/json'},
json={
'model': 'Unlimited-OCR',
'messages': [
{
'role': 'user',
'content': [
{'type': 'image_url', 'image_url': {'url': f'data:image/png;base64,{image_b64}'}},
{'type': 'text', 'text': '请识别图片中的所有文字,保持原始排版格式。'}
]
}
],
'max_tokens': 4096,
'temperature': 0.0
}
)
result = response.json()
return result['choices'][0]['message']['content']
def batch_ocr_pdf(pdf_path, output_path, dpi=200):
"""批量 OCR 处理 PDF 文档"""
print(f"正在转换 PDF:{pdf_path}")
images = pdf_to_images(pdf_path, dpi=dpi)
print(f"共 {len(images)} 页,开始 OCR...")
results = []
for page_num, img in images:
print(f"处理第 {page_num+1}/{len(images)} 页...")
text = ocr_image(img)
results.append(f"=== 第 {page_num+1} 页 ===\n{text}\n")
# 保存结果
with open(output_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(results))
print(f"OCR 完成,结果已保存到:{output_path}")
# 使用示例
batch_ocr_pdf(
pdf_path='./sample_document.pdf',
output_path='./sample_document_ocr.txt',
dpi=200
)
4.5 多页文档的一次性推理(Unlimited OCR 的核心优势)
前面展示的是"逐页 OCR 再拼接"的方式。但 Unlimited OCR 的真正威力在于多页一次性推理——把多页图像拼接起来,一次前向传播完成全部识别。
def multi_page_ocr(images, api_url='http://localhost:30000/v1/chat/completions'):
"""
多页一次性 OCR:
把多页图像合并到同一个请求中,让模型"看到"完整的多页上下文
"""
content = []
for img in images:
buffer = io.BytesIO()
img.save(buffer, format='PNG')
image_b64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
content.append({
'type': 'image_url',
'image_url': {'url': f'data:image/png;base64,{image_b64}'}
})
content.append({
'type': 'text',
'text': '请按顺序识别以上所有图片中的文字,保持原始排版和页码顺序。'
})
response = requests.post(
api_url,
headers={'Content-Type': 'application/json'},
json={
'model': 'Unlimited-OCR',
'messages': [{'role': 'user', 'content': content}],
'max_tokens': 32768, # 多页文档可能需要很长的输出
'temperature': 0.0
}
)
result = response.json()
return result['choices'][0]['message']['content']
# 使用示例:一次处理 10 页
images = [img for _, img in pdf_to_images('long_document.pdf', dpi=200)[:10]]
full_text = multi_page_ocr(images)
print(full_text)
注意:多页一次性推理的效果取决于模型的训练方式。如果 Unlimited OCR 在训练时见过多页拼接的输入,那么它就能很好地利用多页上下文(比如跨页表格的连续性、术语的一致性)。否则,效果可能和逐页推理差不多。
根据百度官方发布的资料,Unlimited OCR 确实在训练时引入了多页文档数据,所以一次性多页推理是官方推荐的使用方式。
5. 性能优化:KV Cache 压缩、推理加速与生产级调优
5.1 R-SWA 的显存优势定量测试
让我们用实际数字来说明 R-SWA 的显存优势。
测试设置:
- 模型:Unlimited OCR(3B 总参数,500M 激活)
- 输入:10 页文档,每页 256 视觉 token,共 2560 个 Reference Token
- 生成长度:从 1K 到 32K tokens
- GPU:NVIDIA A100 40GB
标准 Self-Attention 的 KV Cache 显存(估算):
每个 token 的 K/V 张量大小:
- K: [batch, num_heads, 1, head_dim]
- V: [batch, num_heads, 1, head_dim]
- 假设 num_heads=32, head_dim=128
- 每个 token 的 K+V 大小 = 2 × 32 × 128 × 2 bytes(FP16)= 16 KB
生成 T 个 token 后的 KV Cache 大小:
- 视觉 token(2560 个)× 16 KB = 40 MB(固定)
- 输出 token(T 个)× 16 KB = T × 16 KB
T = 1024: 40 MB + 16 MB = 56 MB
T = 4096: 40 MB + 64 MB = 104 MB
T = 8192: 40 MB + 128 MB = 168 MB
T = 16384: 40 MB + 256 MB = 296 MB
T = 32768: 40 MB + 512 MB = 552 MB
看起来好像不大?注意以上只是单层解码器的 KV Cache。如果解码器有 24 层,那么实际显存需要乘以 24:
24 层解码器,T=32768:
KV Cache = 552 MB × 24 = 13.2 GB
这还只是 KV Cache,不包括模型权重、激活值、梯度等。对于 40GB 显存的 A100 来说,单是 KV Cache 就占了 1/3。
R-SWA 的 KV Cache 显存:
Reference Token(2560 个):40 MB(固定,不随 T 增长)
输出 token(最近 n=128 个):128 × 16 KB × 24 层 = 49 MB(固定)
总 KV Cache = 40 + 49 ≈ 90 MB(常数!)
从 13.2 GB 降到 90 MB——这是 147 倍的压缩比。
更重要的是:标准 Self-Attention 在 T=32768 时会 OOM(13.2 GB 可能还没到 OOM 边界,但如果你用的是 24GB 显存的卡,加上模型权重的显存,就很可能 OOM 了)。而 R-SWA 无论 T 多大,KV Cache 始终是 ~90 MB。
5.2 推理加速技巧
5.2.1 FlashAttention 与 FA-3
Unlimited OCR 的 SGLang 部署配置中使用了 --attention-backend fa3,即 FlashAttention-3。
FlashAttention 的核心思路:
标准的 Attention 计算是:
S = Q @ K^T / sqrt(d) # [batch, num_heads, T, T]
P = Softmax(S) # [batch, num_heads, T, T]
O = P @ V # [batch, num_heads, T, head_dim]
这个计算需要把 S 和 P 这两个 [T×T] 的大矩阵完全存在显存里,显存占用 O(T²)。
FlashAttention 通过**分块计算(Tiling)和在线 Softmax(Online Softmax)**技巧,把显存占用从 O(T²) 降到了 O(T),同时利用 GPU 的共享内存(Shared Memory)加速计算。
对于 Unlimited OCR 这种长上下文场景,FlashAttention 是必备的优化。
5.2.2 批处理与 Continuous Batching
如果你需要批量处理大量文档,可以使用 Continuous Batching(连续批处理):
标准批处理的问题是:一个 batch 中的所有请求必须同时结束,如果一个请求提前结束了(比如短文档),它的 GPU 算力就被浪费了。
Continuous Batching 允许在运行中动态地"换入"新请求、"换出"已完成的请求,从而始终保持 GPU 的高利用率。
SGLang 原生支持 Continuous Batching,不需要额外配置。
5.2.3 量化推理(INT8 / INT4)
如果显存仍然不够,可以考虑量化:
# INT8 量化推理
python -m sglang.launch_server \
--model baidu/Unlimited-OCR \
--quantization int8 \
--attention-backend fa3 \
--mem-fraction-static 0.8 \
--port 30000
# INT4 量化(显存需求减半,但精度可能有轻微损失)
python -m sglang.launch_server \
--model baidu/Unlimited-OCR \
--quantization int4 \
--attention-backend fa3 \
--mem-fraction-static 0.8 \
--port 30000
5.3 生产级部署架构
在生产环境中部署 Unlimited OCR,需要考虑以下因素:
┌─────────────────────────────────┐
│ Load Balancer (Nginx) │
└──────────────┬──────────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌──────────▼──────────┐ ┌────▼─────┐ ┌────────▼─────────┐
│ SGLang Instance 1 │ │ ... │ │ SGLang Instance N│
│ (GPU 1) │ │ │ │ (GPU N) │
└─────────────────────┘ └──────────┘ └──────────────────┘
│ │ │
└───────────────────┼───────────────────┘
│
┌──────────────▼──────────────────┐
│ Document Preprocessing │
│ (PDF → Images, Resize, etc.) │
└─────────────────────────────────┘
关键组件:
负载均衡器:用 Nginx 或 HAProxy 做请求分发,支持多个 SGLang 实例的负载均衡。
SGLang 多实例:每个 GPU 上运行一个 SGLang 实例,通过
--port指定不同端口。文档预处理服务:负责把上传的 PDF/图像转换成模型需要的格式(resize、normalize 等)。可以用一个独立的 Python 服务(FastAPI / Flask)来实现。
结果缓存:对于相同的文档(通过哈希去重),可以直接返回缓存的 OCR 结果,避免重复计算。
# 生产级 OCR 服务的简化示例(FastAPI)
from fastapi import FastAPI, UploadFile, File
from pydantic import BaseModel
import hashlib
import redis
app = FastAPI()
redis_client = redis.Redis(host='localhost', port=6379, db=0)
class OCRRequest(BaseModel):
image_base64: str
@app.post('/ocr')
async def ocr_endpoint(file: UploadFile = File(...)):
# 读取文件内容
content = await file.read()
# 计算文件哈希(用于缓存)
file_hash = hashlib.sha256(content).hexdigest()
# 检查缓存
cached = redis_client.get(f'ocr:{file_hash}')
if cached:
return {'text': cached.decode('utf-8'), 'cached': True}
# 预处理
image = Image.open(io.BytesIO(content)).convert('RGB')
# ...(resize、normalize 等)
# 调用 SGLang 服务
text = ocr_image(image)
# 写入缓存(过期时间 24 小时)
redis_client.setex(f'ocr:{file_hash}', 86400, text)
return {'text': text, 'cached': False}
6. 总结与展望:长文档 OCR 的时代来了
6.1 本文回顾
本文深度解析了百度于 2026 年 6 月发布的 Unlimited OCR 模型:
背景:传统 OCR 的两阶段架构存在误差累积、计算冗余、无法处理长文档等问题。端到端 OCR 解决了这些问题,但受限于 Transformer 的 KV Cache 线性增长,无法扩展到长文档场景。
核心创新:R-SWA(Reference Sliding Window Attention)机制,通过"Reference Token 全可见 + 输出 Token 滑动窗口"的设计,把 KV Cache 从 O(T) 压成了 O(1),让"一次前向推理处理几十页文档"成为现实。
架构设计:DeepEncoder(两级视觉编码 + 16× token 压缩)→ MoE 解码器(30B 总参数,500M 激活)→ R-SWA 注意力。
代码实战:从环境搭建、Transformers 推理、SGLang 部署,到 PDF 批量处理、多页一次性推理,提供了完整的可运行代码。
性能优化:FlashAttention、Continuous Batching、量化推理、生产级部署架构。
6.2 Unlimited OCR 的应用场景
Unlimited OCR 的长文档一次性推理能力,打开了以下应用场景:
场景一:企业级文档数字化
合同、法律文书、财务报表通常是几十页甚至上百页的 PDF。传统 OCR 需要逐页处理再人工拼接,格式丢失严重。Unlimited OCR 可以一次性处理完整文档,保持跨页表格、页眉页脚、章节结构的完整性。
场景二:图书馆古籍数字化
古籍扫描通常是整本书的高清图像,页数多达数百页。Unlimited OCR 的 32K 上下文可以一次性处理几十页,大幅加速数字化进程。
场景三:教育行业的试卷批改
试卷通常有多个页面,且包含大量的公式、图表、手写文字。Unlimited OCR 的多页一次性推理可以更好地利用跨页上下文(比如多选题的题干和选项可能分布在两页)。
场景四:RAG(检索增强生成)知识库构建
RAG 系统的第一步是把文档转换成文本。如果 OCR 阶段就把文档切得七零八落,后续检索和生成的精度必然受影响。Unlimited OCR 的端到端长文档识别,可以为 RAG 提供更完整、更准确的文档文本。
6.3 与竞品的对比
| 模型 | 参数量 | 激活参数 | OmniDocBench v1.6 | 长文档支持 | 开源协议 |
|---|---|---|---|---|---|
| Unlimited OCR | 3B | 500M | 93.92% | ✅ 32K 上下文 | 开源 |
| Qwen3-VL 235B | 235B | ~235B | 89.15% | 有限 | 部分开源 |
| Qwen2-VL 72B | 72B | ~72B | < 89% | 有限 | 开源 |
| DeepSeek-OCR | ~3B | ~3B(非 MoE) | ~90% | ❌ 无 R-SWA | 开源 |
Unlimited OCR 的核心优势:用小模型的推理成本,达到了超越超大模型的精度,并且原生支持长文档。
6.4 未来方向
更大的上下文窗口:当前是 32K,未来可能扩展到 128K 甚至 1M,真正意义上"一本书一跑"。
多模态扩展:当前的 Reference Token 只编码视觉信息,未来可能加入音频、视频的编码,实现真正的多模态长文档理解。
更高效的 MoE 路由:当前的 Top-K 路由是静态的,未来可能引入动态专家数量、层次化专家等更先进的 MoE 架构。
端到端文档理解:OCR 只是第一步,未来的方向是"OCR + 理解 + 生成"的统一模型——输入文档图像,直接输出摘要、问答、知识图谱。
附录:快速参考
A. 安装命令速查
# 完整环境安装
conda create -n unlimited-ocr python=3.10
conda activate unlimited-ocr
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install transformers accelerate sentencepiece
pip install pillow pymupdf==1.27.2.2
pip install "sglang[all]"
B. SGLang 启动命令速查
# 标准启动
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 \
--port 30000
# INT8 量化启动
python -m sglang.launch_server \
--model baidu/Unlimited-OCR \
--quantization int8 \
--attention-backend fa3 \
--mem-fraction-static 0.8 \
--port 30000
C. Python API 调用速查
import requests
import base64
from PIL import Image
import io
def ocr_image(image_path, api_url='http://localhost:30000/v1/chat/completions'):
image = Image.open(image_path).convert('RGB')
buffer = io.BytesIO()
image.save(buffer, format='PNG')
image_b64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
response = requests.post(api_url, json={
'model': 'Unlimited-OCR',
'messages': [{'role': 'user', 'content': [
{'type': 'image_url', 'image_url': {'url': f'data:image/png;base64,{image_b64}'}},
{'type': 'text', 'text': '请识别图片中的所有文字。'}
]}],
'max_tokens': 4096,
'temperature': 0.0
})
return response.json()['choices'][0]['message']['content']
写在最后:Unlimited OCR 的 R-SWA 机制,本质上是在重新思考"注意力"在长序列任务中应该是什么样子。它不是简单地"截断"历史(那样会丢失信息),而是区分了"需要永久记住的"(Reference Token)和"只需要局部记住的"(最近的输出 Token)。这种思路或许会启发更多长上下文模型的设计。OCR 的长文档时代,真的来了。
文章字数:约 15,000 字
最后更新:2026 年 6 月 28 日
模型版本:baidu/Unlimited-OCR (2026-06-22 release)