Download scripts/generate_vbench8_extended_atc.py from Cccccz/Self-Forcing: direct link, hf CLI and curl.
- Browser
- Download file 10.8 kB
-
https://huggingface.co/Cccccz/Self-Forcing/resolve/main/scripts/generate_vbench8_extended_atc.py
- Command line
-
hf download hf://Cccccz/Self-Forcing/scripts/generate_vbench8_extended_atc.py
-
curl -L -o generate_vbench8_extended_atc.py https://huggingface.co/Cccccz/Self-Forcing/resolve/main/scripts/generate_vbench8_extended_atc.py
10.8 kB
| #!/usr/bin/env python3 | |
| """Generate ATC FPPF/FPPP videos against the existing Extended-251 FFFF run.""" | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| from typing import Any | |
| REPO_ROOT = Path(__file__).resolve().parents[1] | |
| if str(REPO_ROOT) not in sys.path: | |
| sys.path.insert(0, str(REPO_ROOT)) | |
| from scripts import generate_vbench8_extended_strategies as extended | |
| import lpips | |
| import torch | |
| from omegaconf import OmegaConf | |
| from safetensors import safe_open | |
| from scripts import evaluate_single_block_fppf as base | |
| from scripts.generate_vbench8_extended_disca import artifact_paths, read_u8_video | |
| from utils.wan_wrapper import WanTextEncoder, WanVAEWrapper | |
| MAPPING_DEFAULT = REPO_ROOT / "assets/vbench8_extended_subset_mapping.json" | |
| REFERENCE_DEFAULT = ( | |
| REPO_ROOT / "evaluation_runs/vbench8_extended_stage1_step2000_20260901" | |
| ) | |
| OUTPUT_DEFAULT = ( | |
| REPO_ROOT / "evaluation_runs/vbench8_extended_atc_stage1_step2000_20260901" | |
| ) | |
| PATTERN_TO_STEPS = {"FPPF": [1, 2], "FPPP": [1, 2, 3]} | |
| def read_predictor_config(weights: Path) -> dict[str, Any]: | |
| with safe_open(weights, framework="pt", device="cpu") as handle: | |
| metadata = handle.metadata() or {} | |
| raw = metadata.get("predictor_config") | |
| if raw is None: | |
| raise ValueError(f"ATC checkpoint has no predictor_config metadata: {weights}") | |
| config = json.loads(raw) | |
| if config.get("input_variant") != "atc": | |
| raise ValueError(f"Checkpoint is not ATC: {config}") | |
| return config | |
| def strategy_configs( | |
| scope: str, patterns: list[str] | tuple[str, ...] | |
| ) -> list[dict[str, Any]]: | |
| return [ | |
| { | |
| "name": f"atc_{scope}_{pattern.lower()}", | |
| "policy": "fppf", | |
| "pattern": pattern, | |
| "candidate_steps": PATTERN_TO_STEPS[pattern], | |
| "allow_chunk0_predictor": False, | |
| "beta": None, | |
| "threshold": None, | |
| "target_accepts": 6 * len(PATTERN_TO_STEPS[pattern]), | |
| "head": None, | |
| "input_variant": "atc", | |
| "atc_previous_scope": scope, | |
| } | |
| for pattern in patterns | |
| ] | |
| def load_models( | |
| weights: Path, scope: str, device: torch.device | |
| ) -> tuple[Any, Any, Any, Any, None, None, Any]: | |
| predictor_config = read_predictor_config(weights) | |
| if predictor_config.get("atc_previous_scope") != scope: | |
| raise ValueError( | |
| f"Checkpoint scope={predictor_config.get('atc_previous_scope')} " | |
| f"does not match requested scope={scope}" | |
| ) | |
| print("[setup] loading VAE", flush=True) | |
| vae = WanVAEWrapper().to(device=device, dtype=torch.bfloat16).eval() | |
| config = OmegaConf.merge( | |
| OmegaConf.load(REPO_ROOT / "configs/default_config.yaml"), | |
| OmegaConf.load(REPO_ROOT / "configs/self_forcing_sid.yaml"), | |
| ) | |
| print(f"[setup] loading Teacher and ATC scope={scope}", flush=True) | |
| pipeline = base.build_pipeline( | |
| config, REPO_ROOT / "checkpoints/self_forcing_dmd.pt", vae, device | |
| ) | |
| predictor = base.load_predictor( | |
| pipeline.generator.model, | |
| { | |
| **predictor_config, | |
| "weights": weights, | |
| "atc_collect_diagnostics": False, | |
| }, | |
| device, | |
| ) | |
| text_encoder = WanTextEncoder().to(device=device, dtype=torch.bfloat16).eval() | |
| text_encoder.requires_grad_(False) | |
| print("[setup] loading LPIPS", flush=True) | |
| lpips_model = lpips.LPIPS(net="alex", verbose=False).to(device).eval() | |
| lpips_model.requires_grad_(False) | |
| return vae, pipeline, predictor, text_encoder, None, None, lpips_model | |
| def is_complete( | |
| output_root: Path, | |
| row: dict[str, Any], | |
| strategies: list[dict[str, Any]], | |
| ) -> bool: | |
| return all( | |
| all(path.is_file() for path in artifact_paths(output_root, row, config["name"])) | |
| for config in strategies | |
| ) | |
| def run_prompt( | |
| *, | |
| row: dict[str, Any], | |
| strategies: list[dict[str, Any]], | |
| reference_root: Path, | |
| output_root: Path, | |
| seed: int, | |
| models: tuple[Any, ...], | |
| device: torch.device, | |
| ) -> None: | |
| global_index = int(row["global_index"]) | |
| suite = str(row["prompt_suite"]) | |
| suite_index = int(row["suite_index"]) | |
| prompt = str(row["extended_prompt"]) | |
| reference_path = ( | |
| reference_root / "generated_videos/ffff" / suite / f"{suite_index:03d}.mp4" | |
| ) | |
| print(f"[prompt] global={global_index} suite={suite}/{suite_index}", flush=True) | |
| conditional = models[3](text_prompts=[prompt]) | |
| for strategy in strategies: | |
| name = str(strategy["name"]) | |
| video_path, record_path = artifact_paths(output_root, row, name) | |
| if video_path.is_file() and record_path.is_file(): | |
| print(f"[cached-strategy] global={global_index} strategy={name}", flush=True) | |
| continue | |
| latent, diagnostic = extended.generate_rollout( | |
| pipeline=models[1], | |
| conditional_dict=conditional, | |
| seed=seed, | |
| device=device, | |
| predictor=models[2], | |
| head=None, | |
| config=strategy, | |
| ) | |
| with torch.autocast(device_type="cuda", dtype=torch.bfloat16): | |
| pixels = models[0].decode_to_pixel(latent, use_cache=False) | |
| prediction_u8 = base.pixels_to_u8(pixels) | |
| extended.atomic_video(prediction_u8, video_path) | |
| reference_decoded = read_u8_video(reference_path) | |
| prediction_decoded = read_u8_video(video_path) | |
| metrics = base.frame_metrics( | |
| reference_u8=reference_decoded, | |
| prediction_u8=prediction_decoded, | |
| lpips_model=models[6], | |
| batch_size=4, | |
| device=device, | |
| ) | |
| extended.atomic_json( | |
| record_path, | |
| { | |
| "status": "complete", | |
| "strategy": name, | |
| "policy": "fppf", | |
| "pattern": strategy["pattern"], | |
| "input_variant": "atc", | |
| "atc_previous_scope": strategy["atc_previous_scope"], | |
| "candidate_steps": strategy["candidate_steps"], | |
| "allow_chunk0_predictor": False, | |
| "global_index": global_index, | |
| "prompt_suite": suite, | |
| "suite_index": suite_index, | |
| "prompt": prompt, | |
| "seed": seed, | |
| "generation": { | |
| key: value | |
| for key, value in diagnostic.items() | |
| if key != "decisions" | |
| }, | |
| "decisions": diagnostic["decisions"], | |
| "pixel_metrics_vs_ffff": extended.compact_metrics(metrics), | |
| "pixel_metric_input": ( | |
| "MP4-decoded uint8 RGB, all 81 frames on both sides" | |
| ), | |
| "reference_video": str(reference_path), | |
| "video": str(video_path.relative_to(output_root)), | |
| }, | |
| ) | |
| print( | |
| f"[result] global={global_index} strategy={name} " | |
| f"accept={diagnostic['accepted_predictor_calls']} " | |
| f"psnr={metrics['psnr']:.4f} ssim={metrics['ssim']:.6f} " | |
| f"lpips={metrics['lpips']:.6f}", | |
| flush=True, | |
| ) | |
| if hasattr(models[0].model, "clear_cache"): | |
| models[0].model.clear_cache() | |
| del latent, pixels, prediction_u8, reference_decoded, prediction_decoded | |
| torch.cuda.empty_cache() | |
| del conditional | |
| def parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--gpu", default=extended.PHYSICAL_GPU) | |
| parser.add_argument("--scope", choices=("chunk", "last_frame"), required=True) | |
| parser.add_argument("--predictor-weights", type=Path, required=True) | |
| parser.add_argument("--mapping", type=Path, default=MAPPING_DEFAULT) | |
| parser.add_argument("--reference-root", type=Path, default=REFERENCE_DEFAULT) | |
| parser.add_argument("--output-root", type=Path, default=OUTPUT_DEFAULT) | |
| parser.add_argument("--pattern", action="append", choices=tuple(PATTERN_TO_STEPS)) | |
| parser.add_argument("--shard-index", type=int, default=0) | |
| parser.add_argument("--num-shards", type=int, default=1) | |
| parser.add_argument("--global-index", action="append", type=int, default=None) | |
| parser.add_argument("--seed", type=int, default=0) | |
| return parser.parse_args() | |
| def main() -> None: | |
| args = parse_args() | |
| if args.num_shards < 1 or not 0 <= args.shard_index < args.num_shards: | |
| raise ValueError("Invalid shard index/number") | |
| weights = args.predictor_weights.resolve() | |
| mapping = extended.read_mapping(args.mapping.resolve()) | |
| if args.global_index is None: | |
| rows = [ | |
| row for index, row in enumerate(mapping) | |
| if index % args.num_shards == args.shard_index | |
| ] | |
| else: | |
| requested = set(args.global_index) | |
| rows = [row for row in mapping if int(row["global_index"]) in requested] | |
| if len(rows) != len(requested): | |
| raise ValueError("Unknown global index") | |
| patterns = args.pattern or ["FPPF", "FPPP"] | |
| strategies = strategy_configs(args.scope, patterns) | |
| reference_root = args.reference_root.resolve() | |
| if len(list((reference_root / "generated_videos/ffff").rglob("*.mp4"))) != 251: | |
| raise ValueError("Incomplete FFFF reference") | |
| output_root = args.output_root.resolve() | |
| output_root.mkdir(parents=True, exist_ok=True) | |
| extended.write_shard_manifest( | |
| output_root, | |
| str(args.gpu), | |
| args.shard_index, | |
| args.num_shards, | |
| rows, | |
| strategies, | |
| "running", | |
| ) | |
| device = torch.device("cuda") | |
| torch.set_grad_enabled(False) | |
| models = load_models(weights, args.scope, device) | |
| completed = 0 | |
| try: | |
| for row in rows: | |
| if is_complete(output_root, row, strategies): | |
| completed += 1 | |
| print(f"[cached] {completed}/{len(rows)} global={row['global_index']}", flush=True) | |
| continue | |
| run_prompt( | |
| row=row, | |
| strategies=strategies, | |
| reference_root=reference_root, | |
| output_root=output_root, | |
| seed=args.seed, | |
| models=models, | |
| device=device, | |
| ) | |
| completed += 1 | |
| print(f"[progress] {completed}/{len(rows)} prompts", flush=True) | |
| finally: | |
| extended.write_shard_manifest( | |
| output_root, | |
| str(args.gpu), | |
| args.shard_index, | |
| args.num_shards, | |
| rows, | |
| strategies, | |
| "complete" if completed == len(rows) else "failed", | |
| ) | |
| print(f"[complete] gpu={args.gpu} scope={args.scope} prompts={completed}", flush=True) | |
| if __name__ == "__main__": | |
| main() | |