news 2026/9/16 3:38:37

多模态模型评测实战:MMBench与OpenCompass从原理到落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
多模态模型评测实战:MMBench与OpenCompass从原理到落地

多模态模型这两年可以说是遍地开花,从开源社区的Qwen-VL、InternVL,到各家闭源的GPT-4V、Claude,代码能力、推理能力一个比一个能打。但真到了要落地选型的时候,问题就来了:排行榜上那些分数到底靠不靠谱?同一个模型,A榜单说第一,B榜单说垫底,论文里晒的指标和自己在业务数据上跑出来的效果,经常是两回事。这种时候,一个设计合理、维度清晰、能复现的评测基准就非常重要了。今天想聊的MMBench,就是OpenCompass体系里专门针对多模态模型设计的一套评测基准,也是我实际用下来觉得最顺手、最能说明问题的一套工具。

这篇文章主要面向三类人:一是准备在论文里放评测结果的同学,二是想从一堆开源多模态模型里挑一个来用的工程师,三是自己微调了模型、想验证一下效果到底有没有提升的研究者。我会把MMBench到底考什么、怎么在OpenCompass里跑通、结果怎么看、以及自己接模型时容易踩的坑一次讲清楚。

1. MMBench在考什么:从"总分好看"到"能力画像清晰"

1.1 为什么需要一套独立的多模态评测基准

先说说背景。多模态模型和纯文本模型有个本质区别:输入不仅有文字,还有图像;模型需要先"看懂图",再结合文字指令给出答案。这就意味着,评测一个多模态模型,光看它文本能力有多强是不够的,还得看它的视觉感知、视觉推理、跨模态对齐能力。很多早期的评测基准其实是直接把纯文本benchmark套过来,在问题里加一张图就完事了,维度很粗糙,模型瞎猜也能拿不错的分数。

MMBench的思路不太一样。它的全称是A Comprehensive Multi-modal Benchmark,主打的就是"细粒度能力拆解"。它不是给你一个笼统的正确率,而是把视觉理解拆成二十个左右的能力维度,比如OCR识别、属性推理、空间位置推理、图表理解、动作识别、常识推理等等,每个维度单独出题、单独计分。跑完一轮评测,你拿到的不是"这个模型准确率82%",而是一张能力雷达图:OCR很强,空间推理一般,动作理解偏弱。这种颗粒度对于模型选型和迭代方向判断,价值比总分高得多。

1.2 选择题为主的设计逻辑与防偏置技巧

MMBench的大部分题目是四选一或多选的选择题,少数是判断题和填空题。为什么用选择题?因为自动评测需要稳定、可比的打分标准。生成式答案要拿去和参考答案做匹配,这对模型的输出格式要求太高,稍有不一致就误判;选择题则天然适合程序判分,模型只需要输出字母选项就行。

但选择题有个隐患:模型可能瞎猜。四个选项,蒙也有25%的正确率。为了压低随机猜测的影响,MMBench的题目并不是简单地把正确答案放在固定位置,而是会做选项打乱和干扰项设计。我在跑评测的时候还注意到一个细节:same/different这类对比题和顺序推理题,命题上会刻意让模型必须真正理解图像内容才能做对,光靠语言先验和上下文套路是答不出来的。这也是为什么MMBench的分数比很多"看图说话"型基准更有区分度。

1.3 DEV测试集与TEST测试集:本地调试和官方验证怎么分工

用过OpenCompass的朋友应该见过MMBench_DEV和MMBench_TEST这两类数据集名。DEV是开发集,题目数量少,主要用于本地快速验证代码有没有跑通、模型有没有加载成功、prompt格式对不对。TEST才是正式测试集,题目多、覆盖全,但不会直接公开标准答案,需要把模型输出提交到官方服务器或者用官方脚本判分。这里有个常见的认知误区:DEV集的分数不能当作最终成绩写进论文,它只能用来做开发阶段的冒烟测试和调参。真正的结论一定要基于TEST集的结果。

版本方面,目前主流的是MMBench_V1.0和更新版本的MMBench_2024系列。我在后面的章节里给出的命令默认基于OpenCompass 0.7.x以上版本和2024版数据集。如果你还在用旧版本,建议尽快升一下,因为旧版数据集在题目清洗和标注质量上确实有些小问题。

2. 在OpenCompass里跑通MMBench:完整实操流程

2.1 环境准备和最容易漏掉的依赖

OpenCompass的安装其实不算复杂,官方推荐用conda建独立环境:

conda create -n opencompass python=3.10 -y conda activate opencompass pip install -U opencompass

但这里有个坑:如果你要评测的是HuggingFace上的开源模型,OpenCompass需要依赖推理后端来做模型加载和生成。默认用的是lmdeployvllm,这两者安装的时候有很多系统级依赖,比如CUDA Toolkit版本需要和你的显卡驱动匹配。我建议先安装PyTorch,再装lmdeploy,最后装opencompass,这个顺序踩坑最少:

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 pip install lmdeploy pip install opencompass

装完以后可以跑一下自检命令,确认各个模块都正常:

python -m opencompass.cli.main --help

如果工具版本冲突,常见于mmcv、mmdet这类视觉库之间的依赖打架。我的建议是别用最新的,就用官方requirements.txt里锁定的版本,省心很多。

2.2 内置的MMBench数据集和模型配置说明

OpenCompass的一个设计亮点是"模型配置"和"数据集配置"分离。你要做的只有两件事:写一个模型文件告诉它"我要测哪些模型",写一个数据集参数告诉它"用哪个基准跑"。

MMBench在OpenCompass里内置的数据集名是:

  • MMBench_DEV_EN:英文开发集
  • MMBench_DEV_CN:中文开发集
  • MMBench_TEST_EN:英文测试集
  • MMBench_TEST_CN:中文测试集
  • MMBench_2024_DEV_ENMMBench_2024_TEST_EN等:更新的版本

实际跑评测时,你不需要自己写数据加载代码,直接用字符串指定就行。模型端则支持两类方式:一类是直接在HuggingFace上跑transformers加载,适合快速验证;另一类是走lmdeploy/vllm等推理后端,适合追求吞吐和批量评测。我自己一般先用transformers方式跑通小样本,再用lmdeploy跑全量TEST集,两个后端的结果一致性在OpenCompass里处理得还算不错。

2.3 跑通一个最小可复现的评测命令

假设我想评测HuggingFace上的Qwen/Qwen2.5-VL-7B-Instruct模型,在英文开发集上做验证,命令长这样:

python run.py \ --models hf_llava_qwen2 \ # 这只是示意,实际有对应的配置名 --datasets MMBench_DEV_EN \ --debug

不过说实话,直接用run.py加内置配置名的方式,对自定义模型并不友好,因为内置配置里的模型路径基本都是预设好的。更通用的做法是写一个Python配置文件,然后传给OpenCompass执行。我先给一个最小可跑的示例。

configs/models/下新建my_mllm.py

from opencompass.models import HuggingFacewithChatTemplate from opencompass.utils.text_postprocessors import first_capital_postprocess models = [ dict( type=HuggingFacewithChatTemplate, abbr='qwen2.5-vl-7b-instruct', path='Qwen/Qwen2.5-VL-7B-Instruct', max_out_len=512, batch_size=4, model_kwargs=dict(device_map='auto', trust_remote_code=True), run_cfg=dict(num_gpus=1), ) ]

configs/datasets/下指定数据集:

from opencompass.configs.datasets.mmbench.mmbench_2024_gen import mmbench_2024_datasets datasets = mmbench_2024_datasets

然后执行:

python run.py --config configs/eval_my_mllm.py

跑完以后,OpenCompass的outputs/目录会生成时间戳文件夹,里面包含模型的推理结果jsonl、评测打分结果json和一份summary汇总文件。这些都是可直接查的。

2.4 评测结果都输出到了哪里

很多新手跑完run.py之后一脸懵:结果呢?其实OpenCompass的输出目录结构是**outputs/{任务时间戳}/{模型名}/{数据集名}/**,时间戳文件夹下会有完整的运行日志。最核心的文件是:

  • .jsonl格式的推理结果:每条记录都带着输入图片路径、原文问题、模型预测输出、参考答案
  • results/下的打分json:按数据集、按科目类别列出准确率和样本数
  • summary/下的汇总表:多个模型横向对比时看这个最方便

我的习惯是:跑完先看jsonl文件,抽查几十条模型预测和参考答案。这一步能在几分钟内发现很多分数解释不了的问题,比如模型可能输出了正确答案但格式是中文括号、多打了空格导致判分失败。光看总分很容易被误导。

3. 从分数看到的东西:MMBench指标解读与对比技巧

3.1 整体准确率 vs 细分维度准确率

MMBench评测结果里,除了一个整体accuracy之外,还会按能力维度输出细分准确率。以2024版为例,常见的细分维度包括字符识别、物体属性、空间关系、计数、图表理解、常识推理、情感理解等。看分数时我的建议是:

  1. 先看整体准确率,判断模型在这套基准上大概处于什么水平,和公开SOTA差距有多大;
  2. 再看细分维度,找出模型的短板。比如一个OCR能力很强的模型,字符识别分数可能接近90%,但空间关系只有50%多,这就说明它在"定位和相对位置理解"上还需要提升;
  3. 对比两个模型时不要只比总分。模型A总分略高于模型B,但模型B在中文科目和图表理解上大幅领先,那在具体场景里模型B可能反而是更好的选择。

我举一个实际对比的例子(数值仅供示意):

模型整体准确率字符识别空间关系图表理解中文题目
模型A82.3%91.5%68.2%76.4%79.8%
模型B80.7%85.2%74.6%83.1%86.3%

如果只看总分,模型A赢了;但从我的使用场景看,模型B的图表理解更稳,中文指令兼容性更好,总分低的2个百分点完全可以用prompt优化或微调补回来。这就是细粒度评测的价值。

3.2 几个判断"分数可信度"的信号

评测过程中我发现,有些分数偏低不是模型能力问题,而是评测链路出问题了。总结几个高频信号:

  • 所有题目都输出同一个选项:这种情况多半是模型的chat template和MMBench的prompt模板不匹配,模型根本没理解指令,一直在重复系统提示词或第一个选项。检查模型配置里的HuggingFacewithChatTemplate应用是否正确。
  • 分数接近随机水平(选择题25%左右):要么模型本身真的没视觉能力,要么图像没有正确传进去。视觉模型输入需要走特殊的message格式,如果直接按纯文本格式拼图,部分模型会直接忽略图片。
  • 中文题目分数异常低但英文正常:可能是中文分词或指令模板里用了英文的标点符号,导致模型对中文指令理解偏弱。这个时候可以先检查prompt里的中文标点是否有问题。

3.3 对比实验时必须要对齐的条件

做模型横向对比时,最忌讳的是各跑各的、参数不统一。我给自己定了几条强制对齐规则:

  • max_out_len一致:输出长度限制影响模型回答完整度。一个限制128,另一个限制1024,分数几乎没有可比性。
  • batch_size保持一致:虽然理论上batch大小不影响单条样本结果,但在lmdeploy等推理后端下,batch变化可能影响beam search的行为,导致细微差异。
  • 少样本示例数量一致:MMBench默认是zero-shot评测,但有些模型在OpenCompass配置里会带上few-shot的示例。对比前一定要确认所有模型都处于同样的few-shot设置,否则就是拿榜单分数和自家分数硬比,没有意义。
  • 运行环境可复现:设置固定随机种子(RANDOM_SEED),虽然模型推理本身是确定性的,但数据加载顺序、打乱逻辑会影响最后的汇总结果。

4. 微调/部署自己的多模态模型?这样把它接进评测

4.1 为什么默认配置不够用

OpenCompass内置的模型配置都是针对公开模型写的,路径直接指向HuggingFace上的官方权重。如果你自己微调过一个模型,或者把模型量化了、换了基础底座,就需要自定义模型配置。这一节我会讲两种主流方式:一种是直接写HuggingFace加载配置,另一种是走推理后端的方式。

4.2 用HuggingFace加载配置接入评测

最简单的方式是写一个Python配置,让OpenCompass用transformers的AutoModelForVision2Seq方式加载模型:

from opencompass.models import HuggingFacewithChatTemplate models = [ dict( type=HuggingFacewithChatTemplate, abbr='my-finetuned-vlm', path='/path/to/your/model', # 本地路径或HuggingFace模型ID max_out_len=1024, batch_size=1, model_kwargs=dict( device_map='auto', torch_dtype='bfloat16', ), run_cfg=dict(num_gpus=1, gpu_ids=[0]), ) ]

这里最关键的参数是type=HuggingFacewithChatTemplate。它会按照模型自身的chat template自动包装对话格式,不用你手动拼prompt。对于多模态模型,OpenCompass会把图像嵌入成特殊的message格式传进去,避免"图片没真正输入"的尴尬。

4.3 unsloth加速加载与评测的实践思路

如果你的模型是用unsloth微调的,或者你想用unsloth的低显存加载特性来降低评测门槛,也有办法。unsloth的FastLanguageModel.from_pretrained在底层是兼容HuggingFace权重格式的,所以微调出的模型可以直接当成普通模型路径传给OpenCompass,不冲突。

我在本地评测一台24GB显存的卡上跑7B级别的多模态模型时,就先用unsloth把模型转换成4bit加载,再接入OpenCompass跑MMBench_DEV。过程和直接加载原模型完全一样,占用的显存从接近20GB降到了10GB左右。不过要提醒一下,4bit量化会略微损失精度,分数可能比bf16加载低1到3个百分点,所以正式出论文结果时建议用bf16或fp16跑一遍确认。

如果你只是想先快速看看unsloth模型在MMBench上的表现,可以在自己的脚本里这样加载模型和处理器:

from unsloth import FastLanguageModel from transformers import AutoProcessor model, tokenizer = FastLanguageModel.from_pretrained( model_name="your-finetuned-model", max_seq_length=4096, load_in_4bit=True, ) processor = AutoProcessor.from_pretrained("your-finetuned-model") # 然后可以自己写一个循环读MMBench的jsonl # 按OpenCompass的格式逐条构造对话,最后算accuracy

这样绕过了OpenCompass的调度,但可以快速验证模型有没有基本的多模态能力,适合在正式接入OpenCompass之前做冒烟测试。注意实际评测时还是要以OpenCompass的结果为准,因为prompt模板和判分逻辑都经过严格校准。

4.4 接入API模型的配置思路

如果你要评测的是API模型(比如GPT-4V或Claude),OpenCompass也提供了对应的API模型类型,例如GPT4VQwenVLAPI等。配置方式类似:

from opencompass.models import GPT4V models = [ dict( type=GPT4V, abbr='gpt-4v', key='YOUR_API_KEY', max_out_len=1024, run_cfg=dict(num_gpus=0), ) ]

跑API模型的好处是本地不需要任何GPU资源,但要注意API限流。MMBench测试集题目多,一次性全跑可能会触发限流报错,建议在配置里降低batch_size或增加任务重试次数。我这个月跑API模型时就被限流过两次,所以现在都会在run_cfg里加retry=3

5. 实操中我踩过的坑与避坑经验

5.1 视觉模型显存占用比想象中高

MMBench的题目是图加文的组合输入,视觉编码器、连接层和语言模型三块都要驻留显存。我第一次用batch_size=8跑一个13B模型,直接OOM。后来把batch_size降到2,再配合device_map='auto'才稳定跑完。经验是:7B模型的batch_size建议不超过4,13B模型的batch_size建议1到2。多卡的话可以在run_cfg里指定num_gpus=2,OpenCompass会自动做张量并行。

5.2 chat template不一致导致莫名其妙低分

这是最坑的一次排错经历。我用一个刚微调完的模型跑MMBench,分数低得离谱,30%不到。排查链路是这样的:

  1. 先看OpenCompass的推理结果文件,发现模型输出的是正常的英文句子,不是字母选项。
  2. 再仔细看,模型的输出里经常出现"Explanation: ..."这类推理过程,把答案埋在一大段文字里。
  3. 判分逻辑是提取首字母或查找选项字母,结果自然找不到。

根因是我的微调数据里包含大量"先推理后作答"的思维链样本,模型学会了长篇输出,而MMBench指令只要求在末尾输出选项字母。解决方案有两种:一是在prompt里追加一句"Answer with the option letter only.";二是在配置里设置postprocessor,例如用OpenCompass内置的first_capital_postprocess函数,提取输出文本中第一个大写字母作为答案。

from opencompass.utils.text_postprocessors import first_capital_postprocess dict( type=HuggingFacewithChatTemplate, ... postprocessor=first_capital_postprocess, )

这个坑说明一个很重要的问题:评测分数低,先别急着否定模型,先看原始输出格式是否满足判分要求

5.3 DEV和TEST选错的教训

有一次跑通了DEV集,结果很理想,我顺手把评测脚本里的数据集名从MMBench_DEV_EN直接改成MMBench_TEST_EN,心里想着无非是题目多一点、跑久一点。结果跑了两个多小时,看到输出文件里有些图片是损坏的、有些选项没对齐,最后那一轮数据几乎报废。后来问了社区才知道,TEST集有些样本的图片存储在第三方图床,网络环境不稳会加载失败。

现在的做法是:跑TEST集之前,先检查图片是否全部成功下载;如果失败,用OpenCompass提供的下载脚本把图片预取到本地缓存。网络条件稳定的环境下,TEST集的题目质量明显高于DEV集,但前提是数据完整加载。

5.4 随机性和复现:评测结果波动多少算正常

有人问我,同一个模型跑两次MMBench,分数差1.5个百分点正常吗?正常。原因有几个:

  • 少样本评测时会随机采样示例,不同次运行示例不同,结果自然有波动;
  • 多模态模型的图像处理器可能引入微小的预处理随机性;
  • 批量推理时,推理后端的beam search和采样参数在不同显存布局下也会有细微差异。

为了尽量复现,我在每次跑之前固定几个东西:

  • 固定seed(在OpenCompass里可以通过环境变量或配置文件设置)
  • 固定推理后端的采样参数,temperature=0do_sample=False,用贪心解码
  • 固定数据集加载时的shuffle种子

但即便如此,不同机器不同版本下1到2个百分点的波动依然存在。所以我的习惯是重要榜单结果要跑三次取均值。这虽然费点时间,但可信度完全不一样。特别是要向外部报告数字的时候,单次分数很容易被质疑。

5.5 中文评测集和英文评测集要分开看

MMBench有英文和中文两个版本,道理上中文版只是把题干和选项翻译成中文,但实测下来,很多模型的英文分数和中文分数能差到15个百分点以上。原因主要有两点:一是模型的中文指令跟随能力本身有差异;二是中文题目涉及的古诗词、俗语、成语等文化常识,对很多主要在英文语料上训练的模型来说,难度更大。所以如果你的业务场景是中文,一定要单独跑MMBench_DEV_CNMMBench_TEST_CN,不能直接用英文TEST集分数代表一切。

我自己在项目里就是中英文分开记录的,选型的时候也看具体业务语言分布来决定权重。单纯追求英文榜单第一,对国内业务落地帮助其实有限。

我在实际使用OpenCompass和MMBench的过程中,最大的感受是它把原来"看一眼模型输出,凭感觉说好坏"的评测过程,变成了一套可有迹可循、可分维度拆解的量化体系。跑一次不容易,但跑完拿到的信息密度,比单纯刷榜单高太多了。

最后再分享一个小技巧:如果你要在论文里报告MMBench分数,官方实际上是要求使用OpenCompass指定版本来跑的,论文里也建议注明"MMBench (OpenCompass version)",因为不同工具链、不同prompt模板下同一模型的分数可能差好几个点。提交结果之前多核对一眼自己的版本号和配置,能省掉很多审稿人追问的麻烦。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 3:37:51

鸿蒙电商全栈实战:从开发到上架变现的完整指南

做了几年移动端开发,身边一直有同行问我:鸿蒙到底值不值得押注?我的答案很明确——如果你要做的是电商购物这类带真实交易闭环的应用,现在进入鸿蒙生态,窗口期的红利比安卓和iOS早期还要明显。我最近完整做完了一个鸿蒙…

作者头像 李华
网站建设 2026/9/16 3:36:39

STM32嵌入式DFT实战:资源受限下的实时频谱分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 3:36:25

Java GC优化实战:从Full GC排查到JVM参数调优

1. 从一个“卡死”的下午说起:我为什么开始认真做GC优化先说个亲身经历。有一年我在负责一个订单履约系统,平时跑得好好的,结果一到午高峰就频繁报警:接口P99延迟从50ms直接飙到800ms,CPU居高不下,老年代GC…

作者头像 李华
网站建设 2026/9/16 3:35:09

COMSOL复现BIC拓扑荷:光子晶体超表面远场偏振涡旋计算全流程

做BICs和光子晶体超表面计算有一段时间了,最让我头疼也最上头的,就是复现文献里那种“围绕BIC动量点的远场偏振矢量涡旋图”。一句话说清楚的话,拓扑BICs(连续谱束缚态)在动量空间是一个偏振奇点,远场偏振矢…

作者头像 李华
网站建设 2026/9/16 3:34:32

51单片机霍尔转速测量与PWM调速设计详解

简介:面向单片机初学者及课程设计/毕业设计学生,这套基于51单片机的综合设计覆盖霍尔转速测量、DS18B20温度检测、LCD1602显示以及按键控制的PWM电机调速,通过L298N驱动电路实现启停、正反转、加减速,功能完整,可作为项…

作者头像 李华
网站建设 2026/9/16 3:33:23

STM32F429双CAN测试程序详解:从引脚映射到Bus Off恢复

简介:面向STM32F429嵌入式开发者的双CAN通道测试程序,用于验证CAN1与CAN2两个独立控制器的初始化配置、报文收发和中断处理,适合汽车电子、工业自动化领域的中初级开发者学习。程序基于HAL库实现,围绕ISO 11898协议标准&#xff0…

作者头像 李华