
最开始接触多模态大模型评测时我遇到过一类很典型的问题模型在自然图像问答上表现不错可一旦面对分子结构图、实验流程图、电极化曲线这类科学图表输出就开始“胡言乱语”——要么把坐标轴数据读错要么把流程箭头关系理解反。这类问题很难通过单纯堆训练数据解决关键是要有一个能系统衡量模型“科学图表理解能力”的评测体系。这其实就是 Diagram-MMU 这类多模态基准Multi-Modal Benchmark要解决的问题。本文将围绕 Diagram-MMU 展开一方面解释它是做什么的、衡量哪些能力另一方面结合真实可运行的代码示例演示如何搭建一条面向科学图表的模型评测流程。适合正在做多模态大模型应用、RAG 知识库图表解析、以及 AI 教育产品评测的开发者阅读。1. 背景与核心概念1.1 科学图表理解是什么科学图表Scientific Diagrams是一个比“图片”更精确的概念。它既包括教科书里常见的细胞结构示意图、电路图、地理剖面图也包括论文中常见的散点图、柱状图、热力图以及工程领域的工艺流程图、机械装配图等。这类图表和自然照片有本质区别信息高度结构化每个图形元素都有明确含义。依赖文字标注、符号、坐标轴、图例等辅助信息。空间位置关系很重要“在左边”“位于上方”“箭头指向”往往直接决定答案。需要领域知识才能完成推理比如知道某个化学符号代表什么、某个统计图的横纵轴含义是什么。多模态大模型要真正“读懂”科学图表不能只做简单的图像识别还要完成信息抽取、空间关系判断、符号语义理解和跨模态推理。这就需要一个统一的评测标准来量化模型能力。1.2 多模态 Benchmark 解决什么问题Benchmark 的中文含义是“基准测试”或“基准测试集”。在多模态模型领域benchmark 通常由三部分组成标准化的数据集包含输入图片和预期输出。统一的任务定义比如视觉问答、图表描述、信息抽取。可量化的评价指标比如准确率、F1 分数、ROUGE-L 等。有了 benchmark不同团队训练出的模型才能在同一个标准下横向比较。否则A 团队说自己准确率 90%B 团队说自己准确率 95%但两人测试的数据和任务都不同结果没有可比性。这里也有一个容易混淆的概念很多人喜欢看“benchmark 胜率”认为某个模型在排行榜上胜率高就说明它全面领先。实际上胜率反映的是模型在特定测试集上的相对表现并不等于真实业务中的可用性。理解 benchmark 的具体评测方式和任务边界比只看排名更重要。1.3 Diagram-MMU 的设计目标从命名上看Diagram-MMU 的核心关键词是 Diagram图表和 MMUMulti-Modal Understanding多模态理解。它的定位是面向科学图表的评测基准重点考察模型以下几方面能力图表元素的识别与定位。文字、数值、符号的准确提取。空间逻辑关系的理解。基于图表信息进行推理和回答。这类 benchmark 的出现是因为传统通用多模态评测集中自然图像占比过高科学图表类任务的难度和针对性不足无法有效暴露模型在专业场景下的短板。通过专门设计的数据集和任务Diagram-MMU 可以让开发者更清晰地了解模型在科学图表理解上的真实水平。需要说明的是本文重点不在于复述某个官方榜单的数据而是结合这类 benchmark 的通用设计思路梳理一套可以落地执行的评估方案。这样即使你面对的是自定义的私有图表数据也能快速搭建出属于自己的评测流程。2. 环境准备与版本说明在开始搭建评测流程之前先准备好运行环境。本节涉及的程序、版本都按当前常见环境编写实际使用时需要根据你的项目情况调整。2.1 运行环境操作系统Windows 10/11、Ubuntu 20.04、macOS 均可。Python 版本3.10 或更高。CUDA可选如果本地调用 GPU 模型建议 CUDA 11.8 或 12.1。内存要求仅运行评测脚本8GB 内存即可本地部署 7B 以上模型建议 16GB 以上显存。2.2 安装 Python 依赖推荐使用 conda 或 venv 创建独立的虚拟环境避免依赖冲突。conda create -n diagram-mmu python3.10 conda activate diagram-mmu创建完毕后在项目根目录下新建requirements.txttorch2.1.0 transformers4.40.0 accelerate0.30.0 datasets2.18.0 scikit-learn1.4.0 Pillow10.0.0 pandas2.0.0 openai1.30.0 tiktoken0.6.0 matplotlib3.8.0然后执行安装pip install -r requirements.txt2.3 项目目录结构建议按照下面结构组织代码和数据diagram-mmu/ ├── data/ │ ├── images/ # 图表图片 │ ├── questions.json # 问题与答案 │ └── predictions/ # 模型输出结果 ├── scripts/ │ ├── eval_openai.py # OpenAI API 评测 │ ├── eval_hf.py # HuggingFace 模型评测 │ └── metrics.py # 指标计算 ├── prompts/ │ └── system_prompt.txt └── requirements.txt目录划分的目的很明确data放数据scripts放脚本prompts放模板。这样当你要切换模型或扩展数据集时不需要重构整个项目。3. 核心原理拆解要设计一个合理的科学图表评测流程先要理解评测的底层逻辑。下面从任务类型、评估维度和现有 benchmark 差异三个角度展开。3.1 评测任务类型Diagram-MMU 这类基准通常不会只设计一种任务而是从多个层面考察模型能力。常见的任务类型包括图表描述Diagram Captioning输入一张图表模型需要生成一段通顺、准确的自然语言描述。这个任务看似简单实际上很考验模型能否抓住图表中的关键信息比如“图中展示了 2015 年到 2024 年某地区气温变化整体呈上升趋势峰值出现在 2022 年”。图表问答Diagram Question Answering输入图表和问题模型输出答案。问题既可以是事实型“图中最大柱状体对应的年份是哪一年”也可以是推理型“如果按照当前趋势2025 年的数值最可能是多少”。信息抽取Information Extraction要求模型从图表中抽出指定字段比如轴标签、图例条目、数据点数值。这类任务和生产场景最贴近因为很多知识库构建流程就是先从图表中抽取结构化信息再入库。结构理解Structure Understanding考察模型是否理解图表中元素之间的空间与逻辑关系。比如流程图中的箭头指向、电路图中的连接关系、组织架构图中的上下级关系。3.2 评估维度与指标不同类型的任务需要匹配不同的指标。下面是一张常用的指标对照表任务类型推荐指标说明图表问答选择题Accuracy答案是否与标准答案一致图表问答开放题F1、ROUGE-L答案与参考答案的语义重叠度图表描述ROUGE-L、BERTScore描述文本与参考描述的一致性信息抽取精确率、召回率、F1抽取字段是否正确结构理解Accuracy、关系 F1空间关系判断是否正确除了自动化指标还可以引入“大模型评估”模式即让一个更强的模型作为裁判对输出答案进行打分。这种方式适合答案难以严格匹配的开放场景但需要注意裁判模型的偏好偏差。3.3 与现有通用 Benchmark 的差异在科学图表评测这个方向上Diagram-MMU 并不是孤立存在的。和它相关的已有 benchmark 包括 ChartQA、DocVQA、MathVista、InfoVQA 等。为了更好理解区别这里做一个横向对比Benchmark主要关注点与 Diagram-MMU 的区别ChartQA常见图表问答主要是柱状图、折线图、饼图结构相对简单DocVQA文档页面问答面向整页文档图表只是其中一部分MathVista数学视觉推理范围更广包含几何图形、自然图像Diagram-MMU 方向科学图表全景理解强调专业图表、符号系统与空间结构推理从表格可以看出Diagram-MMU 更聚焦“科学图表”这一细分场景。它的特点在于对领域符号和结构关系的理解要求更高比如分子结构式、电路原理图、气象图等这类图片在通用数据集中占比很低。4. 完整实战搭建一个科学图表评测流程理论部分讲清楚后下面进入实战。我们将搭建一个最小可用的评测系统支持加载本地图片、调用多模态模型、计算指标并输出报告。4.1 准备评测数据评测数据采用 JSON 格式每条样本包含图片路径、问题、参考答案和任务类型。示例数据如下文件路径data/questions.json[ { id: sample_001, image: images/cell_structure.png, question: 图中标注为“线粒体”的部分主要功能是什么, answer: 有氧呼吸的主要场所为细胞提供能量, task: qa }, { id: sample_002, image: images/temperature_trend.png, question: 2015 年到 2024 年哪一年的温度最高, answer: 2022, task: qa }, { id: sample_003, image: images/flow_chart.png, question: 从“输入数据”到“输出结果”中间经过的第一步操作是什么, answer: 数据清洗, task: structure } ]为了便于测试可以先用 matplotlib 生成一张简单的温度变化图作为实验数据。文件路径scripts/generate_demo_image.pyimport matplotlib.pyplot as plt years [2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024] temps [15.2, 15.6, 16.1, 16.3, 16.8, 17.2, 17.5, 18.1, 17.8, 18.3] plt.figure(figsize(8, 5)) plt.plot(years, temps, markero, labelAverage Temperature) plt.xlabel(Year) plt.ylabel(Temperature (°C)) plt.title(Temperature Trend from 2015 to 2024) plt.legend() plt.grid(True, linestyle--, alpha0.5) plt.savefig(../data/images/temperature_trend.png, dpi150) print(图片已生成)运行命令cd scripts python generate_demo_image.py可以看到这张图的峰值年份是 2023 年而不是 2022 年。这是故意设置的干扰项用于验证模型是否能准确读取坐标轴数值。4.2 加载多模态模型评测脚本里最核心的部分是模型调用。下面给出两种常见方式。方式一调用 OpenAI 视觉模型 API。文件路径scripts/eval_openai.pyimport base64 import json import os from openai import OpenAI client OpenAI(api_keyos.environ.get(OPENAI_API_KEY)) def encode_image(image_path): with open(image_path, rb) as image_file: return base64.b64encode(image_file.read()).decode(utf-8) def build_messages(question, image_base64): return [ { role: user, content: [ {type: text, text: question}, { type: image_url, image_url: { url: fdata:image/png;base64,{image_base64} } } ] } ] def eval_openai(image_path, question): base64_image encode_image(image_path) messages build_messages(question, base64_image) response client.chat.completions.create( modelgpt-4o, # 按你的可用模型调整 messagesmessages, temperature0 ) return response.choices[0].message.content if __name__ __main__: with open(../data/questions.json, r, encodingutf-8) as f: questions json.load(f) for item in questions: image_path os.path.join(../data, item[image]) result eval_openai(image_path, item[question]) print(f[{item[id]}] {result})方式二调用 HuggingFace 本地多模态模型。文件路径scripts/eval_hf.pyimport torch from PIL import Image from transformers import AutoProcessor, AutoModelForVision2Seq model_id Qwen/Qwen2-VL-7B-Instruct # 按实际模型调整 processor AutoProcessor.from_pretrained(model_id, trust_remote_codeTrue) model AutoModelForVision2Seq.from_pretrained( model_id, torch_dtypetorch.float16, device_mapauto, trust_remote_codeTrue ) def eval_hf(image_path, question): image Image.open(image_path).convert(RGB) messages [ { role: user, content: [ {type: image, image: image}, {type: text, text: question} ] } ] text processor.apply_chat_template(messages, tokenizeFalse) inputs processor(text[text], images[image], return_tensorspt) inputs inputs.to(model.device) with torch.no_grad(): outputs model.generate(**inputs, max_new_tokens256) response processor.decode(outputs[0], skip_special_tokensTrue) return response if __name__ __main__: result eval_hf(../data/images/temperature_trend.png, 2015 年到 2024 年哪一年的温度最高) print(result)这里需要提醒一点上面的本地模型代码是示意性的不同模型的AutoProcessor和AutoModelForVision2Seq加载方式可能不完全一致。如果遇到加载报错优先查看模型卡的官方示例。4.3 构造评测 PromptPrompt 对评测结果影响很大。同一个问题直接问和加上约束条件后问答案质量可能完全不同。推荐在评测时使用固定的系统级 Prompt减少变量。文件路径prompts/system_prompt.txt你是一名专业的科学图表分析助手。请仔细观察用户提供的图表回答问题。 要求 1. 只依据图表中的信息作答不要猜测图中不存在的数值。 2. 如果图表中包含坐标轴、图例、单位请优先参考这些信息。 3. 输出答案时保持简洁直接给出结果或简短解释。 4. 如果图表信息不足以回答问题请明确回答“无法判断”。这个 Prompt 有两个重要作用一是要求模型“只依据图表信息作答”减少幻觉二是允许模型回答“无法判断”避免强行输出错误答案。在代码中可以这样调用system_prompt open(../prompts/system_prompt.txt, r, encodingutf-8).read() def build_messages_with_system(question, image_base64, system_prompt): return [ {role: system, content: system_prompt}, { role: user, content: [ {type: text, text: question}, { type: image_url, image_url: { url: fdata:image/png;base64,{image_base64} } } ] } ]4.4 计算评估指标模型输出结果后需要计算指标。下面是一个支持选择题准确率和开放题 F1 分数的脚本。文件路径scripts/metrics.pyimport json import re from sklearn.metrics import accuracy_score from collections import Counter def normalize_answer(text): 简单标准化转小写、去除标点空格 text text.lower().strip() text re.sub(r[^\w\u4e00-\u9fa5], , text) return text def f1_score_for_prediction(prediction, ground_truth): pred_tokens Counter(normalize_answer(prediction)) truth_tokens Counter(normalize_answer(ground_truth)) if len(pred_tokens) 0 or len(truth_tokens) 0: return 0.0 common sum((pred_tokens truth_tokens).values()) if common 0: return 0.0 precision common / sum(pred_tokens.values()) recall common / sum(truth_tokens.values()) return 2 * precision * recall / (precision recall) def compute_metrics(predictions_file): with open(predictions_file, r, encodingutf-8) as f: results json.load(f) acc_list [] f1_list [] for item in results: pred item[prediction] true_answer item[answer] if item.get(task) choice: acc_list.append(normalize_answer(pred) normalize_answer(true_answer)) f1_list.append(f1_score_for_prediction(pred, true_answer)) metrics {} if acc_list: metrics[accuracy] accuracy_score([1] * len(acc_list), acc_list) metrics[f1] sum(f1_list) / len(f1_list) if f1_list else 0.0 return metrics if __name__ __main__: metrics compute_metrics(../data/predictions/results.json) print(metrics)4.5 运行与验证为了串联整个流程写一个批量评测入口脚本。文件路径scripts/run_eval.pyimport json import os from eval_openai import eval_openai DATA_DIR ../data PRED_FILE ../data/predictions/results.json def main(): with open(../data/questions.json, r, encodingutf-8) as f: questions json.load(f) outputs [] for item in questions: image_path os.path.join(DATA_DIR, item[image]) prediction eval_openai(image_path, item[question]) outputs.append({ id: item[id], question: item[question], answer: item[answer], prediction: prediction, task: item.get(task, qa) }) print(f[{item[id]}] 预测: {prediction}) os.makedirs(os.path.dirname(PRED_FILE), exist_okTrue) with open(PRED_FILE, w, encodingutf-8) as f: json.dump(outputs, f, ensure_asciiFalse, indent2) from metrics import compute_metrics print(compute_metrics(PRED_FILE)) if __name__ __main__: main()运行cd scripts export OPENAI_API_KEY你的API Key # Windows 使用 set OPENAI_API_KEYxxx python run_eval.py预期输出类似[sample_001] 预测: 线粒体是有氧呼吸的主要场所与能量供应有关。 [sample_002] 预测: 2023年温度最高。 [sample_003] 预测: 数据清洗 {f1: 0.72}注意sample_002的正确答案是 2023而参考 JSON 中的answer字段如果写成 2022会得到一个较低的 F1。这说明评测数据的质量会直接影响最终分数写评测集时需要反复核对答案。5. 常见问题与排查思路在搭建评测流程时很容易遇到各种问题。下面根据实际经验整理了一份排查表。问题现象常见原因解决思路API 调用报 401API Key 未设置或已过期检查环境变量和密钥是否有效图片编码报错图片路径错误或文件损坏确认image_path存在并检查图片格式模型输出答非所问Prompt 没有给出明确约束使用系统 Prompt要求模型仅依据图表作答答案格式不一致模型自由发挥答案带解释要求模型按固定格式输出或用正则后处理指标分数异常低标准答案与模型答案表达差异大使用更宽松的匹配方式或引入大模型评估本地模型显存不足模型参数过大改用更小模型或使用量化加载并发调用被限流请求频率过高增加请求间隔或使用重试机制生成图片中文乱码matplotlib 未配置中文字体配置系统字体或改用英文标注5.1 一个实际排错案例有次我运行批量评测发现某张流程图的问答结果总是错误。排查后发现问题不在于模型而在于图片标注文字太小模型视觉编码器无法准确识别。这个案例提醒我们在评测科学图表时图片的分辨率、标注字体大小、前景背景对比度都是影响效果的重要因素。在代码层面可以通过预处理提高图片质量from PIL import Image, ImageEnhance def preprocess_image(image_path, scale2.0): image Image.open(image_path).convert(RGB) image image.resize( (int(image.width * scale), int(image.height * scale)), Image.LANCZOS ) enhancer ImageEnhance.Contrast(image) image enhancer.enhance(1.2) return image5.2 如何避免评测结果“虚高”一个很常见的误区是用同一批数据反复调 Prompt直到模型在评测集上得分很高。这种做法会导致评测结果虚高无法反映模型在真实场景下的表现。建议的做法是将数据划分为开发集和测试集。开发集用于调 Prompt测试集只用于最终评测。每次调整后记录版本号和得分变化。最终报告同时给出开发集和测试集指标。6. 最佳实践与工程建议评测工作不只是写脚本跑分数更是一套需要长期维护的工程体系。下面从数据、模型、指标、安全四个角度给出建议。6.1 数据集构建与质量控制数据是 benchmark 的核心资产。构建数据集时要注意答案必须经过人工核对不能只依赖自动抽取。图片要覆盖多种类型统计图、流程图、结构图、示意图。问题难度要有梯度既要有简单的事实题也要有需要推理的复杂题。避免图片中存在被截断的坐标轴、模糊文字等“无效干扰项”。如果数据来自公开论文或书籍还要注意版权问题。比较稳妥的方式是自行绘制或使用开放许可的图表。6.2 评测 Prompt 标准化不同评估者写出的 Prompt 往往差异很大导致同一个模型在不同评测中的分数不同。建议将 Prompt 视为评测配置的一部分纳入版本管理。可以维护三类 Prompt标准评测 Prompt所有模型统一使用保证公平。场景化 Prompt针对特定业务场景模拟真实用户提问方式。压力测试 Prompt故意增加复杂指令考察模型边界。6.3 指标解读与报告不要只看单一指标。以科学图表问答为例准确率只能反映“答对了多少”而 F1 可以反映答案的重叠程度。建议报告时同时列出样本数量和各任务类型分布。分任务准确率。失败样本分析。模型高频错误类型排名例如“数值读取错误”“空间关系判断错误”“符号识别错误”。有了错误类型分析后续优化才能有的放矢。6.4 安全与合规注意事项在多模态评测中图片数据可能包含个人信息、商业机密或受版权保护的内容。在实际工程落地时要注意不将私有图片直接上传到外部 API除非确认数据脱敏且符合公司安全规范。评估外部模型时对数据进行最小化处理只保留评测所需字段。建立评测数据访问权限避免数据泄露。如果需要长期使用外部 API 做批量评测先确认供应商的数据使用条款。7. 总结与下一步实践本文从多模态科学图表评测这个场景出发围绕 Diagram-MMU 这类 benchmark 介绍了背景、核心任务、评估维度并通过一个完整示例演示了评测流程的搭建从数据准备、模型调用、Prompt 设计到指标计算全部环节都给出了可复用的代码。如果你正在做多模态大模型相关的应用下一步有几个可以继续深入的方向扩展评测数据集加入更多专业领域的科学图表。对比多个模型在同一数据集上的表现分析各自的优势与短板。引入更细粒度的评估维度比如空间关系正确率、数值读取误差。将评测流程接入 CI/CD在模型更新时自动执行回归测试。最后补充一个实践经验不要一开始就追求大而全的评测集。先用 50 到 100 条精心标注的样本把流程跑通再逐步扩展这样既能控制数据质量也方便定位问题。如果这篇文章对你有帮助可以收藏备用后续需要搭建多模态模型评测体系时直接照着做。