| from models import ( |
| get_diffusion_wrapper, |
| get_text_encoder_wrapper, |
| get_vae_wrapper |
| ) |
| from models.wan.taehv_wrapper import TAEHVWanVAEWrapper |
| from typing import List |
| import torch |
| import torch.distributed as dist |
| import logging |
|
|
| LOGGER = logging.getLogger(__name__) |
|
|
| class CausalStreamInferencePipeline(torch.nn.Module): |
| def __init__(self, args, device): |
| super().__init__() |
| model_type = args.model_type |
| self.device = device |
| |
| self.generator_model_name = getattr( |
| args, "generator_name", args.model_name) |
| self.generator = get_diffusion_wrapper( |
| model_name=self.generator_model_name)(model_type=model_type) |
| self.text_encoder = get_text_encoder_wrapper( |
| model_name=args.model_name)(model_type=model_type) |
| if getattr(args, "use_taehv", False): |
| LOGGER.info("Using TAEHV VAE wrapper for Wan inference") |
| self.vae = TAEHVWanVAEWrapper( |
| model_type=model_type, |
| checkpoint_path=getattr(args, "taehv_checkpoint_path", None), |
| use_tensorrt=getattr(args, "use_tensorrt", False), |
| ) |
| else: |
| self.vae = get_vae_wrapper(model_name=args.model_name)(model_type=model_type) |
|
|
| |
| self._init_denoising_step_list(args, device) |
|
|
| if model_type == "T2V-1.3B": |
| self.num_transformer_blocks = 30 |
| self.num_heads = 12 |
| elif model_type == "T2V-14B": |
| self.num_transformer_blocks = 40 |
| self.num_heads = 40 |
| else: |
| raise ValueError(f"Model type {model_type} not supported") |
| scale_size = 16 |
| self.height = args.height//scale_size*2 |
| self.width = args.width//scale_size*2 |
| self.frame_seq_length = (args.height//scale_size) * (args.width//scale_size) |
| self.num_kv_cache = args.num_kv_cache |
| self.kv_cache_length = self.frame_seq_length*self.num_kv_cache |
| self.num_sink_tokens = args.num_sink_tokens |
| self.adapt_sink_threshold = args.adapt_sink_threshold |
|
|
| self.conditional_dict = None |
| self.kv_cache1 = None |
| self.kv_cache2 = None |
| self.hidden_states = None |
| self.block_x = None |
| self.args = args |
| self.num_frame_per_block = getattr( |
| args, "num_frame_per_block", 1) |
|
|
| LOGGER.info("KV inference with %s frames per block", self.num_frame_per_block) |
|
|
| if self.num_frame_per_block > 1: |
| self.generator.model.num_frame_per_block = self.num_frame_per_block |
|
|
| self.generator.model.to(self.device) |
|
|
| def _init_denoising_step_list(self, args, device): |
| self.denoising_step_list = torch.tensor( |
| args.denoising_step_list, dtype=torch.long, device=device) |
| assert self.denoising_step_list[-1] == 0 |
| if not args.t2v: |
| |
| self.denoising_step_list = self.denoising_step_list[:-1] |
|
|
| self.scheduler = self.generator.get_scheduler() |
| if args.warp_denoising_step: |
| timesteps = torch.cat( |
| (self.scheduler.timesteps.cpu(), torch.tensor([0], dtype=torch.float32)) |
| ).to(device) |
| self.denoising_step_list = timesteps[1000 - self.denoising_step_list] |
|
|
| def _initialize_kv_cache(self, batch_size, dtype, device): |
| """ |
| Initialize a Per-GPU KV cache for the Wan model. |
| """ |
| kv_cache1 = [] |
| |
| for i in range(self.num_transformer_blocks): |
| cache_length = self.kv_cache_length |
| self.generator.model.blocks[i].self_attn.sink_size = self.num_sink_tokens |
| self.generator.model.blocks[i].self_attn.adapt_sink_thr = self.adapt_sink_threshold |
|
|
| kv_cache1.append({ |
| "k": torch.zeros([batch_size, cache_length, self.num_heads, 128], dtype=dtype, device=device), |
| "v": torch.zeros([batch_size, cache_length, self.num_heads, 128], dtype=dtype, device=device), |
| "global_end_index": torch.tensor([0], dtype=torch.long, device=device), |
| "local_end_index": torch.tensor([0], dtype=torch.long, device=device), |
| "total_steps": len(self.denoising_step_list), |
| "current_step": len(self.denoising_step_list), |
| }) |
|
|
| self.kv_cache1 = kv_cache1 |
|
|
| def _initialize_crossattn_cache(self, batch_size, dtype, device): |
| """ |
| Initialize a Per-GPU cross-attention cache for the Wan model. |
| """ |
| crossattn_cache = [] |
|
|
| for _ in range(self.num_transformer_blocks): |
| crossattn_cache.append({ |
| "k": torch.zeros([batch_size, 512, self.num_heads, 128], dtype=dtype, device=device), |
| "v": torch.zeros([batch_size, 512, self.num_heads, 128], dtype=dtype, device=device), |
| "is_init": False, |
| }) |
|
|
| self.crossattn_cache = crossattn_cache |
| |
| def prepare( |
| self, |
| text_prompts: List[str], |
| device: torch.device, |
| dtype: torch.dtype, |
| block_mode: str='input', |
| noise: torch.Tensor = None, |
| current_start: int = 0, |
| current_end: int = None, |
| block_num: torch.Tensor = None, |
| batch_denoise: bool=True, |
| ): |
| self.device = device |
| batch_size = noise.shape[0] |
|
|
| self.conditional_dict = self.text_encoder( |
| text_prompts=text_prompts |
| ) |
|
|
| |
| if self.kv_cache1 is None: |
| self._initialize_kv_cache( |
| batch_size=batch_size, |
| dtype=dtype, |
| device=device |
| ) |
|
|
| self._initialize_crossattn_cache( |
| batch_size=batch_size, |
| dtype=dtype, |
| device=device |
| ) |
| else: |
| |
| for block_index in range(self.num_transformer_blocks): |
| self.crossattn_cache[block_index]["is_init"] = False |
| |
| current_start = torch.tensor([current_start], dtype=torch.long, device=device) |
| current_end = torch.tensor([current_end], dtype=torch.long, device=device) |
|
|
| for index, current_timestep in enumerate(self.denoising_step_list): |
| |
| timestep = torch.ones( |
| [batch_size, noise.shape[1]], device=noise.device, dtype=torch.int64) * current_timestep |
|
|
| if index < len(self.denoising_step_list) - 1: |
| denoised_pred = self.generator( |
| noisy_image_or_video=noise, |
| conditional_dict=self.conditional_dict, |
| timestep=timestep, |
| kv_cache=self.kv_cache1, |
| crossattn_cache=self.crossattn_cache, |
| current_start=current_start, |
| current_end=current_end |
| ) |
| next_timestep = self.denoising_step_list[index + 1] |
| noise = self.scheduler.add_noise( |
| denoised_pred.flatten(0, 1), |
| torch.randn_like(denoised_pred.flatten(0, 1)), |
| next_timestep * |
| torch.ones([batch_size], device=noise.device, |
| dtype=torch.long) |
| ).unflatten(0, denoised_pred.shape[:2]) |
| else: |
| |
| denoised_pred = self.generator( |
| noisy_image_or_video=noise, |
| conditional_dict=self.conditional_dict, |
| timestep=timestep, |
| kv_cache=self.kv_cache1, |
| crossattn_cache=self.crossattn_cache, |
| current_start=current_start, |
| current_end=current_end |
| ) |
|
|
| if not batch_denoise: |
| return denoised_pred |
|
|
| |
| self.batch_size = len(self.denoising_step_list) |
|
|
| |
| blocks_to_keep = [] |
| if block_num is not None: |
| start_block, end_block = block_num[0].item(), block_num[1].item() |
| blocks_to_keep = list(range(start_block, end_block)) |
| else: |
| blocks_to_keep = list(range(self.num_transformer_blocks)) |
|
|
| |
| for i in range(self.num_transformer_blocks): |
| if dist.is_initialized(): |
| dist.broadcast(self.crossattn_cache[i]['k'], src=0) |
| dist.broadcast(self.crossattn_cache[i]['v'], src=0) |
| dist.broadcast(self.kv_cache1[i]['k'], src=0) |
| dist.broadcast(self.kv_cache1[i]['v'], src=0) |
|
|
| self.kv_cache1[i]['k'] = self.kv_cache1[i]['k'].repeat(self.batch_size, 1, 1, 1) |
| self.kv_cache1[i]['v'] = self.kv_cache1[i]['v'].repeat(self.batch_size, 1, 1, 1) |
|
|
| self.kv_cache1[i]['global_end_index'] = self.kv_cache1[i]['global_end_index'].repeat(self.batch_size) |
| self.kv_cache1[i]['local_end_index'] = self.kv_cache1[i]['local_end_index'].repeat(self.batch_size) |
|
|
| self.crossattn_cache[i]['k'] = self.crossattn_cache[i]['k'].expand(self.batch_size, -1, -1, -1) |
| self.crossattn_cache[i]['v'] = self.crossattn_cache[i]['v'].expand(self.batch_size, -1, -1, -1) |
| |
| |
| if block_num is not None: |
| for i in range(self.num_transformer_blocks): |
| if i not in blocks_to_keep: |
| self.kv_cache1[i]['k'] = self.kv_cache1[i]['k'].cpu() |
| self.kv_cache1[i]['v'] = self.kv_cache1[i]['v'].cpu() |
|
|
| self.hidden_states = torch.zeros( |
| (self.batch_size, self.num_frame_per_block, *noise.shape[2:]), dtype=noise.dtype, device=device |
| ) |
|
|
| if block_mode in ['output', 'middle']: |
| self.block_x = torch.zeros( |
| (self.batch_size, self.frame_seq_length, self.num_heads*128), dtype=noise.dtype, device=device |
| ) |
| else: |
| self.block_x = None |
|
|
| self.kv_cache_starts = torch.ones(self.batch_size, dtype=torch.long, device=device) * current_end |
| self.kv_cache_ends = torch.ones(self.batch_size, dtype=torch.long, device=device) * current_end + self.frame_seq_length |
|
|
| self.timestep = self.denoising_step_list |
|
|
| self.conditional_dict['prompt_embeds'] = self.conditional_dict['prompt_embeds'].repeat(self.batch_size, 1, 1) |
| |
| return denoised_pred |
| |
| def inference_stream( |
| self, |
| noise: torch.Tensor, |
| current_start: int, |
| current_end: int, |
| current_step: int, |
| ) -> torch.Tensor: |
|
|
| self.hidden_states[1:] = self.hidden_states[:-1].clone() |
| self.hidden_states[0] = noise[0] |
|
|
| self.kv_cache_starts[1:] = self.kv_cache_starts[:-1].clone() |
| self.kv_cache_starts[0] = current_start |
| |
| self.kv_cache_ends[1:] = self.kv_cache_ends[:-1].clone() |
| self.kv_cache_ends[0] = current_end |
|
|
| if current_step is not None: |
| self.timestep[0] = current_step |
| |
| self.hidden_states = self.generator( |
| noisy_image_or_video=self.hidden_states, |
| conditional_dict=self.conditional_dict, |
| timestep=self.timestep.unsqueeze(1).expand(-1, self.hidden_states.shape[1]), |
| kv_cache=self.kv_cache1, |
| crossattn_cache=self.crossattn_cache, |
| current_start=self.kv_cache_starts, |
| current_end=self.kv_cache_ends, |
| ) |
|
|
| for i in range(len(self.denoising_step_list) - 1): |
| self.hidden_states[[i]] = self.scheduler.add_noise( |
| self.hidden_states[[i]], |
| torch.randn_like(self.hidden_states[[i]]), |
| self.denoising_step_list[i + 1] * |
| torch.ones([1], device=self.hidden_states.device, |
| dtype=torch.long) |
| ) |
|
|
| return self.hidden_states |
| |
| def inference_wo_batch( |
| self, |
| noise: torch.Tensor, |
| current_start: int, |
| current_end: int, |
| current_step: int, |
| ) -> torch.Tensor: |
|
|
| batch_size = noise.shape[0] |
|
|
| current_start = torch.ones(batch_size, dtype=torch.long, device=self.device) * current_start |
| current_end = torch.ones(batch_size, dtype=torch.long, device=self.device) * current_end |
|
|
| |
| self.denoising_step_list[0] = current_step |
| for index, current_timestep in enumerate(self.denoising_step_list): |
| |
| timestep = torch.ones( |
| [batch_size, noise.shape[1]], device=noise.device, dtype=torch.int64) * current_timestep |
|
|
| if index < len(self.denoising_step_list) - 1: |
| denoised_pred = self.generator( |
| noisy_image_or_video=noise, |
| conditional_dict=self.conditional_dict, |
| timestep=timestep, |
| kv_cache=self.kv_cache1, |
| crossattn_cache=self.crossattn_cache, |
| current_start=current_start, |
| current_end=current_end |
| ) |
| next_timestep = self.denoising_step_list[index + 1] |
| noise = self.scheduler.add_noise( |
| denoised_pred.flatten(0, 1), |
| torch.randn_like(denoised_pred.flatten(0, 1)), |
| next_timestep * |
| torch.ones([batch_size], device=noise.device, |
| dtype=torch.long) |
| ).unflatten(0, denoised_pred.shape[:2]) |
| else: |
| |
| denoised_pred = self.generator( |
| noisy_image_or_video=noise, |
| conditional_dict=self.conditional_dict, |
| timestep=timestep, |
| kv_cache=self.kv_cache1, |
| crossattn_cache=self.crossattn_cache, |
| current_start=current_start, |
| current_end=current_end |
| ) |
|
|
| return denoised_pred |
|
|
| def inference( |
| self, |
| noise: torch.Tensor, |
| current_start: int, |
| current_end: int, |
| current_step: int, |
| block_mode: str='input', |
| block_num=None, |
| patched_x_shape: torch.Tensor=None, |
| block_x: torch.Tensor=None, |
| ) -> torch.Tensor: |
|
|
| if block_mode == 'input': |
| self.hidden_states[1:] = self.hidden_states[:-1].clone() |
| self.hidden_states[0] = noise[0] |
|
|
| self.kv_cache_starts[1:] = self.kv_cache_starts[:-1].clone() |
| self.kv_cache_starts[0] = current_start |
| |
| self.kv_cache_ends[1:] = self.kv_cache_ends[:-1].clone() |
| self.kv_cache_ends[0] = current_end |
| else: |
| self.block_x.copy_(block_x) |
| self.hidden_states.copy_(noise) |
| self.kv_cache_starts.copy_(current_start) |
| self.kv_cache_ends.copy_(current_end) |
|
|
| if current_step is not None: |
| self.timestep[0] = current_step |
| |
| if block_mode == 'output': |
| denoised_pred = self.generator.forward_output( |
| noisy_image_or_video=self.hidden_states, |
| conditional_dict=self.conditional_dict, |
| timestep=self.timestep.unsqueeze(1).expand(-1, self.hidden_states.shape[1]), |
| kv_cache=self.kv_cache1, |
| crossattn_cache=self.crossattn_cache, |
| current_start=self.kv_cache_starts, |
| current_end=self.kv_cache_ends, |
| block_mode=block_mode, |
| block_num=block_num, |
| patched_x_shape=patched_x_shape, |
| block_x=self.block_x |
| ) |
|
|
| for i in range(len(self.denoising_step_list) - 1): |
| denoised_pred[[i]] = self.scheduler.add_noise( |
| denoised_pred[[i]], |
| torch.randn_like(denoised_pred[[i]]), |
| self.denoising_step_list[i + 1] * |
| torch.ones([1], device=denoised_pred.device, |
| dtype=torch.long) |
| ) |
| patched_x_shape = None |
|
|
| else: |
| denoised_pred, patched_x_shape = self.generator.forward_input( |
| noisy_image_or_video=self.hidden_states, |
| conditional_dict=self.conditional_dict, |
| timestep=self.timestep.unsqueeze(1).expand(-1, self.hidden_states.shape[1]), |
| kv_cache=self.kv_cache1, |
| crossattn_cache=self.crossattn_cache, |
| current_start=self.kv_cache_starts, |
| current_end=self.kv_cache_ends, |
| block_mode=block_mode, |
| block_num=block_num, |
| patched_x_shape=patched_x_shape, |
| block_x=self.block_x, |
| ) |
|
|
| return denoised_pred, patched_x_shape |
|
|