Files
hao-ai-lab-FastVideo/scripts/eval/run_vbench_e2e.py
T
shaoxiongduan 960485519f [refactor] eval: dict-based PromptDataset + EvalRunner orchestrator
Continues the eval refactor. Datasets now yield plain dicts (matches
ValidationDataset / VideoGenerator / Evaluator's kwargs-flowing-through
pattern). End-to-end orchestration moves into EvalRunner so neither the
dataset nor the Evaluator owns videos_dir / fps / filename / generator
state.

Layering:
  EvalRunner          dataset / generator / file conventions / manifest
    └── Evaluator     thin dispatcher (single evaluate(), 1 or N samples)
         └── EvalWorker × N    single-GPU metric replicas

Dataset (fastvideo/eval/datasets/):
- PromptDataset is just an Iterable[dict]. _rows holds dicts with
  prompt / n_samples / dimensions / auxiliary_info / ... — unused
  fields stay absent rather than living as Optional[None] on a
  dataclass. BasePromptDataset alias kept for back-compat.
- BenchmarkSample dataclass deleted. Sample TypedDict documents the
  recognized keys without forcing the schema.
- VBench flattens its nested {dim: {key: val}} aux into {key: val} at
  load time. Aux-using metrics already read flat keys; no metric edits.

Runner (fastvideo/eval/runner.py, new):
- Three named constructors: from_dataset, from_videos, from_samples.
- generate() drives the generator over the dataset (writes manifest);
  score() loads videos and dispatches via Evaluator.evaluate(list).
  run() = generate then score.
- Multi-GPU lives in the underlying Evaluator; the runner just hands
  num_gpus through. No double-orchestration.
- File naming convention (filename_fn) and eval-kwargs assembly
  (eval_kwargs_fn) are runner-side hooks, not dataset overrides.

Script (scripts/eval/run_vbench_e2e.py):
- Rewritten against EvalRunner; ~95 LOC, was 149.

Includes prior scaffolding from dataloader (datasets/registry.py,
the test file, the script) carried over so the refactor lands as one
coherent state.
2026-05-05 23:14:01 +00:00

114 lines
3.9 KiB
Python

"""End-to-end VBench evaluation: prompt → generate → score.
# Generate + score:
python scripts/eval/run_vbench_e2e.py \\
--model <hf-id-or-path> \\
--dimensions subject_consistency,aesthetic_quality \\
--videos-dir runs/vbench/videos \\
--results-json runs/vbench/results.json
# Generate only:
python scripts/eval/run_vbench_e2e.py \\
--model <hf-id-or-path> \\
--dimensions subject_consistency \\
--videos-dir runs/vbench/videos \\
--skip-scoring
# Score existing videos:
python scripts/eval/run_vbench_e2e.py \\
--dimensions subject_consistency \\
--videos-dir runs/vbench/videos \\
--results-json runs/vbench/results.json \\
--skip-generation
"""
from __future__ import annotations
import argparse
import logging
from pathlib import Path
logging.basicConfig(level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s: %(message)s")
def main() -> None:
ap = argparse.ArgumentParser(
__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--dimensions", default="all",
help="Comma-separated VBench dimensions, or 'all'.")
ap.add_argument("--max-prompts", type=int, default=None)
ap.add_argument("--videos-dir", required=True, type=Path)
ap.add_argument("--results-json", type=Path, default=None)
ap.add_argument("--metrics", default="vbench")
ap.add_argument("--model", default=None,
help="HF id / path for VideoGenerator.from_pretrained.")
ap.add_argument("--n-samples", type=int, default=None)
ap.add_argument("--seed", type=int, default=0)
ap.add_argument("--height", type=int, default=None)
ap.add_argument("--width", type=int, default=None)
ap.add_argument("--num-frames", type=int, default=None)
ap.add_argument("--num-inference-steps", type=int, default=None)
ap.add_argument("--fps", type=float, default=25.0)
ap.add_argument("--num-gpus", type=int, default=1)
ap.add_argument("--skip-generation", action="store_true")
ap.add_argument("--skip-scoring", action="store_true")
args = ap.parse_args()
from fastvideo.eval import EvalRunner
from fastvideo.eval.datasets import VBenchPromptDataset
dims = ("all" if args.dimensions == "all"
else [d.strip() for d in args.dimensions.split(",") if d.strip()])
dataset = VBenchPromptDataset(dimensions=dims)
if args.max_prompts:
dataset._rows = dataset._rows[:args.max_prompts]
if args.metrics.strip() == "all":
metrics: str | list[str] = "all"
elif "," in args.metrics:
metrics = [m.strip() for m in args.metrics.split(",") if m.strip()]
else:
metrics = args.metrics.strip()
generator = None
if not args.skip_generation:
if not args.model:
ap.error("--model is required unless --skip-generation is set")
from fastvideo.entrypoints.video_generator import VideoGenerator
generator = VideoGenerator.from_pretrained(args.model,
num_gpus=args.num_gpus)
gen_kwargs = {k: v for k, v in {
"height": args.height, "width": args.width,
"num_frames": args.num_frames,
"num_inference_steps": args.num_inference_steps,
"fps": args.fps,
}.items() if v is not None}
runner = EvalRunner.from_dataset(
dataset,
videos_dir=args.videos_dir,
generator=generator,
metrics=metrics if not args.skip_scoring else [],
num_gpus=args.num_gpus,
fps=args.fps,
seed=args.seed,
n_samples=args.n_samples,
gen_kwargs=gen_kwargs,
)
if not args.skip_generation:
runner.generate()
if not args.skip_scoring:
result = runner.score()
result.print()
if args.results_json:
result.save(args.results_json)
if __name__ == "__main__":
main()