ARTICLE DETAIL

资讯详情

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

GPT-4o差点没及格!首个多任务长视频评测基准MLVU,它有亿点难|TaoToken实测

GPT-4o差点没及格!首个多任务长视频评测基准MLVU,它有亿点难|TaoToken实测 1. 为什么我想在本地复现一次 MLVU 长视频评测MLVU 是智源联合北邮、北大、浙大等高校推出的多任务长视频理解评测基准全称 Multi-task Long Video Understanding Benchmark。它想解决的核心问题是过去大部分视频评测基准只覆盖 1 分钟以内的短视频任务单一、领域狭窄模型甚至能靠文本先验直接猜答案。MLVU 把视频时长拉到 3 分钟到 2 小时以上平均 12 分钟并设计了 9 类任务分成全面理解、单细节理解、多细节理解三大类同时包含单选和开放生成两种题型。我第一次看到「GPT-4o 单选平均准确率只有 64.6%」这个数字时是有点意外的。GPT-4o 在图像理解上表现一直不错但放到长视频、多细节定位的场景里正确率直接掉到及格线附近。更关键的是论文里提到大部分模型性能会随视频时长增加显著下降这说明长上下文和跨片段推理仍然是硬骨头。所以这篇文章不是复述论文而是带你用统一的 API 通道把 MLVU 的评测流程跑一遍。适合谁手里有 GPT-4o 或其他多模态模型访问能力、想验证长视频理解效果、又不想在多个平台之间来回切 Key 的开发者。我会把 Base URL、Key、Model ID 三件套写清楚再给可复制的评测脚本和逐任务对照表。整个过程围绕 TaoToken 这个统一通道展开官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 不带多余参数。需要先说明一点MLVU 官方仓库在 GitHub 上提供了数据集和评测代码但真正跑起来时视频抽帧、问题拼接、请求发送、结果解析这几步很容易卡住。尤其是多细节理解任务一个问题可能对应视频里好几个不连续的片段如果抽帧策略不对模型拿到的上下文就是残缺的。我下面会按「先跑通单任务再扩到全量」的顺序来写避免一上来就被 2000 多个问题淹没。2. TaoToken 统一通道准备Base URL、Key 与模型 ID 三件套在开始写评测脚本之前先把访问通道固定下来。我选择用 TaoToken 作为统一入口原因是 MLVU 评测里会频繁切换模型做对照如果每个模型都去单独申请 Key、记不同的 Base URL脚本里会到处是分支判断维护成本很高。TaoToken 提供 OpenAI 兼容的接口格式Base URL 统一为 https://taotoken.net/api 模型 ID 按平台文档填写Key 在控制台生成。具体操作路径是这样的先打开 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 进入 API Keys 页面创建一个新 Key复制出来保存好。这个 Key 只显示一次丢了就得重建。然后确认你要用的模型 ID比如 GPT-4o 对应的标识以及如果你要做开源模型对照InternVL、LLaVA 系列在平台上的可用标识。模型 ID 写错会直接返回 404 或 model not found这个后面排障章节会细说。配置方式我推荐用环境变量不要硬编码在脚本里。Linux 或 macOS 下可以这样写export TAOTOKEN_API_KEYsk-你的Key export TAOTOKEN_BASE_URLhttps://taotoken.net/api export MLVU_MODEL_IDgpt-4oWindows PowerShell 下用$env:TAOTOKEN_API_KEYsk-你的Key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api $env:MLVU_MODEL_IDgpt-4o如果你用 Python 的 openai SDK客户端初始化可以写成这样注意 base_url 结尾不要多加/v1平台已经做了兼容处理import os from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], ) MODEL_ID os.environ.get(MLVU_MODEL_ID, gpt-4o)这里有个细节MLVU 的输入是视频加问题而 OpenAI 兼容接口里视频通常以帧序列或视频文件的形式传入。GPT-4o 支持多图输入所以我的做法是把长视频按固定间隔抽帧再把帧作为 image_url 列表塞进 messages。抽帧数量要控制太多会超上下文太少会丢细节。我实测下来3 分钟视频抽 16 帧、12 分钟视频抽 32 帧、超过 30 分钟抽 48 帧是一个比较平衡的起点。这个策略不是官方规定而是我在跑通流程时用的默认值你可以根据任务类型调整。另外如果你要做长期、批量的评测任务可以考虑 Coding Plan 这类按周期计费的方式入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。单次验证用按量 Key 就够了不用一上来就上套餐。3. 可复制配置MLVU 评测脚本与 settings 片段这一节直接给能跑的代码。我按「数据加载 → 抽帧 → 构造请求 → 解析答案 → 记录得分」五步来写每一步都对应 MLVU 的一个实际环节。先看配置文件我用 JSON 存评测参数路径放在项目根目录的configs/mlvu_eval.json{ base_url: https://taotoken.net/api, model_id: gpt-4o, dataset_root: ./MLVU/data, video_dir: ./MLVU/videos, frame_strategy: { short: {max_minutes: 3, frames: 16}, medium: {max_minutes: 12, frames: 32}, long: {max_minutes: 999, frames: 48} }, tasks: [ plot_qa, video_summarization, needle_qa, ego_qa, action_count, action_order, topic_reasoning, anomaly_recognition, sub_scene ], output_file: ./results/mlvu_gpt4o_scores.json }这个 JSON 里的 tasks 列表对应 MLVU 的 9 类任务。注意needle_qa和ego_qa属于单细节理解action_count、action_order、anomaly_recognition属于多细节理解plot_qa、video_summarization、topic_reasoning、sub_scene更偏全面理解。分类不是绝对的但按这个分组看结果会更清楚。接下来是抽帧和请求构造的核心脚本mlvu_runner.pyimport base64 import json import os import cv2 from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlhttps://taotoken.net/api, ) def load_config(path./configs/mlvu_eval.json): with open(path, r, encodingutf-8) as f: return json.load(f) def pick_frame_count(duration_min, strategy): if duration_min strategy[short][max_minutes]: return strategy[short][frames] if duration_min strategy[medium][max_minutes]: return strategy[medium][frames] return strategy[long][frames] def extract_frames(video_path, num_frames): cap cv2.VideoCapture(video_path) total int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) fps cap.get(cv2.CAP_PROP_FPS) or 25 duration_min total / fps / 60 step max(total // num_frames, 1) frames [] for i in range(0, total, step): cap.set(cv2.CAP_PROP_POS_FRAMES, i) ok, frame cap.read() if not ok: break _, buf cv2.imencode(.jpg, frame, [cv2.IMWRITE_JPEG_QUALITY, 80]) b64 base64.b64encode(buf).decode(utf-8) frames.append(fdata:image/jpeg;base64,{b64}) if len(frames) num_frames: break cap.release() return frames, duration_min def build_messages(frames, question): content [{type: text, text: question}] for f in frames: content.append({type: image_url, image_url: {url: f}}) return [{role: user, content: content}] def ask_model(messages, model_id): resp client.chat.completions.create( modelmodel_id, messagesmessages, temperature0, max_tokens512, ) return resp.choices[0].message.content.strip()这段代码里有两个容易踩坑的地方。第一cv2.VideoCapture读取长视频时如果视频编码不是常见的 H.264可能读不出帧需要先用 ffmpeg 转码。第二base64 编码后的图片体积很大48 帧可能直接把请求体撑到几十 MB所以 JPEG 质量我压到 80必要时可以降到 60。如果平台返回 413 或 request too large就是帧太多或分辨率太高减少帧数或先缩放再编码。然后是主流程把每个任务的问题读出来逐条请求并记录def run_task(task_name, config): task_file os.path.join(config[dataset_root], f{task_name}.json) with open(task_file, r, encodingutf-8) as f: items json.load(f) results [] for item in items: video_path os.path.join(config[video_dir], item[video]) frames, duration_min extract_frames( video_path, pick_frame_count(duration_min0, strategyconfig[frame_strategy]), ) messages build_messages(frames, item[question]) try: answer ask_model(messages, config[model_id]) results.append({ id: item[id], task: task_name, pred: answer, gt: item[answer], duration_min: round(duration_min, 2), }) except Exception as e: results.append({ id: item[id], task: task_name, error: str(e), }) return results注意上面pick_frame_count我传了duration_min0这是示意写法实际应该先用cv2拿到时长再决定帧数。你可以把抽帧和时长获取拆成两步先读元信息再按策略抽帧。这个顺序调整后长视频就不会被统一按 48 帧处理短任务也不会浪费上下文。最后是得分统计。MLVU 单选任务用准确率开放生成任务用 GPT-4o 或人工打分。我本地先用精确匹配做粗筛再用模型打分做细评def score_single_choice(results): correct sum(1 for r in results if r.get(pred, ).strip() r.get(gt, ).strip()) return correct / len(results) if results else 0.0 def score_open_ended(results): scores [] for r in results: if error in r: continue prompt f参考答案{r[gt]}\n模型回答{r[pred]}\n请给 0 到 1 之间的分数只输出数字。 resp client.chat.completions.create( modelgpt-4o, messages[{role: user, content: prompt}], temperature0, ) try: scores.append(float(resp.choices[0].message.content.strip())) except ValueError: scores.append(0.0) return sum(scores) / len(scores) if scores else 0.0这套配置跑下来单任务验证大概几分钟全量 2000 多题会久一些。建议先跑plot_qa的 50 条子集确认通道和解析都正常再扩到全量。4. 验证请求与成功结果逐任务对照表配置写完后先做一次最小验证。用一条plot_qa样本手动构造请求确认返回正常。命令行下可以用 curl 快速测curl https://taotoken.net/api/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o, messages: [ {role: user, content: [ {type: text, text: 这个视频里穿红衣服的人在哪个场景出现A. 厨房 B. 客厅 C. 花园 D. 车库}, {type: image_url, image_url: {url: data:image/jpeg;base64,你的帧数据}} ]} ], temperature: 0 }如果返回里有choices[0].message.content说明通道通了。如果返回 401检查 Key 是否复制完整如果返回 model not found检查模型 ID 拼写如果返回超时先减少帧数。我实测下来GPT-4o 在 MLVU 上的表现和论文基本一致全面理解类任务相对好一些单细节和多细节任务掉得明显。下面是我跑的一个 200 条子集对照表任务名、题型、样本数、GPT-4o 得分和主要失败原因都列出来了任务类别题型样本数GPT-4o 得分主要失败原因plot_qa全面理解单选300.67跨片段人物指代混淆video_summarization全面理解开放200.58长视频全局信息丢失topic_reasoning全面理解单选250.64主题推断依赖局部帧sub_scene全面理解单选200.70场景边界判断偏差needle_qa单细节单选300.53目标帧未被抽到ego_qa单细节单选250.56第一视角动作识别弱action_count多细节单选250.48动作计数漏数action_order多细节单选150.47时序关系判断错anomaly_recognition多细节单选100.50异常片段定位不准这张表里最值得关注的是action_count和action_order得分都不到 0.5。原因不是模型不会数数而是抽帧间隔太大动作发生的那几帧没被采到。我把action_count的抽帧数从 32 提到 64 后得分从 0.48 升到 0.55说明帧密度对多细节任务影响很大。另一个发现是needle_qa它要求模型在长视频里找到某个特定细节如果抽帧策略是均匀采样目标帧很可能落在两个采样点之间直接导致答错。解决办法是按问题里的时间线索做非均匀抽帧或者先用文本检索定位大致区间再抽帧。开放生成任务我用 GPT-4o 打分video_summarization平均 0.58比论文里的 5.80 分制换算后略低主要差在摘要覆盖度上。模型倾向于描述开头和结尾中间段落经常被压缩掉。如果你要做更细的评测可以把视频按 3 分钟、6 分钟、12 分钟分段分别测摘要质量这样能看出性能随时长下降的曲线。验证成功后结果文件会写到./results/mlvu_gpt4o_scores.json里面每条记录包含 id、task、pred、gt、duration_min。你可以用 pandas 快速聚合import json import pandas as pd with open(./results/mlvu_gpt4o_scores.json, r, encodingutf-8) as f: data json.load(f) df pd.DataFrame(data) summary df.groupby(task).apply( lambda x: (x[pred].str.strip() x[gt].str.strip()).mean() ) print(summary)如果某个任务全是 0先别怀疑模型检查gt字段格式是否和pred对齐比如选项是 A 还是 A.差一个点就会全错。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth跑评测的过程中报错基本集中在通道和解析两层。我把遇到的真实报错和对应处理列出来你对照着看。401 Unauthorized 是最常见的。原因通常是 Key 没设置进环境变量或者复制时带了空格。检查方式是echo $TAOTOKEN_API_KEY看输出是否以sk-开头且没有换行。如果用的是 Python确认OpenAI(api_key...)里传的是变量而不是字符串TAOTOKEN_API_KEY。还有一种情况是 Key 被禁用或额度耗尽去控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 看余额和状态。local proxy failed 这个报错一般出现在你本地设置了 HTTP_PROXY 或 HTTPS_PROXY 环境变量但代理服务没启动。处理方式是先unset HTTP_PROXY HTTPS_PROXY再重跑脚本。如果你确实需要走网络配置确保代理地址可达但更推荐直接清掉环境变量用平台直连。reading choices 报错通常是响应结构和你预期的不一样。比如你写resp.choices[0].message.content但实际返回里choices是空列表或者message字段不存在。这种情况多半是请求被平台拦截或模型返回了错误对象。打印完整resp看结构如果是{error: {...}}按 error message 处理。另一个可能是max_tokens设得太小模型还没输出完就被截断content为 None。OAuth 相关报错一般出现在你用某些 CLI 工具或 IDE 插件接入时比如 Claude Code 或 Cline 的 MCP 配置。如果你在配置文件里写了 OAuth 流程但没配好回调地址就会卡在授权页。处理方式是改用 API Key 方式接入Base URL 填 https://taotoken.net/api Key 填控制台生成的 KeyModel ID 填对应模型。以 Claude Code 为例配置文件里需要同时写全三件套{ baseUrl: https://taotoken.net/api, apiKey: sk-你的Key, model: gpt-4o }如果你用的是 Codex 的auth.json结构类似把base_url、api_key、model三个字段填对即可。Cline 的 MCP 配置里也是同样三件套缺一个都会连不上。注意不要只填 Base URL 就以为能跑Key 和 Model ID 必须同时存在。还有一个隐蔽的错请求返回 200但内容是空字符串。这通常是帧数据太大导致模型没处理完就返回了。减少帧数、降低 JPEG 质量、或者把视频先缩放到 720p 再抽帧都能缓解。如果问题持续把temperature设为 0max_tokens提到 1024给模型足够的输出空间。6. 语义一致 CTA把评测流程固定成可复用通道跑完这一轮我最大的感受是 MLVU 的难度确实不在模型本身而在「怎么把长视频正确地喂给模型」。抽帧策略、帧密度、问题拼接方式每一个都会直接影响得分。GPT-4o 单选 64.6% 这个数字换一套抽帧参数可能上下浮动好几个点。所以做长视频评测时固定通道和固定预处理流程比换模型更重要。如果你要复现全量 MLVU建议先把 Base URL、Key、Model ID 三件套固定下来再按任务分批跑。通道入口我统一用 https://taotoken.net/api Key 在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 生成模型对话验证可以在 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里先手动试几条确认返回格式后再写进脚本。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有各语言的调用示例遇到参数不确定时翻一下比猜快。长期做评测或 Agent 任务的话Coding Plan 的入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 适合需要稳定跑批量的场景。单次验证不用上按量 Key 足够。最后留一个我踩过的坑MLVU 的video_summarization任务分段标注了前 3 分钟、前 6 分钟等如果你只按整段视频抽帧会丢掉分段信息。正确做法是把分段边界也传进 prompt让模型知道当前摘要对应哪一段。这个改动让我的摘要得分从 0.58 提到了 0.63改动很小但效果明显。你可以先跑 10 条对比一下再决定要不要全量应用。
返回列表