comdoleger commited on
Commit
ab5dff9
·
verified ·
1 Parent(s): 6843715

Upload extensions_built_in/diffusion_models/wan22/wan22_pipeline.py with huggingface_hub

Browse files
extensions_built_in/diffusion_models/wan22/wan22_pipeline.py ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import torch
3
+ from toolkit.basic import flush
4
+ from transformers import AutoTokenizer, UMT5EncoderModel
5
+ from diffusers import WanPipeline, WanTransformer3DModel, AutoencoderKLWan
6
+ import torch
7
+ from diffusers import FlowMatchEulerDiscreteScheduler
8
+ from typing import List
9
+ from diffusers.pipelines.wan.pipeline_output import WanPipelineOutput
10
+ from diffusers.pipelines.wan.pipeline_wan import XLA_AVAILABLE
11
+ from diffusers.callbacks import MultiPipelineCallbacks, PipelineCallback
12
+ from typing import Any, Callable, Dict, List, Optional, Union
13
+ from diffusers.image_processor import PipelineImageInput
14
+
15
+
16
+ class Wan22Pipeline(WanPipeline):
17
+ def __init__(
18
+ self,
19
+ tokenizer: AutoTokenizer,
20
+ text_encoder: UMT5EncoderModel,
21
+ transformer: WanTransformer3DModel,
22
+ vae: AutoencoderKLWan,
23
+ scheduler: FlowMatchEulerDiscreteScheduler,
24
+ transformer_2: Optional[WanTransformer3DModel] = None,
25
+ boundary_ratio: Optional[float] = None,
26
+ expand_timesteps: bool = False, # Wan2.2 ti2v
27
+ device: torch.device = torch.device("cuda"),
28
+ aggressive_offload: bool = False,
29
+ ):
30
+ super().__init__(
31
+ tokenizer=tokenizer,
32
+ text_encoder=text_encoder,
33
+ transformer=transformer,
34
+ transformer_2=transformer_2,
35
+ boundary_ratio=boundary_ratio,
36
+ expand_timesteps=expand_timesteps,
37
+ vae=vae,
38
+ scheduler=scheduler,
39
+ )
40
+ self._aggressive_offload = aggressive_offload
41
+ self._exec_device = device
42
+ @property
43
+ def _execution_device(self):
44
+ return self._exec_device
45
+
46
+ def __call__(
47
+ self: WanPipeline,
48
+ prompt: Union[str, List[str]] = None,
49
+ negative_prompt: Union[str, List[str]] = None,
50
+ height: int = 480,
51
+ width: int = 832,
52
+ num_frames: int = 81,
53
+ num_inference_steps: int = 50,
54
+ guidance_scale: float = 5.0,
55
+ guidance_scale_2: Optional[float] = None,
56
+ num_videos_per_prompt: Optional[int] = 1,
57
+ generator: Optional[Union[torch.Generator,
58
+ List[torch.Generator]]] = None,
59
+ latents: Optional[torch.Tensor] = None,
60
+ prompt_embeds: Optional[torch.Tensor] = None,
61
+ negative_prompt_embeds: Optional[torch.Tensor] = None,
62
+ output_type: Optional[str] = "np",
63
+ return_dict: bool = True,
64
+ attention_kwargs: Optional[Dict[str, Any]] = None,
65
+ callback_on_step_end: Optional[
66
+ Union[Callable[[int, int, Dict], None],
67
+ PipelineCallback, MultiPipelineCallbacks]
68
+ ] = None,
69
+ callback_on_step_end_tensor_inputs: List[str] = ["latents"],
70
+ max_sequence_length: int = 512,
71
+ noise_mask: Optional[torch.Tensor] = None,
72
+ ):
73
+
74
+ if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
75
+ callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
76
+
77
+ if num_frames % self.vae_scale_factor_temporal != 1:
78
+ num_frames = num_frames // self.vae_scale_factor_temporal * self.vae_scale_factor_temporal + 1
79
+ num_frames = max(num_frames, 1)
80
+
81
+
82
+ width = width // (self.vae.config.scale_factor_spatial * 2) * (self.vae.config.scale_factor_spatial * 2)
83
+ height = height // (self.vae.config.scale_factor_spatial * 2) * (self.vae.config.scale_factor_spatial * 2)
84
+
85
+ # unload vae and transformer
86
+ vae_device = self.vae.device
87
+ transformer_device = self.transformer.device
88
+ text_encoder_device = self.text_encoder.device
89
+ device = self._exec_device
90
+
91
+ if self._aggressive_offload:
92
+ print("Unloading vae")
93
+ self.vae.to("cpu")
94
+ print("Unloading transformer")
95
+ self.transformer.to("cpu")
96
+ if self.transformer_2 is not None:
97
+ self.transformer_2.to("cpu")
98
+ self.text_encoder.to(device)
99
+ flush()
100
+
101
+
102
+ # 1. Check inputs. Raise error if not correct
103
+ self.check_inputs(
104
+ prompt,
105
+ negative_prompt,
106
+ height,
107
+ width,
108
+ prompt_embeds,
109
+ negative_prompt_embeds,
110
+ callback_on_step_end_tensor_inputs,
111
+ guidance_scale_2
112
+ )
113
+
114
+ if self.config.boundary_ratio is not None and guidance_scale_2 is None:
115
+ guidance_scale_2 = guidance_scale
116
+
117
+ self._guidance_scale = guidance_scale
118
+ self._guidance_scale_2 = guidance_scale_2
119
+ self._attention_kwargs = attention_kwargs
120
+ self._current_timestep = None
121
+ self._interrupt = False
122
+
123
+ # 2. Define call parameters
124
+ if prompt is not None and isinstance(prompt, str):
125
+ batch_size = 1
126
+ elif prompt is not None and isinstance(prompt, list):
127
+ batch_size = len(prompt)
128
+ else:
129
+ batch_size = prompt_embeds.shape[0]
130
+
131
+ # 3. Encode input prompt
132
+ prompt_embeds, negative_prompt_embeds = self.encode_prompt(
133
+ prompt=prompt,
134
+ negative_prompt=negative_prompt,
135
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
136
+ num_videos_per_prompt=num_videos_per_prompt,
137
+ prompt_embeds=prompt_embeds,
138
+ negative_prompt_embeds=negative_prompt_embeds,
139
+ max_sequence_length=max_sequence_length,
140
+ device=device,
141
+ )
142
+ if self._aggressive_offload:
143
+ # unload text encoder
144
+ print("Unloading text encoder")
145
+ self.text_encoder.to("cpu")
146
+ self.transformer.to(device)
147
+ flush()
148
+
149
+ transformer_dtype = self.transformer.dtype
150
+ prompt_embeds = prompt_embeds.to(device, transformer_dtype)
151
+ if negative_prompt_embeds is not None:
152
+ negative_prompt_embeds = negative_prompt_embeds.to(
153
+ device, transformer_dtype)
154
+
155
+ # 4. Prepare timesteps
156
+ self.scheduler.set_timesteps(num_inference_steps, device=device)
157
+ timesteps = self.scheduler.timesteps
158
+
159
+ # 5. Prepare latent variables
160
+ num_channels_latents = self.transformer.config.in_channels
161
+
162
+ conditioning = None # wan2.2 i2v conditioning
163
+ # check shape of latents to see if it is first frame conditioned for 2.2 14b i2v
164
+ if latents is not None:
165
+ if latents.shape[1] == 36:
166
+ # first 16 channels are latent. other 20 are conditioning
167
+ conditioning = latents[:, 16:]
168
+ latents = latents[:, :16]
169
+
170
+ # we need to trick the in_channls to think it is only 16 channels
171
+ num_channels_latents = 16
172
+
173
+ latents = self.prepare_latents(
174
+ batch_size * num_videos_per_prompt,
175
+ num_channels_latents,
176
+ height,
177
+ width,
178
+ num_frames,
179
+ torch.float32,
180
+ device,
181
+ generator,
182
+ latents,
183
+ )
184
+
185
+ mask = noise_mask
186
+ if mask is None:
187
+ mask = torch.ones(latents.shape, dtype=torch.float32, device=device)
188
+
189
+ # 6. Denoising loop
190
+ num_warmup_steps = len(timesteps) - \
191
+ num_inference_steps * self.scheduler.order
192
+ self._num_timesteps = len(timesteps)
193
+
194
+ if self.config.boundary_ratio is not None:
195
+ boundary_timestep = self.config.boundary_ratio * self.scheduler.config.num_train_timesteps
196
+ else:
197
+ boundary_timestep = None
198
+
199
+ current_model = self.transformer
200
+
201
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
202
+ for i, t in enumerate(timesteps):
203
+ if self.interrupt:
204
+ continue
205
+
206
+ self._current_timestep = t
207
+
208
+ if boundary_timestep is None or t >= boundary_timestep:
209
+ if self._aggressive_offload and current_model != self.transformer:
210
+ if self.transformer_2 is not None:
211
+ self.transformer_2.to("cpu")
212
+ self.transformer.to(device)
213
+ # wan2.1 or high-noise stage in wan2.2
214
+ current_model = self.transformer
215
+ current_guidance_scale = guidance_scale
216
+ else:
217
+ if self._aggressive_offload and current_model != self.transformer_2:
218
+ if self.transformer is not None:
219
+ self.transformer.to("cpu")
220
+ if self.transformer_2 is not None:
221
+ self.transformer_2.to(device)
222
+ # low-noise stage in wan2.2
223
+ current_model = self.transformer_2
224
+ current_guidance_scale = guidance_scale_2
225
+
226
+ latent_model_input = latents.to(device, transformer_dtype)
227
+ if self.config.expand_timesteps:
228
+ # seq_len: num_latent_frames * latent_height//2 * latent_width//2
229
+ temp_ts = (mask[0][0][:, ::2, ::2] * t).flatten()
230
+ # batch_size, seq_len
231
+ timestep = temp_ts.unsqueeze(0).expand(latents.shape[0], -1)
232
+ else:
233
+ timestep = t.expand(latents.shape[0])
234
+
235
+ pre_condition_latent_model_input = latent_model_input.clone()
236
+
237
+ if conditioning is not None:
238
+ # conditioning is first frame conditioning for 2.2 i2v
239
+ latent_model_input = torch.cat(
240
+ [latent_model_input, conditioning], dim=1)
241
+
242
+ noise_pred = current_model(
243
+ hidden_states=latent_model_input,
244
+ timestep=timestep,
245
+ encoder_hidden_states=prompt_embeds,
246
+ attention_kwargs=attention_kwargs,
247
+ return_dict=False,
248
+ )[0]
249
+
250
+ if self.do_classifier_free_guidance:
251
+ noise_uncond = current_model(
252
+ hidden_states=latent_model_input,
253
+ timestep=timestep,
254
+ encoder_hidden_states=negative_prompt_embeds,
255
+ attention_kwargs=attention_kwargs,
256
+ return_dict=False,
257
+ )[0]
258
+ noise_pred = noise_uncond + current_guidance_scale * \
259
+ (noise_pred - noise_uncond)
260
+
261
+ # compute the previous noisy sample x_t -> x_t-1
262
+ latents = self.scheduler.step(
263
+ noise_pred, t, latents, return_dict=False)[0]
264
+
265
+ # apply i2v mask
266
+ latents = (pre_condition_latent_model_input * (1 - mask)) + (
267
+ latents * mask
268
+ )
269
+
270
+ if callback_on_step_end is not None:
271
+ callback_kwargs = {}
272
+ for k in callback_on_step_end_tensor_inputs:
273
+ callback_kwargs[k] = locals()[k]
274
+ callback_outputs = callback_on_step_end(
275
+ self, i, t, callback_kwargs)
276
+
277
+ latents = callback_outputs.pop("latents", latents)
278
+ prompt_embeds = callback_outputs.pop(
279
+ "prompt_embeds", prompt_embeds)
280
+ negative_prompt_embeds = callback_outputs.pop(
281
+ "negative_prompt_embeds", negative_prompt_embeds)
282
+
283
+ # call the callback, if provided
284
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
285
+ progress_bar.update()
286
+
287
+ if XLA_AVAILABLE:
288
+ xm.mark_step()
289
+
290
+ self._current_timestep = None
291
+
292
+ if self._aggressive_offload:
293
+ # unload transformer
294
+ print("Unloading transformer")
295
+ self.transformer.to("cpu")
296
+ if self.transformer_2 is not None:
297
+ self.transformer_2.to("cpu")
298
+ # load vae
299
+ print("Loading Vae")
300
+ self.vae.to(vae_device)
301
+ flush()
302
+
303
+ if not output_type == "latent":
304
+ latents = latents.to(self.vae.dtype)
305
+ latents_mean = (
306
+ torch.tensor(self.vae.config.latents_mean)
307
+ .view(1, self.vae.config.z_dim, 1, 1, 1)
308
+ .to(latents.device, latents.dtype)
309
+ )
310
+ latents_std = 1.0 / torch.tensor(self.vae.config.latents_std).view(1, self.vae.config.z_dim, 1, 1, 1).to(
311
+ latents.device, latents.dtype
312
+ )
313
+ latents = latents / latents_std + latents_mean
314
+ video = self.vae.decode(latents, return_dict=False)[0]
315
+ video = self.video_processor.postprocess_video(
316
+ video, output_type=output_type)
317
+ else:
318
+ video = latents
319
+
320
+ # Offload all models
321
+ self.maybe_free_model_hooks()
322
+
323
+ # move transformer back to device
324
+ if self._aggressive_offload:
325
+ # print("Moving transformer back to device")
326
+ # self.transformer.to(self._execution_device)
327
+ flush()
328
+
329
+ if not return_dict:
330
+ return (video,)
331
+
332
+ return WanPipelineOutput(frames=video)