Add LADD (#45)

Co-authored-by: Peiyuan Zhang <a1286225768@gmail.com>
This commit is contained in:
Zhang Peiyuan
2024-11-15 19:28:41 -08:00
committed by GitHub
parent f8178a5a14
commit 897afb8b94
16 changed files with 1268 additions and 1060 deletions
+9 -10
View File
@@ -8,9 +8,7 @@ conda create -n fastvideo python=3.10.12
conda activate fastvideo
pip3 install torch==2.5.0 torchvision --index-url https://download.pytorch.org/whl/cu121
pip3 install -U xformers==0.0.28.post2 --index-url https://download.pytorch.org/whl/cu121
pip3 install ray
cd .. && git clone https://github.com/huggingface/diffusers
cd diffusers && pip install -e . && cd ../FastVideo-OSP
pip install git+https://github.com/huggingface/diffusers.git@76b7d86a9a5c0c2186efa09c4a67b5f5666ac9e3
```
```
@@ -26,7 +24,7 @@ python scripts/download_hf.py --repo_id=Stealths-Video/dummyVid --local_dir=data
python scripts/download_hf.py --repo_id=Stealths-Video/mochi_diffuser --local_dir=data/mochi --repo_type=model
python scripts/download_hf.py --repo_id=Stealths-Video/Mochi-Synthetic-Data --local_dir=data/Mochi-Synthetic-Data --repo_type=dataset
python scripts/download_hf.py --repo_id=Stealths-Video/Encoder_Overfit_Data --local_dir=data/Encoder_Overfit_Data --repo_type=dataset
python scripts/download_hf.py --repo_id=Stealths-Video/BW-Finetune-Dataset --local_dir=data/BW-Finetune-Dataset --repo_type=dataset
python scripts/download_hf.py --repo_id=Stealths-Video/Black-Myth-Recaption --local_dir=Stealths-Video/BLACK-MYTH-Finetune-Dataset --repo_type=dataset
```
## How to overfit
@@ -35,10 +33,11 @@ bash scripts/overfit.sh
```
Make sure to edit data/Mochi-Synthetic-Data/videos2caption.json such that this is only one video in the dataset (you can copy multiple annotations of the same video). Also make sure to edit the prompt in scripts/overfit.shto match the prompt in the training data. I observe the overfitting after 50 steps.
## TODO
- [X] Delete all npu related stuff.
- [ ] Remove inpaint.
- [ ] Create dummy debug data.
- [ ] Add Mochi
- [ ] Add Mochi VAE
## Feature for Branding:
- [] LoRA
- [] Low mem. Only load DiT during training.
- [] selective gradient checkpointing
- [] Fused kernel
- [] correct attention mask issue in HF's implementation
- []
+509
View File
@@ -0,0 +1,509 @@
import argparse
import math
import os
from fastvideo.utils.parallel_states import initialize_sequence_parallel_state, \
destroy_sequence_parallel_group, get_sequence_parallel_state, nccl_info
from fastvideo.utils.communications import sp_parallel_dataloader_wrapper, broadcast
from diffusers.training_utils import cast_training_params, compute_density_for_timestep_sampling, compute_loss_weighting_for_sd3
from fastvideo.model.mochi_latents_utils import normalize_mochi_dit_input
from fastvideo.utils.validation import log_validation
import time
from torch.utils.data import DataLoader
import torch
from torch.distributed.fsdp import (
FullyShardedDataParallel as FSDP,
StateDictType,
FullStateDictConfig,
)
import json
from torch.utils.data.distributed import DistributedSampler
import wandb
from accelerate.utils import set_seed
from tqdm.auto import tqdm
from fastvideo.fsdp_util import get_dit_fsdp_kwargs, get_discriminator_fsdp_kwargs, apply_fsdp_checkpointing
from diffusers import (
FlowMatchEulerDiscreteScheduler,
)
import torch.nn.functional as F
import numpy as np
from fastvideo.model.modeling_mochi import MochiTransformer3DModel
from diffusers.utils import check_min_version
from fastvideo.dataset.latent_datasets import LatentDataset, latent_collate_function
from fastvideo.gan.discriminator import LADDDiscriminator
import torch.distributed as dist
from safetensors.torch import save_file
# Will error if the minimal version of diffusers is not installed. Remove at your own risks.
check_min_version("0.31.0")
import sys
import pdb
class ForkedPdb(pdb.Pdb):
"""A Pdb subclass that may be used
from a forked multiprocessing child
"""
def interaction(self, *args, **kwargs):
_stdin = sys.stdin
try:
sys.stdin = open('/dev/stdin')
pdb.Pdb.interaction(self, *args, **kwargs)
finally:
sys.stdin = _stdin
def main_print(content):
if int(os.environ['LOCAL_RANK']) <= 0:
print(content)
def save_checkpoint(transformer: MochiTransformer3DModel, rank, output_dir, step):
main_print(f"--> saving checkpoint at step {step}")
with FSDP.state_dict_type(
transformer, StateDictType.FULL_STATE_DICT, FullStateDictConfig(offload_to_cpu=True, rank0_only=True)
):
cpu_state = transformer.state_dict()
if rank <= 0:
save_dir = os.path.join(output_dir, f"checkpoint-{step}")
os.makedirs(save_dir, exist_ok=True)
# save using safetensors
weight_path = os.path.join(save_dir, "diffusion_pytorch_model.safetensors")
save_file(cpu_state, weight_path)
config_dict = dict(transformer.config)
config_path = os.path.join(save_dir, "config.json")
# save dict as json
with open(config_path, "w") as f:
json.dump(config_dict, f, indent=4)
main_print(f"--> checkpoint saved at step {step}")
def get_sigmas(noise_scheduler, device, timesteps, n_dim=4, dtype=torch.float32):
sigmas = noise_scheduler.sigmas.to(device=device, dtype=dtype)
schedule_timesteps = noise_scheduler.timesteps.to(device)
timesteps = timesteps.to(device)
step_indices = [(schedule_timesteps == t).nonzero().item() for t in timesteps]
sigma = sigmas[step_indices].flatten()
while len(sigma.shape) < n_dim:
sigma = sigma.unsqueeze(-1)
return sigma
def student_forward(transformer, latents, encoder_hidden_states, encoder_attention_mask, sp_size):
batch_size = latents.shape[0]
noise = torch.randn_like(latents)
# uniformly sample from [1.0, 0.75, 0.5, 0.25]
p = [0.25, 0.25, 0.25, 0.25]
sigmas = np.random.choice([1.0, 0.75, 0.5, 0.25], batch_size, p=p)
sigmas = torch.tensor(sigmas, device=latents.device, dtype=latents.dtype)
student_timesteps = (sigmas * 1000).long()
if sp_size > 1:
# Make sure that the timesteps are the same across all sp processes.
broadcast(student_timesteps)
while len(sigmas.shape) < latents.ndim:
sigmas = sigmas.unsqueeze(-1)
noisy_model_input = (1.0 - sigmas) * latents + sigmas * noise
model_pred = transformer(
noisy_model_input,
encoder_hidden_states,
1000 - student_timesteps,
encoder_attention_mask, # B, L
return_dict= False,
)[0]
# calculate X1
model_pred = model_pred * sigmas + noisy_model_input
return model_pred
def teacher_forward(transformer, latents, encoder_hidden_states, encoder_attention_mask, sp_size, noise_scheduler, weighting_scheme, logit_mean, logit_std, mode_scale):
batch_size = latents.shape[0]
noise = torch.randn_like(latents)
u = compute_density_for_timestep_sampling(
weighting_scheme=weighting_scheme,
batch_size=batch_size,
logit_mean=logit_mean,
logit_std=logit_std,
mode_scale=mode_scale,
)
indices = (u * noise_scheduler.config.num_train_timesteps).long()
timesteps = noise_scheduler.timesteps[indices].to(device=latents.device)
if sp_size > 1:
# Make sure that the timesteps are the same across all sp processes.
broadcast(timesteps)
sigmas = get_sigmas(noise_scheduler, latents.device, timesteps, n_dim=latents.ndim, dtype=latents.dtype)
noisy_model_input = (1.0 - sigmas) * latents + sigmas * noise
teacher_features = transformer(
noisy_model_input,
encoder_hidden_states,
noise_scheduler.config.num_train_timesteps - timesteps,
encoder_attention_mask, # B, L
return_dict= False,
output_attn=True
)[1]
return teacher_features
def train_one_step_generator(student_transformer, teacher_transformer, discriminator, student_optimizer, loader,noise_scheduler, gradient_accumulation_steps, sp_size, max_grad_norm, weighting_scheme, logit_mean, logit_std, mode_scale):
total_loss = 0.0
teacher_transformer.requires_grad_(False)
discriminator.requires_grad_(False)
student_transformer.requires_grad_(True)
student_optimizer.zero_grad()
for _ in range(gradient_accumulation_steps):
latents, encoder_hidden_states, latents_attention_mask, encoder_attention_mask = next(loader)
latents = normalize_mochi_dit_input(latents)
student_pred = student_forward(student_transformer, latents, encoder_hidden_states, encoder_attention_mask, sp_size)
teacher_features = teacher_forward(teacher_transformer, student_pred, encoder_hidden_states, encoder_attention_mask, sp_size, noise_scheduler, weighting_scheme, logit_mean, logit_std, mode_scale)
logits = discriminator(teacher_features, encoder_hidden_states)
loss = (F.relu(torch.ones_like(logits) - logits)).mean() / gradient_accumulation_steps
loss.backward()
avg_loss = loss.detach().clone()
dist.all_reduce(avg_loss, op=dist.ReduceOp.AVG)
total_loss += avg_loss.item()
grad_norm = student_transformer.clip_grad_norm_(max_grad_norm)
student_optimizer.step()
return total_loss, grad_norm.item()
def train_one_step_discriminator(student_transformer, teacher_transformer, discriminator, discriminator_optimizer, loader,noise_scheduler, gradient_accumulation_steps, sp_size, max_grad_norm, weighting_scheme, logit_mean, logit_std, mode_scale):
total_loss = 0.0
teacher_transformer.requires_grad_(False)
student_transformer.requires_grad_(False)
discriminator.requires_grad_(True)
discriminator_optimizer.zero_grad()
for _ in range(gradient_accumulation_steps):
latents, encoder_hidden_states, latents_attention_mask, encoder_attention_mask = next(loader)
latents = normalize_mochi_dit_input(latents)
with torch.no_grad():
student_pred = student_forward(student_transformer, latents, encoder_hidden_states, encoder_attention_mask, sp_size)
teacher_features_fake = teacher_forward(teacher_transformer, student_pred, encoder_hidden_states, encoder_attention_mask, sp_size, noise_scheduler, weighting_scheme, logit_mean, logit_std, mode_scale)
teacher_features_real = teacher_forward(teacher_transformer, latents, encoder_hidden_states, encoder_attention_mask, sp_size, noise_scheduler, weighting_scheme, logit_mean, logit_std, mode_scale)
fake_logits = discriminator(teacher_features_fake, encoder_hidden_states)
real_logits = discriminator(teacher_features_real, encoder_hidden_states)
fake_loss = (F.relu(torch.ones_like(fake_logits) + fake_logits)).mean()
real_loss = (F.relu(torch.ones_like(real_logits) - real_logits)).mean()
loss = (fake_loss + real_loss)/ 2 / gradient_accumulation_steps
loss.backward()
avg_loss = loss.detach().clone()
dist.all_reduce(avg_loss, op=dist.ReduceOp.AVG)
total_loss += avg_loss.item()
grad_norm = discriminator.clip_grad_norm_(max_grad_norm)
discriminator_optimizer.step()
return total_loss, grad_norm.item()
def main(args):
# use LayerNorm, GeLu, SiLu always as fp32 mode
# TODO:
if args.enable_stable_fp32:
raise NotImplementedError("enable_stable_fp32 is not supported now.")
torch.backends.cuda.matmul.allow_tf32 = True
local_rank = int(os.environ['LOCAL_RANK'])
rank = int(os.environ['RANK'])
world_size = int(os.environ['WORLD_SIZE'])
dist.init_process_group("nccl")
torch.cuda.set_device(local_rank)
device = torch.cuda.current_device()
initialize_sequence_parallel_state(args.sp_size)
# If passed along, set the training seed now. On GPU...
if args.seed is not None:
# TODO: t within the same seq parallel group should be the same. Noise should be different.
set_seed(args.seed + rank)
# Handle the repository creation
if rank <=0 and args.output_dir is not None:
os.makedirs(args.output_dir, exist_ok=True)
# For mixed precision training we cast all non-trainable weigths to half-precision
# as these weights are only used for inference, keeping weights in full precision is not required.
# Create model:
main_print(f"--> loading model...")
student_transformer = MochiTransformer3DModel.from_pretrained(
args.pretrained_model_name_or_path,
subfolder="transformer",
)
teacher_transformer = MochiTransformer3DModel.from_pretrained(
args.pretrained_model_name_or_path,
subfolder="transformer",
load_dtype=torch.bfloat16, # TODO: FP8 or INT8 weight type
)
# feature_dim, cnn_channel_size, text_cond_dim, num_layers, layers_stride):
discriminator = LADDDiscriminator(feature_dim=3072, text_cond_dim=4096, num_layers=48, layers_stride=4)
# get num of discriminator parameters in billion
main_print(f"--> discriminator parameters: {sum(p.numel() for p in discriminator.parameters()) / 1e9} B")
teacher_transformer.requires_grad_(False)
main_print(f"--> Initializing FSDP...")
dit_fsdp_kwargs = get_dit_fsdp_kwargs("full")
student_transformer = FSDP(
student_transformer,
**dit_fsdp_kwargs,
)
teacher_transformer = FSDP(
teacher_transformer,
**dit_fsdp_kwargs,
)
discriminator_fsdp_kwargs = get_discriminator_fsdp_kwargs("full")
discriminator = FSDP(
discriminator,
**discriminator_fsdp_kwargs,
)
main_print(f"--> model loaded")
if args.gradient_checkpointing:
apply_fsdp_checkpointing(teacher_transformer)
apply_fsdp_checkpointing(student_transformer)
# Set model as trainable.
student_transformer.train()
noise_scheduler = FlowMatchEulerDiscreteScheduler()
student_optimizer = torch.optim.AdamW(
student_transformer.parameters(),
lr=args.student_learning_rate,
betas=(0.9,0.999),
weight_decay=0.01,
eps=1e-8,
)
discriminator_optimizer = torch.optim.AdamW(
discriminator.parameters(),
lr=args.discriminator_learning_rate,
betas=(0.9,0.999),
weight_decay=0.01,
eps=1e-8,
)
train_dataset = LatentDataset(args.data_json_path, args.num_latent_t, args.cfg)
sampler = DistributedSampler(train_dataset, rank=rank, num_replicas=world_size, shuffle=True)
train_dataloader = DataLoader(
train_dataset,
sampler=sampler,
collate_fn=latent_collate_function,
pin_memory=True,
batch_size=args.train_batch_size,
num_workers=args.dataloader_num_workers,
drop_last=True,
)
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps * args.sp_size / args.train_sp_batch_size)
# Afterwards we recalculate our number of training epochs
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
# We need to initialize the trackers we use, and also store our configuration.
# The trackers initializes automatically on the main process.
if rank <= 0:
project = args.tracker_project_name or "fastvideo_distill"
wandb.init(project=project, config=args)
# Train!
total_batch_size = args.train_batch_size * world_size * args.gradient_accumulation_steps / args.sp_size * args.train_sp_batch_size
main_print("***** Running training *****")
main_print(f" Num examples = {len(train_dataset)}")
main_print(f" Dataloader size = {len(train_dataloader)}")
main_print(f" Num Epochs = {args.num_train_epochs}")
main_print(f" Instantaneous batch size per device = {args.train_batch_size}")
main_print(f" Total train batch size (w. data & sequence parallel, accumulation) = {total_batch_size}")
main_print(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}")
main_print(f" Total optimization steps = {args.max_train_steps}")
main_print(f" Total student training parameters per FSDP shard = {sum(p.numel() for p in student_transformer.parameters() if p.requires_grad) / 1e9} B")
main_print(f" Total discriminator training parameters per FSDP shard = {sum(p.numel() for p in discriminator.parameters() if p.requires_grad) / 1e9} B")
main_print(f" Master weight dtype: {student_transformer.parameters().__next__().dtype}")
# Potentially load in the weights and states from a previous save
if args.resume_from_checkpoint:
assert NotImplementedError("resume_from_checkpoint is not supported now.")
# TODO
progress_bar = tqdm(
range(0, args.max_train_steps),
initial=0,
desc="Steps",
# Only show the progress bar once on each machine.
disable= local_rank > 0,
)
loader = sp_parallel_dataloader_wrapper(train_dataloader, device, args.train_batch_size, args.sp_size, args.train_sp_batch_size)
for step in range(1, args.discriminator_warmup_steps+1):
discriminator_loss, discriminator_grad_norm = train_one_step_discriminator(student_transformer, teacher_transformer, discriminator, \
discriminator_optimizer, loader, noise_scheduler, args.gradient_accumulation_steps, args.sp_size, \
args.max_grad_norm, args.weighting_scheme, args.logit_mean, args.logit_std, args.mode_scale)
progress_bar.set_postfix({"d_loss": discriminator_loss, "d_grad_n": discriminator_grad_norm})
progress_bar.update(1)
if rank <= 0:
wandb.log({"discriminator_loss": discriminator_loss, "discriminator_grad_norm": discriminator_grad_norm}, step=step)
for step in range(args.discriminator_warmup_steps+1, args.max_train_steps+1):
stage = (step - args.discriminator_warmup_steps) // args.gan_switch_n_steps
if stage % 2 == 1:
# Train generator
generator_loss, student_grad_norm= train_one_step_generator(student_transformer, teacher_transformer, discriminator, \
student_optimizer, loader, noise_scheduler, args.gradient_accumulation_steps, args.sp_size, \
args.max_grad_norm, args.weighting_scheme, args.logit_mean, args.logit_std, args.mode_scale)
progress_bar.set_postfix({"g_loss": generator_loss, "g_grad_n": student_grad_norm})
if rank <= 0:
wandb.log({"generator_loss": generator_loss, "student_grad_norm": student_grad_norm}, step=step)
else:
# Train discriminator
discriminator_loss, discriminator_grad_norm = train_one_step_discriminator(student_transformer, teacher_transformer, discriminator, \
discriminator_optimizer, loader, noise_scheduler, args.gradient_accumulation_steps, args.sp_size, \
args.max_grad_norm, args.weighting_scheme, args.logit_mean, args.logit_std, args.mode_scale)
progress_bar.set_postfix({"d_loss": discriminator_loss, "d_grad_n": discriminator_grad_norm})
if rank <= 0:
wandb.log({"discriminator_loss": discriminator_loss, "discriminator_grad_norm": discriminator_grad_norm}, step=step)
progress_bar.update(1)
if args.log_validation and step % args.validation_steps == 0:
log_validation(args, student_transformer, device,
torch.bfloat16, step)
if step % args.checkpointing_steps == 0:
save_checkpoint(student_transformer, rank, args.output_dir, step)
dist.barrier()
if get_sequence_parallel_state():
destroy_sequence_parallel_group()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
# dataset & dataloader
parser.add_argument("--data_json_path", type=str, required=True)
parser.add_argument("--num_frames", type=int, default=163)
parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.")
parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.")
parser.add_argument("--num_latent_t", type=int, default=28, help="Number of latent timesteps.")
parser.add_argument("--group_frame", action="store_true") # TODO
parser.add_argument("--group_resolution", action="store_true") # TODO
# text encoder & vae & diffusion model
parser.add_argument("--pretrained_model_name_or_path", type=str)
parser.add_argument("--cache_dir", type=str, default='./cache_dir')
parser.add_argument('--enable_stable_fp32', action='store_true') # TODO
# diffusion setting
parser.add_argument("--ema_decay", type=float, default=0.999)
parser.add_argument("--ema_start_step", type=int, default=0)
parser.add_argument('--cfg', type=float, default=0.1)
parser.add_argument(
"--weighting_scheme",
type=str,
default="uniform",
choices=["sigma_sqrt", "logit_normal", "mode", "cosmap", "uniform"],
)
parser.add_argument(
"--logit_mean", type=float, default=0.0, help="mean to use when using the `'logit_normal'` weighting scheme."
)
parser.add_argument(
"--logit_std", type=float, default=1.0, help="std to use when using the `'logit_normal'` weighting scheme."
)
parser.add_argument(
"--mode_scale",
type=float,
default=1.29,
help="Scale of mode weighting scheme. Only effective when using the `'mode'` as the `weighting_scheme`.",
)
# validation & logs
parser.add_argument("--validation_prompt_dir", type=str)
parser.add_argument("--uncond_prompt_dir", type=str)
parser.add_argument("--validation_sampling_steps", type=int, default=64)
parser.add_argument('--validation_guidance_scale', type=float, default=4.5)
parser.add_argument('--validation_steps', type=float, default=4.5)
parser.add_argument("--log_validation", action="store_true")
parser.add_argument("--tracker_project_name", type=str, default=None)
parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.")
parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.")
parser.add_argument("--checkpoints_total_limit", type=int, default=None, help=("Max number of checkpoints to store."))
parser.add_argument("--checkpointing_steps", type=int, default=500,
help=(
"Save a checkpoint of the training state every X updates. These checkpoints can be used both as final"
" checkpoints in case they are better than the last checkpoint, and are also suitable for resuming"
" training using `--resume_from_checkpoint`."
),
)
parser.add_argument("--resume_from_checkpoint", type=str, default=None,
help=(
"Whether training should be resumed from a previous checkpoint. Use a path saved by"
' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.'
),
)
parser.add_argument("--logging_dir", type=str, default="logs",
help=(
"[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to"
" *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***."
),
)
# optimizer & scheduler & Training
parser.add_argument("--num_train_epochs", type=int, default=100)
parser.add_argument("--max_train_steps", type=int, default=None, help="Total number of training steps to perform. If provided, overrides num_train_epochs.")
parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Number of updates steps to accumulate before performing a backward/update pass.")
parser.add_argument("--student_learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.")
parser.add_argument("--discriminator_learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.")
parser.add_argument("--discriminator_warmup_steps", type=int, default=0)
parser.add_argument("--gan_switch_n_steps", type=int, default=1)
parser.add_argument("--scale_lr", action="store_true", default=False, help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.")
parser.add_argument("--lr_warmup_steps", type=int, default=10, help="Number of steps for the warmup in the lr scheduler.")
parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.")
parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.")
parser.add_argument("--allow_tf32", action="store_true",
help=(
"Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see"
" https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices"
),
)
parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"],
help=(
"Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >="
" 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the"
" flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config."
),
)
parser.add_argument("--sp_size", type=int, default=1, help="For sequence parallel")
parser.add_argument("--train_sp_batch_size", type=int, default=1, help="Batch size for sequence parallel training")
args = parser.parse_args()
main(args)
+48 -3
View File
@@ -35,14 +35,33 @@ non_reentrant_wrapper = partial(
check_fn = lambda submodule: isinstance(submodule, MochiTransformerBlock)
def apply_fsdp_checkpointing(model):
def apply_fsdp_checkpointing(model, p=1):
# https://github.com/foundation-model-stack/fms-fsdp/blob/408c7516d69ea9b6bcd4c0f5efab26c0f64b3c2d/fms_fsdp/policies/ac_handler.py#L16
"""apply activation checkpointing to model
returns None as model is updated directly
"""
print(f"--> applying fdsp activation checkpointing...")
block_idx = 0
cut_off = 1 / 2
# when passing p as a fraction number (e.g. 1/3), it will be interpreted
# as a string in argv, thus we need eval("1/3") here for fractions.
p = eval(p) if isinstance(p, str) else p
def selective_checkpointing(submodule):
nonlocal block_idx
nonlocal cut_off
if isinstance(submodule, MochiTransformerBlock):
block_idx += 1
if block_idx * p >= cut_off:
cut_off += 1
return True
return False
apply_activation_checkpointing(
model, checkpoint_wrapper_fn=non_reentrant_wrapper, check_fn=check_fn
model, checkpoint_wrapper_fn=non_reentrant_wrapper, check_fn=selective_checkpointing
)
bf16_mix = MixedPrecision(
@@ -57,7 +76,7 @@ bf16_mix = MixedPrecision(
def get_fsdp_kwargs(sharding_strategy, use_lora=False, cpu_offload=False):
def get_dit_fsdp_kwargs(sharding_strategy, use_lora=False, cpu_offload=False):
if use_lora:
auto_wrap_policy = fsdp_auto_wrap_policy
else:
@@ -74,6 +93,8 @@ def get_fsdp_kwargs(sharding_strategy, use_lora=False, cpu_offload=False):
if sharding_strategy == "full":
sharding_strategy = ShardingStrategy.FULL_SHARD
elif sharding_strategy == "hybrid_full":
sharding_strategy = ShardingStrategy.HYBRID_SHARD
elif sharding_strategy == "none":
sharding_strategy = ShardingStrategy.NO_SHARD
auto_wrap_policy = None
@@ -101,4 +122,28 @@ def get_fsdp_kwargs(sharding_strategy, use_lora=False, cpu_offload=False):
return fsdp_kwargs
def get_discriminator_fsdp_kwargs(sharding_strategy):
auto_wrap_policy = None
# Use existing mixed precision settings
mixed_precision = bf16_mix
sharding_strategy = ShardingStrategy.NO_SHARD
device_id = torch.cuda.current_device()
fsdp_kwargs = {
"auto_wrap_policy": auto_wrap_policy,
"mixed_precision": mixed_precision,
"sharding_strategy": sharding_strategy,
"device_id": device_id,
"limit_all_gathers": True,
}
return fsdp_kwargs
+123
View File
@@ -0,0 +1,123 @@
import torch
from torch import nn
import numpy as np
from torch.nn.utils.parametrizations import spectral_norm
import os
class DummyDiscriminator(nn.Module):
def __init__(self, dim_in, num_layers):
super().__init__()
self.layers = nn.ModuleList()
for _ in range(num_layers):
self.layers.append(nn.Linear(dim_in, 1))
def forward(self, features):
logits = []
for layer, feature in zip(self.layers, features):
mean = feature.mean(dim=1)
logits.append(layer(mean))
return torch.cat(logits, dim=1)
class ResidualBlock(nn.Module):
def __init__(self, fn):
super().__init__()
self.fn = fn
def forward(self, x: torch.Tensor) -> torch.Tensor:
return (self.fn(x) + x) / np.sqrt(2)
class SpectralConv1d(nn.Module):
def __init__(self, *args, **kwargs):
super().__init__()
self.conv = spectral_norm(nn.Conv1d(*args, **kwargs))
def forward(self, x):
return self.conv(x)
class BatchNormLocal(nn.Module):
def __init__(self, num_features: int, affine: bool = True, virtual_bs: int = 8, eps: float = 1e-5):
super().__init__()
self.virtual_bs = virtual_bs
self.eps = eps
self.affine = affine
if self.affine:
self.weight = nn.Parameter(torch.ones(num_features))
self.bias = nn.Parameter(torch.zeros(num_features))
def forward(self, x: torch.Tensor) -> torch.Tensor:
shape = x.size()
# Calculate stats.
mean = x.mean([0, 2], keepdim=True)
var = x.var([0, 2], keepdim=True, unbiased=False)
x = (x - mean) / (torch.sqrt(var + self.eps))
if self.affine:
x = x * self.weight[None, :, None] + self.bias[None, :, None]
return x.view(shape)
def make_block(channels: int, kernel_size: int) -> nn.Module:
return nn.Sequential(
SpectralConv1d(
channels,
channels,
kernel_size = kernel_size,
padding = kernel_size//2,
padding_mode = 'circular',
),
BatchNormLocal(channels),
nn.LeakyReLU(0.2, True),
)
class DiscHead(nn.Module):
def __init__(self, feature_dim: int, text_c_dim: int, cmap_dim: int = 64, cnn_dim=512):
super().__init__()
self.channels = feature_dim
self.text_c_dim = text_c_dim
self.cmap_dim = cmap_dim
self.down_proj = SpectralConv1d(feature_dim, cnn_dim, kernel_size=1, padding=0)
self.main = nn.Sequential(
make_block(cnn_dim, kernel_size=1),
ResidualBlock(make_block(cnn_dim, kernel_size=9))
)
self.cmapper = nn.Linear(self.text_c_dim, cmap_dim)
self.cls = SpectralConv1d(cnn_dim, cmap_dim, kernel_size=1, padding=0)
def forward(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor:
h = self.down_proj(x)
h = self.main(h)
out = self.cls(h)
cmap = self.cmapper(c).unsqueeze(-1)
out = (out * cmap).sum(1, keepdim=True) * (1 / np.sqrt(self.cmap_dim))
return out
class LADDDiscriminator(nn.Module):
def __init__(self, feature_dim, text_cond_dim, num_layers, layers_stride):
super().__init__()
heads = []
for i in range(0, num_layers, layers_stride):
heads.append(DiscHead(feature_dim, text_cond_dim))
self.heads = nn.ModuleList(heads)
self.layers_stride = layers_stride
self.num_layers = num_layers
def forward(self, features, text_conditions) -> torch.Tensor:
text_conditions = text_conditions.mean(1)
# layer, B, L, C -> layer, B, C, L
features = features.transpose(2, 3)
logits = []
for i in range(0, self.num_layers, self.layers_stride):
head = self.heads[i//self.layers_stride]
feat = features[i]
logits.append(head(feat, text_conditions).view(feat.size(0), -1))
logits = torch.cat(logits, dim=1)
return logits
-33
View File
@@ -1,33 +0,0 @@
class MochiStat:
latents_mean = [
-0.06730895953510081,
-0.038011381506090416,
-0.07477820912866141,
-0.05565264470995561,
0.012767231469026969,
-0.04703542746246419,
0.043896967884726704,
-0.09346305707025976,
-0.09918314763016893,
-0.008729793427399178,
-0.011931556316503654,
-0.0321993391887285
]
latents_std = [
0.9263795028493863,
0.9248894543193766,
0.9393059390890617,
0.959253732819592,
0.8244560132752793,
0.917259975397747,
0.9294154431013696,
1.3720942357788521,
0.881393668867029,
0.9168315692124348,
0.9185249279345552,
0.9274757570805041
]
scaling_factor = 1.0
mochi_stat = MochiStat()
+39
View File
@@ -0,0 +1,39 @@
import torch
mochi_latents_mean = torch.tensor([
-0.06730895953510081,
-0.038011381506090416,
-0.07477820912866141,
-0.05565264470995561,
0.012767231469026969,
-0.04703542746246419,
0.043896967884726704,
-0.09346305707025976,
-0.09918314763016893,
-0.008729793427399178,
-0.011931556316503654,
-0.0321993391887285
]).view(1, 12, 1, 1, 1)
mochi_latents_std = torch.tensor([
0.9263795028493863,
0.9248894543193766,
0.9393059390890617,
0.959253732819592,
0.8244560132752793,
0.917259975397747,
0.9294154431013696,
1.3720942357788521,
0.881393668867029,
0.9168315692124348,
0.9185249279345552,
0.9274757570805041
]).view(1, 12, 1, 1, 1)
mochi_scaling_factor = 1.0
def normalize_mochi_dit_input(latents):
latents_mean = mochi_latents_mean.to(latents.device, latents.dtype)
latents_std = mochi_latents_std.to(latents.device, latents.dtype)
latents = (latents - latents_mean) / latents_std
return latents
+106 -26
View File
@@ -16,21 +16,87 @@ from typing import Any, Dict, Optional, Tuple
import torch
import torch.nn as nn
import diffusers
from diffusers.configuration_utils import ConfigMixin, register_to_config
from diffusers.utils import is_torch_version, logging
from diffusers.utils.torch_utils import maybe_allow_in_graph
from diffusers.models.attention import FeedForward
from diffusers.models.attention import FeedForward as HF_FeedForward
from diffusers.models.attention_processor import Attention
from diffusers.models.embeddings import MochiCombinedTimestepCaptionEmbedding, PatchEmbed
from diffusers.models.modeling_outputs import Transformer2DModelOutput
from diffusers.models.modeling_utils import ModelMixin
from diffusers.models.normalization import AdaLayerNormContinuous, LuminaLayerNormContinuous, MochiRMSNormZero, RMSNorm
from fastvideo.utils.parallel_states import get_sequence_parallel_state, nccl_info
from fastvideo.utils.communications import all_gather, all_to_all_4D
import torch.nn.functional as F
from diffusers.utils.torch_utils import is_torch_version, maybe_allow_in_graph
from einops import rearrange
import numbers
from flash_attn import flash_attn_varlen_qkvpacked_func
from flash_attn.bert_padding import pad_input, unpad_input
from liger_kernel.ops.swiglu import LigerSiLUMulFunction
class FeedForward(HF_FeedForward):
def __init__(
self,
dim: int,
dim_out: Optional[int] = None,
mult: int = 4,
dropout: float = 0.0,
activation_fn: str = "geglu",
final_dropout: bool = False,
inner_dim=None,
bias: bool = True,
):
super().__init__(dim, dim_out, mult, dropout, activation_fn, final_dropout, inner_dim, bias)
assert activation_fn == "swiglu"
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
hidden_states = self.net[0].proj(hidden_states)
hidden_states, gate = hidden_states.chunk(2, dim=-1)
return self.net[2](
LigerSiLUMulFunction.apply(gate, hidden_states)
)
def flash_attn_no_pad(qkv, key_padding_mask, causal=False, dropout_p=0.0, softmax_scale=None):
# adapted from https://github.com/Dao-AILab/flash-attention/blob/13403e81157ba37ca525890f2f0f2137edf75311/flash_attn/flash_attention.py#L27
batch_size = qkv.shape[0]
seqlen = qkv.shape[1]
nheads = qkv.shape[-2]
x = rearrange(qkv, 'b s three h d -> b s (three h d)')
x_unpad, indices, cu_seqlens, max_s, used_seqlens_in_batch = unpad_input(x, key_padding_mask)
x_unpad = rearrange(x_unpad, 'nnz (three h d) -> nnz three h d', three=3, h=nheads)
output_unpad = flash_attn_varlen_qkvpacked_func(
x_unpad, cu_seqlens, max_s, dropout_p,
softmax_scale=softmax_scale, causal=causal
)
output = rearrange(pad_input(rearrange(output_unpad, 'nnz h d -> nnz (h d)'),
indices, batch_size, seqlen),
'b s (h d) -> b s h d', h=nheads)
return output
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
import pdb, sys
class ForkedPdb(pdb.Pdb):
"""A Pdb subclass that may be used
from a forked multiprocessing child
"""
def interaction(self, *args, **kwargs):
_stdin = sys.stdin
try:
sys.stdin = open('/dev/stdin')
pdb.Pdb.interaction(self, *args, **kwargs)
finally:
sys.stdin = _stdin
class MochiAttnProcessor2_0:
@@ -45,6 +111,7 @@ class MochiAttnProcessor2_0:
attn: Attention,
hidden_states: torch.Tensor,
encoder_hidden_states: torch.Tensor,
encoder_attention_mask: torch.Tensor,
attention_mask: Optional[torch.Tensor] = None,
image_rotary_emb: Optional[torch.Tensor] = None,
) -> torch.Tensor:
@@ -113,38 +180,47 @@ class MochiAttnProcessor2_0:
query = apply_rotary_emb(query, freqs_cos, freqs_sin)
key = apply_rotary_emb(key, freqs_cos, freqs_sin)
query, key, value = query.transpose(1, 2), key.transpose(1, 2), value.transpose(1, 2)
encoder_query, encoder_key, encoder_value = (
encoder_query.transpose(1, 2),
encoder_key.transpose(1, 2),
encoder_value.transpose(1, 2),
)
# [b, h, s, d]
sequence_length = query.size(2)
encoder_sequence_length = encoder_query.size(2)
# query, key, value = query.transpose(1, 2), key.transpose(1, 2), value.transpose(1, 2)
# encoder_query, encoder_key, encoder_value = (
# encoder_query.transpose(1, 2),
# encoder_key.transpose(1, 2),
# encoder_value.transpose(1, 2),
# )
# [b, s, h, d]
sequence_length = query.size(1)
encoder_sequence_length = encoder_query.size(1)
# Hint: please check encoder_query.shape
query = torch.cat([query, encoder_query], dim=2)
key = torch.cat([key, encoder_key], dim=2)
value = torch.cat([value, encoder_value], dim=2)
hidden_states = F.scaled_dot_product_attention(query, key, value, dropout_p=0.0, is_causal=False)
# H
query = torch.cat([query, encoder_query], dim=1).unsqueeze(2)
key = torch.cat([key, encoder_key], dim=1).unsqueeze(2)
value = torch.cat([value, encoder_value], dim=1).unsqueeze(2)
# B, S, 3, H, D
qkv = torch.cat([query, key, value], dim=2)
attn_mask = encoder_attention_mask[:, :].bool()
attn_mask = F.pad(attn_mask, (sequence_length, 0), value=True)
# hidden_states = flash_attn_no_pad(qkv, attn_mask, causal=False, dropout_p=0.0, softmax_scale=None)
hidden_states = F.scaled_dot_product_attention(query, key, value, attn_mask = None, dropout_p=0.0, is_causal=False)
# valid_lengths = encoder_attention_mask.sum(dim=1) + sequence_length
# def no_padding_mask(score, b, h, q_idx, kv_idx):
# return torch.where(kv_idx < valid_lengths[b],score, -float("inf"))
# hidden_states = flex_attention(query, key, value, score_mod=no_padding_mask)
if get_sequence_parallel_state():
hidden_states, encoder_hidden_states = hidden_states.split_with_sizes(
(sequence_length, encoder_sequence_length), dim=2
(sequence_length, encoder_sequence_length), dim=1
)
# B, H, S, D
hidden_states = all_to_all_4D(hidden_states, scatter_dim=2, gather_dim=1)
encoder_hidden_states = all_gather(encoder_hidden_states, dim=1).contiguous()
hidden_states = hidden_states.transpose(1,2).flatten(2, 3)
# B, S, H, D
hidden_states = all_to_all_4D(hidden_states, scatter_dim=1, gather_dim=2)
encoder_hidden_states = all_gather(encoder_hidden_states, dim=2).contiguous()
hidden_states = hidden_states.flatten(2, 3)
hidden_states = hidden_states.to(query.dtype)
encoder_hidden_states = encoder_hidden_states.transpose(1, 2).flatten(2, 3)
encoder_hidden_states = encoder_hidden_states.flatten(2, 3)
encoder_hidden_states = encoder_hidden_states.to(query.dtype)
else:
hidden_states = hidden_states.transpose(1, 2).flatten(2, 3)
hidden_states = hidden_states.flatten(2, 3)
hidden_states = hidden_states.to(query.dtype)
hidden_states, encoder_hidden_states = hidden_states.split_with_sizes(
@@ -257,6 +333,7 @@ class MochiTransformerBlock(nn.Module):
self,
hidden_states: torch.Tensor,
encoder_hidden_states: torch.Tensor,
encoder_attention_mask: torch.Tensor,
temb: torch.Tensor,
image_rotary_emb: Optional[torch.Tensor] = None,
output_attn = False,
@@ -274,6 +351,7 @@ class MochiTransformerBlock(nn.Module):
hidden_states=norm_hidden_states,
encoder_hidden_states=norm_encoder_hidden_states,
image_rotary_emb=image_rotary_emb,
encoder_attention_mask=encoder_attention_mask
)
hidden_states = hidden_states + self.norm2(attn_hidden_states) * torch.tanh(gate_msa).unsqueeze(1)
@@ -498,6 +576,7 @@ class MochiTransformer3DModel(ModelMixin, ConfigMixin):
create_custom_forward(block),
hidden_states,
encoder_hidden_states,
encoder_attention_mask,
temb,
image_rotary_emb,
output_attn,
@@ -507,6 +586,7 @@ class MochiTransformer3DModel(ModelMixin, ConfigMixin):
hidden_states, encoder_hidden_states, attn_outputs = block(
hidden_states=hidden_states,
encoder_hidden_states=encoder_hidden_states,
encoder_attention_mask=encoder_attention_mask,
temb=temb,
image_rotary_emb=image_rotary_emb,
output_attn = output_attn,
+168
View File
@@ -0,0 +1,168 @@
import numbers
from typing import Dict, Optional, Tuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from liger_kernel.ops.rms_norm import LigerRMSNormFunction
from liger_kernel.ops.layer_norm import LigerLayerNormFunction
class RMSNorm(nn.Module):
# mbedding_dim, eps=eps, elementwise_affine=elementwise_affine)
def __init__(self, dim, eps: float, elementwise_affine: bool = True):
super().__init__()
self.eps = eps
if isinstance(dim, numbers.Integral):
dim = (dim,)
self.dim = torch.Size(dim)
if elementwise_affine:
self.weight = nn.Parameter(torch.ones(dim))
else:
weight = torch.ones(dim)
self.register_buffer("weight", weight, persistent=False)
def forward(self, hidden_states):
return LigerRMSNormFunction.apply(
hidden_states,
self.weight,
self.eps,
0.0,
"gemma",
True,
)
class LayerNorm(nn.Module):
def __init__(self, dim, eps: float = 1e-5, elementwise_affine: bool = True, bias: bool = True):
super().__init__()
self.eps = eps
if isinstance(dim, numbers.Integral):
dim = (dim,)
self.dim = torch.Size(dim)
if elementwise_affine:
self.weight = nn.Parameter(torch.ones(dim))
self.bias = nn.Parameter(torch.zeros(dim)) if bias else None
else:
weight = torch.ones(dim)
bias = torch.zeros(dim)
self.register_buffer("weight", weight, persistent=False)
self.register_buffer("bias", bias, persistent=False)
def forward(self, hidden_states):
return LigerLayerNormFunction.apply(
hidden_states, self.weight, self.bias, self.eps
)
class MochiRMSNormZero(nn.Module):
r"""
Adaptive RMS Norm used in Mochi.
Parameters:
embedding_dim (`int`): The size of each embedding vector.
"""
def __init__(
self, embedding_dim: int, hidden_dim: int, eps: float = 1e-5, elementwise_affine: bool = False
) -> None:
super().__init__()
self.silu = nn.SiLU()
self.linear = nn.Linear(embedding_dim, hidden_dim)
# TODO: There is a bug in HF. Remove hardcode
self.norm = RMSNorm(hidden_dim//4, eps=eps, elementwise_affine=elementwise_affine)
def forward(
self, hidden_states: torch.Tensor, emb: torch.Tensor
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
emb = self.linear(self.silu(emb))
scale_msa, gate_msa, scale_mlp, gate_mlp = emb.chunk(4, dim=1)
hidden_states = self.norm(hidden_states) * (1 + scale_msa[:, None])
return hidden_states, gate_msa, scale_mlp, gate_mlp
class LuminaLayerNormContinuous(nn.Module):
def __init__(
self,
embedding_dim: int,
conditioning_embedding_dim: int,
# NOTE: It is a bit weird that the norm layer can be configured to have scale and shift parameters
# because the output is immediately scaled and shifted by the projected conditioning embeddings.
# Note that AdaLayerNorm does not let the norm layer have scale and shift parameters.
# However, this is how it was implemented in the original code, and it's rather likely you should
# set `elementwise_affine` to False.
elementwise_affine=True,
eps=1e-5,
bias=True,
norm_type="layer_norm",
out_dim: Optional[int] = None,
):
super().__init__()
# AdaLN
self.silu = nn.SiLU()
self.linear_1 = nn.Linear(conditioning_embedding_dim, embedding_dim, bias=bias)
if norm_type == "layer_norm":
self.norm = LayerNorm(embedding_dim, eps, elementwise_affine, bias)
elif norm_type == "rms_norm":
self.norm = RMSNorm(embedding_dim, eps=eps, elementwise_affine=elementwise_affine)
else:
raise ValueError(f"unknown norm_type {norm_type}")
self.linear_2 = None
if out_dim is not None:
self.linear_2 = nn.Linear(embedding_dim, out_dim, bias=bias)
def forward(
self,
x: torch.Tensor,
conditioning_embedding: torch.Tensor,
) -> torch.Tensor:
# convert back to the original dtype in case `conditioning_embedding`` is upcasted to float32 (needed for hunyuanDiT)
emb = self.linear_1(self.silu(conditioning_embedding).to(x.dtype))
scale = emb
x = self.norm(x) * (1 + scale)[:, None, :]
if self.linear_2 is not None:
x = self.linear_2(x)
return x
class AdaLayerNormContinuous(nn.Module):
def __init__(
self,
embedding_dim: int,
conditioning_embedding_dim: int,
# NOTE: It is a bit weird that the norm layer can be configured to have scale and shift parameters
# because the output is immediately scaled and shifted by the projected conditioning embeddings.
# Note that AdaLayerNorm does not let the norm layer have scale and shift parameters.
# However, this is how it was implemented in the original code, and it's rather likely you should
# set `elementwise_affine` to False.
elementwise_affine=True,
eps=1e-5,
bias=True,
norm_type="layer_norm",
):
super().__init__()
self.silu = nn.SiLU()
self.linear = nn.Linear(conditioning_embedding_dim, embedding_dim * 2, bias=bias)
if norm_type == "layer_norm":
self.norm = LayerNorm(embedding_dim, eps, elementwise_affine, bias)
elif norm_type == "rms_norm":
self.norm = RMSNorm(embedding_dim, eps, elementwise_affine)
else:
raise ValueError(f"unknown norm_type {norm_type}")
def forward(self, x: torch.Tensor, conditioning_embedding: torch.Tensor) -> torch.Tensor:
# convert back to the original dtype in case `conditioning_embedding`` is upcasted to float32 (needed for hunyuanDiT)
emb = self.linear(self.silu(conditioning_embedding).to(x.dtype))
scale, shift = torch.chunk(emb, 2, dim=1)
x = self.norm(x) * (1 + scale)[:, None, :] + shift[:, None, :]
return x
+1 -15
View File
@@ -17,19 +17,6 @@ import pdb
import copy
from typing import Dict
class ForkedPdb(pdb.Pdb):
"""A Pdb subclass that may be used
from a forked multiprocessing child
"""
def interaction(self, *args, **kwargs):
_stdin = sys.stdin
try:
sys.stdin = open('/dev/stdin')
pdb.Pdb.interaction(self, *args, **kwargs)
finally:
sys.stdin = _stdin
def initialize_distributed():
local_rank = int(os.getenv('RANK', 0))
world_size = int(os.getenv('WORLD_SIZE', 1))
@@ -121,7 +108,6 @@ def main(args):
if args.transformer_path is not None:
transformer = MochiTransformer3DModel.from_pretrained(args.transformer_path, torch_dtype=torch.bfloat16)
else:
transformer = MochiTransformer3DModel.from_pretrained(args.model_path, subfolder = 'transformer/', torch_dtype=torch.bfloat16)
if args.lora_checkpoint_dir is not None:
@@ -163,7 +149,7 @@ def main(args):
if prompts is not None:
for video, prompt in zip(videos, prompts):
suffix = prompt.split(".")[0]
export_to_video(video, args.output_path + f"_{suffix}.mp4", fps=30)
export_to_video(video, args.output_path + f"{suffix}.mp4", fps=30)
else:
export_to_video(videos[0], args.output_path + ".mp4", fps=30)
+56 -36
View File
@@ -9,7 +9,7 @@ from diffusers.training_utils import cast_training_params, compute_density_for_t
from fastvideo.utils.parallel_states import initialize_sequence_parallel_state, \
destroy_sequence_parallel_group, get_sequence_parallel_state, nccl_info
from fastvideo.utils.communications import sp_parallel_dataloader_wrapper, broadcast
from fastvideo.model.mochi_latents_stat import mochi_stat
from fastvideo.model.mochi_latents_utils import normalize_mochi_dit_input
from fastvideo.utils.validation import log_validation
import time
from torch.utils.data import DataLoader
@@ -24,7 +24,7 @@ from torch.utils.data.distributed import DistributedSampler
import wandb
from accelerate.utils import set_seed
from tqdm.auto import tqdm
from fastvideo.fsdp_util import get_fsdp_kwargs, apply_fsdp_checkpointing
from fastvideo.fsdp_util import get_dit_fsdp_kwargs, apply_fsdp_checkpointing
import diffusers
from diffusers import (
FlowMatchEulerDiscreteScheduler,
@@ -102,25 +102,22 @@ def get_sigmas(noise_scheduler, device, timesteps, n_dim=4, dtype=torch.float32)
return sigma
def train_one_step_mochi(transformer, optimizer, lr_scheduler, loader,noise_scheduler, gradient_accumulation_steps, sp_size, precondition_outputs, max_grad_norm):
def train_one_step_mochi(transformer, optimizer, lr_scheduler, loader,noise_scheduler, gradient_accumulation_steps, sp_size, precondition_outputs, max_grad_norm, weighting_scheme, logit_mean, logit_std, mode_scale):
total_loss = 0.0
optimizer.zero_grad()
for _ in range(gradient_accumulation_steps):
latents, encoder_hidden_states, latents_attention_mask, encoder_attention_mask = next(loader)
latents = latents.to(dtype=torch.bfloat16)
encoder_hidden_states = encoder_hidden_states.to(dtype=torch.bfloat16)
latents_mean = (
torch.tensor(mochi_stat.latents_mean).view(1, 12, 1, 1, 1).to(latents.device, latents.dtype)
)
latents_std = (
torch.tensor(mochi_stat.latents_std).view(1, 12, 1, 1, 1).to(latents.device, latents.dtype)
)
latents = (latents - latents_mean) / latents_std
latents = normalize_mochi_dit_input(latents)
batch_size = latents.shape[0]
noise = torch.randn_like(latents)
u = torch.rand(size=(batch_size,), device="cpu")
u = compute_density_for_timestep_sampling(
weighting_scheme=weighting_scheme,
batch_size=batch_size,
logit_mean=logit_mean,
logit_std=logit_std,
mode_scale=mode_scale,
)
indices = (u * noise_scheduler.config.num_train_timesteps).long()
timesteps = noise_scheduler.timesteps[indices].to(device=latents.device)
if sp_size > 1:
@@ -145,21 +142,19 @@ def train_one_step_mochi(transformer, optimizer, lr_scheduler, loader,noise_sche
else:
target = latents - noise
loss = torch.mean((model_pred.float() - target.float()) ** 2)
loss = torch.mean((model_pred.float() - target.float()) ** 2) / gradient_accumulation_steps
loss.backward()
avg_loss = loss.detach().clone()
dist.all_reduce(avg_loss, op=dist.ReduceOp.AVG)
total_loss += avg_loss.item() / gradient_accumulation_steps
total_loss += avg_loss.item()
transformer.clip_grad_norm_(max_grad_norm)
grad_norm = transformer.clip_grad_norm_(max_grad_norm)
optimizer.step()
lr_scheduler.step()
return total_loss
return total_loss, grad_norm.item()
def get_lora_model(transformer, lora_config):
transformer.requires_grad_(False)
@@ -286,8 +281,9 @@ def main(args):
transformer = get_lora_model(transformer, lora_config)
main_print(f" Total training parameters = {sum(p.numel() for p in transformer.parameters() if p.requires_grad) / 1e6} M")
main_print(f"--> Initializing FSDP with sharding strategy: full")
fsdp_kwargs = get_fsdp_kwargs("full", args.use_lora, args.use_cpu_offload)
main_print(f"--> Initializing FSDP with sharding strategy: {args.fsdp_sharding_startegy}")
fsdp_kwargs = get_dit_fsdp_kwargs(args.fsdp_sharding_startegy, args.use_lora, args.use_cpu_offload)
if args.use_lora:
transformer.config.lora_rank = args.lora_rank
@@ -304,7 +300,7 @@ def main(args):
main_print(f"--> model loaded")
if args.gradient_checkpointing:
apply_fsdp_checkpointing(transformer)
apply_fsdp_checkpointing(transformer, args.selective_checkpointing)
# Set model as trainable.
transformer.train()
@@ -312,8 +308,8 @@ def main(args):
noise_scheduler = FlowMatchEulerDiscreteScheduler()
params_to_optimize = transformer.parameters()
if args.use_lora:
params_to_optimize = list(filter(lambda p: p.requires_grad, params_to_optimize))
params_to_optimize = list(filter(lambda p: p.requires_grad, params_to_optimize))
optimizer = torch.optim.AdamW(
params_to_optimize,
lr=args.learning_rate,
@@ -352,13 +348,10 @@ def main(args):
drop_last=True,
)
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps * args.sp_size / args.train_sp_batch_size)
# Afterwards we recalculate our number of training epochs
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
# We need to initialize the trackers we use, and also store our configuration.
# The trackers initializes automatically on the main process.
if rank <= 0:
project = args.tracker_project_name or "fastvideo"
wandb.init(project=project, config=args)
@@ -402,7 +395,7 @@ def main(args):
next(loader)
for step in range(init_steps + 1, args.max_train_steps+1):
start_time = time.time()
loss = train_one_step_mochi(transformer, optimizer, lr_scheduler, loader, noise_scheduler, args.gradient_accumulation_steps, args.sp_size, args.precondition_outputs, args.max_grad_norm)
loss, grad_norm= train_one_step_mochi(transformer, optimizer, lr_scheduler, loader, noise_scheduler, args.gradient_accumulation_steps, args.sp_size, args.precondition_outputs, args.max_grad_norm, args.weighting_scheme, args.logit_mean, args.logit_std, args.mode_scale)
step_time = time.time() - start_time
step_times.append(step_time)
@@ -410,7 +403,8 @@ def main(args):
progress_bar.set_postfix({
"loss": f"{loss:.4f}",
"step_time": f"{step_time:.2f}s"
"step_time": f"{step_time:.2f}s",
"grad_norm": grad_norm
})
progress_bar.update(1)
if rank <= 0:
@@ -418,20 +412,25 @@ def main(args):
"train_loss": loss,
"learning_rate": lr_scheduler.get_last_lr()[0],
"step_time": step_time,
"avg_step_time": avg_step_time
"avg_step_time": avg_step_time,
"grad_norm": grad_norm
}, step=step)
if step % args.checkpointing_steps == 0:
save_checkpoint(transformer, rank, args.output_dir, step)
if args.use_lora:
# Save LoRA weights
save_lora_checkpoint(transformer, optimizer, rank, args.output_dir, step)
else:
# Your existing checkpoint saving code
save_checkpoint(transformer, rank, args.output_dir, step)
dist.barrier()
if args.log_validation and step % args.validation_steps == 0:
log_validation(args, transformer, device,
torch.bfloat16, step)
if args.use_lora:
save_lora_checkpoint(transformer, optimizer, rank, args.output_dir, args.max_train_steps)
else:
save_checkpoint(transformer, rank, args.output_dir, args.max_train_steps)
if get_sequence_parallel_state():
destroy_sequence_parallel_group()
@@ -506,6 +505,7 @@ if __name__ == "__main__":
parser.add_argument("--lr_warmup_steps", type=int, default=10, help="Number of steps for the warmup in the lr scheduler.")
parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.")
parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.")
parser.add_argument("--selective_checkpointing", type=float, default=1.0)
parser.add_argument("--allow_tf32", action="store_true",
help=(
"Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see"
@@ -527,8 +527,28 @@ if __name__ == "__main__":
parser.add_argument("--use_lora", action="store_true", default=False, help="Whether to use LoRA for finetuning.")
parser.add_argument("--lora_alpha", type=int, default=256, help="Alpha parameter for LoRA.")
parser.add_argument("--lora_rank", type=int, default=128, help="LoRA rank parameter. ")
parser.add_argument("--fsdp_sharding_startegy", default="full")
parser.add_argument(
"--weighting_scheme",
type=str,
default="uniform",
choices=["sigma_sqrt", "logit_normal", "mode", "cosmap", "uniform"],
)
parser.add_argument(
"--logit_mean", type=float, default=0.0, help="mean to use when using the `'logit_normal'` weighting scheme."
)
parser.add_argument(
"--logit_std", type=float, default=1.0, help="std to use when using the `'logit_normal'` weighting scheme."
)
parser.add_argument(
"--mode_scale",
type=float,
default=1.29,
help="Scale of mode weighting scheme. Only effective when using the `'mode'` as the `weighting_scheme`.",
)
# lr_scheduler
parser.add_argument("--lr_scheduler", type=str, default="cosine_with_restarts",
parser.add_argument("--lr_scheduler", type=str, default="constant",
help=(
'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",'
' "constant", "constant_with_warmup"]'
-867
View File
@@ -1,867 +0,0 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
# This source code is licensed under the license found in the
# LICENSE file in the root directory of this source tree.
"""
A minimal training script for DiT using PyTorch DDP.
"""
import argparse
import logging
import math
import os
import shutil
from pathlib import Path
import gc
import wandb
from einops import rearrange
from tqdm import tqdm
from fastvideo.utils.parallel_states import initialize_sequence_parallel_state, \
destroy_sequence_parallel_group, get_sequence_parallel_state, set_sequence_parallel_state
from fastvideo.utils.communications import prepare_parallel_data, broadcast
import time
from torch.utils.data import DataLoader
from copy import deepcopy
import accelerate
import torch
from torch.nn import functional as F
import transformers
from accelerate import Accelerator
from accelerate.logging import get_logger
from accelerate.utils import DistributedType, ProjectConfiguration, set_seed
from packaging import version
from tqdm.auto import tqdm
import diffusers
from diffusers import (
AutoencoderKL,
FlowMatchEulerDiscreteScheduler,
)
from fastvideo.model.modeling_mochi import MochiTransformer3DModel
from fastvideo.model.pipeline_mochi import MochiPipeline
from diffusers.optimization import get_scheduler
from diffusers.training_utils import compute_snr
from diffusers.utils import check_min_version, is_wandb_available
from fastvideo.utils.ema import EMAModel
from fastvideo.dataset import getdataset
from fastvideo.utils.dataset_utils import Collate, LengthGroupedSampler
# Will error if the minimal version of diffusers is not installed. Remove at your own risks.
check_min_version("0.24.0")
logger = get_logger(__name__)
@torch.inference_mode()
def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False):
logger.info(f"Running validation....\n")
model = accelerator.unwrap_model(model)
scheduler = FlowMatchEulerDiscreteScheduler()
mochi_pipeline = MochiPipeline(vae=vae,
text_encoder=text_encoder,
tokenizer=tokenizer,
scheduler=scheduler,
transformer=model).to(device=accelerator.device)
videos = []
for prompt in args.validaiton_prompt:
logger.info('Processing the ({}) prompt'.format(prompt))
video = mochi_pipeline(
args.prompt,
num_frames=args.num_frames,
height=args.max_height,
width=args.max_width,
num_inference_steps=args.validation_sampling_steps,
guidance_scale=args.guidance_scale,
).images
videos.append(video[0])
# import ipdb;ipdb.set_trace()
gc.collect()
torch.cuda.empty_cache()
videos = torch.stack(videos).numpy()
videos = rearrange(videos, 'b t h w c -> b t c h w')
for tracker in accelerator.trackers:
if videos.shape[1] == 1:
images = rearrange(videos, 'b 1 c h w -> (b 1) h w c')
logs = {
f"{'ema_' if ema else ''}validation": [
wandb.Image(image, caption=f"{i}: {prompt}")
for i, (image, prompt) in enumerate(zip(images, args.validaiton_prompt))
]
}
else:
logs = {
f"{'ema_' if ema else ''}validation": [
wandb.Video(video, caption=f"{i}: {prompt}", fps=24)
for i, (video, prompt) in enumerate(zip(videos, args.validaiton_prompt))
]
}
tracker.log(logs, step=global_step)
del mochi_pipeline
gc.collect()
torch.cuda.empty_cache()
class ProgressInfo:
def __init__(self, global_step, train_loss=0.0):
self.global_step = global_step
self.train_loss = train_loss
#################################################################################
# Training Loop #
#################################################################################
def main(args):
logging_dir = Path(args.output_dir, args.logging_dir)
# use LayerNorm, GeLu, SiLu always as fp32 mode
# TODO:
if args.enable_stable_fp32:
replace_with_fp32_forwards()
accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir)
accelerator = Accelerator(
gradient_accumulation_steps=args.gradient_accumulation_steps,
mixed_precision=args.mixed_precision,
log_with=args.report_to,
project_config=accelerator_project_config,
log_with="wandb"
)
if args.num_frames != 1 and args.use_image_num == 0:
initialize_sequence_parallel_state(args.sp_size)
if args.report_to == "wandb":
if not is_wandb_available():
raise ImportError("Make sure to install wandb if you want to use it for logging during training.")
# Make one log on every process with the configuration for debugging.
logging.basicConfig(
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
datefmt="%m/%d/%Y %H:%M:%S",
level=logging.INFO,
)
logger.info(accelerator.state, main_process_only=False)
if accelerator.is_local_main_process:
transformers.utils.logging.set_verbosity_warning()
diffusers.utils.logging.set_verbosity_info()
else:
transformers.utils.logging.set_verbosity_error()
diffusers.utils.logging.set_verbosity_error()
# If passed along, set the training seed now. On GPU...
if args.seed is not None:
set_seed(args.seed)
# Handle the repository creation
if accelerator.is_main_process:
if args.output_dir is not None:
os.makedirs(args.output_dir, exist_ok=True)
# For mixed precision training we cast all non-trainable weigths to half-precision
# as these weights are only used for inference, keeping weights in full precision is not required.
weight_dtype = torch.float32
if accelerator.mixed_precision == "fp16":
weight_dtype = torch.float16
elif accelerator.mixed_precision == "bf16":
weight_dtype = torch.bfloat16
# Create model:
kwargs = {}
ae = CausalVAEModelWrapper(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval()
if args.enable_tiling:
ae.vae.enable_tiling()
ae.vae.tile_sample_stride = args.tile_sample_stride
kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True}
text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval()
ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae]
ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w)
assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})"
args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w
args.ae_stride = args.ae_stride_h
patch_size = args.model[-3:]
patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2])
args.patch_size = patch_size_h
args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w
assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})"
# assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})."
assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})."
assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})."
args.stride_t = ae_stride_t * patch_size_t
args.stride = ae_stride_h * patch_size_h
latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w)
ae.latent_size = latent_size
if args.num_frames % 2 == 1:
args.latent_size_t = latent_size_t = (args.num_frames - 1) // ae_stride_t + 1
else:
latent_size_t = args.num_frames // ae_stride_t
model = Diffusion_models[args.model](
in_channels=ae_channel_config[args.ae],
out_channels=ae_channel_config[args.ae],
# caption_channels=4096,
# cross_attention_dim=1152,
attention_bias=True,
sample_size=latent_size,
sample_size_t=latent_size_t,
num_vector_embeds=None,
activation_fn="gelu-approximate",
num_embeds_ada_norm=1000,
use_linear_projection=False,
only_cross_attention=False,
double_self_attention=False,
upcast_attention=False,
# norm_type="ada_norm_single",
norm_elementwise_affine=False,
norm_eps=1e-6,
attention_type='default',
attention_mode=args.attention_mode,
interpolation_scale_h=args.interpolation_scale_h,
interpolation_scale_w=args.interpolation_scale_w,
interpolation_scale_t=args.interpolation_scale_t,
downsampler=args.downsampler,
use_rope=args.use_rope,
# text_max_length=args.text_max_length,
use_stable_fp32=args.enable_stable_fp32,
)
model.gradient_checkpointing = args.gradient_checkpointing
# # use pretrained model?
if args.pretrained:
model_state_dict = model.state_dict()
if 'safetensors' in args.pretrained: # pixart series
from safetensors.torch import load_file as safe_load
# import ipdb;ipdb.set_trace()
pretrained_checkpoint = safe_load(args.pretrained, device="cpu")
pretrained_keys = set(list(pretrained_checkpoint.keys()))
model_keys = set(list(model_state_dict.keys()))
common_keys = list(pretrained_keys & model_keys)
checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()}
# if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4:
# logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}")
# repeat = model.pos_embed.proj.weight.shape[2]
# checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat)
# del checkpoint['proj_out.weight'], checkpoint['proj_out.bias']
else: # latest stage training weight
checkpoint = torch.load(args.pretrained, map_location='cpu')
if 'model' in checkpoint:
checkpoint = checkpoint['model']
missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False)
logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}')
logger.info(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!')
# Freeze vae and text encoders.
ae.vae.requires_grad_(False)
text_enc.requires_grad_(False)
# Set model as trainable.
model.train()
noise_scheduler = DDPMScheduler()
# Move unet, vae and text_encoder to device and cast to weight_dtype
# The VAE is in float32 to avoid NaN losses.
ae.vae.to(accelerator.device, dtype=torch.float32)
# ae.vae.to(accelerator.device, dtype=weight_dtype)
text_enc.to(accelerator.device, dtype=weight_dtype)
# Create EMA for the unet.
if args.use_ema:
ema_model = deepcopy(model)
ema_model = EMAModel(ema_model.parameters(), decay=args.ema_decay, update_after_step=args.ema_start_step,
model_cls=Diffusion_models_class[args.model], model_config=ema_model.config)
# `accelerate` 0.16.0 will have better support for customized saving
if version.parse(accelerate.__version__) >= version.parse("0.16.0"):
# create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format
def save_model_hook(models, weights, output_dir):
if accelerator.is_main_process:
if args.use_ema:
ema_model.save_pretrained(os.path.join(output_dir, "model_ema"))
for i, model in enumerate(models):
model.save_pretrained(os.path.join(output_dir, "model"))
if weights: # Don't pop if empty
# make sure to pop weight so that corresponding model is not saved again
weights.pop()
def load_model_hook(models, input_dir):
if args.use_ema:
load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model])
ema_model.load_state_dict(load_model.state_dict())
ema_model.to(accelerator.device)
del load_model
for i in range(len(models)):
# pop models so that they are not loaded again
model = models.pop()
# load diffusers style into model
load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model")
model.register_to_config(**load_model.config)
model.load_state_dict(load_model.state_dict())
del load_model
accelerator.register_save_state_pre_hook(save_model_hook)
accelerator.register_load_state_pre_hook(load_model_hook)
# Enable TF32 for faster training on Ampere GPUs,
# cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices
if args.allow_tf32:
torch.backends.cuda.matmul.allow_tf32 = True
if args.scale_lr:
args.learning_rate = (
args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes
)
params_to_optimize = model.parameters()
# Optimizer creation
if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"):
logger.warning(
f"Unsupported choice of optimizer: {args.optimizer}.Supported optimizers include [adamW, prodigy]."
"Defaulting to adamW"
)
args.optimizer = "adamw"
if args.use_8bit_adam and not args.optimizer.lower() == "adamw":
logger.warning(
f"use_8bit_adam is ignored when optimizer is not set to 'AdamW'. Optimizer was "
f"set to {args.optimizer.lower()}"
)
if args.optimizer.lower() == "adamw":
if args.use_8bit_adam:
try:
import bitsandbytes as bnb
except ImportError:
raise ImportError(
"To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`."
)
optimizer_class = bnb.optim.AdamW8bit
else:
optimizer_class = torch.optim.AdamW
optimizer = optimizer_class(
params_to_optimize,
lr=args.learning_rate,
betas=(args.adam_beta1, args.adam_beta2),
weight_decay=args.adam_weight_decay,
eps=args.adam_epsilon,
)
if args.optimizer.lower() == "prodigy":
try:
import prodigyopt
except ImportError:
raise ImportError("To use Prodigy, please install the prodigyopt library: `pip install prodigyopt`")
optimizer_class = prodigyopt.Prodigy
if args.learning_rate <= 0.1:
logger.warning(
"Learning rate is too low. When using prodigy, it's generally better to set learning rate around 1.0"
)
optimizer = optimizer_class(
params_to_optimize,
lr=args.learning_rate,
betas=(args.adam_beta1, args.adam_beta2),
beta3=args.prodigy_beta3,
weight_decay=args.adam_weight_decay,
eps=args.adam_epsilon,
decouple=args.prodigy_decouple,
use_bias_correction=args.prodigy_use_bias_correction,
safeguard_warmup=args.prodigy_safeguard_warmup,
)
logger.info(f"optimizer: {optimizer}")
# Setup data:
train_dataset = getdataset(args)
sampler = LengthGroupedSampler(
args.train_batch_size,
world_size=accelerator.num_processes,
lengths=train_dataset.lengths,
group_frame=args.group_frame,
group_resolution=args.group_resolution,
) if (args.group_frame or args.group_resolution) else None
train_dataloader = DataLoader(
train_dataset,
shuffle=sampler is None,
# pin_memory=True,
collate_fn=Collate(args),
batch_size=args.train_batch_size,
num_workers=args.dataloader_num_workers,
sampler=sampler if (args.group_frame or args.group_resolution) else None,
drop_last=True,
# prefetch_factor=4
)
logger.info(f'after train_dataloader')
# Scheduler and math around the number of training steps.
overrode_max_train_steps = False
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
if args.max_train_steps is None:
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
overrode_max_train_steps = True
lr_scheduler = get_scheduler(
args.lr_scheduler,
optimizer=optimizer,
num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps,
num_training_steps=args.max_train_steps * args.gradient_accumulation_steps,
)
# Prepare everything with our `accelerator`.
# model.requires_grad_(False)
# model.pos_embed.requires_grad_(True)
logger.info(f'before accelerator.prepare')
model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare(
model, optimizer, train_dataloader, lr_scheduler
)
logger.info(f'after accelerator.prepare')
if args.use_ema:
ema_model.to(accelerator.device)
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
if overrode_max_train_steps:
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
# Afterwards we recalculate our number of training epochs
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
# We need to initialize the trackers we use, and also store our configuration.
# The trackers initializes automatically on the main process.
if accelerator.is_main_process:
accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args))
# Train!
total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps
total_batch_size = total_batch_size // args.sp_size * args.train_sp_batch_size
logger.info("***** Running training *****")
logger.info(f" Num examples = {len(train_dataset)}")
logger.info(f" Num Epochs = {args.num_train_epochs}")
logger.info(f" Instantaneous batch size per device = {args.train_batch_size}")
logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}")
logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}")
logger.info(f" Total optimization steps = {args.max_train_steps}")
logger.info(f" Total training parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B")
global_step = 0
first_epoch = 0
# Potentially load in the weights and states from a previous save
if args.resume_from_checkpoint:
if args.resume_from_checkpoint != "latest":
path = os.path.basename(args.resume_from_checkpoint)
else:
# Get the most recent checkpoint
dirs = os.listdir(args.output_dir)
dirs = [d for d in dirs if d.startswith("checkpoint")]
dirs = sorted(dirs, key=lambda x: int(x.split("-")[1]))
path = dirs[-1] if len(dirs) > 0 else None
if path is None:
accelerator.print(
f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run."
)
args.resume_from_checkpoint = None
initial_global_step = 0
else:
accelerator.print(f"Resuming from checkpoint {path}")
accelerator.load_state(os.path.join(args.output_dir, path))
global_step = int(path.split("-")[1])
initial_global_step = global_step
first_epoch = global_step // num_update_steps_per_epoch
else:
initial_global_step = 0
progress_bar = tqdm(
range(0, args.max_train_steps),
initial=initial_global_step,
desc="Steps",
# Only show the progress bar once on each machine.
disable=not accelerator.is_local_main_process,
)
progress_info = ProgressInfo(global_step, train_loss=0.0)
def sync_gradients_info(loss):
# Checks if the accelerator has performed an optimization step behind the scenes
if args.use_ema:
ema_model.step(model.parameters())
progress_bar.update(1)
progress_info.global_step += 1
end_time = time.time()
one_step_duration = end_time - start_time
accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step)
progress_info.train_loss = 0.0
# DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues.
if accelerator.distributed_type == DistributedType.DEEPSPEED or accelerator.is_main_process:
if progress_info.global_step % args.checkpointing_steps == 0:
# _before_ saving state, check if this save would set us over the `checkpoints_total_limit`
if accelerator.is_main_process and args.checkpoints_total_limit is not None:
checkpoints = os.listdir(args.output_dir)
checkpoints = [d for d in checkpoints if d.startswith("checkpoint")]
checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1]))
# before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints
if len(checkpoints) >= args.checkpoints_total_limit:
num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1
removing_checkpoints = checkpoints[0:num_to_remove]
logger.info(
f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints"
)
logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}")
for removing_checkpoint in removing_checkpoints:
removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint)
shutil.rmtree(removing_checkpoint)
save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}")
accelerator.save_state(save_path)
logger.info(f"Saved state to {save_path}")
logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]}
progress_bar.set_postfix(**logs)
def run(model_input, model_kwargs, prof):
global start_time
start_time = time.time()
noise = torch.randn_like(model_input)
if args.noise_offset:
# https://www.crosslabs.org//blog/diffusion-with-offset-noise
noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1, 1),
device=model_input.device)
bsz = model_input.shape[0]
current_step_frame = model_input.shape[2]
# Sample a random timestep for each image without bias.
timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device)
if current_step_frame != 1 and get_sequence_parallel_state(): # image do not need sp
broadcast(timesteps)
# Add noise to the model input according to the noise magnitude at each timestep
# (this is the forward diffusion process)
noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps)
model_pred = model(
noisy_model_input,
timesteps,
**model_kwargs
)[0]
# Get the target for loss depending on the prediction type
if args.prediction_type is not None:
# set prediction_type of scheduler if defined
noise_scheduler.register_to_config(prediction_type=args.prediction_type)
if noise_scheduler.config.prediction_type == "epsilon":
target = noise
elif noise_scheduler.config.prediction_type == "v_prediction":
target = noise_scheduler.get_velocity(model_input, noise, timesteps)
elif noise_scheduler.config.prediction_type == "sample":
# We set the target to latents here, but the model_pred will return the noise sample prediction.
target = model_input
# We will have to subtract the noise residual from the prediction to get the target sample.
model_pred = model_pred - noise
else:
raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}")
mask = model_kwargs.get('attention_mask', None)
if torch.all(mask.bool()):
mask = None
if get_sequence_parallel_state():
assert mask is None
b, c, _, _, _ = model_pred.shape
if mask is not None:
mask = mask.unsqueeze(1).repeat(1, c, 1, 1, 1).float() # b t h w -> b c t h w
mask = mask.reshape(b, -1)
if args.snr_gamma is None:
# model_pred: b c t h w, attention_mask: b t h w
loss = F.mse_loss(model_pred.float(), target.float(), reduction="none")
loss = loss.reshape(b, -1)
if mask is not None:
loss = (loss * mask).sum() / mask.sum() # mean loss on unpad patches
else:
loss = loss.mean()
else:
# Compute loss-weights as per Section 3.4 of https://arxiv.org/abs/2303.09556.
# Since we predict the noise instead of x_0, the original formulation is slightly changed.
# This is discussed in Section 4.2 of the same paper.
snr = compute_snr(noise_scheduler, timesteps)
mse_loss_weights = torch.stack([snr, args.snr_gamma * torch.ones_like(timesteps)], dim=1).min(
dim=1
)[0]
if noise_scheduler.config.prediction_type == "epsilon":
mse_loss_weights = mse_loss_weights / snr
elif noise_scheduler.config.prediction_type == "v_prediction":
mse_loss_weights = mse_loss_weights / (snr + 1)
loss = F.mse_loss(model_pred.float(), target.float(), reduction="none")
loss = loss.reshape(b, -1)
mse_loss_weights = mse_loss_weights.reshape(b, 1)
if mask is not None:
loss = (loss * mask * mse_loss_weights).sum() / mask.sum() # mean loss on unpad patches
else:
loss = (loss * mse_loss_weights).mean()
# Gather the losses across all processes for logging (if we use distributed training).
avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean()
progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps
# Backpropagate
accelerator.backward(loss)
if accelerator.sync_gradients:
params_to_clip = model.parameters()
accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm)
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
if accelerator.sync_gradients:
sync_gradients_info(loss)
if accelerator.is_main_process:
if progress_info.global_step % args.checkpointing_steps == 0:
if args.enable_tracker:
log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator,
weight_dtype, progress_info.global_step)
if args.use_ema:
# Store the UNet parameters temporarily and load the EMA parameters to perform inference.
ema_model.store(model.parameters())
ema_model.copy_to(model.parameters())
log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator,
weight_dtype, progress_info.global_step, ema=True)
# Switch back to the original UNet parameters.
ema_model.restore(model.parameters())
if prof is not None:
prof.step()
return loss
def train_one_step(step_, data_item_, prof_=None):
train_loss = 0.0
x, attn_mask, input_ids, cond_mask = data_item_
if args.group_frame or args.group_resolution:
if not args.group_frame:
each_latent_frame = torch.any(attn_mask.flatten(-2), dim=-1).int().sum(-1).tolist()
# logger.info(f'rank: {accelerator.process_index}, step {step_}, special batch has attention_mask '
# f'each_latent_frame: {each_latent_frame}')
print(f'rank: {accelerator.process_index}, step {step_}, special batch has attention_mask '
f'each_latent_frame: {each_latent_frame}')
assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))'
x = x.to(accelerator.device, dtype=ae.vae.dtype) # B C T+num_images H W, 16 + 4
attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W
input_ids = input_ids.to(accelerator.device) # B 1+num_images L
cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L
# if accelerator.process_index == 0:
# logger.info(f'rank: {accelerator.process_index}, x: {x.shape}, attn_mask: {attn_mask.shape}')
with torch.no_grad():
B, N, L = input_ids.shape # B 1+num_images L
input_ids_ = input_ids.reshape(-1, L)
cond_mask_ = cond_mask.reshape(-1, L)
cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D
cond = cond.reshape(B, N, L, -1)
x = ae.encode(x) # B C T H W
current_step_frame = x.shape[2]
current_step_sp_state = get_sequence_parallel_state()
if args.sp_size != 1: # enable sp
if current_step_frame == 1: # but image do not need sp
set_sequence_parallel_state(False)
else:
set_sequence_parallel_state(True)
if get_sequence_parallel_state():
x, cond, attn_mask, cond_mask, use_image_num = prepare_parallel_data(x, cond, attn_mask, cond_mask,
args.use_image_num)
for iter in range(args.train_batch_size * args.sp_size // args.train_sp_batch_size):
with accelerator.accumulate(model):
st_idx = iter * args.train_sp_batch_size
ed_idx = (iter + 1) * args.train_sp_batch_size
model_kwargs = dict(encoder_hidden_states=cond[st_idx: ed_idx],
attention_mask=attn_mask[st_idx: ed_idx],
encoder_attention_mask=cond_mask[st_idx: ed_idx], use_image_num=use_image_num)
run(x[st_idx: ed_idx], model_kwargs, prof_)
else:
with accelerator.accumulate(model):
assert not torch.any(torch.isnan(x)), 'after vae'
x = x.to(weight_dtype)
model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask,
encoder_attention_mask=cond_mask, use_image_num=args.use_image_num)
run(x, model_kwargs, prof_)
set_sequence_parallel_state(current_step_sp_state) # in case the next step use sp, which need broadcast(timesteps)
if progress_info.global_step >= args.max_train_steps:
return True
return False
def train_all_epoch(prof_=None):
for epoch in range(first_epoch, args.num_train_epochs):
progress_info.train_loss = 0.0
if progress_info.global_step >= args.max_train_steps:
return True
for step, data_item in enumerate(train_dataloader):
if train_one_step(step, data_item, prof_):
break
train_all_epoch()
accelerator.wait_for_everyone()
accelerator.end_training()
if get_sequence_parallel_state():
destroy_sequence_parallel_group()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
# dataset & dataloader
parser.add_argument("--dataset", type=str, required=True)
parser.add_argument("--data", type=str, required='')
parser.add_argument("--cache_vae_latent", action="store_true")
parser.add_argument("--train_fps", type=int, default=24)
parser.add_argument("--drop_short_ratio", type=float, default=1.0)
parser.add_argument("--speed_factor", type=float, default=1.0)
parser.add_argument("--num_frames", type=int, default=65)
parser.add_argument("--video_length_tolerance_range", type=int, default=2.0)
parser.add_argument("--max_height", type=int, default=320)
parser.add_argument("--max_width", type=int, default=240)
parser.add_argument("--use_image_num", type=int, default=0)
parser.add_argument("--text_max_length", type=int, default=512)
parser.add_argument('--cfg', type=float, default=0.1)
parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.")
parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.")
parser.add_argument("--group_frame", action="store_true")
parser.add_argument("--group_resolution", action="store_true")
# text encoder & vae & diffusion model
parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122")
parser.add_argument('--enable_8bit_t5', action='store_true')
parser.add_argument('--tile_sample_stride', type=float, default=0.125)
parser.add_argument('--enable_tiling', action='store_true')
parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers")
parser.add_argument('--use_rope', action='store_true')
parser.add_argument('--interpolation_scale_h', type=float, default=1.0)
parser.add_argument('--interpolation_scale_w', type=float, default=1.0)
parser.add_argument('--interpolation_scale_t', type=float, default=1.0)
parser.add_argument("--downsampler", type=str, default=None)
parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse")
parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse")
parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl')
parser.add_argument("--cache_dir", type=str, default='./cache_dir')
parser.add_argument("--pretrained", type=str, default=None)
parser.add_argument('--enable_stable_fp32', action='store_true')
parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.")
# diffusion setting
parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.")
parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.")
parser.add_argument("--ema_decay", type=float, default=0.999)
parser.add_argument("--ema_start_step", type=int, default=0)
parser.add_argument("--noise_offset", type=float, default=0.02, help="The scale of noise offset.")
parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.")
# validation & logs
parser.add_argument("--validaiton_prompt", nargs='+', help="List of prompts to use for validation.")
parser.add_argument("--validation_sampling_steps", type=int, default=64)
parser.add_argument('--guidance_scale', type=float, default=4.5)
parser.add_argument("--enable_tracker", action="store_true")
parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.")
parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.")
parser.add_argument("--checkpoints_total_limit", type=int, default=None, help=("Max number of checkpoints to store."))
parser.add_argument("--checkpointing_steps", type=int, default=500,
help=(
"Save a checkpoint of the training state every X updates. These checkpoints can be used both as final"
" checkpoints in case they are better than the last checkpoint, and are also suitable for resuming"
" training using `--resume_from_checkpoint`."
),
)
parser.add_argument("--resume_from_checkpoint", type=str, default=None,
help=(
"Whether training should be resumed from a previous checkpoint. Use a path saved by"
' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.'
),
)
parser.add_argument("--logging_dir", type=str, default="logs",
help=(
"[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to"
" *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***."
),
)
parser.add_argument("--report_to", type=str, default="tensorboard",
help=(
'The integration to report the results and logs to. Supported platforms are `"tensorboard"`'
' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.'
),
)
# optimizer & scheduler
parser.add_argument("--num_train_epochs", type=int, default=100)
parser.add_argument("--max_train_steps", type=int, default=None, help="Total number of training steps to perform. If provided, overrides num_train_epochs.")
parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Number of updates steps to accumulate before performing a backward/update pass.")
parser.add_argument("--optimizer", type=str, default="adamW", help='The optimizer type to use. Choose between ["AdamW", "prodigy"]')
parser.add_argument("--learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.")
parser.add_argument("--scale_lr", action="store_true", default=False, help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.")
parser.add_argument("--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler.")
parser.add_argument("--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes. Ignored if optimizer is not set to AdamW")
parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam and Prodigy optimizers.")
parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam and Prodigy optimizers.")
parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay")
parser.add_argument("--adam_weight_decay", type=float, default=1e-02, help="Weight decay to use for unet params")
parser.add_argument("--adam_weight_decay_text_encoder", type=float, default=None, help="Weight decay to use for text_encoder")
parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer and Prodigy optimizers.")
parser.add_argument("--prodigy_use_bias_correction", type=bool, default=True, help="Turn on Adam's bias correction. True by default. Ignored if optimizer is adamW")
parser.add_argument("--prodigy_safeguard_warmup", type=bool, default=True, help="Remove lr from the denominator of D estimate to avoid issues during warm-up stage. True by default. Ignored if optimizer is adamW")
parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.")
parser.add_argument("--prodigy_beta3", type=float, default=None,
help="coefficients for computing the Prodidy stepsize using running averages. If set to None, "
"uses the value of square root of beta2. Ignored if optimizer is adamW",
)
parser.add_argument("--lr_scheduler", type=str, default="constant",
help=(
'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",'
' "constant", "constant_with_warmup"]'
),
)
parser.add_argument("--allow_tf32", action="store_true",
help=(
"Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see"
" https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices"
),
)
parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"],
help=(
"Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >="
" 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the"
" flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config."
),
)
parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank")
parser.add_argument("--sp_size", type=int, default=1, help="For sequence parallel")
parser.add_argument("--train_sp_batch_size", type=int, default=1, help="Batch size for sequence parallel training")
args = parser.parse_args()
main(args)
+71 -8
View File
@@ -1,24 +1,87 @@
#!/bin/bash
num_gpus=2
prompts=(
"In this animated scene, Tom the cat is peeking out from behind a tall stack of books in a cozy library. His eyes are wide with curiosity as he spots Jerry, the tiny brown mouse, sitting on a book cover at the top of the pile. Jerry is holding a small, crumpled piece of cheese in his paws, looking down at Tom with a mischievous grin. The style of the animation brings warmth to the scene with rich, textured colors that highlight the humorous interaction between the characters."
"In this playful scene, Tom, the gray-blue cat, is dressed as a chef, complete with a white hat and apron. He’s standing over a steaming pot on a stove, stirring with a large spoon. Meanwhile, Jerry, the brown mouse, is perched on the edge of the counter, tossing in tiny sprigs of herbs. Their expressions suggest a lighthearted teamwork moment. The animation style emphasizes vibrant colors and exaggerated movements that enhance the lively interaction between the characters."
)
for prompt in "${prompts[@]}"; do
torchrun --nnodes=1 --nproc_per_node=$num_gpus --master_port 29503 \
fastvideo/sample/sample_t2v_mochi.py \
--model_path data/mochi \
--prompts "$prompt" \
--num_frames 79 \
--height 480 \
--width 848 \
--num_inference_steps 64 \
--guidance_scale 4.5 \
--output_path "outputs_video/T_J_original" \
--seed 12345
done
num_gpus=2
prompts=(
"In this animated scene, Tom the cat is peeking out from behind a tall stack of books in a cozy library. His eyes are wide with curiosity as he spots Jerry, the tiny brown mouse, sitting on a book cover at the top of the pile. Jerry is holding a small, crumpled piece of cheese in his paws, looking down at Tom with a mischievous grin. The style of the animation brings warmth to the scene with rich, textured colors that highlight the humorous interaction between the characters."
"In this playful scene, Tom, the gray-blue cat, is dressed as a chef, complete with a white hat and apron. He’s standing over a steaming pot on a stove, stirring with a large spoon. Meanwhile, Jerry, the brown mouse, is perched on the edge of the counter, tossing in tiny sprigs of herbs. Their expressions suggest a lighthearted teamwork moment. The animation style emphasizes vibrant colors and exaggerated movements that enhance the lively interaction between the characters."
)
for prompt in "${prompts[@]}"; do
torchrun --nnodes=1 --nproc_per_node=$num_gpus --master_port 29503 \
fastvideo/sample/sample_t2v_mochi.py \
--model_path data/mochi \
--prompts "$prompt" \
--num_frames 79 \
--height 480 \
--width 848 \
--num_inference_steps 64 \
--guidance_scale 4.5 \
--output_path "outputs_video/T_J_FT_normal_79_1000" \
--transformer_path data/outputs/T_J_FT/checkpoint-1000 \
--seed 12345
done
black myth: a character with a pig-like face, dressed in a green robe and holding a string of black beads. the character is riding a white horse outdoors, with a mountainous landscape visible in the background. The horse is a strong, muscular white steed with a slightly wild, untamed mane flowing in the wind, suggesting speed and vigor. Its eyes are alert, with a slight intensity that complements the adventurous aura of the character.
black myth: a character dressed in elaborate, ornate armor that is richly decorated with intricate designs and patterns. the armor appears to be of a high-quality material, possibly metal, and is adorned with various embellishments such as jewels and metallic accents. the character is riding a white horse outdoors, with a mountainous landscape visible in the background. the lighting suggests it might be either dawn or dusk, casting a soft glow on the scene. the character's pose is dynamic, suggesting movement or action.
num_gpus=4
torchrun --nnodes=1 --nproc_per_node=4 --master_port 29503 \
torchrun --nnodes=1 --nproc_per_node=$num_gpus --master_port 29503 \
fastvideo/sample/sample_t2v_mochi.py \
--model_path data/mochi \
--prompts "A cute fluffy panda eating Chinese food in a restaurant" \
--num_frames 163 \
--prompts "A hyper-realistic sci-fi scene featuring a brave monkey astronaut, floating in zero gravity aboard a futuristic spacecraft. The monkey wears a sleek, advanced spacesuit with glowing blue accents, gazing through the spacecraft window at a mesmerizing nebula and distant planets. The spacecraft interior is filled with detailed control panels and holographic displays, creating an immersive, high-tech atmosphere." \
--num_frames 91 \
--height 480 \
--width 848 \
--num_inference_steps 64 \
--guidance_scale 4.5 \
--output_path outputs_video/debug \
--output_path outputs_video/\
--seed 12345
num_gpus=4
torchrun --nnodes=1 --nproc_per_node=4 --master_port 29503 \
num_gpus=2
torchrun --nnodes=1 --nproc_per_node=$num_gpus --master_port 29503 \
fastvideo/sample/sample_t2v_mochi.py \
--model_path data/mochi/ \
--prompt_embed_path data/Encoder_Overfit_Data/prompt_embed/0.pt \
+3 -3
View File
@@ -13,15 +13,15 @@ classifiers = [
"License :: OSI Approved :: Apache Software License",
]
dependencies = [
"transformers==4.46.1", "accelerate==1.0.1", "tokenizers==0.20.1", "diffusers==0.31.0", "albumentations==1.4.20", "av==13.1.0",
"transformers==4.46.1", "accelerate==1.0.1", "tokenizers==0.20.1", "albumentations==1.4.20", "av==13.1.0",
"decord==0.6.0", "einops==0.8.0", "fastapi==0.115.3", "gdown==5.2.0", "h5py==3.12.1", "idna==3.6", "imageio==2.36.0",
"matplotlib==3.9.2", "numpy==1.26.3", "omegaconf==2.3.0", "opencv-python==4.10.0.84", "opencv-python-headless==4.10.0.84",
"pandas==2.2.3", "pillow==10.2.0", "pydub==0.25.1", "pytorch-lightning==2.4.0", "pytorchvideo==0.1.5", "PyYAML==6.0.1",
"regex==2024.9.11", "requests==2.31.0", "scikit-learn==1.5.2", "scipy==1.14.1", "six==1.16.0", "test-tube==0.7.5",
"timm==1.0.11", "torchdiffeq==0.2.4", "torchmetrics==1.5.1", "tqdm==4.66.5", "urllib3==2.2.0", "uvicorn==0.32.0",
"scikit-video==1.1.11", "imageio-ffmpeg==0.5.1", "sentencepiece==0.2.0", "beautifulsoup4==4.12.3", "ftfy==6.3.0",
"moviepy==1.0.3", "wandb==0.18.5", "tensorboard==2.18.0", "pydantic==2.9.2", "gradio==5.3.0", "huggingface_hub==0.26.1", "protobuf==5.28.3", "peft==0.13.2"
"watch", "gpustat"
"moviepy==1.0.3", "wandb==0.18.5", "tensorboard==2.18.0", "pydantic==2.9.2", "gradio==5.3.0", "huggingface_hub==0.26.1", "protobuf==5.28.3",
"watch", "gpustat", "peft==0.13.2", "liger_kernel==0.4.1"
]
[project.optional-dependencies]
+5 -1
View File
@@ -4,7 +4,7 @@ MODEL_PATH="/ephemeral/hao.zhang/outputfolder/ckptfolder/mochi_diffuser"
DATA_MERGE_PATH="/ephemeral/hao.zhang/resourcefolder/Mochi-Synthetic-Data-BW-Finetune/merge.txt"
OUTPUT_DIR="./data/BW-Finetune-Synthetic-Data_test"
torchrun --nproc_per_node=$GPU_NUM \
rchrun --nproc_per_node=$GPU_NUM \
./fastvideo/utils/data_preprocess/finetune_data_VAE.py \
--model_path $MODEL_PATH \
--data_merge_path $DATA_MERGE_PATH \
@@ -15,6 +15,10 @@ torchrun --nproc_per_node=$GPU_NUM \
--dataloader_num_workers 1 \
--output_dir=$OUTPUT_DIR
to
torchrun --nproc_per_node=$GPU_NUM \
./fastvideo/utils/data_preprocess/finetune_data_T5.py \
--model_path $MODEL_PATH \
+117
View File
@@ -0,0 +1,117 @@
torchrun --nnodes 4 --nproc_per_node 4 \
--node_rank=3 \
--rdzv_id=456 \
--rdzv_backend=c10d \
--rdzv_endpoint=172.23.30.15:29500 \
fastvideo/train.py \
--seed 42 \
--pretrained_model_name_or_path data/mochi \
--cache_dir "data/.cache" \
--data_json_path "data/BLACK-MYTH-Finetune-Dataset/videos2caption.json" \
--validation_prompt_dir "data/BLACK-MYTH-Finetune-Dataset/validation_prompt_embed_mask" \
--uncond_prompt_dir "data/BLACK-MYTH-Finetune-Dataset/uncond_prompt_embed_mask" \
--gradient_checkpointing \
--train_batch_size=1 \
--num_latent_t 16 \
--sp_size 4 \
--train_sp_batch_size 4 \
--dataloader_num_workers 4 \
--gradient_accumulation_steps=1 \
--max_train_steps=4000 \
--learning_rate=2e-5 \
--mixed_precision="bf16" \
--checkpointing_steps=500 \
--validation_steps=100 \
--validation_sampling_steps 64 \
--checkpoints_total_limit 3 \
--allow_tf32 \
--ema_start_step 0 \
--cfg 0.0 \
--ema_decay 0.999 \
--log_validation \
--output_dir="data/outputs/black_myth_correct_video_mask" \
--weighting_scheme "uniform" \
--num_frames 91 \
--selective_checkpointing 1.0
torchrun --nnodes 4 --nproc_per_node 4 \
--node_rank=3 \
--rdzv_id=456 \
--rdzv_backend=c10d \
--rdzv_endpoint=172.23.30.15:29500 \
fastvideo/train.py \
--seed 42 \
--pretrained_model_name_or_path data/mochi \
--cache_dir "data/.cache" \
--data_json_path "data/BLACK-MYTH-DREAM-BOOTH/videos2caption.json" \
--validation_prompt_dir "data/BLACK-MYTH-DREAM-BOOTH/validation_prompt_embed_mask" \
--uncond_prompt_dir "data/BLACK-MYTH-DREAM-BOOTH/uncond_prompt_embed_mask" \
--gradient_checkpointing \
--train_batch_size=1 \
--num_latent_t 16 \
--sp_size 4 \
--train_sp_batch_size 4 \
--dataloader_num_workers 4 \
--gradient_accumulation_steps=1 \
--max_train_steps=4000 \
--learning_rate=2e-5 \
--mixed_precision="bf16" \
--checkpointing_steps=100 \
--validation_steps=50 \
--validation_sampling_steps 64 \
--checkpoints_total_limit 3 \
--allow_tf32 \
--ema_start_step 0 \
--cfg 0.1 \
--ema_decay 0.999 \
--log_validation \
--output_dir="data/outputs/black_myth_correct_video_mask" \
--weighting_scheme "uniform" \
--num_frames 91 \
--selective_checkpointing 1.0
torchrun --nnodes 1 --nproc_per_node 4 \
fastvideo/train.py \
--seed 42 \
--pretrained_model_name_or_path data/mochi \
--cache_dir "data/.cache" \
--data_json_path "data/BLACK-MYTH-Finetune-Dataset/videos2caption.json" \
--validation_prompt_dir "data/BLACK-MYTH-Finetune-Dataset/validation_prompt_embed_mask" \
--uncond_prompt_dir "data/BLACK-MYTH-Finetune-Dataset/uncond_prompt_embed_mask" \
--gradient_checkpointing \
--train_batch_size=1 \
--num_latent_t 16 \
--sp_size 4 \
--train_sp_batch_size 4 \
--dataloader_num_workers 4 \
--gradient_accumulation_steps=1 \
--max_train_steps=2000 \
--learning_rate=2e-5 \
--mixed_precision="bf16" \
--checkpointing_steps=100 \
--validation_steps=100 \
--validation_sampling_steps 64 \
--checkpoints_total_limit 3 \
--allow_tf32 \
--ema_start_step 0 \
--cfg 0.0 \
--ema_decay 0.999 \
--log_validation \
--output_dir="data/outputs/black_myth_dream" \
--weighting_scheme "uniform" \
--num_frames 91 \
| 4/2000 [01:23<10:59:33, 19.83s/it, loss=0.74, grad_norm=2.6]
| 2/2000 [00:44<11:59:27, 21.61s/it, loss=0.873, grad_norm=1.57]
| 3/2000 [01:03<11:18:51, 20.40s/it, loss=0.813, grad_norm=1.48]
# FSDP Hybrid:
+13 -58
View File
@@ -1,3 +1,4 @@
export WANDB_MODE=online
torchrun --nnodes 1 --nproc_per_node 4 \
fastvideo/train.py \
--seed 42 \
@@ -5,72 +6,26 @@ torchrun --nnodes 1 --nproc_per_node 4 \
--cache_dir "data/.cache" \
--data_json_path "data/Encoder_Overfit_Data/videos2caption.json" \
--validation_prompt_dir "data/Encoder_Overfit_Data/validation_prompt_embed_mask" \
--uncond_prompt_dir "data/Encoder_Overfit_Data/uncond_prompt_embed_mask" \
--uncond_prompt_dir "data/Encoder_Overfit_Dataa/uncond_prompt_embed_mask" \
--gradient_checkpointing \
--train_batch_size=1 \
--num_latent_t 2 \
--sp_size 2 \
--train_sp_batch_size 1 \
--dataloader_num_workers 1 \
--gradient_accumulation_steps 4 \
--max_train_steps 200 \
--learning_rate 1e-4 \
--mixed_precision="bf16" \
--checkpointing_steps 6 \
--validation_steps 40 \
--validation_sampling_steps 2 \
--checkpoints_total_limit 3 \
--allow_tf32 \
--ema_start_step 0 \
--cfg 0.1 \
--ema_decay 0.999 \
--log_validation \
--output_dir="data/outputs/BW_Testrun_Test" \
--use_lora \
--resume_from_lora_checkpoint data/outputs/BW_Testrun/lora-checkpoint-5 \
torchrun --nnodes 1 --nproc_per_node 4 \
fastvideo/train.py \
--seed 42 \
--pretrained_model_name_or_path data/mochi \
--cache_dir data/.cache \
--data_json_path data/Encoder_Overfit_Data/videos2caption.json \
--validation_prompt_dir data/Encoder_Overfit_Data/validation_prompt_embed_mask \
--uncond_prompt_dir data/Encoder_Overfit_Data/uncond_prompt_embed_mask \
--gradient_checkpointing \
--train_batch_size 1 \
--num_latent_t 12 \
--num_latent_t 14 \
--sp_size 2 \
--train_sp_batch_size 2 \
--dataloader_num_workers 1 \
--gradient_accumulation_steps 1 \
--max_train_steps 400 \
--learning_rate 1e-4 \
--mixed_precision bf16 \
--checkpointing_steps 200 \
--validation_steps 50 \
--dataloader_num_workers 4 \
--gradient_accumulation_steps=2 \
--max_train_steps=2000 \
--learning_rate=1e-5 \
--mixed_precision="bf16" \
--checkpointing_steps=250 \
--validation_steps 250 \
--validation_sampling_steps 64 \
--checkpoints_total_limit 3 \
--allow_tf32 \
--ema_start_step 0 \
--cfg 0.1 \
--cfg 0.0 \
--ema_decay 0.999 \
--log_validation \
--output_dir data/outputs/BW_Testrun_Test/ \
--lora_rank 128 \
--lora_alpha 256 \
--use_lora \
--use_cpu_offload \
--resume_from_lora_checkpoint data/outputs/BW_Testrun_2/lora-checkpoint-100 \
# SP single node: 13
# SP multi node: 20
# SP multi node with larger batch size: 15
# No SP Single node: 10.5
# No SP Multe node: 11
--output_dir="data/outputs/T_J_FT" \
--weighting_scheme "uniform"