Download Transfer_and_Test/src/generate_controlnet_two_condition.py from DancingNow/swag-train-bundle: direct link, hf CLI and curl.
- Browser
- Download file 7.78 kB
-
https://huggingface.co/DancingNow/swag-train-bundle/resolve/main/Transfer_and_Test/src/generate_controlnet_two_condition.py
- Command line
-
hf download hf://DancingNow/swag-train-bundle/Transfer_and_Test/src/generate_controlnet_two_condition.py
-
curl -L -o generate_controlnet_two_condition.py https://huggingface.co/DancingNow/swag-train-bundle/resolve/main/Transfer_and_Test/src/generate_controlnet_two_condition.py
7.78 kB
| from __future__ import annotations | |
| import argparse | |
| import csv | |
| import os | |
| import random | |
| import sys | |
| from pathlib import Path | |
| import h5py | |
| import numpy as np | |
| import torch | |
| import torch.distributed as dist | |
| from tqdm import tqdm | |
| # This generator is bundled directly under <bundle>/src on the server. | |
| PROJECT_ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(PROJECT_ROOT / "src")) | |
| from diffusion import create_diffusion | |
| from models_controlnet_two_condition import TwoConditionControlNetSWaG | |
| def distributed_context() -> tuple[int, int, int]: | |
| world_size = int(os.environ.get("WORLD_SIZE", "1")) | |
| rank = int(os.environ.get("RANK", "0")) | |
| local_rank = int(os.environ.get("LOCAL_RANK", "0")) | |
| if world_size > 1: | |
| dist.init_process_group("nccl") | |
| return rank, local_rank, world_size | |
| def shard_path(output_path: Path, rank: int) -> Path: | |
| return output_path.with_name(f".{output_path.name}.rank{rank}.partial.h5") | |
| def main() -> None: | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--checkpoint", type=Path, required=True) | |
| parser.add_argument("--source-h5", type=Path, required=True) | |
| parser.add_argument("--output-h5", type=Path, required=True) | |
| parser.add_argument("--output-meta", type=Path, required=True) | |
| parser.add_argument("--num-samples", type=int, default=100) | |
| parser.add_argument("--batch-size", type=int, default=256) | |
| parser.add_argument("--sampling-steps", type=int, default=25) | |
| parser.add_argument("--sampler", choices=("ddpm", "ddim"), default="ddpm") | |
| parser.add_argument("--cfg-scale", type=float, default=2.0) | |
| parser.add_argument("--seed", type=int, default=2026) | |
| parser.add_argument( | |
| "--source-label-format", | |
| choices=("raw", "two_condition"), | |
| default="raw", | |
| help="Use raw SWaG labels or labels already transformed for two-condition training.", | |
| ) | |
| args = parser.parse_args() | |
| if not torch.cuda.is_available(): | |
| raise RuntimeError("CUDA is required for sampling") | |
| rank, local_rank, world_size = distributed_context() | |
| device = torch.device("cuda", local_rank) | |
| torch.cuda.set_device(device) | |
| if args.batch_size % world_size: | |
| raise ValueError(f"batch-size must be divisible by world size ({world_size})") | |
| local_batch = args.batch_size // world_size | |
| random.seed(args.seed + rank) | |
| np.random.seed(args.seed + rank) | |
| torch.manual_seed(args.seed + rank) | |
| torch.cuda.manual_seed_all(args.seed + rank) | |
| rng = np.random.default_rng(args.seed) | |
| with h5py.File(args.source_h5, "r") as handle: | |
| labels_all = np.asarray(handle["labels"], dtype=np.float32) | |
| if args.num_samples > len(labels_all): | |
| raise ValueError("num-samples exceeds source dataset size") | |
| indices = np.sort(rng.choice(len(labels_all), size=args.num_samples, replace=False)) | |
| if args.source_label_format == "raw": | |
| if labels_all.shape[1] < 3: | |
| raise ValueError(f"Expected raw source labels with at least 3 columns, got {labels_all.shape}") | |
| raw = labels_all[indices, :3] | |
| labels = raw[:, 1:3].astype(np.float32, copy=True) | |
| else: | |
| if labels_all.shape[1] != 2: | |
| raise ValueError(f"Expected two-condition source labels with 2 columns, got {labels_all.shape}") | |
| labels = labels_all[indices, :2].astype(np.float32, copy=True) | |
| checkpoint = torch.load(args.checkpoint, map_location="cpu", weights_only=False) | |
| config = checkpoint["config"] | |
| model = TwoConditionControlNetSWaG(**config["model"]).to(device).eval() | |
| model.load_state_dict(checkpoint["model"], strict=True) | |
| del checkpoint | |
| respacing = f"ddim{args.sampling_steps}" if args.sampler == "ddim" else str(args.sampling_steps) | |
| diffusion = create_diffusion(timestep_respacing=respacing, **config["diffusion"]) | |
| generator = torch.Generator(device=device).manual_seed(args.seed + rank) | |
| if rank == 0: | |
| args.output_h5.parent.mkdir(parents=True, exist_ok=True) | |
| args.output_meta.parent.mkdir(parents=True, exist_ok=True) | |
| for shard_rank in range(world_size): | |
| shard_path(args.output_h5, shard_rank).unlink(missing_ok=True) | |
| if world_size > 1: | |
| dist.barrier() | |
| shape = (args.num_samples, int(config["model"]["length"]), int(config["model"]["in_channels"])) | |
| assigned = np.array_split(np.arange(args.num_samples, dtype=np.int64), world_size)[rank] | |
| local_shape = (len(assigned), shape[1], shape[2]) | |
| with h5py.File(shard_path(args.output_h5, rank), "w") as handle: | |
| output = handle.create_dataset("data", shape=local_shape, dtype="float32", compression="gzip", shuffle=True) | |
| handle.create_dataset("indices", data=assigned, dtype="int64") | |
| with torch.no_grad(): | |
| for start in tqdm( | |
| range(0, len(assigned), local_batch), | |
| desc=f"sampling rank {rank}", | |
| disable=rank != 0, | |
| ): | |
| end = min(start + local_batch, len(assigned)) | |
| global_positions = assigned[start:end] | |
| batch_labels = torch.from_numpy(labels[global_positions]).to(device) | |
| noise = torch.randn( | |
| end - start, | |
| shape[2], | |
| shape[1], | |
| device=device, | |
| generator=generator, | |
| ) | |
| def model_fn(x, t, **kwargs): | |
| return model.forward_with_cfg(x, t, batch_labels, args.cfg_scale) | |
| sample_loop = diffusion.ddim_sample_loop if args.sampler == "ddim" else diffusion.p_sample_loop | |
| with torch.autocast("cuda", dtype=torch.float16): | |
| samples = sample_loop( | |
| model_fn, | |
| noise.shape, | |
| noise=noise, | |
| device=device, | |
| progress=False, | |
| clip_denoised=False, | |
| ) | |
| output[start:end] = samples.float().transpose(1, 2).cpu().numpy().astype(np.float32) | |
| if world_size > 1: | |
| dist.barrier() | |
| if rank == 0: | |
| partial = args.output_h5.with_name(args.output_h5.name + ".partial") | |
| partial.unlink(missing_ok=True) | |
| with h5py.File(partial, "w") as handle: | |
| output = handle.create_dataset("data", shape=shape, dtype="float32", compression="gzip", shuffle=True) | |
| handle.create_dataset("labels", data=labels, dtype="float32") | |
| handle.attrs.update( | |
| checkpoint=str(args.checkpoint.resolve()), | |
| sampling_steps=args.sampling_steps, | |
| sampler=args.sampler, | |
| cfg_scale=args.cfg_scale, | |
| seed=args.seed, | |
| world_size=world_size, | |
| ) | |
| for shard_rank in range(world_size): | |
| current = shard_path(args.output_h5, shard_rank) | |
| with h5py.File(current, "r") as shard: | |
| positions = np.asarray(shard["indices"], dtype=np.int64) | |
| output[positions] = shard["data"][:] | |
| current.unlink() | |
| partial.replace(args.output_h5) | |
| with args.output_meta.open("w", newline="", encoding="utf-8") as handle: | |
| writer = csv.writer(handle) | |
| writer.writerow(["generated_index", "source_test_index", "p_index", "s_index"]) | |
| writer.writerows( | |
| ( | |
| i, | |
| int(indices[i]), | |
| float(labels[i, 0]), | |
| float(labels[i, 1]), | |
| ) | |
| for i in range(len(labels)) | |
| ) | |
| if world_size > 1: | |
| dist.barrier() | |
| dist.destroy_process_group() | |
| if __name__ == "__main__": | |
| main() | |