公司动态

VGI-Bench探针评测:视频生成模型视觉智能量化实践

📅 2026/8/31 7:16:55
VGI-Bench探针评测:视频生成模型视觉智能量化实践
视频生成模型的“视力”到底好不好相信很多做 AIGC 的同学都有这种感觉模型生成的视频画面很清晰、很酷炫但一旦追问细节——画面里物体运动是否合理、物体遮挡后是否还在、因果关系是否成立——就很容易暴露问题。VGI-Bench 这类评测体系就是想解决“视频生成模型视觉智能”如何量化评估的问题。它通过设计一套探针任务像考试一样去“提问”模型从而判断模型是真的理解视觉世界还是仅仅在“照着数据分布画图”。本文将围绕 4 件事展开解释 VGI-Bench 是什么以及“探针”评测和传统评测的区别拆解视频生成模型视觉智能的核心维度给出一套可复用的探针评测流程包含可运行 Python 示例整理本地部署、调用过程、并发评测中的常见坑与工程建议。不管你是做多模态模型研究的同学还是在业务中落地视频生成能力的开发者这篇文章都能提供一个比较完整的实践参考。1. 背景与核心概念1.1 为什么要评测视频生成模型的“视觉智能”过去我们对视频生成模型的评价集中在画面质量上。常用的指标包括 FVDFréchet Video Distance、CLIP-Score、PSNR、SSIM 等这些指标衡量的是“生成画面与真实画面的相似度”。但这类指标有一个明显问题它们不关心模型是否“理解”了内容。举个例子一个模型可以生成非常逼真的猫跳上桌子的视频但如果你换一个角度提问比如“猫落地时四条腿是否符合作息规律”“跳上桌子的过程是否受到重力影响”它可能就答不上来。更常见的是下面这种场景生成视频中杯子被手推到桌边下一帧杯子穿过桌沿悬浮在空中一个球滚进遮挡物后面再出现时颜色发生了变化视频里人物转身五官位置明显错乱。这些在传统指标上未必能发现因为传统指标通常只统计整体分布差异而不是基于视觉内容进行推理。VGI-Bench 的思路是直接用“视觉问题”去探针模型考察它能不能对视频画面中的对象、行为、关系、事件给出合理判断。1.2 探针评测是什么“探针”Probe这个概念在很多领域都有。比如硬件测试里用的探针卡是接触芯片引脚来检测电气性能网络运维中也有 IP 探针用来探测网络连通性。它们的共同点是通过一个外部工具向被测对象发送特定信号然后根据反馈判断被测对象的内部状态。模型评测中的探针也是这个逻辑。我们设计一组“探针任务”——通常是一段视频配一个问题——然后让模型根据视频回答问题或者做判断。如果模型能利用视频中的视觉信息回答正确说明它具备对应的视觉理解能力如果回答错误则说明模型在这类视觉推理上存在短板。VGI-Bench 中的“探针视频生成模型”就是把这套思想用到视频生成模型上将生成模型生成的视频作为输入用专门设计的问题去探测它内部习得的视觉概念。这和“测试集评估”最大的区别是它不是只看生成质量而是看模型生成结果里“隐含的认知能力”。1.3 视频生成模型与视觉智能的边界这里需要区分三个容易混淆的概念视频生成模型通过扩散模型、自回归模型等方式生成视频的模型常见的有 Sora、Runway、可灵、Open-Sora 等。它本身是一个“生成器”输入是文本、图像输出是视频帧序列。视频理解模型输入视频输出文本比如视频问答、视频标注、视频摘要等模型。视频生成模型的视觉智能生成模型中隐含的视觉概念与因果关系。通过生成结果反推观察模型是否掌握了物理常识、空间关系、时间变化等知识。注意VGI-Bench 评测的不是“视频理解模型”而是“视频生成模型”。它把视频生成模型当作被测对象用探针问题去观察它生成视频中的“内在逻辑”。为什么这样做值得因为传统视频生成评估只关心“是否好看”而视觉智能评测关心“是否合理”。在真实业务中这两者同样重要。比如游戏行业生成技能特效视频如果物体运动和物理规律不一致用户会立刻察觉电商生成商品展示视频如果物体的透视关系有问题商品会显得不真实自动驾驶数据增强生成场景视频如果道路车辆运动不遵循交通规则生成的扩展数据反而会污染模型。2. VGI-Bench 的评测体系设计2.1 总体框架VGI-Bench 这类评测体系的整体思路可以拆成 4 层来理解层面内容作用数据层探针视频样本 标注提供测试材料覆盖不同场景、对象、运动方式任务层探针问题 / 提示词将视觉智能能力转化为可执行、可评分的任务模型层被测视频生成模型 主干 LLM 裁判生成视频、回答问题、评估答案指标层准确率、一致性、违规率输出量化结果反映单项能力和整体水平2.2 探针任务的分类为了评测“视觉智能”探针任务需要覆盖多个能力维度。这里给出一个比较常用的分类方式1. 物理常识物体是否遵循重力物体的碰撞、反弹是否合理液体流动、烟雾扩散是否符合物理规律。2. 对象状态变化物体是否保形不随运动发生非自然形变颜色是否变化比如红色球进入阴影是否被错误变为蓝色数量是否变化一个苹果被切开后是否变多。3. 空间关系前后遮挡关系是否正确物体移动后相对位置是否保持一致透视尺寸变化是否合理。4. 时间一致性物体消失再出现后状态是否一致动作时序是否颠倒因果事件是否按正确顺序发生。5. 反事实推理在相同场景下修改一个条件结果是否发生合理变化。这类问题能深度探测模型对因果关系的建模。2.3 探针视频的构造构造探针视频通常会采用两类方法文本引导 视频生成设计一段文本提示词用被评测的视频生成模型生成视频然后基于生成的视频进行提问。适合自动化批量评估。真实视频 扰动使用真实视频施加特定扰动比如删除一段帧、翻转遮挡关系再让模型进行生成或补全观察模型是否理解被扰动部分的语义。在 VGI-Bench 实践中文本引导方式是主路径因为它可以大量构造场景覆盖不同概念。2.4 评分机制设计探针评测不能只靠人工判分否则规模上不来。实际工程中通常采用“LLM 裁判 规则校验”的混合评分方式用大语言模型如 GPT-4o、Qwen-VL、DeepSeek-VL对模型回答进行打分对存在确定答案的问题用规则抽取关键实体进行比对对模型拒绝回答、超出范围回答等情况进行特殊标记不混入正常准确率计算。评分维度一般包括准确率回答与标准答案一致的占比一致性在多个平行视频中模型回答是否保持逻辑一致鲁棒性同一提示词不同随机种子生成的视频得分方差是否过大拒绝率模型是否经常避答这也会影响体验。3. 环境准备与工具链3.1 运行环境说明开始写探针评测代码之前先确认基础环境。下面以常见配置为例操作系统Ubuntu 20.04 / 22.04Windows 也可以但路径写法注意适配Python3.10 及以上视频生成模型接口OpenAI 兼容接口或各自官方 Python SDKLLM 裁判接口支持 openai SDK 的兼容服务视频处理OpenCV、Decord 或 PyAV用于抽帧。版本请根据你的实际项目情况调整本文示例重点是演示评测思路不绑定具体模型。3.2 安装依赖建议新建一个虚拟环境避免依赖冲突python -m venv vgi_bench_env source vgi_bench_env/bin/activate # Windows 使用 vgi_bench_env\Scripts\activate然后安装 Python 依赖pip install openai opencv-python decord pandas numpy pyyaml其中openai调用支持 OpenAI 协议的视频生成模型与 LLM 裁判opencv-python读取视频、抽帧decord高性能视频解码库批量抽帧时比 OpenCV 更快pandas汇总实验结果numpy计算分数pyyaml加载配置文件。如果视频生成模型需要本地部署例如在 Ollama 或 FastAPI 服务中加载视频生成模型那么还需要按具体框架安装对应依赖。这一块在第五节结合本地部署进一步说明。3.3 项目结构规划一个比较清晰的探针评测项目目录结构如下vgi-bench-demo/ ├── configs/ │ └── config.yaml # 全局配置 ├── data/ │ └── probes.json # 探针任务定义 ├── evaluator/ │ ├── __init__.py │ ├── video_generator.py # 调用视频生成模型 │ ├── judge.py # LLM 裁判 │ └── metrics.py # 指标计算 ├── scripts/ │ └── run_eval.py # 主入口脚本 ├── outputs/ │ ├── videos/ # 生成的视频 │ └── results/ └── requirements.txt下面按这个结构逐步实现。4. 完整实战案例搭建视频生成模型视觉智能探针评测流程4.1 定义探针任务在data/probes.json中维护探针任务。每个任务包含id唯一标识prompt生成视频的文本提示词question针对该视频的探针问题answer标准答案或评分要点dimension能力维度。示例[ { id: phys_001, prompt: A red apple falling from a table to the ground, realistic physics, question: 当苹果从桌面掉落到地面的过程中它的运动轨迹是怎样的, answer: 苹果先加速下落接触地面后弹跳或停住, dimension: physical_common_sense }, { id: obj_001, prompt: A green car passing behind a white building, then appearing on the other side, question: 汽车从建筑物一侧消失后它应该出现在哪里, answer: 建筑物的另一侧, dimension: object_permanence }, { id: space_001, prompt: A person walking toward the camera, keeping face shape consistent, question: 随着人物靠近摄像头人物面部的轮廓应该发生什么变化, answer: 面部轮廓变大但五官相对位置保持稳定, dimension: spatial_relationship } ]4.2 编写视频生成调用器新建evaluator/video_generator.py通过 OpenAI 兼容接口调用视频生成模型。# 文件路径evaluator/video_generator.py import base64 import os import time from openai import OpenAI class VideoGenerator: 通过 OpenAI 兼容协议调用视频生成模型。 如果你的视频生成模型部署成本地 OpenAI 接口只需要改 base_url 和 api_key。 def __init__(self, base_url: str, api_key: str, model_name: str, save_dir: str): self.client OpenAI(base_urlbase_url, api_keyapi_key) self.model_name model_name self.save_dir save_dir os.makedirs(save_dir, exist_okTrue) def generate(self, prompt: str, sample_id: str, retries: int 3): result None for attempt in range(retries): try: response self.client.chat.completions.create( modelself.model_name, messages[ { role: user, content: ( f生成一个短视频视频描述{prompt}\n 要求画面清晰运动自然遵守物理规律。 ), } ], # 视频生成参数按实际服务能力调整 n1, ) # 这里假设响应中包含视频的 base64 内容或视频 URL。 # 实际字段名根据服务实现调整。 content response.choices[0].message.content if content.startswith(http): video_path self._download_video(content, sample_id) else: video_path self._save_base64_video(content, sample_id) return video_path except Exception as e: print(f[VideoGenerator] attempt {attempt 1} failed: {e}) time.sleep(2 * (attempt 1)) return None def _download_video(self, url: str, sample_id: str): video_path os.path.join(self.save_dir, f{sample_id}.mp4) # 这里用简单的 HTTP 下载方式。 # 生产环境推荐使用 requests 流式下载并增加超时与重试。 import requests resp requests.get(url, timeout60) resp.raise_for_status() with open(video_path, wb) as f: f.write(resp.content) return video_path def _save_base64_video(self, content: str, sample_id: str): if , in content: content content.split(,)[-1] video_path os.path.join(self.save_dir, f{sample_id}.mp4) with open(video_path, wb) as f: f.write(base64.b64decode(content)) return video_path注意不同视频生成服务返回格式差异很大。有些返回视频 URL有些返回 base64还有一些需要在提交任务后通过任务 ID 轮询结果。这段代码只是一个通用模板实际使用时需要根据你对接的模型服务调整。4.3 编写 LLM 裁判生成视频后需要将视频送入“裁判模型”。这里有两个选择把视频抽帧用多帧图像的方式交给多模态 LLM 判断直接调用支持视频输入的多模态 LLM。第一种方式兼容性更好因为很多 LLM 接口不支持视频输入。下面使用 OpenCV 抽取中间帧、起始帧和结束帧拼接到 prompt 中交给判官模型。# 文件路径evaluator/judge.py import base64 import cv2 from openai import OpenAI class LLMJudge: LLM 裁判基于视频多帧图像 探针问题输出判断结果。 def __init__(self, base_url: str, api_key: str, judge_model: str): self.client OpenAI(base_urlbase_url, api_keyapi_key) self.judge_model judge_model staticmethod def _extract_frames(video_path: str, num_frames: int 5): cap cv2.VideoCapture(video_path) frame_count int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) total_frames max(frame_count, 1) frame_indices [] for i in range(num_frames): frame_indices.append(int(total_frames * i / max(num_frames - 1, 1))) frames [] for idx in frame_indices: cap.set(cv2.CAP_PROP_POS_FRAMES, idx) ret, frame cap.read() if ret: # 压缩图片减少 token 开销 frame cv2.resize(frame, (512, 512)) encode_params [int(cv2.IMWRITE_JPEG_QUALITY), 80] _, buffer cv2.imencode(.jpg, frame, encode_params) frames.append(base64.b64encode(buffer).decode(utf-8)) cap.release() return frames def judge_video(self, video_path: str, question: str, reference_answer: str): frames self._extract_frames(video_path) image_contents [ { type: image_url, image_url: {url: fdata:image/jpeg;base64,{frame}} } for frame in frames ] user_content [ { type: text, text: ( 以下是同一个视频中的若干帧画面。 f请基于这些画面回答下面问题。\n问题{question}\n f参考要点{reference_answer}\n 请以 JSON 格式输出 {reason: 判断理由, score: 0 或 1, answer: 简短回答} ) } ] image_contents response self.client.chat.completions.create( modelself.judge_model, messages[{role: user, content: user_content}], response_format{type: json_object} ) return response.choices[0].message.content这里需要注意抽帧数量不宜过多否则 LLM 接口负载太高抽帧位置尽量均匀覆盖整个视频避免只取开头或结尾评分二值化0/1适合快速统计但也可以设计更细的 0-5 分制需按任务类型确定。4.4 编写指标计算与结果汇总探针评测的核心输出是准确率。同时我们还要统计“按维度拆分”的准确率方便找出模型的弱项。# 文件路径evaluator/metrics.py import json import pandas as pd def aggregate_results(records): records 为列表每个元素是 { id: str, dimension: str, score: int, # 0 或 1 reason: str, answer: str, prompt: str } df pd.DataFrame(records) if df.empty: return {} overall_accuracy df[score].mean() dimension_summary df.groupby(dimension)[score].agg([mean, count]) output { overall_accuracy: round(float(overall_accuracy), 4), total_samples: len(df), dimension_summary: {}, } for dim, row in dimension_summary.iterrows(): output[dimension_summary][dim] { accuracy: round(float(row[mean]), 4), count: int(row[count]), } return output4.5 编写主运行脚本在主脚本中串联整个流程读取配置读取探针任务调用视频生成模型生成视频调用 LLM 裁判评分汇总结果并保存报告。# 文件路径scripts/run_eval.py import json import os from datetime import datetime import yaml from evaluator.video_generator import VideoGenerator from evaluator.judge import LLMJudge from evaluator.metrics import aggregate_results def load_config(config_path: str): with open(config_path, r, encodingutf-8) as f: return yaml.safe_load(f) def main(): config load_config(configs/config.yaml) video_gen VideoGenerator( base_urlconfig[video_generator][base_url], api_keyconfig[video_generator][api_key], model_nameconfig[video_generator][model], save_diroutputs/videos, ) judge LLMJudge( base_urlconfig[judge][base_url], api_keyconfig[judge][api_key], judge_modelconfig[judge][model], ) with open(config[probe_file], r, encodingutf-8) as f: probes json.load(f) records [] for probe in probes: sample_id probe[id] video_path video_gen.generate(probe[prompt], sample_id) if video_path is None: print(f[ERROR] 视频生成失败: {sample_id}) continue judge_output judge.judge_video( video_pathvideo_path, questionprobe[question], reference_answerprobe[answer], ) try: judge_json json.loads(judge_output) except json.JSONDecodeError: judge_json { reason: judge_output, score: 0, answer: judge_output } records.append( { id: probe[id], dimension: probe[dimension], prompt: probe[prompt], question: probe[question], reference_answer: probe[answer], reason: judge_json.get(reason, ), score: int(judge_json.get(score, 0)), answer: judge_json.get(answer, ), } ) print(f[DONE] {sample_id} - score: {judge_json.get(score)}) summary aggregate_results(records) os.makedirs(outputs/results, exist_okTrue) timestamp datetime.now().strftime(%Y%m%d_%H%M%S) result_path foutputs/results/result_{timestamp}.json payload { summary: summary, records: records, } with open(result_path, w, encodingutf-8) as f: json.dump(payload, f, ensure_asciiFalse, indent2) print(json.dumps(summary, ensure_asciiFalse, indent2)) print(f[SAVED] {result_path}) if __name__ __main__: main()4.6 配置文件示例# 文件路径configs/config.yaml probe_file: data/probes.json video_generator: base_url: http://localhost:8000/v1 api_key: sk-local model: video-gen-model-v1 judge: base_url: https://api.example.com/v1 api_key: ${JUDGE_API_KEY} model: multimodal-judge-model${JUDGE_API_KEY}这类占位符建议通过环境变量注入。实际使用时可以用os.getenv读取避免把密钥写进配置文件。4.7 运行与预期结果执行以下命令python scripts/run_eval.py正常流程会输出[DONE] phys_001 - score: 1 [DONE] obj_001 - score: 0 [DONE] space_001 - score: 1 ... { overall_accuracy: 0.6667, total_samples: 3, dimension_summary: { physical_common_sense: { accuracy: 1.0, count: 1 }, object_permanence: { accuracy: 0.0, count: 1 }, spatial_relationship: { accuracy: 1.0, count: 1 } } } [SAVED] outputs/results/result_20250520_153000.json这里的示例结果只是演示不代表真实模型表现。如果你只有 3 个探针样本这个分数没有统计意义。正式评测时单个维度至少需要 50 到 100 个探针样本才能得到比较稳定的结论。5. 常见问题与排查思路用探针评测视频生成模型时踩过的坑可以列出一长串。下面按问题现象、常见原因、解决思路整理。问题现象常见原因解决思路视频生成服务一直超时视频生成耗时长默认 HTTP 超时太短拉长请求超时到 120 秒以上或者改用异步任务轮询生成视频无法被 OpenCV 打开返回内容不是标准 MP4或文件未完整写入校验文件头、文件大小打印返回格式按实际格式解码LLM 裁判返回格式不稳定没有强制 JSON 输出或回答问题太长超出模板使用 response_format 强制 JSON并增加解析兜底逻辑抽帧后画面全是黑帧视频编码格式问题或者按帧索引读取失败改用 Decord 或 PyAV 解码检查视频流编码探针问题太简单所有模型都答对问题区分度过低增加反事实、因果、物理细节问题构造分层难度同一提示词多次生成得分波动大视频生成本身随机性强固定种子如果服务支持或多次生成取平均分模型拒绝回答探针问题LLM 裁判误判视频内容或安全策略过严调整提示词补充“这是视频帧样本请客观描述”等背景说明评分明显和人工判断不一致裁判对视频理解不够或参考要点太抽象细化参考要点增加 0/1 之外的理由约束必要时接入人工复核排错时建议按下面的顺序走先确认视频文件本身能正常播放、能在播放器中打开再确认抽帧逻辑单独跑一次抽帧脚本把帧保存到本地检查接着用一张静态图单独测试 LLM 裁判接口排除视频输入导致的问题最后才跑完整评测流程。6. 最佳实践与工程建议6.1 探针样本要分层设计不要所有探针问题都是同一个难度。建议分三层基础层考察对象是否存在、数量是否正确、颜色是否一致推理层考察遮挡后的对象是否保持存在、运动轨迹是否合理、空间关系是否稳定因果层考察事件顺序、反事实条件、物理交互是否正确。分层的好处是能区分模型是“看起来好”还是“真正理解”。如果基础层得分高、因果层得分低说明模型更多是表面拟合深层推理能力不足。6.2 评测过程要可复现视频生成模型通常带有随机性所以评测时要做好三件事固定随机种子如果模型服务支持 seed 参数务必固定多次运行取平均每个探针至少跑 3 到 5 次统计平均分和方差记录生成参数把 prompt、seed、生成时间、模型版本全部写入结果文件后期排查方便。6.3 本地部署与并发优化如果使用本地部署的视频生成模型通常会有一个明显的瓶颈生成速度慢、显存占用高。这里有几个经验视频生成模型服务建议与评测脚本分开部署避免评测脚本内存过高导致服务被挤挂如果支持批处理尽量用批处理生成视频而不是逐条请求LLM 裁判调用可以并发但要控制并发数避免触发限流。示例代码如下from concurrent.futures import ThreadPoolExecutor def judge_one(item): # 实际评分逻辑 pass with ThreadPoolExecutor(max_workers4) as executor: results list(executor.map(judge_one, probe_list))6.4 指标不要只看准确率准确率是最基础的指标但评测视觉智能不能只看平均分。还需要关注维度差异哪些维度分低意味着模型哪类视觉推理弱方差同一问题多次生成如果方差很大说明模型不稳定拒绝率如果模型经常“答非所问”或拒答说明语义把握不足相关性评测得分是否和人工体验一致这一步需要定期人工抽样复核。6.5 注意数据污染与评测泄漏如果探针样本是从公开视频数据集里取来的而视频生成模型训练时也用了同样的数据评测结果会虚高。因此尽量使用新构造的合成场景避免直接使用公开数据集中的原始视频如果条件允许设计“文本生成 3D 渲染”方式来构造可控视频定期更换探针池防止模型在评测集上过拟合。6.6 安全与授权边界在评测过程中如果你使用的是在线模型 API需要注意两点不要上传包含个人隐私、商业机密或未授权人脸数据的视频涉及生产环境变更、模型替换、评测任务大批量执行时先在测试环境小范围验证确认没有异常再放量。使用本地部署模型时要遵守模型开源许可证要求并按最小权限原则配置访问控制不要将本地评测服务直接暴露到公网。6.7 结果应以可重复验证为基础评测报告不能只给一个平均分建议输出每个探针任务的明细结果生成的视频文件路径抽帧使用的帧序号LLM 裁判的完整理由人工复核抽样的比例和建议。例如可以额外输出一份 CSV 报告import pandas as pd records_df pd.DataFrame(records) records_df.to_csv(outputs/results/detail.csv, indexFalse, encodingutf-8-sig)这样后续分析、复查、审计都有据可查。7. 总结与下一步学习路线这篇文章介绍的 VGI-Bench 探针评测思路核心并不复杂把视频生成模型从“生成器”变成“被测对象”用一组视觉问题去探测它的内部视觉智能。我们完成了这样几件事解释了为什么传统指标不够为什么要用探针评测梳理了探针任务的维度设计与评分机制给出了一个完整的 Python 评测流程包括视频生成调用、抽帧、LLM 裁判、结果汇总整理了常见的排错表和工程建议。如果你准备在自己项目中落地类似评测下一步建议从下面几个方向继续深入扩展探针题库按物理、空间、时间、因果维度各设计 50 个以上问题先跑通全流程搭建自动化评测平台把视频生成、裁判评分、报告生成封装成定时任务或 CI 流水线加入人工复核机制LLM 裁判负责初筛人工负责抽检矛盾样本逐步校准评分标准关注生成模型演进随着视频生成模型能力提升探针任务难度也需要动态升级。对视频生成模型来说“生成清晰画面”只是第一步“理解视觉世界”才是更值得关注的能力。探针评测就是给这类能力画了一面镜子。如果你在搭建评测流程时遇到了奇怪的报错也可以把现象、错误日志、模型服务版本这三样信息整理好去对应模型的开源仓库或官方开发者社区搜索解决问题的速度往往比直接硬猜快很多。如果本文对你有帮助可以收藏备用后面实战的时候直接对照实现。