Download Transfer_and_Test/src/train_controlnet_two_condition.py from DancingNow/swag-train-bundle: direct link, hf CLI and curl.
- Browser
- Download file 9.37 kB
-
https://huggingface.co/DancingNow/swag-train-bundle/resolve/main/Transfer_and_Test/src/train_controlnet_two_condition.py
- Command line
-
hf download hf://DancingNow/swag-train-bundle/Transfer_and_Test/src/train_controlnet_two_condition.py
-
curl -L -o train_controlnet_two_condition.py https://huggingface.co/DancingNow/swag-train-bundle/resolve/main/Transfer_and_Test/src/train_controlnet_two_condition.py
9.37 kB
| from __future__ import annotations | |
| import argparse | |
| import os | |
| import random | |
| from pathlib import Path | |
| import numpy as np | |
| import torch | |
| import torch.distributed as dist | |
| from torch.nn.parallel import DistributedDataParallel | |
| from torch.utils.data import DataLoader | |
| from torch.utils.data.distributed import DistributedSampler | |
| from tqdm import tqdm | |
| from config import load_config, resolve_path | |
| from diffusion import create_diffusion | |
| from two_condition_dataset import TwoConditionDataset | |
| from models_controlnet_two_condition import TwoConditionControlNetSWaG | |
| # This script is bundled directly under <bundle>/src on the server. | |
| ROOT = Path(__file__).resolve().parents[1] | |
| 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 save_checkpoint(path, model, optimizer, epoch, step, config, include_optimizer: bool): | |
| source = model.module if isinstance(model, DistributedDataParallel) else model | |
| payload = { | |
| "model": source.state_dict(), | |
| "epoch": epoch, | |
| "step": step, | |
| "config": config, | |
| } | |
| if include_optimizer: | |
| payload["optimizer"] = optimizer.state_dict() | |
| temporary = path.with_suffix(".tmp") | |
| torch.save(payload, temporary) | |
| temporary.replace(path) | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--config", required=True) | |
| parser.add_argument("--smoke", action="store_true") | |
| parser.add_argument("--target-epoch", type=int, default=0) | |
| parser.add_argument("--resume", type=Path, default=None) | |
| args = parser.parse_args() | |
| if not torch.cuda.is_available(): | |
| raise RuntimeError("CUDA is required") | |
| rank, local_rank, world_size = distributed_context() | |
| device = torch.device("cuda", local_rank) | |
| torch.cuda.set_device(device) | |
| config_path = Path(args.config).resolve() | |
| config = load_config(config_path) | |
| training = config["training"] | |
| seed = int(training.get("seed", 0)) + rank | |
| random.seed(seed) | |
| np.random.seed(seed) | |
| torch.manual_seed(seed) | |
| torch.cuda.manual_seed_all(seed) | |
| output = resolve_path(ROOT, config["output"]["directory"]) | |
| checkpoint_dir = output / "checkpoints" | |
| if rank == 0: | |
| checkpoint_dir.mkdir(parents=True, exist_ok=True) | |
| (output / "config.yaml").write_text(config_path.read_text(encoding="utf-8"), encoding="utf-8") | |
| if world_size > 1: | |
| dist.barrier() | |
| dataset = TwoConditionDataset( | |
| resolve_path(ROOT, config["data"]["train_h5"]), | |
| cache_in_memory=bool(training.get("cache_in_memory", False)), | |
| ) | |
| global_batch = int(training["global_batch_size"]) | |
| if global_batch % world_size: | |
| raise ValueError(f"global_batch_size must be divisible by world_size ({world_size})") | |
| local_batch = global_batch // world_size | |
| sampler = DistributedSampler(dataset, shuffle=True, seed=int(training.get("seed", 0))) if world_size > 1 else None | |
| loader = DataLoader( | |
| dataset, | |
| batch_size=local_batch, | |
| shuffle=sampler is None, | |
| sampler=sampler, | |
| num_workers=int(training.get("num_workers", 0)), | |
| pin_memory=True, | |
| drop_last=True, | |
| ) | |
| model = TwoConditionControlNetSWaG(**config["model"]) | |
| start_epoch = 0 | |
| step = 0 | |
| resume_checkpoint = args.resume.resolve() if args.resume else None | |
| if resume_checkpoint is not None: | |
| checkpoint = torch.load(resume_checkpoint, map_location="cpu", weights_only=False) | |
| model.load_state_dict(checkpoint["model"], strict=True) | |
| start_epoch = int(checkpoint.get("epoch", 0)) | |
| step = int(checkpoint.get("step", 0)) | |
| else: | |
| checkpoint_path = resolve_path(ROOT, training["base_checkpoint"]) | |
| checkpoint = torch.load(checkpoint_path, map_location="cpu", weights_only=False) | |
| base_state = checkpoint["model"] | |
| result = model.load_state_dict(base_state, strict=False) | |
| allowed_missing = ( | |
| "p_embedder.", | |
| "s_embedder.", | |
| "condition_mlp.", | |
| "control_input_zero.", | |
| "control_mid_input_zero.", | |
| "control_large_input_zero.", | |
| "control_blocks.", | |
| "control_zero.", | |
| "control_mid_zero.", | |
| "control_large_zero.", | |
| ) | |
| unexpected = list(result.unexpected_keys) | |
| missing = [key for key in result.missing_keys if not key.startswith(allowed_missing)] | |
| if unexpected or missing: | |
| raise RuntimeError(f"checkpoint mismatch: unexpected={unexpected[:8]} missing={missing[:8]}") | |
| model.copy_backbone_blocks_to_control() | |
| train_prefixes = ( | |
| "p_embedder.", | |
| "s_embedder.", | |
| "condition_mlp.", | |
| "control_input_zero.", | |
| "control_mid_input_zero.", | |
| "control_large_input_zero.", | |
| "control_blocks.", | |
| "control_zero.", | |
| "control_mid_zero.", | |
| "control_large_zero.", | |
| ) | |
| for name, parameter in model.named_parameters(): | |
| parameter.requires_grad_(name.startswith(train_prefixes)) | |
| trainable = [parameter for parameter in model.parameters() if parameter.requires_grad] | |
| if rank == 0: | |
| print(f"resume_checkpoint={resume_checkpoint}") | |
| print(f"world_size={world_size} global_batch={global_batch} per_gpu_batch={local_batch}") | |
| print(f"control_depth={model.control_depth}") | |
| print(f"trainable_parameters={sum(p.numel() for p in trainable):,}") | |
| print(f"frozen_parameters={sum(p.numel() for p in model.parameters() if not p.requires_grad):,}") | |
| model.to(device) | |
| if world_size > 1: | |
| model = DistributedDataParallel(model, device_ids=[local_rank], output_device=local_rank) | |
| optimizer = torch.optim.AdamW( | |
| trainable, | |
| lr=float(training["learning_rate"]), | |
| weight_decay=float(training.get("weight_decay", 0.0)), | |
| ) | |
| diffusion = create_diffusion(timestep_respacing="", **config["diffusion"]) | |
| use_amp = bool(training.get("use_amp", True)) | |
| if hasattr(torch, "amp") and hasattr(torch.amp, "GradScaler"): | |
| scaler = torch.amp.GradScaler("cuda", enabled=use_amp) | |
| else: | |
| scaler = torch.cuda.amp.GradScaler(enabled=use_amp) | |
| if resume_checkpoint is not None and "optimizer" in checkpoint: | |
| optimizer.load_state_dict(checkpoint["optimizer"]) | |
| elif resume_checkpoint is not None: | |
| print("WARNING: resume checkpoint has no optimizer state; optimizer restarts fresh.") | |
| del checkpoint | |
| epochs = 1 if args.smoke else (int(args.target_epoch) if args.target_epoch else int(training["epochs"])) | |
| if epochs < start_epoch: | |
| raise ValueError(f"target epoch {epochs} is before checkpoint epoch {start_epoch}") | |
| optimizer.zero_grad(set_to_none=True) | |
| for epoch in range(start_epoch, epochs): | |
| if sampler is not None: | |
| sampler.set_epoch(epoch) | |
| model.train() | |
| total_loss = 0.0 | |
| for batch_index, (waveforms, labels) in enumerate( | |
| tqdm( | |
| loader, | |
| desc=f"epoch {epoch + 1}/{epochs}", | |
| disable=rank != 0 or not bool(training.get("progress_bar", True)), | |
| ) | |
| ): | |
| waveforms = waveforms.to(device, non_blocking=True) | |
| labels = labels.to(device, non_blocking=True) | |
| timesteps = torch.randint( | |
| 0, diffusion.num_timesteps, (waveforms.shape[0],), device=device | |
| ) | |
| with torch.autocast("cuda", enabled=use_amp, dtype=torch.float16): | |
| loss = diffusion.training_losses( | |
| model, waveforms, timesteps, model_kwargs={"labels": labels} | |
| )["loss"].mean() | |
| scaler.scale(loss).backward() | |
| max_grad_norm = float(training.get("max_grad_norm", 0.0)) | |
| if max_grad_norm > 0: | |
| scaler.unscale_(optimizer) | |
| torch.nn.utils.clip_grad_norm_(trainable, max_grad_norm) | |
| scaler.step(optimizer) | |
| scaler.update() | |
| optimizer.zero_grad(set_to_none=True) | |
| step += 1 | |
| total_loss += float(loss.detach()) | |
| if args.smoke: | |
| break | |
| batches = 1 if args.smoke else len(loader) | |
| mean_loss = torch.tensor(total_loss / batches, device=device) | |
| if world_size > 1: | |
| dist.all_reduce(mean_loss, op=dist.ReduceOp.SUM) | |
| mean_loss /= world_size | |
| if rank == 0: | |
| print(f"epoch={epoch + 1} loss={mean_loss.item():.6f}") | |
| interval = int(config["output"].get("checkpoint_every_epochs", 10)) | |
| if rank == 0 and not args.smoke and ((epoch + 1) % interval == 0 or epoch + 1 == epochs): | |
| save_checkpoint( | |
| checkpoint_dir / f"checkpoint_epoch_{epoch + 1:05d}.pt", | |
| model, | |
| optimizer, | |
| epoch + 1, | |
| step, | |
| config, | |
| include_optimizer=bool(config["output"].get("save_optimizer_state", True)), | |
| ) | |
| if world_size > 1: | |
| dist.barrier() | |
| dataset.close() | |
| if world_size > 1: | |
| dist.destroy_process_group() | |
| if __name__ == "__main__": | |
| main() | |