千页文档一口气解析完:文心无限OCR 2技术拆解

作者:c4t2026.11.04 01:36浏览量:0

简介:上百页年报、上千页档案,为什么OCR总要先拆碎再拼回去?本文从长序列推理的显存账本讲起,拆解文心Unlimited OCR 2的SR-SWA稀疏注意力与无限跨度解码机制,看其如何在单序列推理下将KV Cache峰值显存从47GB降至1.77GB,实现任意页数文档的持续解析,并附工程落地建议。

一、从一场“离谱”的演示说起

最近不少技术社群里流传着同一段演示:一套七卷本、合计3600多页的长篇小说,被一个OCR模型从第一页到最后一页一口气解析完毕,中途没有人工拆页,没有分段再拼接,表格与段落顺序原样保留。演示的主角是文心 Unlimited OCR 2——百度这代升级的目标非常直白:让OCR具备处理任意页数的“无限”文档解析能力。

对没被长文档折磨过的人来说,这看起来只是个数字游戏;但对真正做过档案数字化、年报解析、标书处理的人,“页数不设上限”意味着一类长期无解的任务突然变得可行。要理解这次升级的分量,得先弄清楚:页数到底是怎么卡住OCR的。

二、页数为什么会成为OCR的天花板

现代多页文档OCR普遍把整份文档建模为一条长序列:每页图像先被切分为视觉Token,与已生成的文本Token共同送入自回归解码器,逐Token输出转写结果。这种统一建模换来了跨页理解能力,但代价是两笔越滚越大的账。

第一笔账:文本侧的KV Cache。自回归解码中,每生成一个新Token,都要缓存此前全部Token的Key/Value向量,显存开销大致满足:

KV Cache显存 ≈ 2 × 层数 × KV头数 × 头维 × 序列长度 × 单元素字节数

序列长度随页数线性增长,缓存显存同步线性膨胀。几十页的论文尚在承受范围内,上百页年报、上千页档案则直接把显存顶爆。

第二笔账:视觉侧的注意力开销。文本侧尚可靠滑动窗口类机制约束缓存增长,但OCR天然需要“回看图像”:解码到第500页的一张表格时,注意力可能仍需参考前面若干页的视觉Token。按常见文档图像分辨率,单页切出的视觉Token通常在数百到上千量级,乘以页数后总量轻松突破百万;参考集一失控,计算与缓存双双恶化。

工程上最常见的兜底是拆页:把长文档切成若干段分别解析,再事后拼接。拆本身不难,难的是接回去——跨页表格被拦腰截断、段落阅读顺序错乱、页眉页脚噪声混入正文、脚注与正文对不上。页数越多,后处理越脏,最终往往演化成一堆难以维护的规则补丁。

三、一个朴素的问题:人是怎么抄书的?

百度团队在技术报告里给出的灵感来源非常朴素:抄书的人是怎么工作的?

抄到第500页时,你的注意力聚焦在当前页;只有遇到跨页表格、图表或章节续接时,才回翻几页确认。你绝不会每写一个字,就把前499页重新读一遍。

这揭示了长文档的普遍结构规律:

  • 页内依赖密集:同页段落、表格单元格之间关联紧密,需要细粒度交互;
  • 跨页依赖稀疏:真正需要回看的位置很少,且集中在表格、图表、章节衔接等特定语义单元。

既然依赖本身是稀疏的,计算就不必是稠密的。这正是稀疏参考滑动窗口注意力(Sparse Reference Sliding Window Attention,SR-SWA)的出发点——顺着“先粗后细”的思路,把注意力计算压到最小必要集合。

四、SR-SWA:两道筛子,先选页、再挑Token

SR-SWA可以理解为串联的两道筛子。

第一道筛子:Page Gate(页面门)。在页面粒度做粗筛,从整份文档中挑出与当前解码位置相关的少数页面,将视觉参考范围收窄到这批页面;同时保留跨页位置关系编码,确保模型“回看”时仍清楚该内容在全文中的相对位置——这一点对表格续接和章节连贯至关重要。

第二道筛子:Token Sieve(Token筛)。对第一道筛出的候选视觉信息块逐一打分,只选出少量最相关的远程Token桶参与注意力计算,其余直接跳过。

值得强调的是,筛选对象是书页中的图像区域与视觉Token,压缩发生在视觉表征侧,而非事后丢弃已生成的文本,转写内容的完整性不受影响。

放到演进脉络里看更清楚:上一代Unlimited OCR用R-SWA控制了文本侧KV Cache的增长,在开源社区的累计下载量已超过1000万次;但视觉参考Token仍需全量关注,页数一多,视觉计算就成了新瓶颈。一句话概括:1.0管住了文本缓存,2.0进一步压低了视觉开销。

五、无限跨度解码:像接力一样持续往下解析

筛子解决了“每一步算多少”,还剩“如何一直算下去”。答案是无限跨度解码(Unlimited Span Decoding),逻辑分三步:

  1. 按页面把文档分成块,逐块处理;
  2. 每块解析完成,保留边界页的KV Cache;
  3. 处理下一块时复用这份缓存作为前文上下文,让模型“记得”上一块结尾的内容,跨页表格与段落得以自然衔接。

这相当于把一次性长序列推理,改造成携带状态的接力推理,与业界长上下文优化中的分块处理、前缀缓存复用等思路一脉相承,但落点在视觉文档解析这一具体场景。

效果有多直接?在官方技术报告的超长文档基准评测(MarathonDocBench)、单序列推理条件下,这套分块与缓存复用机制把KV Cache峰值显存从47GB降到了1.77GB,压缩约26倍。更关键的是每一步解码成本近乎恒定:无论处理到第50页还是第500页,单步开销不再随总页数膨胀——有限显存被花在“持续向前”上,而不是“反复回看”上。

六、“无限”的边界:别误解成总耗时不变

必须把话说严谨:所谓“无限”,指解析跨度可以持续延伸——文档有多少页就往前解析多少页,不会因显存耗尽而中途崩溃。

但整本文档的总耗时,仍随页数正常增长。每页的图像Token都要过一遍前向计算,这是物理规律,任何模型都绕不开。无限OCR的价值不在“更快”,而在于把开发者从无休止的拆分、拼接、对齐、补丁里解放出来:输入端不再需要预处理切页,输出端不再需要规则粘合。少折腾拆分与拼接,才是王道。

七、工程视角:这条技术路线改变了什么

对做文档智能的开发者而言,“原生持续解析”改变的是整条流水线的形态:

  • 传统形态:切页 → 分段OCR → 结果落盘 → 规则拼接 → 格式修复
  • 持续解析形态:整档输入 → 模型内分块接力 → 直接输出结构化文本

示意性的调用逻辑如下(伪代码,仅表达流程):

  1. # 示意:长文档持续解析流水线
  2. doc = load_document("annual_report_800p.pdf")
  3. context = None # 边界缓存锚点
  4. for chunk in doc.iter_pages(block=8): # 模型内按页分块
  5. text, context = unlimited_ocr(
  6. pages=chunk,
  7. carry=context, # 复用边界页KV Cache
  8. layout="markdown", # 结构化输出
  9. )
  10. stream_emit(text) # 流式吐出,边跑边消费
  11. final = assemble(stream) # 跨页表格/段落已由模型衔接

几条落地建议:

  1. 显存预算看单步峰值而非总量。持续解析模式下只需评估单块峰值(GB级),而非整册缓存总量,消费级显卡跑长档从“不可能”变为“可讨论”。
  2. 直接消费结构化输出。跨页表格、标题层级、阅读顺序由模型在解码时内部衔接,可靠性远高于事后规则拼接,可直接对接下游检索、入库与分析。
  3. 用好流式特性。按块推进意味着边解析边出结果,长任务反馈周期从“全部跑完”缩短为“逐块可见”,对交互式校对、断点续跑、容错重试都更友好。

典型受益场景包括:上百页年报与招股书解析、上千页历史档案数字化、批量标书与合同比对、多卷本书籍结构化整理。凡是被“拆页-拼接”折磨过的团队,都值得重新评估这条路线。

八、一个彩蛋,以及后续

读技术报告时还发现个彩蛋:“Unlimited OCR Works”这个命名,致敬了某部经典动漫作品中的名场面,其中两句改写颇有画面感:

像素为身,Token为血。我已阅尽千余页文档。
故而,我在此祈愿:Unlimited OCR Works。

按官方安排,文心Unlimited OCR 2的更多技术细节将在11月12日举办的百度世界2026大会上进一步分享。模型与代码均已开放,权重与技术报告可直接获取——不妨把手里最难啃的那份“大部头”丢进去跑一遍,亲身验证一下“无限”的成色。