百度 Unlimited OCR 开源:用常数级 KV Cache 一次性吃下整本书
一个 30 亿参数的端到端 OCR 模型,推理时只激活 5 亿参数,单次前向传播就能识别几十页文档。核心突破:把 KV Cache 从线性增长压成常数,彻底解决长文档"越生成越慢"的顽疾。
一、从 DeepSeek-OCR 说起:端到端 OCR 的时代来临
2025 年,DeepSeek-OCR 横空出世,用端到端架构刷新了 OCR 领域的认知。传统 OCR 流程是"检测 → 识别 → 后处理"三段式,每个环节都可能丢信息、引入错误。端到端模型把整个过程塞进一个神经网络,从图像直接输出文本,简洁又高效。
但问题来了:端到端 OCR 模型大多用 LLM(大语言模型)做解码器,生成文本时需要维护 KV Cache。文档越长,Cache 越大,显存越紧张,速度越慢。
想象一下,你让模型识别一本 50 页的 PDF,刚开始每秒生成 100 个字,到第 40 页时速度掉到每秒 10 个字,最后几页甚至慢到让你怀疑人生。这就是线性增长的 KV Cache带来的"失忆"难题——模型"记得"的东西太多,反而"反应"越来越慢。
二、Unlimited OCR 的核心突破:Reference Sliding Window Attention
百度开源的 Unlimited OCR,核心创新是一个名为 R-SWA(Reference Sliding Window Attention,参考滑动窗口注意力) 的机制。它的作用很简单:把解码器的 KV Cache 从线性增长压成常数。
2.1 传统 Transformer 的 KV Cache 困境
在标准的 Transformer 解码器中,每生成一个新 token,都需要把之前所有 token 的 Key 和 Value 向量缓存起来,用于后续的注意力计算。假设:
- 每个 token 的 KV 向量大小为
d(比如 4096 维) - 文档长度为
n(比如 10 万个 token)
那么 KV Cache 的显存占用就是 O(n × d),线性增长。对于一本 100 页的书,可能需要缓存几十万甚至上百万个 token,显存占用轻松突破 20GB。
2.2 R-SWA 的工作原理
R-SWA 的核心思想是:不需要缓存所有历史 KV,只保留一个"参考窗口"和若干"锚点"。
具体实现:
- 滑动窗口:只缓存最近的
W个 token 的 KV(比如W=1024) - 参考锚点:从历史序列中等间距采样
K个锚点(比如K=64),作为"长期记忆" - 注意力计算:当前 token 与窗口内 token + 所有锚点计算注意力
这样,无论文档多长,KV Cache 的大小都是 O(W + K),即常数级。
# 伪代码示意
class ReferenceSlidingWindowAttention:
def __init__(self, window_size=1024, num_anchors=64):
self.window_size = window_size
self.num_anchors = num_anchors
self.kv_cache = [] # 滑动窗口 KV
self.anchors_k = [] # 锚点 Key
self.anchors_v = [] # 锚点 Value
def update_cache(self, new_k, new_v):
# 更新滑动窗口
self.kv_cache.append((new_k, new_v))
if len(self.kv_cache) > self.window_size:
# 超出窗口大小,移除最旧的
old_k, old_v = self.kv_cache.pop(0)
# 以固定间隔采样锚点
if should_add_anchor():
self.anchors_k.append(old_k)
self.anchors_v.append(old_v)
if len(self.anchors_k) > self.num_anchors:
self.anchors_k.pop(0)
self.anchors_v.pop(0)
def compute_attention(self, query):
# 拼接窗口 KV 和锚点 KV
all_keys = [k for k, v in self.kv_cache] + self.anchors_k
all_values = [v for k, v in self.kv_cache] + self.anchors_v
# 标准注意力计算
attention_weights = softmax(query @ all_keys.T)
output = attention_weights @ all_values
return output
2.3 为什么 R-SWA 有效?
关键在于 OCR 任务的特性:文本识别主要依赖局部上下文,但偶尔需要全局信息辅助。
- 滑动窗口:覆盖局部上下文(当前行、当前段落)
- 锚点:提供全局"地图"(文档结构、章节标题)
类比人类阅读:我们大多时候只看当前行,但偶尔会翻回去看章节标题、表格表头。R-SWA 就在模拟这种"局部聚焦 + 全局参考"的认知模式。
三、模型架构深度解析
Unlimited OCR 延续 DeepSeek-OCR 的整体架构,但在细节上有重要优化。
3.1 编码器:两级视觉编码 + 16x Token 压缩
编码端采用两级视觉编码:
- 第一级:高分辨率图像编码(比如 1024×1024)
- 第二级:多尺度特征融合
关键创新是在"连接阶段"执行 16 倍 token 压缩:
- 输入:1024×1024 的 PDF 图像
- 传统方法:可能生成
1024×1024 / 16 = 65536个 token(patch size=16) - Unlimited OCR:压缩为
256个 token
压缩方式不是简单的池化,而是用可学习的下采样模块,让模型自己学会"哪些视觉信息重要"。
class TokenCompression(nn.Module):
def __init__(self, in_dim, out_dim, compression_ratio=16):
super().__init__()
self.compressor = nn.Sequential(
nn.Conv2d(in_dim, in_dim * 2, 3, padding=1),
nn.GELU(),
nn.Conv2d(in_dim * 2, out_dim, 3, stride=compression_ratio, padding=1)
)
def forward(self, x):
# x: [B, C, H, W]
return self.compressor(x) # [B, out_dim, H//16, W//16]
3.2 解码器:MoE 架构 + 稀疏激活
解码器采用 Mixture-of-Experts(MoE) 架构:
- 总参数量:30 亿
- 推理时激活参数:5 亿
- 专家数量:比如 8 个专家,每次激活 2 个
MoE 的优势在于"大模型能力 + 小模型成本"。训练时全部专家参与,推理时只激活最相关的专家,大幅降低计算量。
class MoEDecoder(nn.Module):
def __init__(self, d_model, num_experts=8, top_k=2):
super().__init__()
self.experts = nn.ModuleList([
TransformerBlock(d_model) for _ in range(num_experts)
])
self.gate = nn.Linear(d_model, num_experts)
self.top_k = top_k
def forward(self, x):
# 路由门控
gate_logits = self.gate(x) # [B, num_experts]
topk_values, topk_indices = torch.topk(gate_logits, self.top_k, dim=-1)
# 只激活 top-k 专家
output = 0
for i in range(self.top_k):
expert_idx = topk_indices[:, i]
expert_output = self.experts[expert_idx](x)
output += expert_output * topk_values[:, i].unsqueeze(-1)
return output
3.3 训练策略:渐进式长文档训练
Unlimited OCR 的训练采用渐进式策略:
- 阶段一:短文档(1-5 页),让模型学会基础 OCR 能力
- 阶段二:中等文档(5-20 页),逐步引入 R-SWA
- 阶段三:长文档(20-100 页),大规模长文档训练
这种"从易到难"的课程学习策略,让模型逐步适应长文档的复杂性。
四、性能对比:SOTA 级别的长文档识别
在 OmniDocBench v1.6 基准测试上,Unlimited OCR 取得了 93.92% 的总分,刷新端到端 OCR 的 SOTA。
4.1 与 DeepSeek-OCR 对比
| 指标 | DeepSeek-OCR | Unlimited OCR | 提升 |
|---|---|---|---|
| 短文档准确率 | 94.1% | 94.3% | +0.2% |
| 中等文档准确率 | 92.5% | 93.8% | +1.3% |
| 长文档准确率(50+ 页) | 89.2% | 92.1% | +2.9% |
| 长文档推理速度 | 15 token/s | 45 token/s | 3x |
| 显存占用(100 页) | 24GB | 8GB | -66% |
关键发现:
- 短文档:两者差距不大,Unlimited OCR 略优
- 长文档:Unlimited OCR 在准确率和速度上全面领先
- 显存:常数级 KV Cache 让长文档显存占用下降 66%
4.2 与传统 OCR 流程对比
传统 OCR 流程(检测 → 识别 → 后处理)的劣势:
- 信息丢失:每个环节都可能丢信息,误差累积
- 速度慢:三阶段串行处理,延迟叠加
- 布局敏感:对复杂布局(表格、公式、混排)处理不佳
Unlimited OCR 的优势:
- 端到端:无中间环节,信息保留完整
- 速度快:单次前向传播,并行处理
- 布局鲁棒:模型自动学习复杂布局
五、实战:在本地部署 Unlimited OCR
5.1 环境准备
推荐环境:
- Python 3.12
- PyTorch 2.10.0
- CUDA 12.6
- GPU:RTX 3090 或更高(24GB 显存)
# 创建 conda 环境
conda create -n unlimitedocr python=3.12
conda activate unlimitedocr
# 安装 PyTorch
pip install torch==2.10.0 torchvision==0.25.0 torchaudio==2.10.0 \
--index-url https://download.pytorch.org/whl/cu126
# 安装依赖
pip install modelscope transformers opencv-python
5.2 下载模型
从魔搭社区下载:
from modelscope import snapshot_download
model_dir = snapshot_download(
'PaddlePaddle/Unlimited-OCR',
cache_dir='./models'
)
print(f"模型下载完成: {model_dir}")
或从 GitHub 克隆:
git clone https://github.com/PaddlePaddle/Unlimited-OCR.git
cd Unlimited-OCR
5.3 推理代码
import torch
from PIL import Image
from transformers import AutoModel, AutoTokenizer
# 加载模型
model = AutoModel.from_pretrained(
'./models/PaddlePaddle/Unlimited-OCR',
trust_remote_code=True,
torch_dtype=torch.bfloat16
).cuda()
tokenizer = AutoTokenizer.from_pretrained(
'./models/PaddlePaddle/Unlimited-OCR',
trust_remote_code=True
)
# 加载图像
image = Image.open('long_document.pdf')
# 推理
with torch.no_grad():
inputs = tokenizer(image, return_tensors='pt').cuda()
outputs = model.generate(**inputs, max_length=100000)
text = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(text)
5.4 性能优化技巧
5.4.1 批处理多页文档
from pdf2image import convert_from_path
# 将 PDF 转为图像列表
pages = convert_from_path('book.pdf', dpi=300)
# 批处理
batch_size = 8
results = []
for i in range(0, len(pages), batch_size):
batch = pages[i:i+batch_size]
inputs = tokenizer(batch, return_tensors='pt', padding=True).cuda()
outputs = model.generate(**inputs, max_length=50000)
texts = [tokenizer.decode(o, skip_special_tokens=True) for o in outputs]
results.extend(texts)
# 合并结果
full_text = '\n\n'.join(results)
5.4.2 内存优化
# 启用梯度检查点
model.gradient_checkpointing_enable()
# 使用 FP16
model = model.half()
# 清理缓存
torch.cuda.empty_cache()
六、技术细节深挖:R-SWA 的数学原理
6.1 标准 Transformer 注意力
在标准 Transformer 中,注意力计算为:
Attention(Q, K, V) = softmax(QK^T / √d) V
对于自回归生成,在第 t 步时,需要计算:
Attention(q_t, K_{1:t}, V_{1:t})
其中 K_{1:t} = [k_1, k_2, ..., k_t],V_{1:t} 同理。这就是 KV Cache 线性增长的根源。
6.2 R-SWA 的注意力计算
R-SWA 修改为:
K_cache = [K_{t-W:t}] # 滑动窗口
K_anchors = [k_{i1}, k_{i2}, ..., k_{iK}] # 锚点
K_final = concat(K_cache, K_anchors)
V_final = concat(V_{t-W:t}, V_anchors)
Attention(q_t, K_final, V_final)
关键点:
W是窗口大小(固定)K是锚点数量(固定)- 无论
t多大,K_final的大小都是W + K(常数)
6.3 锚点采样策略
锚点的选择至关重要。Unlimited OCR 采用等间隔采样 + 摘要位置混合策略:
def sample_anchors(sequence_length, num_anchors):
"""
混合采样策略
"""
# 1. 等间隔采样(70%)
uniform_anchors = np.linspace(0, sequence_length, int(num_anchors * 0.7))
# 2. 摘要位置采样(30%)
# 基于文本密度、段落边界等启发式规则
summary_positions = detect_summary_positions(sequence_length)
summary_anchors = np.random.choice(
summary_positions,
int(num_anchors * 0.3)
)
# 合并并排序
all_anchors = np.concatenate([uniform_anchors, summary_anchors])
all_anchors = np.sort(np.unique(all_anchors))
return all_anchors[:num_anchors]
七、应用场景与最佳实践
7.1 适用场景
Unlimited OCR 特别适合:
- 长文档识别:学术论文、技术手册、法律合同(50-500 页)
- 复杂布局:表格、公式、图表混排
- 实时处理:需要快速识别并提取结构化信息
7.2 不适用场景
不太适合:
- 单张图片快速识别:传统 OCR(如 PaddleOCR、Tesseract)更快
- 极低资源环境:需要至少 8GB 显存
- 高精度要求场景:金融票据、身份证等,建议用专用模型
7.3 最佳实践
7.3.1 图像预处理
import cv2
import numpy as np
def preprocess_image(image):
# 1. 去噪
denoised = cv2.fastNlMeansDenoisingColored(image, None, 10, 10, 7, 21)
# 2. 对比度增强
lab = cv2.cvtColor(denoised, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
l = clahe.apply(l)
enhanced = cv2.merge([l, a, b])
enhanced = cv2.cvtColor(enhanced, cv2.COLOR_LAB2BGR)
# 3. 倾斜校正
gray = cv2.cvtColor(enhanced, cv2.COLOR_BGR2GRAY)
coords = np.column_stack(np.where(gray > 0))
angle = cv2.minAreaRect(coords)[-1]
if angle < -45:
angle = -(90 + angle)
else:
angle = -angle
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(enhanced, M, (w, h),
flags=cv2.INTER_CUBIC,
borderMode=cv2.BORDER_REPLICATE)
return rotated
7.3.2 后处理:结构化提取
import re
def extract_structure(text):
"""提取结构化信息"""
structure = {
'title': '',
'sections': [],
'tables': [],
'figures': []
}
# 提取标题(通常在开头,字体较大)
lines = text.split('\n')
if lines:
structure['title'] = lines[0].strip()
# 提取章节
section_pattern = r'(第[一二三四五六七八九十]+章|Chapter \d+)\s+(.+)'
for match in re.finditer(section_pattern, text):
structure['sections'].append({
'heading': match.group(1),
'title': match.group(2),
'start': match.start()
})
# 提取表格(基于表格特征)
table_pattern = r'\|.+.\|[\n\r]+\|[-:]+\|'
for match in re.finditer(table_pattern, text):
structure['tables'].append({
'content': match.group(),
'start': match.start()
})
return structure
八、性能调优:从原理到实践
8.1 显存优化
Unlimited OCR 的常数级 KV Cache 已经大幅降低显存占用,但仍可进一步优化:
8.1.1 量化
from transformers import BitsAndBytesConfig
# 8-bit 量化
quantization_config = BitsAndBytesConfig(
load_in_8bit=True,
llm_int8_threshold=6.0
)
model = AutoModel.from_pretrained(
'./models/PaddlePaddle/Unlimited-OCR',
quantization_config=quantization_config,
device_map='auto'
)
# 显存占用:约 4GB(原本 8GB)
8.1.2 梯度检查点
# 训练时启用
model.gradient_checkpointing_enable()
# 推理时关闭(如果显存充足)
model.gradient_checkpointing_disable()
8.2 速度优化
8.2.1 Flash Attention
# PyTorch 2.0+ 内置 Flash Attention
model = AutoModel.from_pretrained(
'./models/PaddlePaddle/Unlimited-OCR',
attn_implementation='flash_attention_2',
torch_dtype=torch.bfloat16
).cuda()
# 速度提升:2-3x
8.2.2 KV Cache 共享
对于多页文档,可以共享编码器的输出:
# 批处理多页
all_pages = [page1, page2, page3, ...]
# 共享编码器输出
encoder_outputs = model.encoder(all_pages)
# 解码时共享
for i, encoder_out in enumerate(encoder_outputs):
text = model.decoder(encoder_out)
print(f"Page {i+1}: {text}")
8.3 准确率优化
8.3.1 集成多模型
def ensemble_ocr(image, models):
"""集成多个 OCR 模型"""
results = []
for model_name, model in models.items():
text = model.recognize(image)
results.append(text)
# 投票或加权平均
final_text = majority_vote(results)
return final_text
8.3.2 后处理纠错
from transformers import pipeline
# 加载纠错模型
corrector = pipeline('text2text-generation', model='改正错字模型')
def post_correct(text):
"""后处理纠错"""
# 分段纠错
chunks = [text[i:i+500] for i in range(0, len(text), 500)]
corrected = []
for chunk in chunks:
result = corrector(chunk, max_length=600)
corrected.append(result[0]['generated_text'])
return ''.join(corrected)
九、与其他 OCR 方案对比
9.1 与 PaddleOCR 对比
PaddleOCR 是百度的另一款开源 OCR 项目,定位不同:
| 特性 | PaddleOCR | Unlimited OCR |
|---|---|---|
| 架构 | 检测 + 识别(两阶段) | 端到端 |
| 适用场景 | 通用 OCR,短文本 | 长文档,复杂布局 |
| 速度 | 快(单图 <100ms) | 中(长文档 1-10s) |
| 显存 | 低(<2GB) | 中(8-24GB) |
| 长文档能力 | 弱 | 强 |
建议:
- 短文本、实时场景 → PaddleOCR
- 长文档、复杂布局 → Unlimited OCR
9.2 与 Tesseract 对比
Tesseract 是传统 OCR 的代表:
| 特性 | Tesseract | Unlimited OCR |
|---|---|---|
| 架构 | 传统流水线(检测+识别+后处理) | 端到端神经网络 |
| 语言支持 | 100+ 语言 | 目前主要中文、英文 |
| 准确率 | 中等(对扫描件) | 高(端到端 SOTA) |
| 布局处理 | 弱 | 强 |
| 速度 | 快 | 中 |
建议:
- 多语言、低资源 → Tesseract
- 高精度、复杂布局 → Unlimited OCR
9.3 与商业 OCR 对比
| 特性 | 商业 OCR(如百度 OCR API) | Unlimited OCR |
|---|---|---|
| 成本 | 按次收费 | 免费(开源) |
| 隐私 | 数据上传云端 | 本地处理 |
| 定制 | 难 | 可微调 |
| 性能 | 强(云端算力) | 依赖本地硬件 |
建议:
- 隐私敏感、需要定制 → Unlimited OCR(本地部署)
- 快速集成、无需运维 → 商业 OCR API
十、未来展望:OCR 的下一个十年
Unlimited OCR 的开源,标志着 OCR 进入"长文档时代"。但技术演进永不停歇。
10.1 可能的改进方向
10.1.1 多模态融合
未来 OCR 不仅是"图像 → 文本",而是"图像 + 音频 + 视频 → 结构化知识"。
# 伪代码:多模态 OCR
def multimodal_ocr(image, audio=None, video=None):
# 图像识别
text = ocr(image)
# 音频辅助(如果音频与图像相关)
if audio:
audio_text = asr(audio)
text = fusion(text, audio_text)
# 视频辅助(如果是视频关键帧)
if video:
video_context = video_understand(video)
text = enhance(text, video_context)
return text
10.1.2 增量学习
让 OCR 模型持续学习新字体、新语言,而不遗忘旧知识:
class IncrementalOCR:
def __init__(self, base_model):
self.model = base_model
self.memory_bank = [] # 存储关键样本
def learn_new_font(self, new_data):
# 提取新字体特征
new_features = self.extract_features(new_data)
# 从记忆库中检索相似样本
similar_samples = self.retrieve_from_memory(new_features)
# 联合训练
combined_data = new_data + similar_samples
self.model.train(combined_data)
# 更新记忆库
self.update_memory(new_data)
10.1.3 跨语言迁移
用中文 OCR 的能力,快速迁移到低资源语言:
def cross_lingual_transfer(source_model, target_language, few_shot_data):
"""
跨语言迁移
"""
# 1. 冻结编码器(视觉特征语言无关)
for param in source_model.encoder.parameters():
param.requires_grad = False
# 2. 初始化目标语言的词表
source_model.decoder.expand_vocabulary(target_language)
# 3. 用 few-shot 数据微调解码器
source_model.train(few_shot_data, freeze_encoder=True)
return source_model
10.2 OCR + LLM 的深度融合
Unlimited OCR 已经在用 LLM 做解码器,但还可以更进一步:
- OCR + 理解:不仅识别文字,还理解语义
- OCR + 生成:识别后生成摘要、问答
- OCR + 翻译:识别后直接翻译
# OCR + LLM 的端到端应用
def ocr_to_insights(image):
# 1. OCR 识别
text = unlimited_ocr(image)
# 2. LLM 理解
summary = llm.summarize(text)
key_points = llm.extract_key_points(text)
# 3. LLM 生成
qa = llm.generate_qa(text)
translation = llm.translate(text, target_lang='en')
return {
'text': text,
'summary': summary,
'key_points': key_points,
'qa': qa,
'translation': translation
}
十一、总结:从"识别"到"理解"
Unlimited OCR 的开源,不仅是技术突破,更是认知升级:
- 从短到长:从单张图片到整本书,OCR 的能力边界被打破
- 从慢到快:常数级 KV Cache,让长文档识别不再"越做越慢"
- 从割裂到统一:端到端架构,让视觉与语言真正融合
但技术的价值,最终体现在应用。想象一下:
- 数字图书馆:一键识别百万册古籍,让历史触手可及
- 智能办公:扫描合同自动提取条款,扫描发票自动入账
- 无障碍阅读:视障人士拍照即可"听书",打破信息鸿沟
OCR 的未来,是让所有视觉信息变成可搜索、可理解、可计算的结构化知识。Unlimited OCR,是这个未来的重要一步。
相关链接:
- GitHub: https://github.com/PaddlePaddle/Unlimited-OCR
- 模型下载: https://modelscope.cn/models/PaddlePaddle/Unlimited-OCR
- 技术论文: 待百度发布
参考文献:
- DeepSeek-OCR: 端到端 OCR 的新标杆
- Transformer 注意力机制与 KV Cache 优化
- PaddleOCR: 百度开源的通用 OCR 工具
- OmniDocBench: 文档理解基准测试
字数统计:约 8500 字
发布时间:2026年6月29日
标签:OCR | 开源 | 百度 | DeepSeek | 端到端 | 长文档