Add context forcing, ode_init to sf training

This commit is contained in:
JerryZhou54
2026-01-26 01:01:47 +00:00
parent 0806218156
commit 7d373cd2c4
7 changed files with 222 additions and 117 deletions
@@ -47,7 +47,7 @@ FAKE_SCORE_MODEL_PATH="hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-480p_t2
# DATA_DIR="data/test-text-preprocessing/Node_0_GPU_1_File_1/combined_parquet_dataset/"
# DATA_DIR="/mnt/weka/home/hao.zhang/matthew/FastVideo/data/test-text-preprocessing"
DATA_DIR="/mnt/weka/home/hao.zhang/wl/sharefs/wl/release/vidprom_16k_text_embed"
DATA_DIR_2="/mnt/weka/home/hao.zhang/wl/sharefs/wl/release/fv-ode-preprocessing-16k-hy15-distilled-121"
DATA_DIR_2="/mnt/weka/home/hao.zhang/wl/sharefs/wl/release/fv-ode-preprocessing-16k-hy15-121"
# DATA_DIR=data/crush-smol_processed_t2v/combined_parquet_dataset
# DATA_DIR="/mnt/sharefs/users/hao.zhang/Vchitect-2M/Wan-Syn-upload/latents_i2v/train/"
# VALIDATION_DATASET_FILE="data/crush-smol-single_processed_t2v/validation.json"
@@ -59,7 +59,7 @@ VALIDATION_DATASET_FILE="/mnt/weka/home/hao.zhang/wl/FastVideo/examples/distill/
training_args=(
--tracker_project_name SFhy1.5_t2v_distill_self_forcing_dmd # Updated for Wan2.2
--output_dir "/mnt/weka/home/hao.zhang/wei/SFhy1.5_distill_3333_1e-5_1e-5_cfg6_corrected_scheduler"
--wandb_run_name "self_forcing_3333_1e-5_1e-5_cfg3.5_corrected_scheduler"
--wandb_run_name "self_forcing_3333_context_forcing"
--max_train_steps 4000
--train_batch_size 1
--train_sp_batch_size 1
@@ -73,7 +73,8 @@ training_args=(
--num_frames 81
--num_frame_per_block 3 # Frame generation block size for self-forcing
# --resume-from-checkpoint "/mnt/weka/home/hao.zhang/wei/SFhy1.5_distill_1333/checkpoint-300"
--init_weights_from_safetensors /mnt/weka/home/hao.zhang/wei/hy15_distilled_ode_init_3333/checkpoint-2400/transformer/diffusion_pytorch_model.safetensors
--init_weights_from_safetensors /mnt/weka/home/hao.zhang/wei/hy15_worldplay_df_init_3333/checkpoint-2000/transformer/diffusion_pytorch_model.safetensors
# --init_weights_from_safetensors /mnt/weka/home/hao.zhang/wei/hy15_distilled_ode_init_3333/checkpoint-2400/transformer/diffusion_pytorch_model.safetensors
)
parallel_args=(
@@ -93,7 +94,7 @@ model_args=(
dataset_args=(
--data_path "$DATA_DIR"
--data_path_2 "$DATA_DIR_2"
# --data_path_2 "$DATA_DIR_2"
--dataloader_num_workers 4
)
@@ -108,7 +109,7 @@ validation_args=(
)
optimizer_args=(
--learning_rate 1e-6
--learning_rate 1e-5
--mixed_precision "bf16"
--training_state_checkpointing_steps 100
--weight_only_checkpointing_steps 500
@@ -122,7 +123,7 @@ miscellaneous_args=(
--checkpoints_total_limit 3
--training_cfg_rate 0.0
--dit_precision "fp32"
--flow_shift 7
--flow_shift 5
--seed 1000
--use_ema True
--ema_decay 0.99
@@ -133,11 +134,12 @@ dmd_args=(
--warp_denoising_step
--dmd_denoising_steps '1000,875,750,625,500,375,250,125'
# --dmd_denoising_steps '1000,760,520,280'
# --dmd_denoising_steps '1000,750,500,250'
--min_timestep_ratio 0.02
--max_timestep_ratio 0.98
--dfake_gen_update_ratio 5
--real_score_guidance_scale 3.5
--fake_score_learning_rate 2e-7
--fake_score_learning_rate 5e-6
--fake_score_betas '0.0,0.999'
)
@@ -146,6 +148,7 @@ self_forcing_args=(
--same_step_across_blocks True # Whether to use same denoising step across all blocks
--last_step_only False # Whether to only use the last denoising step
--context_noise 0 # Amount of noise to add during context caching (0 = no noise)
# --use-context-forcing True
)
srun torchrun \
+2 -2
View File
@@ -143,10 +143,10 @@ class Hunyuan15T2V720PConfig(Hunyuan15T2V480PConfig):
@dataclass
class SelfForcingHunyuan15T2V480PConfig(Hunyuan15T2V480PConfig):
flow_shift: int = 7
flow_shift: int = 5
is_causal: bool = True
# dmd_denoising_steps: list[int] | None = field(
# default_factory=lambda: [1000, 760, 520, 280])
# default_factory=lambda: [1000, 750, 500, 250])
dmd_denoising_steps: list[int] | None = field(
default_factory=lambda: [1000, 875, 750, 625, 500, 375, 250, 125])
warp_denoising_step: bool = True
+10
View File
@@ -945,6 +945,8 @@ class TrainingArgs(FastVideoArgs):
same_step_across_blocks: bool = False # Use same exit timestep for all blocks
last_step_only: bool = False # Only use the last timestep for training
context_noise: int = 0 # Context noise level for cache updates
use_context_forcing: bool = False
use_ode_init: bool = False
@classmethod
def from_cli_args(cls, args: argparse.Namespace) -> "TrainingArgs":
@@ -1374,6 +1376,14 @@ class TrainingArgs(FastVideoArgs):
type=int,
default=TrainingArgs.context_noise,
help="Context noise level for cache updates")
parser.add_argument(
"--use-context-forcing",
action=StoreBoolean,
help="Whether to use context forcing")
parser.add_argument(
"--use-ode-init",
action=StoreBoolean,
help="Whether to use ODE init")
return parser
+58 -49
View File
@@ -56,25 +56,6 @@ from fastvideo.models.dits.hunyuanvideo15 import (
logger = init_logger(__name__)
class CacheAppend(torch.autograd.Function):
"""
KV cache with shape [batch, seq_len, heads, head_dim].
"""
@staticmethod
def forward(ctx, storage, active_cache, x, start, end):
# Ensure storage has the same dtype as x
storage.data[:, start:end] = x
ctx.save_for_backward(storage.to(x.dtype))
ctx.start = start
ctx.end = end
return storage[:, :end].to(x.dtype) # Ensure returned value has same dtype as input
@staticmethod
def backward(ctx, grad_output):
start = ctx.start
end = ctx.end
return None, grad_output[:, :start], grad_output[:, start:end], None, None
class MMDoubleStreamBlock(nn.Module):
"""
A multimodal DiT block with separate modulation for text and image/video,
@@ -86,6 +67,8 @@ class MMDoubleStreamBlock(nn.Module):
hidden_size: int,
num_attention_heads: int,
mlp_ratio: float,
local_attn_size: int = -1,
sink_size: int = 0,
dtype: torch.dtype | None = None,
supported_attention_backends: tuple[AttentionBackendEnum, ...]
| None = None,
@@ -96,6 +79,8 @@ class MMDoubleStreamBlock(nn.Module):
self.deterministic = False
self.num_attention_heads = num_attention_heads
self.head_dim = hidden_size // num_attention_heads
self.local_attn_size = local_attn_size
self.sink_size = sink_size
mlp_hidden_dim = int(hidden_size * mlp_ratio)
# Image modulation components
@@ -179,9 +164,7 @@ class MMDoubleStreamBlock(nn.Module):
self.txt_mlp = MLP(hidden_size, mlp_hidden_dim, bias=True, dtype=dtype)
self.max_attention_size = 31 * 1590
self.k_cache = None
self.v_cache = None
self.max_attention_size = 21 * 1590 if local_attn_size == -1 else local_attn_size * 1590
self.attn = LocalAttention(
num_heads=self.num_attention_heads,
@@ -252,17 +235,6 @@ class MMDoubleStreamBlock(nn.Module):
txt_kv_cache: list | None = None,
current_start: int = 0,
) -> tuple[torch.Tensor, torch.Tensor]:
if kv_cache is not None:
# Then we are in autoregressive mode without block-wise attention mask
if self.k_cache is None:
del self.k_cache
self.register_buffer("k_cache", torch.empty(1, self.max_attention_size, self.num_attention_heads, self.head_dim, device=img.device, dtype=img.dtype), persistent=False)
self.k_cache.requires_grad_(True)
if self.v_cache is None:
del self.v_cache
self.register_buffer("v_cache", torch.empty(1, self.max_attention_size, self.num_attention_heads, self.head_dim, device=img.device, dtype=img.dtype), persistent=False)
self.v_cache.requires_grad_(True)
# Process modulation vectors
if vec.dim() == 3:
img_mod_outputs = self.img_mod(vec).unflatten(dim=-1, sizes=(6, -1))
@@ -342,25 +314,54 @@ class MMDoubleStreamBlock(nn.Module):
)[:, :, :-padded_length].transpose(2, 1)
assert img_attn.shape[1] == image_seq_len
updated_kv_cache = None
else:
current_end = current_start + img_q.shape[1]
num_new_tokens = img_q.shape[1]
assert kv_cache["local_end_index"].item() == kv_cache["global_end_index"].item(), "local_end_index and global_end_index must be the same"
# Assign new keys/values directly up to current_end
local_end_index = kv_cache["local_end_index"].item() + current_end - kv_cache["global_end_index"].item()
local_start_index = local_end_index - num_new_tokens
img_local_k = CacheAppend.apply(self.k_cache, kv_cache["k"], img_k, local_start_index, local_end_index)
img_local_v = CacheAppend.apply(self.v_cache, kv_cache["v"], img_v, local_start_index, local_end_index)
sink_tokens = self.sink_size * 1590
# If we are using local attention and the current KV cache size is larger than the local attention size, we need to truncate the KV cache
kv_cache_size = self.max_attention_size
# Clone cache to avoid in-place modification during gradient checkpointing
k_cache = kv_cache["k"].clone()
v_cache = kv_cache["v"].clone()
if self.local_attn_size != -1 and (current_end > kv_cache["global_end_index"].item()) and (
num_new_tokens + kv_cache["local_end_index"].item() > kv_cache_size):
# Calculate the number of new tokens added in this step
# Shift existing cache content left to discard oldest tokens
# Clone the source slice to avoid overlapping memory error
num_evicted_tokens = num_new_tokens + kv_cache["local_end_index"].item() - kv_cache_size
num_rolled_tokens = kv_cache_size - num_new_tokens - sink_tokens
k_cache[:, sink_tokens:sink_tokens + num_rolled_tokens] = \
k_cache[:, sink_tokens + num_evicted_tokens:sink_tokens + num_evicted_tokens + num_rolled_tokens].clone()
v_cache[:, sink_tokens:sink_tokens + num_rolled_tokens] = \
v_cache[:, sink_tokens + num_evicted_tokens:sink_tokens + num_evicted_tokens + num_rolled_tokens].clone()
# Insert the new keys/values at the end
local_end_index = kv_cache["local_end_index"].item() + current_end - \
kv_cache["global_end_index"].item() - num_evicted_tokens
local_start_index = local_end_index - num_new_tokens
assert local_end_index == self.max_attention_size
else:
# Assign new keys/values directly up to current_end
local_end_index = kv_cache["local_end_index"].item() + current_end - kv_cache["global_end_index"].item()
local_start_index = local_end_index - num_new_tokens
assert local_start_index >= 0
q = img_q
k = torch.cat([img_local_k, txt_kv_cache["k_txt"]], dim=1)
v = torch.cat([img_local_v, txt_kv_cache["v_txt"]], dim=1)
k = torch.cat([k_cache[:, :local_start_index], img_k, txt_kv_cache["k_txt"]], dim=1)
v = torch.cat([v_cache[:, :local_start_index], img_v, txt_kv_cache["v_txt"]], dim=1)
img_attn = self.attn(q, k, v)
kv_cache["k"] = img_local_k
kv_cache["v"] = img_local_v
kv_cache["global_end_index"].fill_(current_end)
kv_cache["local_end_index"].fill_(local_end_index)
k_cache[:, local_start_index:local_end_index] = img_k
v_cache[:, local_start_index:local_end_index] = img_v
updated_kv_cache = {
"k": k_cache,
"v": v_cache,
"global_end_index": torch.tensor([current_end], dtype=torch.long, device=k_cache.device),
"local_end_index": torch.tensor([local_end_index], dtype=torch.long, device=k_cache.device)
}
img_attn_out, _ = self.img_attn_proj(
img_attn.view(batch_size, image_seq_len, -1))
@@ -372,7 +373,7 @@ class MMDoubleStreamBlock(nn.Module):
img_mlp_out = self.img_mlp(img_mlp_input)
img = self.img_mlp_residual(img_residual, img_mlp_out, img_mlp_gate)
return img
return img, updated_kv_cache
def forward(
self,
@@ -444,6 +445,8 @@ class CausalHunyuanVideo15Transformer3DModel(CachableDiT):
hidden_size=self.hidden_size,
num_attention_heads=config.num_attention_heads,
mlp_ratio=config.mlp_ratio,
local_attn_size=config.local_attn_size,
sink_size=config.sink_size,
dtype=None,
supported_attention_backends=self._supported_attention_backends,
prefix=f"{config.prefix}.double_blocks.{i}"
@@ -751,7 +754,7 @@ class CausalHunyuanVideo15Transformer3DModel(CachableDiT):
# 4. Transformer blocks
if torch.is_grad_enabled() and self.gradient_checkpointing:
for block_index, block in enumerate(self.double_blocks):
hidden_states = self._gradient_checkpointing_func(
hidden_states, new_cache = self._gradient_checkpointing_func(
block,
txt_inference=False,
vision_inference=True,
@@ -763,10 +766,13 @@ class CausalHunyuanVideo15Transformer3DModel(CachableDiT):
txt_kv_cache=txt_kv_cache[block_index],
current_start=current_start
)
if new_cache is not None and kv_cache is not None:
for k in new_cache.keys():
kv_cache[block_index][k] = new_cache[k].clone()
else:
for block_index, block in enumerate(self.double_blocks):
hidden_states = block(
hidden_states, new_cache = block(
txt_inference=False,
vision_inference=True,
img=hidden_states,
@@ -777,13 +783,16 @@ class CausalHunyuanVideo15Transformer3DModel(CachableDiT):
txt_kv_cache=txt_kv_cache[block_index],
current_start=current_start
)
if new_cache is not None and kv_cache is not None:
for k in new_cache.keys():
kv_cache[block_index][k] = new_cache[k].clone()
# Final layer processing
hidden_states = self.final_layer(hidden_states, temb)
# Unpatchify to get original shape
hidden_states = unpatchify(hidden_states, post_patch_num_frames, post_patch_height, post_patch_width, self.patch_size, self.out_channels)
return hidden_states
return hidden_states, kv_cache
def forward(
self,
@@ -52,7 +52,7 @@ class CausalDMDDenosingStage(DenoisingStage):
self.sliding_window_num_frames = self.transformer.config.sliding_window_num_frames
try:
self.local_attn_size = getattr(self.transformer.model,
self.local_attn_size = getattr(self.transformer.config,
"local_attn_size",
-1) # type: ignore
except Exception:
@@ -364,7 +364,7 @@ class CausalDMDDenosingStage(DenoisingStage):
(latent_model_input.shape[0], current_num_frames),
device=latent_model_input.device,
dtype=torch.long)
pred_noise_btchw = current_model(
pred_noise_btchw, kv_cache1 = current_model(
hidden_states=latent_model_input,
timestep=t_expanded_noise,
kv_cache=_get_kv_cache(t_cur),
@@ -374,7 +374,8 @@ class CausalDMDDenosingStage(DenoisingStage):
self.frame_seq_length,
rope_start_idx=start_index,
**vision_input_kwargs,
).permute(0, 2, 1, 3, 4)
)
pred_noise_btchw = pred_noise_btchw.permute(0, 2, 1, 3, 4)
# Convert pred noise to pred video with FM Euler scheduler utilities
# if i < len(timesteps) - 1:
@@ -489,7 +490,7 @@ class CausalDMDDenosingStage(DenoisingStage):
**vision_input_kwargs,
)
self.transformer(
_, kv_cache1 = self.transformer(
hidden_states=context_bcthw,
timestep=t_context,
kv_cache=kv_cache1,
+9 -4
View File
@@ -147,7 +147,6 @@ class DistillationPipeline(TrainingPipeline):
self.vae.requires_grad_(False)
self.timestep_shift = self.training_args.pipeline_config.flow_shift
assert self.timestep_shift == 7.0, "flow_shift must be 7.0"
# self.noise_scheduler.set_timesteps(sigmas=list(np.linspace(1.0, 0.0, 50 + 1)[:-1]), device=get_local_torch_device())
self.noise_scheduler.set_timesteps(num_inference_steps=1000, extra_one_step=True, device=get_local_torch_device())
@@ -789,9 +788,15 @@ class DistillationPipeline(TrainingPipeline):
original_latent - real_score_pred_video).mean()
grad = torch.nan_to_num(grad)
dmd_loss = 0.5 * F.mse_loss(
original_latent.float(),
(original_latent.float() - grad.float()).detach())
if self.training_args.use_context_forcing and training_batch.trajectory_latents is not None:
context_forcing_length = training_batch.trajectory_latents.shape[1]
dmd_loss = 0.5 * F.mse_loss(
original_latent.float()[:, context_forcing_length:],
(original_latent.float()[:, context_forcing_length:] - grad.float()[:, context_forcing_length:]).detach())
else:
dmd_loss = 0.5 * F.mse_loss(
original_latent.float(),
(original_latent.float() - grad.float()).detach())
training_batch.dmd_latent_vis_dict.update({
"training_batch_dmd_fwd_clean_latent":
@@ -150,24 +150,54 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
return flow_matching_loss, log_dict
def _prepare_gt_inputs(self, training_batch: TrainingBatch) -> TrainingBatch:
def _prepare_context_forcing_inputs(self, training_batch: TrainingBatch) -> TrainingBatch:
trajectory_latents = training_batch.trajectory_latents
assert trajectory_latents.shape[1] == len(self.denoising_step_list) + 1
# start_timestep_index = torch.randint(0, len(self.denoising_step_list), (1, ), device=self.device)
# if dist.is_initialized():
# dist.broadcast(start_timestep_index, src=0)
# start_timestep_index = start_timestep_index.cpu().item()
training_batch.trajectory_latents = trajectory_latents[:, :, :21]
# _cached_closest_idx_per_dmd = torch.tensor(
# [0, 12, 24, 36, 50], dtype=torch.long).cpu()
# training_batch.trajectory_latents = torch.index_select(
# trajectory_latents,
# dim=1,
# index=_cached_closest_idx_per_dmd.to(trajectory_latents.device))
total_num_chunks = self.training_args.num_latent_t // self.num_frame_per_block
# Gradually increase maximum number of chunks
# Increase max_num_chunks by 1 every 100 training steps until it reaches total_num_chunks
max_num_chunks = min(total_num_chunks, self.current_trainstep // 100 + 2)
context_forcing_length = torch.randint(0, max_num_chunks, (1, ), device=get_local_torch_device()) * self.num_frame_per_block
# broadcast context_forcing_length to all ranks
if dist.is_initialized():
dist.broadcast(context_forcing_length, src=0)
context_forcing_length = context_forcing_length.cpu().item()
assert context_forcing_length < self.training_args.num_latent_t, "Context forcing length is greater than the number of latent frames"
if context_forcing_length > 0:
training_batch.trajectory_latents = trajectory_latents[:, -1, :context_forcing_length]
else:
training_batch.trajectory_latents = None
del trajectory_latents
# training_batch.start_timestep_index = start_timestep_index
return training_batch
def _prepare_ode_init_inputs(self, training_batch: TrainingBatch) -> TrainingBatch:
trajectory_latents = training_batch.trajectory_latents
trajectory_latents = trajectory_latents[:, :, :21]
_cached_closest_idx_per_dmd = torch.tensor(
[0, 12, 24, 36, 50], dtype=torch.long).cpu()
training_batch.trajectory_latents = torch.index_select(
trajectory_latents,
dim=1,
index=_cached_closest_idx_per_dmd.to(trajectory_latents.device))
del trajectory_latents
return training_batch
def _prepare_gt_inputs(self, training_batch: TrainingBatch) -> TrainingBatch:
trajectory_latents = training_batch.trajectory_latents
start_timestep_index = torch.randint(1, len(self.denoising_step_list), (1, ), device=self.device)
if dist.is_initialized():
dist.broadcast(start_timestep_index, src=0)
start_timestep_index = start_timestep_index.cpu().item()
trajectory_latents = trajectory_latents[:, -1, :21]
training_batch.trajectory_latents = self.noise_scheduler.add_noise(
trajectory_latents.flatten(0, 1),
torch.randn_like(trajectory_latents.flatten(0, 1)),
self.denoising_step_list[start_timestep_index] * torch.ones([trajectory_latents.shape[0] * trajectory_latents.shape[1]], device=self.device, dtype=torch.long)).unflatten(0, (trajectory_latents.shape[0], trajectory_latents.shape[1]))
del trajectory_latents
training_batch.start_timestep_index = start_timestep_index
return training_batch
def _generator_multi_step_simulation_forward(
@@ -219,10 +249,10 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
batch_size, num_generated_frames, *self.video_latent_shape[2:]
]
# if training_batch.use_gt_trajectory:
# noise = training_batch.trajectory_latents.to(self.device, dtype=dtype)
# else:
noise = torch.randn(noise_shape, device=self.device, dtype=dtype)
if training_batch.use_gt_trajectory:
noise = training_batch.trajectory_latents.to(self.device, dtype=dtype)
else:
noise = torch.randn(noise_shape, device=self.device, dtype=dtype)
if self.sp_world_size > 1:
noise = rearrange(noise,
"b (n t) c h w -> b n t c h w",
@@ -292,16 +322,16 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
# Step 3: Temporal denoising loop
all_num_frames = [self.num_frame_per_block] * num_blocks
all_num_frames[0] = 1
# all_num_frames[0] = 1
if self.independent_first_frame and initial_latent is None:
all_num_frames = [1] + all_num_frames
# if training_batch.use_gt_trajectory:
# start_timestep_index = training_batch.start_timestep_index
# end_timestep_index = len(self.denoising_step_list)
# else:
start_timestep_index = 0
end_timestep_index = len(self.denoising_step_list)
if training_batch.use_gt_trajectory:
start_timestep_index = training_batch.start_timestep_index
end_timestep_index = len(self.denoising_step_list)
else:
start_timestep_index = 0
end_timestep_index = len(self.denoising_step_list)
exit_flags = self.generate_and_sync_list(len(all_num_frames),
start_timestep_index,
end_timestep_index,
@@ -321,6 +351,45 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
cache_txt=True,
)
# Context forcing logic
if self.training_args.use_context_forcing and training_batch.trajectory_latents is not None:
gt_latents = training_batch.trajectory_latents
context_forcing_length = gt_latents.shape[1]
output[:, :context_forcing_length] = gt_latents.detach()
num_residual_blocks = (self.training_args.num_latent_t - context_forcing_length) // self.num_frame_per_block
assert num_residual_blocks > 0, "Number of residual blocks is less than 0"
all_num_frames = [self.num_frame_per_block] * num_residual_blocks
if training_batch.video_latent is not None:
gt_latents = torch.cat([
gt_latents,
training_batch.video_latent[:, current_start_frame:current_start_frame + context_forcing_length],
torch.zeros_like(gt_latents),
], dim=2)
with torch.no_grad():
training_batch_temp = self._build_distill_input_kwargs(
gt_latents, torch.zeros([batch_size, context_forcing_length],
device=noise.device,
dtype=torch.int64),
training_batch.conditional_dict, training_batch)
self.transformer(
txt_inference=False,
vision_inference=True,
hidden_states=training_batch_temp.
input_kwargs['hidden_states'],
timestep=training_batch_temp.
input_kwargs['timestep'],
kv_cache=kv_cache1,
txt_kv_cache=txt_kv_cache,
current_start=current_start_frame *
self.frame_seq_length,
rope_start_idx=current_start_frame
)
current_start_frame += context_forcing_length
for block_index, current_num_frames in enumerate(all_num_frames):
noisy_input = noise[:, current_start_frame -
num_input_frames:current_start_frame +
@@ -328,6 +397,8 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
index = start_timestep_index
current_timestep = self.denoising_step_list[index]
assert index < len(self.denoising_step_list), "Index is greater than the number of denoising steps"
# Step 3.1: Spatial denoising loop
while index < len(self.denoising_step_list):
noisy_input_latent = noisy_input.clone()
@@ -359,7 +430,7 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
noisy_input_latent, timestep,
training_batch.conditional_dict, training_batch)
pred_flow = current_model(
pred_flow, kv_cache1 = current_model(
txt_inference=False,
vision_inference=True,
hidden_states=training_batch_temp.
@@ -377,8 +448,8 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
# crossattn_cache=crossattn_cache,
current_start=current_start_frame *
self.frame_seq_length,
rope_start_idx=current_start_frame).permute(
0, 2, 1, 3, 4)
rope_start_idx=current_start_frame)
pred_flow = pred_flow.permute(0, 2, 1, 3, 4)
denoised_pred = pred_noise_to_pred_video(
pred_noise=pred_flow.flatten(0, 1),
@@ -411,7 +482,7 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
noisy_input_latent, timestep,
training_batch.conditional_dict, training_batch)
pred_flow = current_model(
pred_flow, kv_cache1 = current_model(
txt_inference=False,
vision_inference=True,
hidden_states=training_batch_temp.
@@ -429,14 +500,14 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
# crossattn_cache=crossattn_cache,
current_start=current_start_frame *
self.frame_seq_length,
rope_start_idx=current_start_frame).permute(
0, 2, 1, 3, 4)
rope_start_idx=current_start_frame)
pred_flow = pred_flow.permute(0, 2, 1, 3, 4)
else:
training_batch_temp = self._build_distill_input_kwargs(
noisy_input_latent, timestep,
training_batch.conditional_dict, training_batch)
pred_flow = current_model(
pred_flow, kv_cache1 = current_model(
txt_inference=False,
vision_inference=True,
hidden_states=training_batch_temp.
@@ -454,8 +525,8 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
# crossattn_cache=crossattn_cache,
current_start=current_start_frame *
self.frame_seq_length,
rope_start_idx=current_start_frame).permute(
0, 2, 1, 3, 4)
rope_start_idx=current_start_frame)
pred_flow = pred_flow.permute(0, 2, 1, 3, 4)
denoised_pred = pred_noise_to_pred_video(
pred_noise=pred_flow.flatten(0, 1),
@@ -493,7 +564,7 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
# context_timestep is 0 so we use transformer_2
current_model = self.transformer_2 if self.transformer_2 is not None else self.transformer
current_model(
_, kv_cache1 = current_model(
txt_inference=False,
vision_inference=True,
hidden_states=training_batch_temp.
@@ -640,7 +711,8 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
if max_num_frames is None:
max_num_frames = num_frames
num_max_frames = max(max_num_frames, num_frames)
kv_cache_size = num_max_frames * frame_seq_length
local_attn_size = getattr(self.transformer.config, 'local_attn_size', -1)
kv_cache_size = num_max_frames * frame_seq_length if local_attn_size == -1 else local_attn_size * frame_seq_length
kv_cache = []
for _ in range(num_transformer_blocks):
@@ -959,25 +1031,30 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
train_generator = (self.current_trainstep %
self.dfake_gen_update_ratio == 0)
if getattr(self, "train_dataloader_2", None) is not None and train_generator:
# use_gt_trajectory = torch.rand(1, device=get_local_torch_device())
# if dist.is_initialized():
# dist.broadcast(use_gt_trajectory, src=0)
# training_batch.use_gt_trajectory = (use_gt_trajectory < 0.8).item()
training_batch.use_gt_trajectory = True
if getattr(self, "train_dataloader_2", None) is not None and not self.training_args.use_ode_init and not self.training_args.use_context_forcing:
use_gt_trajectory = torch.rand(1, device=get_local_torch_device())
if dist.is_initialized():
dist.broadcast(use_gt_trajectory, src=0)
training_batch.use_gt_trajectory = (use_gt_trajectory < 0.5).item()
# training_batch.use_gt_trajectory = True
else:
training_batch.use_gt_trajectory = False
batches = []
for _ in range(gradient_accumulation_steps):
# if training_batch.use_gt_trajectory:
# batch = self._get_next_batch_2(training_batch)
# else:
batch = self._get_next_batch(training_batch)
if training_batch.use_gt_trajectory:
batch = self._get_next_batch_2(training_batch)
else:
if self.training_args.use_context_forcing:
batch = self._get_next_batch_2(training_batch)
else:
batch = self._get_next_batch(training_batch)
batch = self._normalize_dit_input(batch)
batch = self._prepare_dit_inputs(batch)
# if training_batch.use_gt_trajectory:
# batch = self._prepare_gt_inputs(batch)
if self.training_args.use_context_forcing:
batch = self._prepare_context_forcing_inputs(batch)
if training_batch.use_gt_trajectory:
batch = self._prepare_gt_inputs(batch)
batch = self._build_attention_metadata(batch)
batch.attn_metadata_vsa = copy.deepcopy(batch.attn_metadata)
if batch.attn_metadata is not None:
@@ -1014,10 +1091,10 @@ class SelfForcingDistillationPipeline(DistillationPipeline):
setattr(batch_gen, key, copy.deepcopy(value))
generator_loss, gen_log_dict = self.generator_loss(batch_gen)
if training_batch.use_gt_trajectory:
if self.training_args.use_ode_init:
batch_gt = self._get_next_batch_2(training_batch)
batch_gt = self._prepare_dit_inputs(batch_gt)
batch_gt = self._prepare_gt_inputs(batch_gt)
batch_gt = self._prepare_ode_init_inputs(batch_gt)
batch_gt = self._build_attention_metadata(batch_gt)
batch_gt.attn_metadata_vsa = copy.deepcopy(batch_gt.attn_metadata)
batch_gt_gen = TrainingBatch()