comdoleger commited on
Commit
174dbe3
·
verified ·
1 Parent(s): 33fc823

Upload extensions_built_in/diffusion_models/omnigen2/__init__.py with huggingface_hub

Browse files
extensions_built_in/diffusion_models/omnigen2/__init__.py ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import TYPE_CHECKING, List, Optional
3
+
4
+ import torch
5
+ import yaml
6
+ from toolkit.config_modules import GenerateImageConfig, ModelConfig
7
+ from toolkit.models.base_model import BaseModel
8
+ from diffusers import AutoencoderKL
9
+ from toolkit.basic import flush
10
+ from toolkit.prompt_utils import PromptEmbeds
11
+ from toolkit.samplers.custom_flowmatch_sampler import (
12
+ CustomFlowMatchEulerDiscreteScheduler,
13
+ )
14
+ from toolkit.accelerator import unwrap_model
15
+ from optimum.quanto import freeze
16
+ from toolkit.util.quantize import quantize, get_qtype
17
+ from .src.pipelines.omnigen2.pipeline_omnigen2 import OmniGen2Pipeline
18
+ from .src.models.transformers import OmniGen2Transformer2DModel
19
+ from .src.models.transformers.repo import OmniGen2RotaryPosEmbed
20
+ from .src.schedulers.scheduling_flow_match_euler_discrete import (
21
+ FlowMatchEulerDiscreteScheduler as OmniFlowMatchEuler,
22
+ )
23
+ from PIL import Image
24
+ from transformers import (
25
+ CLIPProcessor,
26
+ Qwen2_5_VLForConditionalGeneration,
27
+ )
28
+ import torch.nn.functional as F
29
+
30
+ if TYPE_CHECKING:
31
+ from toolkit.data_transfer_object.data_loader import DataLoaderBatchDTO
32
+
33
+ scheduler_config = {"num_train_timesteps": 1000}
34
+
35
+ BASE_MODEL_PATH = "OmniGen2/OmniGen2"
36
+
37
+
38
+ class OmniGen2Model(BaseModel):
39
+ arch = "omnigen2"
40
+
41
+ def __init__(
42
+ self,
43
+ device,
44
+ model_config: ModelConfig,
45
+ dtype="bf16",
46
+ custom_pipeline=None,
47
+ noise_scheduler=None,
48
+ **kwargs,
49
+ ):
50
+ super().__init__(
51
+ device, model_config, dtype, custom_pipeline, noise_scheduler, **kwargs
52
+ )
53
+ self.is_flow_matching = True
54
+ self.is_transformer = True
55
+ self.target_lora_modules = ["OmniGen2Transformer2DModel"]
56
+ self._control_latent = None
57
+
58
+ # static method to get the noise scheduler
59
+ @staticmethod
60
+ def get_train_scheduler():
61
+ return CustomFlowMatchEulerDiscreteScheduler(**scheduler_config)
62
+
63
+ def get_bucket_divisibility(self):
64
+ return 16
65
+
66
+ def load_model(self):
67
+ dtype = self.torch_dtype
68
+ # HiDream-ai/HiDream-I1-Full
69
+ self.print_and_status_update("Loading OmniGen2 model")
70
+ # will be updated if we detect a existing checkpoint in training folder
71
+ model_path = self.model_config.name_or_path
72
+ extras_path = self.model_config.extras_name_or_path
73
+
74
+ scheduler = OmniGen2Model.get_train_scheduler()
75
+
76
+ self.print_and_status_update("Loading Qwen2.5 VL")
77
+ processor = CLIPProcessor.from_pretrained(
78
+ extras_path, subfolder="processor", use_fast=True
79
+ )
80
+
81
+ mllm = Qwen2_5_VLForConditionalGeneration.from_pretrained(
82
+ extras_path, subfolder="mllm", torch_dtype=torch.bfloat16
83
+ )
84
+ mllm.to(self.device_torch, dtype=dtype)
85
+ if self.model_config.quantize_te:
86
+ self.print_and_status_update("Quantizing Qwen2.5 VL model")
87
+ quantization_type = get_qtype(self.model_config.qtype_te)
88
+ quantize(mllm, weights=quantization_type)
89
+ freeze(mllm)
90
+
91
+ if self.low_vram:
92
+ # unload it for now
93
+ mllm.to("cpu")
94
+
95
+ flush()
96
+
97
+ self.print_and_status_update("Loading transformer")
98
+
99
+ transformer = OmniGen2Transformer2DModel.from_pretrained(
100
+ model_path, subfolder="transformer", torch_dtype=torch.bfloat16
101
+ )
102
+
103
+ if not self.low_vram:
104
+ transformer.to(self.device_torch, dtype=dtype)
105
+
106
+ if self.model_config.quantize:
107
+ self.print_and_status_update("Quantizing transformer")
108
+ quantization_type = get_qtype(self.model_config.qtype)
109
+ quantize(transformer, weights=quantization_type)
110
+ freeze(transformer)
111
+
112
+ if self.low_vram:
113
+ # unload it for now
114
+ transformer.to("cpu")
115
+
116
+ flush()
117
+
118
+ self.print_and_status_update("Loading vae")
119
+
120
+ vae = AutoencoderKL.from_pretrained(
121
+ extras_path, subfolder="vae", torch_dtype=torch.bfloat16
122
+ ).to(self.device_torch, dtype=dtype)
123
+
124
+ flush()
125
+ self.print_and_status_update("Loading Qwen2.5 VLProcessor")
126
+
127
+ flush()
128
+
129
+ if self.low_vram:
130
+ self.print_and_status_update("Moving everything to device")
131
+ # move it all back
132
+ transformer.to(self.device_torch, dtype=dtype)
133
+ vae.to(self.device_torch, dtype=dtype)
134
+ mllm.to(self.device_torch, dtype=dtype)
135
+
136
+ # set to eval mode
137
+ # transformer.eval()
138
+ vae.eval()
139
+ mllm.eval()
140
+ mllm.requires_grad_(False)
141
+
142
+ pipe: OmniGen2Pipeline = OmniGen2Pipeline(
143
+ transformer=transformer,
144
+ vae=vae,
145
+ scheduler=scheduler,
146
+ mllm=mllm,
147
+ processor=processor,
148
+ )
149
+
150
+ flush()
151
+
152
+ text_encoder_list = [mllm]
153
+ tokenizer_list = [processor]
154
+
155
+ flush()
156
+
157
+ # save it to the model class
158
+ self.vae = vae
159
+ self.text_encoder = text_encoder_list # list of text encoders
160
+ self.tokenizer = tokenizer_list # list of tokenizers
161
+ self.model = pipe.transformer
162
+ self.pipeline = pipe
163
+
164
+ self.freqs_cis = OmniGen2RotaryPosEmbed.get_freqs_cis(
165
+ transformer.config.axes_dim_rope,
166
+ transformer.config.axes_lens,
167
+ theta=10000,
168
+ )
169
+
170
+ self.print_and_status_update("Model Loaded")
171
+
172
+ def get_generation_pipeline(self):
173
+ scheduler = OmniFlowMatchEuler(
174
+ dynamic_time_shift=True, num_train_timesteps=1000
175
+ )
176
+
177
+ pipeline: OmniGen2Pipeline = OmniGen2Pipeline(
178
+ transformer=self.model,
179
+ vae=self.vae,
180
+ scheduler=scheduler,
181
+ mllm=self.text_encoder[0],
182
+ processor=self.tokenizer[0],
183
+ )
184
+
185
+ pipeline = pipeline.to(self.device_torch)
186
+
187
+ return pipeline
188
+
189
+ def generate_single_image(
190
+ self,
191
+ pipeline: OmniGen2Pipeline,
192
+ gen_config: GenerateImageConfig,
193
+ conditional_embeds: PromptEmbeds,
194
+ unconditional_embeds: PromptEmbeds,
195
+ generator: torch.Generator,
196
+ extra: dict,
197
+ ):
198
+ input_images = []
199
+ if gen_config.ctrl_img is not None:
200
+ control_img = Image.open(gen_config.ctrl_img)
201
+ control_img = control_img.convert("RGB")
202
+ # resize to width and height
203
+ if control_img.size != (gen_config.width, gen_config.height):
204
+ control_img = control_img.resize(
205
+ (gen_config.width, gen_config.height), Image.BILINEAR
206
+ )
207
+ input_images = [control_img]
208
+
209
+ img = pipeline(
210
+ prompt_embeds=conditional_embeds.text_embeds,
211
+ prompt_attention_mask=conditional_embeds.attention_mask,
212
+ negative_prompt_embeds=unconditional_embeds.text_embeds,
213
+ negative_prompt_attention_mask=unconditional_embeds.attention_mask,
214
+ height=gen_config.height,
215
+ width=gen_config.width,
216
+ num_inference_steps=gen_config.num_inference_steps,
217
+ text_guidance_scale=gen_config.guidance_scale,
218
+ image_guidance_scale=1.0, # reference image guidance scale. Add this for controls
219
+ latents=gen_config.latents,
220
+ align_res=False,
221
+ generator=generator,
222
+ input_images=input_images,
223
+ **extra,
224
+ ).images[0]
225
+ return img
226
+
227
+ def get_noise_prediction(
228
+ self,
229
+ latent_model_input: torch.Tensor,
230
+ timestep: torch.Tensor, # 0 to 1000 scale
231
+ text_embeddings: PromptEmbeds,
232
+ **kwargs,
233
+ ):
234
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
235
+ try:
236
+ timestep = timestep.expand(latent_model_input.shape[0]).to(
237
+ latent_model_input.dtype
238
+ )
239
+ except Exception as e:
240
+ pass
241
+
242
+ timesteps = timestep / 1000 # convert to 0 to 1 scale
243
+ # timestep for model starts at 0 instead of 1. So we need to reverse them
244
+ timestep = 1 - timesteps
245
+ model_pred = self.model(
246
+ latent_model_input,
247
+ timestep,
248
+ text_embeddings.text_embeds,
249
+ self.freqs_cis,
250
+ text_embeddings.attention_mask,
251
+ ref_image_hidden_states=self._control_latent,
252
+ )
253
+
254
+ return model_pred
255
+
256
+ def condition_noisy_latents(
257
+ self, latents: torch.Tensor, batch: "DataLoaderBatchDTO"
258
+ ):
259
+ # reset the control latent
260
+ self._control_latent = None
261
+ with torch.no_grad():
262
+ control_tensor = batch.control_tensor
263
+ if control_tensor is not None:
264
+ self.vae.to(self.device_torch)
265
+ # we are not packed here, so we just need to pass them so we can pack them later
266
+ control_tensor = control_tensor * 2 - 1
267
+ control_tensor = control_tensor.to(
268
+ self.vae_device_torch, dtype=self.torch_dtype
269
+ )
270
+
271
+ # if it is not the size of batch.tensor, (bs,ch,h,w) then we need to resize it
272
+ # todo, we may not need to do this, check
273
+ if batch.tensor is not None:
274
+ target_h, target_w = batch.tensor.shape[2], batch.tensor.shape[3]
275
+ else:
276
+ # When caching latents, batch.tensor is None. We get the size from the file_items instead.
277
+ target_h = batch.file_items[0].crop_height
278
+ target_w = batch.file_items[0].crop_width
279
+
280
+ if (
281
+ control_tensor.shape[2] != target_h
282
+ or control_tensor.shape[3] != target_w
283
+ ):
284
+ control_tensor = F.interpolate(
285
+ control_tensor, size=(target_h, target_w), mode="bilinear"
286
+ )
287
+
288
+ control_latent = self.encode_images(control_tensor).to(
289
+ latents.device, latents.dtype
290
+ )
291
+ self._control_latent = [
292
+ [x.squeeze(0)]
293
+ for x in torch.chunk(control_latent, control_latent.shape[0], dim=0)
294
+ ]
295
+
296
+ return latents.detach()
297
+
298
+ def get_prompt_embeds(self, prompt: str) -> PromptEmbeds:
299
+ prompt = [prompt] if isinstance(prompt, str) else prompt
300
+ prompt = [self.pipeline._apply_chat_template(_prompt) for _prompt in prompt]
301
+ self.text_encoder_to(self.device_torch, dtype=self.torch_dtype)
302
+ max_sequence_length = 256
303
+ prompt_embeds, prompt_attention_mask, _, _ = self.pipeline.encode_prompt(
304
+ prompt=prompt,
305
+ do_classifier_free_guidance=False,
306
+ device=self.device_torch,
307
+ max_sequence_length=max_sequence_length,
308
+ )
309
+ pe = PromptEmbeds(prompt_embeds)
310
+ pe.attention_mask = prompt_attention_mask
311
+ return pe
312
+
313
+ def get_model_has_grad(self):
314
+ # return from a weight if it has grad
315
+ return False
316
+
317
+ def get_te_has_grad(self):
318
+ # assume no one wants to finetune 4 text encoders.
319
+ return False
320
+
321
+ def save_model(self, output_path, meta, save_dtype):
322
+ # only save the transformer
323
+ transformer: OmniGen2Transformer2DModel = unwrap_model(self.model)
324
+ transformer.save_pretrained(
325
+ save_directory=os.path.join(output_path, "transformer"),
326
+ safe_serialization=True,
327
+ )
328
+
329
+ meta_path = os.path.join(output_path, "aitk_meta.yaml")
330
+ with open(meta_path, "w") as f:
331
+ yaml.dump(meta, f)
332
+
333
+ def get_loss_target(self, *args, **kwargs):
334
+ noise = kwargs.get("noise")
335
+ batch = kwargs.get("batch")
336
+ # return (noise - batch.latents).detach()
337
+ return (batch.latents - noise).detach()
338
+
339
+ def get_transformer_block_names(self) -> Optional[List[str]]:
340
+ # omnigen2 had a few blocks for things like noise_refiner, ref_image_refiner, context_refiner, and layers.
341
+ # lets do all but image refiner until we add it
342
+ if self.model_config.model_kwargs.get("use_image_refiner", False):
343
+ return ["noise_refiner", "context_refiner", "ref_image_refiner", "layers"]
344
+ return ["noise_refiner", "context_refiner", "layers"]
345
+
346
+ def convert_lora_weights_before_save(self, state_dict):
347
+ # currently starte with transformer. but needs to start with diffusion_model. for comfyui
348
+ new_sd = {}
349
+ for key, value in state_dict.items():
350
+ new_key = key.replace("transformer.", "diffusion_model.")
351
+ new_sd[new_key] = value
352
+ return new_sd
353
+
354
+ def convert_lora_weights_before_load(self, state_dict):
355
+ # saved as diffusion_model. but needs to be transformer. for ai-toolkit
356
+ new_sd = {}
357
+ for key, value in state_dict.items():
358
+ new_key = key.replace("diffusion_model.", "transformer.")
359
+ new_sd[new_key] = value
360
+ return new_sd
361
+
362
+ def get_base_model_version(self):
363
+ return "omnigen2"