首页 / 资讯中心 / 文章详情

金融表格抽取为何难?图神经网络GFTE如何破解结构关系难题

金融表格抽取为何难?图神经网络GFTE如何破解结构关系难题 ★ FEATURED ARTICLE
1. 金融表格抽取看着简单做起来头疼1.1 为什么金融表格比想象中难做文档智能的同学应该都有体会文字抽取、关键字段抽取现在用大模型或者成熟OCR工具都能跑个大概真正让人夜不能寐的往往是表格。尤其是金融场景下的表格——财报附注、招股书、审计报告、信贷审批材料、估值模型底稿——这些东西的版式精密数字密集一个单元格错位后面整条数据分析链路全废。Graph-based Financial Table ExtractionGFTE这个名字最早是我在一个模拟项目X里给整套表格解析方案起的代号后来发现这个思路比我想象中通用于是整理成了一套可以复用的方法。为什么金融表格这么难第一来源太杂。同样是三大报表有的来自PDF电子版有的来自扫描件有的来自Excel导出后转图片的版本。第二样式太多。明明是一张利润表有的带合计行有的带跨页重复表头有的把营业收入拆成主营业务收入和其他业务收入两个子行还有的把多个年份并列排放。第三数字信息密度极高一个解析错误会直接被下游财务模型放大。你抽一段文字抽错了人一眼能看出来你抽一张表错了一个单元格监控不仔细的话这个错误可能一直潜伏到最终报告里。更麻烦的是表格的语义不是靠看到什么字就能确定的。表格里每个单元格的位置、它和表头的关系、它属于哪个行/列分组、它是不是合并单元格的一部分这些信息全藏在排版结构里。传统的OCR只能给文本框和置信度给不出单元格之间的关联。GFTE要解决的恰恰就是这个藏在排版结构里的关系。1.2 GFTE解决的核心矛盾表格是关系不是像素我们一开始也走过弯路试图用规则去匹配表格线用模板去对齐固定版式。规则方案在某个单一版式上可以做到很高的准确率但一旦换一家券商、换一份招股书、换一个年份的披露格式规则就崩了。原因很简单规则描述的是某个版式长什么样而不是表格是什么。后来我们发现与其死磕版式不如换个角度表格本质上是一个由单元格节点和有向关系边组成的结构。行关系、列关系、合并关系、层次关系都是边单元格里的文本、坐标、字体、跨行跨列信息都是节点属性。如果能把表格抽取转换成预测单元格节点之间的关系的图学习问题那么版式差异就只是特征差异而不是规则差异。这就是GFTE的核心思路。它跟普通表格识别最大的区别在于不是先画格子再去填格子里的字而是先检测文本区域再用图神经网络判断哪些文本区域应该组成一个单元格、哪些单元格属于同一行/同一列、哪些单元格之间有父子层级关系。这个思路对金融表格特别有用因为金融表格经常没有完整的表格线而是靠缩进、字体、对齐方式来暗示结构。图模型天然容忍这种弱版面约束。2. 先想清楚表格在计算机里到底是什么2.1 表格结构的三层拆解要设计一个表格抽取模型首先得明确输出什么。我习惯把表格结构拆成三层内容层单元格里的原始文本包括数字、金额、百分比、注释。几何层每个单元格在页面上的边界框以及跨行跨列的合并信息。关系层单元格与单元格之间的行列归属、父子层级、表头与数据体的对应关系。很多OCR系统做到了内容层和几何层就收工了给出一堆带坐标的文本框。但在金融场景下关系层才是最有价值的。一个财务分析师要的不是页面上第320像素到450像素之间写着净利润而是净利润这个指标对应2023年这一列数值是1.2亿指标层级位于利润表第二层其上一级是营业利润。如果没有关系层前面的坐标和文本就是散沙。GFTE的输出形式我一般定义为以单元格为节点、以关系为边的表格图再从这个图无损还原成HTML表格、Markdown表格或者 DataFrame。这样做的好处是下游系统拿到的始终是结构化对象而不是看起来像表格的OCR结果。2.2 表格抽取的常见基线方法及短板为了说明GFTE的定位先列出几类常见方案以及我在实践中感受到的短板。方法类型基本思路典型问题纯规则/模板匹配按坐标区间或表格线交点切分单元格适应性差扫描件/无线表直接失效基于图像分割的方法用卷积网络分割单元格区域再做文字识别对合并单元格、无框表格鲁棒性一般基于序列到序列的方法把表格转成HTML或Markdown标记序列直接生成长表格生成误差累积结构一长就崩基于图的方法GFTE路线先检测单元格再用图网络预测关系关系预测精度是关键需要高质量标注数据模板匹配在金融数据抽取里仍然大量存在因为很多系统只处理固定的几家机构、固定的报表模板。但它的天花板很明显模板本质上是一种人工特征工程遇到新型披露格式就得重新写规则。分割方法的问题在于它把表格当作像素问题单元格合并信息rowspan/colspan经常被忽略模型看到的是一堆网格但真实表格往往不是规整网格。序列生成方法我也试过短表格效果不错但金融表格动辄二十列、四十行序列化之后标记很长一旦中间错一个标签后面全乱。图方法的直觉是表格里的单元格自然就是节点而同一行同一列合并父子这些关系就是边。这种表示不依赖表格线是否完整也不要求表格是矩形网格更接近表格的语义本质。2.3 为什么图模型适合表格单元格天然是节点我最早对图模型有顾虑金融表格的单元格数量通常只有几十到几百个图神经网络是不是有点杀鸡用牛刀后来实测下来反而觉得刚刚好。考虑一个简单的场景一张损益表第一行是营业收入下面四个子行分别列示主营业务收入、其他业务收入、营业成本、税金及附加。其中营业收入这个单元格在视觉上是合并单元格纵向跨了四行。在像素视角下它是一个不规则的矩形在序列视角下它对应的文本和子行文本的对应关系很难准确建模但在图视角下这只是一个边关系定义营业收入节点与四个子行节点之间的包含边以及营业收入与同行年份列单元格之间的对齐边。还有一类很常见的情况表格里某些单元格是空的但空单元格不是没有信息而是继承上方或左方的语义。比如资产负债表里的流动资产合计下面常常是空行占位后面才出现细分项。图模型处理这种结构比较自然因为空单元格可以被建模成孤立节点或者与相邻节点存在空间邻近边后续由后处理规则解释而不是像序列模型那样必须生成一个标记。从实现角度讲图模型还有一个隐藏优势方便融合不同模态的特征。文本特征可以来自OCR结果坐标特征可以来自版面分析字体、是否加粗、是否缩进都可以作为节点属性拼接在一起。多模态特征融合是图模型天然擅长的操作这一点在后面构建特征时体现得很明显。3. GFTE的系统设计与关键模块3.1 整体管线从PDF到结构化数据GFTE的完整管线可以概括为四个阶段文档预处理、单元格检测与OCR、图构建、关系推理与表格还原。预处理阶段处理的是PDF或扫描图片的物理问题页面旋转、插值、灰度化、倾斜校正、去噪。金融扫描件经常有印章、折痕、手写批注这些噪声如果不过滤很容易在后续OCR中被误识别成文字。OCR阶段不仅要输出文本还要输出每个文本行的精确坐标和置信度。单元格检测阶段需要判断哪些文本行应该属于同一个逻辑单元格——这在无边框表格里尤其重要后面我会专门展开。图构建阶段是GFTE的中间产物每个节点对应一个逻辑单元格边则由空间相邻关系、同行同列先验、版面分割线等启发式信息初始化。推理阶段使用图神经网络从初始边出发预测最终的单元格关系矩阵再解码成表格结构。听起来流程很长但在工程实现上每个模块都有成熟组件可以替换。GFTE的重点不在某一单独模块而在如何把表格结构建模成图并让模型学会关系推理。3.2 单元格检测与文本识别金融表格里的单元格和PDF里文本框不是一回事。很多时候一整个单元格的内容会被OCR拆成三四个文本框比如营业收入万元在扫描件里可能被识别成两个文本块反过来一个文本框也可能跨越两个逻辑单元格。所以我把单元格检测定义为对OCR文本框做聚类聚成逻辑单元格。聚类依据有哪些第一坐标距离。相邻文本框在垂直方向距离很小、水平方向有重叠大概率属于同一单元格。第二字体特征。同一单元格内的文本通常字体大小、颜色、字重接近。第三阅读顺序。考虑到中文金融表格常见的纵向表头、横向表头交叉单纯从左到右、从上到下排序是不够的要结合表格语义。第四置信度。OCR置信度低的区域需要更保守的聚类避免把噪声并进单元格。这个阶段我踩过最大的坑是表格里的合并单元格在视觉上是一个大矩形但里面的文本可能横跨在中间偏左位置周围大片空白。如果聚类逻辑只按坐标距离很容易把合并单元格里空白的部分当成无文本区域从而忽略这个单元格的存在。所以在聚类时我会额外使用版面分析中检测到的分隔线、底色块、边框信息作为辅助信号。这些信号在金融表格里很常见不能浪费。3.3 图构建节点特征、边关系与邻接矩阵这是GFTE最核心的环节。节点特征我通常拼接以下几类文本特征单元格文本经过编码器得到的向量。金融数字很多建议在词表中保留数字模式或将数字归一化为特殊标记。几何特征归一化中心坐标、宽高、上下左右边界与页面边界的距离。版面特征是否加粗、是否含货币符号、是否含百分号、文字数量、平均字号。上下文特征同一页中前后节点的文本嵌入用来辅助判断表头语义。边关系分为两类。第一类是空间邻近边每个节点连接自己周围K个最近邻节点。这是图计算的基础边集保证信息能够在局部传播。第二类是结构化先验边比如同一检测行内左右相邻的节点、同一检测列内上下相邻的节点、被表格线分隔但纵向对齐的节点。结构化先验边可以看成是弱规则注入帮助图网络更快收敛。这里给出一个简化的图构建伪代码思路比代码本身更重要import torch from torch_geometric.data import Data def build_table_graph(cells): num_nodes len(cells) edge_index [] edge_type [] for i, a in enumerate(cells): for j, b in enumerate(cells): if i j: continue # 空间近邻中心欧式距离小于阈值 if a.center_distance(b) spatial_threshold: edge_index.append([i, j]) edge_type.append(0) # 同行先验垂直中心接近水平不重叠 if a.same_row_bias(b): edge_index.append([i, j]) edge_type.append(1) # 同列先验水平中心接近垂直不重叠 if a.same_col_bias(b): edge_index.append([i, j]) edge_type.append(2) return Data( xtorch.tensor([cell.feature for cell in cells]), edge_indextorch.tensor(edge_index, dtypetorch.long).t().contiguous(), edge_typetorch.tensor(edge_type) )边类型非常重要因为同行关系和同列关系在金融表格中语义完全不同不能混在一起让模型自己猜。金融表格经常出现数字对齐、跨列合并如果不区分边类型模型很可能把水平相邻的单元格之间的关系和垂直相邻的关系搞混。3.4 图神经网络的推理与单元格合并预测在这个阶段模型要做三件预测任务节点属性预测、边类型预测、合并单元格预测。我采用的骨干网络是一个异构图注意力网络根据边类型聚合邻居信息。每一层会分别聚合空间近邻、同行边、同列边的消息再通过注意力机制融合。这样做的好处是模型对行的语义和列的语义分别建模。金融表格中表头通常在列方向扩张指标名通常在行方向展开二者通过交叉位置形成数值单元格。如果不区分行列聚合这种交叉语义很难学出来。边类型预测是GFTE的关键输出。对于任意两个节点模型输出它们属于同一单元格内部同一行同一列父子包含或无关系的概率。其中父子包含这个关系对金融表格的层级结构非常重要比如流动资产合计包含货币资金、应收账款等子项。这个关系不能单靠文本判断有时候货币资金在缩进上比流动资产合计多两个字符有时候完全靠字体大小区分图模型可以通过节点上下文特征学到这种隐含规则。合并单元格预测上我用的是单元格边界框回归合并标签分类的组合。具体来说模型对每个节点预测它向右合并的宽度、向下合并的高度以及是否作为合并单元格的左上角起点。这个设计参考了目标检测里的anchor思想但把anchor换成了合并偏移量对金融表格常见的跨行表头特别有效。3.5 解码输出把图还原成表格模型预测得到的是概率矩阵还不能直接当表格用。解码阶段的思路是先根据同一行/同一列的预测边把节点聚类成行列分组再根据父子包含边在行分组内部构建层级树最后结合合并单元格偏移量还原每个节点的rowspan和colspan。解码时我强烈建议做一步行列坐标排序而不是单纯按置信度取边。金融表格的行列顺序本身是有语义的表头的出现顺序、指标的顺序都不能乱。我们会用拓扑排序把同行节点按照从左到右排序同列节点按照从上到下排序再根据父子层级关系决定谁是一级表头、谁是二级表头。排序完成后表格就可以无损转换成HTML字符串或者DataFrame。解码这一步看着简单实则是错误高发区。最典型的问题是预测边带出现互相矛盾比如A和B预测为同行B和C预测为同列形成冲突。我的处理方式是引入一个简单的规则层优先保留置信度最高的边同时在行列冲突时参考几何位置如果两个节点的中心坐标几乎重合则优先认为是同一单元格内部关系而不是同行关系。这个规则层能过滤掉大约百分之十几的明显噪声大大减少下游表格错乱。4. 数据、训练与评估里的那些坑4.1 训练数据标注与合成策略GFTE需要训练数据包含三个层面的标注单元格边界框、单元格文本、单元格关系图。纯人工作业成本极高一张复杂表格标注半小时是常态。我的经验是三层结合真实数据精标、半自动预标注、合成表格补充。半自动预标注可以这样做先用成熟OCR工具抽文本再用简单行列聚类生成一个初始表格标注员只需要修正关系和合并单元格而不是从零开始画框。这种模式能让标注速度提升两到三倍。合成表格方面因为金融表格的格式规律性较强可以基于少量真实表格模板自动改变行列数、文本长度、数字分布、合并方式批量生成带精确标注的表格图片。合成数据不能替代真实数据但可以让模型先学会大部分常见版面结构真实数据用来适配金融领域的特殊表达。还有一个容易忽略的点表格数据集里的负面样本。模型需要知道哪些文本区域不是表格比如页眉页脚、图片注释。如果训练数据里全是表格模型在推理时会把页面上任何结构完整的东西都当成表格导致输出一大堆垃圾。我会在训练集中混入比例大约10%的非表格页面片段并要求模型对非表格区域输出无表格标记。4.2 坐标归一化与数据增强坐标特征如果不做归一化模型基本学不出来。单据扫描的分辨率从72DPI到300DPI都有同样的表格在不同DPI下坐标数值可能相差四倍。我常用的做法是把所有坐标归一化到0到1区间以页面宽高为基准同时额外记录单元格相对邻近单元格的偏移量。相对偏移量对版面变化的鲁棒性远高于绝对坐标。几何增强也是关键。表格虽然看起来是刚性结构但扫描过程中会出现轻微旋转、透视变形、压边、裁切。数据增强时可以对渲染后的表格图片做随机小角度旋转、随机缩放、随机局部遮挡同时同步更新坐标标注。注意遮挡增强要谨慎金融表格里数字被遮挡一半OCR可能识别出错误但置信度很高的文本这属于不可恢复的噪声模型更应该学习的是识别不了就输出低置信度而不是强行猜测。金融表格还有一类特殊的数据漂移不同年份的报告哪怕同一家机构版式也会微调。所以训练集一定要覆盖多个年份、多种披露格式否则模型很容易在时间维度上过拟合。我遇到过在2019到2022年数据上准确率很高、一测2023年新样式立刻掉10个点的情况后来把跨年份样本均衡加入训练才稳下来。4.3 评估指标怎么定评估表格抽取质量只算单元格文本准确率是不够的。结构错了文本再准也没用。我用一套三层指标来评估单元格级预测单元格与真值单元格的IoU是否大于阈值文本是否匹配。这是最基础的指标。结构级预测行列结构是否与真值一致比如这家银行共预测出12行8列真值是13行8列这里错在合并单元格漏判。图表级将预测表格转换成HTML或DataFrame后逐单元格比较文本与行列位置。只有三个层级同时达标才算一条真正正确的抽取结果。表格结构评估里有个著名的坑必须使用最小编辑距离而不是逐单元格相等。因为一个合并单元格的rowspan预测错1会导致其下所有单元格的行号整体偏移如果逐格比对误差会被重复计算成几十个错误但本质上只是一个合并错误。我通常将预测表格和真值表格转成树结构用树编辑距离来计算结构相似度这样更接近真实业务影响。4.4 实际评测中的常见失败模式我记录了GFTE在真实金融样本上的主要失败模式按频率排序表头合并单元格错位。跨页表格的表头在下一页被重复识别模型经常把重复表头当成实际数据行导致多出一行。数字列对齐错误。有些表格的数字单元格右对齐但文本框本身有前后空格聚类阶段没处理好导致2,000和2000被分到不同单元格。无框表格的隐性层级。靠缩进表达层级时模型有时抓不到缩进两个字符这种弱信号需要结合字体大小一起判断。多行表头的顺序颠倒。横向表头分两行表达时模型的拓扑排序偶尔会把二级表头放在一级表头前面。这些失败模式反馈到模型侧改进方向不太一样。表头重复问题更适合在后处理阶段加规则当前一页最后一行与下一页第一行文本高度相似时自动合并。数字对齐问题应该在预处理阶段做文本标准化统一全半角、去除千分位空格。隐性层级问题则需要加入更丰富的版面特征和更大比例的缩进样本。工程化过程中模型、数据、后处理三者的迭代缺一不可。5. 工程化落地的笔记从Demo到生产5.1 OCR误差如何传导如何设计容错GFTE这类图模型有一个绕不开的问题误差传导。下游依赖上游的OCR结果OCR一旦把30,000识别成3000表格结构再准也救不了这个数字错误。尤其是在金融表格里千分位分隔符、货币符号、负号括号表示OCR经常出错。我的容错设计分三层。第一层文本后处理。针对金额数字做格式校验比如检查千分位、括号负数、百分比范围如果发现明显冲突标记为低置信度字段而不是直接丢弃或修复。第二层模型输入置信度。把OCR置信度作为节点特征输入给图网络让模型学会在低置信度区域降低结构预测的可靠性。第三层人工复核批次。凡是表格结构置信度低或者关键金额字段异常自动进入人工复核队列。在金融业务里这个人工复核环节比算法优化更能快速止血。同时我强烈建议对OCR模型做针对性微调。通用OCR模型对连串数字、大写金额、财务术语的识别效果不佳拿一小批金融表格文本做微调对下游GFTE的收益非常明显。5.2 模板表 vs 自由表的处理策略很多人会问GFTE会不会太复杂是不是只需要做模板匹配就够了我的答案是分场景。如果业务固定在少量PDF模板上模板匹配确实性价比最高。比如固定处理某一种标准化的监管报送表模板匹配能做到接近满分而图模型可能只有95%准确率没必要为了技术而技术。但金融数据往往来自大量不同来源一个机构可能要处理几百种格式的报表模板维护成本根本不可控。这时候GFTE才有明显优势。我在工程上采用混合架构有一个轻量的模板检测模块先判断当前文档是否命中已知模板命中则走模板解析快速且稳定未命中则进入GFTE自由解析流程。这个架构既保留了高频表格的速度又保证了长尾表格的覆盖率。模板检测本身不复杂对页面的表格线样式、表头关键词做哈希比对即可。5.3 性能优化与推理加速GFTE每个阶段都有性能瓶颈但核心瓶颈在图神经网络的推理。金融表格通常几百个节点全连接边会爆炸所以图构建时要用K近邻稀疏化而不是全连接。在实验里K取8到12时效果最好再大边际收益很小但显存占用会明显上升。推理加速方面我可以分享几个实用做法把OCR和单元格检测合并到一个模型里避免两遍前向计算。图神经网络层数不要贪多两层到三层就够。表格的语义传播半径有限太深反而过平滑所有节点特征趋于一致。批量推理时按表格页动态padding不要统一padding到最大长度。将预处理好的PDF页面缓存成结构化特征二次解析时跳过OCR步骤。实测下来在一个标准CPU加单卡GPU的环境里单页复杂财务报表的解析时间可以控制在1到2秒内。这个速度对批量离线处理完全够用如果需要实时解析则重点优化OCR部分的并发。5.4 落地建议与个人体会最后说一点实际的建议。表格抽取项目最怕的不是模型效果差而是没有一套可靠的评估基准。建议项目一开始就建立百页黄金测试集里面包含各类真实金融表格、各类已知难点每次模型改动都在这个测试集上回归。没有回归基准的优化很容易变成修好一个错误的同时引入两个新错误。另一个体会是不要把GFTE当成魔法。它解决的是从版面到结构的映射问题但金融表格里的复杂语义比如指标口径、会计科目之间的勾稽关系还是要靠业务规则和人工校验来兜底。技术能做到的是把表格从图片变成可靠的结构化数据而怎么解读这些数据永远需要领域知识参与。我在模拟项目X里用这套方案处理了几百份不同来源的财报和监管报表总体结构级准确率从最初的70%多提升到接近95%其中围绕图建模、数据增强和后处理规则这三块的迭代贡献最大。如果你手头也有金融表格抽取的难题建议先别急着追最新模型找一批代表性样本把表格从像素到结构的每个环节都拆开看一遍你会发现大多数瓶颈不在模型而在数据质量和结构定义的清晰度上。
阅读完成 · 觉得有帮助?
咨询建站