Files
2025-11-29 18:29:19 +08:00

229 lines
8.1 KiB
Python

from regex import F
from torch.distributed.fsdp.wrap import transformer_auto_wrap_policy
from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy
from torch.distributed.fsdp.wrap import ModuleWrapPolicy
from torch.distributed.fsdp import (
FullStateDictConfig,
StateDictType,
MixedPrecision,
ShardingStrategy,
FullyShardedDataParallel as FSDP
)
from torchvision.utils import make_grid
from datetime import timedelta, datetime
import torch.distributed as dist
from omegaconf import OmegaConf
from functools import partial
import numpy as np
import random
import torch
import os
def launch_distributed_job(backend: str = "nccl"):
rank = int(os.environ["RANK"])
local_rank = int(os.environ["LOCAL_RANK"])
world_size = int(os.environ["WORLD_SIZE"])
host = os.environ["MASTER_ADDR"]
port = int(os.environ["MASTER_PORT"])
if ":" in host: # IPv6
init_method = f"tcp://[{host}]:{port}"
else: # IPv4
init_method = f"tcp://{host}:{port}"
dist.init_process_group(rank=rank, world_size=world_size, backend=backend,
init_method=init_method, timeout=timedelta(minutes=30))
torch.cuda.set_device(local_rank)
def set_seed(seed: int, deterministic: bool = False):
"""
Helper function for reproducible behavior to set the seed in `random`, `numpy`, `torch`.
Args:
seed (`int`):
The seed to set.
deterministic (`bool`, *optional*, defaults to `False`):
Whether to use deterministic algorithms where available. Can slow down training.
"""
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
if deterministic:
torch.use_deterministic_algorithms(True)
def init_logging_folder(args):
import wandb
date = str(datetime.now()).replace(" ", "-").replace(":", "-")
output_path = os.path.join(
args.output_path,
f"{date}_seed{args.seed}"
)
os.makedirs(output_path, exist_ok=False)
os.makedirs(args.output_path, exist_ok=True)
wandb.login(host=args.wandb_host, key=args.wandb_key)
run = wandb.init(config=OmegaConf.to_container(args, resolve=True), dir=args.output_path, **
{"mode": "online", "entity": args.wandb_entity, "project": args.wandb_project})
wandb.run.log_code(".")
wandb.run.name = args.wandb_name
print(f"run dir: {run.dir}")
wandb_folder = run.dir
os.makedirs(wandb_folder, exist_ok=True)
return output_path, wandb_folder
def fsdp_wrap(module, sharding_strategy="full", mixed_precision=False, wrap_strategy="size", min_num_params=int(5e7), transformer_module=None):
if mixed_precision:
mixed_precision_policy = MixedPrecision(
param_dtype=torch.bfloat16,
reduce_dtype=torch.float32,
buffer_dtype=torch.float32,
cast_forward_inputs=False
)
else:
mixed_precision_policy = None
if wrap_strategy == "transformer":
auto_wrap_policy = partial(
transformer_auto_wrap_policy,
transformer_layer_cls=transformer_module
)
elif wrap_strategy == "size":
auto_wrap_policy = partial(
size_based_auto_wrap_policy,
min_num_params=min_num_params
)
else:
raise ValueError(f"Invalid wrap strategy: {wrap_strategy}")
os.environ["NCCL_CROSS_NIC"] = "1"
sharding_strategy = {
"full": ShardingStrategy.FULL_SHARD,
"hybrid_full": ShardingStrategy.HYBRID_SHARD,
"hybrid_zero2": ShardingStrategy._HYBRID_SHARD_ZERO2,
"no_shard": ShardingStrategy.NO_SHARD,
}[sharding_strategy]
module = FSDP(
module,
auto_wrap_policy=auto_wrap_policy,
sharding_strategy=sharding_strategy,
mixed_precision=mixed_precision_policy,
device_id=torch.cuda.current_device(),
limit_all_gathers=True,
sync_module_states=False # Load ckpt on rank 0 and sync to other ranks
)
return module
def cycle(dl):
while True:
for data in dl:
yield data
def fsdp_state_dict(model):
fsdp_fullstate_save_policy = FullStateDictConfig(
offload_to_cpu=True, rank0_only=True
)
with FSDP.state_dict_type(
model, StateDictType.FULL_STATE_DICT, fsdp_fullstate_save_policy
):
checkpoint = model.state_dict()
return checkpoint
def barrier():
if dist.is_initialized():
dist.barrier()
def prepare_for_saving(tensor, fps=16, caption=None):
import wandb
# Convert range [-1, 1] to [0, 1]
tensor = (tensor * 0.5 + 0.5).clamp(0, 1).detach()
if tensor.ndim == 4:
# Assuming it's an image and has shape [batch_size, 3, height, width]
tensor = make_grid(tensor, 4, padding=0, normalize=False)
return wandb.Image((tensor * 255).cpu().numpy().astype(np.uint8), caption=caption)
elif tensor.ndim == 5:
# Assuming it's a video and has shape [batch_size, num_frames, 3, height, width]
return wandb.Video((tensor * 255).cpu().numpy().astype(np.uint8), fps=fps, format="webm", caption=caption)
else:
raise ValueError("Unsupported tensor shape for saving. Expected 4D (image) or 5D (video) tensor.")
def validate_tensor(tensor, expected_shape, expected_dtype, expected_device):
"""验证单个tensor的形状、dtype和device"""
try:
if not isinstance(tensor, torch.Tensor):
raise Exception("tensor is not a torch.Tensor")
if tensor.shape != expected_shape:
raise Exception("tensor shape is not expected")
if tensor.dtype != expected_dtype:
raise Exception("tensor dtype is not expected")
if tensor.device != expected_device:
raise Exception("tensor device is not expected")
except Exception as e:
# import pdb;pdb.set_trace()
print(f"error: {e}")
return False
return True
def check_input(noise_input,timestep,arg_c):
"""
验证输入参数的tensor是否符合预期规格
Args:
arg_c: 包含各种tensor的字典
Returns:
bool: 所有验证通过返回True,否则返回False
"""
try:
bs = noise_input.__len__() # batch_size
noise_input = noise_input[0] # [c,f,h,w]
f = noise_input.shape[1] # frames
h = noise_input.shape[2] # height
w = noise_input.shape[3] # width
motion_frames_rgb = arg_c["motion_frames"][0]
motion_frames_latent = arg_c["motion_frames"][1]
expected_dtype = noise_input.dtype
expected_device = noise_input.device
validation_configs = [
("cond_states", (bs, 16, f, h, w), True),
("ref_latents", (bs, 16,1, h, w), True),
("motion_latents", (bs, 16, motion_frames_latent, h, w), False),
("audio_input", (bs, 25, 1024, 4*f), True),#数据集里 1的部分在 motion_frames,后面都是 4n
("context", (bs, 512, 4096), True),
]
if validate_tensor(timestep, (bs, f), expected_dtype, expected_device) is False:
print(f"input timestep, shape {timestep.shape}, dtype {timestep.dtype}, device {timestep.device} is not valid")
print(f"expected shape {bs, f}, expected dtype {expected_dtype}, expected device {expected_device}")
return False
for key, expected_shape, is_required in validation_configs:
if key in arg_c:
if not validate_tensor(arg_c[key], expected_shape, expected_dtype, expected_device):
print(f"input {key}, shape {arg_c[key].shape}, dtype {arg_c[key].dtype}, device {arg_c[key].device} is not valid")
print(f"expected shape {expected_shape}, expected dtype {expected_dtype}, expected device {expected_device}")
return False
elif is_required:
print(f"input {key} is required, but not found")
return False
print(f"input {key} is required, but not found")
except Exception as e:
print(f"error: {e}")
return False
return True