最近我在折腾文档检索相关的实验时发现一件挺尴尬的事——现在绝大多数号称“多模态检索”的系统实际跑起来只是把图片里的OCR文字拼回文档里继续当纯文本检索用。图表理解、跨模态证据定位、图文联合推理这些关键能力几乎没有公开数据能稳定评测。正好我们组这轮工作搭了一个新的评测基准MMDOCIR专门用来解决“长文档多模态检索”的评估空白。这轮分享就把这个基准的设计思路、数据构建、评估协议和实测踩坑完整聊一遍给你一份可以照着复现的技术笔记。先交代这个项目是什么。MMDOCIR的全称是Multimodal Document Retrieval for Long Documents简单说就是构造一个专门用来评测“模型在长文档里做多模态检索”能力的基准数据集与评测协议。它的特色有三个文档刻意做长、检索目标不仅限于文本片段、评测覆盖图文联合证据。适合人群很明确——做信息检索、文档问答、大模型RAG、知识库系统的人尤其是当你发现纯文本向量检索在真实业务里不够用但又没有一个统一测试集来定位问题的时候这个基准的用法会非常顺手。1. 为什么需要“多模态长文档检索”基准1.1 现有检索任务到底缺了什么传统检索基准比如一堆纯文本的段落检索集合往往有一个共同前提答案存在于文本里。但在真实的PDF文档、财报、技术手册、学术论文里很大一部分关键信息压根不在正文文字里而是在表格、折线图、结构图、算法流程图中。一篇几十页的论文可能结论部分只有一句话但支撑那句话的核心证据藏在第23页的Figure 6和Table 2里。如果你把文档直接转成纯文本来建索引会面临三个层次的问题。第一个层次是信息丢失。表格通过pdf转文字之后行列关系经常直接碎掉横排变成长串数字检索系统根本分不清哪个数字对应哪个指标。图就更别提了转出来只剩一个题注。第二个层次是语义错位。用户的查询可能是“对比两个方法的准确率增量”这需要用图中的曲线和表格里的数值共同回答单看文本片段找不到直接匹配整段检索分数会被严重稀释。第三个层次是评估失真。很多现有基准的“正确答案”是预先标注的文本段落对于跨模态证据既没有标注格式也没有判定方法。模型就算具备看图能力你也测不出来。1.2 MMDOCIR 想解决的三个关键问题我们在设计MMDOCIR时把痛点拆成了三个可操作的问题。第一个可操作问题文档粒度。文档长度必须达到“真实文档”的水平而不是平均几百词的短文。每条测试样本至少来自一份10页以上的完整文档长度分布刻意覆盖20页到80页区间逼着你处理长上下文带来的索引和检索压力而不是投机取巧把全文塞进去硬算相似度。第二个可操作问题模态锚点。检索结果不能只是一个文本片段必须支持定位到文档里的某个表格区域或图片区域并且附上“为什么它是证据”的文本解释。这样模型系统需要真正做到图文跨模态理解而不是把图像特征粗略池化。第三个可操作问题评估一致性。不同系统给出的答案形式可能完全不同为了公平比较我们设计了一套统一的证据覆盖判分协议让带表格的答案和带图片的答案能在同一套标准下算分。这三点直接决定了后续数据格式、标注规范和基线实验的全部走向。提示如果你是做RAG落地的话对这三个问题的感受会更直观——很多用户反馈“检索不到”往往不是模型弱而是评估集根本没有覆盖图表类证据。2. 基准数据的构建思路与核心细节2.1 文档池选择与质量过滤构建一个能服众的基准第一件事不是写样例而是把文档池的底子打好。我们的文档来源以可合法获取的开放论文和公开技术报告为主领域聚焦在机器学习系统、医疗健康报告和金融财报分析这三类原因很直接这三类领域的图表出现密度最高而且跨模态证据是刚需。文档池的筛选流程分四步初筛按页数、文件大小、文本可提取率排序优先保留10页以上且包含至少5个图或表的文档。格式校验用解析器提取每个文件的页面结构统计图片块数量和表格块数量低于阈值的直接丢弃。隐私与合规筛查对所有文档做文本指纹匹配剔除包含不可公开内容的部分。去重按照段落哈希去重防止同一份图在多个文档里反复出现导致评估泄漏。这一步容易被忽略但实际工作量大到超出预期。原始语料里大概有30%的PDF存在图片嵌入异常要么是扫描件没有文本层要么是表格被裁成碎片。最后真正能进文档池的文档占初始候选的比例不到六成。2.2 输入输出设计检索到“证据片段”而非整篇MMDOCIR的样本结构和传统段落检索有明显差异。每条数据包含以下核心字段query自然语言查询模拟真实用户会问的问题比如“该模型在跨语言任务上的平均性能如何”。document_id所属文档标识。evidence_span支撑答案的文本片段必须是文档中可定位的原句。modality_anchor模态锚点比如table:12表示第12页的某个表格figure:7表示第7页的某张图。evidence_relation证据关系取值可以是text_only、table_only、figure_only、table_and_text、figure_and_text。为什么要求evidence_span而不是直接给整页或者整张图因为实际检索场景里用户要的不是“第6页有相关内容”而是“哪一句话、哪一个表格单元格、图中哪一段曲线支持这个答案”。如果只给整页标注系统完全可以靠位置先验蒙对评测噪音太大。证据片段粒度越细越能反映系统真正的细粒度建模能力。在标注过程中我们采用了人机协同的策略先让一个较强的生成模型产出候选证据片段再由标注人员做二轮确认和修正。每个样本至少经过两人独立复核遇到分歧的样本直接进入仲裁池。仲裁池里最常见的分歧是表格证据边界问题比如查询问的是“两轮训练之间的收益变化”有人标了整张表有人只标了两列。这种情况会根据查询动词的指向来决定偏向“差异对比”时保留跨列合并区域。2.3 模态锚点怎么定位到图里的具体区域图片类证据不能简单给一个整图路径。仔细想想一张Figure里可能包含子图a、b、c三个小图查询可能只针对其中一条曲线。所以MMDOCIR对图片类证据做了子区域切分锚点格式设计成figure_id sub_region bounding_box。具体处理流程是这样用检测模型把文档图片里的子图区域切出来。每个子图区域用编号标识同时保留它在原始页面上的坐标框。标注人员针对查询选择具体子图并写一句自然语言说明描述“这个子图是如何支撑答案的”。这样设计之后评测系统就能支持更严格的地图定位评估你检索到正确的图还不够还需要定位到图内正确的子区域才算覆盖完全。对目前大多数只做文档级图向量检索的方案来说这一步会直接暴露差距。这里有一个实际心得不要轻易把子图切换得太碎否则会把合理的跨子图推理样本全部切成无头碎片。我们的经验是只有当子图之间坐标距离足够远、边界清晰时再切分。靠太近的复合图宁可保留整图并额外提供一个区域描述字段。3. 评估指标设计与任务拆分3.1 指标选型为什么不用精确匹配检索评测最常见的指标是RecallK、MRR、nDCG。但这些指标在长文档多模态场景里都有一个隐性假设答案必须是单一文本片段。MMDOCIR的答案既有文本片段又有表格单元格区域和图片子区域直接套这些指标会出问题。我们采用了两层次评估指标召回指标和证据覆盖指标。召回指标沿用常规做法。给定查询按系统输出分数取Top K结果判断结果里是否包含标注的文本片段。这里的匹配方式用语义相似度松弛匹配即允许模型输出与标注片段不完全一样但语义等价的改写。我们预先用另一套重写模型把每个证据片段扩写成5个同义变体判分时看模型结果是否命中了任意变体。证据覆盖指标是本基准的重头戏。对于多模态证据样本系统返回结果要同时包含modality_anchor和evidence_span我们按模态类型分别计算覆盖率证据类型覆盖判定标准text_only返回片段与标注片段语义匹配table_only返回锚点表ID一致且命中的行数≥标准行的60%figure_only返回锚点子图ID一致不要求坐标完全一致figure_and_text图ID匹配且文本部分语义匹配这种设计最大的优势是兼容性。纯文本检索系统和带多模态检索的系统可以在同一套指标下比较。前者在table_only样本上分数很低但在text_only样本上不吃亏后者必须靠真实图文理解才能拿分没法浑水摸鱼。3.2 任务变体检索、重排、证据选择为了让基准适用于不同研究目标我们把评测拆成三个递进的任务变体。第一个变体叫前端检索。输入是查询和整份文档库输出Top K个文档。这个任务主要测粗粒度召回能力看系统能不能在一堆文档里定位到正确的文档。评估指标用文档级RecallKK通常取5、10、50。第二个变体叫证据重排。输入是查询和一段候选证据集合其中混有正样本和干扰项输出重新排序的结果。这个任务更像实际RAG里第二阶段的rerank评估指标用MRR和nDCG5。注意候选证据集合故意加入了同文档里不同章节的相似文本就是为了测试系统能不能抵抗局部相似度带来的诱惑。第三个变体叫端到端证据选择。输入只有查询和文档库系统需要直接输出证据片段和模态锚点最终分数由覆盖指标加权得到。这个任务难度最大对系统的索引架构、多模态编码、跨模态对齐能力提出了全链路要求。三个变体对应的难度差异我们在实验里看得很清楚前端检索的Top-10命中率普遍能做到80%以上但端到端证据选择里带figure_and_text类型的样本准确率直接跌到40%以下。这个落差就是当前系统的主要瓶颈区。4. 实测流程与工程实现4.1 文档解析与多模态切分跑基准的第一步是把PDF文档转成可检索的结构化对象。我强烈建议不要用一刀切的文本抽取而是保留页面布局。我们的处理pipeline分三步。第一步用解析器抽取每个页面的文本块、图片块、表格块以及它们的页面坐标。如果解析结果里表格行数小于3就把该块标记为“疑似图片表格”后续走OCR通道。第二步把图片块切成子图区域。切割依据是版面分析模型输出的子图边界框。这一步产出每个子图的裁剪图片、原始坐标和所在页码。第三步将文本块按语义段落切分同时保留段落和页码、附近图表锚点之间的关联关系。关键点是必须双向建模文本段落的元数据里存该页的图表ID图表单元的元数据里也存附近文本的首句摘要。这样后续建索引时图文信息不会彻底割裂。代码示例大致是这样from layout_parser import DocumentParser from region_cutter import SubFigureCutter doc DocumentParser.load_pdf(sample_report.pdf) pages doc.extract_pages() for page in pages: text_spans page.get_text_spans(min_len20) table_regions page.get_table_regions() figure_regions page.get_figure_regions() cutter SubFigureCutter(min_area_ratio0.05) subfigs [] for fig in figure_regions: subfigs.extend(cutter.crop(page.image, fig.bbox)) page_obj { page_id: page.page_id, text_spans: text_spans, tables: table_regions, subfigures: subfigs, }这里有一个细节子图切分不是每张图都需要做如果一张图是单个独立图表且没有内部多子图结构直接保留整图就能减少后续索引负担。所以代码里min_area_ratio参数可以适当调大避免把一张大图切成十几个碎块。4.2 召回通道从文本向量到图文混合索引检索系统需要一个能同时处理文本、表格、子图的混合索引。我们采用的方案是“多向量双塔”结构每个模态各自过编码器但输出向量统一映射到同一个语义空间。具体拆分两个通道文本通道段落文本经过一个编码器生成稠密向量同时用BM25生成稀疏向量最后做分数融合。融合权重我用0.6给稠密、0.4给稀疏这个比例在召回调优时比较稳。视觉通道每个子图经过视觉编码器生成向量表格除了保留结构信息之外把每行拼接成一句话再过文本编码器。用户查询进来之后分别和文本索引与视觉索引算相似度。为了让多渠道分数可比我在每个通道内部做了归一化再用可学习的加权参数融合。初始权重的设置思路很简单text_weight0.7, table_weight0.15, figure_weight0.15然后根据验证集做一个小范围搜索。实际写出来的召回代码核心就这部分from retrieval_index import DenseIndex, SparseIndex text_index DenseIndex.build(paragraph_vectors) sparse_index SparseIndex.build(paragraph_texts) table_index DenseIndex.build(table_sentence_vectors) figure_index DenseIndex.build(figure_vectors) query_vec encode_query(query_text) text_scores 0.6 * text_index.search(query_vec, k100) text_scores 0.4 * sparse_index.search(query_vec, k100) table_scores table_index.search(query_vec, k50) figure_scores figure_index.search(query_vec, k50) final_scores fuse_scores(text_scores, table_scores, figure_scores) candidates retrieve_topk(final_scores, k200)这层设计最需要注意的地方是图文索引的语义空间对齐。视觉编码器和文本编码器如果是分开训练的两边的向量分布差异很大直接相加会出问题。我在实验里深有体会用一个统一的多模态编码器做双塔比用两个单模态编码器拼起来的效果好至少一截。4.3 重排序跨模态打分细节召回阶段输出的候选集合里错误样本往往和正确样本长得非常相似比如同一页里的两个不同表格。重排序阶段要解决的就是这种细粒度差异。我的做法是构造一个跨模态打分器把查询、候选文本、候选表格或子图三部分拼在一起编码直接输出一个匹配分数。这里用交叉注意力结构的效果优于双塔结构因为模型能够在编码阶段就让查询和证据做细粒度对齐而不是最后靠向量点积硬碰。具体到打分逻辑有几个参数值得注意最大序列长度。文本截断到512 token足够了但表格序列化之后容易超长我会限制表格最大行数为20行超过部分截断。温度系数。重排分数需要经过softmax归一化温度设置为0.05拉大正负样本分数差距。归一化策略。如果不做归一化重排分数可能被某个模态的编码器主导导致表格样本全部排名靠前或者靠后。按模态分别做max-min归一化后效果稳定很多。重排实现示例from reranker import CrossModalScorer scorer CrossModalScorer(max_text_len512, max_table_len512) rerank_scores [] for candidate in candidates: text candidate.text table candidate.table_sentence figure candidate.figure_crop score scorer.score(query_text, text, table, figure) rerank_scores.append(score) normalized_scores min_max_normalize(rerank_scores, per_modalityTrue) final_ranking order_by_score(candidates, normalized_scores)重排带来的收益比较可观。在我们的验证集上重排后MRR5大约提升了11个百分点其中最明显的提升集中在table_and_text这类混合证据样本上。这说明跨模态交互确实在起作用不是简单调参带来的分。5. 踩坑记录与排查速查表5.1 表格被拆成“乱码”怎么办这是整个pipeline里出问题频率最高的环节。一个多列表格从PDF解析出来后经常变成一长串连续数字或者行列错位。我踩过几次坑之后总结出三条应对策略。第一如果表格被抽取成文本块后行数明显小于预期不要硬用结构化解析直接把表格区域截图走OCR甚至视觉编码。很多情况下视觉信号比文本抽取可靠。第二给表格建立一个“序列化语义”通道。将表头和数据行拼接成自然语言描述比如“准确率指标在迭代100轮时达到87%”而不是保留“87%”这种孤立数值。这样文本检索时能更好地命中语义表述。第三检查表格区域坐标是否有跨页断开。长表格分页后第二页的表头往往丢失拼接时要靠列名相似度做自动表头复制否则后续标注对齐会乱。5.2 召回结果分数对齐错位融合多个通道分数时我遇到了一个诡异问题单独看文本通道Top结果正确率很高单独看表格通道Top结果也正确但融合之后正确答案反而被挤到了前十名之外。根因是各通道的原始分数分布差异太大文本通道分数集中在0.6到0.9视觉通道集中在0.2到0.5融合时没做归一化。解决办法是每次融合之前对各通道分数做分位数归一化把原始分数转成百分位排名再按权重相加。这样融合结果对通道分数尺度变化不再敏感。5.3 评估结果波动大种子与采样早期跑实验时我发现同一个系统跑两次结果能差三四个点。排查下来有两个原因一是文档切分时的随机采样如果没有固定随机种子子图切割的边界会在实例化时出现偏差二是评估集样本量不够某些子类别太少波动被放大。修复手段很直接固定所有随机种子并且给每个文档设计多个查询使最细粒度的证据类型样本数不低于200条。如果条件允许跑三次取均值和标准差再上报。5.4 问题速查表现象可能原因排查方向表格证据命中率低于10%表格被解析成纯文本或乱码检查解析结果表格行数和列数检索结果全是文本图表从不出现视觉通道未正确建索引检查子图裁剪后的图片数量和向量写入日志重排后分数反而下降各模态分数未归一化添加按模态的分位数归一化文档级召回高证据级召回低粗召回通道缺少跨模态融合检查文本和视觉通道融合权重同一样本多次评测分数差异大随机种子未固定固定采样种子和推理随机种子6. 通用经验与扩展方向6.1 实测中得到的三条经验第一模态锚点标注比想象中重要得多。如果你的数据只标文本片段不标图表锚点后续所有评估都只能退回到文本匹配多模态部分直接失效。做数据集设计时宁可少做文本样本也要保住图表锚点的完整覆盖。第二统一语义空间是系统上限的瓶颈。召回阶段如果强行用分开训练的文本编码器和视觉编码器拼起来跨模态查询的分数融合效果非常差。建议至少用一句“图文对”做一次对比学习微调把两个模态的向量分布拉到接近的位置。第三长文档不能只靠切块。更好的做法是保留文档层级结构页面级、段落级、图表级三层索引同时保留检索时先定位页面再下钻到证据片段。这样做的好处是能利用页面上下文信息过滤掉不少跨页干扰项。6.2 后续可以怎么扩展这轮基准做完之后我已经开始琢磨能不能把时间维度加进去。真实业务里很多文档是持续更新的同一个说法的统计口径在不同版本里可能完全不一样。将来的基准可以设计“版本检索”任务即给定查询要求模型检索到特定版本的证据同时区分不同版本之间的差异这会比单纯做静态检索更有挑战性。另外可以尝试把重排序模型蒸馏成召回阶段可用的轻量编码器这样既能保留交叉注意力的精度又能提升召回侧的响应速度。对生产环境来说两阶段之间的延迟差距还是不可忽略的。最后分享一个实用小技巧如果你要快速验证新模态编码器到底有没有提升先在figure_and_text类型的子集上看指标不要看整体平均。整体平均容易被大量text_only样本稀释。用这个子集调参收敛方向会明确得多。我在实际跑这轮基准时最意外的一点是前期在数据清洗和标注上花的时间远超模型调优。很多人以为多模态检索的瓶颈是模型结构真实瓶颈其实是“证据单元的定义”。能够把一张图内部的不同子图、一个表格内部的不同行组都当成独立可检索的证据单元系统的整体效果才会有本质提升。希望这份实操记录能帮你在做自己的图文检索系统时少走几步弯路。 SEO 优化官网定制响应式建站教育培训建站