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

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

Browse files
extensions_built_in/diffusion_models/wan22/wan22_14b_model.py ADDED
@@ -0,0 +1,583 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from functools import partial
2
+ import os
3
+ from typing import Any, Dict, Optional, Union, List
4
+ from typing_extensions import Self
5
+ import torch
6
+ import yaml
7
+ from toolkit.accelerator import unwrap_model
8
+ from toolkit.basic import flush
9
+ from toolkit.models.wan21.wan_utils import add_first_frame_conditioning
10
+ from toolkit.prompt_utils import PromptEmbeds
11
+ from PIL import Image
12
+ from diffusers import UniPCMultistepScheduler
13
+ import torch
14
+ from toolkit.config_modules import GenerateImageConfig, ModelConfig
15
+ from toolkit.samplers.custom_flowmatch_sampler import (
16
+ CustomFlowMatchEulerDiscreteScheduler,
17
+ )
18
+ from toolkit.util.quantize import quantize_model
19
+ from .wan22_pipeline import Wan22Pipeline
20
+ from diffusers import WanTransformer3DModel
21
+
22
+ from toolkit.data_transfer_object.data_loader import DataLoaderBatchDTO
23
+ from torchvision.transforms import functional as TF
24
+
25
+ from toolkit.models.wan21.wan21 import Wan21
26
+ from .wan22_5b_model import (
27
+ scheduler_config,
28
+ time_text_monkeypatch,
29
+ )
30
+ from safetensors.torch import load_file, save_file
31
+
32
+
33
+ boundary_ratio_t2v = 0.875
34
+ boundary_ratio_i2v = 0.9
35
+
36
+ scheduler_configUniPC = {
37
+ "_class_name": "UniPCMultistepScheduler",
38
+ "_diffusers_version": "0.35.0.dev0",
39
+ "beta_end": 0.02,
40
+ "beta_schedule": "linear",
41
+ "beta_start": 0.0001,
42
+ "disable_corrector": [],
43
+ "dynamic_thresholding_ratio": 0.995,
44
+ "final_sigmas_type": "zero",
45
+ "flow_shift": 3.0,
46
+ "lower_order_final": True,
47
+ "num_train_timesteps": 1000,
48
+ "predict_x0": True,
49
+ "prediction_type": "flow_prediction",
50
+ "rescale_betas_zero_snr": False,
51
+ "sample_max_value": 1.0,
52
+ "solver_order": 2,
53
+ "solver_p": None,
54
+ "solver_type": "bh2",
55
+ "steps_offset": 0,
56
+ "thresholding": False,
57
+ "time_shift_type": "exponential",
58
+ "timestep_spacing": "linspace",
59
+ "trained_betas": None,
60
+ "use_beta_sigmas": False,
61
+ "use_dynamic_shifting": False,
62
+ "use_exponential_sigmas": False,
63
+ "use_flow_sigmas": True,
64
+ "use_karras_sigmas": False,
65
+ }
66
+
67
+
68
+ class DualWanTransformer3DModel(torch.nn.Module):
69
+ def __init__(
70
+ self,
71
+ transformer_1: WanTransformer3DModel,
72
+ transformer_2: WanTransformer3DModel,
73
+ torch_dtype: Optional[Union[str, torch.dtype]] = None,
74
+ device: Optional[Union[str, torch.device]] = None,
75
+ boundary_ratio: float = boundary_ratio_t2v,
76
+ low_vram: bool = False,
77
+ ) -> None:
78
+ super().__init__()
79
+ self.transformer_1: WanTransformer3DModel = transformer_1
80
+ self.transformer_2: WanTransformer3DModel = transformer_2
81
+ self.torch_dtype: torch.dtype = torch_dtype
82
+ self.device_torch: torch.device = device
83
+ self.boundary_ratio: float = boundary_ratio
84
+ self.boundary: float = self.boundary_ratio * 1000
85
+ self.low_vram: bool = low_vram
86
+ self._active_transformer_name = "transformer_1" # default to transformer_1
87
+
88
+ @property
89
+ def device(self) -> torch.device:
90
+ return self.device_torch
91
+
92
+ @property
93
+ def dtype(self) -> torch.dtype:
94
+ return self.torch_dtype
95
+
96
+ @property
97
+ def config(self):
98
+ return self.transformer_1.config
99
+
100
+ @property
101
+ def transformer(self) -> WanTransformer3DModel:
102
+ return getattr(self, self._active_transformer_name)
103
+
104
+ def enable_gradient_checkpointing(self):
105
+ """
106
+ Enable gradient checkpointing for both transformers.
107
+ """
108
+ self.transformer_1.enable_gradient_checkpointing()
109
+ self.transformer_2.enable_gradient_checkpointing()
110
+
111
+ def forward(
112
+ self,
113
+ hidden_states: torch.Tensor,
114
+ timestep: torch.LongTensor,
115
+ encoder_hidden_states: torch.Tensor,
116
+ encoder_hidden_states_image: Optional[torch.Tensor] = None,
117
+ return_dict: bool = True,
118
+ attention_kwargs: Optional[Dict[str, Any]] = None,
119
+ **kwargs
120
+ ) -> Union[torch.Tensor, Dict[str, torch.Tensor]]:
121
+ # determine if doing high noise or low noise by meaning the timestep.
122
+ # timesteps are in the range of 0 to 1000, so we can use a threshold
123
+ with torch.no_grad():
124
+ if timestep.float().mean().item() > self.boundary:
125
+ t_name = "transformer_1"
126
+ else:
127
+ t_name = "transformer_2"
128
+
129
+ # check if we are changing the active transformer, if so, we need to swap the one in
130
+ # vram if low_vram is enabled
131
+ # todo swap the loras as well
132
+ if t_name != self._active_transformer_name:
133
+ if self.low_vram:
134
+ getattr(self, self._active_transformer_name).to("cpu")
135
+ getattr(self, t_name).to(self.device_torch)
136
+ torch.cuda.empty_cache()
137
+ self._active_transformer_name = t_name
138
+
139
+ if self.transformer.device != hidden_states.device:
140
+ if self.low_vram:
141
+ # move other transformer to cpu
142
+ other_tname = (
143
+ "transformer_1" if t_name == "transformer_2" else "transformer_2"
144
+ )
145
+ getattr(self, other_tname).to("cpu")
146
+
147
+ self.transformer.to(hidden_states.device)
148
+
149
+ return self.transformer(
150
+ hidden_states=hidden_states,
151
+ timestep=timestep,
152
+ encoder_hidden_states=encoder_hidden_states,
153
+ encoder_hidden_states_image=encoder_hidden_states_image,
154
+ return_dict=return_dict,
155
+ attention_kwargs=attention_kwargs,
156
+ )
157
+
158
+ def to(self, *args, **kwargs) -> Self:
159
+ # do not do to, this will be handled separately
160
+ return self
161
+
162
+
163
+ class Wan2214bModel(Wan21):
164
+ arch = "wan22_14b"
165
+ _wan_generation_scheduler_config = scheduler_configUniPC
166
+ _wan_expand_timesteps = False
167
+ _wan_vae_path = "ai-toolkit/wan2.1-vae"
168
+
169
+ def __init__(
170
+ self,
171
+ device,
172
+ model_config: ModelConfig,
173
+ dtype="bf16",
174
+ custom_pipeline=None,
175
+ noise_scheduler=None,
176
+ **kwargs,
177
+ ):
178
+ super().__init__(
179
+ device=device,
180
+ model_config=model_config,
181
+ dtype=dtype,
182
+ custom_pipeline=custom_pipeline,
183
+ noise_scheduler=noise_scheduler,
184
+ **kwargs,
185
+ )
186
+ # target it so we can target both transformers
187
+ self.target_lora_modules = ["DualWanTransformer3DModel"]
188
+ self._wan_cache = None
189
+
190
+ self.is_multistage = True
191
+ # multistage boundaries split the models up when sampling timesteps
192
+ # for wan 2.2 14b. the timesteps are 1000-875 for transformer 1 and 875-0 for transformer 2
193
+ self.multistage_boundaries: List[float] = [0.875, 0.0]
194
+
195
+ self.train_high_noise = model_config.model_kwargs.get("train_high_noise", True)
196
+ self.train_low_noise = model_config.model_kwargs.get("train_low_noise", True)
197
+
198
+ self.trainable_multistage_boundaries: List[int] = []
199
+ if self.train_high_noise:
200
+ self.trainable_multistage_boundaries.append(0)
201
+ if self.train_low_noise:
202
+ self.trainable_multistage_boundaries.append(1)
203
+
204
+ if len(self.trainable_multistage_boundaries) == 0:
205
+ raise ValueError(
206
+ "At least one of train_high_noise or train_low_noise must be True in model.model_kwargs"
207
+ )
208
+
209
+ # if we are only training one or the other, the target LoRA modules will be the wan transformer class
210
+ if not self.train_high_noise or not self.train_low_noise:
211
+ self.target_lora_modules = ["WanTransformer3DModel"]
212
+
213
+ @property
214
+ def max_step_saves_to_keep_multiplier(self):
215
+ # the cleanup mechanism checks this to see how many saves to keep
216
+ # if we are training a LoRA, we need to set this to 2 so we keep both the high noise and low noise LoRAs at saves to keep
217
+ if (
218
+ self.network is not None
219
+ and self.network.network_config.split_multistage_loras
220
+ ):
221
+ return 2
222
+ return 1
223
+
224
+ def load_model(self):
225
+ # load model from patent parent. Wan21 not immediate parent
226
+ # super().load_model()
227
+ super().load_model()
228
+
229
+ # we have to split up the model on the pipeline
230
+ self.pipeline.transformer = self.model.transformer_1
231
+ self.pipeline.transformer_2 = self.model.transformer_2
232
+
233
+ # patch the condition embedder
234
+ self.model.transformer_1.condition_embedder.forward = partial(
235
+ time_text_monkeypatch, self.model.transformer_1.condition_embedder
236
+ )
237
+ self.model.transformer_2.condition_embedder.forward = partial(
238
+ time_text_monkeypatch, self.model.transformer_2.condition_embedder
239
+ )
240
+
241
+ def get_bucket_divisibility(self):
242
+ # 8x compression and 2x2 patch size
243
+ return 16
244
+
245
+ def load_wan_transformer(self, transformer_path, subfolder=None):
246
+ if self.model_config.split_model_over_gpus:
247
+ raise ValueError(
248
+ "Splitting model over gpus is not supported for Wan2.2 models"
249
+ )
250
+
251
+ if (
252
+ self.model_config.assistant_lora_path is not None
253
+ or self.model_config.inference_lora_path is not None
254
+ ):
255
+ raise ValueError(
256
+ "Assistant LoRA is not supported for Wan2.2 models currently"
257
+ )
258
+
259
+ if self.model_config.lora_path is not None:
260
+ raise ValueError(
261
+ "Loading LoRA is not supported for Wan2.2 models currently"
262
+ )
263
+
264
+ # transformer path can be a directory that ends with /transformer or a hf path.
265
+
266
+ transformer_path_1 = transformer_path
267
+ subfolder_1 = subfolder
268
+
269
+ transformer_path_2 = transformer_path
270
+ subfolder_2 = subfolder
271
+
272
+ if subfolder_2 is None:
273
+ # we have a local path, replace it with transformer_2 folder
274
+ transformer_path_2 = os.path.join(
275
+ os.path.dirname(transformer_path_1), "transformer_2"
276
+ )
277
+ else:
278
+ # we have a hf path, replace it with transformer_2 subfolder
279
+ subfolder_2 = "transformer_2"
280
+
281
+ self.print_and_status_update("Loading transformer 1")
282
+ dtype = self.torch_dtype
283
+ transformer_1 = WanTransformer3DModel.from_pretrained(
284
+ transformer_path_1,
285
+ subfolder=subfolder_1,
286
+ torch_dtype=dtype,
287
+ ).to(dtype=dtype)
288
+
289
+ flush()
290
+
291
+ if not self.model_config.low_vram:
292
+ # quantize on the device
293
+ transformer_1.to(self.quantize_device, dtype=dtype)
294
+ flush()
295
+
296
+ if self.model_config.quantize and self.model_config.accuracy_recovery_adapter is None:
297
+ # todo handle two ARAs
298
+ self.print_and_status_update("Quantizing Transformer 1")
299
+ quantize_model(self, transformer_1)
300
+ flush()
301
+
302
+ if self.model_config.low_vram:
303
+ self.print_and_status_update("Moving transformer 1 to CPU")
304
+ transformer_1.to("cpu")
305
+
306
+ self.print_and_status_update("Loading transformer 2")
307
+ dtype = self.torch_dtype
308
+ transformer_2 = WanTransformer3DModel.from_pretrained(
309
+ transformer_path_2,
310
+ subfolder=subfolder_2,
311
+ torch_dtype=dtype,
312
+ ).to(dtype=dtype)
313
+
314
+ flush()
315
+
316
+ if not self.model_config.low_vram:
317
+ # quantize on the device
318
+ transformer_2.to(self.quantize_device, dtype=dtype)
319
+ flush()
320
+
321
+ if self.model_config.quantize and self.model_config.accuracy_recovery_adapter is None:
322
+ # todo handle two ARAs
323
+ self.print_and_status_update("Quantizing Transformer 2")
324
+ quantize_model(self, transformer_2)
325
+ flush()
326
+
327
+ if self.model_config.low_vram:
328
+ self.print_and_status_update("Moving transformer 2 to CPU")
329
+ transformer_2.to("cpu")
330
+
331
+ # make the combined model
332
+ self.print_and_status_update("Creating DualWanTransformer3DModel")
333
+ transformer = DualWanTransformer3DModel(
334
+ transformer_1=transformer_1,
335
+ transformer_2=transformer_2,
336
+ torch_dtype=self.torch_dtype,
337
+ device=self.device_torch,
338
+ boundary_ratio=boundary_ratio_t2v,
339
+ low_vram=self.model_config.low_vram,
340
+ )
341
+
342
+ if self.model_config.quantize and self.model_config.accuracy_recovery_adapter is not None:
343
+ # apply the accuracy recovery adapter to both transformers
344
+ self.print_and_status_update("Applying Accuracy Recovery Adapter to Transformers")
345
+ quantize_model(self, transformer)
346
+ flush()
347
+
348
+ return transformer
349
+
350
+ def get_generation_pipeline(self):
351
+ scheduler = UniPCMultistepScheduler(**self._wan_generation_scheduler_config)
352
+ pipeline = Wan22Pipeline(
353
+ vae=self.vae,
354
+ transformer=self.model.transformer_1,
355
+ transformer_2=self.model.transformer_2,
356
+ text_encoder=self.text_encoder,
357
+ tokenizer=self.tokenizer,
358
+ scheduler=scheduler,
359
+ expand_timesteps=self._wan_expand_timesteps,
360
+ device=self.device_torch,
361
+ aggressive_offload=self.model_config.low_vram,
362
+ # todo detect if it is i2v or t2v
363
+ boundary_ratio=boundary_ratio_t2v,
364
+ )
365
+
366
+ # pipeline = pipeline.to(self.device_torch)
367
+
368
+ return pipeline
369
+
370
+ # static method to get the scheduler
371
+ @staticmethod
372
+ def get_train_scheduler():
373
+ scheduler = CustomFlowMatchEulerDiscreteScheduler(**scheduler_config)
374
+ return scheduler
375
+
376
+ def get_base_model_version(self):
377
+ return "wan_2.2_14b"
378
+
379
+ def generate_single_image(
380
+ self,
381
+ pipeline: Wan22Pipeline,
382
+ gen_config: GenerateImageConfig,
383
+ conditional_embeds: PromptEmbeds,
384
+ unconditional_embeds: PromptEmbeds,
385
+ generator: torch.Generator,
386
+ extra: dict,
387
+ ):
388
+ return super().generate_single_image(
389
+ pipeline=pipeline,
390
+ gen_config=gen_config,
391
+ conditional_embeds=conditional_embeds,
392
+ unconditional_embeds=unconditional_embeds,
393
+ generator=generator,
394
+ extra=extra,
395
+ )
396
+
397
+ def get_noise_prediction(
398
+ self,
399
+ latent_model_input: torch.Tensor,
400
+ timestep: torch.Tensor, # 0 to 1000 scale
401
+ text_embeddings: PromptEmbeds,
402
+ batch: DataLoaderBatchDTO,
403
+ **kwargs,
404
+ ):
405
+ # todo do we need to override this? Adjust timesteps?
406
+ return super().get_noise_prediction(
407
+ latent_model_input=latent_model_input,
408
+ timestep=timestep,
409
+ text_embeddings=text_embeddings,
410
+ batch=batch,
411
+ **kwargs,
412
+ )
413
+
414
+ def get_model_has_grad(self):
415
+ return False
416
+
417
+ def get_te_has_grad(self):
418
+ return False
419
+
420
+ def save_model(self, output_path, meta, save_dtype):
421
+ transformer_combo: DualWanTransformer3DModel = unwrap_model(self.model)
422
+ transformer_combo.transformer_1.save_pretrained(
423
+ save_directory=os.path.join(output_path, "transformer"),
424
+ safe_serialization=True,
425
+ )
426
+ transformer_combo.transformer_2.save_pretrained(
427
+ save_directory=os.path.join(output_path, "transformer_2"),
428
+ safe_serialization=True,
429
+ )
430
+
431
+ meta_path = os.path.join(output_path, "aitk_meta.yaml")
432
+ with open(meta_path, "w") as f:
433
+ yaml.dump(meta, f)
434
+
435
+ def save_lora(
436
+ self,
437
+ state_dict: Dict[str, torch.Tensor],
438
+ output_path: str,
439
+ metadata: Optional[Dict[str, Any]] = None,
440
+ ):
441
+ if not self.network.network_config.split_multistage_loras:
442
+ # just save as a combo lora
443
+ save_file(state_dict, output_path, metadata=metadata)
444
+ return
445
+
446
+ # we need to build out both dictionaries for high and low noise LoRAs
447
+ high_noise_lora = {}
448
+ low_noise_lora = {}
449
+
450
+ only_train_high_noise = self.train_high_noise and not self.train_low_noise
451
+ only_train_low_noise = self.train_low_noise and not self.train_high_noise
452
+
453
+ for key in state_dict:
454
+ if ".transformer_1." in key or only_train_high_noise:
455
+ # this is a high noise LoRA
456
+ new_key = key.replace(".transformer_1.", ".")
457
+ high_noise_lora[new_key] = state_dict[key]
458
+ elif ".transformer_2." in key or only_train_low_noise:
459
+ # this is a low noise LoRA
460
+ new_key = key.replace(".transformer_2.", ".")
461
+ low_noise_lora[new_key] = state_dict[key]
462
+
463
+ # loras have either LORA_MODEL_NAME_000005000.safetensors or LORA_MODEL_NAME.safetensors
464
+ if len(high_noise_lora.keys()) > 0:
465
+ # save the high noise LoRA
466
+ high_noise_lora_path = output_path.replace(
467
+ ".safetensors", "_high_noise.safetensors"
468
+ )
469
+ save_file(high_noise_lora, high_noise_lora_path, metadata=metadata)
470
+
471
+ if len(low_noise_lora.keys()) > 0:
472
+ # save the low noise LoRA
473
+ low_noise_lora_path = output_path.replace(
474
+ ".safetensors", "_low_noise.safetensors"
475
+ )
476
+ save_file(low_noise_lora, low_noise_lora_path, metadata=metadata)
477
+
478
+ def load_lora(self, file: str):
479
+ # if it doesnt have high_noise or low_noise, it is a combo LoRA
480
+ if (
481
+ "_high_noise.safetensors" not in file
482
+ and "_low_noise.safetensors" not in file
483
+ ):
484
+ # this is a combined LoRA, we dont need to split it up
485
+ sd = load_file(file)
486
+ return sd
487
+
488
+ # we may have been passed the high_noise or the low_noise LoRA path, but we need to load both
489
+ high_noise_lora_path = file.replace(
490
+ "_low_noise.safetensors", "_high_noise.safetensors"
491
+ )
492
+ low_noise_lora_path = file.replace(
493
+ "_high_noise.safetensors", "_low_noise.safetensors"
494
+ )
495
+
496
+ combined_dict = {}
497
+
498
+ if os.path.exists(high_noise_lora_path) and self.train_high_noise:
499
+ # load the high noise LoRA
500
+ high_noise_lora = load_file(high_noise_lora_path)
501
+ for key in high_noise_lora:
502
+ new_key = key.replace(
503
+ "diffusion_model.", "diffusion_model.transformer_1."
504
+ )
505
+ combined_dict[new_key] = high_noise_lora[key]
506
+ if os.path.exists(low_noise_lora_path) and self.train_low_noise:
507
+ # load the low noise LoRA
508
+ low_noise_lora = load_file(low_noise_lora_path)
509
+ for key in low_noise_lora:
510
+ new_key = key.replace(
511
+ "diffusion_model.", "diffusion_model.transformer_2."
512
+ )
513
+ combined_dict[new_key] = low_noise_lora[key]
514
+
515
+ # if we are not training both stages, we wont have transformer designations in the keys
516
+ if not self.train_high_noise or not self.train_low_noise:
517
+ new_dict = {}
518
+ for key in combined_dict:
519
+ if ".transformer_1." in key:
520
+ new_key = key.replace(".transformer_1.", ".")
521
+ elif ".transformer_2." in key:
522
+ new_key = key.replace(".transformer_2.", ".")
523
+ else:
524
+ new_key = key
525
+ new_dict[new_key] = combined_dict[key]
526
+ combined_dict = new_dict
527
+
528
+ return combined_dict
529
+
530
+ def generate_single_image(
531
+ self,
532
+ pipeline,
533
+ gen_config: GenerateImageConfig,
534
+ conditional_embeds: PromptEmbeds,
535
+ unconditional_embeds: PromptEmbeds,
536
+ generator: torch.Generator,
537
+ extra: dict,
538
+ ):
539
+ # reactivate progress bar since this is slooooow
540
+ pipeline.set_progress_bar_config(disable=False)
541
+ # todo, figure out how to do video
542
+ output = pipeline(
543
+ prompt_embeds=conditional_embeds.text_embeds.to(
544
+ self.device_torch, dtype=self.torch_dtype),
545
+ negative_prompt_embeds=unconditional_embeds.text_embeds.to(
546
+ self.device_torch, dtype=self.torch_dtype),
547
+ height=gen_config.height,
548
+ width=gen_config.width,
549
+ num_inference_steps=gen_config.num_inference_steps,
550
+ guidance_scale=gen_config.guidance_scale,
551
+ latents=gen_config.latents,
552
+ num_frames=gen_config.num_frames,
553
+ generator=generator,
554
+ return_dict=False,
555
+ output_type="pil",
556
+ **extra
557
+ )[0]
558
+
559
+ # shape = [1, frames, channels, height, width]
560
+ batch_item = output[0] # list of pil images
561
+ if gen_config.num_frames > 1:
562
+ return batch_item # return the frames.
563
+ else:
564
+ # get just the first image
565
+ img = batch_item[0]
566
+ return img
567
+
568
+ def get_model_to_train(self):
569
+ # todo, loras wont load right unless they have the transformer_1 or transformer_2 in the key.
570
+ # called when setting up the LoRA. We only need to get the model for the stages we want to train.
571
+ if self.train_high_noise and self.train_low_noise:
572
+ # we are training both stages, return the unified model
573
+ return self.model
574
+ elif self.train_high_noise:
575
+ # we are only training the high noise stage, return transformer_1
576
+ return self.model.transformer_1
577
+ elif self.train_low_noise:
578
+ # we are only training the low noise stage, return transformer_2
579
+ return self.model.transformer_2
580
+ else:
581
+ raise ValueError(
582
+ "At least one of train_high_noise or train_low_noise must be True in model.model_kwargs"
583
+ )