ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

表格结构检测数据集实战:从HTML标注到TEDS评估

表格结构检测数据集实战:从HTML标注到TEDS评估 简介该数据集面向表格结构识别与文档数字化场景包含1327个YOLO格式txt标注文件、671张表格图像及1个模型配置文件yaml共2000个文件压缩包整体仅38.51MB。训练集1279张、验证集48张标注类别严格区分table column、table row、table spanning cell三类元素边界框精准可直接用于YOLOv5/v8等主流目标检测框架训练解决发票、报表、合同等图像中的表格行列解析与合并单元格检测问题。数据来源于多样化表格文档布局复杂度覆盖全面既适合学术研究中的文档布局分析Document Layout Analysis也可支撑财务、物流、RPA等企业自动化系统开发。目前已有235人学习资源另附docx说明文档便于快速理解标注规范与数据组织方式训练/验证集划分清晰省去自行拆分与格式转换的步骤算法工程师与研究者可直接开展模型训练与效果评估。1. 表格结构检测数据集2.zip它不是一份普通的目标检测数据如果你刚把“表格结构检测数据集2.zip”解压发现里面是一堆 JSON 和 HTML 文件而不是熟悉的“图片 四个坐标”别急着怀疑自己下错了资源。表格结构检测这个任务本来就不等于目标检测它要解决的不是“表格在哪”而是“表格图片里几行几列、哪些格子合并了、单元格之间是什么关系”。这类数据常见带着一串tabletrtd结构标注跟 HRSC2016、CCPD、DOTA 这类单层矩形框数据集完全是两套玩法属于文档智能和 OCR 下游解析的核心原料。适合正在做文档解析、财报表格还原、票据信息抽取的工程师。新手要跨的坎是“怎么把 HTML 标注转成能训练的格式”熟手踩的坑则是“怎么让结构还原度而不是 mAP 说话”。2. 动手前先钉死四件事任务边界、标注格式、表单类型、评估口径2.1 表格检测与表格结构识别是两道题别拿检测框的逻辑硬套把表格结构检测当成目标检测来做第一版模型常常翻车。目标检测数据集的标注是“框 类别”比如 HRSC2016 一类的遥感数据集给你船和舰的旋转框CCPD 给你车牌的水平框模型学的是“哪里有东西、东西是哪一类”。而表格结构检测要输出的是另一层信息表格里有哪些行、哪些列、单元格从第几行第几列开始、跨越了几行几列。前者是几何定位后者是结构预测。所以行业里的常见做法是把它拆成两段。先用一个检测模型把整张表格区域找出来裁剪之后交给结构识别模型结构识别模型的输入是被裁好的表格图输出是一棵表格结构树或者一组带 row_span、col_span 属性的单元格框。检测模型可以用我们熟悉的 YOLO 系列训结构识别模型则多半要处理序列或图结构两者数据需求、损失函数、评估指标都不一样。拿到 zip 之后的第一件事不是急着跑训练而是确认这份数据到底标的是哪一层。标注如果是清一色的矩形框它只能撑起“检测表格区域”这一半标注里出现table、colspan、bbox 这些字段才说明它可以支撑结构识别。这两类数据决定你要训几个头、数据怎么预处理、评测怎么出指标。很多表格结构检测数据集会同时提供检测框和 HTML/JSON 结构串因为两段式训练的标注来源是同一批图片省去自己配对的麻烦。但也有的只给结构串不给框这时候只能靠 3.2 节那种转换脚本把结构坐标反推出来当中间表示用。另外提醒一句不要因为名字里有“检测”就按目标检测的习惯对样本做随机裁剪增强。表格结构标注的层级是跨整张图的随机裁剪会把单元格的 rowspan 上下文切碎模型学到的是一堆残缺结构。这一点在后面的避坑章节还会反复出现。2.2 标注格式决定预处理成本HTML、JSON 与坐标表格结构检测数据集的标注格式基本就三种下面这张表可以当作开工前的对照标注形态携带的结构信息携带的坐标信息典型预处理HTML 串行/列/合并关系完整无解析成树或网格再对齐图像JSON 结构行/列/合并关系完整单元格有 bbox可直接训练偶尔补表格级 bbox矩形框 类别只有“表格”类别表格级 bbox 完整只能训练检测器难还原结构HTML 标注最常见的是一段完整源码像tabletrtd colspan2 rowspan1金额/td/tr这样。它的优点是结构信息一点不丢缺点是需要遍历 DOM把每个 td 在逻辑网格里的行列索引算出来再结合 OCR 或版面分析结果反推像素坐标。JSON 标注则通常在每个 cell 对象里直接给 bbox 和 row_span、col_span省掉解析 DOM 的步骤。但 JSON 里要确认三件事坐标是绝对像素、相对表格区域、还是归一化后的 0~1表格级 bbox 是否单独提供坐标原点在左上角还是左下角。这三项里任何一项搞错可视化验证时就会出现边框集体飞出图像的“鬼图”。拿到 zip 后我建议先扫一遍 readme 文件再用一个小脚本统计若干样本的标注字段。重点确认坐标归一化方式、图片尺寸是否统一、表格框是否存在。混合标注的数据集经常出现 HTML 只覆盖一部分图、JSON 覆盖另一部分图的情况训练时按“文件对得上”为准不要看目录名想当然。2.3 有线表、无线表、拍照表表单类型先对齐业务场景表格形式决定了模型该学什么特征。有线表格数据库导出的报表、税务票证可以大量依赖横竖线响应检测线框就能把行列位置定个大概无线表或叫少线表财报排版表格、简历样本几乎没边框可用要靠文本对齐、列间距、字体基线来推断结构拍照表格则额外面对透视、纸面弯曲、反光噪声。同一个 zip 里这三类占比不同训练出来的模型行为差异很大。你的业务是拍票据数据集里 80% 却是渲染出来的矢量表那 AP 再高上线后也会被透视样本打穿。我的习惯是先抽样 100 张图按有线表、无线表、拍照表三类人工计数再和 readme 的说明比对。这个方法从工业数据集的思路借过来的PHM2012 这类故障诊断数据也是一样样本数量不大时宁可每张样本的标注质量高也不要盲目堆量。表格结构检测数据集大多长这样图少、标注密度高一张图里有几十个单元格和多组行列关系信息量远大于一张普通目标检测图。所以清洗错位标注比多凑几千张噪声图有用得多。评估口径也要在动手前定好。目标检测习惯看 mAP0.5但表格结构还原度的通行指标是 TEDS基于树编辑距离计算相似度。原因是检测框差几个像素人眼看不出来对业务也无感但如果模型把两列合并成一个单元格导出 Excel 后整张表就废了。所以这个方向训练时别只盯验证集的 mAP结构指标才是验收核心。mAP 高、TEDS 低说明模型“框得准但拆得乱”大概率是合并单元格和空单元格处理出了问题具体排查方向在第 4 章。3. 从 zip 到训练管线解压校验、标注转换、数据拆分三步3.1 先校验 zip 完整性再解压编码与坏包的坑解压一个几个 GB 的 zip最怕解到一半报“文件损坏”。常见原因有三个下载被截断、传输字节错位、打包端没按完整归档写。经验做法是不急着用 GUI 双击解压先用一两行 Python 把每个文件读一遍让 zipfile 的 CRC 去校验有坏文件立刻定位到具体文件名。import zipfile def audit_zip(path): bad [] with zipfile.ZipFile(path) as zf: # 先看有没有加密条目 for info in zf.infolist(): if info.flag_bits 0x1: bad.append((info.filename, encrypted)) # 逐个读文件zipfile 会自动核对 CRC for name in zf.namelist(): try: zf.read(name) except Exception as e: bad.append((name, str(e))) if bad: print(损坏条目) for name, err in bad: print( -, repr(name), err) else: print(zip 完整共, len(zf.namelist()), 个条目) return bad audit_zip(表格结构检测数据集2.zip)逻辑上用 flag_bits 检查加密标志再用zf.read触发 CRC 校验。大多数 zip 文件在 read 之后数据完整性是有保证的。这里有个高频坑zipfile 默认按 UTF-8 解码文件名如果打包端是 Windows 下用 GBK 压的中文文件名读出来就是乱码。zipfile 没有一键切换编码的参数常见做法是读取 info.filename 的原始字节先用 GBK 解码失败再退回 UTF-8。这段逻辑建议直接写进解压工具里不然中文命名的图片解压后全是一串菱形乱码后续文件配对也麻烦。解压完还要抽查“标注与图片是否一一配对”遍历一遍 JSON、HTML 的文件名前缀和图片目录做差集找出只有标注没有图、或只有图没有标注的孤儿文件。表格结构标注经常分散在子目录里目录层级一多就容易漏挂。3.2 把 HTML 标注转成目标检测可用的坐标最小转换脚本结构识别模型需要的是单元格级坐标和目标结构。如果 zip 里只提供 HTML 没有 bbox就得把table的 DOM 解析成网格再映射到图像坐标系。下面这个脚本只用 Python 标准库不需要装 lxml核心思路是逐行累积网格索引。from html.parser import HTMLParser class TableHTMLParser(HTMLParser): def __init__(self): super().__init__() self.rows [] # 每一行是该行 td 的列表 self.cur_row None self.cur_td None def handle_starttag(self, tag, attrs): if tag tr: self.cur_row [] self.rows.append(self.cur_row) elif tag td and self.cur_row is not None: a dict(attrs) self.cur_td { rowspan: int(a.get(rowspan, 1)), colspan: int(a.get(colspan, 1)), text: , } self.cur_row.append(self.cur_td) def handle_data(self, data): if self.cur_td is not None: self.cur_td[text] data.strip() def handle_endtag(self, tag): if tag td: self.cur_td None def html_to_grid(html): parser TableHTMLParser() parser.feed(html) return parser.rows这里要留意两点遇到 td 内嵌套 div、span 时handle_data 会多次触发所以 text 字段用累加而不是赋值rowspan 和 colspan 取不到属性时默认成 1避免无属性标签直接抛异常。拿到网格后再去算实际像素坐标时需要一个基准要么图片里正好有检测出的表格边框用横线竖线的交点作为行列锚点要么用 OCR 输出的字符框做近似列对齐。所以 HTML 反推的坐标天然带误差适合喂给结构分支不适合拿去训练检测分支。把网格转成 YOLO 矩形框的通用做法是先统计每行的最大高度作为行高、每列的最大宽度作为列宽单元格左上角等于累加的行列偏移右下角按 colspan 和 rowspan 跨过去。没有真实图像内容参与时这个坐标是“布局坐标”而不是“像素坐标”导入模型前最好用第 5 章的脚本画框人工确认否则很容易出现整列偏移的隐蔽错误。3.3 拆训练/验证/测试集按图分层别把同一张表散到多折表格结构检测数据集的样本独立性比目标检测要求更高。同一张长表格如果被切成上下两段分别标注这两段同时进训练集和验证集验证指标会虚高。拆集的原则是同一张原始表格的样本只进一个折。import random from pathlib import Path def split_by_group(image_paths, group_of, ratios(0.8, 0.1, 0.1), seed42): rng random.Random(seed) groups {} for p in image_paths: groups.setdefault(group_of(p), []).append(p) group_ids list(groups.keys()) rng.shuffle(group_ids) n len(group_ids) n_tr int(n * ratios[0]) n_va int(n * ratios[1]) splits { train: [g for g in group_ids[:n_tr]], val: [g for g in group_ids[n_tr:n_tr n_va]], test: [g for g in group_ids[n_tr n_va:]], } return {k: [p for g in v for p in groups[g]] for k, v in splits.items()}group_of 通常是从文件名里提取表格 ID 的函数比如 “table_001_page_2.jpg” 里取 table_001。seed 一定要固定否则复现实验时划分一变线上和论文指标都对不上。ratios 按数据量调整样本少的时候验证集不要低于 10%不然 TEDS 的置信区间太宽模型改几个参数也看不出来好坏。拆分完顺手把每个集合里的有线表、无线表占比打出来如果某一折全是无线表训练时验证 loss 就会忽高忽低。4. 表格结构检测数据集避坑现象、原因与解决4.1 解压后 JSON 打开全是乱码被“用 Excel 打开”骗了现象从 zip 里解压出的 JSON 或 CSV用 Excel 双击打开一片乱码用记事本打开也有菱形替换符。原因数据集在 Windows 侧生成时文件名或文件内容是 GBK 编码而 JSON 内部声明的是 UTF-8Excel 读 JSON 时习惯按本地 ANSI 编码猜两边一错位就乱码。还有一种情况是 zip 条目文件名是 GBKzipfile 按 UTF-8 解出来文件名乱码文件内容本身反而没坏。解决先用 3.1 的脚本 audit 确认文件没有加密或损坏再分别处理。文件名乱码就判断编码方式并转码内容乱码就在打开脚本时显式指定 encodingutf-8不行再试 gbk。这个坑基本是每个做表格数据集的人都会遇上的下马威别急着拿脚本改数据先确认是显示问题还是真实编码问题。4.2 合并单元格被拆散解析 rowspan/colspan 不彻底现象模型预测的结构树里行数比 GT 多或者一行里多出几个不该有的列TEDS 上不去可视化时发现一个跨行的单元格被拆成两三个小框。原因解析 DOM 时只取到当前 td 的位置没有标记被 rowspan 和 colspan 覆盖的行列区域。比如一个从第 2 行开始、rowspan2 的单元格它实际占掉了第 3 行的第一列第 3 行的第一个 td 应排在第二列。如果解析逻辑没有维护这样一张“占用表”后面所有列的坐标累加就会集体错位。解决解析时维护一个二维 occupancy 数组遇到带 rowspan/colspan 的 td先把它覆盖的行列全部标记成 occupied后续单元格碰到 occupied 位置直接跳过把逻辑列指针右移。这样给单元格分配的列索引才能和视觉位置对齐。这段代码建议单独写成单元测试用一段已知 HTML 断言输出的网格尺寸和行列数防止以后换一份数据集时解析悄悄退化。4.3 只有 HTML 没有框反推的坐标别拿去训检测器现象zip 里图片和 HTML 齐全但没有 bbox 文件。自己把 HTML 网格换算成坐标后拿去训练检测器训练 loss 能降得很低验证输出却全是位置偏移的框。原因HTML 结构标注只记录逻辑行列关系不包含图像坐标。从 DOM 网格反推出来的“坐标”是布局格点不是像素位置。列宽随文字内容自适应、没有表格线、背景色很少的表格布局格点和真实像素位置能差出几十像素甚至一整列。解决把检测和结构识别拆开对待。检测表格区域用带真实 bbox 的检测数据没有就先用版面分析脚本粗标再人工修正结构识别再用 HTML 标注训练。反推坐标只用于给结构分支做行列 anchor 的中间表示不要一份数据两个头共用。这是我反复踩过最深的坑后面每次拿到新数据集都会先确认 bbox 来源。4.4 超长表格和跨页表格坐标出界、显存暴涨现象训练时报 “all box coords should be in [0, 1]”或者某个 batch 显存占用突增。检查样本会发现里面有横跨整页的长表resize 到 640x640 后行文字被压成一条线坐标也超出归一化范围。原因表格数据集的样本尺寸方差比自然图像大很多长宽比可以到 1:10 以上。统一 resize 到固定尺寸时长边压缩、短边拉伸单元格比例严重失真归一化坐标跟着越界。解决训练管线里设置长宽比阈值比如大于 2.5 的样本走独立处理分支按行切分或按页切分后分别训练resize 时保持宽高比剩余区域用 0 值 padding 到输入尺寸。对越界坐标做 clip 是一种后悔药可以防止训练中断但不要默认它没问题——clip 后行之间的空隙会形成假特征模型更容易学歪。更稳的做法是在验证脚本里统计每张图的宽高比分布找到异常样本逐个查标注。4.5 加密 zip 与“伪损坏”先看末尾再动手现象解压到 90% 弹出密码框或者解完所有文件打几个标注发现内容截断、字段缺失。原因加密 zip 常见于数据集二次分发时加固伪损坏则多半是下载截断。分辨办法很简单查看 zip 文件末尾两个字节是不是 PK 结尾。末尾有完整结束标记文件基本完整如果是下载工具占了最后一块末尾往往是乱码或直接缺失。解决先确认文件完整再确认加密方式。zipfile 能打开加密条目但不能解压audit 脚本里已经能看到 encrypted 标记。这种情况不要指望网上那些“zip 密码移除”的偏门小工具大部分只是剥离加密标志解出来的内容依然是错的。直接联系数据集来源方核对密码或换官方渠道重新下载更省时间。自己重新打包分发时建议在 Linux 或 macOS 上用 zip -r并在打包后跑一遍 audit 确认没有加密条目和损坏文件避免给别人留下同样的坑。5. 把结果钉死TEDS 评估与可视化验证一起上5.1 用 TEDS 而不是 mAP 验收结构还原度表格结构任务里mAP 高不代表结构对。我习惯每个 epoch 结束后同时算 TEDS思路是把预测和 GT 的 HTML 各自转成树再用树编辑距离算相似度。from zss import simple_tree_distance, Node def html_to_tree(html): # 把 table/tr/td 压成树td 保留 rowspan/colspan 属性 ... def teds(pred_html, gt_html): t1, t2 html_to_tree(pred_html), html_to_tree(gt_html) dist simple_tree_distance(t1, t2) max_nodes ... # t1、t2 节点数之和 return 1.0 - dist / max_nodeszss 库实现了标准的树编辑距离节点操作权重默认 1对大多数场景够用如果你的管线需要更细可以把单元格文本和合并属性的替换成本调高。TEDS 的通行报告阈值是 0.9 和 1.0业务上一般看 0.9因为 1.0 要求逐字符一致对 OCR 文本噪声太敏感。这个指标是表格结构领域的硬通货建议从第一天起就加进训练日志。5.2 把预测 HTML 渲染成图和 GT 并排看差异TEDS 给出分数但分数相近的两个模型错法可能完全不同。我每天收工前的固定动作是取验证集里 TEDS 最低的 20 个样本用 headless 浏览器把预测 HTML 和 GT HTML 渲染成 PNG再和原图三张并排摆在一起肉眼看是哪一行对错。google-chrome --headless --screenshotpred.png --window-size1200,800 \ http://127.0.0.1:8899/pred.html然后按同样方式渲染 gt.html把原图、pred、GT 三张用 PIL 横向拼接存成一张对比图。竖排合并单元格多跨一格、无线表列边界偏一列在 TEDS 上只是几分差距图上却一眼能看到。坚持做这个动作很多“指标不错但交付被拒”的问题都能拦在早期。5.3 几个值得带走的收尾习惯验证时给每个样本输出“检测框数量”和“结构树节点数”两个辅助字段。节点数异常少说明模型把一整行吞掉了节点数异常多大概率是合并单元格被拆开回到 4.2 的解析逻辑排查。部署时保持和训练一致的预处理等比缩放、padding、不做随机裁剪表格结构感知对这些细节比分类任务敏感得多。所以直到现在我拿到新数据的第一天还是先解压、audit、画框流程走完才开始调模型——这套顺序帮我拦下了无数次无效训练希望你也能少走我走过的弯路。本文还有配套的精品资源点击获取
返回列表