multishot / overfit_infer_debug.py
PencilHu's picture
Upload folder using huggingface_hub
85752bc verified
Raw
History Blame Contribute Delete
10.6 kB
import argparse
import json
import os
import re
import sys
import time
from pathlib import Path
from types import SimpleNamespace
import numpy as np
import torch
import yaml
_THIS_DIR = Path(__file__).resolve().parent
_DIFFSYNTH_ROOT = _THIS_DIR / "multi_view" / "DiffSynth-Studio-main"
if _DIFFSYNTH_ROOT.exists():
sys.path.insert(0, str(_DIFFSYNTH_ROOT))
from diffsynth.pipelines.wan_video_new import WanVideoPipeline
from diffsynth.utils import ModelConfig
from multi_view.datasets.videodataset import MulltiShot_MultiView_Dataset
def save_video(frames, path: str, fps: int = 16, **_kwargs) -> None:
import imageio.v2 as imageio
if not frames:
raise ValueError("No frames to save.")
writer = imageio.get_writer(path, fps=fps)
try:
for frame in frames:
if isinstance(frame, torch.Tensor):
frame = frame.detach().cpu().numpy()
if hasattr(frame, "convert"):
frame = np.asarray(frame.convert("RGB"))
frame = np.asarray(frame)
if frame.dtype != np.uint8:
frame = np.clip(frame, 0, 255).astype(np.uint8)
writer.append_data(frame)
finally:
writer.close()
def load_config(train_yaml: str) -> dict:
with open(train_yaml, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
def build_runtime_args(conf_info: dict) -> SimpleNamespace:
train_args = conf_info.get("train_args", {})
return SimpleNamespace(
zero_face_ratio=float(train_args.get("zero_face_ratio", 0.0)),
shot_rope=bool(train_args.get("shot_rope", False)),
split_rope=bool(train_args.get("split_rope", False)),
split1=bool(train_args.get("split1", False)),
split2=bool(train_args.get("split2", False)),
split3=bool(train_args.get("split3", False)),
)
def resolve_checkpoint(checkpoint_path: str, output_root: Path) -> Path:
if checkpoint_path:
ckpt = Path(checkpoint_path)
if ckpt.is_dir():
ckpt = ckpt / "weights.safetensors"
if not ckpt.exists():
raise FileNotFoundError(f"Checkpoint not found: {ckpt}")
return ckpt
pattern = re.compile(r"checkpoint-step-(\d+)-epoch-(\d+)")
latest_step = -1
latest_ckpt = None
for path in output_root.glob("checkpoint-step-*"):
match = pattern.search(path.name)
if not match:
continue
step = int(match.group(1))
if step > latest_step:
candidate = path / "weights.safetensors"
if candidate.exists():
latest_step = step
latest_ckpt = candidate
if latest_ckpt is None:
raise FileNotFoundError(f"No checkpoint found under {output_root}")
return latest_ckpt
def maybe_convert_checkpoint(checkpoint: Path, output_dir: Path) -> Path:
if checkpoint.name != "model.safetensors":
return checkpoint
try:
from safetensors.torch import load_file, save_file
except ImportError as exc:
raise ImportError("safetensors is required to convert final_model checkpoints.") from exc
state_dict = load_file(str(checkpoint), device="cpu")
if not state_dict:
return checkpoint
prefix = "pipe.dit."
if not any(key.startswith(prefix) for key in state_dict.keys()):
return checkpoint
stripped = {key[len(prefix):]: value for key, value in state_dict.items() if key.startswith(prefix)}
if not stripped:
return checkpoint
converted_path = output_dir / "converted_weights.safetensors"
save_file(stripped, str(converted_path))
return converted_path
def load_dataset_meta(dataset_json: str) -> dict:
with open(dataset_json, "r", encoding="utf-8") as f:
meta = json.load(f)
return {value.get("disk_path"): value for value in meta.values() if isinstance(value, dict)}
def ensure_dir(path: Path) -> None:
path.mkdir(parents=True, exist_ok=True)
def log_device_stats(log, label: str) -> None:
if not torch.cuda.is_available():
return
allocated = torch.cuda.memory_allocated() / (1024 ** 3)
reserved = torch.cuda.memory_reserved() / (1024 ** 3)
log(f"[GPU] {label} allocated={allocated:.2f}GB reserved={reserved:.2f}GB")
def main() -> int:
parser = argparse.ArgumentParser(description="Overfit inference debug script.")
parser.add_argument("--train_yaml", type=str, required=True)
parser.add_argument("--dataset_json", type=str, default="")
parser.add_argument("--checkpoint_path", type=str, default="")
parser.add_argument("--indices", type=int, nargs="+", default=[0])
parser.add_argument("--output_dir", type=str, default="")
parser.add_argument("--num_inference_steps", type=int, default=50)
parser.add_argument("--cfg_scale", type=float, default=5.0)
parser.add_argument("--cfg_scale_face", type=float, default=5.0)
parser.add_argument("--seed", type=int, default=42)
parser.add_argument(
"--split",
choices=["train", "test", "all"],
default="all",
help="Which dataset split to use. Default is all for overfit checks.",
)
parser.add_argument("--tiled", action="store_true")
parser.add_argument("--use_input_video", action="store_true")
parser.add_argument(
"--no_input_video",
action="store_true",
help="Deprecated: input video is off by default.",
)
parser.add_argument("--save_input_video", action="store_true")
parser.add_argument("--negative_prompt", type=str, default="")
args = parser.parse_args()
conf_info = load_config(args.train_yaml)
dataset_args = conf_info.get("dataset_args", {})
train_args = conf_info.get("train_args", {})
dataset_json = args.dataset_json or dataset_args.get("base_path", "")
if not dataset_json:
raise ValueError("dataset_json is required (or set dataset_args.base_path in YAML).")
output_root = Path(train_args.get("output_path", "./ckpts")) / train_args.get("visual_log_project_name", "debug")
output_dir = Path(args.output_dir) if args.output_dir else (output_root / "debug_infer")
ensure_dir(output_dir)
log_path = output_dir / "infer_debug.log"
def log(message: str) -> None:
print(message)
with log_path.open("a", encoding="utf-8") as f:
f.write(message + "\n")
log(f"Train YAML: {args.train_yaml}")
log(f"Dataset JSON: {dataset_json}")
log(f"Output root: {output_root}")
log(f"Output dir: {output_dir}")
checkpoint = resolve_checkpoint(args.checkpoint_path, output_root)
checkpoint = maybe_convert_checkpoint(checkpoint, output_dir)
log(f"Checkpoint: {checkpoint}")
runtime_args = build_runtime_args(conf_info)
height = int(dataset_args.get("height", 480))
width = int(dataset_args.get("width", 832))
ref_num = int(dataset_args.get("ref_num", 3))
dataset = MulltiShot_MultiView_Dataset(
dataset_base_path=dataset_json,
resolution=(height, width),
ref_num=ref_num,
training=args.split != "test",
)
if args.split == "all":
dataset.data_train = dataset.data
dataset.data_test = dataset.data
dataset.training = True
meta_map = load_dataset_meta(dataset_json)
log(f"Dataset split: {args.split}")
log(f"Dataset size: {len(dataset)}")
local_model_path = train_args.get("local_model_path", "")
model_id = "Wan2.2-TI2V-5B"
model_configs = [
ModelConfig(path=os.path.join(local_model_path, model_id, "models_t5_umt5-xxl-enc-bf16.pth"), offload_device="cuda"),
ModelConfig(path=os.path.join(local_model_path, model_id, "Wan2.2_VAE.pth"), offload_device="cuda"),
ModelConfig(path=str(checkpoint), offload_device="cuda"),
]
pipe = WanVideoPipeline.from_pretrained(
torch_dtype=torch.bfloat16,
device="cuda",
model_configs=model_configs,
redirect_common_files=False,
)
pipe.enable_vram_management()
use_input_video = bool(args.use_input_video) and not args.no_input_video
for idx in args.indices:
if idx < 0 or idx >= len(dataset):
log(f"[Skip] index {idx} out of range.")
continue
log("=" * 80)
log(f"[Sample {idx}]")
sample = dataset[idx]
video_path = sample.get("video_path")
meta = meta_map.get(video_path, {})
text = meta.get("text", "").strip()
shot_caption = [text] if text else sample.get("pre_shot_caption", ["xxx"])
log(f"video_path: {video_path}")
log(f"text: {text if text else '(empty)'}")
log(f"shot_caption: {shot_caption}")
log(f"num_frames: {len(sample.get('video', []))}")
log(f"ref_num: {sample.get('ref_num')}, ID_num: {sample.get('ID_num')}")
ref_images = sample.get("ref_images", [])
ref_dir = output_dir / f"ref_images_{idx}"
ensure_dir(ref_dir)
for id_index, image_group in enumerate(ref_images):
for img_index, img in enumerate(image_group):
img_path = ref_dir / f"id{id_index}_img{img_index}.png"
img.save(img_path)
log(f"saved ref images: {ref_dir}")
input_video = sample.get("video", [])
if input_video and (use_input_video or args.save_input_video):
input_path = output_dir / f"input_{idx}.mp4"
save_video(input_video, str(input_path), fps=16)
log(f"saved input video: {input_path}")
log_device_stats(log, "before_infer")
start_time = time.time()
video, _ = pipe(
args=runtime_args,
prompt=[shot_caption],
negative_prompt=[args.negative_prompt],
input_video=[input_video] if use_input_video else None,
ref_images=[ref_images],
seed=args.seed,
tiled=args.tiled,
height=height,
width=width,
num_frames=len(input_video),
cfg_scale=args.cfg_scale,
cfg_scale_face=args.cfg_scale_face,
num_inference_steps=args.num_inference_steps,
num_ref_images=sample.get("ref_num"),
)
duration = time.time() - start_time
log_device_stats(log, "after_infer")
log(f"inference_time: {duration:.2f}s")
output_video_path = output_dir / f"output_{idx}.mp4"
save_video(video, str(output_video_path), fps=16, quality=8)
log(f"saved output video: {output_video_path}")
log("Done.")
return 0
if __name__ == "__main__":
raise SystemExit(main())