OCR 大模型(如 GPT-4V、Qwen-VL、Donut、Pix2Struct、LLaVA-Document 等)在处理多页文档(如 PDF、扫描册子、合同、报告)时,面临核心挑战:上下文长度限制(如 LLM 仅支持 4K--32K tokens)与跨页语义连贯性。
以下是当前主流解决方案,分为 预处理策略、模型架构优化、推理策略 三大类:
一、预处理策略:将多页文档"切"为模型可处理的单元
1. 按页独立处理(最常用)
-
做法:将每页作为独立图像输入模型,分别 OCR + 理解;
-
优点:简单、并行化、避免 token 溢出;
-
缺点:丢失跨页上下文(如表格跨页、脚注引用);
-
适用场景:发票、单页合同、独立表单。
-
优化:在 prompt 中加入页码和总页数
2.智能分块(Semantic Chunking)
-
做法:
-
用 Layout 分析(如 LayoutParser、DocLayNet)检测文档结构;
-
将逻辑连贯的内容块(如一个完整表格、一章正文)合并为"超级页";
-
仅当块过大时才拆分,并保留上下文提示。
-
-
优点:最大限度保留语义完整性;
-
代表系统:Google Document AI、Amazon Textract。
📌 示例:
-
一个 3 页的表格 → 合并为 1 个输入;
-
10 页小说 → 按章节分块。
3.关键页优先(Relevance Filtering)
-
做法:
-
先用轻量模型(如 CLIP)对所有页面做语义检索;
-
仅将与任务相关的页面送入大模型(如"提取签名" → 只处理最后一页);
-
-
优点:大幅减少计算量;
-
适用场景:长合同、法律文书、技术手册。
二、模型架构优化:让模型"记住"跨页信息
1.层次化建模(Hierarchical Modeling)
-
架构:
-
底层:每页用 ViT 编码 → 得到 page-level embeddings;
-
顶层:用 RNN / Transformer 对页面序列建模,生成文档级表示;
-
-
代表工作:DocFormer、LayoutLMv3(虽非纯大模型,但思想可迁移)。
🔁 推理时可缓存页面 embedding,避免重复计算。
2.外部记忆机制(External Memory)
-
做法:
-
用向量数据库(如 FAISS)存储每页的 embedding;
-
大模型在处理当前页时,动态检索相关历史页(如前一页、标题页);
-
-
优点:突破上下文长度限制;
-
代表框架:RAG(Retrieval-Augmented Generation) for Documents。
3. 端到端多页训练(如 Pix2Struct)
-
Pix2Struct(Google) 专为文档设计:
-
输入:多页拼接为"长图";
-
使用 sparse Transformer 处理超长序列;
-
在 DocVQA、InfographicVQA 等多页数据集上训练。
-
-
局限:仍受限于最大分辨率(通常 ≤ 2048px 高度)。
三、推理策略:后处理与融合
1. 结果融合(Result Aggregation)
-
做法:
-
各页独立输出结构化结果(如 JSON);
-
用规则/小模型合并跨页内容:
-
表格:拼接分页表格;
-
列表:合并编号列表;
-
实体:去重+关联(如"张三(第2页)是经理,负责第5页项目")。
-
-
-
工具:LangChain 的 Document 类、LlamaIndex。
2. 迭代 refinement(Reflexion)
-
做法:
-
第一轮:各页独立处理;
-
第二轮:将首轮结果作为上下文,重新处理关键页;
-
-
例如:发现"见第8页附录" → 主动 fetch 第8页再分析。
-
代表:Self-Refine、CRITIC 框架。
工业实践方案
|---------|-----------------------------------|
| 场景 | 推荐方案 |
| 跨页表格/表单 | 智能分块 + Pix2Struct / Donut 请给出详细方案 |
核心挑战
|--------|---------------------------------|
| 问题 | 说明 |
| 表格跨页断裂 | 表头在第1页,数据延续到第2--3页 |
| 表单字段分散 | "姓名"在第1页,"签名"在最后一页 |
| 版面复杂 | 多栏、嵌套表格、手写+印刷混合 |
| 模型输入限制 | Pix2Struct/Donut 最大分辨率 ~2048px |
整体架构

详细实现步骤
步骤1:文档预处理与版面分析
(1) PDF 转图像
(2) 版面分析
-
模型:DocLayNet(IBM)或 LayoutParser(Detectron2)
-
检测元素:
-
Table(表格区域)
-
Text(段落)
-
Title(标题)
-
List(列表)
-
-
输出:每页的版面结构 JSON
(3) 表格检测与跨页识别
-
表格检测:TableMaster(SOTA)或 PaddleOCR table detection
-
跨页判断
def is_cross_page_table(page1, page2, dpi=300):
"""
判断page1和page2是否属于同一跨页表格
Args:
page1, page2: 页面解析结果,需包含:
- tables: 检测到的表格列表
- image_height: 页面高度(pixel)
dpi: 文档分辨率
"""
# 获取最后1个表格(假设跨页表格在页尾)
table1 = page1.tables[-1] if page1.tables else None
table2 = page2.tables[0] if page2.tables else None # 假设跨页表格在页首if not table1 or not table2: return False # ===== 必要条件(必须全部满足)===== # 1. 位置连续性:table1在page1底部,table2在page2顶部 page1_bottom_margin = page1.image_height - table1.bbox[3] # table1底边距 page2_top_margin = table2.bbox[1] # table2顶边距 max_margin = int(50 * dpi / 300) # 50mm容差(适应不同DPI) if page1_bottom_margin > max_margin or page2_top_margin > max_margin: return False # 2. 列数一致性(核心特征) if abs(table1.col_count - table2.col_count) > 1: # 允许1列误差(合并单元格) return False # 3. 表格宽度一致性 width_diff = abs(table1.width - table2.width) if width_diff > max(20, 0.1 * table1.width): # 动态容差:20px或宽度10% return False # ===== 充分条件(满足任一即可)===== # 方案A:检测表格线连续性(需高质量渲染) if hasattr(table1, 'has_bottom_border') and not table1.has_bottom_border: return True # 方案B:内容连贯性(首选) if _is_row_continuation(table1.last_row, table2.first_row): return True # 方案C:语义特征(兜底) if (not table2.has_header and table2.row_count > 1 and _is_data_row(table2.first_row)): return True return Falsedef _is_row_continuation(row1, row2):
"""判断row2是否是row1的延续(基于内容模式)"""
# 规则1: 行高接近
if abs(row1.height - row2.height) > 5:
return False# 规则2: 列内容类型一致(如全是数字/文本) for cell1, cell2 in zip(row1.cells, row2.cells): if _get_cell_type(cell1.text) != _get_cell_type(cell2.text): return False return Truedef _get_cell_type(text):
"""简单类型推断"""
if text.replace(' ', '').isdigit():
return "number"
elif re.match(r'^[\u4e00-\u9fa5a-zA-Z\s]+$', text):
return "text"
return "mixed"def _is_data_row(row):
"""判断是否为数据行(非表头)"""
# 表头特征:包含"名称/项目/日期"等关键词,且无数字
header_keywords = ["名称", "项目", "日期", "编号", "类型"]
text = " ".join([cell.text for cell in row.cells])
has_keyword = any(kw in text for kw in header_keywords)
has_number = any(char.isdigit() for char in text)
return not (has_keyword and not has_number)
步骤2:智能分块与虚拟拼接
(1) 表格分组
-
输入:所有页面的表格检测结果
-
算法:
-
初始化:第1页表格为 group_1
-
迭代:对 page_i,检查与上一页表格是否跨页
-
是 → 归入同一 group
-
否 → 新建 group
-
-
输出:[group1=[p1,p2], group2=[p3], group3=[p4,p5,p6]]
(2) 虚拟长图拼接
-
目标:将跨页表格拼成单张"超长图"供模型处理
-
拼接策略:
非表格区域(干扰内容):
-
保留表头:仅第1页保留表头,后续页只拼接数据行
-
对齐列宽:以第1页为基准,调整后续页列宽
-
填充间隙:用白色填充非表格区域,避免干扰
-
页眉/页脚(如"第1页 共5页")
-
段落文字(如"根据保险法第XX条...")
-
公司Logo、水印
-
扫描噪点、装订孔阴影
-
步骤三:模型选择与推理
方案A:Pix2Struct(推荐)
-
优势:
-
专为结构化文档设计
-
原生支持表格 → HTML 输出
-
在 DocVQA、InfographicVQA 表现 SOTA
-
-
输入:拼接后的长图(≤2048px)
-
输出:HTML 表格(可转 CSV/JSON)
-
Prompt:"Extract the table as HTML."