1. 从“答对题”到“指对地方”的鸿沟
DocAttriBench 这个基准测试最近在圈子里讨论度不低,核心结论一句话就能说清:16 个主流多模态模型,在文档视觉问答任务上答题准确率能刷到很高,但让它们把答案对应的原文位置框出来,几乎集体翻车。这个现象特别值得聊,因为它戳破了一个很多人默认成立的假设——模型答对了,就说明它真的“看懂”了文档。
我自己做 DocVQA 相关项目有一段时间了,最早也踩过这个坑。当时用某开源多模态模型做票据信息抽取,问答准确率测下来 90% 以上,团队都挺乐观。结果一上真实业务,需要把抽取到的金额、日期在原始扫描件上高亮标注给审核人员看,模型给出的坐标框要么偏到隔壁字段,要么框住整页。用户直接反馈“这标注还不如不做”。后来才明白,答题和定位是两种能力,前者可以靠语言先验蒙对,后者必须真正建立视觉与文本的对应关系。
DocAttriBench 做的事情,就是把这个被掩盖的短板量化出来。它属于 Visual Answer Grounding 这个任务范畴,要求模型不仅输出答案文本,还要输出答案在文档图像中的空间位置,通常是一个边界框。这个任务比纯 DocVQA 难得多,因为它同时考验文字识别、版面理解、跨模态对齐和空间回归四件事。适合谁来关注这个内容?做文档智能、票据审核、合同比对、表单自动录入的工程师,以及正在选型多模态模型的技术负责人。如果你只是拿模型做纯文本问答,这个短板可能暂时不影响你;但只要你的业务需要“指给用户看”,这就是绕不过去的坎。
MAPPET 这个数据集在圈内被反复提及,它是评估文档视觉定位能力的代表性基准之一,DocAttriBench 在它的基础上做了更细粒度的归因设计。接下来我会从任务设计、模型表现、复现方法、排查技巧几个层面,把这个短板拆开讲透。
2. DocAttriBench 到底在测什么
2.1 任务定义:答案文本加空间坐标的双重输出
先把这个任务的形式化定义讲清楚,不然后面聊模型表现会没有参照。给定一张文档图像 I 和一个自然语言问题 Q,模型需要输出两部分:答案文本 A,以及答案在图像中的位置 B,B 通常表示为 (x1, y1, x2, y2) 的边界框坐标。评测时,文本部分用传统的准确率或 F1 衡量,位置部分用 IoU 或者更严格的命中率衡量——预测框和真实框的交并比超过某个阈值才算对。
这里有个容易被忽略的细节:位置标注的粒度。同一个答案,标注框可以框住单个词、整个字段值、整行、甚至整个表格单元格。DocAttriBench 在标注规范上做了统一,一般以“最小完整语义单元”为准。比如问“合同签署日期是什么”,答案“2024年3月15日”对应的框应该紧贴这串字符,而不是框住整个签署栏。这个粒度选择直接影响评测结果,因为模型如果框大了,IoU 会被稀释;框小了,又可能漏掉部分字符。
提示:自己复现评测时,一定要先确认标注粒度和你的业务需求是否一致。业务上要高亮整个字段,但基准标注是单词级,直接套用会低估模型表现。
2.2 为什么答题准确率会“虚高”
这是整个问题的核心。多模态模型在 DocVQA 上准确率高,很大程度上依赖语言先验和文档结构先验。举个例子,一张发票上问“总金额是多少”,模型即使没精确定位到金额数字,也能根据“发票通常右下角有总金额”这种统计规律猜个八九不离十。再比如问“日期”,文档里日期格式高度模式化,模型靠 OCR 文本加语言模型就能拼出答案,根本不需要精确的视觉定位。
这种“捷径学习”在纯问答评测里完全看不出来,因为评测只看最终文本对不对。但一旦要求输出位置,模型就必须真的知道答案来自图像哪个区域。DocAttriBench 的价值就在于,它把这条捷径堵死了——你答对文本但框错位置,定位指标照样是零分。16 个模型集体暴露短板,说明这不是个别模型的缺陷,而是当前多模态架构的普遍性问题。
2.3 评测指标设计里的门道
DocAttriBench 的指标设计有几个值得注意的地方。文本指标和定位指标是分开算的,不混合成一个总分,这样能清晰看出模型是“答对但指错”还是“答错也指错”。定位指标通常报告多个 IoU 阈值下的命中率,比如 IoU@0.5 和 IoU@0.7,前者宽松后者严格。实测下来,很多模型在 IoU@0.5 下还能看,到 IoU@0.7 就断崖式下跌,说明它们的框普遍偏大或者偏移。
还有一个细节是“无答案”情况的处理。有些问题在文档里根本没有对应内容,模型应该输出“无法定位”。这部分样本往往被忽略,但它恰恰考验模型是否诚实——是老老实实说找不到,还是硬框一个区域凑数。我在复现时发现,不少模型在这类样本上倾向于强行输出一个框,导致假阳性率偏高。
3. 十六个模型集体翻车的根因拆解
3.1 架构层面的错配:视觉编码器与文本解码器脱节
当前主流多模态模型大多是“视觉编码器 + 投影层 + 语言模型”的三段式结构。视觉编码器把文档图像切成 patch 提取特征,投影层把视觉特征映射到语言模型的嵌入空间,语言模型负责生成答案。问题出在投影层——它做的是全局对齐,把整张图的视觉信息压缩成一组 token,语言模型在生成时很难精确回溯到某个 patch。
打个比方,这就像你让一个人看完一整页文件后回答问题,但不允许他回头看具体位置,只能凭记忆。他可能记得“金额是五千”,但说不清这个数字在页面哪个角落。文档图像分辨率高、文字密集,patch 之间的视觉差异又小,投影层的压缩损失更严重。这就是为什么模型能答对却指不准——答案信息在压缩过程中已经和空间位置解耦了。
3.2 训练目标的偏差:只优化文本损失
大部分多模态模型的训练目标就是下一个 token 预测,损失函数只关心生成的文本对不对,完全不涉及位置回归。模型没有任何动力去学习“答案在图像哪里”这件事。即使训练数据里有位置标注,如果损失函数不包含定位项,模型也不会主动利用这些信息。
有些模型尝试加辅助任务,比如让模型预测答案区域的 patch 索引,但这类辅助任务往往权重很低,被主任务的语言损失淹没。DocAttriBench 的结果说明,这种“顺带学一下定位”的方式效果有限,定位能力需要专门的训练目标和足够的监督信号。
3.3 数据层面的缺失:定位标注稀缺且昂贵
高质量的文档定位标注非常难做。纯文本问答标注,标注员读一遍文档写答案就行;定位标注要求标注员在图像上精确画框,还要保证框的粒度一致、边界准确。一张复杂版面的文档,标注一个问答对可能要几分钟。这导致公开的文档定位数据集规模远小于纯问答数据集,模型能学到的定位监督信号严重不足。
MAPPET 这类数据集的出现缓解了这个问题,但相比动辄百万级的纯文本问答数据,定位数据的量级还是差了一两个数量级。数据不够,模型自然学不好。这也是为什么 DocAttriBench 要专门设计归因评测——它逼着研究者正视数据缺口,而不是继续在纯问答指标上刷分。
3.4 分辨率与 patch 划分的取舍困境
文档图像有个特点:信息密度极高。一页 A4 扫描件可能有上千个字符,每个字符在图像里只占几十个像素。如果视觉编码器的输入分辨率不够高,或者 patch 划分太粗,相邻字符的特征就会混在一起,模型根本无法区分。
但提高分辨率又带来计算成本问题。patch 数量随分辨率平方增长,注意力计算量爆炸。很多模型为了效率,把输入分辨率压到 224x224 或 336x336,这个分辨率下文档里的小字基本糊成一团。我在复现时试过把输入分辨率从 336 提到 768,定位 IoU 有明显提升,但推理时间翻了近三倍。这个取舍在实际业务里很现实——你要精度还是要速度。
4. 复现 DocAttriBench 评测的完整流程
4.1 环境准备与依赖安装
复现这套评测,环境配置是第一步。我用的是一台单卡 24G 显存的机器,跑 7B 级别的多模态模型够用,更大的模型需要多卡或者量化。基础依赖包括 PyTorch、Transformers、以及处理文档图像常用的 Pillow 和 OpenCV。
conda create -n docattri python=3.10 conda activate docattri pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install transformers pillow opencv-python numpy scipy pip install datasets # 用于加载基准数据如果你的模型需要特定的推理框架,比如某些模型依赖自定义的视觉编码器实现,记得按官方仓库的说明装对应版本。我踩过的坑是 Transformers 版本和模型权重不匹配,导致视觉编码器加载失败,报错信息还很隐晦。建议先确认模型卡上标注的推荐版本。
4.2 数据加载与预处理
DocAttriBench 的数据一般以 JSON 加图像文件夹的形式组织。每条样本包含图像路径、问题、答案文本、答案边界框坐标。加载时要注意坐标的归一化方式——有些数据集用绝对像素坐标,有些用相对坐标(0到1之间),混用会导致框全错位。
import json from PIL import Image def load_sample(json_path, img_dir): with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) samples = [] for item in data: img = Image.open(f"{img_dir}/{item['image']}").convert("RGB") samples.append({ "image": img, "question": item["question"], "answer": item["answer"], "bbox": item["bbox"], # 确认是绝对坐标还是归一化坐标 "width": img.width, "height": img.height }) return samples预处理阶段,图像要 resize 到模型要求的输入尺寸。这里有个关键点:resize 之后边界框坐标也要同步缩放,否则评测时框的位置全偏。我建议把原始坐标和缩放后坐标都存下来,评测时用原始坐标算 IoU,避免缩放引入误差。
4.3 模型推理与输出解析
推理部分,不同模型的调用方式差异很大。有的模型直接输出文本,需要你从文本里解析出坐标;有的模型有专门的定位头,直接输出框。对于前者,解析是最大的坑——模型输出的坐标格式可能五花八门,有的是[x1, y1, x2, y2],有的是(x1, y1, x2, y2),有的甚至用自然语言描述“左上角在...”。
import re def parse_bbox(text, width, height): # 尝试匹配常见的坐标格式 pattern = r'[\[\(]?\s*(\d+\.?\d*)\s*[,,]\s*(\d+\.?\d*)\s*[,,]\s*(\d+\.?\d*)\s*[,,]\s*(\d+\.?\d*)\s*[\]\)]?' match = re.search(pattern, text) if not match: return None coords = [float(x) for x in match.groups()] # 判断是否归一化坐标 if max(coords) <= 1.0: coords = [coords[0]*width, coords[1]*height, coords[2]*width, coords[3]*height] return coords注意:解析失败率是复现时的重要观察指标。如果某个模型解析失败率超过 20%,说明它的输出格式和基准要求不兼容,这时候讨论定位能力意义不大,得先解决格式对齐问题。
4.4 指标计算与结果记录
IoU 计算本身很简单,但有几个边界情况要处理。预测框可能超出图像边界,需要裁剪到图像范围内;预测框可能宽高为负(坐标顺序颠倒),需要归一化;预测框可能完全无效(解析失败),记为 IoU 为 0。
def compute_iou(box1, box2): x1 = max(box1[0], box2[0]) y1 = max(box1[1], box2[1]) x2 = min(box1[2], box2[2]) y2 = min(box1[3], box2[3]) inter = max(0, x2 - x1) * max(0, y2 - y1) area1 = (box1[2]-box1[0]) * (box1[3]-box1[1]) area2 = (box2[2]-box2[0]) * (box2[3]-box2[1]) union = area1 + area2 - inter return inter / union if union > 0 else 0记录结果时,我习惯把文本准确率和定位命中率分开存,同时保存每个样本的预测框和真实框,方便后续做错误分析。错误分析比看总分有价值得多——你能看出模型是系统性偏移,还是随机乱框。
5. 实测中暴露的典型问题与排查技巧
5.1 坐标解析失败:格式不统一的隐形杀手
这是复现时最先遇到的问题。我测的第一个模型,文本答案全对,但定位指标接近零。排查半天发现,模型输出的坐标是归一化到 0 到 1000 的整数,而不是 0 到 1 的浮点数。我的解析函数按 0 到 1 处理,所有框都被缩到了左上角一个像素点。
排查这类问题,最直接的办法是打印几条原始输出,肉眼看一下坐标范围。如果坐标值普遍在几百到一千之间,大概率是 0 到 1000 归一化;如果在 0 到 1 之间,是标准归一化;如果和图像宽高接近,是绝对像素坐标。三种格式都要能识别,解析函数里加个范围判断就行。
5.2 框普遍偏大:模型在“猜区域”而非“定位”
另一个高频问题是预测框系统性偏大。模型倾向于框住整个段落甚至整页,因为这样 IoU 至少不会为零,能“蹭”到一点分。这种行为在 IoU@0.5 下可能蒙混过关,到 IoU@0.7 就原形毕露。
判断方法很简单:统计预测框面积和真实框面积的比值。如果中位数明显大于 1,说明模型在放大框。解决思路有两个,一是在后处理阶段对预测框做收缩,按面积比中位数反向缩放;二是换用对框大小更敏感的指标,比如 GIoU,它会对过大过小的框都施加惩罚。
5.3 多答案场景:模型只框一个
文档问答里有些问题对应多个答案位置,比如“列出所有日期”,文档里可能有三处日期。基准标注会给出多个框,但模型往往只输出一个。这种情况下,即使那一个框很准,整体召回率也很低。
处理多答案场景,评测时要用匹配算法而不是简单 IoU。常见做法是匈牙利匹配或者贪心匹配,把预测框和真实框做最优配对,再算平均 IoU。模型侧则需要在训练时加入多框输出的监督,或者用检测式的定位头替代回归式输出。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决思路 |
|---|---|---|---|
| 定位指标接近零但文本全对 | 坐标格式解析错误 | 打印原始输出看坐标范围 | 支持多种坐标格式解析 |
| 预测框普遍偏大 | 模型倾向框大区域蹭分 | 统计预测框与真实框面积比 | 后处理收缩或换 GIoU 指标 |
| 多答案只框一个 | 模型输出单框 | 检查标注是否多框 | 用匹配算法评测,训练加多框监督 |
| IoU@0.5 尚可但 @0.7 暴跌 | 框边界不精确 | 看框中心偏移和宽高误差 | 提高输入分辨率,加定位损失 |
| 无答案样本假阳性高 | 模型不愿输出空 | 单独统计无答案样本 | 训练时加入拒答样本 |
5.5 提高定位精度的几个实操方向
基于我自己的复现经验,有几个方向对提升定位精度比较有效。第一是提高输入分辨率,文档场景下这个收益最直接,但要注意显存和速度的平衡。第二是在微调时加入定位损失,比如 L1 回归损失或者 IoU 损失,让模型有动力学位置。第三是用更强的视觉编码器,比如支持动态分辨率的编码器,能更好处理不同版面的文档。
还有一个容易被忽略的点是数据增强。文档图像的缩放、旋转、裁剪增强,如果不同步调整边界框,会引入噪声标注。我建议做几何增强时一定要同步变换框坐标,或者干脆只用颜色和对比度增强,避免坐标错位。
6. 这个短板对实际业务意味着什么
6.1 哪些场景会被直接影响
如果你的业务只需要模型输出答案文本,比如自动问答机器人、文档摘要,那这个定位短板暂时不影响你。但只要涉及“指给用户看”,问题就来了。票据审核要高亮金额字段,合同比对要标出差异条款,表单录入要回显识别位置,这些场景都强依赖定位能力。模型答对但指错,用户信任度会直接崩塌——他们宁可你答“不确定”,也不愿你自信地指错地方。
6.2 选型时的评估建议
选多模态模型做文档业务,别只看 DocVQA 准确率。我的建议是,自己构造一批带定位标注的测试样本,哪怕只有一两百条,也能看出模型定位能力的真实水平。测试时重点看 IoU@0.7 的命中率,这个指标比 @0.5 更能反映实际可用性。另外要单独测无答案样本,看模型会不会强行乱框。
如果定位精度实在上不去,可以考虑混合方案:用多模态模型出答案,再用专门的文档检测模型或者 OCR 引擎做定位。OCR 引擎返回的文本框坐标往往比多模态模型回归的框准得多,代价是多一次推理,但精度提升明显。
6.3 后续可以扩展的方向
这个基准暴露的问题,其实指向了几个值得深入的方向。一是专门针对文档定位的训练目标和架构设计,而不是把定位当作文本生成的副产品。二是定位数据的自动标注方法,用弱监督或者半监督降低标注成本。三是评测指标的细化,比如区分“框偏了”和“框大了”这两种错误类型,针对性改进。
我自己后续打算试试把 OCR 的文本框作为先验,让多模态模型在候选框里做选择而不是从零回归坐标。这个思路在理论上能结合 OCR 的定位精度和多模态的语义理解,实际效果还得跑实验验证。文档视觉定位这个方向,短期内应该还会有不少新工作出来,值得持续关注。