TienVu2204 commited on
Commit
ed8f267
·
1 Parent(s): 086a810

upload file

Browse files
Files changed (40) hide show
  1. README.md +3 -3
  2. app.py +479 -0
  3. configs/datasets/deepgen_512_fix_pixels/cc12m.py +26 -0
  4. configs/datasets/deepgen_512_fix_pixels/edit_pretrain.py +128 -0
  5. configs/datasets/deepgen_512_fix_pixels/edit_sft_zh.py +128 -0
  6. configs/datasets/deepgen_512_fix_pixels/joint_pretrain.py +47 -0
  7. configs/datasets/deepgen_512_fix_pixels/joint_sft_zh.py +47 -0
  8. configs/datasets/deepgen_512_fix_pixels/laion6m.py +27 -0
  9. configs/datasets/deepgen_512_fix_pixels/megalith10m.py +27 -0
  10. configs/datasets/deepgen_512_fix_pixels/processors.py +41 -0
  11. configs/datasets/deepgen_512_fix_pixels/redcaps5m.py +27 -0
  12. configs/datasets/deepgen_512_fix_pixels/t2i_pretrain.py +35 -0
  13. configs/datasets/deepgen_512_fix_pixels/t2i_sft_zh.py +97 -0
  14. configs/datasets/deepgen_512_fix_pixels/text2image2m.py +42 -0
  15. configs/finetune/deepgen_joint_sft.py +115 -0
  16. configs/finetune/deepgen_joint_sft_scb.py +114 -0
  17. configs/models/deepgen.py +71 -0
  18. configs/models/deepgen_scb.py +71 -0
  19. configs/pretrain/deepgen_joint_pretrain.py +114 -0
  20. configs/pretrain/deepgen_joint_pretrain_scb.py +112 -0
  21. requirements.txt +13 -0
  22. src/datasets/collate_functions.py +228 -0
  23. src/datasets/image2image/edit_datasets.py +88 -0
  24. src/datasets/samplers/multi_source_sampler.py +203 -0
  25. src/datasets/text2image/blip3_o.py +49 -0
  26. src/datasets/text2image/caption_datasets.py +226 -0
  27. src/datasets/utils.py +186 -0
  28. src/models/connector/__init__.py +2 -0
  29. src/models/connector/configuration_connector.py +27 -0
  30. src/models/connector/modeling_connector.py +507 -0
  31. src/models/connector/modeling_qwen2.py +50 -0
  32. src/models/sd3_kontext/pipeline_stable_diffusion_3.py +1256 -0
  33. src/models/sd3_kontext/pipeline_stable_diffusion_3_dynamic.py +1257 -0
  34. src/models/sd3_kontext/qwen2_5_vl_sd3_hf_dynamic.py +792 -0
  35. src/models/sd3_kontext/qwen2_5_vl_sd3_hf_dynamic_fusion.py +824 -0
  36. src/models/sd3_kontext/sd3_hf.py +486 -0
  37. src/models/sd3_kontext/sd3_hf_dynamic.py +353 -0
  38. src/models/sd3_kontext/transformer_sd3_dynamic.py +639 -0
  39. src/optimisers/custom_adamw.py +45 -0
  40. src/runners/custom_runner.py +177 -0
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
  title: DeepGen Test
3
- emoji: 🐠
4
- colorFrom: yellow
5
- colorTo: yellow
6
  sdk: gradio
7
  sdk_version: 6.6.0
8
  app_file: app.py
 
1
  ---
2
  title: DeepGen Test
3
+ emoji: 📚
4
+ colorFrom: red
5
+ colorTo: gray
6
  sdk: gradio
7
  sdk_version: 6.6.0
8
  app_file: app.py
app.py ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spaces
2
+ import os
3
+ import sys
4
+ import subprocess
5
+ import importlib.util
6
+
7
+ # --- CÀI ĐẶT ÉP BUỘC XTUNER (Bỏ qua kiểm tra xung đột) ---
8
+ if importlib.util.find_spec("xtuner") is None:
9
+ print("Đang cài đặt xtuner bằng lệnh ngầm...")
10
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "xtuner==0.2.0", "--no-deps"])
11
+
12
+ import torch
13
+ import torch.utils._pytree as _torch_pytree
14
+
15
+ # 1. Vá lỗi PyTree cho Transformers mới
16
+ def smart_pytree_patch():
17
+ orig_func = getattr(_torch_pytree, '_register_pytree_node', None)
18
+ if orig_func:
19
+ def patched_register(cls, to_iter, from_iter, serialized_type_name=None):
20
+ return orig_func(cls, to_iter, from_iter)
21
+ _torch_pytree.register_pytree_node = patched_register
22
+ _torch_pytree._register_pytree_node = patched_register
23
+
24
+ smart_pytree_patch()
25
+
26
+ import torch.distributed
27
+
28
+ # 2. Vá lỗi Torch XPU
29
+ if not hasattr(torch, 'xpu'):
30
+ class DummyXPU:
31
+ @staticmethod
32
+ def is_available(): return False
33
+ @staticmethod
34
+ def empty_cache(): pass
35
+ @staticmethod
36
+ def device_count(): return 0
37
+ @staticmethod
38
+ def current_device(): return 0
39
+ @staticmethod
40
+ def get_device_name(device=None): return "DummyXPU"
41
+ @staticmethod
42
+ def is_bf16_supported(): return False
43
+ @staticmethod
44
+ def synchronize(device=None): pass
45
+ @staticmethod
46
+ def set_device(device): pass
47
+ @staticmethod
48
+ def manual_seed(seed): pass
49
+ @staticmethod
50
+ def manual_seed_all(seed): pass
51
+ @staticmethod
52
+ def seed(): pass
53
+ @staticmethod
54
+ def seed_all(): pass
55
+ @staticmethod
56
+ def initial_seed(): return 0
57
+ torch.xpu = DummyXPU()
58
+
59
+ # 3. Vá lỗi Device Mesh
60
+ if not hasattr(torch.distributed, 'device_mesh'):
61
+ class DummyDeviceMesh: pass
62
+ class DummyDeviceMeshModule: DeviceMesh = DummyDeviceMesh
63
+ dummy_module = DummyDeviceMeshModule()
64
+ sys.modules['torch.distributed.device_mesh'] = dummy_module
65
+ torch.distributed.device_mesh = dummy_module
66
+
67
+ # 4. Vá lỗi pad_sequence() không hỗ trợ padding_side (cần PyTorch >= 2.5)
68
+ # ---------------------------------------------------------------
69
+ import torch.nn.utils.rnn as _rnn
70
+ _orig_pad_sequence = _rnn.pad_sequence
71
+
72
+ def _patched_pad_sequence(sequences, batch_first=False, padding_value=0.0, padding_side='right'):
73
+ if padding_side == 'left':
74
+ sequences = [seq.flip(0) for seq in sequences]
75
+ padded = _orig_pad_sequence(sequences, batch_first=batch_first, padding_value=padding_value)
76
+ flip_dim = 1 if batch_first else 0
77
+ return padded.flip(flip_dim)
78
+ else:
79
+ return _orig_pad_sequence(sequences, batch_first=batch_first, padding_value=padding_value)
80
+
81
+ _rnn.pad_sequence = _patched_pad_sequence
82
+ torch.nn.utils.rnn.pad_sequence = _patched_pad_sequence
83
+
84
+ import importlib, sys as _sys
85
+ def _patch_pad_sequence_in_module(module_name):
86
+ mod = _sys.modules.get(module_name)
87
+ if mod and hasattr(mod, 'pad_sequence'):
88
+ mod.pad_sequence = _patched_pad_sequence
89
+
90
+ import builtins as _builtins
91
+ _orig_builtins_import = _builtins.__import__
92
+
93
+ def _import_hook(name, *args, **kwargs):
94
+ module = _orig_builtins_import(name, *args, **kwargs)
95
+ for mod_name, mod in list(_sys.modules.items()):
96
+ if mod_name.startswith('src.') and hasattr(mod, 'pad_sequence'):
97
+ mod.pad_sequence = _patched_pad_sequence
98
+ return module
99
+
100
+ _builtins.__import__ = _import_hook
101
+ print("✅ Đã patch pad_sequence() để hỗ trợ padding_side")
102
+
103
+ # 5. Vá lỗi F.interpolate bilinear nhận 3D input thay vì 4D
104
+ # ---------------------------------------------------------------
105
+ import torch.nn.functional as _F
106
+ _orig_interpolate = _F.interpolate
107
+
108
+ def _patched_interpolate(input, size=None, scale_factor=None, mode='nearest',
109
+ align_corners=None, recompute_scale_factor=None, antialias=False):
110
+ squeezed = False
111
+ if mode in ('bilinear', 'bicubic') and input.dim() == 3:
112
+ input = input.unsqueeze(0)
113
+ squeezed = True
114
+ result = _orig_interpolate(
115
+ input, size=size, scale_factor=scale_factor, mode=mode,
116
+ align_corners=align_corners, recompute_scale_factor=recompute_scale_factor,
117
+ antialias=antialias
118
+ )
119
+ if squeezed:
120
+ result = result.squeeze(0)
121
+ return result
122
+
123
+ _F.interpolate = _patched_interpolate
124
+ torch.nn.functional.interpolate = _patched_interpolate
125
+ print("✅ Đã patch F.interpolate() để hỗ trợ 3D input với bilinear mode")
126
+
127
+ # 6. Vá lỗi vae.encode() / vae.decode() nhận 3D input thay vì 4D
128
+ # ---------------------------------------------------------------
129
+ try:
130
+ from diffusers.models.autoencoders.autoencoder_kl import AutoencoderKL as _AutoencoderKL
131
+ _orig_vae_encode = _AutoencoderKL.encode
132
+ _orig_vae_decode = _AutoencoderKL.decode
133
+
134
+ def _patched_vae_encode(self, x, *args, **kwargs):
135
+ if x.dim() == 3:
136
+ x = x.unsqueeze(0)
137
+ if x.dim() == 4 and x.shape[1] == 1:
138
+ x = x.repeat(1, 3, 1, 1)
139
+ elif x.dim() == 4 and x.shape[1] == 4:
140
+ x = x[:, :3, :, :]
141
+ result = _orig_vae_encode(self, x, *args, **kwargs)
142
+ return result
143
+
144
+ def _patched_vae_decode(self, z, *args, **kwargs):
145
+ if z.dim() == 3:
146
+ z = z.unsqueeze(0)
147
+ return _orig_vae_decode(self, z, *args, **kwargs)
148
+
149
+ _AutoencoderKL.encode = _patched_vae_encode
150
+ _AutoencoderKL.decode = _patched_vae_decode
151
+ print("✅ Đã patch AutoencoderKL.encode/decode() để hỗ trợ 3D input")
152
+ except Exception as _e:
153
+ print(f"⚠️ Không thể patch AutoencoderKL: {_e}")
154
+
155
+ # 7. Vá lỗi dtype string + flash_attention_2
156
+ # ---------------------------------------------------------------
157
+ STRING_TO_TORCH_DTYPE = {
158
+ "float16": torch.float16,
159
+ "float32": torch.float32,
160
+ "float64": torch.float64,
161
+ "bfloat16": torch.bfloat16,
162
+ "torch.bfloat16": torch.bfloat16,
163
+ "half": torch.float16,
164
+ "int8": torch.int8,
165
+ }
166
+
167
+ def str_to_torch_dtype(dtype):
168
+ if isinstance(dtype, str):
169
+ return STRING_TO_TORCH_DTYPE.get(dtype.lower(), torch.float32)
170
+ return dtype
171
+
172
+ try:
173
+ from transformers import PretrainedConfig
174
+ original_pretrained_init = PretrainedConfig.__init__
175
+
176
+ def patched_pretrained_config_init(self, *args, **kwargs):
177
+ if 'torch_dtype' in kwargs and isinstance(kwargs['torch_dtype'], str):
178
+ kwargs['torch_dtype'] = str_to_torch_dtype(kwargs['torch_dtype'])
179
+ if kwargs.get('attn_implementation') == 'flash_attention_2':
180
+ kwargs['attn_implementation'] = 'sdpa'
181
+ original_pretrained_init(self, *args, **kwargs)
182
+ if hasattr(self, 'torch_dtype') and isinstance(self.torch_dtype, str):
183
+ self.torch_dtype = str_to_torch_dtype(self.torch_dtype)
184
+ if getattr(self, '_attn_implementation', None) == 'flash_attention_2':
185
+ self._attn_implementation = 'sdpa'
186
+ if getattr(self, '_attn_implementation_internal', None) == 'flash_attention_2':
187
+ self._attn_implementation_internal = 'sdpa'
188
+
189
+ PretrainedConfig.__init__ = patched_pretrained_config_init
190
+ print("✅ Đã patch PretrainedConfig.__init__")
191
+ except Exception as e:
192
+ print(f"⚠️ Không thể patch PretrainedConfig: {e}")
193
+
194
+ orig_is_floating_point = torch.is_floating_point
195
+ def patched_is_floating_point(obj):
196
+ if isinstance(obj, str):
197
+ return obj.lower() in ["bfloat16", "float16", "float32", "float64", "half"]
198
+ return orig_is_floating_point(obj)
199
+ torch.is_floating_point = patched_is_floating_point
200
+
201
+ # ============================================================
202
+ # IMPORTS CHÍNH
203
+ # ============================================================
204
+ import time
205
+ import psutil
206
+ import numpy as np
207
+ import gradio as gr
208
+ from PIL import Image
209
+ from einops import rearrange
210
+ from huggingface_hub import hf_hub_download
211
+
212
+ from xtuner.registry import BUILDER
213
+ from mmengine.config import Config
214
+
215
+ # ============================================================
216
+ # CONFIG PATCHES
217
+ # ============================================================
218
+ LOCAL_TO_HF_PATH = {
219
+ "model_zoo/Qwen2.5-VL-3B-Instruct": "Qwen/Qwen2.5-VL-3B-Instruct",
220
+ "model_zoo/UniPic2-SD3.5M-Kontext-2B": "Skywork/UniPic2-SD3.5M-Kontext-2B",
221
+ }
222
+
223
+ def patch_config_paths(cfg):
224
+ cfg_text = cfg.pretty_text
225
+ changed = False
226
+ for local_path, hf_path in LOCAL_TO_HF_PATH.items():
227
+ if local_path in cfg_text:
228
+ cfg_text = cfg_text.replace(local_path, hf_path)
229
+ print(f" → Đã thay path: '{local_path}' → '{hf_path}'")
230
+ changed = True
231
+ if changed:
232
+ return Config.fromstring(cfg_text, file_format='.py')
233
+ return cfg
234
+
235
+ def patch_config_dtype(cfg):
236
+ if isinstance(cfg, dict):
237
+ for key in list(cfg.keys()):
238
+ val = cfg[key]
239
+ if key in ('torch_dtype', 'dtype', 'param_dtype', 'compute_dtype') and isinstance(val, str):
240
+ cfg[key] = str_to_torch_dtype(val)
241
+ print(f" → Convert dtype cfg['{key}'] = '{val}' → {cfg[key]}")
242
+ elif key == 'attn_implementation' and val == 'flash_attention_2':
243
+ cfg[key] = 'sdpa'
244
+ print(f" → Thay attn_implementation: flash_attention_2 → sdpa")
245
+ else:
246
+ patch_config_dtype(val)
247
+ elif isinstance(cfg, (list, tuple)):
248
+ for item in cfg:
249
+ patch_config_dtype(item)
250
+ elif hasattr(cfg, '_cfg_dict'):
251
+ patch_config_dtype(cfg._cfg_dict)
252
+ return cfg
253
+
254
+ from xtuner.model.utils import guess_load_checkpoint
255
+
256
+ REPO_ID = "oedevs/DeepGen-1.0"
257
+ MODEL_WEIGHTS = {
258
+ "Pretrain (Alignment)": "iter_200000.pth",
259
+ "RL with MR-GRPO (Tốt nhất)": "model.pt"
260
+ }
261
+
262
+ current_loaded_method = None
263
+ model = None
264
+
265
+ def load_model(method_name):
266
+ global current_loaded_method, model
267
+ if current_loaded_method == method_name and model is not None:
268
+ return model
269
+ print(f"Đang chuẩn bị tải và nạp model: {method_name}...")
270
+ filename = MODEL_WEIGHTS[method_name]
271
+ weight_path = hf_hub_download(repo_id=REPO_ID, filename=filename)
272
+ config = Config.fromfile("configs/models/deepgen_scb.py")
273
+ print("Đang patch đường dẫn config...")
274
+ config = patch_config_paths(config)
275
+ print("Đang patch dtype và attn_implementation...")
276
+ patch_config_dtype(config)
277
+ new_model = BUILDER.build(config.model)
278
+ if weight_path.endswith('.pt'):
279
+ state_dict = torch.load(weight_path, map_location="cpu")
280
+ else:
281
+ state_dict = guess_load_checkpoint(weight_path)
282
+ new_model.load_state_dict(state_dict, strict=False)
283
+ model_dtype = new_model.dtype
284
+ if isinstance(model_dtype, str):
285
+ model_dtype = str_to_torch_dtype(model_dtype)
286
+ new_model = new_model.to(model_dtype)
287
+ new_model.eval()
288
+ model = new_model
289
+ current_loaded_method = method_name
290
+ return model
291
+
292
+ def _process_image(image):
293
+ image = image.convert('RGB')
294
+ image = image.resize(size=(512, 512))
295
+ pixel_values = torch.from_numpy(np.array(image)).float()
296
+ pixel_values = pixel_values / 255
297
+ pixel_values = 2 * pixel_values - 1
298
+ pixel_values = rearrange(pixel_values, 'h w c -> c h w')
299
+ return pixel_values
300
+
301
+ # ============================================================
302
+ # HELPER: ĐO RAM CPU & VRAM GPU
303
+ # ============================================================
304
+ def _get_ram_mb():
305
+ return psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024
306
+
307
+ def _get_vram_mb():
308
+ return torch.cuda.memory_allocated() / 1024 / 1024 if torch.cuda.is_available() else 0.0
309
+
310
+ def _get_vram_reserved_mb():
311
+ return torch.cuda.memory_reserved() / 1024 / 1024 if torch.cuda.is_available() else 0.0
312
+
313
+ def _log_resources(label: str):
314
+ ram = _get_ram_mb()
315
+ vram_alloc = _get_vram_mb()
316
+ vram_reserved = _get_vram_reserved_mb()
317
+ print(f" 📊 [{label}]"
318
+ f" RAM: {ram:.0f} MB"
319
+ f" | VRAM alloc: {vram_alloc:.0f} MB"
320
+ f" | VRAM reserved: {vram_reserved:.0f} MB")
321
+
322
+ # ============================================================
323
+ # INFERENCE
324
+ # ============================================================
325
+ @spaces.GPU(duration=120)
326
+ def run_inference(task_type, prompt, cfg_prompt, cfg_scale, num_steps, seed, method, src_img=None):
327
+ t_total_start = time.perf_counter()
328
+ mode_label = "Text-to-Image" if task_type == "t2i" else "Image-Editing"
329
+
330
+ print(f"\n{'='*60}")
331
+ print(f"🚀 BẮT ĐẦU [{mode_label}] steps={num_steps} cfg={cfg_scale} seed={seed}")
332
+ print(f" Prompt: {prompt[:100]}{'...' if len(prompt) > 100 else ''}")
333
+ print(f"{'='*60}")
334
+
335
+ # Reset peak VRAM counter cho lần chạy này
336
+ if torch.cuda.is_available():
337
+ torch.cuda.reset_peak_memory_stats()
338
+
339
+ _log_resources("Khởi đầu")
340
+
341
+ try:
342
+ # ── 1. Load model ──────────────────────────────────────
343
+ t0 = time.perf_counter()
344
+ net = load_model(method)
345
+ net = net.to("cuda")
346
+ if torch.cuda.is_available():
347
+ torch.cuda.synchronize()
348
+ t_load = time.perf_counter() - t0
349
+ print(f" ⏱️ Load/move model to CUDA : {t_load:.2f}s")
350
+ _log_resources("Sau load model")
351
+
352
+ generator = torch.Generator(device=net.device).manual_seed(int(seed))
353
+ prompts = [prompt.strip()]
354
+ cfg_prompts = [cfg_prompt]
355
+
356
+ # ── 2. Tiền xử lý ảnh nguồn (chỉ i2i) ────────────────
357
+ t_img = 0.0
358
+ pixel_values_src = None
359
+ if task_type == "i2i" and src_img is not None:
360
+ t0 = time.perf_counter()
361
+ processed_img = _process_image(src_img).to(net.dtype).to(net.device)
362
+ pixel_values_src = processed_img.unsqueeze(0)
363
+ t_img = time.perf_counter() - t0
364
+ print(f" ⏱️ Tiền xử lý ảnh nguồn : {t_img:.3f}s")
365
+ _log_resources("Sau tiền xử lý ảnh")
366
+
367
+ # ── 3. Generate (bước nặng) ────────────────────────────
368
+ _log_resources("Trước generate")
369
+ t0 = time.perf_counter()
370
+ with torch.no_grad():
371
+ images = net.generate(
372
+ prompt=prompts,
373
+ cfg_prompt=cfg_prompts,
374
+ pixel_values_src=pixel_values_src,
375
+ cfg_scale=cfg_scale,
376
+ num_steps=int(num_steps),
377
+ progress_bar=False,
378
+ generator=generator,
379
+ height=512,
380
+ width=512
381
+ )
382
+ if torch.cuda.is_available():
383
+ torch.cuda.synchronize()
384
+ t_gen = time.perf_counter() - t0
385
+ print(f" ⏱️ Generate ({num_steps} steps) : {t_gen:.2f}s "
386
+ f"({t_gen / int(num_steps) * 1000:.1f} ms/step)")
387
+ _log_resources("Sau generate")
388
+
389
+ # ── 4. Post-process ────────────────────────────────────
390
+ t0 = time.perf_counter()
391
+ images = rearrange(images, 'b c h w -> b h w c')
392
+ images = torch.clamp(127.5 * images + 128.0, 0, 255).to("cpu", dtype=torch.uint8).numpy()
393
+ t_post = time.perf_counter() - t0
394
+ print(f" ⏱️ Post-process : {t_post:.3f}s")
395
+
396
+ # ── 5. Offload về CPU (ZeroGPU rule) ──────────────────
397
+ net = net.to("cpu")
398
+ torch.cuda.empty_cache()
399
+
400
+ # ── Tổng kết ──────────────────────────────────────────
401
+ t_total = time.perf_counter() - t_total_start
402
+ vram_peak = torch.cuda.max_memory_allocated() / 1024 / 1024 if torch.cuda.is_available() else 0
403
+ vram_now = _get_vram_mb()
404
+ ram_now = _get_ram_mb()
405
+
406
+ sep = "=" * 60
407
+ print(f"\n{sep}")
408
+ print(f"✅ KẾT QUẢ [{mode_label}]")
409
+ print(f" ┌─ Thời gian ──────────────────────────────")
410
+ print(f" │ Tổng : {t_total:.2f}s")
411
+ print(f" │ Load model : {t_load:.2f}s")
412
+ if task_type == "i2i" and src_img is not None:
413
+ print(f" │ Tiền xử lý ảnh : {t_img:.3f}s")
414
+ print(f" │ Generate : {t_gen:.2f}s ({t_gen/int(num_steps)*1000:.1f} ms/step)")
415
+ print(f" │ Post-process : {t_post:.3f}s")
416
+ print(f" ├─ Bộ nhớ ────────────────────────────────")
417
+ print(f" │ RAM CPU hiện tại : {ram_now:.0f} MB")
418
+ print(f" │ VRAM peak : {vram_peak:.0f} MB")
419
+ print(f" │ VRAM hiện tại : {vram_now:.0f} MB")
420
+ print(f" └──────────────────────────────────────────")
421
+ print(f"{sep}\n")
422
+
423
+ return Image.fromarray(images[0])
424
+
425
+ except Exception as e:
426
+ import traceback
427
+ t_total = time.perf_counter() - t_total_start
428
+ print(f"\n❌ LỖI sau {t_total:.2f}s [{mode_label}]: {str(e)}")
429
+ traceback.print_exc()
430
+ return None
431
+
432
+ # --- GIAO DIỆN GRADIO ---
433
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
434
+ gr.Markdown("# 🚀 DeepGen-1.0 Demo trên ZeroGPU")
435
+
436
+ method_dropdown = gr.Dropdown(
437
+ choices=list(MODEL_WEIGHTS.keys()),
438
+ value="RL with MR-GRPO (Tốt nhất)",
439
+ label="Cấu hình Model / Weights"
440
+ )
441
+
442
+ with gr.Tabs():
443
+ with gr.Tab("🖼️ Text-to-Image"):
444
+ with gr.Row():
445
+ with gr.Column():
446
+ t2i_prompt = gr.Textbox(label="Prompt", value="A quiet bookstore with a sign that says 'READ'. A coffee cup on the table with the word 'MORNING'.")
447
+ t2i_cfg_prompt = gr.Textbox(label="Negative / CFG Prompt", value="")
448
+ t2i_scale = gr.Slider(1.0, 10.0, value=4.0, label="CFG Scale")
449
+ t2i_steps = gr.Slider(10, 100, value=50, step=1, label="Steps")
450
+ t2i_seed = gr.Number(value=42, label="Seed")
451
+ t2i_btn = gr.Button("Tạo ảnh", variant="primary")
452
+ with gr.Column():
453
+ t2i_output = gr.Image(label="Kết quả")
454
+ t2i_btn.click(
455
+ fn=lambda *args: run_inference("t2i", *args),
456
+ inputs=[t2i_prompt, t2i_cfg_prompt, t2i_scale, t2i_steps, t2i_seed, method_dropdown],
457
+ outputs=t2i_output
458
+ )
459
+
460
+ with gr.Tab("🎨 Image Editing"):
461
+ with gr.Row():
462
+ with gr.Column():
463
+ i2i_src = gr.Image(label="Ảnh nguồn (Source Image)", type="pil")
464
+ i2i_prompt = gr.Textbox(label="Editing Prompt", value="Transform into a vibrant 1980s concert poster with bold colors. Keep the band members and text, add warm orange and yellow tones, retro typography style.")
465
+ i2i_cfg_prompt = gr.Textbox(label="Negative Prompt", value="blurry, distorted faces, extra limbs, text errors, low quality, oversaturated")
466
+ i2i_scale = gr.Slider(1.0, 10.0, value=4.0, label="CFG Scale")
467
+ i2i_steps = gr.Slider(10, 100, value=20, step=1, label="Steps")
468
+ i2i_seed = gr.Number(value=42, label="Seed")
469
+ i2i_btn = gr.Button("Chỉnh sửa ảnh", variant="primary")
470
+ with gr.Column():
471
+ i2i_output = gr.Image(label="Kết quả chỉnh sửa")
472
+ i2i_btn.click(
473
+ fn=lambda src, p, cfg, s, st, sd, m: run_inference("i2i", p, cfg, s, st, sd, m, src),
474
+ inputs=[i2i_src, i2i_prompt, i2i_cfg_prompt, i2i_scale, i2i_steps, i2i_seed, method_dropdown],
475
+ outputs=i2i_output
476
+ )
477
+
478
+ # KHÔNG XÓA DÒNG NÀY
479
+ demo.launch()
configs/datasets/deepgen_512_fix_pixels/cc12m.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmengine.config import read_base
2
+ from mmengine.dataset import InfiniteSampler
3
+ from src.datasets.collate_functions import collate_func_gen_txt_dynamic
4
+ from src.datasets.text2image.caption_datasets import CaptionDataset
5
+
6
+
7
+ with read_base():
8
+ from .processors import image_size, image_process
9
+
10
+ dataset = dict(type=CaptionDataset,
11
+ image_size=image_size,
12
+ image_process=image_process,
13
+ cap_folder='data/cc12m/captions',
14
+ data_path='data/cc12m/data.json',
15
+ image_folder='data/cc12m/raw',
16
+ ceph_folder=None,
17
+ ceph_config=None)
18
+
19
+ train_dataloader = dict(
20
+ batch_size=8,
21
+ num_workers=4,
22
+ pin_memory=True,
23
+ dataset=dataset,
24
+ sampler=dict(type=InfiniteSampler, shuffle=True),
25
+ collate_fn=dict(type=collate_func_gen_txt_dynamic)
26
+ )
configs/datasets/deepgen_512_fix_pixels/edit_pretrain.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.datasets.collate_functions import collate_func_img2img_txt_dynamic
2
+ from mmengine.config import read_base
3
+ from mmengine.dataset import InfiniteSampler
4
+ from xtuner.dataset import ConcatDataset
5
+ from src.datasets.image2image.edit_datasets import ImageEditDataset, ReconstructDataset
6
+ from PIL import Image
7
+ with read_base():
8
+ from .processors import image_size, image_process
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+ Open4oEdit = dict(type=ImageEditDataset,
18
+ image_size=image_size,
19
+ image_process=image_process,
20
+ data_path='data/OpenGPT-4o-Image/editing.json',
21
+ image_folder = "data/OpenGPT-4o-Image/editing" ,
22
+ ceph_folder=None,
23
+ ceph_config=None)
24
+
25
+ Share4oEdit = dict(type=ImageEditDataset,
26
+ image_size=image_size,
27
+ image_process=image_process,
28
+ data_path='data/ShareGPT-4o-Image/text_and_image_to_image.json',
29
+ image_folder = "data/ShareGPT-4o-Image/editing" ,
30
+ ceph_folder=None,
31
+ ceph_config=None)
32
+
33
+ nano150Edit = dict(type=ImageEditDataset,
34
+ image_size=image_size,
35
+ image_process=image_process,
36
+ data_path='data/Nano-150k/data.json',
37
+ image_folder = "data/Nano-150k" ,
38
+ ceph_folder=None,
39
+ ceph_config=None)
40
+
41
+ picobananaEdit = dict(type=ImageEditDataset,
42
+ image_size=image_size,
43
+ image_process=image_process,
44
+ data_path='data/pico-banana/sft_with_local_source_image_path.json',
45
+ image_folder = "data/pico-banana" ,
46
+ ceph_folder=None,
47
+ ceph_config=None)
48
+
49
+
50
+ UniworldEdit = dict(type=ImageEditDataset,
51
+ image_size=image_size,
52
+ image_process=image_process,
53
+ data_path='data/UniWorld-V1-new/UniworldEdit.json',
54
+ image_folder = "data/UniWorld-V1-new" ,
55
+ ceph_folder=None,
56
+ ceph_config=None)
57
+
58
+
59
+
60
+ GPT4oEdit_hqedit = dict(type=ImageEditDataset,
61
+ image_size=image_size,
62
+ image_process=image_process,
63
+ data_path='data/GPT-Image-Edit-1.5M/gpt-edit/hqedit/hqedit.json',
64
+ image_folder = "data/GPT-Image-Edit-1.5M" ,
65
+ ceph_folder=None,
66
+ ceph_config=None)
67
+
68
+ GPT4oEdit_omniedit = dict(type=ImageEditDataset,
69
+ image_size=image_size,
70
+ image_process=image_process,
71
+ data_path='data/GPT-Image-Edit-1.5M/gpt-edit/omniedit/omniedit.json',
72
+ image_folder = "data/GPT-Image-Edit-1.5M" ,
73
+ ceph_folder=None,
74
+ ceph_config=None)
75
+
76
+ GPT4oEdit_ultraedit = dict(type=ImageEditDataset,
77
+ image_size=image_size,
78
+ image_process=image_process,
79
+ data_path='data/GPT-Image-Edit-1.5M/gpt-edit/ultraedit/ultraedit.json',
80
+ image_folder = "data/GPT-Image-Edit-1.5M" ,
81
+ ceph_folder=None,
82
+ ceph_config=None)
83
+
84
+ Reason_edit = dict(type=ImageEditDataset,
85
+ image_size=image_size,
86
+ image_process=image_process,
87
+ data_path='data/unireason/reson_edit.json',
88
+ image_folder = "data/unireason/edit" ,
89
+ ceph_folder=None,
90
+ ceph_config=None)
91
+
92
+ Omnigen_edit = dict(type=ImageEditDataset,
93
+ image_size=image_size,
94
+ image_process=image_process,
95
+ data_path='data/X2I2/Omnigen.json',
96
+ image_folder = "data/X2I2/images" ,
97
+ ceph_folder=None,
98
+ ceph_config=None)
99
+
100
+ nhr_edit_1 = dict(type=ImageEditDataset,
101
+ image_size=image_size,
102
+ image_process=image_process,
103
+ data_path='data/NHR-Edit/NHR-Edit_1.json',
104
+ image_folder = "data/NHR-Edit" ,
105
+ ceph_folder=None,
106
+ ceph_config=None)
107
+
108
+ nhr_edit_2 = dict(type=ImageEditDataset,
109
+ image_size=image_size,
110
+ image_process=image_process,
111
+ data_path='data/NHR-Edit-part2/NHR-Edit_2.json',
112
+ image_folder = "data/NHR-Edit-part2" ,
113
+ ceph_folder=None,
114
+ ceph_config=None)
115
+
116
+ dataset = dict(
117
+ type=ConcatDataset,
118
+ datasets=[Open4oEdit,Share4oEdit,nano150Edit,picobananaEdit,UniworldEdit,GPT4oEdit_hqedit,GPT4oEdit_omniedit,GPT4oEdit_ultraedit,Reason_edit,Omnigen_edit,nhr_edit_1,nhr_edit_2],
119
+ )
120
+
121
+ train_dataloader = dict(
122
+ batch_size=16,
123
+ num_workers=4,
124
+ pin_memory=True,
125
+ dataset=dataset,
126
+ sampler=dict(type=InfiniteSampler, shuffle=True),
127
+ collate_fn=dict(type=collate_func_img2img_txt_dynamic)
128
+ )
configs/datasets/deepgen_512_fix_pixels/edit_sft_zh.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.datasets.collate_functions import collate_func_img2img_txt_dynamic
2
+ from mmengine.config import read_base
3
+ from mmengine.dataset import InfiniteSampler
4
+ from xtuner.dataset import ConcatDataset
5
+ from src.datasets.image2image.edit_datasets import ImageEditDataset, ReconstructDataset
6
+ from PIL import Image
7
+ with read_base():
8
+ from .processors import image_size, image_process
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+ Open4oEdit = dict(type=ImageEditDataset,
18
+ image_size=image_size,
19
+ image_process=image_process,
20
+ data_path='data/OpenGPT-4o-Image/editing.json',
21
+ image_folder = "data/OpenGPT-4o-Image/editing" ,
22
+ ceph_folder=None,
23
+ ceph_config=None)
24
+
25
+ Share4oEdit = dict(type=ImageEditDataset,
26
+ image_size=image_size,
27
+ image_process=image_process,
28
+ data_path='data/ShareGPT-4o-Image/text_and_image_to_image.json',
29
+ image_folder = "data/ShareGPT-4o-Image/editing" ,
30
+ ceph_folder=None,
31
+ ceph_config=None)
32
+
33
+ nano150Edit = dict(type=ImageEditDataset,
34
+ image_size=image_size,
35
+ image_process=image_process,
36
+ data_path='data/Nano-150k/data.json',
37
+ image_folder = "data/Nano-150k" ,
38
+ ceph_folder=None,
39
+ ceph_config=None)
40
+
41
+ picobananaEdit = dict(type=ImageEditDataset,
42
+ image_size=image_size,
43
+ image_process=image_process,
44
+ data_path='data/pico-banana/sft_with_local_source_image_path.json',
45
+ image_folder = "data/pico-banana" ,
46
+ ceph_folder=None,
47
+ ceph_config=None)
48
+
49
+
50
+ UniworldEdit = dict(type=ImageEditDataset,
51
+ image_size=image_size,
52
+ image_process=image_process,
53
+ data_path='data/UniWorld-V1-new/UniworldEdit.json',
54
+ image_folder = "data/UniWorld-V1-new" ,
55
+ ceph_folder=None,
56
+ ceph_config=None)
57
+
58
+
59
+
60
+ GPT4oEdit_hqedit = dict(type=ImageEditDataset,
61
+ image_size=image_size,
62
+ image_process=image_process,
63
+ data_path='data/GPT-Image-Edit-1.5M/gpt-edit/hqedit/hqedit.json',
64
+ image_folder = "data/GPT-Image-Edit-1.5M" ,
65
+ ceph_folder=None,
66
+ ceph_config=None)
67
+
68
+ GPT4oEdit_omniedit = dict(type=ImageEditDataset,
69
+ image_size=image_size,
70
+ image_process=image_process,
71
+ data_path='data/GPT-Image-Edit-1.5M/gpt-edit/omniedit/omniedit.json',
72
+ image_folder = "data/GPT-Image-Edit-1.5M" ,
73
+ ceph_folder=None,
74
+ ceph_config=None)
75
+
76
+ GPT4oEdit_ultraedit = dict(type=ImageEditDataset,
77
+ image_size=image_size,
78
+ image_process=image_process,
79
+ data_path='data/GPT-Image-Edit-1.5M/gpt-edit/ultraedit/ultraedit.json',
80
+ image_folder = "data/GPT-Image-Edit-1.5M" ,
81
+ ceph_folder=None,
82
+ ceph_config=None)
83
+
84
+ Reason_edit = dict(type=ImageEditDataset,
85
+ image_size=image_size,
86
+ image_process=image_process,
87
+ data_path='data/unireason/reson_edit.json',
88
+ image_folder = "data/unireason/edit" ,
89
+ ceph_folder=None,
90
+ ceph_config=None)
91
+
92
+ Omnigen_edit = dict(type=ImageEditDataset,
93
+ image_size=image_size,
94
+ image_process=image_process,
95
+ data_path='data/X2I2/Omnigen.json',
96
+ image_folder = "data/X2I2/images" ,
97
+ ceph_folder=None,
98
+ ceph_config=None)
99
+
100
+ nhr_edit_1 = dict(type=ImageEditDataset,
101
+ image_size=image_size,
102
+ image_process=image_process,
103
+ data_path='data/NHR-Edit/NHR-Edit_1.json',
104
+ image_folder = "data/NHR-Edit" ,
105
+ ceph_folder=None,
106
+ ceph_config=None)
107
+
108
+ nhr_edit_2 = dict(type=ImageEditDataset,
109
+ image_size=image_size,
110
+ image_process=image_process,
111
+ data_path='data/NHR-Edit-part2/NHR-Edit_2.json',
112
+ image_folder = "data/NHR-Edit-part2" ,
113
+ ceph_folder=None,
114
+ ceph_config=None)
115
+
116
+ dataset = dict(
117
+ type=ConcatDataset,
118
+ datasets=[Open4oEdit,Share4oEdit,nano150Edit,picobananaEdit,UniworldEdit,GPT4oEdit_hqedit,GPT4oEdit_omniedit,GPT4oEdit_ultraedit,Reason_edit,Omnigen_edit,nhr_edit_1,nhr_edit_2],
119
+ )
120
+
121
+ train_dataloader = dict(
122
+ batch_size=16,
123
+ num_workers=4,
124
+ pin_memory=True,
125
+ dataset=dataset,
126
+ sampler=dict(type=InfiniteSampler, shuffle=True),
127
+ collate_fn=dict(type=collate_func_img2img_txt_dynamic)
128
+ )
configs/datasets/deepgen_512_fix_pixels/joint_pretrain.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.datasets.collate_functions import (collate_func_img2img_txt_dynamic,
2
+ collate_func_gen_txt_dynamic, CollateConcat)
3
+ from mmengine.config import read_base
4
+ from src.datasets.samplers.multi_source_sampler import MultiSourceSampler, MultiSourceBatchSampler
5
+
6
+ from xtuner.dataset import ConcatDataset
7
+
8
+
9
+ with read_base():
10
+ from .processors import image_size, image_process
11
+ from .t2i_pretrain import dataset as t2i_pretrain_dataset
12
+ from .edit_pretrain import dataset as edit_pretrain_dataset
13
+
14
+
15
+ dataset = dict(
16
+ type=ConcatDataset,
17
+ datasets=[edit_pretrain_dataset, t2i_pretrain_dataset]
18
+ )
19
+
20
+ group_keys = ['image2image', 'text2image']
21
+ repeats = [1, 3] # the radio between editing and generation task
22
+ batch_sizes = [4, 4]
23
+ batch_size = sum([repeat * batch_size for repeat, batch_size in zip(repeats, batch_sizes)]) // sum(repeats)
24
+
25
+
26
+ train_dataloader = dict(
27
+ batch_size=batch_size,
28
+ num_workers=4,
29
+ prefetch_factor=1,
30
+ persistent_workers=False,
31
+ pin_memory=True,
32
+ dataset=dataset,
33
+ sampler=dict(type=MultiSourceSampler,
34
+ repeats=repeats,
35
+ batch_sizes=batch_sizes, # fixed batch size for all sources
36
+ shuffle=True),
37
+ batch_sampler=dict(type=MultiSourceBatchSampler,
38
+ repeats=repeats,
39
+ batch_sizes=batch_sizes,
40
+ ),
41
+ collate_fn=dict(type=CollateConcat,
42
+ collate_fns=[dict(type=collate_func_img2img_txt_dynamic),
43
+ dict(type=collate_func_gen_txt_dynamic),
44
+ ],
45
+ keys=group_keys
46
+ )
47
+ )
configs/datasets/deepgen_512_fix_pixels/joint_sft_zh.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.datasets.collate_functions import (collate_func_img2img_txt_dynamic,
2
+ collate_func_gen_txt_dynamic, CollateConcat)
3
+ from mmengine.config import read_base
4
+ from src.datasets.samplers.multi_source_sampler import MultiSourceSampler, MultiSourceBatchSampler
5
+
6
+ from xtuner.dataset import ConcatDataset
7
+
8
+
9
+ with read_base():
10
+ from .processors import image_size, image_process
11
+ from .t2i_sft_zh import dataset as t2i_sft_dataset
12
+ from .edit_sft_zh import dataset as edit_sft_dataset
13
+
14
+
15
+ dataset = dict(
16
+ type=ConcatDataset,
17
+ datasets=[edit_sft_dataset, t2i_sft_dataset]
18
+ )
19
+
20
+ group_keys = ['image2image', 'text2image']
21
+ repeats = [1, 2] # the radio between editing and generation task
22
+ batch_sizes = [4, 4]
23
+ batch_size = sum([repeat * batch_size for repeat, batch_size in zip(repeats, batch_sizes)]) // sum(repeats)
24
+
25
+
26
+ train_dataloader = dict(
27
+ batch_size=batch_size,
28
+ num_workers=4,
29
+ prefetch_factor=1,
30
+ persistent_workers=False,
31
+ pin_memory=True,
32
+ dataset=dataset,
33
+ sampler=dict(type=MultiSourceSampler,
34
+ repeats=repeats,
35
+ batch_sizes=batch_sizes, # fixed batch size for all sources
36
+ shuffle=True),
37
+ batch_sampler=dict(type=MultiSourceBatchSampler,
38
+ repeats=repeats,
39
+ batch_sizes=batch_sizes,
40
+ ),
41
+ collate_fn=dict(type=CollateConcat,
42
+ collate_fns=[dict(type=collate_func_img2img_txt_dynamic),
43
+ dict(type=collate_func_gen_txt_dynamic),
44
+ ],
45
+ keys=group_keys
46
+ )
47
+ )
configs/datasets/deepgen_512_fix_pixels/laion6m.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmengine.config import read_base
2
+ from mmengine.dataset import InfiniteSampler
3
+ from src.datasets.collate_functions import collate_func_gen_txt_dynamic
4
+ from src.datasets.text2image.caption_datasets import CaptionDataset
5
+
6
+
7
+ with read_base():
8
+ from .processors import image_size, image_process
9
+
10
+ dataset = dict(type=CaptionDataset,
11
+ image_size=image_size,
12
+ cap_source='caption',
13
+ image_process=image_process,
14
+ cap_folder='data/laion6m/captions',
15
+ data_path='data/laion6m/data.json',
16
+ image_folder='data/laion6m/raw',
17
+ ceph_folder=None,
18
+ ceph_config=None)
19
+
20
+ train_dataloader = dict(
21
+ batch_size=8,
22
+ num_workers=4,
23
+ pin_memory=True,
24
+ dataset=dataset,
25
+ sampler=dict(type=InfiniteSampler, shuffle=True),
26
+ collate_fn=dict(type=collate_func_gen_txt_dynamic)
27
+ )
configs/datasets/deepgen_512_fix_pixels/megalith10m.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmengine.config import read_base
2
+ from mmengine.dataset import InfiniteSampler
3
+ from src.datasets.collate_functions import collate_func_gen_txt_dynamic
4
+ from src.datasets.text2image.caption_datasets import CaptionDataset
5
+
6
+
7
+ with read_base():
8
+ from .processors import image_size, image_process
9
+
10
+ dataset = dict(type=CaptionDataset,
11
+ image_size=image_size,
12
+ image_process=image_process,
13
+ cap_source='caption_internlm2_short',
14
+ cap_folder='data/megalith-10m/captions',
15
+ data_path='data/megalith-10m/megalith10m_all.json',
16
+ image_folder='data/megalith-10m/raw',
17
+ ceph_folder=None,
18
+ ceph_config=None)
19
+
20
+ train_dataloader = dict(
21
+ batch_size=8,
22
+ num_workers=4,
23
+ pin_memory=True,
24
+ dataset=dataset,
25
+ sampler=dict(type=InfiniteSampler, shuffle=True),
26
+ collate_fn=dict(type=collate_func_gen_txt_dynamic)
27
+ )
configs/datasets/deepgen_512_fix_pixels/processors.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoImageProcessor, AutoTokenizer
2
+
3
+ IMAGE_MEAN = (0.48145466, 0.4578275, 0.40821073)
4
+ IMAGE_STD = (0.26862954, 0.26130258, 0.27577711)
5
+
6
+
7
+ qwen2_5_vl_model_name_or_path = "model_zoo/Qwen2.5-VL-3B-Instruct"
8
+
9
+ prompt_template = dict(
10
+ IMG_START_TOKEN='<|vision_start|>',
11
+ IMG_END_TOKEN='<|vision_end|>',
12
+ IMG_CONTEXT_TOKEN='<|image_pad|>',
13
+ IMG_START_TOKEN_FOR_GENERATION=False,
14
+ SYSTEM=('<|im_start|>system\n{system}<|im_end|>\n'),
15
+ INSTRUCTION=('<|im_start|>user\n{input}<|im_end|>\n'
16
+ '<|im_start|>assistant\n'),
17
+ SUFFIX='<|im_end|>',
18
+ SUFFIX_AS_EOS=True,
19
+ SEP='\n',
20
+ STOP_WORDS=['<|im_end|>', '<|endoftext|>'],
21
+ GENERATION='Generate an image: {input}',
22
+ CFG='Generate an image.'
23
+ )
24
+
25
+ tokenizer_kwargs = dict(add_special_tokens=True)
26
+
27
+ pad_index = 0
28
+ image_size = 512
29
+ image_process = 'fix_pixels'
30
+ #######################################################################
31
+ # PART 2 Model & Tokenizer & Image Processor #
32
+ #######################################################################
33
+ tokenizer = dict(
34
+ type=AutoTokenizer.from_pretrained,
35
+ pretrained_model_name_or_path=qwen2_5_vl_model_name_or_path,
36
+ trust_remote_code=True,
37
+ padding_side='right')
38
+
39
+
40
+ image_processor = dict(type=AutoImageProcessor.from_pretrained,
41
+ pretrained_model_name_or_path=qwen2_5_vl_model_name_or_path)
configs/datasets/deepgen_512_fix_pixels/redcaps5m.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmengine.config import read_base
2
+ from mmengine.dataset import InfiniteSampler
3
+ from src.datasets.collate_functions import collate_func_gen_txt_dynamic
4
+ from src.datasets.text2image.caption_datasets import CaptionDataset
5
+
6
+
7
+ with read_base():
8
+ from .processors import image_size, image_process
9
+
10
+ dataset = dict(type=CaptionDataset,
11
+ image_size=image_size,
12
+ image_process=image_process,
13
+ cap_source='caption',
14
+ cap_folder='data/redcaps5m_resized/raw',
15
+ data_path='data/redcaps5m_resized/redcaps5m_data.json',
16
+ image_folder='data/redcaps5m_resized/raw',
17
+ ceph_folder=None,
18
+ ceph_config=None)
19
+
20
+ train_dataloader = dict(
21
+ batch_size=8,
22
+ num_workers=4,
23
+ pin_memory=True,
24
+ dataset=dataset,
25
+ sampler=dict(type=InfiniteSampler, shuffle=True),
26
+ collate_fn=dict(type=collate_func_gen_txt_dynamic)
27
+ )
configs/datasets/deepgen_512_fix_pixels/t2i_pretrain.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.datasets.collate_functions import collate_func_gen_txt_dynamic
2
+ from mmengine.config import read_base
3
+ from mmengine.dataset import InfiniteSampler
4
+ from xtuner.dataset import ConcatDataset
5
+ from src.datasets.text2image.blip3_o import BLIP3oDataset
6
+
7
+
8
+
9
+ with read_base():
10
+ from .processors import image_size, image_process
11
+ from .redcaps5m import dataset as redcaps5m_datasets
12
+ from .laion6m import dataset as laion6m_dataset
13
+ from .text2image2m import dataset as text2image2m_dataset
14
+ from .megalith10m import dataset as megalith10m_dataset
15
+ from .cc12m import dataset as cc12m_dataset
16
+
17
+
18
+
19
+
20
+
21
+ dataset = dict(
22
+ type=ConcatDataset,
23
+ datasets=[redcaps5m_datasets, laion6m_dataset, text2image2m_dataset, megalith10m_dataset,cc12m_dataset],
24
+ )
25
+
26
+
27
+ train_dataloader = dict(
28
+ batch_size=4,
29
+ num_workers=4,
30
+ pin_memory=True,
31
+ dataset=dataset,
32
+ sampler=dict(type=InfiniteSampler, shuffle=True),
33
+ collate_fn=dict(type=collate_func_gen_txt_dynamic)
34
+ )
35
+
configs/datasets/deepgen_512_fix_pixels/t2i_sft_zh.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmengine.config import read_base
2
+ from mmengine.dataset import InfiniteSampler
3
+ from src.datasets.collate_functions import collate_func_gen_txt_dynamic
4
+ from src.datasets.text2image.blip3_o import BLIP3oDataset
5
+ from xtuner.dataset import ConcatDataset
6
+
7
+ with read_base():
8
+ from .processors import image_size, image_process
9
+
10
+ image_process = 'fix_pixels'
11
+ dataset_blip3o60k = dict(type=BLIP3oDataset,
12
+ image_size=image_size,
13
+ data_path='data/BLIP3o/blip3o_60k.json',
14
+ image_folder ='data/BLIP3o',
15
+ image_process = image_process,
16
+ ceph_folder=None,
17
+ ceph_config=None)
18
+
19
+ dataset_share4oimg = dict(type=BLIP3oDataset,
20
+ image_size=image_size,
21
+ data_path='data/ShareGPT-4o-Image/share_4o_img.json',
22
+ image_folder = "data/ShareGPT-4o-Image/t2i" ,
23
+ image_process =image_process,
24
+ ceph_folder=None,
25
+ ceph_config=None)
26
+
27
+ dataset_echo4oimg = dict(type=BLIP3oDataset,
28
+ image_size=image_size,
29
+ data_path='data/Echo4o/echo-4o-image_t2i.json',
30
+ image_folder = "data/Echo4o" ,
31
+ image_process =image_process,
32
+ ceph_folder=None,
33
+ ceph_config=None)
34
+
35
+ dataset_open4oimg = dict(type=BLIP3oDataset,
36
+ image_size=image_size,
37
+ data_path='data/OpenGPT-4o-Image/OpenGPT-4o-Image.json',
38
+ image_folder = "data/OpenGPT-4o-Image/t2i" ,
39
+ image_process= image_process,
40
+ ceph_folder=None,
41
+ ceph_config=None)
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+ dataset_reason_img = dict(type=BLIP3oDataset,
50
+ image_size=image_size,
51
+ data_path='data/unireason/reson_t2i.json',
52
+ image_folder = "data/unireason/t2i" ,
53
+ image_process = image_process,
54
+ ceph_folder=None,
55
+ ceph_config=None)
56
+
57
+ dataset_banana = dict(type=BLIP3oDataset,
58
+ image_size=image_size,
59
+ data_path='data/banana/banana-50k.json',
60
+ image_folder = "data/banana" ,
61
+ image_process = image_process,
62
+ ceph_folder=None,
63
+ ceph_config=None)
64
+
65
+ dataset_text = dict(type=BLIP3oDataset,
66
+ image_size=image_size,
67
+ data_path='data/text_render/text.json',
68
+ image_folder = "data/text_render" ,
69
+ image_process = image_process,
70
+ ceph_folder=None,
71
+ ceph_config=None)
72
+
73
+
74
+
75
+ dataset_poster = dict(type=BLIP3oDataset,
76
+ image_size=image_size,
77
+ data_path='data/poster/data.json',
78
+ image_folder = "data/poster" ,
79
+ image_process = image_process,
80
+ ceph_folder=None,
81
+ ceph_config=None)
82
+
83
+
84
+
85
+ dataset = dict(
86
+ type=ConcatDataset,
87
+ datasets=[dataset_blip3o60k,dataset_share4oimg,dataset_echo4oimg,dataset_open4oimg,dataset_reason_img,dataset_banana,dataset_text,dataset_poster],
88
+ )
89
+
90
+ train_dataloader = dict(
91
+ batch_size=2,
92
+ num_workers=4,
93
+ pin_memory=True,
94
+ dataset=dataset,
95
+ sampler=dict(type=InfiniteSampler, shuffle=True),
96
+ collate_fn=dict(type=collate_func_gen_txt_dynamic)
97
+ )
configs/datasets/deepgen_512_fix_pixels/text2image2m.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.datasets.text2image.caption_datasets import CaptionDataset
2
+ from src.datasets.collate_functions import collate_func_gen_txt_dynamic
3
+ from mmengine.config import read_base
4
+ from mmengine.dataset import InfiniteSampler
5
+ from xtuner.dataset import ConcatDataset
6
+
7
+ with read_base():
8
+ from .processors import image_size, image_process
9
+
10
+ t2i_2m = dict(type=CaptionDataset,
11
+ image_size=image_size,
12
+ image_process=image_process,
13
+ cap_source='prompt',
14
+ data_path='data/text-to-image-2M/data/data_512_2M.json',
15
+ cap_folder='data/text-to-image-2M/raw/data_512_2M',
16
+ image_folder='data/text-to-image-2M/raw/data_512_2M',
17
+ ceph_folder=None,
18
+ ceph_config=None,)
19
+
20
+ t2i_10k = dict(type=CaptionDataset,
21
+ image_size=image_size,
22
+ image_process=image_process,
23
+ cap_source='prompt',
24
+ data_path='data/text-to-image-2M/data/data_1024_10K.json',
25
+ cap_folder='data/text-to-image-2M/raw/data_1024_10K',
26
+ image_folder='data/text-to-image-2M/raw/data_1024_10K',
27
+ ceph_folder=None,
28
+ ceph_config=None)
29
+
30
+ dataset = dict(
31
+ type=ConcatDataset,
32
+ datasets=[t2i_2m, t2i_10k]
33
+ )
34
+
35
+ train_dataloader = dict(
36
+ batch_size=8,
37
+ num_workers=4,
38
+ pin_memory=True,
39
+ dataset=dataset,
40
+ sampler=dict(type=InfiniteSampler, shuffle=True),
41
+ collate_fn=dict(type=collate_func_gen_txt_dynamic)
42
+ )
configs/finetune/deepgen_joint_sft.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmengine.config import read_base
2
+ from mmengine.hooks import (CheckpointHook, DistSamplerSeedHook, IterTimerHook,
3
+ LoggerHook, ParamSchedulerHook)
4
+ from mmengine.optim import AmpOptimWrapper, CosineAnnealingLR, LinearLR
5
+ from xtuner.engine.runner import TrainLoop
6
+ from src.optimisers.custom_adamw import CustomAdamW
7
+ # from torch.optim import AdamW
8
+
9
+ with read_base():
10
+ from ..models.deepgen import model
11
+ from ..datasets.deepgen_512_fix_pixels.joint_sft import train_dataloader
12
+
13
+
14
+ model.num_queries = 128
15
+ model.use_activation_checkpointing = False
16
+ model.freeze_transformer = False
17
+ model.lora_modules = 'auto'
18
+ model.lora_rank = 64
19
+ model.lora_alpha = 128
20
+ model.pretrained_pth = "your_path_to_pretrained_pth"
21
+ # Scheduler & Optimizer
22
+ accumulative_counts = 3
23
+ dataloader_num_workers = 4
24
+ max_iters = 400000
25
+ optim_type = CustomAdamW
26
+ lr = 5e-5
27
+ betas = (0.9, 0.95)
28
+ weight_decay = 0.05
29
+ max_norm = 1.0 # grad clip
30
+ warmup_ratio = 0.01
31
+
32
+
33
+ # Save
34
+ save_steps = 40000
35
+ save_total_limit = 5 # Maximum checkpoints to keep (-1 means unlimited)
36
+
37
+
38
+ # optimizer
39
+ optim_wrapper = dict(
40
+ type=AmpOptimWrapper,
41
+ optimizer=dict(type=optim_type, lr=lr, betas=betas, weight_decay=weight_decay),
42
+ clip_grad=dict(max_norm=max_norm, error_if_nonfinite=False),
43
+ accumulative_counts=accumulative_counts,
44
+ loss_scale="dynamic",
45
+ dtype="bfloat16",
46
+ )
47
+
48
+ # learning policy
49
+ # More information: https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/param_scheduler.md # noqa: E501
50
+ param_scheduler = [
51
+ dict(
52
+ type=LinearLR,
53
+ start_factor=1e-5,
54
+ by_epoch=False,
55
+ begin=0,
56
+ end=warmup_ratio * max_iters),
57
+ dict(
58
+ type=CosineAnnealingLR,
59
+ eta_min=0.0,
60
+ by_epoch=False,
61
+ begin=warmup_ratio * max_iters,
62
+ end=max_iters)
63
+ ]
64
+
65
+ # train, val, test setting
66
+ train_cfg = dict(type=TrainLoop, max_iters=max_iters)
67
+
68
+ #######################################################################
69
+ # PART 5 Runtime #
70
+ #######################################################################
71
+ # configure default hooks
72
+ default_hooks = dict(
73
+ # record the time of every iteration.
74
+ timer=dict(type=IterTimerHook),
75
+ # print log every 10 iterations.
76
+ logger=dict(type=LoggerHook, log_metric_by_epoch=False, interval=10),
77
+ # enable the parameter scheduler.
78
+ param_scheduler=dict(type=ParamSchedulerHook),
79
+ # save checkpoint per `save_steps`.
80
+ checkpoint=dict(
81
+ type=CheckpointHook,
82
+ by_epoch=False,
83
+ interval=save_steps,
84
+ max_keep_ckpts=save_total_limit),
85
+ # set sampler seed in distributed evrionment.
86
+ sampler_seed=dict(type=DistSamplerSeedHook),
87
+ )
88
+
89
+ # configure environment
90
+ env_cfg = dict(
91
+ # whether to enable cudnn benchmark
92
+ cudnn_benchmark=False,
93
+ # set multi process parameters
94
+ mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),
95
+ # set distributed parameters
96
+ dist_cfg=dict(backend='nccl'),
97
+ )
98
+
99
+ # set visualizer
100
+ visualizer = None
101
+
102
+ # set log level
103
+ log_level = 'INFO'
104
+
105
+ # load from which checkpoint
106
+ load_from = None
107
+
108
+ # whether to resume training from the loaded checkpoint
109
+ resume = False
110
+
111
+ # Defaults to use random seed and disable `deterministic`
112
+ randomness = dict(seed=None, deterministic=False)
113
+
114
+ # set log processor
115
+ log_processor = dict(by_epoch=False)
configs/finetune/deepgen_joint_sft_scb.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmengine.config import read_base
2
+ from mmengine.hooks import (CheckpointHook, DistSamplerSeedHook, IterTimerHook,
3
+ LoggerHook, ParamSchedulerHook)
4
+ from mmengine.optim import AmpOptimWrapper, CosineAnnealingLR, LinearLR
5
+ from xtuner.engine.runner import TrainLoop
6
+ from src.optimisers.custom_adamw import CustomAdamW
7
+ # from torch.optim import AdamW
8
+
9
+ with read_base():
10
+ from ..models.deepgen_scb import model
11
+ from ..datasets.deepgen_512_fix_pixels.joint_sft_zh import train_dataloader
12
+
13
+ model.num_queries = 128
14
+ model.use_activation_checkpointing = False
15
+ model.freeze_transformer = False
16
+ model.lora_modules = 'auto'
17
+ model.lora_rank = 64
18
+ model.lora_alpha = 128
19
+ model.pretrained_pth = 'your_path_to_pretrained_pth'
20
+ # Scheduler & Optimizer
21
+ accumulative_counts = 3
22
+ dataloader_num_workers = 4
23
+ max_iters = 400000
24
+ optim_type = CustomAdamW
25
+ lr = 5e-5
26
+ betas = (0.9, 0.95)
27
+ weight_decay = 0.05
28
+ max_norm = 1.0 # grad clip
29
+ warmup_ratio = 0.01
30
+
31
+
32
+ # Save
33
+ save_steps = 40000
34
+ save_total_limit = 5 # Maximum checkpoints to keep (-1 means unlimited)
35
+
36
+
37
+ # optimizer
38
+ optim_wrapper = dict(
39
+ type=AmpOptimWrapper,
40
+ optimizer=dict(type=optim_type, lr=lr, betas=betas, weight_decay=weight_decay),
41
+ clip_grad=dict(max_norm=max_norm, error_if_nonfinite=False),
42
+ accumulative_counts=accumulative_counts,
43
+ loss_scale="dynamic",
44
+ dtype="bfloat16",
45
+ )
46
+
47
+ # learning policy
48
+ # More information: https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/param_scheduler.md # noqa: E501
49
+ param_scheduler = [
50
+ dict(
51
+ type=LinearLR,
52
+ start_factor=1e-5,
53
+ by_epoch=False,
54
+ begin=0,
55
+ end=warmup_ratio * max_iters),
56
+ dict(
57
+ type=CosineAnnealingLR,
58
+ eta_min=0.0,
59
+ by_epoch=False,
60
+ begin=warmup_ratio * max_iters,
61
+ end=max_iters)
62
+ ]
63
+
64
+ # train, val, test setting
65
+ train_cfg = dict(type=TrainLoop, max_iters=max_iters)
66
+
67
+ #######################################################################
68
+ # PART 5 Runtime #
69
+ #######################################################################
70
+ # configure default hooks
71
+ default_hooks = dict(
72
+ # record the time of every iteration.
73
+ timer=dict(type=IterTimerHook),
74
+ # print log every 10 iterations.
75
+ logger=dict(type=LoggerHook, log_metric_by_epoch=False, interval=10),
76
+ # enable the parameter scheduler.
77
+ param_scheduler=dict(type=ParamSchedulerHook),
78
+ # save checkpoint per `save_steps`.
79
+ checkpoint=dict(
80
+ type=CheckpointHook,
81
+ by_epoch=False,
82
+ interval=save_steps,
83
+ max_keep_ckpts=save_total_limit),
84
+ # set sampler seed in distributed evrionment.
85
+ sampler_seed=dict(type=DistSamplerSeedHook),
86
+ )
87
+
88
+ # configure environment
89
+ env_cfg = dict(
90
+ # whether to enable cudnn benchmark
91
+ cudnn_benchmark=False,
92
+ # set multi process parameters
93
+ mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),
94
+ # set distributed parameters
95
+ dist_cfg=dict(backend='nccl'),
96
+ )
97
+
98
+ # set visualizer
99
+ visualizer = None
100
+
101
+ # set log level
102
+ log_level = 'INFO'
103
+
104
+ # load from which checkpoint
105
+ load_from = None
106
+
107
+ # whether to resume training from the loaded checkpoint
108
+ resume = False
109
+
110
+ # Defaults to use random seed and disable `deterministic`
111
+ randomness = dict(seed=None, deterministic=False)
112
+
113
+ # set log processor
114
+ log_processor = dict(by_epoch=False)
configs/models/deepgen.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from src.models.sd3_kontext.qwen2_5_vl_sd3_hf_dynamic import Qwen2p5VLStableDiffusion3HF
3
+ from diffusers import FlowMatchEulerDiscreteScheduler, AutoencoderKL
4
+ from transformers import Qwen2_5_VLForConditionalGeneration, AutoTokenizer
5
+ from src.models.sd3_kontext.transformer_sd3_dynamic import SD3Transformer2DModel
6
+
7
+
8
+ sd3_5_model_name_or_path = "model_zoo/UniPic2-SD3.5M-Kontext-2B"
9
+ qwen2_5_vl_model_name_or_path = "model_zoo/Qwen2.5-VL-3B-Instruct"
10
+
11
+ tokenizer = dict(
12
+ type=AutoTokenizer.from_pretrained,
13
+ pretrained_model_name_or_path=qwen2_5_vl_model_name_or_path,
14
+ trust_remote_code=True,
15
+ padding_side='right')
16
+
17
+ prompt_template = dict(
18
+ IMG_START_TOKEN='<|vision_start|>',
19
+ IMG_END_TOKEN='<|vision_end|>',
20
+ IMG_CONTEXT_TOKEN='<|image_pad|>',
21
+ IMG_START_TOKEN_FOR_GENERATION=False,
22
+ SYSTEM=('<|im_start|>system\n{system}<|im_end|>\n'),
23
+ INSTRUCTION=('<|im_start|>user\n{input}<|im_end|>\n'
24
+ '<|im_start|>assistant\n'),
25
+ SUFFIX='<|im_end|>',
26
+ SUFFIX_AS_EOS=True,
27
+ SEP='\n',
28
+ STOP_WORDS=['<|im_end|>', '<|endoftext|>'],
29
+ GENERATION='Generate an image: {input}',
30
+ CFG='Generate an image.'
31
+ )
32
+
33
+
34
+ model = dict(
35
+ type=Qwen2p5VLStableDiffusion3HF,
36
+ num_queries=128,
37
+ connector=dict(
38
+ hidden_size=2048,
39
+ intermediate_size=11946,
40
+ num_hidden_layers=6,
41
+ _attn_implementation='flash_attention_2',
42
+ num_attention_heads=32, ),
43
+ lmm=dict(type=Qwen2_5_VLForConditionalGeneration.from_pretrained,
44
+ pretrained_model_name_or_path=qwen2_5_vl_model_name_or_path,
45
+ torch_dtype=torch.bfloat16,
46
+ attn_implementation="flash_attention_2", ),
47
+ tokenizer=tokenizer,
48
+ prompt_template=prompt_template,
49
+ freeze_lmm=True,
50
+ transformer=dict(
51
+ type=SD3Transformer2DModel.from_pretrained,
52
+ pretrained_model_name_or_path=sd3_5_model_name_or_path,
53
+ subfolder="transformer",
54
+ torch_dtype=torch.bfloat16),
55
+ test_scheduler=dict(
56
+ type=FlowMatchEulerDiscreteScheduler.from_pretrained,
57
+ pretrained_model_name_or_path=sd3_5_model_name_or_path,
58
+ subfolder="scheduler"),
59
+ train_scheduler=dict(
60
+ type=FlowMatchEulerDiscreteScheduler.from_pretrained,
61
+ pretrained_model_name_or_path=sd3_5_model_name_or_path,
62
+ subfolder="scheduler"),
63
+ vae=dict(
64
+ type=AutoencoderKL.from_pretrained,
65
+ pretrained_model_name_or_path=sd3_5_model_name_or_path,
66
+ subfolder="vae",
67
+ torch_dtype=torch.bfloat16),
68
+ pretrained_pth=None,
69
+ use_activation_checkpointing=False,
70
+ freeze_transformer=True,
71
+ )
configs/models/deepgen_scb.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from src.models.sd3_kontext.qwen2_5_vl_sd3_hf_dynamic_fusion import Qwen2p5VLStableDiffusion3HF
3
+ from diffusers import FlowMatchEulerDiscreteScheduler, AutoencoderKL
4
+ from transformers import Qwen2_5_VLForConditionalGeneration, AutoTokenizer
5
+ from src.models.sd3_kontext.transformer_sd3_dynamic import SD3Transformer2DModel
6
+
7
+
8
+ sd3_5_model_name_or_path = "model_zoo/UniPic2-SD3.5M-Kontext-2B"
9
+ qwen2_5_vl_model_name_or_path = "model_zoo/Qwen2.5-VL-3B-Instruct"
10
+
11
+ tokenizer = dict(
12
+ type=AutoTokenizer.from_pretrained,
13
+ pretrained_model_name_or_path=qwen2_5_vl_model_name_or_path,
14
+ trust_remote_code=True,
15
+ padding_side='right')
16
+
17
+ prompt_template = dict(
18
+ IMG_START_TOKEN='<|vision_start|>',
19
+ IMG_END_TOKEN='<|vision_end|>',
20
+ IMG_CONTEXT_TOKEN='<|image_pad|>',
21
+ IMG_START_TOKEN_FOR_GENERATION=False,
22
+ SYSTEM=('<|im_start|>system\n{system}<|im_end|>\n'),
23
+ INSTRUCTION=('<|im_start|>user\n{input}<|im_end|>\n'
24
+ '<|im_start|>assistant\n'),
25
+ SUFFIX='<|im_end|>',
26
+ SUFFIX_AS_EOS=True,
27
+ SEP='\n',
28
+ STOP_WORDS=['<|im_end|>', '<|endoftext|>'],
29
+ GENERATION='Generate an image: {input}',
30
+ CFG='Generate an image.'
31
+ )
32
+
33
+
34
+ model = dict(
35
+ type=Qwen2p5VLStableDiffusion3HF,
36
+ num_queries=128,
37
+ connector=dict(
38
+ hidden_size=2048,
39
+ intermediate_size=11946,
40
+ num_hidden_layers=6,
41
+ _attn_implementation='flash_attention_2',
42
+ num_attention_heads=32, ),
43
+ lmm=dict(type=Qwen2_5_VLForConditionalGeneration.from_pretrained,
44
+ pretrained_model_name_or_path=qwen2_5_vl_model_name_or_path,
45
+ torch_dtype=torch.bfloat16,
46
+ attn_implementation="flash_attention_2", ),
47
+ tokenizer=tokenizer,
48
+ prompt_template=prompt_template,
49
+ freeze_lmm=True,
50
+ transformer=dict(
51
+ type=SD3Transformer2DModel.from_pretrained,
52
+ pretrained_model_name_or_path=sd3_5_model_name_or_path,
53
+ subfolder="transformer",
54
+ torch_dtype=torch.bfloat16),
55
+ test_scheduler=dict(
56
+ type=FlowMatchEulerDiscreteScheduler.from_pretrained,
57
+ pretrained_model_name_or_path=sd3_5_model_name_or_path,
58
+ subfolder="scheduler"),
59
+ train_scheduler=dict(
60
+ type=FlowMatchEulerDiscreteScheduler.from_pretrained,
61
+ pretrained_model_name_or_path=sd3_5_model_name_or_path,
62
+ subfolder="scheduler"),
63
+ vae=dict(
64
+ type=AutoencoderKL.from_pretrained,
65
+ pretrained_model_name_or_path=sd3_5_model_name_or_path,
66
+ subfolder="vae",
67
+ torch_dtype=torch.bfloat16),
68
+ pretrained_pth=None,
69
+ use_activation_checkpointing=False,
70
+ freeze_transformer=True,
71
+ )
configs/pretrain/deepgen_joint_pretrain.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmengine.config import read_base
2
+ from mmengine.hooks import (CheckpointHook, DistSamplerSeedHook, IterTimerHook,
3
+ LoggerHook, ParamSchedulerHook)
4
+ from mmengine.optim import AmpOptimWrapper, CosineAnnealingLR, LinearLR
5
+ from xtuner.engine.runner import TrainLoop
6
+ from src.optimisers.custom_adamw import CustomAdamW
7
+ # from torch.optim import AdamW
8
+
9
+ with read_base():
10
+ from ..models.deepgen import model
11
+ from ..datasets.deepgen_512_fix_pixels.joint_pretrain import train_dataloader
12
+
13
+
14
+
15
+ model.num_queries = 128
16
+ model.use_activation_checkpointing = False
17
+ model.freeze_transformer = True
18
+ model.lora_modules = None
19
+
20
+ # Scheduler & Optimizer
21
+ accumulative_counts = 4
22
+ dataloader_num_workers = 4
23
+ max_iters = 200000
24
+ optim_type = CustomAdamW
25
+ lr = 1e-4
26
+ betas = (0.9, 0.95)
27
+ weight_decay = 0.05
28
+ max_norm = 1.0 # grad clip
29
+ warmup_ratio = 0.01
30
+
31
+
32
+ # Save
33
+ save_steps = 40000
34
+ save_total_limit = 5 # Maximum checkpoints to keep (-1 means unlimited)
35
+
36
+
37
+ # optimizer
38
+ optim_wrapper = dict(
39
+ type=AmpOptimWrapper,
40
+ optimizer=dict(type=optim_type, lr=lr, betas=betas, weight_decay=weight_decay),
41
+ clip_grad=dict(max_norm=max_norm, error_if_nonfinite=False),
42
+ accumulative_counts=accumulative_counts,
43
+ loss_scale="dynamic",
44
+ dtype="bfloat16",
45
+ )
46
+
47
+ # learning policy
48
+ # More information: https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/param_scheduler.md # noqa: E501
49
+ param_scheduler = [
50
+ dict(
51
+ type=LinearLR,
52
+ start_factor=1e-5,
53
+ by_epoch=False,
54
+ begin=0,
55
+ end=warmup_ratio * max_iters),
56
+ dict(
57
+ type=CosineAnnealingLR,
58
+ eta_min=0.0,
59
+ by_epoch=False,
60
+ begin=warmup_ratio * max_iters,
61
+ end=max_iters)
62
+ ]
63
+
64
+ # train, val, test setting
65
+ train_cfg = dict(type=TrainLoop, max_iters=max_iters)
66
+
67
+ #######################################################################
68
+ # PART 5 Runtime #
69
+ #######################################################################
70
+ # configure default hooks
71
+ default_hooks = dict(
72
+ # record the time of every iteration.
73
+ timer=dict(type=IterTimerHook),
74
+ # print log every 10 iterations.
75
+ logger=dict(type=LoggerHook, log_metric_by_epoch=False, interval=10),
76
+ # enable the parameter scheduler.
77
+ param_scheduler=dict(type=ParamSchedulerHook),
78
+ # save checkpoint per `save_steps`.
79
+ checkpoint=dict(
80
+ type=CheckpointHook,
81
+ by_epoch=False,
82
+ interval=save_steps,
83
+ max_keep_ckpts=save_total_limit),
84
+ # set sampler seed in distributed evrionment.
85
+ sampler_seed=dict(type=DistSamplerSeedHook),
86
+ )
87
+
88
+ # configure environment
89
+ env_cfg = dict(
90
+ # whether to enable cudnn benchmark
91
+ cudnn_benchmark=False,
92
+ # set multi process parameters
93
+ mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),
94
+ # set distributed parameters
95
+ dist_cfg=dict(backend='nccl'),
96
+ )
97
+
98
+ # set visualizer
99
+ visualizer = None
100
+
101
+ # set log level
102
+ log_level = 'INFO'
103
+
104
+ # load from which checkpoint
105
+ load_from = None
106
+
107
+ # whether to resume training from the loaded checkpoint
108
+ resume = False
109
+
110
+ # Defaults to use random seed and disable `deterministic`
111
+ randomness = dict(seed=None, deterministic=False)
112
+
113
+ # set log processor
114
+ log_processor = dict(by_epoch=False)
configs/pretrain/deepgen_joint_pretrain_scb.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmengine.config import read_base
2
+ from mmengine.hooks import (CheckpointHook, DistSamplerSeedHook, IterTimerHook,
3
+ LoggerHook, ParamSchedulerHook)
4
+ from mmengine.optim import AmpOptimWrapper, CosineAnnealingLR, LinearLR
5
+ from xtuner.engine.runner import TrainLoop
6
+ from src.optimisers.custom_adamw import CustomAdamW
7
+ # from torch.optim import AdamW
8
+
9
+ with read_base():
10
+ from ..models.deepgen_scb import model
11
+ from ..datasets.deepgen_512_fix_pixels.joint_pretrain import train_dataloader
12
+
13
+ model.num_queries = 128
14
+ model.use_activation_checkpointing = False
15
+ model.freeze_transformer = True
16
+ model.lora_modules = None
17
+
18
+ # Scheduler & Optimizer
19
+ accumulative_counts = 4
20
+ dataloader_num_workers = 4
21
+ max_iters = 200000
22
+ optim_type = CustomAdamW
23
+ lr = 1e-4
24
+ betas = (0.9, 0.95)
25
+ weight_decay = 0.05
26
+ max_norm = 1.0 # grad clip
27
+ warmup_ratio = 0.01
28
+
29
+
30
+ # Save
31
+ save_steps = 40000
32
+ save_total_limit = 5 # Maximum checkpoints to keep (-1 means unlimited)
33
+
34
+
35
+ # optimizer
36
+ optim_wrapper = dict(
37
+ type=AmpOptimWrapper,
38
+ optimizer=dict(type=optim_type, lr=lr, betas=betas, weight_decay=weight_decay),
39
+ clip_grad=dict(max_norm=max_norm, error_if_nonfinite=False),
40
+ accumulative_counts=accumulative_counts,
41
+ loss_scale="dynamic",
42
+ dtype="bfloat16",
43
+ )
44
+
45
+ # learning policy
46
+ # More information: https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/param_scheduler.md # noqa: E501
47
+ param_scheduler = [
48
+ dict(
49
+ type=LinearLR,
50
+ start_factor=1e-5,
51
+ by_epoch=False,
52
+ begin=0,
53
+ end=warmup_ratio * max_iters),
54
+ dict(
55
+ type=CosineAnnealingLR,
56
+ eta_min=0.0,
57
+ by_epoch=False,
58
+ begin=warmup_ratio * max_iters,
59
+ end=max_iters)
60
+ ]
61
+
62
+ # train, val, test setting
63
+ train_cfg = dict(type=TrainLoop, max_iters=max_iters)
64
+
65
+ #######################################################################
66
+ # PART 5 Runtime #
67
+ #######################################################################
68
+ # configure default hooks
69
+ default_hooks = dict(
70
+ # record the time of every iteration.
71
+ timer=dict(type=IterTimerHook),
72
+ # print log every 10 iterations.
73
+ logger=dict(type=LoggerHook, log_metric_by_epoch=False, interval=10),
74
+ # enable the parameter scheduler.
75
+ param_scheduler=dict(type=ParamSchedulerHook),
76
+ # save checkpoint per `save_steps`.
77
+ checkpoint=dict(
78
+ type=CheckpointHook,
79
+ by_epoch=False,
80
+ interval=save_steps,
81
+ max_keep_ckpts=save_total_limit),
82
+ # set sampler seed in distributed evrionment.
83
+ sampler_seed=dict(type=DistSamplerSeedHook),
84
+ )
85
+
86
+ # configure environment
87
+ env_cfg = dict(
88
+ # whether to enable cudnn benchmark
89
+ cudnn_benchmark=False,
90
+ # set multi process parameters
91
+ mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),
92
+ # set distributed parameters
93
+ dist_cfg=dict(backend='nccl'),
94
+ )
95
+
96
+ # set visualizer
97
+ visualizer = None
98
+
99
+ # set log level
100
+ log_level = 'INFO'
101
+
102
+ # load from which checkpoint
103
+ load_from = None
104
+
105
+ # whether to resume training from the loaded checkpoint
106
+ resume = False
107
+
108
+ # Defaults to use random seed and disable `deterministic`
109
+ randomness = dict(seed=None, deterministic=False)
110
+
111
+ # set log processor
112
+ log_processor = dict(by_epoch=False)
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ numpy<2
2
+ gradio
3
+ huggingface_hub
4
+ accelerate
5
+ einops
6
+ inflect
7
+ peft
8
+ opencv-python-headless
9
+ spaces
10
+ diffusers
11
+ triton==2.1.0
12
+ transformers==4.56.1
13
+ mmengine
src/datasets/collate_functions.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from xtuner.utils import DEFAULT_PAD_TOKEN_INDEX, IGNORE_INDEX
3
+ from typing import Dict, Sequence
4
+ from torch.nn.utils.rnn import pad_sequence
5
+ from functools import partial
6
+ from dataclasses import dataclass
7
+
8
+
9
+ def collate_func_img2img(instances: Sequence[Dict],
10
+ pad_index: int = DEFAULT_PAD_TOKEN_INDEX):
11
+ pixel_values_src_list, pixel_values_list, input_ids_list, texts = [], [], [], []
12
+ for instance in instances:
13
+ pixel_values_src_ = instance.pop('pixel_values_src')
14
+ if isinstance(pixel_values_src_, torch.Tensor):
15
+ pixel_values_src_ = [pixel_values_src_]
16
+ pixel_values_src_list += pixel_values_src_
17
+ pixel_values_list.append(instance.pop('pixel_values'))
18
+ input_ids_list.append(instance.pop('input_ids'))
19
+ texts.append(instance.pop('text', None))
20
+
21
+ ori_length = [len(ids) for ids in input_ids_list]
22
+ pad_length = max(ori_length)
23
+ attention_mask = torch.zeros(len(instances), pad_length, dtype=torch.bool)
24
+ input_ids = torch.full(size=(len(instances), pad_length),
25
+ fill_value=pad_index, dtype=torch.long)
26
+
27
+ # left padding for editing
28
+ for i, length in enumerate(ori_length):
29
+ attention_mask[i, -length:] = True
30
+ input_ids_i = input_ids_list[i]
31
+ if not isinstance(input_ids_i, torch.Tensor):
32
+ input_ids_i = torch.tensor(input_ids_i, dtype=torch.long)
33
+ input_ids[i, -length:] = input_ids_i
34
+
35
+ pixel_values = torch.stack(pixel_values_list)
36
+ pixel_values_src = torch.stack(pixel_values_src_list)
37
+
38
+ data_dict = dict(input_ids=input_ids, attention_mask=attention_mask,
39
+ pixel_values=pixel_values, pixel_values_src=pixel_values_src, texts=texts)
40
+
41
+ return {'data': data_dict, 'data_samples': None}
42
+
43
+
44
+ def collate_func_img2img_text(instances: Sequence[Dict]):
45
+ pixel_values_src_list, pixel_values_list, texts = [], [], []
46
+ for instance in instances:
47
+ pixel_values_src_ = instance.pop('pixel_values_src')
48
+ if isinstance(pixel_values_src_, torch.Tensor):
49
+ pixel_values_src_ = [pixel_values_src_]
50
+ pixel_values_src_list += pixel_values_src_
51
+ pixel_values_list.append(instance.pop('pixel_values'))
52
+ texts.append(instance.pop('text'))
53
+
54
+ pixel_values = torch.stack(pixel_values_list)
55
+ pixel_values_src = torch.stack(pixel_values_src_list)
56
+
57
+ data_dict = dict(pixel_values=pixel_values, pixel_values_src=pixel_values_src, texts=texts)
58
+
59
+ return {'data': data_dict, 'data_samples': None}
60
+
61
+
62
+ def collate_func_img2img_txt_dynamic(instances: Sequence[Dict]):
63
+ pixel_values_src, pixel_values, texts = [], [], []
64
+ for instance in instances:
65
+ pixel_values_src_ = instance.pop('pixel_values_src')
66
+ if isinstance(pixel_values_src_, torch.Tensor): # only has one ref image
67
+ pixel_values_src_ = [pixel_values_src_]
68
+ pixel_values_src.append(pixel_values_src_)
69
+ pixel_values.append(instance.pop('pixel_values'))
70
+ texts.append(instance.pop('text'))
71
+
72
+ data_dict = dict(pixel_values=pixel_values, pixel_values_src=pixel_values_src, texts=texts)
73
+
74
+ return {'data': data_dict, 'data_samples': None}
75
+
76
+
77
+ def collate_func_gen_txt_dynamic(instances: Sequence[Dict]):
78
+ pixel_values, texts = [], []
79
+ for example in instances:
80
+ pixel_values.append(example.pop('pixel_values'))
81
+ texts.append(example.pop('text'))
82
+
83
+ data_dict = dict(pixel_values=pixel_values, texts=texts)
84
+
85
+ return {'data': data_dict, 'data_samples': None}
86
+
87
+
88
+
89
+ def collate_func_gen(instances: Sequence[Dict],
90
+ pad_index: int = DEFAULT_PAD_TOKEN_INDEX):
91
+ pixel_values, input_ids, input_lengths, texts, pixel_init = [], [], [], [], []
92
+ for example in instances:
93
+ pixel_values.append(example.pop('pixel_values'))
94
+ input_lengths.append(len(example['input_ids']))
95
+ input_ids.append(example.pop('input_ids'))
96
+ texts.append(example.pop('text', None))
97
+ pixel_init.append(example.pop('pixel_init'))
98
+
99
+ input_ids = pad_sequence(input_ids, batch_first=True, padding_value=pad_index)
100
+ attention_mask = torch.zeros_like(input_ids).bool()
101
+ for i in range(len(input_ids)):
102
+ attention_mask[i, :input_lengths[i]] = True
103
+
104
+ data_dict = dict(pixel_values=torch.stack(pixel_values),
105
+ pixel_init = pixel_init,
106
+ input_ids=input_ids,
107
+ attention_mask=attention_mask,
108
+ texts=texts)
109
+
110
+ return {'data': data_dict, 'data_samples': None}
111
+
112
+
113
+ def collate_func_gen_text(instances: Sequence[Dict]):
114
+ pixel_values, texts = [], []
115
+ for example in instances:
116
+ pixel_values.append(example.pop('pixel_values'))
117
+ texts.append(example.pop('text'))
118
+
119
+ data_dict = dict(pixel_values=torch.stack(pixel_values), texts=texts)
120
+
121
+ return {'data': data_dict, 'data_samples': None}
122
+
123
+
124
+ def collate_func_gen_tokens(instances: Sequence[Dict],
125
+ pad_index: int = DEFAULT_PAD_TOKEN_INDEX):
126
+ image_tokens, input_ids, input_lengths, texts = [], [], [], []
127
+ for example in instances:
128
+ image_tokens.append(example.pop('image_tokens'))
129
+ input_lengths.append(len(example['input_ids']))
130
+ input_ids.append(example.pop('input_ids'))
131
+ texts.append(example.pop('text', None))
132
+
133
+ input_ids = pad_sequence(input_ids, batch_first=True, padding_value=pad_index)
134
+ attention_mask = torch.zeros_like(input_ids).bool()
135
+ for i in range(len(input_ids)):
136
+ attention_mask[i, :input_lengths[i]] = True
137
+
138
+ data_dict = dict(image_tokens=torch.stack(image_tokens),
139
+ input_ids=input_ids,
140
+ attention_mask=attention_mask,
141
+ texts=texts)
142
+
143
+ return {'data': data_dict, 'data_samples': None}
144
+
145
+
146
+ def collate_func_gen_latents(instances: Sequence[Dict],
147
+ pad_index: int = DEFAULT_PAD_TOKEN_INDEX):
148
+ image_latents, input_ids, input_lengths, texts = [], [], [], []
149
+ for example in instances:
150
+ image_latents.append(example.pop('image_latents'))
151
+ input_lengths.append(len(example['input_ids']))
152
+ input_ids.append(example.pop('input_ids'))
153
+ texts.append(example.pop('text', None))
154
+
155
+ input_ids = pad_sequence(input_ids, batch_first=True, padding_value=pad_index)
156
+ attention_mask = torch.zeros_like(input_ids).bool()
157
+ for i in range(len(input_ids)):
158
+ attention_mask[i, :input_lengths[i]] = True
159
+
160
+ data_dict = dict(image_latents=torch.stack(image_latents),
161
+ input_ids=input_ids,
162
+ attention_mask=attention_mask,
163
+ texts=texts)
164
+
165
+ return {'data': data_dict, 'data_samples': None}
166
+
167
+
168
+ def collate_func_gen_text_latents(instances: Sequence[Dict]):
169
+ image_latents, texts = [], []
170
+ for example in instances:
171
+ image_latents.append(example.pop('image_latents'))
172
+ texts.append(example.pop('text', None))
173
+
174
+ data_dict = dict(image_latents=torch.stack(image_latents), texts=texts)
175
+
176
+ return {'data': data_dict, 'data_samples': None}
177
+
178
+
179
+ def collate_func_und(instances, pad_index=DEFAULT_PAD_TOKEN_INDEX):
180
+ input_ids_list, labels_list, pixel_values_list = [], [], []
181
+
182
+ for sample in instances:
183
+ input_ids_list.append(torch.LongTensor(sample['input_ids']))
184
+ labels_list.append(torch.LongTensor(sample['labels']))
185
+
186
+ if 'pixel_values' in sample:
187
+ pixel_values_list.append(sample['pixel_values'])
188
+
189
+ ori_length = [len(input_ids_) for input_ids_ in input_ids_list]
190
+ # right padding
191
+ if len(instances) > 1:
192
+ input_ids = pad_sequence(
193
+ input_ids_list, batch_first=True, padding_value=pad_index)
194
+ labels = pad_sequence(
195
+ labels_list, batch_first=True, padding_value=IGNORE_INDEX)
196
+ else:
197
+ input_ids = torch.stack(input_ids_list)
198
+ labels = torch.stack(labels_list)
199
+
200
+ attention_mask = torch.zeros_like(input_ids).bool()
201
+ for i, length in enumerate(ori_length):
202
+ attention_mask[i, :length] = True # right padding
203
+
204
+ data_dict = {
205
+ 'input_ids': input_ids,
206
+ 'attention_mask': attention_mask,
207
+ 'labels': labels,
208
+ 'pixel_values': torch.stack(pixel_values_list) if len(pixel_values_list) > 0 else None
209
+ }
210
+
211
+ return {'data': data_dict, 'data_samples': None}
212
+
213
+
214
+ class CollateConcat(object):
215
+ def __init__(self, collate_fns, keys):
216
+ self.keys = keys
217
+ self.collate_fns = {}
218
+ for key, collate_fn in zip(keys, collate_fns):
219
+ func = collate_fn.pop('type')
220
+ self.collate_fns[key] = partial(func, **collate_fn)
221
+
222
+ def __call__(self, data_samples):
223
+ data_samples = [data_sample for data_sample in data_samples if len(data_sample) > 0]
224
+ data_dict = {}
225
+ key = data_samples[0]['type']
226
+ data_dict[key] = self.collate_fns[key](data_samples)['data']
227
+
228
+ return {'data': data_dict, 'data_samples': None}
src/datasets/image2image/edit_datasets.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ from einops import rearrange
4
+ from src.datasets.utils import crop2square
5
+ from src.datasets.text2image.caption_datasets import CaptionDataset
6
+ from PIL import Image
7
+ import os
8
+
9
+ class ImageEditDataset(CaptionDataset):
10
+ def _process_image(self, image):
11
+ assert self.image_process != 'crop2square'
12
+ return super()._process_image(image)['pixel_values']
13
+ # image = image.resize(size=(self.image_size, self.image_size))
14
+ # pixel_values = torch.from_numpy(np.array(image)).float()
15
+ # pixel_values = pixel_values / 255
16
+ # pixel_values = 2 * pixel_values - 1
17
+ # pixel_values = rearrange(pixel_values, 'h w c -> c h w')
18
+ # return pixel_values
19
+
20
+ def _process_text(self, text):
21
+ prompt_template = self.prompt_template
22
+ image_tokens = prompt_template['IMG_START_TOKEN'] + \
23
+ prompt_template['IMG_CONTEXT_TOKEN'] * self.image_length + \
24
+ prompt_template['IMG_END_TOKEN']
25
+ prompt = f'{image_tokens}\n{text}'
26
+ prompt = self.prompt_template['INSTRUCTION'].format(input=prompt)
27
+ if self.prompt_template.get('IMG_START_TOKEN_FOR_GENERATION', True):
28
+ prompt += prompt_template['IMG_START_TOKEN']
29
+ input_ids = self.tokenizer.encode(prompt, return_tensors='pt', **self.tokenizer_kwargs)[0]
30
+
31
+ return dict(input_ids=input_ids)
32
+
33
+ def __getitem__(self, idx):
34
+ if self.debug:
35
+ idx = 0
36
+ try:
37
+ data_sample = self.data_list[idx]
38
+ if self.image_folder is not None:
39
+ source_image = Image.open(os.path.join(self.image_folder,data_sample['input_image'][0])).convert('RGB')
40
+ target_image = Image.open(os.path.join(self.image_folder,data_sample['output_image'])).convert('RGB')
41
+ else:
42
+ source_image = Image.open(data_sample['input_image'][0]).convert('RGB')
43
+ target_image = Image.open(data_sample['output_image']).convert('RGB')
44
+ # prompt = self._read_json(data_sample['annotation'])[self.cap_source]
45
+ prompt = data_sample['instruction']
46
+
47
+ pixel_values_src = self._process_image(source_image)
48
+ pixel_values = self._process_image(target_image)
49
+
50
+ data = self._process_text(prompt) if self.tokenizer is not None else dict()
51
+
52
+ data.update(
53
+ pixel_values_src=pixel_values_src, pixel_values=pixel_values,
54
+ image_dir=self.image_folder,type='image2image', text=prompt)
55
+
56
+ return data
57
+
58
+ except Exception as e:
59
+ print(f"Error when reading {self.data_path}:{self.data_list[idx]}: {e}", flush=True)
60
+ return self._retry()
61
+
62
+
63
+ class ReconstructDataset(CaptionDataset):
64
+ def _process_image(self, image):
65
+ assert self.image_process != 'crop2square'
66
+ return super()._process_image(image)['pixel_values']
67
+
68
+ def __getitem__(self, idx):
69
+ if self.debug:
70
+ idx = 0
71
+ try:
72
+ data_sample = self.data_list[idx]
73
+ image = self._read_image(data_sample['image']).convert('RGB')
74
+ prompt = "Keep the image as it is."
75
+ pixel_values = pixel_values_src = self._process_image(image)
76
+
77
+ data = self._process_text(prompt) if self.tokenizer is not None else dict()
78
+
79
+ data.update(
80
+ pixel_values_src=pixel_values_src, pixel_values=pixel_values,
81
+ image_dir=self.image_folder, image_file=data_sample['image'],
82
+ type='image2image', text=prompt)
83
+
84
+ return data
85
+
86
+ except Exception as e:
87
+ print(f"Error when reading {self.data_path}:{self.data_list[idx]}: {e}", flush=True)
88
+ return self._retry()
src/datasets/samplers/multi_source_sampler.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) OpenMMLab. All rights reserved.
2
+ import itertools
3
+ from typing import Iterator, List, Optional, Sized, Union
4
+ import torch
5
+ from mmengine.dist import get_dist_info, sync_random_seed
6
+ from torch.utils.data import Sampler
7
+
8
+
9
+ class FixedBatchMultiSourceSampler(Sampler):
10
+ r"""Multi-Source Infinite Sampler.
11
+
12
+ According to the sampling ratio, sample data from different
13
+ datasets to form batches.
14
+
15
+ Args:
16
+ repeat (tuple): repeat factor
17
+ dataset (Sized): The dataset.
18
+ batch_size (int): Size of mini-batch.
19
+ shuffle (bool): Whether shuffle the dataset or not. Defaults to True.
20
+ seed (int, optional): Random seed. If None, set a random seed.
21
+ Defaults to None.
22
+ """
23
+
24
+ def __init__(self,
25
+ repeat,
26
+ dataset: Sized,
27
+ batch_size: int,
28
+ shuffle: bool = True,
29
+ seed: Optional[int] = None) -> None:
30
+
31
+ assert hasattr(dataset, 'cumulative_sizes'),\
32
+ f'The dataset must be ConcatDataset, but get {dataset}'
33
+ assert isinstance(batch_size, int) and batch_size > 0, \
34
+ 'batch_size must be a positive integer value, ' \
35
+ f'but got batch_size={batch_size}'
36
+ assert len(repeat) == len(dataset.cumulative_sizes), \
37
+ 'The length of repeat must be equal to ' \
38
+ f'the number of datasets, but got repeat={repeat}'
39
+
40
+ rank, world_size = get_dist_info()
41
+ self.rank = rank
42
+ self.world_size = world_size
43
+
44
+ self.dataset = dataset
45
+ self.repeat = repeat
46
+ self.cumulative_sizes = [0] + dataset.cumulative_sizes
47
+ self.batch_size = batch_size
48
+
49
+ self.seed = sync_random_seed() if seed is None else seed
50
+ self.shuffle = shuffle
51
+ self.source2inds = {
52
+ source: self._indices_of_rank(len(ds))
53
+ for source, ds in enumerate(dataset.datasets)
54
+ }
55
+
56
+ def _infinite_indices(self, sample_size: int) -> Iterator[int]:
57
+ """Infinitely yield a sequence of indices."""
58
+ g = torch.Generator()
59
+ g.manual_seed(self.seed)
60
+ while True:
61
+ if self.shuffle:
62
+ yield from torch.randperm(sample_size, generator=g).tolist()
63
+ else:
64
+ yield from torch.arange(sample_size).tolist()
65
+
66
+ def _indices_of_rank(self, sample_size: int) -> Iterator[int]:
67
+ """Slice the infinite indices by rank."""
68
+ yield from itertools.islice(
69
+ self._infinite_indices(sample_size), self.rank, None,
70
+ self.world_size)
71
+
72
+ def __len__(self) -> int:
73
+ return len(self.dataset)
74
+
75
+ def set_epoch(self, epoch: int) -> None:
76
+ """Not supported in `epoch-based runner."""
77
+ pass
78
+
79
+ def __iter__(self) -> Iterator[int]:
80
+ while True:
81
+ for source, repeat in enumerate(self.repeat):
82
+ for _ in range(repeat):
83
+ batch_buffer_per_source = []
84
+ while len(batch_buffer_per_source) < self.batch_size:
85
+ idx = next(self.source2inds[source])
86
+ idx += self.cumulative_sizes[source]
87
+ batch_buffer_per_source.append(idx)
88
+
89
+ yield from batch_buffer_per_source
90
+
91
+
92
+
93
+ class MultiSourceSampler(Sampler):
94
+ def __init__(self,
95
+ repeats,
96
+ dataset: Sized,
97
+ batch_sizes: list[int],
98
+ shuffle: bool = True,
99
+ seed: Optional[int] = None) -> None:
100
+
101
+ assert hasattr(dataset, 'cumulative_sizes'),\
102
+ f'The dataset must be ConcatDataset, but get {dataset}'
103
+
104
+ assert isinstance(batch_sizes, list), \
105
+ f'source_ratio must be a list, but got batch_sizes={batch_sizes}'
106
+ assert len(batch_sizes) == len(dataset.cumulative_sizes), \
107
+ 'The length of batch_sizes must be equal to ' \
108
+ f'the number of datasets, but got batch_sizes={batch_sizes}'
109
+
110
+ rank, world_size = get_dist_info()
111
+ self.rank = rank
112
+ self.world_size = world_size
113
+
114
+ self.dataset = dataset
115
+ self.cumulative_sizes = [0] + dataset.cumulative_sizes
116
+ self.batch_sizes = batch_sizes
117
+
118
+
119
+ self.seed = sync_random_seed() if seed is None else seed
120
+ self.shuffle = shuffle
121
+ self.source2inds = {
122
+ source: self._indices_of_rank(len(ds))
123
+ for source, ds in enumerate(dataset.datasets)
124
+ }
125
+
126
+ self.repeats = repeats
127
+ assert len(self.repeats) == len(self.batch_sizes)
128
+
129
+ def _infinite_indices(self, sample_size: int) -> Iterator[int]:
130
+ """Infinitely yield a sequence of indices."""
131
+ g = torch.Generator()
132
+ g.manual_seed(self.seed)
133
+ while True:
134
+ if self.shuffle:
135
+ yield from torch.randperm(sample_size, generator=g).tolist()
136
+ else:
137
+ yield from torch.arange(sample_size).tolist()
138
+
139
+ def _indices_of_rank(self, sample_size: int) -> Iterator[int]:
140
+ """Slice the infinite indices by rank."""
141
+ yield from itertools.islice(
142
+ self._infinite_indices(sample_size), self.rank, None,
143
+ self.world_size)
144
+
145
+
146
+ def __len__(self) -> int:
147
+ return len(self.dataset)
148
+
149
+ def set_epoch(self, epoch: int) -> None:
150
+ """Not supported in `epoch-based runner."""
151
+ pass
152
+
153
+ def __iter__(self) -> Iterator[int]:
154
+ while True:
155
+ for source, (batch_size, repeat) in enumerate(zip(self.batch_sizes, self.repeats)):
156
+ for _ in range(repeat):
157
+ batch_buffer_per_source = []
158
+ while len(batch_buffer_per_source) < batch_size:
159
+ idx = next(self.source2inds[source])
160
+ idx += self.cumulative_sizes[source]
161
+ batch_buffer_per_source.append(idx)
162
+
163
+ yield from batch_buffer_per_source
164
+
165
+ @property
166
+ def batch_size(self):
167
+ batch_size_sum = sum([batch_size * repeat for batch_size, repeat in zip(self.batch_sizes, self.repeats)])
168
+ batch_size_ave = batch_size_sum // sum(self.repeats)
169
+
170
+ return batch_size_ave
171
+
172
+
173
+ class MultiSourceBatchSampler(Sampler[list[int]]):
174
+ def __init__(
175
+ self,
176
+ sampler: Union[FixedBatchMultiSourceSampler, MultiSourceSampler],
177
+ batch_sizes: list[int],
178
+ repeats: list[int],
179
+ **kwargs
180
+ ) -> None:
181
+ self.sampler = sampler
182
+ self.batch_sizes = batch_sizes
183
+ self.repeats = repeats
184
+
185
+ def __iter__(self) -> Iterator[list[int]]:
186
+ # Implemented based on the benchmarking in https://github.com/pytorch/pytorch/pull/76951
187
+ sampler_iter = iter(self.sampler)
188
+
189
+ while True:
190
+ for source, (batch_size, repeat) in enumerate(zip(self.batch_sizes, self.repeats)):
191
+ for _ in range(repeat):
192
+ batch = [*itertools.islice(sampler_iter, batch_size)]
193
+ yield batch
194
+
195
+ @property
196
+ def batch_size(self):
197
+ batch_size_sum = sum([batch_size * repeat for batch_size, repeat in zip(self.batch_sizes, self.repeats)])
198
+ batch_size_ave = batch_size_sum // sum(self.repeats)
199
+
200
+ return batch_size_ave
201
+
202
+ def __len__(self) -> int:
203
+ return len(self.sampler) // self.batch_size
src/datasets/text2image/blip3_o.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import os
3
+ from src.datasets.text2image.caption_datasets import CaptionDataset
4
+ from PIL import Image
5
+
6
+ class BLIP3oDataset(CaptionDataset):
7
+ def __getitem__(self, idx):
8
+ if self.debug:
9
+ idx = 0
10
+ try:
11
+ data_sample = self.data_list[idx]
12
+
13
+ if self.image_tokens_folder is not None:
14
+ image_tokens = torch.load(os.path.join(self.image_tokens_folder,
15
+ data_sample['image'] + '.pt')).long()
16
+ data = dict(image_tokens=image_tokens)
17
+ elif self.latents_ceph_folder is not None:
18
+ image_latents = torch.load(
19
+ self._read_ceph(
20
+ os.path.join(
21
+ self.latents_ceph_folder, data_sample['image'] + '.pt'
22
+ )
23
+ )
24
+ )
25
+ data = dict(image_latents=image_latents)
26
+ elif self.image_latents_folder is not None:
27
+ image_latents = torch.load(os.path.join(self.image_latents_folder,
28
+ data_sample['image'] + '.pt'))
29
+ data = dict(image_latents=image_latents)
30
+ else:
31
+ if self.image_folder is not None:
32
+ image = Image.open(os.path.join(self.image_folder,data_sample['image_path'])).convert('RGB')
33
+ else:
34
+ image = Image.open(data_sample['image_path']).convert('RGB')
35
+ data = self._process_image(image)
36
+
37
+ caption = data_sample['txt']
38
+
39
+ # print(caption)
40
+ data["pixel_init"] = image
41
+ data.update(self._process_text(caption))
42
+ data.update(image_dir=self.image_folder, image_file=None,
43
+ type='text2image',text=caption)
44
+
45
+ return data
46
+
47
+ except Exception as e:
48
+ print(f"Error when reading {self.data_path}:{self.data_list[idx]}: {e}", flush=True)
49
+ return self._retry()
src/datasets/text2image/caption_datasets.py ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from torch.utils.data import Dataset
2
+ from PIL import Image
3
+ import os
4
+ import io
5
+ import json
6
+ import random
7
+ import torch
8
+ try:
9
+ from aoss_client.client import Client
10
+ except:
11
+ try:
12
+ from petrel_client.client import Client
13
+ except:
14
+ Client = None
15
+ from glob import glob
16
+ from xtuner.registry import BUILDER
17
+ from src.datasets.utils import crop2square, resize_image_fix_pixels, resize_image_dynamic
18
+ from einops import rearrange
19
+ import numpy as np
20
+
21
+
22
+ class CaptionDataset(Dataset):
23
+ def __init__(self,
24
+ data_path,
25
+ image_folder=None,
26
+ debug=False,
27
+ image_processor=None,
28
+ image_process='crop2square',
29
+ ceph_folder=None,
30
+ latents_ceph_folder=None,
31
+ ceph_config=None,
32
+ tokenizer=None,
33
+ prompt_template=None,
34
+ max_length=2048,
35
+ min_image_size=80,
36
+ image_size=256,
37
+ image_length=256,
38
+ unit_image_size=32,
39
+ image_tokens_folder=None,
40
+ image_latents_folder=None,
41
+ cap_folder=None,
42
+ cap_source='caption',
43
+ tokenizer_kwargs=dict(add_special_tokens=True),
44
+ unconditional=0.1
45
+ ):
46
+ super().__init__()
47
+ self.data_path = data_path
48
+ self._load_data(data_path)
49
+ self.image_folder = image_folder
50
+ self.cap_folder = cap_folder
51
+ self.cap_source = cap_source
52
+ self.debug = debug
53
+
54
+ if image_processor is not None:
55
+ self.image_processor = BUILDER.build(image_processor)
56
+ else:
57
+ self.image_processor = None
58
+
59
+ if tokenizer is not None:
60
+ self.tokenizer = BUILDER.build(tokenizer)
61
+ else:
62
+ self.tokenizer = None
63
+ self.prompt_template = prompt_template
64
+
65
+ self.max_length = max_length
66
+ self.image_process = image_process
67
+ self.image_length = image_length
68
+ self.image_tokens_folder = image_tokens_folder
69
+ self.image_latents_folder = image_latents_folder
70
+ self.min_image_size = min_image_size
71
+ self.image_size = image_size
72
+ self.unit_image_size = unit_image_size
73
+ self.unconditional = unconditional
74
+ self.tokenizer_kwargs = tokenizer_kwargs
75
+
76
+ self.FILE_CLIENT = None
77
+ self.ceph_folder = ceph_folder
78
+ self.ceph_config = ceph_config
79
+ self.latents_ceph_folder = latents_ceph_folder
80
+ self.use_ceph = ((Client is not None) and (ceph_config is not None) and os.path.exists(ceph_config))
81
+
82
+ def _load_data(self, data_path: str): # image path and annotation path are saved in a json file
83
+ if data_path.endswith('.json'):
84
+ with open(data_path, 'r') as f:
85
+ self.data_list = json.load(f)
86
+ else:
87
+ json_files = glob(f"{data_path}/*.json")
88
+ data_list = []
89
+ for json_file in json_files:
90
+ with open(json_file, 'r') as f:
91
+ data_list += json.load(f)
92
+
93
+ self.data_list = data_list
94
+
95
+ print(f"Load {len(self.data_list)} data samples from {data_path}", flush=True)
96
+
97
+ def __len__(self):
98
+ return len(self.data_list)
99
+
100
+ def _read_ceph(self, ceph_path):
101
+ if self.FILE_CLIENT is None:
102
+ self.FILE_CLIENT = Client(self.ceph_config)
103
+ data_bytes = self.FILE_CLIENT.get(ceph_path)
104
+
105
+ return io.BytesIO(data_bytes)
106
+
107
+ def _read_image(self, image_file):
108
+ if self.image_folder is None:
109
+ assert self.use_ceph
110
+ assert self.ceph_folder is not None
111
+ image = Image.open(
112
+ self._read_ceph(
113
+ os.path.join(self.ceph_folder, image_file)
114
+ )
115
+ )
116
+ else:
117
+ image = Image.open(
118
+ os.path.join(self.image_folder, image_file)
119
+ )
120
+ assert image.width > self.min_image_size and image.height > self.min_image_size, f"Image: {image.size}"
121
+ assert image.width / image.height > 0.1, f"Image: {image.size}"
122
+ assert image.width / image.height < 10, f"Image: {image.size}"
123
+ return image.convert('RGB')
124
+
125
+ def _read_json(self, annotation_file):
126
+ if self.cap_folder is None:
127
+ assert self.use_ceph
128
+ assert self.ceph_folder is not None
129
+ annotation = json.load(
130
+ self._read_ceph(
131
+ os.path.join(self.ceph_folder, annotation_file)
132
+ )
133
+ )
134
+ else:
135
+ with open(os.path.join(self.cap_folder, annotation_file), 'r') as f:
136
+ annotation = json.load(f)
137
+
138
+ return annotation
139
+
140
+ def _process_image(self, image):
141
+ data = dict()
142
+ if self.image_process == 'crop2square':
143
+ image = crop2square(image)
144
+ image = image.resize(size=(self.image_size, self.image_size))
145
+ elif self.image_process == 'dynamic': # dynamic and make sure the largest edge <= self.image_size
146
+ image = resize_image_dynamic(x=image, image_size=self.image_size, unit_image_size=self.unit_image_size)
147
+ elif self.image_process == 'fix_pixels': # fix pixels contain radio of image
148
+ # import pdb; pdb.set_trace()
149
+ image = resize_image_fix_pixels(x=image, image_size=self.image_size, unit_image_size=self.unit_image_size)
150
+ elif self.image_process == 'resize2square':
151
+ image = image.resize(size=(self.image_size, self.image_size))
152
+ else:
153
+ raise NotImplementedError
154
+
155
+ # assert image.width <= self.image_size
156
+ # assert image.height <= self.image_size
157
+ assert image.width % self.unit_image_size == 0
158
+ assert image.height % self.unit_image_size == 0
159
+
160
+ pixel_values = torch.from_numpy(np.array(image)).float()
161
+ pixel_values = pixel_values / 255
162
+ pixel_values = 2 * pixel_values - 1
163
+ pixel_values = rearrange(pixel_values, 'h w c -> c h w')
164
+
165
+ data.update(pixel_values=pixel_values)
166
+ return data
167
+
168
+ def _process_text(self, text):
169
+ if self.tokenizer is None:
170
+ return {}
171
+ if random.uniform(0, 1) < self.unconditional:
172
+ prompt = self.prompt_template['CFG']
173
+ else:
174
+ prompt = self.prompt_template['GENERATION'].format(input=text.strip())
175
+
176
+ prompt = self.prompt_template['INSTRUCTION'].format(input=prompt)
177
+ if self.prompt_template.get('IMG_START_TOKEN_FOR_GENERATION', True):
178
+ prompt += self.prompt_template['IMG_START_TOKEN']
179
+ input_ids = self.tokenizer.encode(prompt, return_tensors='pt', **self.tokenizer_kwargs)[0]
180
+
181
+ return dict(input_ids=input_ids[:self.max_length])
182
+
183
+ def _retry(self):
184
+ return self.__getitem__(random.choice(range(self.__len__())))
185
+
186
+ def __getitem__(self, idx):
187
+ if self.debug:
188
+ idx = 0
189
+ try:
190
+ data_sample = self.data_list[idx]
191
+
192
+ if self.image_tokens_folder is not None:
193
+ image_tokens = torch.load(os.path.join(self.image_tokens_folder,
194
+ data_sample['image'] + '.pt')).long()
195
+ data = dict(image_tokens=image_tokens)
196
+ elif self.latents_ceph_folder is not None:
197
+ image_latents = torch.load(
198
+ self._read_ceph(
199
+ os.path.join(
200
+ self.latents_ceph_folder, data_sample['image'] + '.pt'
201
+ )
202
+ )
203
+ )
204
+ data = dict(image_latents=image_latents)
205
+ elif self.image_latents_folder is not None:
206
+ image_latents = torch.load(os.path.join(self.image_latents_folder,
207
+ data_sample['image'] + '.pt'))
208
+ data = dict(image_latents=image_latents)
209
+ else:
210
+ image = self._read_image(data_sample['image']).convert('RGB')
211
+ data = self._process_image(image)
212
+
213
+ caption = self._read_json(data_sample['annotation'])[self.cap_source]
214
+ # caption = self._read_json(data_sample['annotation'])
215
+ # print(caption)
216
+
217
+ data.update(self._process_text(caption))
218
+ data['pixel_init'] = image
219
+ data.update(image_dir=self.image_folder, image_file=data_sample['image'],
220
+ type='text2image',text=caption)
221
+
222
+ return data
223
+
224
+ except Exception as e:
225
+ print(f"Error when reading {self.data_path}:{self.data_list[idx]}: {e}", flush=True)
226
+ return self._retry()
src/datasets/utils.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import random
3
+ from xtuner.dataset.utils import get_bos_eos_token_ids
4
+ from xtuner.utils import DEFAULT_IMAGE_TOKEN, IGNORE_INDEX, IMAGE_TOKEN_INDEX
5
+ import json
6
+ import math
7
+
8
+ INPUT_IMAGE_TOKEN_INDEX = IMAGE_TOKEN_INDEX
9
+ OUTPUT_IMAGE_TOKEN_INDEX = -300
10
+
11
+
12
+ def resize_image_fix_pixels(x, image_size, unit_image_size=32):
13
+ # import pdb; pdb.set_trace()
14
+ w, h = x.size
15
+ ratio = image_size / ((h * w) ** 0.5)
16
+
17
+ target_h = math.ceil(h * ratio / unit_image_size) * unit_image_size
18
+ target_w = math.ceil(w * ratio / unit_image_size) * unit_image_size
19
+
20
+ x = x.resize(size=(target_w, target_h))
21
+
22
+ return x
23
+
24
+
25
+ def resize_image_dynamic(x, image_size, unit_image_size=32):
26
+ w, h = x.size
27
+ if w >= h and w >= image_size:
28
+ target_w = image_size
29
+ target_h = h * (target_w / w)
30
+ target_h = math.ceil(target_h / unit_image_size) * unit_image_size
31
+
32
+ elif h >= w and h >= image_size:
33
+ target_h = image_size
34
+ target_w = w * (target_h / h)
35
+ target_w = math.ceil(target_w / unit_image_size) * unit_image_size
36
+
37
+ else:
38
+ target_h = math.ceil(h / unit_image_size) * unit_image_size
39
+ target_w = math.ceil(w / unit_image_size) * unit_image_size
40
+
41
+ x = x.resize(size=(target_w, target_h))
42
+
43
+ return x
44
+
45
+
46
+
47
+ def crop2square(pil_img):
48
+ width, height = pil_img.width, pil_img.height
49
+
50
+ if width > height:
51
+ y0, y1 = 0, height
52
+ x0 = random.randint(0, width - height) # [0, w - h]
53
+ x1 = x0 + height # [h, w]
54
+ else:
55
+ x0, x1 = 0, width
56
+ y0 = random.randint(0, height - width) # [0, h - w]
57
+ y1 = y0 + width # [w, h]
58
+
59
+ return pil_img.crop(box=(x0, y0, x1, y1))
60
+
61
+
62
+ def load_jsonl(json_file):
63
+ with open(json_file) as f:
64
+ lines = f.readlines()
65
+ data = []
66
+ for line in lines:
67
+ data.append(json.loads(line))
68
+ return data
69
+
70
+
71
+ def encode_fn(example,
72
+ tokenizer,
73
+ max_length=None,
74
+ image_length=1,
75
+ input_ids_with_output=True,
76
+ with_image_token=False,
77
+ truncation='right'):
78
+ """We only support the following three scenarios:
79
+
80
+ 1. Incremental pretraining dataset.
81
+ example['conversation'] = [
82
+ {
83
+ 'input': '',
84
+ 'output': '### Human: Can you write xxx'
85
+ }
86
+ ]
87
+
88
+ 2. Single-turn conversation dataset.
89
+ example['conversation'] = [
90
+ {
91
+ 'input': 'Give three tips for staying healthy.',
92
+ 'output': '1.Eat a balanced diet xxx'
93
+ }
94
+ ]
95
+
96
+ 3. Multi-turn conversation dataset.
97
+ example['conversation'] = [
98
+ {
99
+ 'input': 'Give three tips for staying healthy.',
100
+ 'output': '1.Eat a balanced diet xxx'
101
+ },
102
+ {
103
+ 'input': 'Please expand on the second point.',
104
+ 'output': 'Here is an expanded explanation of the xxx'
105
+ }
106
+ ]
107
+ """
108
+ bos_token_id, eos_token_id = get_bos_eos_token_ids(tokenizer)
109
+ is_multi_turn_conversation = len(example['conversation']) > 1
110
+ if is_multi_turn_conversation:
111
+ assert input_ids_with_output
112
+
113
+ input_ids, labels = [], []
114
+ next_needs_bos_token = True
115
+ for single_turn_conversation in example['conversation']:
116
+ input = single_turn_conversation['input']
117
+ if DEFAULT_IMAGE_TOKEN in input and with_image_token:
118
+ chunk_encode = [
119
+ tokenizer.encode(chunk, add_special_tokens=False)
120
+ for chunk in input.split(DEFAULT_IMAGE_TOKEN)
121
+ ]
122
+ assert len(chunk_encode) == 2
123
+ input_encode = []
124
+ for idx, cur_chunk_encode in enumerate(chunk_encode):
125
+ input_encode.extend(cur_chunk_encode)
126
+ if idx != len(chunk_encode) - 1:
127
+ # input_encode.append(IMAGE_TOKEN_INDEX)
128
+ input_encode += [IMAGE_TOKEN_INDEX] * image_length
129
+ else:
130
+ input_encode = tokenizer.encode(input, add_special_tokens=False)
131
+ if next_needs_bos_token:
132
+ input_ids += bos_token_id
133
+ labels += [IGNORE_INDEX] * len(bos_token_id)
134
+ input_ids += input_encode
135
+ labels += [IGNORE_INDEX] * len(input_encode)
136
+ if input_ids_with_output and 'output' in single_turn_conversation:
137
+ # Add output
138
+ output_with_loss = single_turn_conversation.get(
139
+ 'output_with_loss', True)
140
+ output = single_turn_conversation['output']
141
+ if DEFAULT_IMAGE_TOKEN in output and with_image_token:
142
+ chunk_encode = [
143
+ tokenizer.encode(chunk, add_special_tokens=False)
144
+ for chunk in output.split(DEFAULT_IMAGE_TOKEN)
145
+ ]
146
+ assert len(chunk_encode) == 2
147
+ output_encode = []
148
+ for idx, cur_chunk_encode in enumerate(chunk_encode):
149
+ output_encode.extend(cur_chunk_encode)
150
+ if idx != len(chunk_encode) - 1:
151
+ output_encode += [IMAGE_TOKEN_INDEX] * image_length
152
+ else:
153
+ output_encode = tokenizer.encode(output, add_special_tokens=False)
154
+ # output_encode = tokenizer.encode(output, add_special_tokens=False)
155
+ input_ids += output_encode
156
+ if output_with_loss:
157
+ labels += copy.deepcopy(output_encode)
158
+ else:
159
+ labels += [IGNORE_INDEX] * len(output_encode)
160
+ # Add EOS_TOKEN (with loss)
161
+ if single_turn_conversation.get('need_eos_token', True):
162
+ next_needs_bos_token = True
163
+ input_ids += eos_token_id
164
+ if output_with_loss:
165
+ labels += copy.deepcopy(eos_token_id)
166
+ else:
167
+ labels += [IGNORE_INDEX] * len(eos_token_id)
168
+ else:
169
+ next_needs_bos_token = False
170
+ # Add SEP (without loss)
171
+ sep = single_turn_conversation.get('sep', '')
172
+ if sep != '':
173
+ sep_encode = tokenizer.encode(sep, add_special_tokens=False)
174
+ input_ids += sep_encode
175
+ labels += [IGNORE_INDEX] * len(sep_encode)
176
+
177
+ if max_length is not None and len(input_ids) > max_length:
178
+ if truncation == 'right':
179
+ input_ids = input_ids[:max_length]
180
+ labels = labels[:max_length]
181
+ elif truncation == 'left':
182
+ input_ids = input_ids[-max_length:]
183
+ labels = labels[-max_length:]
184
+ else:
185
+ assert truncation is None
186
+ return {'input_ids': input_ids, 'labels': labels}
src/models/connector/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from .configuration_connector import ConnectorConfig
2
+ from .modeling_connector import ConnectorEncoder
src/models/connector/configuration_connector.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers.configuration_utils import PretrainedConfig
2
+ from transformers.utils import logging
3
+
4
+ logger = logging.get_logger(__name__)
5
+
6
+
7
+ class ConnectorConfig(PretrainedConfig):
8
+ def __init__(
9
+ self,
10
+ hidden_size=768,
11
+ intermediate_size=3072,
12
+ num_hidden_layers=12,
13
+ num_attention_heads=12,
14
+ hidden_act="gelu_pytorch_tanh",
15
+ layer_norm_eps=1e-6,
16
+ attention_dropout=0.0,
17
+ **kwargs,
18
+ ):
19
+ super().__init__(**kwargs)
20
+
21
+ self.hidden_size = hidden_size
22
+ self.intermediate_size = intermediate_size
23
+ self.num_hidden_layers = num_hidden_layers
24
+ self.num_attention_heads = num_attention_heads
25
+ self.attention_dropout = attention_dropout
26
+ self.layer_norm_eps = layer_norm_eps
27
+ self.hidden_act = hidden_act
src/models/connector/modeling_connector.py ADDED
@@ -0,0 +1,507 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2024 Google AI and The HuggingFace Team. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """PyTorch Connector model."""
16
+
17
+ import math
18
+ import warnings
19
+ from typing import Any, Optional, Tuple, Union
20
+
21
+ import torch
22
+ import torch.utils.checkpoint
23
+ from torch import nn
24
+ from torch.nn.init import _calculate_fan_in_and_fan_out
25
+
26
+ from transformers.activations import ACT2FN
27
+ from transformers.modeling_attn_mask_utils import _prepare_4d_attention_mask
28
+ from transformers.modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling, ImageClassifierOutput
29
+ from transformers.modeling_utils import PreTrainedModel
30
+ from transformers.utils import (
31
+ ModelOutput,
32
+ is_flash_attn_2_available,
33
+ is_flash_attn_greater_or_equal_2_10,
34
+ logging,
35
+ replace_return_docstrings,
36
+ torch_int,
37
+ )
38
+ from .configuration_connector import ConnectorConfig
39
+
40
+
41
+ if is_flash_attn_2_available():
42
+ from transformers.modeling_flash_attention_utils import _flash_attention_forward
43
+
44
+
45
+ logger = logging.get_logger(__name__)
46
+
47
+
48
+ def init_weights(module):
49
+ """Initialize the weights"""
50
+ if isinstance(module, nn.Embedding):
51
+ default_flax_embed_init(module.weight)
52
+ elif isinstance(module, ConnectorAttention):
53
+ nn.init.xavier_uniform_(module.q_proj.weight)
54
+ nn.init.xavier_uniform_(module.k_proj.weight)
55
+ nn.init.xavier_uniform_(module.v_proj.weight)
56
+ nn.init.xavier_uniform_(module.out_proj.weight)
57
+ nn.init.zeros_(module.q_proj.bias)
58
+ nn.init.zeros_(module.k_proj.bias)
59
+ nn.init.zeros_(module.v_proj.bias)
60
+ nn.init.zeros_(module.out_proj.bias)
61
+ elif isinstance(module, ConnectorMLP):
62
+ nn.init.xavier_uniform_(module.fc1.weight)
63
+ nn.init.xavier_uniform_(module.fc2.weight)
64
+ nn.init.normal_(module.fc1.bias, std=1e-6)
65
+ nn.init.normal_(module.fc2.bias, std=1e-6)
66
+ elif isinstance(module, (nn.Linear, nn.Conv2d)):
67
+ lecun_normal_(module.weight)
68
+ if module.bias is not None:
69
+ nn.init.zeros_(module.bias)
70
+ elif isinstance(module, nn.LayerNorm):
71
+ module.bias.data.zero_()
72
+ module.weight.data.fill_(1.0)
73
+
74
+
75
+ def _trunc_normal_(tensor, mean, std, a, b):
76
+ # Cut & paste from PyTorch official master until it's in a few official releases - RW
77
+ # Method based on https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf
78
+ def norm_cdf(x):
79
+ # Computes standard normal cumulative distribution function
80
+ return (1.0 + math.erf(x / math.sqrt(2.0))) / 2.0
81
+
82
+ if (mean < a - 2 * std) or (mean > b + 2 * std):
83
+ warnings.warn(
84
+ "mean is more than 2 std from [a, b] in nn.init.trunc_normal_. "
85
+ "The distribution of values may be incorrect.",
86
+ stacklevel=2,
87
+ )
88
+
89
+ # Values are generated by using a truncated uniform distribution and
90
+ # then using the inverse CDF for the normal distribution.
91
+ # Get upper and lower cdf values
92
+ l = norm_cdf((a - mean) / std)
93
+ u = norm_cdf((b - mean) / std)
94
+
95
+ # Uniformly fill tensor with values from [l, u], then translate to
96
+ # [2l-1, 2u-1].
97
+ tensor.uniform_(2 * l - 1, 2 * u - 1)
98
+
99
+ # Use inverse cdf transform for normal distribution to get truncated
100
+ # standard normal
101
+ tensor.erfinv_()
102
+
103
+ # Transform to proper mean, std
104
+ tensor.mul_(std * math.sqrt(2.0))
105
+ tensor.add_(mean)
106
+
107
+ # Clamp to ensure it's in the proper range
108
+ tensor.clamp_(min=a, max=b)
109
+
110
+
111
+ def trunc_normal_tf_(
112
+ tensor: torch.Tensor, mean: float = 0.0, std: float = 1.0, a: float = -2.0, b: float = 2.0
113
+ ) -> torch.Tensor:
114
+ """Fills the input Tensor with values drawn from a truncated
115
+ normal distribution. The values are effectively drawn from the
116
+ normal distribution :math:`\\mathcal{N}(\text{mean}, \text{std}^2)`
117
+ with values outside :math:`[a, b]` redrawn until they are within
118
+ the bounds. The method used for generating the random values works
119
+ best when :math:`a \\leq \text{mean} \\leq b`.
120
+
121
+ NOTE: this 'tf' variant behaves closer to Tensorflow / JAX impl where the
122
+ bounds [a, b] are applied when sampling the normal distribution with mean=0, std=1.0
123
+ and the result is subsequently scaled and shifted by the mean and std args.
124
+
125
+ Args:
126
+ tensor: an n-dimensional `torch.Tensor`
127
+ mean: the mean of the normal distribution
128
+ std: the standard deviation of the normal distribution
129
+ a: the minimum cutoff value
130
+ b: the maximum cutoff value
131
+ """
132
+ with torch.no_grad():
133
+ _trunc_normal_(tensor, 0, 1.0, a, b)
134
+ tensor.mul_(std).add_(mean)
135
+
136
+
137
+ def variance_scaling_(tensor, scale=1.0, mode="fan_in", distribution="normal"):
138
+ fan_in, fan_out = _calculate_fan_in_and_fan_out(tensor)
139
+ if mode == "fan_in":
140
+ denom = fan_in
141
+ elif mode == "fan_out":
142
+ denom = fan_out
143
+ elif mode == "fan_avg":
144
+ denom = (fan_in + fan_out) / 2
145
+
146
+ variance = scale / denom
147
+
148
+ if distribution == "truncated_normal":
149
+ # constant is stddev of standard normal truncated to (-2, 2)
150
+ trunc_normal_tf_(tensor, std=math.sqrt(variance) / 0.87962566103423978)
151
+ elif distribution == "normal":
152
+ with torch.no_grad():
153
+ tensor.normal_(std=math.sqrt(variance))
154
+ elif distribution == "uniform":
155
+ bound = math.sqrt(3 * variance)
156
+ with torch.no_grad():
157
+ tensor.uniform_(-bound, bound)
158
+ else:
159
+ raise ValueError(f"invalid distribution {distribution}")
160
+
161
+
162
+ def lecun_normal_(tensor):
163
+ variance_scaling_(tensor, mode="fan_in", distribution="truncated_normal")
164
+
165
+
166
+ def default_flax_embed_init(tensor):
167
+ variance_scaling_(tensor, mode="fan_in", distribution="normal")
168
+
169
+
170
+ class ConnectorAttention(nn.Module):
171
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
172
+
173
+ # Copied from transformers.models.clip.modeling_clip.CLIPAttention.__init__
174
+ def __init__(self, config):
175
+ super().__init__()
176
+ self.config = config
177
+ self.embed_dim = config.hidden_size
178
+ self.num_heads = config.num_attention_heads
179
+ self.head_dim = self.embed_dim // self.num_heads
180
+ if self.head_dim * self.num_heads != self.embed_dim:
181
+ raise ValueError(
182
+ f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
183
+ f" {self.num_heads})."
184
+ )
185
+ self.scale = self.head_dim**-0.5
186
+ self.dropout = config.attention_dropout
187
+
188
+ self.k_proj = nn.Linear(self.embed_dim, self.embed_dim)
189
+ self.v_proj = nn.Linear(self.embed_dim, self.embed_dim)
190
+ self.q_proj = nn.Linear(self.embed_dim, self.embed_dim)
191
+ self.out_proj = nn.Linear(self.embed_dim, self.embed_dim)
192
+
193
+ def forward(
194
+ self,
195
+ hidden_states: torch.Tensor,
196
+ attention_mask: Optional[torch.Tensor] = None,
197
+ output_attentions: Optional[bool] = False,
198
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
199
+ """Input shape: Batch x Time x Channel"""
200
+
201
+ batch_size, q_len, _ = hidden_states.size()
202
+
203
+ query_states = self.q_proj(hidden_states)
204
+ key_states = self.k_proj(hidden_states)
205
+ value_states = self.v_proj(hidden_states)
206
+
207
+ query_states = query_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2)
208
+ key_states = key_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2)
209
+ value_states = value_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2)
210
+
211
+ k_v_seq_len = key_states.shape[-2]
212
+ attn_weights = torch.matmul(query_states, key_states.transpose(2, 3)) * self.scale
213
+
214
+ if attn_weights.size() != (batch_size, self.num_heads, q_len, k_v_seq_len):
215
+ raise ValueError(
216
+ f"Attention weights should be of size {(batch_size, self.num_heads, q_len, k_v_seq_len)}, but is"
217
+ f" {attn_weights.size()}"
218
+ )
219
+
220
+ if attention_mask is not None:
221
+ if attention_mask.size() != (batch_size, 1, q_len, k_v_seq_len):
222
+ raise ValueError(
223
+ f"Attention mask should be of size {(batch_size, 1, q_len, k_v_seq_len)}, but is {attention_mask.size()}"
224
+ )
225
+ attn_weights = attn_weights + attention_mask
226
+
227
+ # upcast attention to fp32
228
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query_states.dtype)
229
+ attn_weights = nn.functional.dropout(attn_weights, p=self.dropout, training=self.training)
230
+ attn_output = torch.matmul(attn_weights, value_states)
231
+
232
+ if attn_output.size() != (batch_size, self.num_heads, q_len, self.head_dim):
233
+ raise ValueError(
234
+ f"`attn_output` should be of size {(batch_size, self.num_heads, q_len, self.head_dim)}, but is"
235
+ f" {attn_output.size()}"
236
+ )
237
+
238
+ attn_output = attn_output.transpose(1, 2).contiguous()
239
+ attn_output = attn_output.reshape(batch_size, q_len, self.embed_dim)
240
+
241
+ attn_output = self.out_proj(attn_output)
242
+
243
+ return attn_output, attn_weights
244
+
245
+
246
+ class ConnectorFlashAttention2(ConnectorAttention):
247
+ """
248
+ ConnectorAttention flash attention module. This module inherits from `ConnectorAttention` as the weights of the module stays
249
+ untouched. The only required change would be on the forward pass where it needs to correctly call the public API of
250
+ flash attention and deal with padding tokens in case the input contains any of them.
251
+ """
252
+
253
+ is_causal = False
254
+
255
+ # Copied from transformers.models.llama.modeling_llama.LlamaFlashAttention2.__init__
256
+ def __init__(self, *args, **kwargs):
257
+ super().__init__(*args, **kwargs)
258
+
259
+ # TODO: Should be removed once Flash Attention for RoCm is bumped to 2.1.
260
+ # flash_attn<2.1 generates top-left aligned causal mask, while what is needed here is bottom-right alignement, that was made default for flash_attn>=2.1. This attribute is used to handle this difference. Reference: https://github.com/Dao-AILab/flash-attention/releases/tag/v2.1.0.
261
+ # Beware that with flash_attn<2.1, using q_seqlen != k_seqlen (except for the case q_seqlen == 1) produces a wrong mask (top-left).
262
+ self._flash_attn_uses_top_left_mask = not is_flash_attn_greater_or_equal_2_10()
263
+
264
+ # Adapted from transformers.models.llama.modeling_llama.LlamaFlashAttention2.forward
265
+ def forward(
266
+ self,
267
+ hidden_states: torch.Tensor,
268
+ attention_mask: Optional[torch.LongTensor] = None,
269
+ output_attentions: bool = False,
270
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
271
+ output_attentions = False
272
+
273
+ batch_size, q_len, _ = hidden_states.size()
274
+
275
+ query_states = self.q_proj(hidden_states)
276
+ key_states = self.k_proj(hidden_states)
277
+ value_states = self.v_proj(hidden_states)
278
+
279
+ # Flash attention requires the input to have the shape
280
+ # batch_size x seq_length x head_dim x hidden_dim
281
+ # therefore we just need to keep the original shape
282
+ query_states = query_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2)
283
+ key_states = key_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2)
284
+ value_states = value_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2)
285
+
286
+ # TODO: These transpose are quite inefficient but Flash Attention requires the layout [batch_size, sequence_length, num_heads, head_dim]. We would need to refactor the KV cache
287
+ # to be able to avoid many of these transpose/reshape/view.
288
+ query_states = query_states.transpose(1, 2)
289
+ key_states = key_states.transpose(1, 2)
290
+ value_states = value_states.transpose(1, 2)
291
+
292
+ dropout_rate = self.dropout if self.training else 0.0
293
+
294
+ # In PEFT, usually we cast the layer norms in float32 for training stability reasons
295
+ # therefore the input hidden states gets silently casted in float32. Hence, we need
296
+ # cast them back in the correct dtype just to be sure everything works as expected.
297
+ # This might slowdown training & inference so it is recommended to not cast the LayerNorms
298
+ # in fp32.
299
+
300
+ input_dtype = query_states.dtype
301
+ if input_dtype == torch.float32:
302
+ if torch.is_autocast_enabled():
303
+ target_dtype = torch.get_autocast_gpu_dtype()
304
+ # Handle the case where the model is quantized
305
+ elif hasattr(self.config, "_pre_quantization_dtype"):
306
+ target_dtype = self.config._pre_quantization_dtype
307
+ else:
308
+ target_dtype = self.q_proj.weight.dtype
309
+
310
+ logger.warning_once(
311
+ f"The input hidden states seems to be silently casted in float32, this might be related to"
312
+ f" the fact you have upcasted embedding or layer norm layers in float32. We will cast back the input in"
313
+ f" {target_dtype}."
314
+ )
315
+
316
+ query_states = query_states.to(target_dtype)
317
+ key_states = key_states.to(target_dtype)
318
+ value_states = value_states.to(target_dtype)
319
+
320
+ attn_output = _flash_attention_forward(
321
+ query_states,
322
+ key_states,
323
+ value_states,
324
+ attention_mask,
325
+ q_len,
326
+ dropout=dropout_rate,
327
+ is_causal=self.is_causal,
328
+ use_top_left_mask=self._flash_attn_uses_top_left_mask,
329
+ )
330
+
331
+ attn_output = attn_output.reshape(batch_size, q_len, self.embed_dim).contiguous()
332
+ attn_output = self.out_proj(attn_output)
333
+
334
+ if not output_attentions:
335
+ attn_weights = None
336
+
337
+ return attn_output, attn_weights
338
+
339
+
340
+ class ConnectorSdpaAttention(ConnectorAttention):
341
+ """
342
+ Connector attention module using torch.nn.functional.scaled_dot_product_attention. This module inherits from
343
+ `ConnectorAttention` as the weights of the module stays untouched. The only changes are on the forward pass to adapt to
344
+ SDPA API.
345
+ """
346
+
347
+ is_causal = False
348
+
349
+ # Adapted from ConnectorAttention.forward and transformers.models.llama.modeling_llama.LlamaSdpaAttention.forward
350
+ def forward(
351
+ self,
352
+ hidden_states: torch.Tensor,
353
+ attention_mask: Optional[torch.Tensor] = None,
354
+ output_attentions: Optional[bool] = False,
355
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
356
+ if output_attentions:
357
+ # TODO: Improve this warning with e.g. `model.config.attn_implementation = "manual"` once this is implemented.
358
+ logger.warning_once(
359
+ "ConnectorModel is using ConnectorSdpaAttention, but `torch.nn.functional.scaled_dot_product_attention` does not support `output_attentions=True`. Falling back to the manual attention implementation, "
360
+ 'but specifying the manual implementation will be required from Transformers version v5.0.0 onwards. This warning can be removed using the argument `attn_implementation="eager"` when loading the model.'
361
+ )
362
+ return super().forward(
363
+ hidden_states=hidden_states,
364
+ attention_mask=attention_mask,
365
+ output_attentions=output_attentions,
366
+ )
367
+
368
+ batch_size, q_len, _ = hidden_states.size()
369
+
370
+ query_states = self.q_proj(hidden_states)
371
+ key_states = self.k_proj(hidden_states)
372
+ value_states = self.v_proj(hidden_states)
373
+
374
+ query_states = query_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2)
375
+ key_states = key_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2)
376
+ value_states = value_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2)
377
+
378
+ # SDPA with memory-efficient backend is currently (torch==2.1.2) bugged with non-contiguous inputs with custom attn_mask,
379
+ # Reference: https://github.com/pytorch/pytorch/issues/112577.
380
+ if query_states.device.type == "cuda" and attention_mask is not None:
381
+ query_states = query_states.contiguous()
382
+ key_states = key_states.contiguous()
383
+ value_states = value_states.contiguous()
384
+
385
+ # We dispatch to SDPA's Flash Attention or Efficient kernels via this `is_causal` if statement instead of an inline conditional assignment
386
+ # in SDPA to support both torch.compile's dynamic shapes and full graph options. An inline conditional prevents dynamic shapes from compiling.
387
+ is_causal = True if self.is_causal and q_len > 1 else False
388
+
389
+ attn_output = torch.nn.functional.scaled_dot_product_attention(
390
+ query_states,
391
+ key_states,
392
+ value_states,
393
+ attn_mask=attention_mask,
394
+ dropout_p=self.dropout if self.training else 0.0,
395
+ is_causal=is_causal,
396
+ )
397
+
398
+ attn_output = attn_output.transpose(1, 2).contiguous()
399
+ attn_output = attn_output.view(batch_size, q_len, self.embed_dim)
400
+
401
+ attn_output = self.out_proj(attn_output)
402
+
403
+ return attn_output, None
404
+
405
+
406
+ CONNECTOR_ATTENTION_CLASSES = {
407
+ "eager": ConnectorAttention,
408
+ "flash_attention_2": ConnectorFlashAttention2,
409
+ "sdpa": ConnectorSdpaAttention,
410
+ }
411
+
412
+
413
+ # Copied from transformers.models.clip.modeling_clip.CLIPMLP with CLIP->Connector
414
+ class ConnectorMLP(nn.Module):
415
+ def __init__(self, config):
416
+ super().__init__()
417
+ self.config = config
418
+ self.activation_fn = ACT2FN[config.hidden_act]
419
+ self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size)
420
+ self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size)
421
+
422
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
423
+ hidden_states = self.fc1(hidden_states)
424
+ hidden_states = self.activation_fn(hidden_states)
425
+ hidden_states = self.fc2(hidden_states)
426
+ return hidden_states
427
+
428
+
429
+ class ConnectorEncoderLayer(nn.Module):
430
+ def __init__(self, config: ConnectorConfig):
431
+ super().__init__()
432
+ self.embed_dim = config.hidden_size
433
+ self.self_attn = CONNECTOR_ATTENTION_CLASSES[config._attn_implementation](config=config)
434
+ self.layer_norm1 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
435
+ self.mlp = ConnectorMLP(config)
436
+ self.layer_norm2 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
437
+
438
+ # Ignore copy
439
+ def forward(
440
+ self,
441
+ hidden_states: torch.Tensor,
442
+ attention_mask: torch.Tensor,
443
+ output_attentions: Optional[bool] = False,
444
+ ) -> Tuple[torch.FloatTensor]:
445
+ """
446
+ Args:
447
+ hidden_states (`torch.FloatTensor`):
448
+ Input to the layer of shape `(batch, seq_len, embed_dim)`.
449
+ attention_mask (`torch.FloatTensor`):
450
+ Attention mask of shape `(batch, 1, q_len, k_v_seq_len)` where padding elements are indicated by very large negative values.
451
+ output_attentions (`bool`, *optional*, defaults to `False`):
452
+ Whether or not to return the attentions tensors of all attention layers. See `attentions` under
453
+ returned tensors for more detail.
454
+ """
455
+ residual = hidden_states
456
+
457
+ hidden_states = self.layer_norm1(hidden_states)
458
+ hidden_states, attn_weights = self.self_attn(
459
+ hidden_states=hidden_states,
460
+ attention_mask=attention_mask,
461
+ output_attentions=output_attentions,
462
+ )
463
+ hidden_states = residual + hidden_states
464
+
465
+ residual = hidden_states
466
+ hidden_states = self.layer_norm2(hidden_states)
467
+ hidden_states = self.mlp(hidden_states)
468
+ hidden_states = residual + hidden_states
469
+
470
+ outputs = (hidden_states,)
471
+
472
+ if output_attentions:
473
+ outputs += (attn_weights,)
474
+
475
+ return outputs
476
+
477
+
478
+ # Copied from transformers.models.altclip.modeling_altclip.AltCLIPEncoder with AltCLIP->Connector
479
+ class ConnectorEncoder(nn.Module):
480
+ def __init__(self, config: ConnectorConfig):
481
+ super().__init__()
482
+ self.config = config
483
+ self.layers = nn.ModuleList([ConnectorEncoderLayer(config) for _ in range(config.num_hidden_layers)])
484
+ self.gradient_checkpointing = False
485
+ self.apply(init_weights)
486
+
487
+ def forward(self, inputs_embeds):
488
+ hidden_states = inputs_embeds
489
+ for encoder_layer in self.layers:
490
+ if self.gradient_checkpointing and self.training:
491
+ layer_outputs = torch.utils.checkpoint.checkpoint(
492
+ encoder_layer.__call__,
493
+ hidden_states,
494
+ None,
495
+ False,
496
+ use_reentrant=False
497
+ )
498
+ else:
499
+ layer_outputs = encoder_layer(
500
+ hidden_states,
501
+ None,
502
+ output_attentions=False,
503
+ )
504
+
505
+ hidden_states = layer_outputs[0]
506
+
507
+ return hidden_states
src/models/connector/modeling_qwen2.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from transformers import Qwen2PreTrainedModel, Qwen2Config
4
+ from transformers.models.qwen2.modeling_qwen2 import Qwen2RMSNorm, Qwen2DecoderLayer
5
+
6
+
7
+ class Qwen2Connector(Qwen2PreTrainedModel):
8
+ def __init__(self, config: Qwen2Config):
9
+ super().__init__(config)
10
+ self.layers = nn.ModuleList(
11
+ [Qwen2DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
12
+ )
13
+
14
+ for layer in self.layers:
15
+ layer.self_attn.is_causal = False
16
+
17
+ self._attn_implementation = config._attn_implementation
18
+ assert self._attn_implementation == 'flash_attention_2'
19
+ self.norm = Qwen2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
20
+
21
+ self.gradient_checkpointing = False
22
+ # Initialize weights and apply final processing
23
+ self.post_init()
24
+
25
+ def forward(self, inputs_embeds):
26
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device)
27
+ position_ids = position_ids.expand(inputs_embeds.shape[0], -1)
28
+ hidden_states = inputs_embeds
29
+
30
+ for encoder_layer in self.layers:
31
+ if self.gradient_checkpointing and self.training:
32
+ layer_outputs = self._gradient_checkpointing_func(
33
+ encoder_layer.__call__,
34
+ hidden_states,
35
+ None,
36
+ position_ids,
37
+ use_reentrant=False
38
+ )
39
+ else:
40
+ layer_outputs = encoder_layer(
41
+ hidden_states,
42
+ attention_mask=None,
43
+ position_ids=position_ids,
44
+ )
45
+
46
+ hidden_states = layer_outputs[0]
47
+
48
+ hidden_states = self.norm(hidden_states)
49
+
50
+ return hidden_states
src/models/sd3_kontext/pipeline_stable_diffusion_3.py ADDED
@@ -0,0 +1,1256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 Stability AI, The HuggingFace Team and The InstantX Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ from typing import Any, Callable, Dict, List, Optional, Union
17
+
18
+ import torch
19
+ from transformers import (
20
+ CLIPTextModelWithProjection,
21
+ CLIPTokenizer,
22
+ SiglipImageProcessor,
23
+ SiglipVisionModel,
24
+ T5EncoderModel,
25
+ T5TokenizerFast,
26
+ )
27
+
28
+ from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
29
+ from diffusers.loaders import FromSingleFileMixin, SD3IPAdapterMixin, SD3LoraLoaderMixin
30
+ from diffusers.models.autoencoders import AutoencoderKL
31
+ from diffusers.models.transformers import SD3Transformer2DModel
32
+ from diffusers.schedulers import FlowMatchEulerDiscreteScheduler
33
+ from diffusers.utils import (
34
+ USE_PEFT_BACKEND,
35
+ is_torch_xla_available,
36
+ logging,
37
+ replace_example_docstring,
38
+ scale_lora_layers,
39
+ unscale_lora_layers,
40
+ )
41
+ from diffusers.utils.torch_utils import randn_tensor
42
+ from diffusers.pipelines.pipeline_utils import DiffusionPipeline
43
+ from diffusers.pipelines.stable_diffusion_3.pipeline_output import StableDiffusion3PipelineOutput
44
+
45
+
46
+ if is_torch_xla_available():
47
+ import torch_xla.core.xla_model as xm
48
+
49
+ XLA_AVAILABLE = True
50
+ else:
51
+ XLA_AVAILABLE = False
52
+
53
+
54
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
55
+
56
+ EXAMPLE_DOC_STRING = """
57
+ Examples:
58
+ ```py
59
+ >>> import torch
60
+ >>> from diffusers import StableDiffusion3Pipeline
61
+
62
+ >>> pipe = StableDiffusion3Pipeline.from_pretrained(
63
+ ... "stabilityai/stable-diffusion-3-medium-diffusers", torch_dtype=torch.float16
64
+ ... )
65
+ >>> pipe.to("cuda")
66
+ >>> prompt = "A cat holding a sign that says hello world"
67
+ >>> image = pipe(prompt).images[0]
68
+ >>> image.save("sd3.png")
69
+ ```
70
+ """
71
+
72
+
73
+ # Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
74
+ def calculate_shift(
75
+ image_seq_len,
76
+ base_seq_len: int = 256,
77
+ max_seq_len: int = 4096,
78
+ base_shift: float = 0.5,
79
+ max_shift: float = 1.15,
80
+ ):
81
+ m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
82
+ b = base_shift - m * base_seq_len
83
+ mu = image_seq_len * m + b
84
+ return mu
85
+
86
+
87
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
88
+ def retrieve_timesteps(
89
+ scheduler,
90
+ num_inference_steps: Optional[int] = None,
91
+ device: Optional[Union[str, torch.device]] = None,
92
+ timesteps: Optional[List[int]] = None,
93
+ sigmas: Optional[List[float]] = None,
94
+ **kwargs,
95
+ ):
96
+ r"""
97
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
98
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
99
+
100
+ Args:
101
+ scheduler (`SchedulerMixin`):
102
+ The scheduler to get timesteps from.
103
+ num_inference_steps (`int`):
104
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
105
+ must be `None`.
106
+ device (`str` or `torch.device`, *optional*):
107
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
108
+ timesteps (`List[int]`, *optional*):
109
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
110
+ `num_inference_steps` and `sigmas` must be `None`.
111
+ sigmas (`List[float]`, *optional*):
112
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
113
+ `num_inference_steps` and `timesteps` must be `None`.
114
+
115
+ Returns:
116
+ `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
117
+ second element is the number of inference steps.
118
+ """
119
+ if timesteps is not None and sigmas is not None:
120
+ raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
121
+ if timesteps is not None:
122
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
123
+ if not accepts_timesteps:
124
+ raise ValueError(
125
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
126
+ f" timestep schedules. Please check whether you are using the correct scheduler."
127
+ )
128
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
129
+ timesteps = scheduler.timesteps
130
+ num_inference_steps = len(timesteps)
131
+ elif sigmas is not None:
132
+ accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
133
+ if not accept_sigmas:
134
+ raise ValueError(
135
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
136
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
137
+ )
138
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
139
+ timesteps = scheduler.timesteps
140
+ num_inference_steps = len(timesteps)
141
+ else:
142
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
143
+ timesteps = scheduler.timesteps
144
+ return timesteps, num_inference_steps
145
+
146
+
147
+ class StableDiffusion3Pipeline(DiffusionPipeline, SD3LoraLoaderMixin, FromSingleFileMixin, SD3IPAdapterMixin):
148
+ r"""
149
+ Args:
150
+ transformer ([`SD3Transformer2DModel`]):
151
+ Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
152
+ scheduler ([`FlowMatchEulerDiscreteScheduler`]):
153
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
154
+ vae ([`AutoencoderKL`]):
155
+ Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
156
+ text_encoder ([`CLIPTextModelWithProjection`]):
157
+ [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModelWithProjection),
158
+ specifically the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant,
159
+ with an additional added projection layer that is initialized with a diagonal matrix with the `hidden_size`
160
+ as its dimension.
161
+ text_encoder_2 ([`CLIPTextModelWithProjection`]):
162
+ [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModelWithProjection),
163
+ specifically the
164
+ [laion/CLIP-ViT-bigG-14-laion2B-39B-b160k](https://huggingface.co/laion/CLIP-ViT-bigG-14-laion2B-39B-b160k)
165
+ variant.
166
+ text_encoder_3 ([`T5EncoderModel`]):
167
+ Frozen text-encoder. Stable Diffusion 3 uses
168
+ [T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel), specifically the
169
+ [t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
170
+ tokenizer (`CLIPTokenizer`):
171
+ Tokenizer of class
172
+ [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
173
+ tokenizer_2 (`CLIPTokenizer`):
174
+ Second Tokenizer of class
175
+ [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
176
+ tokenizer_3 (`T5TokenizerFast`):
177
+ Tokenizer of class
178
+ [T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer).
179
+ image_encoder (`SiglipVisionModel`, *optional*):
180
+ Pre-trained Vision Model for IP Adapter.
181
+ feature_extractor (`SiglipImageProcessor`, *optional*):
182
+ Image processor for IP Adapter.
183
+ """
184
+
185
+ model_cpu_offload_seq = "text_encoder->text_encoder_2->text_encoder_3->image_encoder->transformer->vae"
186
+ _optional_components = ["image_encoder", "feature_extractor"]
187
+ _callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds", "negative_pooled_prompt_embeds"]
188
+
189
+ def __init__(
190
+ self,
191
+ transformer: SD3Transformer2DModel,
192
+ scheduler: FlowMatchEulerDiscreteScheduler,
193
+ vae: AutoencoderKL,
194
+ text_encoder: CLIPTextModelWithProjection,
195
+ tokenizer: CLIPTokenizer,
196
+ text_encoder_2: CLIPTextModelWithProjection,
197
+ tokenizer_2: CLIPTokenizer,
198
+ text_encoder_3: T5EncoderModel,
199
+ tokenizer_3: T5TokenizerFast,
200
+ image_encoder: SiglipVisionModel = None,
201
+ feature_extractor: SiglipImageProcessor = None,
202
+ ):
203
+ super().__init__()
204
+
205
+ self.register_modules(
206
+ vae=vae,
207
+ text_encoder=text_encoder,
208
+ text_encoder_2=text_encoder_2,
209
+ text_encoder_3=text_encoder_3,
210
+ tokenizer=tokenizer,
211
+ tokenizer_2=tokenizer_2,
212
+ tokenizer_3=tokenizer_3,
213
+ transformer=transformer,
214
+ scheduler=scheduler,
215
+ image_encoder=image_encoder,
216
+ feature_extractor=feature_extractor,
217
+ )
218
+ self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
219
+ self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
220
+ self.tokenizer_max_length = (
221
+ self.tokenizer.model_max_length if hasattr(self, "tokenizer") and self.tokenizer is not None else 77
222
+ )
223
+ self.default_sample_size = (
224
+ self.transformer.config.sample_size
225
+ if hasattr(self, "transformer") and self.transformer is not None
226
+ else 128
227
+ )
228
+ self.patch_size = (
229
+ self.transformer.config.patch_size if hasattr(self, "transformer") and self.transformer is not None else 2
230
+ )
231
+
232
+ def _get_t5_prompt_embeds(
233
+ self,
234
+ prompt: Union[str, List[str]] = None,
235
+ num_images_per_prompt: int = 1,
236
+ max_sequence_length: int = 256,
237
+ device: Optional[torch.device] = None,
238
+ dtype: Optional[torch.dtype] = None,
239
+ ):
240
+ device = device or self._execution_device
241
+ dtype = dtype or self.text_encoder.dtype
242
+
243
+ prompt = [prompt] if isinstance(prompt, str) else prompt
244
+ batch_size = len(prompt)
245
+
246
+ if self.text_encoder_3 is None:
247
+ return torch.zeros(
248
+ (
249
+ batch_size * num_images_per_prompt,
250
+ self.tokenizer_max_length,
251
+ self.transformer.config.joint_attention_dim,
252
+ ),
253
+ device=device,
254
+ dtype=dtype,
255
+ )
256
+
257
+ text_inputs = self.tokenizer_3(
258
+ prompt,
259
+ padding="max_length",
260
+ max_length=max_sequence_length,
261
+ truncation=True,
262
+ add_special_tokens=True,
263
+ return_tensors="pt",
264
+ )
265
+ text_input_ids = text_inputs.input_ids
266
+ untruncated_ids = self.tokenizer_3(prompt, padding="longest", return_tensors="pt").input_ids
267
+
268
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
269
+ removed_text = self.tokenizer_3.batch_decode(untruncated_ids[:, self.tokenizer_max_length - 1 : -1])
270
+ # logger.warning(
271
+ # "The following part of your input was truncated because `max_sequence_length` is set to "
272
+ # f" {max_sequence_length} tokens: {removed_text}"
273
+ # )
274
+
275
+ prompt_embeds = self.text_encoder_3(text_input_ids.to(device))[0]
276
+
277
+ dtype = self.text_encoder_3.dtype
278
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
279
+
280
+ _, seq_len, _ = prompt_embeds.shape
281
+
282
+ # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
283
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
284
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
285
+
286
+ return prompt_embeds
287
+
288
+ def _get_clip_prompt_embeds(
289
+ self,
290
+ prompt: Union[str, List[str]],
291
+ num_images_per_prompt: int = 1,
292
+ device: Optional[torch.device] = None,
293
+ clip_skip: Optional[int] = None,
294
+ clip_model_index: int = 0,
295
+ ):
296
+ device = device or self._execution_device
297
+
298
+ clip_tokenizers = [self.tokenizer, self.tokenizer_2]
299
+ clip_text_encoders = [self.text_encoder, self.text_encoder_2]
300
+
301
+ tokenizer = clip_tokenizers[clip_model_index]
302
+ text_encoder = clip_text_encoders[clip_model_index]
303
+
304
+ prompt = [prompt] if isinstance(prompt, str) else prompt
305
+ batch_size = len(prompt)
306
+
307
+ text_inputs = tokenizer(
308
+ prompt,
309
+ padding="max_length",
310
+ max_length=self.tokenizer_max_length,
311
+ truncation=True,
312
+ return_tensors="pt",
313
+ )
314
+
315
+ text_input_ids = text_inputs.input_ids
316
+ untruncated_ids = tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
317
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
318
+ removed_text = tokenizer.batch_decode(untruncated_ids[:, self.tokenizer_max_length - 1 : -1])
319
+ # logger.warning(
320
+ # "The following part of your input was truncated because CLIP can only handle sequences up to"
321
+ # f" {self.tokenizer_max_length} tokens: {removed_text}"
322
+ # )
323
+ prompt_embeds = text_encoder(text_input_ids.to(device), output_hidden_states=True)
324
+ pooled_prompt_embeds = prompt_embeds[0]
325
+
326
+ if clip_skip is None:
327
+ prompt_embeds = prompt_embeds.hidden_states[-2]
328
+ else:
329
+ prompt_embeds = prompt_embeds.hidden_states[-(clip_skip + 2)]
330
+
331
+ prompt_embeds = prompt_embeds.to(dtype=self.text_encoder.dtype, device=device)
332
+
333
+ _, seq_len, _ = prompt_embeds.shape
334
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
335
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
336
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
337
+
338
+ pooled_prompt_embeds = pooled_prompt_embeds.repeat(1, num_images_per_prompt, 1)
339
+ pooled_prompt_embeds = pooled_prompt_embeds.view(batch_size * num_images_per_prompt, -1)
340
+
341
+ return prompt_embeds, pooled_prompt_embeds
342
+
343
+ def encode_pooled_prompt(
344
+ self,
345
+ prompt: Union[str, List[str]],
346
+ prompt_2: Union[str, List[str]],
347
+ device: Optional[torch.device] = None,
348
+ num_images_per_prompt: int = 1,
349
+ do_classifier_free_guidance: bool = True,
350
+ negative_prompt: Optional[Union[str, List[str]]] = None,
351
+ negative_prompt_2: Optional[Union[str, List[str]]] = None,
352
+ prompt_embeds: Optional[torch.FloatTensor] = None,
353
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
354
+ pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
355
+ negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
356
+ clip_skip: Optional[int] = None,
357
+ lora_scale: Optional[float] = None,
358
+ ):
359
+ device = device or self._execution_device
360
+
361
+ # set lora scale so that monkey patched LoRA
362
+ # function of text encoder can correctly access it
363
+ if lora_scale is not None and isinstance(self, SD3LoraLoaderMixin):
364
+ self._lora_scale = lora_scale
365
+
366
+ # dynamically adjust the LoRA scale
367
+ if self.text_encoder is not None and USE_PEFT_BACKEND:
368
+ scale_lora_layers(self.text_encoder, lora_scale)
369
+ if self.text_encoder_2 is not None and USE_PEFT_BACKEND:
370
+ scale_lora_layers(self.text_encoder_2, lora_scale)
371
+
372
+ prompt = [prompt] if isinstance(prompt, str) else prompt
373
+ if prompt is not None:
374
+ batch_size = len(prompt)
375
+ else:
376
+ batch_size = prompt_embeds.shape[0]
377
+
378
+ if prompt_embeds is None:
379
+ prompt_2 = prompt_2 or prompt
380
+ prompt_2 = [prompt_2] if isinstance(prompt_2, str) else prompt_2
381
+
382
+ _, pooled_prompt_embed = self._get_clip_prompt_embeds(
383
+ prompt=prompt,
384
+ device=device,
385
+ num_images_per_prompt=num_images_per_prompt,
386
+ clip_skip=clip_skip,
387
+ clip_model_index=0,
388
+ )
389
+ _, pooled_prompt_2_embed = self._get_clip_prompt_embeds(
390
+ prompt=prompt_2,
391
+ device=device,
392
+ num_images_per_prompt=num_images_per_prompt,
393
+ clip_skip=clip_skip,
394
+ clip_model_index=1,
395
+ )
396
+
397
+ pooled_prompt_embeds = torch.cat([pooled_prompt_embed, pooled_prompt_2_embed], dim=-1)
398
+
399
+ if do_classifier_free_guidance and negative_prompt_embeds is None:
400
+ negative_prompt = negative_prompt or ""
401
+ negative_prompt_2 = negative_prompt_2 or negative_prompt
402
+
403
+ # normalize str to list
404
+ negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
405
+ negative_prompt_2 = (
406
+ batch_size * [negative_prompt_2] if isinstance(negative_prompt_2, str) else negative_prompt_2
407
+ )
408
+
409
+
410
+ if prompt is not None and type(prompt) is not type(negative_prompt):
411
+ raise TypeError(
412
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
413
+ f" {type(prompt)}."
414
+ )
415
+ elif batch_size != len(negative_prompt):
416
+ raise ValueError(
417
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
418
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
419
+ " the batch size of `prompt`."
420
+ )
421
+
422
+ _, negative_pooled_prompt_embed = self._get_clip_prompt_embeds(
423
+ negative_prompt,
424
+ device=device,
425
+ num_images_per_prompt=num_images_per_prompt,
426
+ clip_skip=None,
427
+ clip_model_index=0,
428
+ )
429
+ _, negative_pooled_prompt_2_embed = self._get_clip_prompt_embeds(
430
+ negative_prompt_2,
431
+ device=device,
432
+ num_images_per_prompt=num_images_per_prompt,
433
+ clip_skip=None,
434
+ clip_model_index=1,
435
+ )
436
+
437
+ negative_pooled_prompt_embeds = torch.cat(
438
+ [negative_pooled_prompt_embed, negative_pooled_prompt_2_embed], dim=-1
439
+ )
440
+
441
+ if self.text_encoder is not None:
442
+ if isinstance(self, SD3LoraLoaderMixin) and USE_PEFT_BACKEND:
443
+ # Retrieve the original scale by scaling back the LoRA layers
444
+ unscale_lora_layers(self.text_encoder, lora_scale)
445
+
446
+ if self.text_encoder_2 is not None:
447
+ if isinstance(self, SD3LoraLoaderMixin) and USE_PEFT_BACKEND:
448
+ # Retrieve the original scale by scaling back the LoRA layers
449
+ unscale_lora_layers(self.text_encoder_2, lora_scale)
450
+
451
+ return pooled_prompt_embeds, negative_pooled_prompt_embeds
452
+
453
+
454
+ def encode_prompt(
455
+ self,
456
+ prompt: Union[str, List[str]],
457
+ prompt_2: Union[str, List[str]],
458
+ prompt_3: Union[str, List[str]],
459
+ device: Optional[torch.device] = None,
460
+ num_images_per_prompt: int = 1,
461
+ do_classifier_free_guidance: bool = True,
462
+ negative_prompt: Optional[Union[str, List[str]]] = None,
463
+ negative_prompt_2: Optional[Union[str, List[str]]] = None,
464
+ negative_prompt_3: Optional[Union[str, List[str]]] = None,
465
+ prompt_embeds: Optional[torch.FloatTensor] = None,
466
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
467
+ pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
468
+ negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
469
+ clip_skip: Optional[int] = None,
470
+ max_sequence_length: int = 256,
471
+ lora_scale: Optional[float] = None,
472
+ ):
473
+ r"""
474
+
475
+ Args:
476
+ prompt (`str` or `List[str]`, *optional*):
477
+ prompt to be encoded
478
+ prompt_2 (`str` or `List[str]`, *optional*):
479
+ The prompt or prompts to be sent to the `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
480
+ used in all text-encoders
481
+ prompt_3 (`str` or `List[str]`, *optional*):
482
+ The prompt or prompts to be sent to the `tokenizer_3` and `text_encoder_3`. If not defined, `prompt` is
483
+ used in all text-encoders
484
+ device: (`torch.device`):
485
+ torch device
486
+ num_images_per_prompt (`int`):
487
+ number of images that should be generated per prompt
488
+ do_classifier_free_guidance (`bool`):
489
+ whether to use classifier free guidance or not
490
+ negative_prompt (`str` or `List[str]`, *optional*):
491
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
492
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
493
+ less than `1`).
494
+ negative_prompt_2 (`str` or `List[str]`, *optional*):
495
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and
496
+ `text_encoder_2`. If not defined, `negative_prompt` is used in all the text-encoders.
497
+ negative_prompt_3 (`str` or `List[str]`, *optional*):
498
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_3` and
499
+ `text_encoder_3`. If not defined, `negative_prompt` is used in all the text-encoders.
500
+ prompt_embeds (`torch.FloatTensor`, *optional*):
501
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
502
+ provided, text embeddings will be generated from `prompt` input argument.
503
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
504
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
505
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
506
+ argument.
507
+ pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
508
+ Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
509
+ If not provided, pooled text embeddings will be generated from `prompt` input argument.
510
+ negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
511
+ Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
512
+ weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt`
513
+ input argument.
514
+ clip_skip (`int`, *optional*):
515
+ Number of layers to be skipped from CLIP while computing the prompt embeddings. A value of 1 means that
516
+ the output of the pre-final layer will be used for computing the prompt embeddings.
517
+ lora_scale (`float`, *optional*):
518
+ A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
519
+ """
520
+ device = device or self._execution_device
521
+
522
+ # set lora scale so that monkey patched LoRA
523
+ # function of text encoder can correctly access it
524
+ if lora_scale is not None and isinstance(self, SD3LoraLoaderMixin):
525
+ self._lora_scale = lora_scale
526
+
527
+ # dynamically adjust the LoRA scale
528
+ if self.text_encoder is not None and USE_PEFT_BACKEND:
529
+ scale_lora_layers(self.text_encoder, lora_scale)
530
+ if self.text_encoder_2 is not None and USE_PEFT_BACKEND:
531
+ scale_lora_layers(self.text_encoder_2, lora_scale)
532
+
533
+ prompt = [prompt] if isinstance(prompt, str) else prompt
534
+ if prompt is not None:
535
+ batch_size = len(prompt)
536
+ else:
537
+ batch_size = prompt_embeds.shape[0]
538
+
539
+ if prompt_embeds is None:
540
+ prompt_2 = prompt_2 or prompt
541
+ prompt_2 = [prompt_2] if isinstance(prompt_2, str) else prompt_2
542
+
543
+ prompt_3 = prompt_3 or prompt
544
+ prompt_3 = [prompt_3] if isinstance(prompt_3, str) else prompt_3
545
+
546
+ prompt_embed, pooled_prompt_embed = self._get_clip_prompt_embeds(
547
+ prompt=prompt,
548
+ device=device,
549
+ num_images_per_prompt=num_images_per_prompt,
550
+ clip_skip=clip_skip,
551
+ clip_model_index=0,
552
+ )
553
+ prompt_2_embed, pooled_prompt_2_embed = self._get_clip_prompt_embeds(
554
+ prompt=prompt_2,
555
+ device=device,
556
+ num_images_per_prompt=num_images_per_prompt,
557
+ clip_skip=clip_skip,
558
+ clip_model_index=1,
559
+ )
560
+ clip_prompt_embeds = torch.cat([prompt_embed, prompt_2_embed], dim=-1)
561
+
562
+ t5_prompt_embed = self._get_t5_prompt_embeds(
563
+ prompt=prompt_3,
564
+ num_images_per_prompt=num_images_per_prompt,
565
+ max_sequence_length=max_sequence_length,
566
+ device=device,
567
+ )
568
+
569
+ clip_prompt_embeds = torch.nn.functional.pad(
570
+ clip_prompt_embeds, (0, t5_prompt_embed.shape[-1] - clip_prompt_embeds.shape[-1])
571
+ )
572
+
573
+ prompt_embeds = torch.cat([clip_prompt_embeds, t5_prompt_embed], dim=-2)
574
+ pooled_prompt_embeds = torch.cat([pooled_prompt_embed, pooled_prompt_2_embed], dim=-1)
575
+
576
+ if do_classifier_free_guidance and negative_prompt_embeds is None:
577
+ negative_prompt = negative_prompt or ""
578
+ negative_prompt_2 = negative_prompt_2 or negative_prompt
579
+ negative_prompt_3 = negative_prompt_3 or negative_prompt
580
+
581
+ # normalize str to list
582
+ negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
583
+ negative_prompt_2 = (
584
+ batch_size * [negative_prompt_2] if isinstance(negative_prompt_2, str) else negative_prompt_2
585
+ )
586
+ negative_prompt_3 = (
587
+ batch_size * [negative_prompt_3] if isinstance(negative_prompt_3, str) else negative_prompt_3
588
+ )
589
+
590
+ if prompt is not None and type(prompt) is not type(negative_prompt):
591
+ raise TypeError(
592
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
593
+ f" {type(prompt)}."
594
+ )
595
+ elif batch_size != len(negative_prompt):
596
+ raise ValueError(
597
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
598
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
599
+ " the batch size of `prompt`."
600
+ )
601
+
602
+ negative_prompt_embed, negative_pooled_prompt_embed = self._get_clip_prompt_embeds(
603
+ negative_prompt,
604
+ device=device,
605
+ num_images_per_prompt=num_images_per_prompt,
606
+ clip_skip=None,
607
+ clip_model_index=0,
608
+ )
609
+ negative_prompt_2_embed, negative_pooled_prompt_2_embed = self._get_clip_prompt_embeds(
610
+ negative_prompt_2,
611
+ device=device,
612
+ num_images_per_prompt=num_images_per_prompt,
613
+ clip_skip=None,
614
+ clip_model_index=1,
615
+ )
616
+ negative_clip_prompt_embeds = torch.cat([negative_prompt_embed, negative_prompt_2_embed], dim=-1)
617
+
618
+ t5_negative_prompt_embed = self._get_t5_prompt_embeds(
619
+ prompt=negative_prompt_3,
620
+ num_images_per_prompt=num_images_per_prompt,
621
+ max_sequence_length=max_sequence_length,
622
+ device=device,
623
+ )
624
+
625
+ negative_clip_prompt_embeds = torch.nn.functional.pad(
626
+ negative_clip_prompt_embeds,
627
+ (0, t5_negative_prompt_embed.shape[-1] - negative_clip_prompt_embeds.shape[-1]),
628
+ )
629
+
630
+ negative_prompt_embeds = torch.cat([negative_clip_prompt_embeds, t5_negative_prompt_embed], dim=-2)
631
+ negative_pooled_prompt_embeds = torch.cat(
632
+ [negative_pooled_prompt_embed, negative_pooled_prompt_2_embed], dim=-1
633
+ )
634
+
635
+ if self.text_encoder is not None:
636
+ if isinstance(self, SD3LoraLoaderMixin) and USE_PEFT_BACKEND:
637
+ # Retrieve the original scale by scaling back the LoRA layers
638
+ unscale_lora_layers(self.text_encoder, lora_scale)
639
+
640
+ if self.text_encoder_2 is not None:
641
+ if isinstance(self, SD3LoraLoaderMixin) and USE_PEFT_BACKEND:
642
+ # Retrieve the original scale by scaling back the LoRA layers
643
+ unscale_lora_layers(self.text_encoder_2, lora_scale)
644
+
645
+ return prompt_embeds, negative_prompt_embeds, pooled_prompt_embeds, negative_pooled_prompt_embeds
646
+
647
+ def check_inputs(
648
+ self,
649
+ prompt,
650
+ prompt_2,
651
+ prompt_3,
652
+ height,
653
+ width,
654
+ negative_prompt=None,
655
+ negative_prompt_2=None,
656
+ negative_prompt_3=None,
657
+ prompt_embeds=None,
658
+ negative_prompt_embeds=None,
659
+ pooled_prompt_embeds=None,
660
+ negative_pooled_prompt_embeds=None,
661
+ callback_on_step_end_tensor_inputs=None,
662
+ max_sequence_length=None,
663
+ ):
664
+ if (
665
+ height % (self.vae_scale_factor * self.patch_size) != 0
666
+ or width % (self.vae_scale_factor * self.patch_size) != 0
667
+ ):
668
+ raise ValueError(
669
+ f"`height` and `width` have to be divisible by {self.vae_scale_factor * self.patch_size} but are {height} and {width}."
670
+ f"You can use height {height - height % (self.vae_scale_factor * self.patch_size)} and width {width - width % (self.vae_scale_factor * self.patch_size)}."
671
+ )
672
+
673
+ if callback_on_step_end_tensor_inputs is not None and not all(
674
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
675
+ ):
676
+ raise ValueError(
677
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
678
+ )
679
+
680
+ if prompt is not None and prompt_embeds is not None:
681
+ raise ValueError(
682
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
683
+ " only forward one of the two."
684
+ )
685
+ elif prompt_2 is not None and prompt_embeds is not None:
686
+ raise ValueError(
687
+ f"Cannot forward both `prompt_2`: {prompt_2} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
688
+ " only forward one of the two."
689
+ )
690
+ elif prompt_3 is not None and prompt_embeds is not None:
691
+ raise ValueError(
692
+ f"Cannot forward both `prompt_3`: {prompt_2} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
693
+ " only forward one of the two."
694
+ )
695
+ elif prompt is None and prompt_embeds is None:
696
+ raise ValueError(
697
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
698
+ )
699
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
700
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
701
+ elif prompt_2 is not None and (not isinstance(prompt_2, str) and not isinstance(prompt_2, list)):
702
+ raise ValueError(f"`prompt_2` has to be of type `str` or `list` but is {type(prompt_2)}")
703
+ elif prompt_3 is not None and (not isinstance(prompt_3, str) and not isinstance(prompt_3, list)):
704
+ raise ValueError(f"`prompt_3` has to be of type `str` or `list` but is {type(prompt_3)}")
705
+
706
+ if negative_prompt is not None and negative_prompt_embeds is not None:
707
+ raise ValueError(
708
+ f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
709
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
710
+ )
711
+ elif negative_prompt_2 is not None and negative_prompt_embeds is not None:
712
+ raise ValueError(
713
+ f"Cannot forward both `negative_prompt_2`: {negative_prompt_2} and `negative_prompt_embeds`:"
714
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
715
+ )
716
+ elif negative_prompt_3 is not None and negative_prompt_embeds is not None:
717
+ raise ValueError(
718
+ f"Cannot forward both `negative_prompt_3`: {negative_prompt_3} and `negative_prompt_embeds`:"
719
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
720
+ )
721
+
722
+ if prompt_embeds is not None and negative_prompt_embeds is not None:
723
+ if prompt_embeds.shape != negative_prompt_embeds.shape:
724
+ raise ValueError(
725
+ "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
726
+ f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
727
+ f" {negative_prompt_embeds.shape}."
728
+ )
729
+
730
+ if prompt_embeds is not None and pooled_prompt_embeds is None:
731
+ raise ValueError(
732
+ "If `prompt_embeds` are provided, `pooled_prompt_embeds` also have to be passed. Make sure to generate `pooled_prompt_embeds` from the same text encoder that was used to generate `prompt_embeds`."
733
+ )
734
+
735
+ if negative_prompt_embeds is not None and negative_pooled_prompt_embeds is None:
736
+ raise ValueError(
737
+ "If `negative_prompt_embeds` are provided, `negative_pooled_prompt_embeds` also have to be passed. Make sure to generate `negative_pooled_prompt_embeds` from the same text encoder that was used to generate `negative_prompt_embeds`."
738
+ )
739
+
740
+ if max_sequence_length is not None and max_sequence_length > 512:
741
+ raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
742
+
743
+ def prepare_latents(
744
+ self,
745
+ batch_size,
746
+ num_channels_latents,
747
+ height,
748
+ width,
749
+ dtype,
750
+ device,
751
+ generator,
752
+ latents=None,
753
+ ):
754
+ if latents is not None:
755
+ return latents.to(device=device, dtype=dtype)
756
+
757
+ shape = (
758
+ batch_size,
759
+ num_channels_latents,
760
+ int(height) // self.vae_scale_factor,
761
+ int(width) // self.vae_scale_factor,
762
+ )
763
+
764
+ if isinstance(generator, list) and len(generator) != batch_size:
765
+ raise ValueError(
766
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
767
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
768
+ )
769
+
770
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
771
+
772
+ return latents
773
+
774
+ @property
775
+ def guidance_scale(self):
776
+ return self._guidance_scale
777
+
778
+ @property
779
+ def skip_guidance_layers(self):
780
+ return self._skip_guidance_layers
781
+
782
+ @property
783
+ def clip_skip(self):
784
+ return self._clip_skip
785
+
786
+ # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)
787
+ # of the Imagen paper: https://huggingface.co/papers/2205.11487 . `guidance_scale = 1`
788
+ # corresponds to doing no classifier free guidance.
789
+ @property
790
+ def do_classifier_free_guidance(self):
791
+ return self._guidance_scale > 1
792
+
793
+ @property
794
+ def joint_attention_kwargs(self):
795
+ return self._joint_attention_kwargs
796
+
797
+ @property
798
+ def num_timesteps(self):
799
+ return self._num_timesteps
800
+
801
+ @property
802
+ def interrupt(self):
803
+ return self._interrupt
804
+
805
+ # Adapted from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.encode_image
806
+ def encode_image(self, image: PipelineImageInput, device: torch.device) -> torch.Tensor:
807
+ """Encodes the given image into a feature representation using a pre-trained image encoder.
808
+
809
+ Args:
810
+ image (`PipelineImageInput`):
811
+ Input image to be encoded.
812
+ device: (`torch.device`):
813
+ Torch device.
814
+
815
+ Returns:
816
+ `torch.Tensor`: The encoded image feature representation.
817
+ """
818
+ if not isinstance(image, torch.Tensor):
819
+ image = self.feature_extractor(image, return_tensors="pt").pixel_values
820
+
821
+ image = image.to(device=device, dtype=self.dtype)
822
+
823
+ return self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
824
+
825
+ # Adapted from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.prepare_ip_adapter_image_embeds
826
+ def prepare_ip_adapter_image_embeds(
827
+ self,
828
+ ip_adapter_image: Optional[PipelineImageInput] = None,
829
+ ip_adapter_image_embeds: Optional[torch.Tensor] = None,
830
+ device: Optional[torch.device] = None,
831
+ num_images_per_prompt: int = 1,
832
+ do_classifier_free_guidance: bool = True,
833
+ ) -> torch.Tensor:
834
+ """Prepares image embeddings for use in the IP-Adapter.
835
+
836
+ Either `ip_adapter_image` or `ip_adapter_image_embeds` must be passed.
837
+
838
+ Args:
839
+ ip_adapter_image (`PipelineImageInput`, *optional*):
840
+ The input image to extract features from for IP-Adapter.
841
+ ip_adapter_image_embeds (`torch.Tensor`, *optional*):
842
+ Precomputed image embeddings.
843
+ device: (`torch.device`, *optional*):
844
+ Torch device.
845
+ num_images_per_prompt (`int`, defaults to 1):
846
+ Number of images that should be generated per prompt.
847
+ do_classifier_free_guidance (`bool`, defaults to True):
848
+ Whether to use classifier free guidance or not.
849
+ """
850
+ device = device or self._execution_device
851
+
852
+ if ip_adapter_image_embeds is not None:
853
+ if do_classifier_free_guidance:
854
+ single_negative_image_embeds, single_image_embeds = ip_adapter_image_embeds.chunk(2)
855
+ else:
856
+ single_image_embeds = ip_adapter_image_embeds
857
+ elif ip_adapter_image is not None:
858
+ single_image_embeds = self.encode_image(ip_adapter_image, device)
859
+ if do_classifier_free_guidance:
860
+ single_negative_image_embeds = torch.zeros_like(single_image_embeds)
861
+ else:
862
+ raise ValueError("Neither `ip_adapter_image_embeds` or `ip_adapter_image_embeds` were provided.")
863
+
864
+ image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
865
+
866
+ if do_classifier_free_guidance:
867
+ negative_image_embeds = torch.cat([single_negative_image_embeds] * num_images_per_prompt, dim=0)
868
+ image_embeds = torch.cat([negative_image_embeds, image_embeds], dim=0)
869
+
870
+ return image_embeds.to(device=device)
871
+
872
+ def enable_sequential_cpu_offload(self, *args, **kwargs):
873
+ if self.image_encoder is not None and "image_encoder" not in self._exclude_from_cpu_offload:
874
+ logger.warning(
875
+ "`pipe.enable_sequential_cpu_offload()` might fail for `image_encoder` if it uses "
876
+ "`torch.nn.MultiheadAttention`. You can exclude `image_encoder` from CPU offloading by calling "
877
+ "`pipe._exclude_from_cpu_offload.append('image_encoder')` before `pipe.enable_sequential_cpu_offload()`."
878
+ )
879
+
880
+ super().enable_sequential_cpu_offload(*args, **kwargs)
881
+
882
+ @torch.no_grad()
883
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
884
+ def __call__(
885
+ self,
886
+ prompt: Union[str, List[str]] = None,
887
+ prompt_2: Optional[Union[str, List[str]]] = None,
888
+ prompt_3: Optional[Union[str, List[str]]] = None,
889
+ height: Optional[int] = None,
890
+ width: Optional[int] = None,
891
+ num_inference_steps: int = 28,
892
+ sigmas: Optional[List[float]] = None,
893
+ guidance_scale: float = 7.0,
894
+ negative_prompt: Optional[Union[str, List[str]]] = None,
895
+ negative_prompt_2: Optional[Union[str, List[str]]] = None,
896
+ negative_prompt_3: Optional[Union[str, List[str]]] = None,
897
+ num_images_per_prompt: Optional[int] = 1,
898
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
899
+ latents: Optional[torch.FloatTensor] = None,
900
+ cond_latents: Optional[torch.FloatTensor] = None,
901
+ prompt_embeds: Optional[torch.FloatTensor] = None,
902
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
903
+ pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
904
+ negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
905
+ ip_adapter_image: Optional[PipelineImageInput] = None,
906
+ ip_adapter_image_embeds: Optional[torch.Tensor] = None,
907
+ output_type: Optional[str] = "pil",
908
+ return_dict: bool = True,
909
+ joint_attention_kwargs: Optional[Dict[str, Any]] = None,
910
+ clip_skip: Optional[int] = None,
911
+ callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
912
+ callback_on_step_end_tensor_inputs: List[str] = ["latents"],
913
+ max_sequence_length: int = 256,
914
+ skip_guidance_layers: List[int] = None,
915
+ skip_layer_guidance_scale: float = 2.8,
916
+ skip_layer_guidance_stop: float = 0.2,
917
+ skip_layer_guidance_start: float = 0.01,
918
+ mu: Optional[float] = None,
919
+ ):
920
+ r"""
921
+ Function invoked when calling the pipeline for generation.
922
+
923
+ Args:
924
+ prompt (`str` or `List[str]`, *optional*):
925
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
926
+ instead.
927
+ prompt_2 (`str` or `List[str]`, *optional*):
928
+ The prompt or prompts to be sent to `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
929
+ will be used instead
930
+ prompt_3 (`str` or `List[str]`, *optional*):
931
+ The prompt or prompts to be sent to `tokenizer_3` and `text_encoder_3`. If not defined, `prompt` is
932
+ will be used instead
933
+ height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
934
+ The height in pixels of the generated image. This is set to 1024 by default for the best results.
935
+ width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
936
+ The width in pixels of the generated image. This is set to 1024 by default for the best results.
937
+ num_inference_steps (`int`, *optional*, defaults to 50):
938
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
939
+ expense of slower inference.
940
+ sigmas (`List[float]`, *optional*):
941
+ Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
942
+ their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
943
+ will be used.
944
+ guidance_scale (`float`, *optional*, defaults to 7.0):
945
+ Guidance scale as defined in [Classifier-Free Diffusion
946
+ Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
947
+ of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
948
+ `guidance_scale > 1`. Higher guidance scale encourages to generate images that are closely linked to
949
+ the text `prompt`, usually at the expense of lower image quality.
950
+ negative_prompt (`str` or `List[str]`, *optional*):
951
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
952
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
953
+ less than `1`).
954
+ negative_prompt_2 (`str` or `List[str]`, *optional*):
955
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and
956
+ `text_encoder_2`. If not defined, `negative_prompt` is used instead
957
+ negative_prompt_3 (`str` or `List[str]`, *optional*):
958
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_3` and
959
+ `text_encoder_3`. If not defined, `negative_prompt` is used instead
960
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
961
+ The number of images to generate per prompt.
962
+ generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
963
+ One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
964
+ to make generation deterministic.
965
+ latents (`torch.FloatTensor`, *optional*):
966
+ Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
967
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
968
+ tensor will ge generated by sampling using the supplied random `generator`.
969
+ prompt_embeds (`torch.FloatTensor`, *optional*):
970
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
971
+ provided, text embeddings will be generated from `prompt` input argument.
972
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
973
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
974
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
975
+ argument.
976
+ pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
977
+ Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
978
+ If not provided, pooled text embeddings will be generated from `prompt` input argument.
979
+ negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
980
+ Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
981
+ weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt`
982
+ input argument.
983
+ ip_adapter_image (`PipelineImageInput`, *optional*):
984
+ Optional image input to work with IP Adapters.
985
+ ip_adapter_image_embeds (`torch.Tensor`, *optional*):
986
+ Pre-generated image embeddings for IP-Adapter. Should be a tensor of shape `(batch_size, num_images,
987
+ emb_dim)`. It should contain the negative image embedding if `do_classifier_free_guidance` is set to
988
+ `True`. If not provided, embeddings are computed from the `ip_adapter_image` input argument.
989
+ output_type (`str`, *optional*, defaults to `"pil"`):
990
+ The output format of the generate image. Choose between
991
+ [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
992
+ return_dict (`bool`, *optional*, defaults to `True`):
993
+ Whether or not to return a [`~pipelines.stable_diffusion_3.StableDiffusion3PipelineOutput`] instead of
994
+ a plain tuple.
995
+ joint_attention_kwargs (`dict`, *optional*):
996
+ A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
997
+ `self.processor` in
998
+ [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
999
+ callback_on_step_end (`Callable`, *optional*):
1000
+ A function that calls at the end of each denoising steps during the inference. The function is called
1001
+ with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
1002
+ callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
1003
+ `callback_on_step_end_tensor_inputs`.
1004
+ callback_on_step_end_tensor_inputs (`List`, *optional*):
1005
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
1006
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
1007
+ `._callback_tensor_inputs` attribute of your pipeline class.
1008
+ max_sequence_length (`int` defaults to 256): Maximum sequence length to use with the `prompt`.
1009
+ skip_guidance_layers (`List[int]`, *optional*):
1010
+ A list of integers that specify layers to skip during guidance. If not provided, all layers will be
1011
+ used for guidance. If provided, the guidance will only be applied to the layers specified in the list.
1012
+ Recommended value by StabiltyAI for Stable Diffusion 3.5 Medium is [7, 8, 9].
1013
+ skip_layer_guidance_scale (`int`, *optional*): The scale of the guidance for the layers specified in
1014
+ `skip_guidance_layers`. The guidance will be applied to the layers specified in `skip_guidance_layers`
1015
+ with a scale of `skip_layer_guidance_scale`. The guidance will be applied to the rest of the layers
1016
+ with a scale of `1`.
1017
+ skip_layer_guidance_stop (`int`, *optional*): The step at which the guidance for the layers specified in
1018
+ `skip_guidance_layers` will stop. The guidance will be applied to the layers specified in
1019
+ `skip_guidance_layers` until the fraction specified in `skip_layer_guidance_stop`. Recommended value by
1020
+ StabiltyAI for Stable Diffusion 3.5 Medium is 0.2.
1021
+ skip_layer_guidance_start (`int`, *optional*): The step at which the guidance for the layers specified in
1022
+ `skip_guidance_layers` will start. The guidance will be applied to the layers specified in
1023
+ `skip_guidance_layers` from the fraction specified in `skip_layer_guidance_start`. Recommended value by
1024
+ StabiltyAI for Stable Diffusion 3.5 Medium is 0.01.
1025
+ mu (`float`, *optional*): `mu` value used for `dynamic_shifting`.
1026
+
1027
+ Examples:
1028
+
1029
+ Returns:
1030
+ [`~pipelines.stable_diffusion_3.StableDiffusion3PipelineOutput`] or `tuple`:
1031
+ [`~pipelines.stable_diffusion_3.StableDiffusion3PipelineOutput`] if `return_dict` is True, otherwise a
1032
+ `tuple`. When returning a tuple, the first element is a list with the generated images.
1033
+ """
1034
+
1035
+ height = height or self.default_sample_size * self.vae_scale_factor
1036
+ width = width or self.default_sample_size * self.vae_scale_factor
1037
+
1038
+ # 1. Check inputs. Raise error if not correct
1039
+ self.check_inputs(
1040
+ prompt,
1041
+ prompt_2,
1042
+ prompt_3,
1043
+ height,
1044
+ width,
1045
+ negative_prompt=negative_prompt,
1046
+ negative_prompt_2=negative_prompt_2,
1047
+ negative_prompt_3=negative_prompt_3,
1048
+ prompt_embeds=prompt_embeds,
1049
+ negative_prompt_embeds=negative_prompt_embeds,
1050
+ pooled_prompt_embeds=pooled_prompt_embeds,
1051
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
1052
+ callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
1053
+ max_sequence_length=max_sequence_length,
1054
+ )
1055
+
1056
+ self._guidance_scale = guidance_scale
1057
+ self._skip_layer_guidance_scale = skip_layer_guidance_scale
1058
+ self._clip_skip = clip_skip
1059
+ self._joint_attention_kwargs = joint_attention_kwargs
1060
+ self._interrupt = False
1061
+
1062
+ # 2. Define call parameters
1063
+ if prompt is not None and isinstance(prompt, str):
1064
+ batch_size = 1
1065
+ elif prompt is not None and isinstance(prompt, list):
1066
+ batch_size = len(prompt)
1067
+ else:
1068
+ batch_size = prompt_embeds.shape[0]
1069
+
1070
+ device = self._execution_device
1071
+
1072
+ lora_scale = (
1073
+ self.joint_attention_kwargs.get("scale", None) if self.joint_attention_kwargs is not None else None
1074
+ )
1075
+ (
1076
+ prompt_embeds,
1077
+ negative_prompt_embeds,
1078
+ pooled_prompt_embeds,
1079
+ negative_pooled_prompt_embeds,
1080
+ ) = self.encode_prompt(
1081
+ prompt=prompt,
1082
+ prompt_2=prompt_2,
1083
+ prompt_3=prompt_3,
1084
+ negative_prompt=negative_prompt,
1085
+ negative_prompt_2=negative_prompt_2,
1086
+ negative_prompt_3=negative_prompt_3,
1087
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
1088
+ prompt_embeds=prompt_embeds,
1089
+ negative_prompt_embeds=negative_prompt_embeds,
1090
+ pooled_prompt_embeds=pooled_prompt_embeds,
1091
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
1092
+ device=device,
1093
+ clip_skip=self.clip_skip,
1094
+ num_images_per_prompt=num_images_per_prompt,
1095
+ max_sequence_length=max_sequence_length,
1096
+ lora_scale=lora_scale,
1097
+ )
1098
+
1099
+ if self.do_classifier_free_guidance:
1100
+ if skip_guidance_layers is not None:
1101
+ original_prompt_embeds = prompt_embeds
1102
+ original_pooled_prompt_embeds = pooled_prompt_embeds
1103
+ prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0)
1104
+ pooled_prompt_embeds = torch.cat([negative_pooled_prompt_embeds, pooled_prompt_embeds], dim=0)
1105
+
1106
+ # 4. Prepare latent variables
1107
+ num_channels_latents = self.transformer.config.in_channels
1108
+ latents = self.prepare_latents(
1109
+ batch_size * num_images_per_prompt,
1110
+ num_channels_latents,
1111
+ height,
1112
+ width,
1113
+ prompt_embeds.dtype,
1114
+ device,
1115
+ generator,
1116
+ latents,
1117
+ )
1118
+
1119
+ # 5. Prepare timesteps
1120
+ scheduler_kwargs = {}
1121
+ if self.scheduler.config.get("use_dynamic_shifting", None) and mu is None:
1122
+ _, _, height, width = latents.shape
1123
+ image_seq_len = (height // self.transformer.config.patch_size) * (
1124
+ width // self.transformer.config.patch_size
1125
+ )
1126
+ mu = calculate_shift(
1127
+ image_seq_len,
1128
+ self.scheduler.config.get("base_image_seq_len", 256),
1129
+ self.scheduler.config.get("max_image_seq_len", 4096),
1130
+ self.scheduler.config.get("base_shift", 0.5),
1131
+ self.scheduler.config.get("max_shift", 1.16),
1132
+ )
1133
+ scheduler_kwargs["mu"] = mu
1134
+ elif mu is not None:
1135
+ scheduler_kwargs["mu"] = mu
1136
+ timesteps, num_inference_steps = retrieve_timesteps(
1137
+ self.scheduler,
1138
+ num_inference_steps,
1139
+ device,
1140
+ sigmas=sigmas,
1141
+ **scheduler_kwargs,
1142
+ )
1143
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
1144
+ self._num_timesteps = len(timesteps)
1145
+
1146
+ # 6. Prepare image embeddings
1147
+ if (ip_adapter_image is not None and self.is_ip_adapter_active) or ip_adapter_image_embeds is not None:
1148
+ ip_adapter_image_embeds = self.prepare_ip_adapter_image_embeds(
1149
+ ip_adapter_image,
1150
+ ip_adapter_image_embeds,
1151
+ device,
1152
+ batch_size * num_images_per_prompt,
1153
+ self.do_classifier_free_guidance,
1154
+ )
1155
+
1156
+ if self.joint_attention_kwargs is None:
1157
+ self._joint_attention_kwargs = {"ip_adapter_image_embeds": ip_adapter_image_embeds}
1158
+ else:
1159
+ self._joint_attention_kwargs.update(ip_adapter_image_embeds=ip_adapter_image_embeds)
1160
+
1161
+ if cond_latents is not None and self.do_classifier_free_guidance:
1162
+ if cond_latents.shape[0] == latents.shape[0]:
1163
+ cond_latents = torch.cat([cond_latents]*2)
1164
+
1165
+ # 7. Denoising loop
1166
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
1167
+ for i, t in enumerate(timesteps):
1168
+ if self.interrupt:
1169
+ continue
1170
+
1171
+ # expand the latents if we are doing classifier free guidance
1172
+ latent_model_input = torch.cat([latents] * 2) if self.do_classifier_free_guidance else latents
1173
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
1174
+ timestep = t.expand(latent_model_input.shape[0])
1175
+
1176
+ noise_pred = self.transformer(
1177
+ hidden_states=latent_model_input,
1178
+ cond_hidden_states=cond_latents,
1179
+ timestep=timestep,
1180
+ encoder_hidden_states=prompt_embeds,
1181
+ pooled_projections=pooled_prompt_embeds,
1182
+ joint_attention_kwargs=self.joint_attention_kwargs,
1183
+ return_dict=False,
1184
+ )[0]
1185
+
1186
+ # perform guidance
1187
+ if self.do_classifier_free_guidance:
1188
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
1189
+ noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_text - noise_pred_uncond)
1190
+ should_skip_layers = (
1191
+ True
1192
+ if i > num_inference_steps * skip_layer_guidance_start
1193
+ and i < num_inference_steps * skip_layer_guidance_stop
1194
+ else False
1195
+ )
1196
+ if skip_guidance_layers is not None and should_skip_layers:
1197
+ timestep = t.expand(latents.shape[0])
1198
+ latent_model_input = latents
1199
+ noise_pred_skip_layers = self.transformer(
1200
+ hidden_states=latent_model_input,
1201
+ timestep=timestep,
1202
+ encoder_hidden_states=original_prompt_embeds,
1203
+ pooled_projections=original_pooled_prompt_embeds,
1204
+ joint_attention_kwargs=self.joint_attention_kwargs,
1205
+ return_dict=False,
1206
+ skip_layers=skip_guidance_layers,
1207
+ )[0]
1208
+ noise_pred = (
1209
+ noise_pred + (noise_pred_text - noise_pred_skip_layers) * self._skip_layer_guidance_scale
1210
+ )
1211
+
1212
+ # compute the previous noisy sample x_t -> x_t-1
1213
+ latents_dtype = latents.dtype
1214
+ latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
1215
+
1216
+ if latents.dtype != latents_dtype:
1217
+ if torch.backends.mps.is_available():
1218
+ # some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
1219
+ latents = latents.to(latents_dtype)
1220
+
1221
+ if callback_on_step_end is not None:
1222
+ callback_kwargs = {}
1223
+ for k in callback_on_step_end_tensor_inputs:
1224
+ callback_kwargs[k] = locals()[k]
1225
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
1226
+
1227
+ latents = callback_outputs.pop("latents", latents)
1228
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
1229
+ negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
1230
+ negative_pooled_prompt_embeds = callback_outputs.pop(
1231
+ "negative_pooled_prompt_embeds", negative_pooled_prompt_embeds
1232
+ )
1233
+
1234
+ # call the callback, if provided
1235
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
1236
+ progress_bar.update()
1237
+
1238
+ if XLA_AVAILABLE:
1239
+ xm.mark_step()
1240
+
1241
+ if output_type == "latent":
1242
+ image = latents
1243
+
1244
+ else:
1245
+ latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
1246
+
1247
+ image = self.vae.decode(latents, return_dict=False)[0]
1248
+ image = self.image_processor.postprocess(image, output_type=output_type)
1249
+
1250
+ # Offload all models
1251
+ self.maybe_free_model_hooks()
1252
+
1253
+ if not return_dict:
1254
+ return (image,)
1255
+
1256
+ return StableDiffusion3PipelineOutput(images=image)
src/models/sd3_kontext/pipeline_stable_diffusion_3_dynamic.py ADDED
@@ -0,0 +1,1257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 Stability AI, The HuggingFace Team and The InstantX Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ from typing import Any, Callable, Dict, List, Optional, Union
17
+
18
+ import torch
19
+ from transformers import (
20
+ CLIPTextModelWithProjection,
21
+ CLIPTokenizer,
22
+ SiglipImageProcessor,
23
+ SiglipVisionModel,
24
+ T5EncoderModel,
25
+ T5TokenizerFast,
26
+ )
27
+
28
+ from diffusers.image_processor import PipelineImageInput, VaeImageProcessor
29
+ from diffusers.loaders import FromSingleFileMixin, SD3IPAdapterMixin, SD3LoraLoaderMixin
30
+ from diffusers.models.autoencoders import AutoencoderKL
31
+ from diffusers.models.transformers import SD3Transformer2DModel
32
+ from diffusers.schedulers import FlowMatchEulerDiscreteScheduler
33
+ from diffusers.utils import (
34
+ USE_PEFT_BACKEND,
35
+ is_torch_xla_available,
36
+ logging,
37
+ replace_example_docstring,
38
+ scale_lora_layers,
39
+ unscale_lora_layers,
40
+ )
41
+ from diffusers.utils.torch_utils import randn_tensor
42
+ from diffusers.pipelines.pipeline_utils import DiffusionPipeline
43
+ from diffusers.pipelines.stable_diffusion_3.pipeline_output import StableDiffusion3PipelineOutput
44
+
45
+
46
+ if is_torch_xla_available():
47
+ import torch_xla.core.xla_model as xm
48
+
49
+ XLA_AVAILABLE = True
50
+ else:
51
+ XLA_AVAILABLE = False
52
+
53
+
54
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
55
+
56
+ EXAMPLE_DOC_STRING = """
57
+ Examples:
58
+ ```py
59
+ >>> import torch
60
+ >>> from diffusers import StableDiffusion3Pipeline
61
+
62
+ >>> pipe = StableDiffusion3Pipeline.from_pretrained(
63
+ ... "stabilityai/stable-diffusion-3-medium-diffusers", torch_dtype=torch.float16
64
+ ... )
65
+ >>> pipe.to("cuda")
66
+ >>> prompt = "A cat holding a sign that says hello world"
67
+ >>> image = pipe(prompt).images[0]
68
+ >>> image.save("sd3.png")
69
+ ```
70
+ """
71
+
72
+
73
+ # Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
74
+ def calculate_shift(
75
+ image_seq_len,
76
+ base_seq_len: int = 256,
77
+ max_seq_len: int = 4096,
78
+ base_shift: float = 0.5,
79
+ max_shift: float = 1.15,
80
+ ):
81
+ m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
82
+ b = base_shift - m * base_seq_len
83
+ mu = image_seq_len * m + b
84
+ return mu
85
+
86
+
87
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
88
+ def retrieve_timesteps(
89
+ scheduler,
90
+ num_inference_steps: Optional[int] = None,
91
+ device: Optional[Union[str, torch.device]] = None,
92
+ timesteps: Optional[List[int]] = None,
93
+ sigmas: Optional[List[float]] = None,
94
+ **kwargs,
95
+ ):
96
+ r"""
97
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
98
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
99
+
100
+ Args:
101
+ scheduler (`SchedulerMixin`):
102
+ The scheduler to get timesteps from.
103
+ num_inference_steps (`int`):
104
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
105
+ must be `None`.
106
+ device (`str` or `torch.device`, *optional*):
107
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
108
+ timesteps (`List[int]`, *optional*):
109
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
110
+ `num_inference_steps` and `sigmas` must be `None`.
111
+ sigmas (`List[float]`, *optional*):
112
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
113
+ `num_inference_steps` and `timesteps` must be `None`.
114
+
115
+ Returns:
116
+ `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
117
+ second element is the number of inference steps.
118
+ """
119
+ if timesteps is not None and sigmas is not None:
120
+ raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
121
+ if timesteps is not None:
122
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
123
+ if not accepts_timesteps:
124
+ raise ValueError(
125
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
126
+ f" timestep schedules. Please check whether you are using the correct scheduler."
127
+ )
128
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
129
+ timesteps = scheduler.timesteps
130
+ num_inference_steps = len(timesteps)
131
+ elif sigmas is not None:
132
+ accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
133
+ if not accept_sigmas:
134
+ raise ValueError(
135
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
136
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
137
+ )
138
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
139
+ timesteps = scheduler.timesteps
140
+ num_inference_steps = len(timesteps)
141
+ else:
142
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
143
+ timesteps = scheduler.timesteps
144
+ return timesteps, num_inference_steps
145
+
146
+
147
+ class StableDiffusion3Pipeline(DiffusionPipeline, SD3LoraLoaderMixin, FromSingleFileMixin, SD3IPAdapterMixin):
148
+ r"""
149
+ Args:
150
+ transformer ([`SD3Transformer2DModel`]):
151
+ Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
152
+ scheduler ([`FlowMatchEulerDiscreteScheduler`]):
153
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
154
+ vae ([`AutoencoderKL`]):
155
+ Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
156
+ text_encoder ([`CLIPTextModelWithProjection`]):
157
+ [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModelWithProjection),
158
+ specifically the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant,
159
+ with an additional added projection layer that is initialized with a diagonal matrix with the `hidden_size`
160
+ as its dimension.
161
+ text_encoder_2 ([`CLIPTextModelWithProjection`]):
162
+ [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModelWithProjection),
163
+ specifically the
164
+ [laion/CLIP-ViT-bigG-14-laion2B-39B-b160k](https://huggingface.co/laion/CLIP-ViT-bigG-14-laion2B-39B-b160k)
165
+ variant.
166
+ text_encoder_3 ([`T5EncoderModel`]):
167
+ Frozen text-encoder. Stable Diffusion 3 uses
168
+ [T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel), specifically the
169
+ [t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
170
+ tokenizer (`CLIPTokenizer`):
171
+ Tokenizer of class
172
+ [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
173
+ tokenizer_2 (`CLIPTokenizer`):
174
+ Second Tokenizer of class
175
+ [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer).
176
+ tokenizer_3 (`T5TokenizerFast`):
177
+ Tokenizer of class
178
+ [T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer).
179
+ image_encoder (`SiglipVisionModel`, *optional*):
180
+ Pre-trained Vision Model for IP Adapter.
181
+ feature_extractor (`SiglipImageProcessor`, *optional*):
182
+ Image processor for IP Adapter.
183
+ """
184
+
185
+ model_cpu_offload_seq = "text_encoder->text_encoder_2->text_encoder_3->image_encoder->transformer->vae"
186
+ _optional_components = ["image_encoder", "feature_extractor"]
187
+ _callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds", "negative_pooled_prompt_embeds"]
188
+
189
+ def __init__(
190
+ self,
191
+ transformer: SD3Transformer2DModel,
192
+ scheduler: FlowMatchEulerDiscreteScheduler,
193
+ vae: AutoencoderKL,
194
+ text_encoder: CLIPTextModelWithProjection,
195
+ tokenizer: CLIPTokenizer,
196
+ text_encoder_2: CLIPTextModelWithProjection,
197
+ tokenizer_2: CLIPTokenizer,
198
+ text_encoder_3: T5EncoderModel,
199
+ tokenizer_3: T5TokenizerFast,
200
+ image_encoder: SiglipVisionModel = None,
201
+ feature_extractor: SiglipImageProcessor = None,
202
+ ):
203
+ super().__init__()
204
+
205
+ self.register_modules(
206
+ vae=vae,
207
+ text_encoder=text_encoder,
208
+ text_encoder_2=text_encoder_2,
209
+ text_encoder_3=text_encoder_3,
210
+ tokenizer=tokenizer,
211
+ tokenizer_2=tokenizer_2,
212
+ tokenizer_3=tokenizer_3,
213
+ transformer=transformer,
214
+ scheduler=scheduler,
215
+ image_encoder=image_encoder,
216
+ feature_extractor=feature_extractor,
217
+ )
218
+ self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
219
+ self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
220
+ self.tokenizer_max_length = (
221
+ self.tokenizer.model_max_length if hasattr(self, "tokenizer") and self.tokenizer is not None else 77
222
+ )
223
+ self.default_sample_size = (
224
+ self.transformer.config.sample_size
225
+ if hasattr(self, "transformer") and self.transformer is not None
226
+ else 128
227
+ )
228
+ self.patch_size = (
229
+ self.transformer.config.patch_size if hasattr(self, "transformer") and self.transformer is not None else 2
230
+ )
231
+
232
+ def _get_t5_prompt_embeds(
233
+ self,
234
+ prompt: Union[str, List[str]] = None,
235
+ num_images_per_prompt: int = 1,
236
+ max_sequence_length: int = 256,
237
+ device: Optional[torch.device] = None,
238
+ dtype: Optional[torch.dtype] = None,
239
+ ):
240
+ device = device or self._execution_device
241
+ dtype = dtype or self.text_encoder.dtype
242
+
243
+ prompt = [prompt] if isinstance(prompt, str) else prompt
244
+ batch_size = len(prompt)
245
+
246
+ if self.text_encoder_3 is None:
247
+ return torch.zeros(
248
+ (
249
+ batch_size * num_images_per_prompt,
250
+ self.tokenizer_max_length,
251
+ self.transformer.config.joint_attention_dim,
252
+ ),
253
+ device=device,
254
+ dtype=dtype,
255
+ )
256
+
257
+ text_inputs = self.tokenizer_3(
258
+ prompt,
259
+ padding="max_length",
260
+ max_length=max_sequence_length,
261
+ truncation=True,
262
+ add_special_tokens=True,
263
+ return_tensors="pt",
264
+ )
265
+ text_input_ids = text_inputs.input_ids
266
+ untruncated_ids = self.tokenizer_3(prompt, padding="longest", return_tensors="pt").input_ids
267
+
268
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
269
+ removed_text = self.tokenizer_3.batch_decode(untruncated_ids[:, self.tokenizer_max_length - 1 : -1])
270
+ # logger.warning(
271
+ # "The following part of your input was truncated because `max_sequence_length` is set to "
272
+ # f" {max_sequence_length} tokens: {removed_text}"
273
+ # )
274
+
275
+ prompt_embeds = self.text_encoder_3(text_input_ids.to(device))[0]
276
+
277
+ dtype = self.text_encoder_3.dtype
278
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
279
+
280
+ _, seq_len, _ = prompt_embeds.shape
281
+
282
+ # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
283
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
284
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
285
+
286
+ return prompt_embeds
287
+
288
+ def _get_clip_prompt_embeds(
289
+ self,
290
+ prompt: Union[str, List[str]],
291
+ num_images_per_prompt: int = 1,
292
+ device: Optional[torch.device] = None,
293
+ clip_skip: Optional[int] = None,
294
+ clip_model_index: int = 0,
295
+ ):
296
+ device = device or self._execution_device
297
+
298
+ clip_tokenizers = [self.tokenizer, self.tokenizer_2]
299
+ clip_text_encoders = [self.text_encoder, self.text_encoder_2]
300
+
301
+ tokenizer = clip_tokenizers[clip_model_index]
302
+ text_encoder = clip_text_encoders[clip_model_index]
303
+
304
+ prompt = [prompt] if isinstance(prompt, str) else prompt
305
+ batch_size = len(prompt)
306
+
307
+ text_inputs = tokenizer(
308
+ prompt,
309
+ padding="max_length",
310
+ max_length=self.tokenizer_max_length,
311
+ truncation=True,
312
+ return_tensors="pt",
313
+ )
314
+
315
+ text_input_ids = text_inputs.input_ids
316
+ untruncated_ids = tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
317
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
318
+ removed_text = tokenizer.batch_decode(untruncated_ids[:, self.tokenizer_max_length - 1 : -1])
319
+ # logger.warning(
320
+ # "The following part of your input was truncated because CLIP can only handle sequences up to"
321
+ # f" {self.tokenizer_max_length} tokens: {removed_text}"
322
+ # )
323
+ prompt_embeds = text_encoder(text_input_ids.to(device), output_hidden_states=True)
324
+ pooled_prompt_embeds = prompt_embeds[0]
325
+
326
+ if clip_skip is None:
327
+ prompt_embeds = prompt_embeds.hidden_states[-2]
328
+ else:
329
+ prompt_embeds = prompt_embeds.hidden_states[-(clip_skip + 2)]
330
+
331
+ prompt_embeds = prompt_embeds.to(dtype=self.text_encoder.dtype, device=device)
332
+
333
+ _, seq_len, _ = prompt_embeds.shape
334
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
335
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
336
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
337
+
338
+ pooled_prompt_embeds = pooled_prompt_embeds.repeat(1, num_images_per_prompt, 1)
339
+ pooled_prompt_embeds = pooled_prompt_embeds.view(batch_size * num_images_per_prompt, -1)
340
+
341
+ return prompt_embeds, pooled_prompt_embeds
342
+
343
+ def encode_pooled_prompt(
344
+ self,
345
+ prompt: Union[str, List[str]],
346
+ prompt_2: Union[str, List[str]],
347
+ device: Optional[torch.device] = None,
348
+ num_images_per_prompt: int = 1,
349
+ do_classifier_free_guidance: bool = True,
350
+ negative_prompt: Optional[Union[str, List[str]]] = None,
351
+ negative_prompt_2: Optional[Union[str, List[str]]] = None,
352
+ prompt_embeds: Optional[torch.FloatTensor] = None,
353
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
354
+ pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
355
+ negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
356
+ clip_skip: Optional[int] = None,
357
+ lora_scale: Optional[float] = None,
358
+ ):
359
+ device = device or self._execution_device
360
+
361
+ # set lora scale so that monkey patched LoRA
362
+ # function of text encoder can correctly access it
363
+ if lora_scale is not None and isinstance(self, SD3LoraLoaderMixin):
364
+ self._lora_scale = lora_scale
365
+
366
+ # dynamically adjust the LoRA scale
367
+ if self.text_encoder is not None and USE_PEFT_BACKEND:
368
+ scale_lora_layers(self.text_encoder, lora_scale)
369
+ if self.text_encoder_2 is not None and USE_PEFT_BACKEND:
370
+ scale_lora_layers(self.text_encoder_2, lora_scale)
371
+
372
+ prompt = [prompt] if isinstance(prompt, str) else prompt
373
+ if prompt is not None:
374
+ batch_size = len(prompt)
375
+ else:
376
+ batch_size = prompt_embeds.shape[0]
377
+
378
+ if prompt_embeds is None:
379
+ prompt_2 = prompt_2 or prompt
380
+ prompt_2 = [prompt_2] if isinstance(prompt_2, str) else prompt_2
381
+
382
+ _, pooled_prompt_embed = self._get_clip_prompt_embeds(
383
+ prompt=prompt,
384
+ device=device,
385
+ num_images_per_prompt=num_images_per_prompt,
386
+ clip_skip=clip_skip,
387
+ clip_model_index=0,
388
+ )
389
+ _, pooled_prompt_2_embed = self._get_clip_prompt_embeds(
390
+ prompt=prompt_2,
391
+ device=device,
392
+ num_images_per_prompt=num_images_per_prompt,
393
+ clip_skip=clip_skip,
394
+ clip_model_index=1,
395
+ )
396
+
397
+ pooled_prompt_embeds = torch.cat([pooled_prompt_embed, pooled_prompt_2_embed], dim=-1)
398
+
399
+ if do_classifier_free_guidance and negative_prompt_embeds is None:
400
+ negative_prompt = negative_prompt or ""
401
+ negative_prompt_2 = negative_prompt_2 or negative_prompt
402
+
403
+ # normalize str to list
404
+ negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
405
+ negative_prompt_2 = (
406
+ batch_size * [negative_prompt_2] if isinstance(negative_prompt_2, str) else negative_prompt_2
407
+ )
408
+
409
+
410
+ if prompt is not None and type(prompt) is not type(negative_prompt):
411
+ raise TypeError(
412
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
413
+ f" {type(prompt)}."
414
+ )
415
+ elif batch_size != len(negative_prompt):
416
+ raise ValueError(
417
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
418
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
419
+ " the batch size of `prompt`."
420
+ )
421
+
422
+ _, negative_pooled_prompt_embed = self._get_clip_prompt_embeds(
423
+ negative_prompt,
424
+ device=device,
425
+ num_images_per_prompt=num_images_per_prompt,
426
+ clip_skip=None,
427
+ clip_model_index=0,
428
+ )
429
+ _, negative_pooled_prompt_2_embed = self._get_clip_prompt_embeds(
430
+ negative_prompt_2,
431
+ device=device,
432
+ num_images_per_prompt=num_images_per_prompt,
433
+ clip_skip=None,
434
+ clip_model_index=1,
435
+ )
436
+
437
+ negative_pooled_prompt_embeds = torch.cat(
438
+ [negative_pooled_prompt_embed, negative_pooled_prompt_2_embed], dim=-1
439
+ )
440
+
441
+ if self.text_encoder is not None:
442
+ if isinstance(self, SD3LoraLoaderMixin) and USE_PEFT_BACKEND:
443
+ # Retrieve the original scale by scaling back the LoRA layers
444
+ unscale_lora_layers(self.text_encoder, lora_scale)
445
+
446
+ if self.text_encoder_2 is not None:
447
+ if isinstance(self, SD3LoraLoaderMixin) and USE_PEFT_BACKEND:
448
+ # Retrieve the original scale by scaling back the LoRA layers
449
+ unscale_lora_layers(self.text_encoder_2, lora_scale)
450
+
451
+ return pooled_prompt_embeds, negative_pooled_prompt_embeds
452
+
453
+
454
+ def encode_prompt(
455
+ self,
456
+ prompt: Union[str, List[str]],
457
+ prompt_2: Union[str, List[str]],
458
+ prompt_3: Union[str, List[str]],
459
+ device: Optional[torch.device] = None,
460
+ num_images_per_prompt: int = 1,
461
+ do_classifier_free_guidance: bool = True,
462
+ negative_prompt: Optional[Union[str, List[str]]] = None,
463
+ negative_prompt_2: Optional[Union[str, List[str]]] = None,
464
+ negative_prompt_3: Optional[Union[str, List[str]]] = None,
465
+ prompt_embeds: Optional[torch.FloatTensor] = None,
466
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
467
+ pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
468
+ negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
469
+ clip_skip: Optional[int] = None,
470
+ max_sequence_length: int = 256,
471
+ lora_scale: Optional[float] = None,
472
+ ):
473
+ r"""
474
+
475
+ Args:
476
+ prompt (`str` or `List[str]`, *optional*):
477
+ prompt to be encoded
478
+ prompt_2 (`str` or `List[str]`, *optional*):
479
+ The prompt or prompts to be sent to the `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
480
+ used in all text-encoders
481
+ prompt_3 (`str` or `List[str]`, *optional*):
482
+ The prompt or prompts to be sent to the `tokenizer_3` and `text_encoder_3`. If not defined, `prompt` is
483
+ used in all text-encoders
484
+ device: (`torch.device`):
485
+ torch device
486
+ num_images_per_prompt (`int`):
487
+ number of images that should be generated per prompt
488
+ do_classifier_free_guidance (`bool`):
489
+ whether to use classifier free guidance or not
490
+ negative_prompt (`str` or `List[str]`, *optional*):
491
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
492
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
493
+ less than `1`).
494
+ negative_prompt_2 (`str` or `List[str]`, *optional*):
495
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and
496
+ `text_encoder_2`. If not defined, `negative_prompt` is used in all the text-encoders.
497
+ negative_prompt_3 (`str` or `List[str]`, *optional*):
498
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_3` and
499
+ `text_encoder_3`. If not defined, `negative_prompt` is used in all the text-encoders.
500
+ prompt_embeds (`torch.FloatTensor`, *optional*):
501
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
502
+ provided, text embeddings will be generated from `prompt` input argument.
503
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
504
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
505
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
506
+ argument.
507
+ pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
508
+ Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
509
+ If not provided, pooled text embeddings will be generated from `prompt` input argument.
510
+ negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
511
+ Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
512
+ weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt`
513
+ input argument.
514
+ clip_skip (`int`, *optional*):
515
+ Number of layers to be skipped from CLIP while computing the prompt embeddings. A value of 1 means that
516
+ the output of the pre-final layer will be used for computing the prompt embeddings.
517
+ lora_scale (`float`, *optional*):
518
+ A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
519
+ """
520
+ device = device or self._execution_device
521
+
522
+ # set lora scale so that monkey patched LoRA
523
+ # function of text encoder can correctly access it
524
+ if lora_scale is not None and isinstance(self, SD3LoraLoaderMixin):
525
+ self._lora_scale = lora_scale
526
+
527
+ # dynamically adjust the LoRA scale
528
+ if self.text_encoder is not None and USE_PEFT_BACKEND:
529
+ scale_lora_layers(self.text_encoder, lora_scale)
530
+ if self.text_encoder_2 is not None and USE_PEFT_BACKEND:
531
+ scale_lora_layers(self.text_encoder_2, lora_scale)
532
+
533
+ prompt = [prompt] if isinstance(prompt, str) else prompt
534
+ if prompt is not None:
535
+ batch_size = len(prompt)
536
+ else:
537
+ batch_size = prompt_embeds.shape[0]
538
+
539
+ if prompt_embeds is None:
540
+ prompt_2 = prompt_2 or prompt
541
+ prompt_2 = [prompt_2] if isinstance(prompt_2, str) else prompt_2
542
+
543
+ prompt_3 = prompt_3 or prompt
544
+ prompt_3 = [prompt_3] if isinstance(prompt_3, str) else prompt_3
545
+
546
+ prompt_embed, pooled_prompt_embed = self._get_clip_prompt_embeds(
547
+ prompt=prompt,
548
+ device=device,
549
+ num_images_per_prompt=num_images_per_prompt,
550
+ clip_skip=clip_skip,
551
+ clip_model_index=0,
552
+ )
553
+ prompt_2_embed, pooled_prompt_2_embed = self._get_clip_prompt_embeds(
554
+ prompt=prompt_2,
555
+ device=device,
556
+ num_images_per_prompt=num_images_per_prompt,
557
+ clip_skip=clip_skip,
558
+ clip_model_index=1,
559
+ )
560
+ clip_prompt_embeds = torch.cat([prompt_embed, prompt_2_embed], dim=-1)
561
+
562
+ t5_prompt_embed = self._get_t5_prompt_embeds(
563
+ prompt=prompt_3,
564
+ num_images_per_prompt=num_images_per_prompt,
565
+ max_sequence_length=max_sequence_length,
566
+ device=device,
567
+ )
568
+
569
+ clip_prompt_embeds = torch.nn.functional.pad(
570
+ clip_prompt_embeds, (0, t5_prompt_embed.shape[-1] - clip_prompt_embeds.shape[-1])
571
+ )
572
+
573
+ prompt_embeds = torch.cat([clip_prompt_embeds, t5_prompt_embed], dim=-2)
574
+ pooled_prompt_embeds = torch.cat([pooled_prompt_embed, pooled_prompt_2_embed], dim=-1)
575
+
576
+ if do_classifier_free_guidance and negative_prompt_embeds is None:
577
+ negative_prompt = negative_prompt or ""
578
+ negative_prompt_2 = negative_prompt_2 or negative_prompt
579
+ negative_prompt_3 = negative_prompt_3 or negative_prompt
580
+
581
+ # normalize str to list
582
+ negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
583
+ negative_prompt_2 = (
584
+ batch_size * [negative_prompt_2] if isinstance(negative_prompt_2, str) else negative_prompt_2
585
+ )
586
+ negative_prompt_3 = (
587
+ batch_size * [negative_prompt_3] if isinstance(negative_prompt_3, str) else negative_prompt_3
588
+ )
589
+
590
+ if prompt is not None and type(prompt) is not type(negative_prompt):
591
+ raise TypeError(
592
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
593
+ f" {type(prompt)}."
594
+ )
595
+ elif batch_size != len(negative_prompt):
596
+ raise ValueError(
597
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
598
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
599
+ " the batch size of `prompt`."
600
+ )
601
+
602
+ negative_prompt_embed, negative_pooled_prompt_embed = self._get_clip_prompt_embeds(
603
+ negative_prompt,
604
+ device=device,
605
+ num_images_per_prompt=num_images_per_prompt,
606
+ clip_skip=None,
607
+ clip_model_index=0,
608
+ )
609
+ negative_prompt_2_embed, negative_pooled_prompt_2_embed = self._get_clip_prompt_embeds(
610
+ negative_prompt_2,
611
+ device=device,
612
+ num_images_per_prompt=num_images_per_prompt,
613
+ clip_skip=None,
614
+ clip_model_index=1,
615
+ )
616
+ negative_clip_prompt_embeds = torch.cat([negative_prompt_embed, negative_prompt_2_embed], dim=-1)
617
+
618
+ t5_negative_prompt_embed = self._get_t5_prompt_embeds(
619
+ prompt=negative_prompt_3,
620
+ num_images_per_prompt=num_images_per_prompt,
621
+ max_sequence_length=max_sequence_length,
622
+ device=device,
623
+ )
624
+
625
+ negative_clip_prompt_embeds = torch.nn.functional.pad(
626
+ negative_clip_prompt_embeds,
627
+ (0, t5_negative_prompt_embed.shape[-1] - negative_clip_prompt_embeds.shape[-1]),
628
+ )
629
+
630
+ negative_prompt_embeds = torch.cat([negative_clip_prompt_embeds, t5_negative_prompt_embed], dim=-2)
631
+ negative_pooled_prompt_embeds = torch.cat(
632
+ [negative_pooled_prompt_embed, negative_pooled_prompt_2_embed], dim=-1
633
+ )
634
+
635
+ if self.text_encoder is not None:
636
+ if isinstance(self, SD3LoraLoaderMixin) and USE_PEFT_BACKEND:
637
+ # Retrieve the original scale by scaling back the LoRA layers
638
+ unscale_lora_layers(self.text_encoder, lora_scale)
639
+
640
+ if self.text_encoder_2 is not None:
641
+ if isinstance(self, SD3LoraLoaderMixin) and USE_PEFT_BACKEND:
642
+ # Retrieve the original scale by scaling back the LoRA layers
643
+ unscale_lora_layers(self.text_encoder_2, lora_scale)
644
+
645
+ return prompt_embeds, negative_prompt_embeds, pooled_prompt_embeds, negative_pooled_prompt_embeds
646
+
647
+ def check_inputs(
648
+ self,
649
+ prompt,
650
+ prompt_2,
651
+ prompt_3,
652
+ height,
653
+ width,
654
+ negative_prompt=None,
655
+ negative_prompt_2=None,
656
+ negative_prompt_3=None,
657
+ prompt_embeds=None,
658
+ negative_prompt_embeds=None,
659
+ pooled_prompt_embeds=None,
660
+ negative_pooled_prompt_embeds=None,
661
+ callback_on_step_end_tensor_inputs=None,
662
+ max_sequence_length=None,
663
+ ):
664
+ if (
665
+ height % (self.vae_scale_factor * self.patch_size) != 0
666
+ or width % (self.vae_scale_factor * self.patch_size) != 0
667
+ ):
668
+ raise ValueError(
669
+ f"`height` and `width` have to be divisible by {self.vae_scale_factor * self.patch_size} but are {height} and {width}."
670
+ f"You can use height {height - height % (self.vae_scale_factor * self.patch_size)} and width {width - width % (self.vae_scale_factor * self.patch_size)}."
671
+ )
672
+
673
+ if callback_on_step_end_tensor_inputs is not None and not all(
674
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
675
+ ):
676
+ raise ValueError(
677
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
678
+ )
679
+
680
+ if prompt is not None and prompt_embeds is not None:
681
+ raise ValueError(
682
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
683
+ " only forward one of the two."
684
+ )
685
+ elif prompt_2 is not None and prompt_embeds is not None:
686
+ raise ValueError(
687
+ f"Cannot forward both `prompt_2`: {prompt_2} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
688
+ " only forward one of the two."
689
+ )
690
+ elif prompt_3 is not None and prompt_embeds is not None:
691
+ raise ValueError(
692
+ f"Cannot forward both `prompt_3`: {prompt_2} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
693
+ " only forward one of the two."
694
+ )
695
+ elif prompt is None and prompt_embeds is None:
696
+ raise ValueError(
697
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
698
+ )
699
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
700
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
701
+ elif prompt_2 is not None and (not isinstance(prompt_2, str) and not isinstance(prompt_2, list)):
702
+ raise ValueError(f"`prompt_2` has to be of type `str` or `list` but is {type(prompt_2)}")
703
+ elif prompt_3 is not None and (not isinstance(prompt_3, str) and not isinstance(prompt_3, list)):
704
+ raise ValueError(f"`prompt_3` has to be of type `str` or `list` but is {type(prompt_3)}")
705
+
706
+ if negative_prompt is not None and negative_prompt_embeds is not None:
707
+ raise ValueError(
708
+ f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
709
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
710
+ )
711
+ elif negative_prompt_2 is not None and negative_prompt_embeds is not None:
712
+ raise ValueError(
713
+ f"Cannot forward both `negative_prompt_2`: {negative_prompt_2} and `negative_prompt_embeds`:"
714
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
715
+ )
716
+ elif negative_prompt_3 is not None and negative_prompt_embeds is not None:
717
+ raise ValueError(
718
+ f"Cannot forward both `negative_prompt_3`: {negative_prompt_3} and `negative_prompt_embeds`:"
719
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
720
+ )
721
+
722
+ if prompt_embeds is not None and negative_prompt_embeds is not None:
723
+ if prompt_embeds.shape != negative_prompt_embeds.shape:
724
+ raise ValueError(
725
+ "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
726
+ f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
727
+ f" {negative_prompt_embeds.shape}."
728
+ )
729
+
730
+ if prompt_embeds is not None and pooled_prompt_embeds is None:
731
+ raise ValueError(
732
+ "If `prompt_embeds` are provided, `pooled_prompt_embeds` also have to be passed. Make sure to generate `pooled_prompt_embeds` from the same text encoder that was used to generate `prompt_embeds`."
733
+ )
734
+
735
+ if negative_prompt_embeds is not None and negative_pooled_prompt_embeds is None:
736
+ raise ValueError(
737
+ "If `negative_prompt_embeds` are provided, `negative_pooled_prompt_embeds` also have to be passed. Make sure to generate `negative_pooled_prompt_embeds` from the same text encoder that was used to generate `negative_prompt_embeds`."
738
+ )
739
+
740
+ if max_sequence_length is not None and max_sequence_length > 512:
741
+ raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
742
+
743
+ def prepare_latents(
744
+ self,
745
+ batch_size,
746
+ num_channels_latents,
747
+ height,
748
+ width,
749
+ dtype,
750
+ device,
751
+ generator,
752
+ latents=None,
753
+ ):
754
+ if latents is not None:
755
+ return latents.to(device=device, dtype=dtype)
756
+
757
+ shape = (
758
+ batch_size,
759
+ num_channels_latents,
760
+ int(height) // self.vae_scale_factor,
761
+ int(width) // self.vae_scale_factor,
762
+ )
763
+
764
+ if isinstance(generator, list) and len(generator) != batch_size:
765
+ raise ValueError(
766
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
767
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
768
+ )
769
+
770
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
771
+
772
+ return latents
773
+
774
+ @property
775
+ def guidance_scale(self):
776
+ return self._guidance_scale
777
+
778
+ @property
779
+ def skip_guidance_layers(self):
780
+ return self._skip_guidance_layers
781
+
782
+ @property
783
+ def clip_skip(self):
784
+ return self._clip_skip
785
+
786
+ # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)
787
+ # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`
788
+ # corresponds to doing no classifier free guidance.
789
+ @property
790
+ def do_classifier_free_guidance(self):
791
+ return self._guidance_scale > 1
792
+
793
+ @property
794
+ def joint_attention_kwargs(self):
795
+ return self._joint_attention_kwargs
796
+
797
+ @property
798
+ def num_timesteps(self):
799
+ return self._num_timesteps
800
+
801
+ @property
802
+ def interrupt(self):
803
+ return self._interrupt
804
+
805
+ # Adapted from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.encode_image
806
+ def encode_image(self, image: PipelineImageInput, device: torch.device) -> torch.Tensor:
807
+ """Encodes the given image into a feature representation using a pre-trained image encoder.
808
+
809
+ Args:
810
+ image (`PipelineImageInput`):
811
+ Input image to be encoded.
812
+ device: (`torch.device`):
813
+ Torch device.
814
+
815
+ Returns:
816
+ `torch.Tensor`: The encoded image feature representation.
817
+ """
818
+ if not isinstance(image, torch.Tensor):
819
+ image = self.feature_extractor(image, return_tensors="pt").pixel_values
820
+
821
+ image = image.to(device=device, dtype=self.dtype)
822
+
823
+ return self.image_encoder(image, output_hidden_states=True).hidden_states[-2]
824
+
825
+ # Adapted from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_xl.StableDiffusionXLPipeline.prepare_ip_adapter_image_embeds
826
+ def prepare_ip_adapter_image_embeds(
827
+ self,
828
+ ip_adapter_image: Optional[PipelineImageInput] = None,
829
+ ip_adapter_image_embeds: Optional[torch.Tensor] = None,
830
+ device: Optional[torch.device] = None,
831
+ num_images_per_prompt: int = 1,
832
+ do_classifier_free_guidance: bool = True,
833
+ ) -> torch.Tensor:
834
+ """Prepares image embeddings for use in the IP-Adapter.
835
+
836
+ Either `ip_adapter_image` or `ip_adapter_image_embeds` must be passed.
837
+
838
+ Args:
839
+ ip_adapter_image (`PipelineImageInput`, *optional*):
840
+ The input image to extract features from for IP-Adapter.
841
+ ip_adapter_image_embeds (`torch.Tensor`, *optional*):
842
+ Precomputed image embeddings.
843
+ device: (`torch.device`, *optional*):
844
+ Torch device.
845
+ num_images_per_prompt (`int`, defaults to 1):
846
+ Number of images that should be generated per prompt.
847
+ do_classifier_free_guidance (`bool`, defaults to True):
848
+ Whether to use classifier free guidance or not.
849
+ """
850
+ device = device or self._execution_device
851
+
852
+ if ip_adapter_image_embeds is not None:
853
+ if do_classifier_free_guidance:
854
+ single_negative_image_embeds, single_image_embeds = ip_adapter_image_embeds.chunk(2)
855
+ else:
856
+ single_image_embeds = ip_adapter_image_embeds
857
+ elif ip_adapter_image is not None:
858
+ single_image_embeds = self.encode_image(ip_adapter_image, device)
859
+ if do_classifier_free_guidance:
860
+ single_negative_image_embeds = torch.zeros_like(single_image_embeds)
861
+ else:
862
+ raise ValueError("Neither `ip_adapter_image_embeds` or `ip_adapter_image_embeds` were provided.")
863
+
864
+ image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
865
+
866
+ if do_classifier_free_guidance:
867
+ negative_image_embeds = torch.cat([single_negative_image_embeds] * num_images_per_prompt, dim=0)
868
+ image_embeds = torch.cat([negative_image_embeds, image_embeds], dim=0)
869
+
870
+ return image_embeds.to(device=device)
871
+
872
+ def enable_sequential_cpu_offload(self, *args, **kwargs):
873
+ if self.image_encoder is not None and "image_encoder" not in self._exclude_from_cpu_offload:
874
+ logger.warning(
875
+ "`pipe.enable_sequential_cpu_offload()` might fail for `image_encoder` if it uses "
876
+ "`torch.nn.MultiheadAttention`. You can exclude `image_encoder` from CPU offloading by calling "
877
+ "`pipe._exclude_from_cpu_offload.append('image_encoder')` before `pipe.enable_sequential_cpu_offload()`."
878
+ )
879
+
880
+ super().enable_sequential_cpu_offload(*args, **kwargs)
881
+
882
+ @torch.no_grad()
883
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
884
+ def __call__(
885
+ self,
886
+ prompt: Union[str, List[str]] = None,
887
+ prompt_2: Optional[Union[str, List[str]]] = None,
888
+ prompt_3: Optional[Union[str, List[str]]] = None,
889
+ height: Optional[int] = None,
890
+ width: Optional[int] = None,
891
+ num_inference_steps: int = 28,
892
+ sigmas: Optional[List[float]] = None,
893
+ guidance_scale: float = 7.0,
894
+ negative_prompt: Optional[Union[str, List[str]]] = None,
895
+ negative_prompt_2: Optional[Union[str, List[str]]] = None,
896
+ negative_prompt_3: Optional[Union[str, List[str]]] = None,
897
+ num_images_per_prompt: Optional[int] = 1,
898
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
899
+ latents: Optional[torch.FloatTensor] = None,
900
+ cond_latents: Optional[list[torch.FloatTensor]] = None,
901
+ prompt_embeds: Optional[torch.FloatTensor] = None,
902
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
903
+ pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
904
+ negative_pooled_prompt_embeds: Optional[torch.FloatTensor] = None,
905
+ ip_adapter_image: Optional[PipelineImageInput] = None,
906
+ ip_adapter_image_embeds: Optional[torch.Tensor] = None,
907
+ output_type: Optional[str] = "pil",
908
+ return_dict: bool = True,
909
+ joint_attention_kwargs: Optional[Dict[str, Any]] = None,
910
+ clip_skip: Optional[int] = None,
911
+ callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
912
+ callback_on_step_end_tensor_inputs: List[str] = ["latents"],
913
+ max_sequence_length: int = 256,
914
+ skip_guidance_layers: List[int] = None,
915
+ skip_layer_guidance_scale: float = 2.8,
916
+ skip_layer_guidance_stop: float = 0.2,
917
+ skip_layer_guidance_start: float = 0.01,
918
+ mu: Optional[float] = None,
919
+ ):
920
+ r"""
921
+ Function invoked when calling the pipeline for generation.
922
+
923
+ Args:
924
+ prompt (`str` or `List[str]`, *optional*):
925
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
926
+ instead.
927
+ prompt_2 (`str` or `List[str]`, *optional*):
928
+ The prompt or prompts to be sent to `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
929
+ will be used instead
930
+ prompt_3 (`str` or `List[str]`, *optional*):
931
+ The prompt or prompts to be sent to `tokenizer_3` and `text_encoder_3`. If not defined, `prompt` is
932
+ will be used instead
933
+ height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
934
+ The height in pixels of the generated image. This is set to 1024 by default for the best results.
935
+ width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
936
+ The width in pixels of the generated image. This is set to 1024 by default for the best results.
937
+ num_inference_steps (`int`, *optional*, defaults to 50):
938
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
939
+ expense of slower inference.
940
+ sigmas (`List[float]`, *optional*):
941
+ Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
942
+ their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
943
+ will be used.
944
+ guidance_scale (`float`, *optional*, defaults to 7.0):
945
+ Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598).
946
+ `guidance_scale` is defined as `w` of equation 2. of [Imagen
947
+ Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale >
948
+ 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`,
949
+ usually at the expense of lower image quality.
950
+ negative_prompt (`str` or `List[str]`, *optional*):
951
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
952
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
953
+ less than `1`).
954
+ negative_prompt_2 (`str` or `List[str]`, *optional*):
955
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and
956
+ `text_encoder_2`. If not defined, `negative_prompt` is used instead
957
+ negative_prompt_3 (`str` or `List[str]`, *optional*):
958
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_3` and
959
+ `text_encoder_3`. If not defined, `negative_prompt` is used instead
960
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
961
+ The number of images to generate per prompt.
962
+ generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
963
+ One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
964
+ to make generation deterministic.
965
+ latents (`torch.FloatTensor`, *optional*):
966
+ Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
967
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
968
+ tensor will ge generated by sampling using the supplied random `generator`.
969
+ prompt_embeds (`torch.FloatTensor`, *optional*):
970
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
971
+ provided, text embeddings will be generated from `prompt` input argument.
972
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
973
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
974
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
975
+ argument.
976
+ pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
977
+ Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
978
+ If not provided, pooled text embeddings will be generated from `prompt` input argument.
979
+ negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
980
+ Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
981
+ weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt`
982
+ input argument.
983
+ ip_adapter_image (`PipelineImageInput`, *optional*):
984
+ Optional image input to work with IP Adapters.
985
+ ip_adapter_image_embeds (`torch.Tensor`, *optional*):
986
+ Pre-generated image embeddings for IP-Adapter. Should be a tensor of shape `(batch_size, num_images,
987
+ emb_dim)`. It should contain the negative image embedding if `do_classifier_free_guidance` is set to
988
+ `True`. If not provided, embeddings are computed from the `ip_adapter_image` input argument.
989
+ output_type (`str`, *optional*, defaults to `"pil"`):
990
+ The output format of the generate image. Choose between
991
+ [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
992
+ return_dict (`bool`, *optional*, defaults to `True`):
993
+ Whether or not to return a [`~pipelines.stable_diffusion_3.StableDiffusion3PipelineOutput`] instead of
994
+ a plain tuple.
995
+ joint_attention_kwargs (`dict`, *optional*):
996
+ A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
997
+ `self.processor` in
998
+ [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
999
+ callback_on_step_end (`Callable`, *optional*):
1000
+ A function that calls at the end of each denoising steps during the inference. The function is called
1001
+ with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
1002
+ callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
1003
+ `callback_on_step_end_tensor_inputs`.
1004
+ callback_on_step_end_tensor_inputs (`List`, *optional*):
1005
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
1006
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
1007
+ `._callback_tensor_inputs` attribute of your pipeline class.
1008
+ max_sequence_length (`int` defaults to 256): Maximum sequence length to use with the `prompt`.
1009
+ skip_guidance_layers (`List[int]`, *optional*):
1010
+ A list of integers that specify layers to skip during guidance. If not provided, all layers will be
1011
+ used for guidance. If provided, the guidance will only be applied to the layers specified in the list.
1012
+ Recommended value by StabiltyAI for Stable Diffusion 3.5 Medium is [7, 8, 9].
1013
+ skip_layer_guidance_scale (`int`, *optional*): The scale of the guidance for the layers specified in
1014
+ `skip_guidance_layers`. The guidance will be applied to the layers specified in `skip_guidance_layers`
1015
+ with a scale of `skip_layer_guidance_scale`. The guidance will be applied to the rest of the layers
1016
+ with a scale of `1`.
1017
+ skip_layer_guidance_stop (`int`, *optional*): The step at which the guidance for the layers specified in
1018
+ `skip_guidance_layers` will stop. The guidance will be applied to the layers specified in
1019
+ `skip_guidance_layers` until the fraction specified in `skip_layer_guidance_stop`. Recommended value by
1020
+ StabiltyAI for Stable Diffusion 3.5 Medium is 0.2.
1021
+ skip_layer_guidance_start (`int`, *optional*): The step at which the guidance for the layers specified in
1022
+ `skip_guidance_layers` will start. The guidance will be applied to the layers specified in
1023
+ `skip_guidance_layers` from the fraction specified in `skip_layer_guidance_start`. Recommended value by
1024
+ StabiltyAI for Stable Diffusion 3.5 Medium is 0.01.
1025
+ mu (`float`, *optional*): `mu` value used for `dynamic_shifting`.
1026
+
1027
+ Examples:
1028
+
1029
+ Returns:
1030
+ [`~pipelines.stable_diffusion_3.StableDiffusion3PipelineOutput`] or `tuple`:
1031
+ [`~pipelines.stable_diffusion_3.StableDiffusion3PipelineOutput`] if `return_dict` is True, otherwise a
1032
+ `tuple`. When returning a tuple, the first element is a list with the generated images.
1033
+ """
1034
+
1035
+ height = height or self.default_sample_size * self.vae_scale_factor
1036
+ width = width or self.default_sample_size * self.vae_scale_factor
1037
+
1038
+ # 1. Check inputs. Raise error if not correct
1039
+ self.check_inputs(
1040
+ prompt,
1041
+ prompt_2,
1042
+ prompt_3,
1043
+ height,
1044
+ width,
1045
+ negative_prompt=negative_prompt,
1046
+ negative_prompt_2=negative_prompt_2,
1047
+ negative_prompt_3=negative_prompt_3,
1048
+ prompt_embeds=prompt_embeds,
1049
+ negative_prompt_embeds=negative_prompt_embeds,
1050
+ pooled_prompt_embeds=pooled_prompt_embeds,
1051
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
1052
+ callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
1053
+ max_sequence_length=max_sequence_length,
1054
+ )
1055
+
1056
+ self._guidance_scale = guidance_scale
1057
+ self._skip_layer_guidance_scale = skip_layer_guidance_scale
1058
+ self._clip_skip = clip_skip
1059
+ self._joint_attention_kwargs = joint_attention_kwargs
1060
+ self._interrupt = False
1061
+
1062
+ # 2. Define call parameters
1063
+ if prompt is not None and isinstance(prompt, str):
1064
+ batch_size = 1
1065
+ elif prompt is not None and isinstance(prompt, list):
1066
+ batch_size = len(prompt)
1067
+ else:
1068
+ batch_size = prompt_embeds.shape[0]
1069
+
1070
+ device = self._execution_device
1071
+
1072
+ lora_scale = (
1073
+ self.joint_attention_kwargs.get("scale", None) if self.joint_attention_kwargs is not None else None
1074
+ )
1075
+ (
1076
+ prompt_embeds,
1077
+ negative_prompt_embeds,
1078
+ pooled_prompt_embeds,
1079
+ negative_pooled_prompt_embeds,
1080
+ ) = self.encode_prompt(
1081
+ prompt=prompt,
1082
+ prompt_2=prompt_2,
1083
+ prompt_3=prompt_3,
1084
+ negative_prompt=negative_prompt,
1085
+ negative_prompt_2=negative_prompt_2,
1086
+ negative_prompt_3=negative_prompt_3,
1087
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
1088
+ prompt_embeds=prompt_embeds,
1089
+ negative_prompt_embeds=negative_prompt_embeds,
1090
+ pooled_prompt_embeds=pooled_prompt_embeds,
1091
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
1092
+ device=device,
1093
+ clip_skip=self.clip_skip,
1094
+ num_images_per_prompt=num_images_per_prompt,
1095
+ max_sequence_length=max_sequence_length,
1096
+ lora_scale=lora_scale,
1097
+ )
1098
+
1099
+ if self.do_classifier_free_guidance:
1100
+ if skip_guidance_layers is not None:
1101
+ original_prompt_embeds = prompt_embeds
1102
+ original_pooled_prompt_embeds = pooled_prompt_embeds
1103
+ prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0)
1104
+ pooled_prompt_embeds = torch.cat([negative_pooled_prompt_embeds, pooled_prompt_embeds], dim=0)
1105
+
1106
+ # 4. Prepare latent variables
1107
+ num_channels_latents = self.transformer.config.in_channels
1108
+ latents = self.prepare_latents(
1109
+ batch_size * num_images_per_prompt,
1110
+ num_channels_latents,
1111
+ height,
1112
+ width,
1113
+ prompt_embeds.dtype,
1114
+ device,
1115
+ generator,
1116
+ latents,
1117
+ )
1118
+
1119
+ # 5. Prepare timesteps
1120
+ scheduler_kwargs = {}
1121
+ if self.scheduler.config.get("use_dynamic_shifting", None) and mu is None:
1122
+ _, _, height, width = latents.shape
1123
+ image_seq_len = (height // self.transformer.config.patch_size) * (
1124
+ width // self.transformer.config.patch_size
1125
+ )
1126
+ mu = calculate_shift(
1127
+ image_seq_len,
1128
+ self.scheduler.config.get("base_image_seq_len", 256),
1129
+ self.scheduler.config.get("max_image_seq_len", 4096),
1130
+ self.scheduler.config.get("base_shift", 0.5),
1131
+ self.scheduler.config.get("max_shift", 1.16),
1132
+ )
1133
+ scheduler_kwargs["mu"] = mu
1134
+ elif mu is not None:
1135
+ scheduler_kwargs["mu"] = mu
1136
+ timesteps, num_inference_steps = retrieve_timesteps(
1137
+ self.scheduler,
1138
+ num_inference_steps,
1139
+ device,
1140
+ sigmas=sigmas,
1141
+ **scheduler_kwargs,
1142
+ )
1143
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
1144
+ self._num_timesteps = len(timesteps)
1145
+
1146
+ # 6. Prepare image embeddings
1147
+ if (ip_adapter_image is not None and self.is_ip_adapter_active) or ip_adapter_image_embeds is not None:
1148
+ ip_adapter_image_embeds = self.prepare_ip_adapter_image_embeds(
1149
+ ip_adapter_image,
1150
+ ip_adapter_image_embeds,
1151
+ device,
1152
+ batch_size * num_images_per_prompt,
1153
+ self.do_classifier_free_guidance,
1154
+ )
1155
+
1156
+ if self.joint_attention_kwargs is None:
1157
+ self._joint_attention_kwargs = {"ip_adapter_image_embeds": ip_adapter_image_embeds}
1158
+ else:
1159
+ self._joint_attention_kwargs.update(ip_adapter_image_embeds=ip_adapter_image_embeds)
1160
+
1161
+
1162
+ if cond_latents is not None and self.do_classifier_free_guidance:
1163
+ if len(cond_latents) == latents.shape[0]:
1164
+ cond_latents = cond_latents * 2
1165
+
1166
+ # 7. Denoising loop
1167
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
1168
+ for i, t in enumerate(timesteps):
1169
+ if self.interrupt:
1170
+ continue
1171
+
1172
+ # expand the latents if we are doing classifier free guidance
1173
+ latent_model_input = torch.cat([latents] * 2) if self.do_classifier_free_guidance else latents
1174
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
1175
+ timestep = t.expand(latent_model_input.shape[0])
1176
+
1177
+ noise_pred = self.transformer(
1178
+ hidden_states=latent_model_input,
1179
+ cond_hidden_states=cond_latents,
1180
+ timestep=timestep,
1181
+ encoder_hidden_states=prompt_embeds,
1182
+ pooled_projections=pooled_prompt_embeds,
1183
+ joint_attention_kwargs=self.joint_attention_kwargs,
1184
+ return_dict=False,
1185
+ )[0]
1186
+
1187
+ # perform guidance
1188
+ if self.do_classifier_free_guidance:
1189
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
1190
+ noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_text - noise_pred_uncond)
1191
+ should_skip_layers = (
1192
+ True
1193
+ if i > num_inference_steps * skip_layer_guidance_start
1194
+ and i < num_inference_steps * skip_layer_guidance_stop
1195
+ else False
1196
+ )
1197
+ if skip_guidance_layers is not None and should_skip_layers:
1198
+ timestep = t.expand(latents.shape[0])
1199
+ latent_model_input = latents
1200
+ noise_pred_skip_layers = self.transformer(
1201
+ hidden_states=latent_model_input,
1202
+ timestep=timestep,
1203
+ encoder_hidden_states=original_prompt_embeds,
1204
+ pooled_projections=original_pooled_prompt_embeds,
1205
+ joint_attention_kwargs=self.joint_attention_kwargs,
1206
+ return_dict=False,
1207
+ skip_layers=skip_guidance_layers,
1208
+ )[0]
1209
+ noise_pred = (
1210
+ noise_pred + (noise_pred_text - noise_pred_skip_layers) * self._skip_layer_guidance_scale
1211
+ )
1212
+
1213
+ # compute the previous noisy sample x_t -> x_t-1
1214
+ latents_dtype = latents.dtype
1215
+ latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
1216
+
1217
+ if latents.dtype != latents_dtype:
1218
+ if torch.backends.mps.is_available():
1219
+ # some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
1220
+ latents = latents.to(latents_dtype)
1221
+
1222
+ if callback_on_step_end is not None:
1223
+ callback_kwargs = {}
1224
+ for k in callback_on_step_end_tensor_inputs:
1225
+ callback_kwargs[k] = locals()[k]
1226
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
1227
+
1228
+ latents = callback_outputs.pop("latents", latents)
1229
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
1230
+ negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
1231
+ negative_pooled_prompt_embeds = callback_outputs.pop(
1232
+ "negative_pooled_prompt_embeds", negative_pooled_prompt_embeds
1233
+ )
1234
+
1235
+ # call the callback, if provided
1236
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
1237
+ progress_bar.update()
1238
+
1239
+ if XLA_AVAILABLE:
1240
+ xm.mark_step()
1241
+
1242
+ if output_type == "latent":
1243
+ image = latents
1244
+
1245
+ else:
1246
+ latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
1247
+
1248
+ image = self.vae.decode(latents, return_dict=False)[0]
1249
+ image = self.image_processor.postprocess(image, output_type=output_type)
1250
+
1251
+ # Offload all models
1252
+ self.maybe_free_model_hooks()
1253
+
1254
+ if not return_dict:
1255
+ return (image,)
1256
+
1257
+ return StableDiffusion3PipelineOutput(images=image)
src/models/sd3_kontext/qwen2_5_vl_sd3_hf_dynamic.py ADDED
@@ -0,0 +1,792 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import torch
3
+ import math
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+ from torch.nn.modules.module import T
7
+ import torch.distributed as dist
8
+ from mmengine.logging import print_log
9
+ from src.models.connector import ConnectorConfig, ConnectorEncoder
10
+ from xtuner.model.utils import guess_load_checkpoint
11
+ from xtuner.registry import BUILDER
12
+ from diffusers.training_utils import compute_density_for_timestep_sampling, compute_loss_weighting_for_sd3
13
+ from peft import LoraConfig
14
+ from src.models.sd3_kontext.pipeline_stable_diffusion_3_dynamic import StableDiffusion3Pipeline, calculate_shift
15
+ from mmengine.model import BaseModel
16
+ from functools import partial
17
+ from six.moves import map, zip
18
+ from copy import deepcopy
19
+ from einops import rearrange
20
+
21
+
22
+ IMAGE_MEAN = (0.48145466, 0.4578275, 0.40821073)
23
+ IMAGE_STD = (0.26862954, 0.26130258, 0.27577711)
24
+
25
+
26
+ def multi_apply(func, *args, **kwargs):
27
+ pfunc = partial(func, **kwargs) if kwargs else func
28
+ map_results = map(pfunc, *args)
29
+ return tuple(map(list, zip(*map_results)))
30
+
31
+
32
+ def find_target_linear_names(model, num_lora_modules=-1, lora_namespan_exclude=[], verbose=True):
33
+ linear_cls = torch.nn.modules.Linear
34
+ embedding_cls = torch.nn.modules.Embedding
35
+ lora_module_names = []
36
+
37
+ for name, module in model.named_modules():
38
+ if any(ex_keyword in name for ex_keyword in lora_namespan_exclude):
39
+ continue
40
+ if isinstance(module, (linear_cls, embedding_cls)):
41
+ lora_module_names.append(name)
42
+
43
+ if num_lora_modules > 0:
44
+ lora_module_names = lora_module_names[-num_lora_modules:]
45
+ if verbose:
46
+ print(f"Found {len(lora_module_names)} lora modules: {lora_module_names}")
47
+ return lora_module_names
48
+
49
+
50
+ class Qwen2p5VLStableDiffusion3HF(BaseModel):
51
+ def __init__(self,
52
+ transformer,
53
+ train_scheduler,
54
+ test_scheduler,
55
+ vae,
56
+ lmm,
57
+ tokenizer,
58
+ prompt_template,
59
+ connector,
60
+ num_queries=64,
61
+ vit_input_size=448,
62
+ max_length=1024,
63
+ freeze_lmm=True,
64
+ freeze_mq=False,
65
+ res_vit=False,
66
+ pretrained_pth=None,
67
+ use_activation_checkpointing=False,
68
+ lora_modules='auto', # ["to_k", "to_q", "to_v", "to_out.0"], 'auto'
69
+ lora_rank=64,
70
+ lora_alpha=128,
71
+ freeze_transformer=True,
72
+ unconditional=0.1,
73
+ ema_cfg=None,
74
+ weighting_scheme='none',
75
+ logit_mean=0.0,
76
+ logit_std=1.0,
77
+ ):
78
+ super().__init__()
79
+
80
+ self.lmm = BUILDER.build(lmm)
81
+ if freeze_lmm:
82
+ self.lmm.requires_grad_(False)
83
+ self.freeze_lmm = freeze_lmm
84
+
85
+ self.transformer = BUILDER.build(transformer)
86
+ if freeze_transformer:
87
+ self.transformer.requires_grad_(False)
88
+ self.freeze_transformer = freeze_transformer
89
+ self.res_vit = res_vit
90
+
91
+
92
+
93
+ self.weighting_scheme = weighting_scheme
94
+ self.logit_mean = logit_mean
95
+ self.logit_std = logit_std
96
+
97
+ self.vae = BUILDER.build(vae)
98
+ self.vae.requires_grad_(False)
99
+
100
+ self.use_activation_checkpointing = use_activation_checkpointing
101
+ self.tokenizer = BUILDER.build(tokenizer)
102
+
103
+ self.prompt_template = prompt_template
104
+ self.vit_input_size = vit_input_size
105
+ self.max_length = max_length
106
+ self.image_token_id = self.tokenizer.convert_tokens_to_ids(prompt_template['IMG_CONTEXT_TOKEN'])
107
+ self.register_buffer('vit_mean', torch.tensor(IMAGE_MEAN), persistent=False)
108
+ self.register_buffer('vit_std', torch.tensor(IMAGE_STD), persistent=False)
109
+
110
+ self.num_queries = num_queries
111
+ self.connector = ConnectorEncoder(ConnectorConfig(**connector))
112
+
113
+
114
+ self.projector_1 = nn.Linear(self.llm.config.hidden_size, self.connector.config.hidden_size)
115
+ self.projector_2 = nn.Linear(self.connector.config.hidden_size, self.transformer.config.pooled_projection_dim)
116
+ self.projector_3 = nn.Linear(self.connector.config.hidden_size, self.transformer.config.joint_attention_dim)
117
+
118
+ # zero out
119
+ nn.init.zeros_(self.projector_2.weight)
120
+ nn.init.zeros_(self.projector_3.weight)
121
+ nn.init.zeros_(self.projector_2.bias)
122
+ nn.init.zeros_(self.projector_3.bias)
123
+
124
+ self.meta_queries = nn.Parameter(
125
+ torch.zeros(num_queries, self.llm.config.hidden_size))
126
+ nn.init.normal_(self.meta_queries, std=1 / math.sqrt(self.llm.config.hidden_size))
127
+
128
+ if freeze_mq:
129
+ self.projector_1.requires_grad_(False)
130
+ self.projector_2.requires_grad_(False)
131
+ self.projector_3.requires_grad_(False)
132
+ self.connector.requires_grad_(False)
133
+ self.meta_queries.requires_grad_(False)
134
+ self.freeze_mq = freeze_mq
135
+
136
+ self.unconditional = unconditional
137
+
138
+ self.train_scheduler = BUILDER.build(train_scheduler)
139
+ self.test_scheduler = BUILDER.build(test_scheduler)
140
+
141
+ if use_activation_checkpointing:
142
+ self.gradient_checkpointing_enable()
143
+
144
+ if lora_modules is not None:
145
+ assert self.freeze_lmm
146
+ self.llm.config.tie_word_embeddings = False
147
+ if lora_modules == 'auto':
148
+ lora_modules = find_target_linear_names(self.lmm)
149
+ # now we will add new LoRA weights the transformer layers
150
+ transformer_lora_config = LoraConfig(
151
+ r=lora_rank,
152
+ lora_alpha=lora_alpha,
153
+ init_lora_weights="gaussian",
154
+ target_modules=lora_modules,
155
+ lora_dropout=0.05,
156
+ )
157
+ self.lmm.add_adapter(transformer_lora_config)
158
+
159
+ if pretrained_pth is not None:
160
+ pretrained_state_dict = guess_load_checkpoint(pretrained_pth)
161
+ info = self.load_state_dict(pretrained_state_dict, strict=False)
162
+ print_log(f'Load pretrained weight from {pretrained_pth}')
163
+
164
+ self.ema_cfg = ema_cfg
165
+ if ema_cfg is not None:
166
+ self.ema = nn.ModuleDict()
167
+ self.ema.steps = 0
168
+ if not self.freeze_transformer:
169
+ self.ema.update(dict(transformer=deepcopy(self.transformer)))
170
+
171
+ if not self.freeze_mq:
172
+ self.ema.update(dict(projector_1=deepcopy(self.projector_1),
173
+ projector_2=deepcopy(self.projector_2),
174
+ projector_3=deepcopy(self.projector_3),
175
+ connector=deepcopy(self.connector)
176
+ )
177
+ )
178
+ self.ema.register_buffer('meta_queries', deepcopy(self.meta_queries.data))
179
+
180
+ self.ema.requires_grad_(False) # parameters in ema are not learnable
181
+
182
+ if 'checkpoint' in ema_cfg:
183
+ ema_state_dict = guess_load_checkpoint(ema_cfg['checkpoint'])
184
+ info = self.ema.load_state_dict(ema_state_dict, strict=False)
185
+ print_log(f"Load ema weight from {ema_cfg['checkpoint']}")
186
+
187
+ @torch.no_grad()
188
+ def ema_step(self, ):
189
+ if self.ema_cfg is None:
190
+ return
191
+
192
+ steps = self.ema.steps
193
+ update_interval = self.ema_cfg.get('update_interval', 1)
194
+ save_interval = self.ema_cfg.get('save_interval', 1000)
195
+ momentum = self.ema_cfg.get('momentum', 0.99)
196
+
197
+ if steps % update_interval == 0 and steps > 0:
198
+ if not self.freeze_mq:
199
+ for ema_param, base_param in zip(self.ema.projector_1.parameters(), self.projector_1.parameters()):
200
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
201
+ for ema_param, base_param in zip(self.ema.projector_2.parameters(), self.projector_2.parameters()):
202
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
203
+ for ema_param, base_param in zip(self.ema.projector_3.parameters(), self.projector_3.parameters()):
204
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
205
+ for ema_param, base_param in zip(self.ema.connector.parameters(), self.connector.parameters()):
206
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
207
+ self.ema.meta_queries.data.lerp_(self.meta_queries.data.detach(), 1.0 - momentum)
208
+
209
+ if not self.freeze_transformer:
210
+ for ema_param, base_param in zip(self.ema.transformer.parameters(), self.transformer.parameters()):
211
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
212
+
213
+ # print(f"steps: {steps}, rank: {dist.get_rank()},", flush=True)
214
+
215
+ if steps % save_interval == 0 and steps > 0:
216
+ is_ddp = dist.is_available() and dist.is_initialized()
217
+ is_primary_proc = (not is_ddp) or dist.get_rank() == 0
218
+ print(f"steps: {steps}, rank: {dist.get_rank()}, is_ddp:{is_ddp}, is_primary_proc: {is_primary_proc}.", flush=True)
219
+ if is_primary_proc:
220
+ save_path = self.ema_cfg.get('save_path')
221
+ torch.save(self.ema.state_dict(), save_path)
222
+ if is_ddp:
223
+ dist.barrier()
224
+
225
+ self.ema.steps = self.ema.steps + 1
226
+
227
+ def llm2dit(self, x):
228
+ x = self.connector(self.projector_1(x))
229
+ pooled_out = self.projector_2(x.mean(1))
230
+ seq_out = self.projector_3(x)
231
+
232
+ return pooled_out, seq_out
233
+
234
+ @property
235
+ def llm(self):
236
+ return self.lmm.language_model
237
+
238
+ def gradient_checkpointing_enable(self):
239
+ self.activation_checkpointing_enable()
240
+
241
+ def activation_checkpointing_enable(self):
242
+ self.llm.gradient_checkpointing_enable()
243
+ self.transformer.enable_gradient_checkpointing()
244
+ self.connector.gradient_checkpointing = True
245
+
246
+ def gradient_checkpointing_disable(self):
247
+ self.activation_checkpointing_disable()
248
+
249
+ def activation_checkpointing_disable(self):
250
+ self.llm.gradient_checkpointing_disable()
251
+ self.transformer.disable_gradient_checkpointing()
252
+ self.connector.gradient_checkpointing = False
253
+
254
+ @property
255
+ def device(self):
256
+ return self.llm.device
257
+
258
+ @property
259
+ def dtype(self):
260
+ return self.llm.dtype
261
+
262
+ def train(self: T, mode: bool = True) -> T:
263
+ super().train(mode=mode)
264
+ if self.vae is not None:
265
+ self.vae.train(mode=False)
266
+ if not mode:
267
+ self.gradient_checkpointing_disable()
268
+
269
+ return self
270
+
271
+ def state_dict(self, *args, **kwargs) -> dict:
272
+ state_dict = super().state_dict(*args, **kwargs)
273
+ state_dict = {k: v for k, v in state_dict.items()
274
+ if 'vae.' not in k and 'lmm.' not in k and 'ema.' not in k}
275
+ return state_dict
276
+
277
+ @torch.no_grad()
278
+ def pixels_to_latents(self, x):
279
+ z = self.vae.encode(x).latent_dist.sample()
280
+ z = (z - self.vae.config.shift_factor) * self.vae.config.scaling_factor
281
+ return z
282
+
283
+ @torch.no_grad()
284
+ def latents_to_pixels(self, z):
285
+ z = (z / self.vae.config.scaling_factor) + self.vae.config.shift_factor
286
+ x_rec = self.vae.decode(z).sample
287
+ return x_rec
288
+
289
+ def forward(self, data, data_samples=None, mode='loss'):
290
+ if mode == 'loss':
291
+ self.ema_step()
292
+ return self.compute_loss(data_dict=data)
293
+ else:
294
+ raise NotImplementedError
295
+
296
+ def compute_loss(self, data_dict):
297
+ losses = {}
298
+ for data_type in ['text2image', 'image2image']:
299
+ if data_type in data_dict:
300
+ losses[f'loss_{data_type}'] = getattr(self, f'{data_type}_loss')(data_dict[data_type])
301
+ if len(losses) == 0:
302
+ if 'pixel_values_src' in data_dict:
303
+ losses[f'loss_image2image'] = self.image2image_loss(data_dict)
304
+ else:
305
+ losses[f'loss_text2image'] = self.text2image_loss(data_dict)
306
+
307
+ return losses
308
+
309
+
310
+ def prepare_forward_input(self,
311
+ query_embeds,
312
+ input_ids=None,
313
+ image_embeds=None,
314
+ image_grid_thw=None,
315
+ attention_mask=None,
316
+ past_key_values=None):
317
+ b, l, _ = query_embeds.shape
318
+ assert l > 0
319
+ attention_mask = attention_mask.to(device=self.device, dtype=torch.bool)
320
+
321
+ assert l == self.num_queries
322
+
323
+ input_ids = torch.cat([input_ids, input_ids.new_zeros(b, l)], dim=1)
324
+ attention_mask = torch.cat([attention_mask, attention_mask.new_ones(b, l)], dim=1)
325
+
326
+ position_ids, _ = self.lmm.model.get_rope_index(
327
+ input_ids=input_ids,
328
+ image_grid_thw=image_grid_thw,
329
+ video_grid_thw=None,
330
+ second_per_grid_ts=None,
331
+ attention_mask=attention_mask,
332
+ )
333
+
334
+ # prepare context
335
+ if past_key_values is not None:
336
+ inputs_embeds = query_embeds
337
+ position_ids = position_ids[..., -l:]
338
+ else:
339
+ input_ids = input_ids[:, :-l] # context input_ids
340
+
341
+ if image_embeds is None:
342
+ inputs_embeds = self.llm.get_input_embeddings()(input_ids)
343
+ else:
344
+ inputs_embeds = torch.zeros(*input_ids.shape, self.llm.config.hidden_size,
345
+ device=self.device, dtype=self.dtype)
346
+ inputs_embeds[input_ids == self.image_token_id] = \
347
+ image_embeds.contiguous().view(-1, self.llm.config.hidden_size)
348
+ inputs_embeds[input_ids != self.image_token_id] = self.llm.get_input_embeddings()(
349
+ input_ids[input_ids != self.image_token_id]
350
+ )
351
+
352
+ inputs_embeds = torch.cat([inputs_embeds, query_embeds], dim=1)
353
+
354
+ inputs = dict(inputs_embeds=inputs_embeds,
355
+ attention_mask=attention_mask,
356
+ position_ids=position_ids,
357
+ past_key_values=past_key_values)
358
+
359
+ return inputs
360
+
361
+
362
+ @torch.no_grad()
363
+ def get_semantic_features_dynamic(self, pixel_values):
364
+ # e.g., 512 -> 448
365
+ pixel_values = [F.interpolate(p[None], scale_factor=28 / 32, mode='bilinear') for p in pixel_values]
366
+ image_embeds, image_grid_thw = multi_apply(self.get_semantic_features,
367
+ pixel_values, resize=False)
368
+ image_embeds = [x[0] for x in image_embeds] # a list of embeds
369
+ image_grid_thw = torch.cat(image_grid_thw, dim=0) # b 3
370
+
371
+ return image_embeds, image_grid_thw
372
+
373
+ @torch.no_grad()
374
+ def get_semantic_features(self, pixel_values, resize=True):
375
+ # pixel_values: [-1, 1]
376
+ pixel_values = (pixel_values + 1.0) / 2 # [0, 1]
377
+ pixel_values = pixel_values - self.vit_mean.view(1, 3, 1, 1)
378
+ pixel_values = pixel_values / self.vit_std.view(1, 3, 1, 1)
379
+
380
+ if resize:
381
+ pixel_values = F.interpolate(pixel_values, size=(self.vit_input_size, self.vit_input_size),
382
+ mode='bilinear')
383
+ b, c, h, w = pixel_values.shape
384
+
385
+ patch_size = self.lmm.config.vision_config.patch_size
386
+ spatial_merge_size = self.lmm.config.vision_config.spatial_merge_size
387
+ temporal_patch_size = self.lmm.config.vision_config.temporal_patch_size
388
+
389
+ pixel_values = pixel_values[:, None].expand(b, temporal_patch_size, c, h, w)
390
+
391
+ grid_t = 1
392
+ grid_h, grid_w = h // patch_size, w // patch_size
393
+
394
+ pixel_values = pixel_values.view(
395
+ b,
396
+ grid_t,
397
+ temporal_patch_size,
398
+ c,
399
+ grid_h // spatial_merge_size,
400
+ spatial_merge_size,
401
+ patch_size,
402
+ grid_w // spatial_merge_size,
403
+ spatial_merge_size,
404
+ patch_size,
405
+ )
406
+
407
+ pixel_values = rearrange(
408
+ pixel_values, 'b t tp c h m p w n q -> (b t h w m n) (c tp p q)')
409
+
410
+ image_grid_thw = torch.tensor([(grid_t, grid_h, grid_w)] * b).to(self.device).long()
411
+
412
+ image_embeds = self.lmm.visual(pixel_values, grid_thw=image_grid_thw)
413
+ image_embeds = rearrange(image_embeds, '(b l) d -> b l d', b=b)
414
+
415
+ return image_embeds, image_grid_thw
416
+
417
+ @torch.no_grad()
418
+ def prepare_text2image_prompts(self, texts):
419
+ texts = [self.prompt_template['GENERATION'].format(input=text) for text in texts]
420
+ texts = [self.prompt_template['INSTRUCTION'].format(input=text) for text in texts]
421
+
422
+ return self.tokenizer(
423
+ texts, add_special_tokens=True, return_tensors='pt', padding=True, padding_side='left').to(self.device)
424
+
425
+ @torch.no_grad()
426
+ def prepare_image2image_prompts(self, texts, num_refs, ref_lens):
427
+ prompts = []
428
+ cnt = 0
429
+ for text, num_ref in zip(texts, num_refs):
430
+ image_tokens = ''
431
+ for _ in range(num_ref):
432
+ image_tokens += self.prompt_template['IMG_START_TOKEN'] + \
433
+ self.prompt_template['IMG_CONTEXT_TOKEN'] * ref_lens[cnt] + \
434
+ self.prompt_template['IMG_END_TOKEN']
435
+ cnt += 1
436
+
437
+ prompts.append(self.prompt_template['INSTRUCTION'].format(input=f'{image_tokens}\n{text}'))
438
+
439
+ return self.tokenizer(
440
+ prompts, add_special_tokens=True, return_tensors='pt', padding=True, padding_side='left').to(self.device)
441
+
442
+
443
+ def text2image_loss(self, data_dict):
444
+ # obtain image latents
445
+ if 'image_latents' in data_dict:
446
+ image_latents = data_dict['image_latents'] # .to(dtype=self.dtype, device=self.device)
447
+ image_latents = [x.to(dtype=self.dtype, device=self.device) for x in image_latents]
448
+ else:
449
+ pixel_values = [p.to(dtype=self.dtype, device=self.device) for p in data_dict['pixel_values']]
450
+ image_latents = [self.pixels_to_latents(p[None])[0] for p in pixel_values]
451
+
452
+ b = len(image_latents)
453
+
454
+ texts = ['' if random.uniform(0, 1) < self.unconditional else text
455
+ for text in data_dict['texts']]
456
+
457
+ text_inputs = self.prepare_text2image_prompts(texts)
458
+ hidden_states = self.meta_queries[None].expand(b, self.num_queries, -1)
459
+
460
+ inputs = self.prepare_forward_input(query_embeds=hidden_states, **text_inputs)
461
+
462
+ max_length = self.max_length + self.num_queries
463
+ inputs_embeds = inputs['inputs_embeds'][:, -max_length:]
464
+ attention_mask = inputs['attention_mask'][:, -max_length:]
465
+ position_ids = inputs['position_ids'][..., -max_length:]
466
+
467
+ output = self.llm(inputs_embeds=inputs_embeds,
468
+ attention_mask=attention_mask,
469
+ position_ids=position_ids,
470
+ return_dict=True)
471
+
472
+ # hidden_states = output.last_hidden_state[:, -self.num_queries:]#query only
473
+ hidden_states = output.last_hidden_state
474
+ pooled_out, seq_out = self.llm2dit(hidden_states)
475
+
476
+ loss_diff = self.diff_loss(model_input=image_latents,
477
+ pooled_prompt_embeds=pooled_out,
478
+ prompt_embeds=seq_out)
479
+
480
+ return loss_diff
481
+
482
+
483
+ def image2image_loss(self, data_dict):
484
+ pixel_values_src = data_dict['pixel_values_src']
485
+
486
+ num_refs = [len(ref_images) for ref_images in pixel_values_src]
487
+
488
+ pixel_values_src = [[img.to(dtype=self.dtype, device=self.device) for img in ref_images]
489
+ for ref_images in pixel_values_src]
490
+ image_latents_src = [[self.pixels_to_latents(img[None])[0] for img in ref_images]
491
+ for ref_images in pixel_values_src]
492
+ image_embeds, image_grid_thw = self.get_semantic_features_dynamic(
493
+ [img for ref_images in pixel_values_src for img in ref_images])
494
+
495
+ ref_lens = [len(x) for x in image_embeds]
496
+
497
+ pixel_values = [p.to(dtype=self.dtype, device=self.device) for p in data_dict['pixel_values']]
498
+ image_latents = [self.pixels_to_latents(p[None])[0] for p in pixel_values]
499
+
500
+ b = len(image_latents)
501
+ text_inputs = self.prepare_image2image_prompts(data_dict['texts'], num_refs=num_refs, ref_lens=ref_lens)
502
+
503
+ hidden_states = self.meta_queries[None].expand(b, self.num_queries, -1)
504
+
505
+ inputs = self.prepare_forward_input(query_embeds=hidden_states,
506
+ image_embeds=torch.cat(image_embeds),
507
+ image_grid_thw=image_grid_thw,
508
+ **text_inputs)
509
+
510
+
511
+ max_length = self.max_length + max(num_refs) * max(ref_lens) + self.num_queries
512
+ inputs_embeds = inputs['inputs_embeds'][:, -max_length:]
513
+ attention_mask = inputs['attention_mask'][:, -max_length:]
514
+ position_ids = inputs['position_ids'][..., -max_length:]
515
+
516
+ output = self.llm(inputs_embeds=inputs_embeds,
517
+ attention_mask=attention_mask,
518
+ position_ids=position_ids,
519
+ return_dict=True)
520
+
521
+ # hidden_states = output.last_hidden_state[:, -self.num_queries:] #query only
522
+ hidden_states = output.last_hidden_state
523
+
524
+ # if res_vit:
525
+ # image_embeds=torch.cat(image_embeds)
526
+ # hidden_states[input_ids == self.image_token_id] = 0.5 * (hidden_states[input_ids == self.image_token_id]) + 0.5 * (image_embeds.contiguous().view(-1, self.llm.config.hidden_size))
527
+
528
+
529
+ pooled_out, seq_out = self.llm2dit(hidden_states)
530
+
531
+
532
+ loss_diff = self.diff_loss(model_input=image_latents,
533
+ pooled_prompt_embeds=pooled_out,
534
+ prompt_embeds=seq_out,
535
+ cond_intput=image_latents_src)
536
+
537
+ return loss_diff
538
+
539
+
540
+
541
+ @torch.no_grad()
542
+ def generate(self,
543
+ prompt,
544
+ cfg_prompt,
545
+ pixel_values_src=None,
546
+ cfg_scale=4.5,
547
+ num_steps=50,
548
+ generator=None,
549
+ height=512,
550
+ width=512,
551
+ progress_bar=True):
552
+ assert len(prompt) == len(cfg_prompt)
553
+ b = len(prompt)
554
+
555
+ if pixel_values_src is not None:
556
+ num_refs = [len(ref_images) for ref_images in pixel_values_src]
557
+ pixel_values_src = [[img.to(dtype=self.dtype, device=self.device) for img in ref_imgs]
558
+ for ref_imgs in pixel_values_src]
559
+ image_embeds, image_grid_thw = self.get_semantic_features_dynamic(
560
+ [img for ref_images in pixel_values_src for img in ref_images])
561
+ ref_lens = [len(x) for x in image_embeds]
562
+
563
+ text_inputs = self.prepare_image2image_prompts(prompt + cfg_prompt, num_refs=num_refs*2, ref_lens=ref_lens*2)
564
+ text_inputs.update(image_embeds=torch.cat(image_embeds*2),
565
+ image_grid_thw=torch.cat([image_grid_thw]*2),)
566
+ cond_latents = [[self.pixels_to_latents(img[None])[0] for img in ref_imgs]
567
+ for ref_imgs in pixel_values_src]
568
+ cond_latents = cond_latents * 2
569
+ else:
570
+ text_inputs = self.prepare_text2image_prompts(prompt + cfg_prompt)
571
+ cond_latents = None
572
+
573
+ hidden_states = self.meta_queries[None].expand(2*b, self.num_queries, -1)
574
+ inputs = self.prepare_forward_input(query_embeds=hidden_states, **text_inputs)
575
+
576
+ output = self.llm(**inputs, return_dict=True)
577
+
578
+ # hidden_states = output.last_hidden_state[:, -self.num_queries:] #query only
579
+ hidden_states = output.last_hidden_state
580
+ pooled_out, seq_out = self.llm2dit(hidden_states)
581
+
582
+ pipeline = StableDiffusion3Pipeline(
583
+ transformer=self.transformer,
584
+ scheduler=self.test_scheduler,
585
+ vae=self.vae,
586
+ text_encoder=None,
587
+ tokenizer=None,
588
+ text_encoder_2=None,
589
+ tokenizer_2=None,
590
+ text_encoder_3=None,
591
+ tokenizer_3=None,
592
+ )
593
+
594
+ pipeline.set_progress_bar_config(disable=not progress_bar)
595
+
596
+ samples = pipeline(
597
+ height=height,
598
+ width=width,
599
+ guidance_scale=cfg_scale,
600
+ num_inference_steps=num_steps,
601
+ prompt_embeds=seq_out[:b],
602
+ pooled_prompt_embeds=pooled_out[:b],
603
+ negative_prompt_embeds=seq_out[b:],
604
+ negative_pooled_prompt_embeds=pooled_out[b:],
605
+ generator=generator,
606
+ output_type='latent',
607
+ cond_latents=cond_latents
608
+ ).images.to(self.dtype)
609
+
610
+ return self.latents_to_pixels(samples)
611
+
612
+ def diff_loss(self, model_input, pooled_prompt_embeds, prompt_embeds, cond_intput=None):
613
+ # Sample noise that we'll add to the latents
614
+ # import pdb; pdb.set_trace()
615
+ noise = [torch.randn_like(x) for x in model_input]
616
+ bsz = len(model_input)
617
+
618
+ u = compute_density_for_timestep_sampling(
619
+ weighting_scheme=self.weighting_scheme,
620
+ batch_size=bsz,
621
+ logit_mean=self.logit_mean,
622
+ logit_std=self.logit_std,
623
+ )
624
+
625
+ if self.train_scheduler.use_dynamic_shifting:
626
+ assert self.weighting_scheme == 'logit_normal'
627
+ # follow flux
628
+ # import pdb; pdb.set_trace()
629
+ image_seq_lens = [math.prod(x.shape[-2:]) // self.transformer.patch_size ** 2 for x in model_input]
630
+ mu = calculate_shift(
631
+ torch.tensor(image_seq_lens, dtype=self.dtype, device=self.device),
632
+ self.train_scheduler.config.get("base_image_seq_len", 256),
633
+ self.train_scheduler.config.get("max_image_seq_len", 4096),
634
+ self.train_scheduler.config.get("base_shift", 0.5),
635
+ self.train_scheduler.config.get("max_shift", 1.15)
636
+ )
637
+
638
+ if self.train_scheduler.config.time_shift_type == "exponential":
639
+ shift = torch.exp(mu)
640
+ elif self.train_scheduler.config.time_shift_type == "linear":
641
+ shift = mu
642
+ else:
643
+ raise NotImplementedError
644
+
645
+ sigmas = u.to(dtype=self.dtype, device=self.device)
646
+ sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)
647
+ timesteps = sigmas * self.train_scheduler.num_train_timesteps
648
+ sigmas = sigmas.view(-1, 1, 1, 1)
649
+
650
+ else:
651
+ # Sample a random timestep for each image
652
+ # for weighting schemes where we sample timesteps non-uniformly
653
+ indices = (u * self.train_scheduler.config.num_train_timesteps).long()
654
+ timesteps = self.train_scheduler.timesteps[indices].to(device=self.device)
655
+
656
+ # Add noise according to flow matching.
657
+ # zt = (1 - texp) * x + texp * z1
658
+ sigmas = self.get_sigmas(timesteps, n_dim=model_input[0].ndim + 1)
659
+
660
+ noisy_model_input = [(1.0 - x) * y + x * z for x, y, z in zip(sigmas, model_input, noise)]
661
+
662
+ # Predict the noise residual
663
+ model_pred = self.transformer(
664
+ hidden_states=noisy_model_input,
665
+ cond_hidden_states=cond_intput,
666
+ encoder_hidden_states=prompt_embeds,
667
+ pooled_projections=pooled_prompt_embeds,
668
+ timestep=timesteps,
669
+ return_dict=False,
670
+ )[0]
671
+
672
+
673
+ # these weighting schemes use a uniform timestep sampling
674
+ # and instead post-weight the loss
675
+ weighting = compute_loss_weighting_for_sd3(weighting_scheme=self.weighting_scheme, sigmas=sigmas)
676
+
677
+ # flow matching loss
678
+ # target = noise - model_input
679
+ target = [x - y for x, y in zip(noise, model_input)]
680
+
681
+ loss = [(x.float() * (y.float() - z.float()) ** 2).mean() for x, y, z in zip(weighting, model_pred, target)]
682
+ loss = sum(loss) / len(loss)
683
+
684
+ return loss
685
+
686
+ def get_sigmas(self, timesteps, n_dim=4):
687
+ sigmas = self.train_scheduler.sigmas.to(device=self.device, dtype=self.dtype)
688
+ schedule_timesteps = self.train_scheduler.timesteps.to(self.device)
689
+ timesteps = timesteps.to(self.device)
690
+ step_indices = [(schedule_timesteps == t).nonzero().item() for t in timesteps]
691
+
692
+ sigma = sigmas[step_indices].flatten()
693
+ while len(sigma.shape) < n_dim:
694
+ sigma = sigma.unsqueeze(-1)
695
+ return sigma
696
+
697
+
698
+ def resize_image(x, image_size, unit_image_size=32):
699
+ w, h = x.size
700
+ if w >= h and w >= image_size:
701
+ target_w = image_size
702
+ target_h = h * (target_w / w)
703
+ target_h = math.ceil(target_h / unit_image_size) * unit_image_size
704
+
705
+ elif h >= w and h >= image_size:
706
+ target_h = image_size
707
+ target_w = w * (target_h / h)
708
+ target_w = math.ceil(target_w / unit_image_size) * unit_image_size
709
+
710
+ else:
711
+ target_h = math.ceil(h / unit_image_size) * unit_image_size
712
+ target_w = math.ceil(w / unit_image_size) * unit_image_size
713
+
714
+ x = x.resize(size=(target_w, target_h))
715
+
716
+ return x
717
+
718
+
719
+ if __name__ == "__main__":
720
+ import os
721
+ import argparse
722
+ from glob import glob
723
+ from mmengine.config import Config
724
+ from PIL import Image
725
+ import numpy as np
726
+
727
+
728
+ parser = argparse.ArgumentParser()
729
+ parser.add_argument('config', help='log file path.')
730
+ parser.add_argument("--checkpoint", type=str, default=None)
731
+ parser.add_argument("--image", type=str, default=None)
732
+ parser.add_argument("--prompt", type=str, default='a dog on the left and a cat on the right')
733
+ parser.add_argument("--cfg_prompt", type=str, default='')
734
+ parser.add_argument("--cfg_scale", type=float, default=4.0)
735
+ parser.add_argument("--num_steps", type=int, default=50)
736
+ parser.add_argument("--height", type=int, default=512)
737
+ parser.add_argument("--width", type=int, default=512)
738
+ parser.add_argument("--seed", type=int, default=42)
739
+ parser.add_argument("--grid_size", type=int, default=2)
740
+ parser.add_argument('--output', type=str, default='output.jpg')
741
+
742
+ args = parser.parse_args()
743
+ config = Config.fromfile(args.config)
744
+ model = BUILDER.build(config.model).cuda().bfloat16().eval()
745
+
746
+ if args.checkpoint is not None:
747
+ print(f"Load checkpoint: {args.checkpoint}", flush=True)
748
+ checkpoint = guess_load_checkpoint(args.checkpoint)
749
+ info = model.load_state_dict(checkpoint, strict=False)
750
+
751
+ generator = torch.Generator(device=model.device).manual_seed(args.seed)
752
+ # repeat
753
+ bsz = args.grid_size ** 2
754
+
755
+ prompt = [args.prompt] * bsz
756
+ cfg_prompt = [args.cfg_prompt] * bsz
757
+
758
+ if args.image is not None:
759
+
760
+ if os.path.isdir(args.image):
761
+ ref_images = glob(f"{args.image}/*")
762
+ ref_images = [Image.open(path) for path in ref_images]
763
+ else:
764
+ ref_images = [Image.open(args.image)]
765
+
766
+ ref_images = [resize_image(img, max(args.width, args.height), 32) for img in ref_images]
767
+
768
+ if len(ref_images) == 1:
769
+ width, height = ref_images[0].size
770
+ else:
771
+ width, height = args.width, args.height
772
+
773
+ pixel_values_src = [torch.from_numpy(np.array(img)).to(dtype=model.dtype, device=model.device)
774
+ for img in ref_images]
775
+ pixel_values_src = [rearrange(img, 'h w c -> c h w') for img in pixel_values_src]
776
+ pixel_values_src = [2 * (img / 255) - 1 for img in pixel_values_src]
777
+
778
+ pixel_values_src = [pixel_values_src, ] * bsz
779
+ else:
780
+ width, height = args.width, args.height
781
+ pixel_values_src = None
782
+
783
+ samples = model.generate(prompt=prompt, cfg_prompt=cfg_prompt, pixel_values_src=pixel_values_src,
784
+ cfg_scale=args.cfg_scale, num_steps=args.num_steps,
785
+ generator=generator, height=height, width=width)
786
+
787
+
788
+ samples = rearrange(samples, '(m n) c h w -> (m h) (n w) c', m=args.grid_size, n=args.grid_size)
789
+ samples = torch.clamp(
790
+ 127.5 * samples + 128.0, 0, 255).to("cpu", dtype=torch.uint8).numpy()
791
+
792
+ Image.fromarray(samples).save(args.output)
src/models/sd3_kontext/qwen2_5_vl_sd3_hf_dynamic_fusion.py ADDED
@@ -0,0 +1,824 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import torch
3
+ import math
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+ from torch.nn.modules.module import T
7
+ import torch.distributed as dist
8
+ from mmengine.logging import print_log
9
+ from src.models.connector import ConnectorConfig, ConnectorEncoder
10
+ from xtuner.model.utils import guess_load_checkpoint
11
+ from xtuner.registry import BUILDER
12
+ from diffusers.training_utils import compute_density_for_timestep_sampling, compute_loss_weighting_for_sd3
13
+ from peft import LoraConfig
14
+ from src.models.sd3_kontext.pipeline_stable_diffusion_3_dynamic import StableDiffusion3Pipeline, calculate_shift
15
+ from mmengine.model import BaseModel
16
+ from functools import partial
17
+ from six.moves import map, zip
18
+ from copy import deepcopy
19
+ from einops import rearrange
20
+
21
+
22
+ IMAGE_MEAN = (0.48145466, 0.4578275, 0.40821073)
23
+ IMAGE_STD = (0.26862954, 0.26130258, 0.27577711)
24
+
25
+
26
+ def multi_apply(func, *args, **kwargs):
27
+ pfunc = partial(func, **kwargs) if kwargs else func
28
+ map_results = map(pfunc, *args)
29
+ return tuple(map(list, zip(*map_results)))
30
+
31
+
32
+ def find_target_linear_names(model, num_lora_modules=-1, lora_namespan_exclude=[], verbose=True):
33
+ linear_cls = torch.nn.modules.Linear
34
+ embedding_cls = torch.nn.modules.Embedding
35
+ lora_module_names = []
36
+
37
+ for name, module in model.named_modules():
38
+ if any(ex_keyword in name for ex_keyword in lora_namespan_exclude):
39
+ continue
40
+ if isinstance(module, (linear_cls, embedding_cls)):
41
+ lora_module_names.append(name)
42
+
43
+ if num_lora_modules > 0:
44
+ lora_module_names = lora_module_names[-num_lora_modules:]
45
+ if verbose:
46
+ print(f"Found {len(lora_module_names)} lora modules: {lora_module_names}")
47
+ return lora_module_names
48
+
49
+
50
+ class Qwen2p5VLStableDiffusion3HF(BaseModel):
51
+ def __init__(self,
52
+ transformer,
53
+ train_scheduler,
54
+ test_scheduler,
55
+ vae,
56
+ lmm,
57
+ tokenizer,
58
+ prompt_template,
59
+ connector,
60
+ num_queries=64,
61
+ vit_input_size=448,
62
+ max_length=1024,
63
+ freeze_lmm=True,
64
+ freeze_mq=False,
65
+ res_vit=False,
66
+ pretrained_pth=None,
67
+ use_activation_checkpointing=False,
68
+ lora_modules='auto',
69
+ lora_rank=64,
70
+ lora_alpha=128,
71
+ freeze_transformer=True,
72
+ unconditional=0.1,
73
+ ema_cfg=None,
74
+ weighting_scheme='none',
75
+ logit_mean=0.0,
76
+ logit_std=1.0,
77
+ ):
78
+ super().__init__()
79
+
80
+ self.lmm = BUILDER.build(lmm)
81
+ if freeze_lmm:
82
+ self.lmm.requires_grad_(False)
83
+ self.freeze_lmm = freeze_lmm
84
+
85
+ self.transformer = BUILDER.build(transformer)
86
+ if freeze_transformer:
87
+ self.transformer.requires_grad_(False)
88
+ self.freeze_transformer = freeze_transformer
89
+ self.res_vit = res_vit
90
+
91
+
92
+
93
+ self.weighting_scheme = weighting_scheme
94
+ self.logit_mean = logit_mean
95
+ self.logit_std = logit_std
96
+
97
+ self.vae = BUILDER.build(vae)
98
+ self.vae.requires_grad_(False)
99
+
100
+ self.use_activation_checkpointing = use_activation_checkpointing
101
+ self.tokenizer = BUILDER.build(tokenizer)
102
+
103
+ self.prompt_template = prompt_template
104
+ self.vit_input_size = vit_input_size
105
+ self.max_length = max_length
106
+ self.image_token_id = self.tokenizer.convert_tokens_to_ids(prompt_template['IMG_CONTEXT_TOKEN'])
107
+ self.register_buffer('vit_mean', torch.tensor(IMAGE_MEAN), persistent=False)
108
+ self.register_buffer('vit_std', torch.tensor(IMAGE_STD), persistent=False)
109
+
110
+ self.num_queries = num_queries
111
+ self.connector = ConnectorEncoder(ConnectorConfig(**connector))
112
+
113
+
114
+ self.projector_1 = nn.Linear(self.llm.config.hidden_size*6, self.connector.config.hidden_size)
115
+ self.projector_2 = nn.Linear(self.connector.config.hidden_size, self.transformer.config.pooled_projection_dim)
116
+ self.projector_3 = nn.Linear(self.connector.config.hidden_size, self.transformer.config.joint_attention_dim)
117
+
118
+ # zero out
119
+ nn.init.zeros_(self.projector_2.weight)
120
+ nn.init.zeros_(self.projector_3.weight)
121
+ nn.init.zeros_(self.projector_2.bias)
122
+ nn.init.zeros_(self.projector_3.bias)
123
+
124
+ self.meta_queries = nn.Parameter(
125
+ torch.zeros(num_queries, self.llm.config.hidden_size))
126
+ nn.init.normal_(self.meta_queries, std=1 / math.sqrt(self.llm.config.hidden_size))
127
+
128
+ if freeze_mq:
129
+ self.projector_1.requires_grad_(False)
130
+ self.projector_2.requires_grad_(False)
131
+ self.projector_3.requires_grad_(False)
132
+ self.connector.requires_grad_(False)
133
+ self.meta_queries.requires_grad_(False)
134
+ self.freeze_mq = freeze_mq
135
+
136
+ self.unconditional = unconditional
137
+
138
+ self.train_scheduler = BUILDER.build(train_scheduler)
139
+ self.test_scheduler = BUILDER.build(test_scheduler)
140
+
141
+ if use_activation_checkpointing:
142
+ self.gradient_checkpointing_enable()
143
+
144
+ if lora_modules is not None:
145
+ assert self.freeze_lmm
146
+ self.llm.config.tie_word_embeddings = False
147
+ if lora_modules == 'auto':
148
+ lora_modules = find_target_linear_names(self.lmm)
149
+ # now we will add new LoRA weights the transformer layers
150
+ transformer_lora_config = LoraConfig(
151
+ r=lora_rank,
152
+ lora_alpha=lora_alpha,
153
+ init_lora_weights="gaussian",
154
+ target_modules=lora_modules,
155
+ lora_dropout=0.05,
156
+ )
157
+ self.lmm.add_adapter(transformer_lora_config)
158
+
159
+ if pretrained_pth is not None:
160
+ pretrained_state_dict = guess_load_checkpoint(pretrained_pth)
161
+ info = self.load_state_dict(pretrained_state_dict, strict=False)
162
+ print_log(f'Load pretrained weight from {pretrained_pth}')
163
+
164
+
165
+ self.ema_cfg = ema_cfg
166
+ if ema_cfg is not None:
167
+ self.ema = nn.ModuleDict()
168
+ self.ema.steps = 0
169
+ if not self.freeze_transformer:
170
+ self.ema.update(dict(transformer=deepcopy(self.transformer)))
171
+
172
+ if not self.freeze_mq:
173
+ self.ema.update(dict(projector_1=deepcopy(self.projector_1),
174
+ projector_2=deepcopy(self.projector_2),
175
+ projector_3=deepcopy(self.projector_3),
176
+ connector=deepcopy(self.connector)
177
+ )
178
+ )
179
+ self.ema.register_buffer('meta_queries', deepcopy(self.meta_queries.data))
180
+
181
+ self.ema.requires_grad_(False) # parameters in ema are not learnable
182
+
183
+ if 'checkpoint' in ema_cfg:
184
+ ema_state_dict = guess_load_checkpoint(ema_cfg['checkpoint'])
185
+ info = self.ema.load_state_dict(ema_state_dict, strict=False)
186
+ print_log(f"Load ema weight from {ema_cfg['checkpoint']}")
187
+
188
+ @torch.no_grad()
189
+ def ema_step(self, ):
190
+ if self.ema_cfg is None:
191
+ return
192
+
193
+ steps = self.ema.steps
194
+ update_interval = self.ema_cfg.get('update_interval', 1)
195
+ save_interval = self.ema_cfg.get('save_interval', 1000)
196
+ momentum = self.ema_cfg.get('momentum', 0.99)
197
+
198
+ if steps % update_interval == 0 and steps > 0:
199
+ if not self.freeze_mq:
200
+ for ema_param, base_param in zip(self.ema.projector_1.parameters(), self.projector_1.parameters()):
201
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
202
+ for ema_param, base_param in zip(self.ema.projector_2.parameters(), self.projector_2.parameters()):
203
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
204
+ for ema_param, base_param in zip(self.ema.projector_3.parameters(), self.projector_3.parameters()):
205
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
206
+ for ema_param, base_param in zip(self.ema.connector.parameters(), self.connector.parameters()):
207
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
208
+ self.ema.meta_queries.data.lerp_(self.meta_queries.data.detach(), 1.0 - momentum)
209
+
210
+ if not self.freeze_transformer:
211
+ for ema_param, base_param in zip(self.ema.transformer.parameters(), self.transformer.parameters()):
212
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
213
+
214
+ # print(f"steps: {steps}, rank: {dist.get_rank()},", flush=True)
215
+
216
+ if steps % save_interval == 0 and steps > 0:
217
+ is_ddp = dist.is_available() and dist.is_initialized()
218
+ is_primary_proc = (not is_ddp) or dist.get_rank() == 0
219
+ print(f"steps: {steps}, rank: {dist.get_rank()}, is_ddp:{is_ddp}, is_primary_proc: {is_primary_proc}.", flush=True)
220
+ if is_primary_proc:
221
+ save_path = self.ema_cfg.get('save_path')
222
+ torch.save(self.ema.state_dict(), save_path)
223
+ if is_ddp:
224
+ dist.barrier()
225
+
226
+ self.ema.steps = self.ema.steps + 1
227
+
228
+ def llm2dit(self, x):
229
+ x = self.connector(self.projector_1(x))
230
+ pooled_out = self.projector_2(x.mean(1))
231
+ seq_out = self.projector_3(x)
232
+
233
+ return pooled_out, seq_out
234
+
235
+ @property
236
+ def llm(self):
237
+ return self.lmm.language_model
238
+
239
+ def gradient_checkpointing_enable(self):
240
+ self.activation_checkpointing_enable()
241
+
242
+ def activation_checkpointing_enable(self):
243
+ self.llm.gradient_checkpointing_enable()
244
+ self.transformer.enable_gradient_checkpointing()
245
+ self.connector.gradient_checkpointing = True
246
+
247
+ def gradient_checkpointing_disable(self):
248
+ self.activation_checkpointing_disable()
249
+
250
+ def activation_checkpointing_disable(self):
251
+ self.llm.gradient_checkpointing_disable()
252
+ self.transformer.disable_gradient_checkpointing()
253
+ self.connector.gradient_checkpointing = False
254
+
255
+ @property
256
+ def device(self):
257
+ return self.llm.device
258
+
259
+ @property
260
+ def dtype(self):
261
+ return self.llm.dtype
262
+
263
+ def train(self: T, mode: bool = True) -> T:
264
+ super().train(mode=mode)
265
+ if self.vae is not None:
266
+ self.vae.train(mode=False)
267
+ if not mode:
268
+ self.gradient_checkpointing_disable()
269
+
270
+ return self
271
+
272
+ def state_dict(self, *args, **kwargs) -> dict:
273
+ state_dict = super().state_dict(*args, **kwargs)
274
+ state_dict = {k: v for k, v in state_dict.items()
275
+ if 'vae.' not in k and 'lmm.' not in k and 'ema.' not in k}
276
+ return state_dict
277
+
278
+ @torch.no_grad()
279
+ def pixels_to_latents(self, x):
280
+ z = self.vae.encode(x).latent_dist.sample()
281
+ z = (z - self.vae.config.shift_factor) * self.vae.config.scaling_factor
282
+ return z
283
+
284
+ @torch.no_grad()
285
+ def latents_to_pixels(self, z):
286
+ z = (z / self.vae.config.scaling_factor) + self.vae.config.shift_factor
287
+ x_rec = self.vae.decode(z).sample
288
+ return x_rec
289
+
290
+ def forward(self, data, data_samples=None, mode='loss'):
291
+ if mode == 'loss':
292
+ self.ema_step()
293
+ return self.compute_loss(data_dict=data)
294
+ else:
295
+ raise NotImplementedError
296
+
297
+ def compute_loss(self, data_dict):
298
+ losses = {}
299
+ for data_type in ['text2image', 'image2image']:
300
+ if data_type in data_dict:
301
+ losses[f'loss_{data_type}'] = getattr(self, f'{data_type}_loss')(data_dict[data_type])
302
+ if len(losses) == 0:
303
+ if 'pixel_values_src' in data_dict:
304
+ losses[f'loss_image2image'] = self.image2image_loss(data_dict)
305
+ else:
306
+ losses[f'loss_text2image'] = self.text2image_loss(data_dict)
307
+
308
+ return losses
309
+
310
+
311
+ def prepare_forward_input(self,
312
+ query_embeds,
313
+ input_ids=None,
314
+ image_embeds=None,
315
+ image_grid_thw=None,
316
+ attention_mask=None,
317
+ past_key_values=None):
318
+ b, l, _ = query_embeds.shape
319
+ assert l > 0
320
+ attention_mask = attention_mask.to(device=self.device, dtype=torch.bool)
321
+
322
+ assert l == self.num_queries
323
+
324
+ input_ids = torch.cat([input_ids, input_ids.new_zeros(b, l)], dim=1)
325
+ attention_mask = torch.cat([attention_mask, attention_mask.new_ones(b, l)], dim=1)
326
+
327
+ position_ids, _ = self.lmm.model.get_rope_index(
328
+ input_ids=input_ids,
329
+ image_grid_thw=image_grid_thw,
330
+ video_grid_thw=None,
331
+ second_per_grid_ts=None,
332
+ attention_mask=attention_mask,
333
+ )
334
+
335
+ # prepare context
336
+ if past_key_values is not None:
337
+ inputs_embeds = query_embeds
338
+ position_ids = position_ids[..., -l:]
339
+ else:
340
+ input_ids = input_ids[:, :-l] # context input_ids
341
+
342
+ if image_embeds is None:
343
+ inputs_embeds = self.llm.get_input_embeddings()(input_ids)
344
+ else:
345
+ inputs_embeds = torch.zeros(*input_ids.shape, self.llm.config.hidden_size,
346
+ device=self.device, dtype=self.dtype)
347
+ inputs_embeds[input_ids == self.image_token_id] = \
348
+ image_embeds.contiguous().view(-1, self.llm.config.hidden_size)
349
+ inputs_embeds[input_ids != self.image_token_id] = self.llm.get_input_embeddings()(
350
+ input_ids[input_ids != self.image_token_id]
351
+ )
352
+
353
+ inputs_embeds = torch.cat([inputs_embeds, query_embeds], dim=1)
354
+
355
+ inputs = dict(inputs_embeds=inputs_embeds,
356
+ attention_mask=attention_mask,
357
+ position_ids=position_ids,
358
+ past_key_values=past_key_values)
359
+
360
+ return inputs
361
+
362
+
363
+ @torch.no_grad()
364
+ def get_semantic_features_dynamic(self, pixel_values):
365
+ # e.g., 512 -> 448
366
+ pixel_values = [F.interpolate(p[None], scale_factor=28 / 32, mode='bilinear') for p in pixel_values]
367
+ image_embeds, image_grid_thw = multi_apply(self.get_semantic_features,
368
+ pixel_values, resize=False)
369
+ image_embeds = [x[0] for x in image_embeds] # a list of embeds
370
+ image_grid_thw = torch.cat(image_grid_thw, dim=0) # b 3
371
+
372
+ return image_embeds, image_grid_thw
373
+
374
+ @torch.no_grad()
375
+ def get_semantic_features(self, pixel_values, resize=True):
376
+ # pixel_values: [-1, 1]
377
+ pixel_values = (pixel_values + 1.0) / 2 # [0, 1]
378
+ pixel_values = pixel_values - self.vit_mean.view(1, 3, 1, 1)
379
+ pixel_values = pixel_values / self.vit_std.view(1, 3, 1, 1)
380
+
381
+ if resize:
382
+ pixel_values = F.interpolate(pixel_values, size=(self.vit_input_size, self.vit_input_size),
383
+ mode='bilinear')
384
+ b, c, h, w = pixel_values.shape
385
+
386
+ patch_size = self.lmm.config.vision_config.patch_size
387
+ spatial_merge_size = self.lmm.config.vision_config.spatial_merge_size
388
+ temporal_patch_size = self.lmm.config.vision_config.temporal_patch_size
389
+
390
+ pixel_values = pixel_values[:, None].expand(b, temporal_patch_size, c, h, w)
391
+
392
+ grid_t = 1
393
+ grid_h, grid_w = h // patch_size, w // patch_size
394
+
395
+ pixel_values = pixel_values.view(
396
+ b,
397
+ grid_t,
398
+ temporal_patch_size,
399
+ c,
400
+ grid_h // spatial_merge_size,
401
+ spatial_merge_size,
402
+ patch_size,
403
+ grid_w // spatial_merge_size,
404
+ spatial_merge_size,
405
+ patch_size,
406
+ )
407
+
408
+ pixel_values = rearrange(
409
+ pixel_values, 'b t tp c h m p w n q -> (b t h w m n) (c tp p q)')
410
+
411
+ image_grid_thw = torch.tensor([(grid_t, grid_h, grid_w)] * b).to(self.device).long()
412
+
413
+ image_embeds = self.lmm.visual(pixel_values, grid_thw=image_grid_thw)
414
+ image_embeds = rearrange(image_embeds, '(b l) d -> b l d', b=b)
415
+
416
+ return image_embeds, image_grid_thw
417
+
418
+ @torch.no_grad()
419
+ def prepare_text2image_prompts(self, texts):
420
+ texts = [self.prompt_template['GENERATION'].format(input=text) for text in texts]
421
+ texts = [self.prompt_template['INSTRUCTION'].format(input=text) for text in texts]
422
+
423
+ return self.tokenizer(
424
+ texts, add_special_tokens=True, return_tensors='pt', padding=True, padding_side='left').to(self.device)
425
+
426
+ @torch.no_grad()
427
+ def prepare_image2image_prompts(self, texts, num_refs, ref_lens):
428
+ prompts = []
429
+ cnt = 0
430
+ for text, num_ref in zip(texts, num_refs):
431
+ image_tokens = ''
432
+ for _ in range(num_ref):
433
+ image_tokens += self.prompt_template['IMG_START_TOKEN'] + \
434
+ self.prompt_template['IMG_CONTEXT_TOKEN'] * ref_lens[cnt] + \
435
+ self.prompt_template['IMG_END_TOKEN']
436
+ cnt += 1
437
+
438
+ prompts.append(self.prompt_template['INSTRUCTION'].format(input=f'{image_tokens}\n{text}'))
439
+
440
+ return self.tokenizer(
441
+ prompts, add_special_tokens=True, return_tensors='pt', padding=True, padding_side='left').to(self.device)
442
+
443
+
444
+ def text2image_loss(self, data_dict):
445
+ # obtain image latents
446
+ if 'image_latents' in data_dict:
447
+ image_latents = data_dict['image_latents'] # .to(dtype=self.dtype, device=self.device)
448
+ image_latents = [x.to(dtype=self.dtype, device=self.device) for x in image_latents]
449
+ else:
450
+ pixel_values = [p.to(dtype=self.dtype, device=self.device) for p in data_dict['pixel_values']]
451
+ image_latents = [self.pixels_to_latents(p[None])[0] for p in pixel_values]
452
+
453
+ b = len(image_latents)
454
+
455
+ texts = ['' if random.uniform(0, 1) < self.unconditional else text
456
+ for text in data_dict['texts']]
457
+
458
+ text_inputs = self.prepare_text2image_prompts(texts)
459
+ hidden_states = self.meta_queries[None].expand(b, self.num_queries, -1)
460
+
461
+ inputs = self.prepare_forward_input(query_embeds=hidden_states, **text_inputs)
462
+
463
+ max_length = self.max_length + self.num_queries
464
+ inputs_embeds = inputs['inputs_embeds'][:, -max_length:]
465
+ attention_mask = inputs['attention_mask'][:, -max_length:]
466
+ position_ids = inputs['position_ids'][..., -max_length:]
467
+
468
+ output = self.llm(inputs_embeds=inputs_embeds,
469
+ attention_mask=attention_mask,
470
+ position_ids=position_ids,
471
+ output_hidden_states=True,
472
+ return_dict=True)
473
+
474
+ hidden_states = output.hidden_states
475
+ num_layers = len(hidden_states) - 1 # excpet embedding
476
+
477
+
478
+ selected_layers = list(range(num_layers - 1, 0, -6))
479
+ # [-2, -8, -14, ...]
480
+
481
+
482
+ selected_hiddens = [hidden_states[i] for i in selected_layers]
483
+ merged_hidden = torch.cat(selected_hiddens, dim=-1)
484
+ pooled_out, seq_out = self.llm2dit(merged_hidden)
485
+
486
+ loss_diff = self.diff_loss(model_input=image_latents,
487
+ pooled_prompt_embeds=pooled_out,
488
+ prompt_embeds=seq_out)
489
+
490
+ return loss_diff
491
+
492
+
493
+ def image2image_loss(self, data_dict):
494
+ pixel_values_src = data_dict['pixel_values_src']
495
+
496
+ num_refs = [len(ref_images) for ref_images in pixel_values_src]
497
+
498
+ pixel_values_src = [[img.to(dtype=self.dtype, device=self.device) for img in ref_images]
499
+ for ref_images in pixel_values_src]
500
+ image_latents_src = [[self.pixels_to_latents(img[None])[0] for img in ref_images]
501
+ for ref_images in pixel_values_src]
502
+ image_embeds, image_grid_thw = self.get_semantic_features_dynamic(
503
+ [img for ref_images in pixel_values_src for img in ref_images])
504
+
505
+ ref_lens = [len(x) for x in image_embeds]
506
+
507
+ pixel_values = [p.to(dtype=self.dtype, device=self.device) for p in data_dict['pixel_values']]
508
+ image_latents = [self.pixels_to_latents(p[None])[0] for p in pixel_values]
509
+
510
+ b = len(image_latents)
511
+ text_inputs = self.prepare_image2image_prompts(data_dict['texts'], num_refs=num_refs, ref_lens=ref_lens)
512
+
513
+ hidden_states = self.meta_queries[None].expand(b, self.num_queries, -1)
514
+
515
+ inputs = self.prepare_forward_input(query_embeds=hidden_states,
516
+ image_embeds=torch.cat(image_embeds),
517
+ image_grid_thw=image_grid_thw,
518
+ **text_inputs)
519
+
520
+
521
+ max_length = self.max_length + max(num_refs) * max(ref_lens) + self.num_queries
522
+ inputs_embeds = inputs['inputs_embeds'][:, -max_length:]
523
+ attention_mask = inputs['attention_mask'][:, -max_length:]
524
+ position_ids = inputs['position_ids'][..., -max_length:]
525
+
526
+ output = self.llm(inputs_embeds=inputs_embeds,
527
+ attention_mask=attention_mask,
528
+ position_ids=position_ids,
529
+ output_hidden_states = True,
530
+ return_dict=True)
531
+
532
+
533
+ hidden_states = output.hidden_states
534
+ num_layers = len(hidden_states) - 1 # except embedding
535
+
536
+
537
+ selected_layers = list(range(num_layers - 1, 0, -6))
538
+ # [-2, -8, -14, ...]
539
+
540
+
541
+
542
+ selected_hiddens = [hidden_states[i] for i in selected_layers]
543
+ merged_hidden = torch.cat(selected_hiddens, dim=-1)
544
+ pooled_out, seq_out = self.llm2dit(merged_hidden)
545
+
546
+ # if res_vit:
547
+ # image_embeds=torch.cat(image_embeds)
548
+ # hidden_states[input_ids == self.image_token_id] = 0.5 * (hidden_states[input_ids == self.image_token_id]) + 0.5 * (image_embeds.contiguous().view(-1, self.llm.config.hidden_size))
549
+
550
+
551
+
552
+
553
+ loss_diff = self.diff_loss(model_input=image_latents,
554
+ pooled_prompt_embeds=pooled_out,
555
+ prompt_embeds=seq_out,
556
+ cond_intput=image_latents_src)
557
+
558
+ return loss_diff
559
+
560
+
561
+
562
+ @torch.no_grad()
563
+ def generate(self,
564
+ prompt,
565
+ cfg_prompt,
566
+ pixel_values_src=None,
567
+ cfg_scale=4.5,
568
+ num_steps=50,
569
+ generator=None,
570
+ height=512,
571
+ width=512,
572
+ progress_bar=True):
573
+ assert len(prompt) == len(cfg_prompt)
574
+ b = len(prompt)
575
+
576
+ if pixel_values_src is not None:
577
+ num_refs = [len(ref_images) for ref_images in pixel_values_src]
578
+ pixel_values_src = [[img.to(dtype=self.dtype, device=self.device) for img in ref_imgs]
579
+ for ref_imgs in pixel_values_src]
580
+ image_embeds, image_grid_thw = self.get_semantic_features_dynamic(
581
+ [img for ref_images in pixel_values_src for img in ref_images])
582
+ ref_lens = [len(x) for x in image_embeds]
583
+
584
+ text_inputs = self.prepare_image2image_prompts(prompt + cfg_prompt, num_refs=num_refs*2, ref_lens=ref_lens*2)
585
+ text_inputs.update(image_embeds=torch.cat(image_embeds*2),
586
+ image_grid_thw=torch.cat([image_grid_thw]*2),)
587
+ cond_latents = [[self.pixels_to_latents(img[None])[0] for img in ref_imgs]
588
+ for ref_imgs in pixel_values_src]
589
+ cond_latents = cond_latents * 2
590
+ else:
591
+ text_inputs = self.prepare_text2image_prompts(prompt + cfg_prompt)
592
+ cond_latents = None
593
+
594
+ hidden_states = self.meta_queries[None].expand(2*b, self.num_queries, -1)
595
+ inputs = self.prepare_forward_input(query_embeds=hidden_states, **text_inputs)
596
+
597
+ output = self.llm(**inputs, return_dict=True,output_hidden_states = True)
598
+
599
+
600
+ hidden_states = output.hidden_states
601
+ num_layers = len(hidden_states) - 1
602
+
603
+
604
+ selected_layers = list(range(num_layers - 1, 0, -6))
605
+
606
+
607
+
608
+
609
+ selected_hiddens = [hidden_states[i] for i in selected_layers]
610
+ merged_hidden = torch.cat(selected_hiddens, dim=-1)
611
+ pooled_out, seq_out = self.llm2dit(merged_hidden)
612
+
613
+
614
+ pipeline = StableDiffusion3Pipeline(
615
+ transformer=self.transformer,
616
+ scheduler=self.test_scheduler,
617
+ vae=self.vae,
618
+ text_encoder=None,
619
+ tokenizer=None,
620
+ text_encoder_2=None,
621
+ tokenizer_2=None,
622
+ text_encoder_3=None,
623
+ tokenizer_3=None,
624
+ )
625
+
626
+ pipeline.set_progress_bar_config(disable=not progress_bar)
627
+
628
+ samples = pipeline(
629
+ height=height,
630
+ width=width,
631
+ guidance_scale=cfg_scale,
632
+ num_inference_steps=num_steps,
633
+ prompt_embeds=seq_out[:b],
634
+ pooled_prompt_embeds=pooled_out[:b],
635
+ negative_prompt_embeds=seq_out[b:],
636
+ negative_pooled_prompt_embeds=pooled_out[b:],
637
+ generator=generator,
638
+ output_type='latent',
639
+ cond_latents=cond_latents
640
+ ).images.to(self.dtype)
641
+
642
+ return self.latents_to_pixels(samples)
643
+
644
+ def diff_loss(self, model_input, pooled_prompt_embeds, prompt_embeds, cond_intput=None):
645
+ # Sample noise that we'll add to the latents
646
+ # import pdb; pdb.set_trace()
647
+ noise = [torch.randn_like(x) for x in model_input]
648
+ bsz = len(model_input)
649
+
650
+ u = compute_density_for_timestep_sampling(
651
+ weighting_scheme=self.weighting_scheme,
652
+ batch_size=bsz,
653
+ logit_mean=self.logit_mean,
654
+ logit_std=self.logit_std,
655
+ )
656
+
657
+ if self.train_scheduler.use_dynamic_shifting:
658
+ assert self.weighting_scheme == 'logit_normal'
659
+ # follow flux
660
+ # import pdb; pdb.set_trace()
661
+ image_seq_lens = [math.prod(x.shape[-2:]) // self.transformer.patch_size ** 2 for x in model_input]
662
+ mu = calculate_shift(
663
+ torch.tensor(image_seq_lens, dtype=self.dtype, device=self.device),
664
+ self.train_scheduler.config.get("base_image_seq_len", 256),
665
+ self.train_scheduler.config.get("max_image_seq_len", 4096),
666
+ self.train_scheduler.config.get("base_shift", 0.5),
667
+ self.train_scheduler.config.get("max_shift", 1.15)
668
+ )
669
+
670
+ if self.train_scheduler.config.time_shift_type == "exponential":
671
+ shift = torch.exp(mu)
672
+ elif self.train_scheduler.config.time_shift_type == "linear":
673
+ shift = mu
674
+ else:
675
+ raise NotImplementedError
676
+
677
+ sigmas = u.to(dtype=self.dtype, device=self.device)
678
+ sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)
679
+ timesteps = sigmas * self.train_scheduler.num_train_timesteps
680
+ sigmas = sigmas.view(-1, 1, 1, 1)
681
+
682
+ else:
683
+ # Sample a random timestep for each image
684
+ # for weighting schemes where we sample timesteps non-uniformly
685
+ indices = (u * self.train_scheduler.config.num_train_timesteps).long()
686
+ timesteps = self.train_scheduler.timesteps[indices].to(device=self.device)
687
+
688
+ # Add noise according to flow matching.
689
+ # zt = (1 - texp) * x + texp * z1
690
+ sigmas = self.get_sigmas(timesteps, n_dim=model_input[0].ndim + 1)
691
+
692
+ noisy_model_input = [(1.0 - x) * y + x * z for x, y, z in zip(sigmas, model_input, noise)]
693
+
694
+ # Predict the noise residual
695
+ model_pred = self.transformer(
696
+ hidden_states=noisy_model_input,
697
+ cond_hidden_states=cond_intput,
698
+ encoder_hidden_states=prompt_embeds,
699
+ pooled_projections=pooled_prompt_embeds,
700
+ timestep=timesteps,
701
+ return_dict=False,
702
+ )[0]
703
+
704
+
705
+ # these weighting schemes use a uniform timestep sampling
706
+ # and instead post-weight the loss
707
+ weighting = compute_loss_weighting_for_sd3(weighting_scheme=self.weighting_scheme, sigmas=sigmas)
708
+
709
+ # flow matching loss
710
+ # target = noise - model_input
711
+ target = [x - y for x, y in zip(noise, model_input)]
712
+
713
+ loss = [(x.float() * (y.float() - z.float()) ** 2).mean() for x, y, z in zip(weighting, model_pred, target)]
714
+ loss = sum(loss) / len(loss)
715
+
716
+ return loss
717
+
718
+ def get_sigmas(self, timesteps, n_dim=4):
719
+ sigmas = self.train_scheduler.sigmas.to(device=self.device, dtype=self.dtype)
720
+ schedule_timesteps = self.train_scheduler.timesteps.to(self.device)
721
+ timesteps = timesteps.to(self.device)
722
+ step_indices = [(schedule_timesteps == t).nonzero().item() for t in timesteps]
723
+
724
+ sigma = sigmas[step_indices].flatten()
725
+ while len(sigma.shape) < n_dim:
726
+ sigma = sigma.unsqueeze(-1)
727
+ return sigma
728
+
729
+
730
+ def resize_image(x, image_size, unit_image_size=32):
731
+ w, h = x.size
732
+ if w >= h and w >= image_size:
733
+ target_w = image_size
734
+ target_h = h * (target_w / w)
735
+ target_h = math.ceil(target_h / unit_image_size) * unit_image_size
736
+
737
+ elif h >= w and h >= image_size:
738
+ target_h = image_size
739
+ target_w = w * (target_h / h)
740
+ target_w = math.ceil(target_w / unit_image_size) * unit_image_size
741
+
742
+ else:
743
+ target_h = math.ceil(h / unit_image_size) * unit_image_size
744
+ target_w = math.ceil(w / unit_image_size) * unit_image_size
745
+
746
+ x = x.resize(size=(target_w, target_h))
747
+
748
+ return x
749
+
750
+
751
+ if __name__ == "__main__":
752
+ import os
753
+ import argparse
754
+ from glob import glob
755
+ from mmengine.config import Config
756
+ from PIL import Image
757
+ import numpy as np
758
+
759
+
760
+ parser = argparse.ArgumentParser()
761
+ parser.add_argument('config', help='log file path.')
762
+ parser.add_argument("--checkpoint", type=str, default=None)
763
+ parser.add_argument("--image", type=str, default=None)
764
+ parser.add_argument("--prompt", type=str, default='a dog on the left and a cat on the right')
765
+ parser.add_argument("--cfg_prompt", type=str, default='')
766
+ parser.add_argument("--cfg_scale", type=float, default=4.0)
767
+ parser.add_argument("--num_steps", type=int, default=50)
768
+ parser.add_argument("--height", type=int, default=512)
769
+ parser.add_argument("--width", type=int, default=512)
770
+ parser.add_argument("--seed", type=int, default=42)
771
+ parser.add_argument("--grid_size", type=int, default=2)
772
+ parser.add_argument('--output', type=str, default='output.jpg')
773
+
774
+ args = parser.parse_args()
775
+ config = Config.fromfile(args.config)
776
+ model = BUILDER.build(config.model).cuda().bfloat16().eval()
777
+
778
+ if args.checkpoint is not None:
779
+ print(f"Load checkpoint: {args.checkpoint}", flush=True)
780
+ checkpoint = guess_load_checkpoint(args.checkpoint)
781
+ info = model.load_state_dict(checkpoint, strict=False)
782
+
783
+ generator = torch.Generator(device=model.device).manual_seed(args.seed)
784
+ # repeat
785
+ bsz = args.grid_size ** 2
786
+
787
+ prompt = [args.prompt] * bsz
788
+ cfg_prompt = [args.cfg_prompt] * bsz
789
+
790
+ if args.image is not None:
791
+
792
+ if os.path.isdir(args.image):
793
+ ref_images = glob(f"{args.image}/*")
794
+ ref_images = [Image.open(path) for path in ref_images]
795
+ else:
796
+ ref_images = [Image.open(args.image)]
797
+
798
+ ref_images = [resize_image(img, max(args.width, args.height), 32) for img in ref_images]
799
+
800
+ if len(ref_images) == 1:
801
+ width, height = ref_images[0].size
802
+ else:
803
+ width, height = args.width, args.height
804
+
805
+ pixel_values_src = [torch.from_numpy(np.array(img)).to(dtype=model.dtype, device=model.device)
806
+ for img in ref_images]
807
+ pixel_values_src = [rearrange(img, 'h w c -> c h w') for img in pixel_values_src]
808
+ pixel_values_src = [2 * (img / 255) - 1 for img in pixel_values_src]
809
+
810
+ pixel_values_src = [pixel_values_src, ] * bsz
811
+ else:
812
+ width, height = args.width, args.height
813
+ pixel_values_src = None
814
+
815
+ samples = model.generate(prompt=prompt, cfg_prompt=cfg_prompt, pixel_values_src=pixel_values_src,
816
+ cfg_scale=args.cfg_scale, num_steps=args.num_steps,
817
+ generator=generator, height=height, width=width)
818
+
819
+
820
+ samples = rearrange(samples, '(m n) c h w -> (m h) (n w) c', m=args.grid_size, n=args.grid_size)
821
+ samples = torch.clamp(
822
+ 127.5 * samples + 128.0, 0, 255).to("cpu", dtype=torch.uint8).numpy()
823
+
824
+ Image.fromarray(samples).save(args.output)
src/models/sd3_kontext/sd3_hf.py ADDED
@@ -0,0 +1,486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.distributed as dist
5
+ from copy import deepcopy
6
+ from torch.nn.modules.module import T
7
+ from xtuner.registry import BUILDER
8
+ from mmengine.logging import print_log
9
+ from xtuner.model.utils import guess_load_checkpoint
10
+ from diffusers.training_utils import compute_density_for_timestep_sampling, compute_loss_weighting_for_sd3
11
+ from peft import LoraConfig
12
+ from src.models.sd3_kontext.pipeline_stable_diffusion_3 import StableDiffusion3Pipeline
13
+ from mmengine.model import BaseModel
14
+
15
+
16
+
17
+ class StableDiffusion3HF(BaseModel):
18
+ def __init__(self,
19
+ text_encoder,
20
+ text_encoder_2,
21
+ text_encoder_3,
22
+ transformer,
23
+ train_scheduler,
24
+ test_scheduler,
25
+ vae,
26
+ tokenizer,
27
+ tokenizer_2,
28
+ tokenizer_3,
29
+ pretrained_pth=None,
30
+ use_activation_checkpointing=True,
31
+ lora_modules=None, # ["to_k", "to_q", "to_v", "to_out.0"],
32
+ lora_rank=8,
33
+ lora_alpha=8,
34
+ freeze_transformer=True,
35
+ unconditional=0.1,
36
+ weighting_scheme='none',
37
+ logit_mean=0.0,
38
+ logit_std=1.0,
39
+ ema_cfg=None,
40
+ ):
41
+ super().__init__()
42
+ self.use_activation_checkpointing = use_activation_checkpointing
43
+ self.text_encoder = BUILDER.build(text_encoder)
44
+ self.text_encoder_2 = BUILDER.build(text_encoder_2)
45
+ self.text_encoder_3 = BUILDER.build(text_encoder_3)
46
+ self.text_encoder.requires_grad_(False)
47
+ self.text_encoder_2.requires_grad_(False)
48
+ self.text_encoder_3.requires_grad_(False)
49
+
50
+ self.tokenizer = BUILDER.build(tokenizer)
51
+ self.tokenizer_2 = BUILDER.build(tokenizer_2)
52
+ self.tokenizer_3 = BUILDER.build(tokenizer_3)
53
+
54
+ self.unconditional = unconditional
55
+
56
+ self.train_scheduler = BUILDER.build(train_scheduler)
57
+ self.test_scheduler = BUILDER.build(test_scheduler)
58
+
59
+ self.transformer = BUILDER.build(transformer)
60
+ if freeze_transformer:
61
+ self.transformer.requires_grad_(False)
62
+ self.freeze_transformer = freeze_transformer
63
+
64
+ self.vae = BUILDER.build(vae)
65
+ self.vae.requires_grad_(False)
66
+
67
+ self.weighting_scheme = weighting_scheme
68
+ self.logit_mean = logit_mean
69
+ self.logit_std = logit_std
70
+
71
+ if use_activation_checkpointing:
72
+ self.gradient_checkpointing_enable()
73
+
74
+ if lora_modules is not None:
75
+ assert self.freeze_transformer
76
+ # now we will add new LoRA weights the transformer layers
77
+ transformer_lora_config = LoraConfig(
78
+ r=lora_rank,
79
+ lora_alpha=lora_alpha,
80
+ init_lora_weights="gaussian",
81
+ target_modules=lora_modules,
82
+ )
83
+ self.transformer.add_adapter(transformer_lora_config)
84
+
85
+ if pretrained_pth is not None:
86
+ pretrained_state_dict = guess_load_checkpoint(pretrained_pth)
87
+ info = self.load_state_dict(pretrained_state_dict, strict=False)
88
+ print_log(f'Load pretrained weight from {pretrained_pth}')
89
+
90
+
91
+ self.ema_cfg = ema_cfg
92
+ if ema_cfg is not None:
93
+ self.ema = nn.ModuleDict()
94
+ self.ema.steps = 0
95
+ assert not self.freeze_transformer
96
+ self.ema.update(dict(transformer=deepcopy(self.transformer)))
97
+ self.ema.requires_grad_(False) # parameters in ema are not learnable
98
+
99
+ if 'checkpoint' in ema_cfg:
100
+ ema_state_dict = guess_load_checkpoint(ema_cfg['checkpoint'])
101
+ info = self.ema.load_state_dict(ema_state_dict, strict=False)
102
+ print_log(f"Load ema weight from {ema_cfg['checkpoint']}")
103
+
104
+ @torch.no_grad()
105
+ def ema_step(self, ):
106
+ if self.ema_cfg is None:
107
+ return
108
+
109
+ steps = self.ema.steps
110
+ update_interval = self.ema_cfg.get('update_interval', 1)
111
+ save_interval = self.ema_cfg.get('save_interval', 1)
112
+ momentum = self.ema_cfg.get('momentum', 0.99)
113
+
114
+ if steps % update_interval == 0 and steps > 0:
115
+ for ema_param, base_param in zip(self.ema.transformer.parameters(), self.transformer.parameters()):
116
+ ema_param.data.lerp_(base_param.data.detach(), 1.0 - momentum)
117
+
118
+ if steps % save_interval == 0 and steps > 0:
119
+ is_ddp = dist.is_available() and dist.is_initialized()
120
+ is_primary_proc = (not is_ddp) or dist.get_rank() == 0
121
+ print(f"steps: {steps}, rank: {dist.get_rank()}, is_ddp:{is_ddp}, is_primary_proc: {is_primary_proc}.", flush=True)
122
+ if is_primary_proc:
123
+ save_path = self.ema_cfg.get('save_path')
124
+ torch.save(self.ema.state_dict(), save_path)
125
+ if is_ddp:
126
+ dist.barrier()
127
+
128
+ self.ema.steps = self.ema.steps + 1
129
+
130
+
131
+ def gradient_checkpointing_enable(self):
132
+ self.activation_checkpointing_enable()
133
+
134
+ def activation_checkpointing_enable(self):
135
+ self.transformer.enable_gradient_checkpointing()
136
+
137
+ def gradient_checkpointing_disable(self):
138
+ self.activation_checkpointing_disable()
139
+
140
+ def activation_checkpointing_disable(self):
141
+ self.transformer.disable_gradient_checkpointing()
142
+
143
+ @property
144
+ def device(self):
145
+ return self.transformer.device
146
+
147
+ @property
148
+ def dtype(self):
149
+ return self.transformer.dtype
150
+
151
+ def train(self: T, mode: bool = True) -> T:
152
+ super().train(mode=mode)
153
+ self.vae.train(mode=False)
154
+ self.text_encoder.train(mode=False)
155
+ self.text_encoder_2.train(mode=False)
156
+ self.text_encoder_3.train(mode=False)
157
+ if not mode:
158
+ self.gradient_checkpointing_disable()
159
+
160
+ return self
161
+
162
+ def state_dict(self, *args, **kwargs) -> dict:
163
+ state_dict = super().state_dict(*args, **kwargs)
164
+ return {k: v for k, v in state_dict.items() if k.startswith('transformer.')}
165
+
166
+ @torch.no_grad()
167
+ def pixels_to_latents(self, x):
168
+ z = self.vae.encode(x).latent_dist.sample()
169
+ z = (z - self.vae.config.shift_factor) * self.vae.config.scaling_factor
170
+ return z
171
+
172
+ @torch.no_grad()
173
+ def latents_to_pixels(self, z):
174
+ z = (z / self.vae.config.scaling_factor) + self.vae.config.shift_factor
175
+ x_rec = self.vae.decode(z).sample
176
+ return x_rec
177
+
178
+ def forward(self, data, data_samples=None, mode='loss'):
179
+ if mode == 'loss':
180
+ self.ema_step()
181
+ return self.compute_loss(data_dict=data)
182
+ else:
183
+ raise NotImplementedError
184
+
185
+ def compute_loss(self, data_dict):
186
+ losses = {}
187
+ for data_type in ['text2image', 'image2image']:
188
+ if data_type in data_dict:
189
+ losses[f'loss_{data_type}'] = getattr(self, f'{data_type}_loss')(data_dict[data_type])
190
+ if len(losses) == 0:
191
+ if 'pixel_values_src' in data_dict:
192
+ losses[f'loss_image2image'] = self.image2image_loss(data_dict)
193
+ else:
194
+ losses[f'loss_text2image'] = self.text2image_loss(data_dict)
195
+
196
+ return losses
197
+
198
+ def text2image_loss(self, data_dict):
199
+
200
+ # obtain image latents
201
+ if 'image_latents' in data_dict:
202
+ image_latents = data_dict['image_latents'].to(dtype=self.dtype, device=self.device)
203
+ else:
204
+ pixel_values = data_dict['pixel_values'].to(dtype=self.dtype, device=self.device)
205
+ image_latents = self.pixels_to_latents(pixel_values)
206
+
207
+ texts = ['' if random.uniform(0, 1) < self.unconditional else text
208
+ for text in data_dict['texts']]
209
+
210
+ pipeline = StableDiffusion3Pipeline(
211
+ transformer=None,
212
+ scheduler=None,
213
+ vae=None,
214
+ text_encoder=self.text_encoder,
215
+ tokenizer=self.tokenizer,
216
+ text_encoder_2=self.text_encoder_2,
217
+ tokenizer_2=self.tokenizer_2,
218
+ text_encoder_3=self.text_encoder_3,
219
+ tokenizer_3=self.tokenizer_3,
220
+ )
221
+
222
+ with torch.no_grad():
223
+ (
224
+ prompt_embeds,
225
+ _,
226
+ pooled_prompt_embeds,
227
+ _,
228
+ ) = pipeline.encode_prompt(
229
+ prompt=texts,
230
+ prompt_2=None,
231
+ prompt_3=None,
232
+ negative_prompt=None,
233
+ negative_prompt_2=None,
234
+ negative_prompt_3=None,
235
+ do_classifier_free_guidance=False,
236
+ device=self.device,
237
+ clip_skip=None,
238
+ num_images_per_prompt=1,
239
+ max_sequence_length=256,
240
+ lora_scale=None,
241
+ )
242
+
243
+ loss_diff = self.diff_loss(model_input=image_latents,
244
+ pooled_prompt_embeds=pooled_prompt_embeds,
245
+ prompt_embeds=prompt_embeds)
246
+
247
+ return loss_diff
248
+
249
+
250
+ def image2image_loss(self, data_dict):
251
+
252
+ pixel_values_src = data_dict['pixel_values_src'].to(dtype=self.dtype, device=self.device)
253
+ image_latents_src = self.pixels_to_latents(pixel_values_src)
254
+
255
+ pixel_values = data_dict['pixel_values'].to(dtype=self.dtype, device=self.device)
256
+ image_latents = self.pixels_to_latents(pixel_values)
257
+
258
+ pipeline = StableDiffusion3Pipeline(
259
+ transformer=None,
260
+ scheduler=None,
261
+ vae=None,
262
+ text_encoder=self.text_encoder,
263
+ tokenizer=self.tokenizer,
264
+ text_encoder_2=self.text_encoder_2,
265
+ tokenizer_2=self.tokenizer_2,
266
+ text_encoder_3=self.text_encoder_3,
267
+ tokenizer_3=self.tokenizer_3,
268
+ )
269
+
270
+ with torch.no_grad():
271
+ (
272
+ prompt_embeds,
273
+ _,
274
+ pooled_prompt_embeds,
275
+ _,
276
+ ) = pipeline.encode_prompt(
277
+ prompt=data_dict['texts'],
278
+ prompt_2=None,
279
+ prompt_3=None,
280
+ negative_prompt=None,
281
+ negative_prompt_2=None,
282
+ negative_prompt_3=None,
283
+ do_classifier_free_guidance=False,
284
+ device=self.device,
285
+ clip_skip=None,
286
+ num_images_per_prompt=1,
287
+ max_sequence_length=256,
288
+ lora_scale=None,
289
+ )
290
+
291
+
292
+ loss_diff = self.diff_loss(model_input=image_latents,
293
+ pooled_prompt_embeds=pooled_prompt_embeds,
294
+ prompt_embeds=prompt_embeds,
295
+ cond_intput=image_latents_src)
296
+
297
+ return loss_diff
298
+
299
+
300
+ @torch.no_grad()
301
+ def generate(self,
302
+ prompt,
303
+ cfg_prompt,
304
+ pixel_values_src=None,
305
+ cfg_scale=4.5,
306
+ num_steps=50,
307
+ generator=None,
308
+ height=512,
309
+ width=512,
310
+ progress_bar=True):
311
+
312
+ pipeline = StableDiffusion3Pipeline(
313
+ transformer=self.transformer,
314
+ scheduler=self.test_scheduler,
315
+ vae=self.vae,
316
+ text_encoder=self.text_encoder,
317
+ tokenizer=self.tokenizer,
318
+ text_encoder_2=self.text_encoder_2,
319
+ tokenizer_2=self.tokenizer_2,
320
+ text_encoder_3=self.text_encoder_3,
321
+ tokenizer_3=self.tokenizer_3,
322
+ )
323
+ (
324
+ prompt_embeds,
325
+ negative_prompt_embeds,
326
+ pooled_prompt_embeds,
327
+ negative_pooled_prompt_embeds,
328
+ ) = pipeline.encode_prompt(
329
+ prompt=prompt,
330
+ prompt_2=None,
331
+ prompt_3=None,
332
+ negative_prompt=cfg_prompt,
333
+ negative_prompt_2=None,
334
+ negative_prompt_3=None,
335
+ do_classifier_free_guidance=True,
336
+ device=self.device,
337
+ clip_skip=None,
338
+ num_images_per_prompt=1,
339
+ max_sequence_length=256,
340
+ lora_scale=None,
341
+ )
342
+
343
+ pipeline.set_progress_bar_config(disable=not progress_bar)
344
+
345
+ if pixel_values_src is not None:
346
+ cond_latents = self.pixels_to_latents(pixel_values_src)
347
+ cond_latents = torch.cat([cond_latents] * 2)
348
+ else:
349
+ cond_latents = None
350
+
351
+ samples = pipeline(
352
+ height=height,
353
+ width=width,
354
+ guidance_scale=cfg_scale,
355
+ num_inference_steps=num_steps,
356
+ prompt_embeds=prompt_embeds,
357
+ pooled_prompt_embeds=pooled_prompt_embeds,
358
+ negative_prompt_embeds=negative_prompt_embeds,
359
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
360
+ generator=generator,
361
+ output_type='latent',
362
+ cond_latents=cond_latents,
363
+ ).images.to(self.dtype)
364
+
365
+ return self.latents_to_pixels(samples)
366
+
367
+ def diff_loss(self, model_input, pooled_prompt_embeds, prompt_embeds, cond_intput=None):
368
+ # Sample noise that we'll add to the latents
369
+ noise = torch.randn_like(model_input)
370
+ bsz = model_input.shape[0]
371
+
372
+ # Sample a random timestep for each image
373
+ # for weighting schemes where we sample timesteps non-uniformly
374
+ u = compute_density_for_timestep_sampling(
375
+ weighting_scheme=self.weighting_scheme,
376
+ batch_size=bsz,
377
+ logit_mean=self.logit_mean,
378
+ logit_std=self.logit_std,
379
+ )
380
+ indices = (u * self.train_scheduler.config.num_train_timesteps).long()
381
+ timesteps = self.train_scheduler.timesteps[indices].to(device=model_input.device)
382
+
383
+ # Add noise according to flow matching.
384
+ # zt = (1 - texp) * x + texp * z1
385
+ sigmas = self.get_sigmas(timesteps, n_dim=model_input.ndim)
386
+ noisy_model_input = (1.0 - sigmas) * model_input + sigmas * noise
387
+
388
+ # Predict the noise residual
389
+ model_pred = self.transformer(
390
+ hidden_states=noisy_model_input,
391
+ cond_hidden_states=cond_intput,
392
+ encoder_hidden_states=prompt_embeds,
393
+ pooled_projections=pooled_prompt_embeds,
394
+ timestep=timesteps,
395
+ return_dict=False,
396
+ )[0]
397
+
398
+ # these weighting schemes use a uniform timestep sampling
399
+ # and instead post-weight the loss
400
+ weighting = compute_loss_weighting_for_sd3(weighting_scheme=self.weighting_scheme, sigmas=sigmas)
401
+
402
+ # flow matching loss
403
+ target = noise - model_input
404
+
405
+ # Compute regular loss.
406
+ loss = torch.mean(
407
+ (weighting.float() * (model_pred.float() - target.float()) ** 2).reshape(target.shape[0], -1),
408
+ 1,
409
+ )
410
+ loss = loss.mean()
411
+
412
+ return loss
413
+
414
+ def get_sigmas(self, timesteps, n_dim=4):
415
+ sigmas = self.train_scheduler.sigmas.to(device=self.device, dtype=self.dtype)
416
+ schedule_timesteps = self.train_scheduler.timesteps.to(self.device)
417
+ timesteps = timesteps.to(self.device)
418
+ step_indices = [(schedule_timesteps == t).nonzero().item() for t in timesteps]
419
+
420
+ sigma = sigmas[step_indices].flatten()
421
+ while len(sigma.shape) < n_dim:
422
+ sigma = sigma.unsqueeze(-1)
423
+ return sigma
424
+
425
+
426
+ if __name__ == "__main__":
427
+ import argparse
428
+ from mmengine.config import Config
429
+ from einops import rearrange
430
+ from PIL import Image
431
+ import numpy as np
432
+ import torch.nn.functional as F
433
+
434
+ parser = argparse.ArgumentParser()
435
+ parser.add_argument('config', help='log file path.')
436
+ parser.add_argument("--checkpoint", type=str, default=None)
437
+ parser.add_argument("--image", type=str, default=None)
438
+ parser.add_argument("--prompt", type=str, default='a dog on the left and a cat on the right')
439
+ parser.add_argument("--cfg_prompt", type=str, default='')
440
+ parser.add_argument("--cfg_scale", type=float, default=3.5)
441
+ parser.add_argument("--num_steps", type=int, default=50)
442
+ parser.add_argument("--height", type=int, default=512)
443
+ parser.add_argument("--width", type=int, default=512)
444
+ parser.add_argument("--seed", type=int, default=42)
445
+ parser.add_argument("--grid_size", type=int, default=2)
446
+ parser.add_argument('--output', type=str, default='output.jpg')
447
+
448
+ args = parser.parse_args()
449
+ config = Config.fromfile(args.config)
450
+ model = BUILDER.build(config.model).cuda().bfloat16().eval()
451
+
452
+ if args.checkpoint is not None:
453
+ print(f"Load checkpoint: {args.checkpoint}", flush=True)
454
+ checkpoint = guess_load_checkpoint(args.checkpoint)
455
+ info = model.load_state_dict(checkpoint, strict=False)
456
+
457
+ generator = torch.Generator(device=model.device).manual_seed(args.seed)
458
+ # repeat
459
+ bsz = args.grid_size ** 2
460
+
461
+ prompt = [args.prompt] * bsz
462
+ cfg_prompt = [args.cfg_prompt] * bsz
463
+
464
+ if args.image is not None:
465
+ image = Image.open(args.image)
466
+ img_w, img_h = image.size
467
+ image = image.resize(size=(args.height, args.width))
468
+ pixel_values_src = torch.from_numpy(np.array(image)).to(dtype=model.dtype, device=model.device)
469
+ pixel_values_src = rearrange(pixel_values_src, 'h w c -> c h w')[None]
470
+ pixel_values_src = 2 * (pixel_values_src / 255) - 1
471
+ pixel_values_src = pixel_values_src.expand(bsz, -1, -1, -1)
472
+ else:
473
+ pixel_values_src = None
474
+
475
+ samples = model.generate(prompt=prompt, cfg_prompt=cfg_prompt, pixel_values_src=pixel_values_src,
476
+ cfg_scale=args.cfg_scale, num_steps=args.num_steps,
477
+ generator=generator, height=args.height, width=args.width)
478
+
479
+ if pixel_values_src is not None:
480
+ samples = F.interpolate(samples, size=(img_h, img_w), mode='bilinear')
481
+
482
+ samples = rearrange(samples, '(m n) c h w -> (m h) (n w) c', m=args.grid_size, n=args.grid_size)
483
+ samples = torch.clamp(
484
+ 127.5 * samples + 128.0, 0, 255).to("cpu", dtype=torch.uint8).numpy()
485
+
486
+ Image.fromarray(samples).save(args.output)
src/models/sd3_kontext/sd3_hf_dynamic.py ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import torch
3
+ import math
4
+ from src.models.sd3_kontext.pipeline_stable_diffusion_3_dynamic import StableDiffusion3Pipeline, calculate_shift
5
+ from src.models.sd3_kontext.sd3_hf import StableDiffusion3HF
6
+ from diffusers.training_utils import compute_density_for_timestep_sampling, compute_loss_weighting_for_sd3
7
+
8
+
9
+ class StableDiffusion3HFDynamic(StableDiffusion3HF):
10
+
11
+ def text2image_loss(self, data_dict):
12
+
13
+ pixel_values = [img.to(dtype=self.dtype, device=self.device) for img in data_dict['pixel_values']]
14
+ image_latents = [self.pixels_to_latents(img[None])[0] for img in pixel_values]
15
+
16
+ texts = ['' if random.uniform(0, 1) < self.unconditional else text
17
+ for text in data_dict['texts']]
18
+
19
+ pipeline = StableDiffusion3Pipeline(
20
+ transformer=None,
21
+ scheduler=None,
22
+ vae=None,
23
+ text_encoder=self.text_encoder,
24
+ tokenizer=self.tokenizer,
25
+ text_encoder_2=self.text_encoder_2,
26
+ tokenizer_2=self.tokenizer_2,
27
+ text_encoder_3=self.text_encoder_3,
28
+ tokenizer_3=self.tokenizer_3,
29
+ )
30
+
31
+ with torch.no_grad():
32
+ (
33
+ prompt_embeds,
34
+ _,
35
+ pooled_prompt_embeds,
36
+ _,
37
+ ) = pipeline.encode_prompt(
38
+ prompt=texts,
39
+ prompt_2=None,
40
+ prompt_3=None,
41
+ negative_prompt=None,
42
+ negative_prompt_2=None,
43
+ negative_prompt_3=None,
44
+ do_classifier_free_guidance=False,
45
+ device=self.device,
46
+ clip_skip=None,
47
+ num_images_per_prompt=1,
48
+ max_sequence_length=512,
49
+ lora_scale=None,
50
+ )
51
+
52
+ loss_diff = self.diff_loss(model_input=image_latents,
53
+ pooled_prompt_embeds=pooled_prompt_embeds,
54
+ prompt_embeds=prompt_embeds)
55
+
56
+ return loss_diff
57
+
58
+
59
+ def image2image_loss(self, data_dict):
60
+ pixel_values_src = [[img.to(dtype=self.dtype, device=self.device) for img in ref_imgs]
61
+ for ref_imgs in data_dict['pixel_values_src']]
62
+ image_latents_src = [[self.pixels_to_latents(img[None])[0] for img in ref_imgs]
63
+ for ref_imgs in pixel_values_src]
64
+
65
+ pixel_values = [img.to(dtype=self.dtype, device=self.device) for img in data_dict['pixel_values']]
66
+ image_latents = [self.pixels_to_latents(img[None])[0] for img in pixel_values]
67
+
68
+ pipeline = StableDiffusion3Pipeline(
69
+ transformer=None,
70
+ scheduler=None,
71
+ vae=None,
72
+ text_encoder=self.text_encoder,
73
+ tokenizer=self.tokenizer,
74
+ text_encoder_2=self.text_encoder_2,
75
+ tokenizer_2=self.tokenizer_2,
76
+ text_encoder_3=self.text_encoder_3,
77
+ tokenizer_3=self.tokenizer_3,
78
+ )
79
+
80
+ with torch.no_grad():
81
+ (
82
+ prompt_embeds,
83
+ _,
84
+ pooled_prompt_embeds,
85
+ _,
86
+ ) = pipeline.encode_prompt(
87
+ prompt=data_dict['texts'],
88
+ prompt_2=None,
89
+ prompt_3=None,
90
+ negative_prompt=None,
91
+ negative_prompt_2=None,
92
+ negative_prompt_3=None,
93
+ do_classifier_free_guidance=False,
94
+ device=self.device,
95
+ clip_skip=None,
96
+ num_images_per_prompt=1,
97
+ max_sequence_length=512,
98
+ lora_scale=None,
99
+ )
100
+
101
+
102
+ loss_diff = self.diff_loss(model_input=image_latents,
103
+ pooled_prompt_embeds=pooled_prompt_embeds,
104
+ prompt_embeds=prompt_embeds,
105
+ cond_intput=image_latents_src)
106
+
107
+ return loss_diff
108
+
109
+
110
+ def diff_loss(self, model_input, pooled_prompt_embeds, prompt_embeds, cond_intput=None):
111
+ # Sample noise that we'll add to the latents
112
+ # import pdb; pdb.set_trace()
113
+ noise = [torch.randn_like(x) for x in model_input]
114
+ bsz = len(model_input)
115
+
116
+ u = compute_density_for_timestep_sampling(
117
+ weighting_scheme=self.weighting_scheme,
118
+ batch_size=bsz,
119
+ logit_mean=self.logit_mean,
120
+ logit_std=self.logit_std,
121
+ )
122
+
123
+ if self.train_scheduler.use_dynamic_shifting:
124
+ assert self.weighting_scheme == 'logit_normal'
125
+ # follow flux
126
+ # import pdb; pdb.set_trace()
127
+ image_seq_lens = [math.prod(x.shape[-2:]) // self.transformer.patch_size ** 2 for x in model_input]
128
+ mu = calculate_shift(
129
+ torch.tensor(image_seq_lens, dtype=self.dtype, device=self.device),
130
+ self.train_scheduler.config.get("base_image_seq_len", 256),
131
+ self.train_scheduler.config.get("max_image_seq_len", 4096),
132
+ self.train_scheduler.config.get("base_shift", 0.5),
133
+ self.train_scheduler.config.get("max_shift", 1.15)
134
+ )
135
+
136
+ if self.train_scheduler.config.time_shift_type == "exponential":
137
+ shift = torch.exp(mu)
138
+ elif self.train_scheduler.config.time_shift_type == "linear":
139
+ shift = mu
140
+ else:
141
+ raise NotImplementedError
142
+
143
+ sigmas = u.to(dtype=self.dtype, device=self.device)
144
+ sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)
145
+ timesteps = sigmas * self.train_scheduler.num_train_timesteps
146
+ sigmas = sigmas.view(-1, 1, 1, 1)
147
+
148
+ else:
149
+ # Sample a random timestep for each image
150
+ # for weighting schemes where we sample timesteps non-uniformly
151
+ indices = (u * self.train_scheduler.config.num_train_timesteps).long()
152
+ timesteps = self.train_scheduler.timesteps[indices].to(device=self.device)
153
+
154
+ # Add noise according to flow matching.
155
+ # zt = (1 - texp) * x + texp * z1
156
+ sigmas = self.get_sigmas(timesteps, n_dim=model_input[0].ndim + 1)
157
+
158
+ noisy_model_input = [(1.0 - x) * y + x * z for x, y, z in zip(sigmas, model_input, noise)]
159
+
160
+ # Predict the noise residual
161
+ model_pred = self.transformer(
162
+ hidden_states=noisy_model_input,
163
+ cond_hidden_states=cond_intput,
164
+ encoder_hidden_states=prompt_embeds,
165
+ pooled_projections=pooled_prompt_embeds,
166
+ timestep=timesteps,
167
+ return_dict=False,
168
+ )[0]
169
+
170
+
171
+ # these weighting schemes use a uniform timestep sampling
172
+ # and instead post-weight the loss
173
+ weighting = compute_loss_weighting_for_sd3(weighting_scheme=self.weighting_scheme, sigmas=sigmas)
174
+
175
+ # flow matching loss
176
+ # target = noise - model_input
177
+ target = [x - y for x, y in zip(noise, model_input)]
178
+
179
+ loss = [(x.float() * (y.float() - z.float()) ** 2).mean() for x, y, z in zip(weighting, model_pred, target)]
180
+ loss = sum(loss) / len(loss)
181
+
182
+ return loss
183
+
184
+
185
+
186
+ @torch.no_grad()
187
+ def generate(self,
188
+ prompt,
189
+ cfg_prompt,
190
+ pixel_values_src=None,
191
+ cfg_scale=4.5,
192
+ num_steps=50,
193
+ generator=None,
194
+ height=512,
195
+ width=512,
196
+ progress_bar=True):
197
+
198
+ pipeline = StableDiffusion3Pipeline(
199
+ transformer=self.transformer,
200
+ scheduler=self.test_scheduler,
201
+ vae=self.vae,
202
+ text_encoder=self.text_encoder,
203
+ tokenizer=self.tokenizer,
204
+ text_encoder_2=self.text_encoder_2,
205
+ tokenizer_2=self.tokenizer_2,
206
+ text_encoder_3=self.text_encoder_3,
207
+ tokenizer_3=self.tokenizer_3,
208
+ )
209
+ (
210
+ prompt_embeds,
211
+ negative_prompt_embeds,
212
+ pooled_prompt_embeds,
213
+ negative_pooled_prompt_embeds,
214
+ ) = pipeline.encode_prompt(
215
+ prompt=prompt,
216
+ prompt_2=None,
217
+ prompt_3=None,
218
+ negative_prompt=cfg_prompt,
219
+ negative_prompt_2=None,
220
+ negative_prompt_3=None,
221
+ do_classifier_free_guidance=True,
222
+ device=self.device,
223
+ clip_skip=None,
224
+ num_images_per_prompt=1,
225
+ max_sequence_length=512,
226
+ lora_scale=None,
227
+ )
228
+
229
+ pipeline.set_progress_bar_config(disable=not progress_bar)
230
+
231
+ if pixel_values_src is not None:
232
+ pixel_values_src = [[img.to(dtype=self.dtype, device=self.device) for img in ref_imgs]
233
+ for ref_imgs in pixel_values_src]
234
+ cond_latents = [[self.pixels_to_latents(img[None])[0] for img in ref_imgs]
235
+ for ref_imgs in pixel_values_src]
236
+ cond_latents = cond_latents * 2
237
+ else:
238
+ cond_latents = None
239
+
240
+ samples = pipeline(
241
+ height=height,
242
+ width=width,
243
+ guidance_scale=cfg_scale,
244
+ num_inference_steps=num_steps,
245
+ prompt_embeds=prompt_embeds,
246
+ pooled_prompt_embeds=pooled_prompt_embeds,
247
+ negative_prompt_embeds=negative_prompt_embeds,
248
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
249
+ generator=generator,
250
+ output_type='latent',
251
+ cond_latents=cond_latents,
252
+ ).images.to(self.dtype)
253
+
254
+ return self.latents_to_pixels(samples)
255
+
256
+
257
+ def resize_image(x, image_size, unit_image_size=32):
258
+ w, h = x.size
259
+ if w >= h and w >= image_size:
260
+ target_w = image_size
261
+ target_h = h * (target_w / w)
262
+ target_h = math.ceil(target_h / unit_image_size) * unit_image_size
263
+
264
+ elif h >= w and h >= image_size:
265
+ target_h = image_size
266
+ target_w = w * (target_h / h)
267
+ target_w = math.ceil(target_w / unit_image_size) * unit_image_size
268
+
269
+ else:
270
+ target_h = math.ceil(h / unit_image_size) * unit_image_size
271
+ target_w = math.ceil(w / unit_image_size) * unit_image_size
272
+
273
+ x = x.resize(size=(target_w, target_h))
274
+
275
+ return x
276
+
277
+
278
+ if __name__ == "__main__":
279
+ import os
280
+ import argparse
281
+ from glob import glob
282
+ from mmengine.config import Config
283
+ from einops import rearrange
284
+ from PIL import Image
285
+ import numpy as np
286
+ from xtuner.model.utils import guess_load_checkpoint
287
+ from xtuner.registry import BUILDER
288
+
289
+ parser = argparse.ArgumentParser()
290
+ parser.add_argument('config', help='log file path.')
291
+ parser.add_argument("--checkpoint", type=str, default=None)
292
+ parser.add_argument("--image", type=str, default=None)
293
+ parser.add_argument("--prompt", type=str, default='a dog on the left and a cat on the right')
294
+ parser.add_argument("--cfg_prompt", type=str, default='')
295
+ parser.add_argument("--cfg_scale", type=float, default=3.5)
296
+ parser.add_argument("--num_steps", type=int, default=50)
297
+ parser.add_argument("--height", type=int, default=512)
298
+ parser.add_argument("--width", type=int, default=512)
299
+ parser.add_argument("--seed", type=int, default=42)
300
+ parser.add_argument("--grid_size", type=int, default=2)
301
+ parser.add_argument('--output', type=str, default='output.jpg')
302
+
303
+ args = parser.parse_args()
304
+ config = Config.fromfile(args.config)
305
+ model = BUILDER.build(config.model).cuda().bfloat16().eval()
306
+
307
+ if args.checkpoint is not None:
308
+ print(f"Load checkpoint: {args.checkpoint}", flush=True)
309
+ checkpoint = guess_load_checkpoint(args.checkpoint)
310
+ info = model.load_state_dict(checkpoint, strict=False)
311
+
312
+ generator = torch.Generator(device=model.device).manual_seed(args.seed)
313
+ # repeat
314
+ bsz = args.grid_size ** 2
315
+
316
+ prompt = [args.prompt] * bsz
317
+ cfg_prompt = [args.cfg_prompt] * bsz
318
+
319
+ if args.image is not None:
320
+
321
+ if os.path.isdir(args.image):
322
+ ref_images = glob(f"{args.image}/*")
323
+ ref_images = [Image.open(path) for path in ref_images]
324
+ else:
325
+ ref_images = [Image.open(args.image)]
326
+
327
+ ref_images = [resize_image(img, max(args.width, args.height), 32) for img in ref_images]
328
+
329
+ if len(ref_images) == 1:
330
+ width, height = ref_images[0].size
331
+ else:
332
+ width, height = args.width, args.height
333
+
334
+ pixel_values_src = [torch.from_numpy(np.array(img)).to(dtype=model.dtype, device=model.device)
335
+ for img in ref_images]
336
+ pixel_values_src = [rearrange(img, 'h w c -> c h w') for img in pixel_values_src]
337
+ pixel_values_src = [2 * (img / 255) - 1 for img in pixel_values_src]
338
+
339
+ pixel_values_src = [pixel_values_src, ] * bsz
340
+ else:
341
+ width, height = args.width, args.height
342
+ pixel_values_src = None
343
+
344
+ samples = model.generate(prompt=prompt, cfg_prompt=cfg_prompt, pixel_values_src=pixel_values_src,
345
+ cfg_scale=args.cfg_scale, num_steps=args.num_steps,
346
+ generator=generator, height=height, width=width)
347
+
348
+
349
+ samples = rearrange(samples, '(m n) c h w -> (m h) (n w) c', m=args.grid_size, n=args.grid_size)
350
+ samples = torch.clamp(
351
+ 127.5 * samples + 128.0, 0, 255).to("cpu", dtype=torch.uint8).numpy()
352
+
353
+ Image.fromarray(samples).save(args.output)
src/models/sd3_kontext/transformer_sd3_dynamic.py ADDED
@@ -0,0 +1,639 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 Stability AI, The HuggingFace Team and The InstantX Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from typing import Any, Dict, List, Optional, Tuple, Union
15
+ import math
16
+ import torch
17
+ import torch.nn as nn
18
+ import torch.nn.functional as F
19
+ from einops import rearrange
20
+ from torch.nn.utils.rnn import pad_sequence
21
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
22
+ from diffusers.loaders import FromOriginalModelMixin, PeftAdapterMixin, SD3Transformer2DLoadersMixin
23
+ from diffusers.models.attention import FeedForward, JointTransformerBlock, _chunked_feed_forward
24
+ from diffusers.models.attention_processor import (
25
+ Attention,
26
+ AttentionProcessor,
27
+ FusedJointAttnProcessor2_0,
28
+ JointAttnProcessor2_0,
29
+ )
30
+ from diffusers.models.modeling_utils import ModelMixin
31
+ from diffusers.models.normalization import AdaLayerNormContinuous, AdaLayerNormZero
32
+ from diffusers.utils import USE_PEFT_BACKEND, logging, scale_lora_layers, unscale_lora_layers
33
+ from diffusers.utils.torch_utils import maybe_allow_in_graph
34
+ from diffusers.models.embeddings import CombinedTimestepTextProjEmbeddings, PatchEmbed
35
+ from diffusers.models.modeling_outputs import Transformer2DModelOutput
36
+
37
+
38
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
39
+
40
+
41
+ class CustomJointAttnProcessor2_0:
42
+ """Attention processor used typically in processing the SD3-like self-attention projections."""
43
+
44
+ def __init__(self):
45
+ if not hasattr(F, "scaled_dot_product_attention"):
46
+ raise ImportError("JointAttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.")
47
+
48
+ def __call__(
49
+ self,
50
+ attn: Attention,
51
+ hidden_states: torch.FloatTensor,
52
+ encoder_hidden_states: torch.FloatTensor = None,
53
+ attention_mask: Optional[torch.FloatTensor] = None,
54
+ *args,
55
+ **kwargs,
56
+ ) -> torch.FloatTensor:
57
+ residual = hidden_states
58
+
59
+ batch_size = hidden_states.shape[0]
60
+
61
+ # `sample` projections.
62
+ query = attn.to_q(hidden_states)
63
+ key = attn.to_k(hidden_states)
64
+ value = attn.to_v(hidden_states)
65
+
66
+ inner_dim = key.shape[-1]
67
+ head_dim = inner_dim // attn.heads
68
+
69
+ query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
70
+ key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
71
+ value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2)
72
+
73
+ if attn.norm_q is not None:
74
+ query = attn.norm_q(query)
75
+ if attn.norm_k is not None:
76
+ key = attn.norm_k(key)
77
+
78
+ # `context` projections.
79
+ if encoder_hidden_states is not None:
80
+ ctx_len = encoder_hidden_states.shape[1]
81
+ encoder_hidden_states_query_proj = attn.add_q_proj(encoder_hidden_states)
82
+ encoder_hidden_states_key_proj = attn.add_k_proj(encoder_hidden_states)
83
+ encoder_hidden_states_value_proj = attn.add_v_proj(encoder_hidden_states)
84
+
85
+ encoder_hidden_states_query_proj = encoder_hidden_states_query_proj.view(
86
+ batch_size, -1, attn.heads, head_dim
87
+ ).transpose(1, 2)
88
+ encoder_hidden_states_key_proj = encoder_hidden_states_key_proj.view(
89
+ batch_size, -1, attn.heads, head_dim
90
+ ).transpose(1, 2)
91
+ encoder_hidden_states_value_proj = encoder_hidden_states_value_proj.view(
92
+ batch_size, -1, attn.heads, head_dim
93
+ ).transpose(1, 2)
94
+
95
+ if attn.norm_added_q is not None:
96
+ encoder_hidden_states_query_proj = attn.norm_added_q(encoder_hidden_states_query_proj)
97
+ if attn.norm_added_k is not None:
98
+ encoder_hidden_states_key_proj = attn.norm_added_k(encoder_hidden_states_key_proj)
99
+
100
+ query = torch.cat([query, encoder_hidden_states_query_proj], dim=2)
101
+ key = torch.cat([key, encoder_hidden_states_key_proj], dim=2)
102
+ value = torch.cat([value, encoder_hidden_states_value_proj], dim=2)
103
+
104
+ if attention_mask is not None:
105
+ # import pdb; pdb.set_trace()
106
+ encoder_attention_mask = torch.ones(
107
+ batch_size, ctx_len, dtype=torch.bool, device=hidden_states.device)
108
+ attention_mask = torch.cat([attention_mask, encoder_attention_mask], dim=1)
109
+
110
+
111
+ # import pdb; pdb.set_trace()
112
+ if attention_mask is not None:
113
+ attention_mask = attention_mask[:, None] * attention_mask[..., None] # bsz, seqlen, seqlen
114
+ indices = range(attention_mask.shape[1])
115
+ attention_mask[:, indices, indices] = True
116
+ attention_mask = attention_mask[:, None]
117
+
118
+ hidden_states = F.scaled_dot_product_attention(query, key, value, dropout_p=0.0, is_causal=False,
119
+ attn_mask=attention_mask)
120
+ hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim)
121
+ hidden_states = hidden_states.to(query.dtype)
122
+
123
+ if encoder_hidden_states is not None:
124
+ # import pdb; pdb.set_trace()
125
+ # Split the attention outputs.
126
+ hidden_states, encoder_hidden_states = (
127
+ hidden_states[:, : residual.shape[1]],
128
+ hidden_states[:, residual.shape[1] :],
129
+ )
130
+ if not attn.context_pre_only:
131
+ encoder_hidden_states = attn.to_add_out(encoder_hidden_states)
132
+
133
+ # linear proj
134
+ hidden_states = attn.to_out[0](hidden_states)
135
+ # dropout
136
+ hidden_states = attn.to_out[1](hidden_states)
137
+
138
+ if encoder_hidden_states is not None:
139
+ return hidden_states, encoder_hidden_states
140
+ else:
141
+ return hidden_states
142
+
143
+
144
+ class CustomJointTransformerBlock(JointTransformerBlock):
145
+ def __init__(self, *args, **kwargs):
146
+ super().__init__(*args, **kwargs)
147
+ self.attn.set_processor(CustomJointAttnProcessor2_0())
148
+ if self.attn2 is not None:
149
+ self.attn2.set_processor(CustomJointAttnProcessor2_0())
150
+
151
+ def forward(
152
+ self,
153
+ hidden_states: torch.FloatTensor,
154
+ encoder_hidden_states: torch.FloatTensor,
155
+ temb: torch.FloatTensor,
156
+ attention_mask: Optional[torch.BoolTensor] = None,
157
+ joint_attention_kwargs: Optional[Dict[str, Any]] = None,
158
+ ):
159
+ joint_attention_kwargs = joint_attention_kwargs or {}
160
+ if self.use_dual_attention:
161
+ norm_hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp, norm_hidden_states2, gate_msa2 = self.norm1(
162
+ hidden_states, emb=temb
163
+ )
164
+ else:
165
+ norm_hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.norm1(hidden_states, emb=temb)
166
+
167
+ if self.context_pre_only:
168
+ norm_encoder_hidden_states = self.norm1_context(encoder_hidden_states, temb)
169
+ else:
170
+ norm_encoder_hidden_states, c_gate_msa, c_shift_mlp, c_scale_mlp, c_gate_mlp = self.norm1_context(
171
+ encoder_hidden_states, emb=temb
172
+ )
173
+
174
+ # Attention.
175
+ attn_output, context_attn_output = self.attn(
176
+ hidden_states=norm_hidden_states,
177
+ attention_mask=attention_mask,
178
+ encoder_hidden_states=norm_encoder_hidden_states,
179
+ **joint_attention_kwargs,
180
+ )
181
+
182
+ # Process attention outputs for the `hidden_states`.
183
+ attn_output = gate_msa.unsqueeze(1) * attn_output
184
+ hidden_states = hidden_states + attn_output
185
+
186
+ if self.use_dual_attention:
187
+ attn_output2 = self.attn2(hidden_states=norm_hidden_states2, attention_mask=attention_mask,
188
+ **joint_attention_kwargs)
189
+ attn_output2 = gate_msa2.unsqueeze(1) * attn_output2
190
+ hidden_states = hidden_states + attn_output2
191
+
192
+ norm_hidden_states = self.norm2(hidden_states)
193
+ norm_hidden_states = norm_hidden_states * (1 + scale_mlp[:, None]) + shift_mlp[:, None]
194
+ if self._chunk_size is not None:
195
+ # "feed_forward_chunk_size" can be used to save memory
196
+ ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size)
197
+ else:
198
+ ff_output = self.ff(norm_hidden_states)
199
+ ff_output = gate_mlp.unsqueeze(1) * ff_output
200
+
201
+ hidden_states = hidden_states + ff_output
202
+
203
+ # Process attention outputs for the `encoder_hidden_states`.
204
+ if self.context_pre_only:
205
+ encoder_hidden_states = None
206
+ else:
207
+ context_attn_output = c_gate_msa.unsqueeze(1) * context_attn_output
208
+ encoder_hidden_states = encoder_hidden_states + context_attn_output
209
+
210
+ norm_encoder_hidden_states = self.norm2_context(encoder_hidden_states)
211
+ norm_encoder_hidden_states = norm_encoder_hidden_states * (1 + c_scale_mlp[:, None]) + c_shift_mlp[:, None]
212
+ if self._chunk_size is not None:
213
+ # "feed_forward_chunk_size" can be used to save memory
214
+ context_ff_output = _chunked_feed_forward(
215
+ self.ff_context, norm_encoder_hidden_states, self._chunk_dim, self._chunk_size
216
+ )
217
+ else:
218
+ context_ff_output = self.ff_context(norm_encoder_hidden_states)
219
+ encoder_hidden_states = encoder_hidden_states + c_gate_mlp.unsqueeze(1) * context_ff_output
220
+
221
+ return encoder_hidden_states, hidden_states
222
+
223
+
224
+ @maybe_allow_in_graph
225
+ class SD3SingleTransformerBlock(nn.Module):
226
+ def __init__(
227
+ self,
228
+ dim: int,
229
+ num_attention_heads: int,
230
+ attention_head_dim: int,
231
+ ):
232
+ super().__init__()
233
+
234
+ self.norm1 = AdaLayerNormZero(dim)
235
+ self.attn = Attention(
236
+ query_dim=dim,
237
+ dim_head=attention_head_dim,
238
+ heads=num_attention_heads,
239
+ out_dim=dim,
240
+ bias=True,
241
+ processor=JointAttnProcessor2_0(),
242
+ eps=1e-6,
243
+ )
244
+
245
+ self.norm2 = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
246
+ self.ff = FeedForward(dim=dim, dim_out=dim, activation_fn="gelu-approximate")
247
+
248
+ def forward(self, hidden_states: torch.Tensor, temb: torch.Tensor):
249
+ # 1. Attention
250
+ norm_hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.norm1(hidden_states, emb=temb)
251
+ attn_output = self.attn(hidden_states=norm_hidden_states, encoder_hidden_states=None)
252
+ attn_output = gate_msa.unsqueeze(1) * attn_output
253
+ hidden_states = hidden_states + attn_output
254
+
255
+ # 2. Feed Forward
256
+ norm_hidden_states = self.norm2(hidden_states)
257
+ norm_hidden_states = norm_hidden_states * (1 + scale_mlp.unsqueeze(1)) + shift_mlp.unsqueeze(1)
258
+ ff_output = self.ff(norm_hidden_states)
259
+ ff_output = gate_mlp.unsqueeze(1) * ff_output
260
+ hidden_states = hidden_states + ff_output
261
+
262
+ return hidden_states
263
+
264
+
265
+ class SD3Transformer2DModel(
266
+ ModelMixin, ConfigMixin, PeftAdapterMixin, FromOriginalModelMixin, SD3Transformer2DLoadersMixin
267
+ ):
268
+ """
269
+ The Transformer model introduced in [Stable Diffusion 3](https://huggingface.co/papers/2403.03206).
270
+
271
+ Parameters:
272
+ sample_size (`int`, defaults to `128`):
273
+ The width/height of the latents. This is fixed during training since it is used to learn a number of
274
+ position embeddings.
275
+ patch_size (`int`, defaults to `2`):
276
+ Patch size to turn the input data into small patches.
277
+ in_channels (`int`, defaults to `16`):
278
+ The number of latent channels in the input.
279
+ num_layers (`int`, defaults to `18`):
280
+ The number of layers of transformer blocks to use.
281
+ attention_head_dim (`int`, defaults to `64`):
282
+ The number of channels in each head.
283
+ num_attention_heads (`int`, defaults to `18`):
284
+ The number of heads to use for multi-head attention.
285
+ joint_attention_dim (`int`, defaults to `4096`):
286
+ The embedding dimension to use for joint text-image attention.
287
+ caption_projection_dim (`int`, defaults to `1152`):
288
+ The embedding dimension of caption embeddings.
289
+ pooled_projection_dim (`int`, defaults to `2048`):
290
+ The embedding dimension of pooled text projections.
291
+ out_channels (`int`, defaults to `16`):
292
+ The number of latent channels in the output.
293
+ pos_embed_max_size (`int`, defaults to `96`):
294
+ The maximum latent height/width of positional embeddings.
295
+ dual_attention_layers (`Tuple[int, ...]`, defaults to `()`):
296
+ The number of dual-stream transformer blocks to use.
297
+ qk_norm (`str`, *optional*, defaults to `None`):
298
+ The normalization to use for query and key in the attention layer. If `None`, no normalization is used.
299
+ """
300
+
301
+ _supports_gradient_checkpointing = True
302
+ _no_split_modules = ["JointTransformerBlock", "CustomJointTransformerBlock"]
303
+ _skip_layerwise_casting_patterns = ["pos_embed", "norm"]
304
+
305
+ @register_to_config
306
+ def __init__(
307
+ self,
308
+ sample_size: int = 128,
309
+ patch_size: int = 2,
310
+ in_channels: int = 16,
311
+ num_layers: int = 18,
312
+ attention_head_dim: int = 64,
313
+ num_attention_heads: int = 18,
314
+ joint_attention_dim: int = 4096,
315
+ caption_projection_dim: int = 1152,
316
+ pooled_projection_dim: int = 2048,
317
+ out_channels: int = 16,
318
+ pos_embed_max_size: int = 96,
319
+ dual_attention_layers: Tuple[
320
+ int, ...
321
+ ] = (), # () for sd3.0; (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) for sd3.5
322
+ qk_norm: Optional[str] = None,
323
+ ):
324
+ super().__init__()
325
+ self.out_channels = out_channels if out_channels is not None else in_channels
326
+ self.inner_dim = num_attention_heads * attention_head_dim
327
+
328
+ self.pos_embed = PatchEmbed(
329
+ height=sample_size,
330
+ width=sample_size,
331
+ patch_size=patch_size,
332
+ in_channels=in_channels,
333
+ embed_dim=self.inner_dim,
334
+ pos_embed_max_size=pos_embed_max_size, # hard-code for now.
335
+ )
336
+ self.time_text_embed = CombinedTimestepTextProjEmbeddings(
337
+ embedding_dim=self.inner_dim, pooled_projection_dim=pooled_projection_dim
338
+ )
339
+ self.context_embedder = nn.Linear(joint_attention_dim, caption_projection_dim)
340
+
341
+ self.transformer_blocks = nn.ModuleList(
342
+ [
343
+ CustomJointTransformerBlock(
344
+ dim=self.inner_dim,
345
+ num_attention_heads=num_attention_heads,
346
+ attention_head_dim=attention_head_dim,
347
+ context_pre_only=i == num_layers - 1,
348
+ qk_norm=qk_norm,
349
+ use_dual_attention=True if i in dual_attention_layers else False,
350
+ )
351
+ for i in range(num_layers)
352
+ ]
353
+ )
354
+
355
+ self.norm_out = AdaLayerNormContinuous(self.inner_dim, self.inner_dim, elementwise_affine=False, eps=1e-6)
356
+ self.proj_out = nn.Linear(self.inner_dim, patch_size * patch_size * self.out_channels, bias=True)
357
+
358
+ self.gradient_checkpointing = False
359
+
360
+ # Copied from diffusers.models.unets.unet_3d_condition.UNet3DConditionModel.enable_forward_chunking
361
+ def enable_forward_chunking(self, chunk_size: Optional[int] = None, dim: int = 0) -> None:
362
+ """
363
+ Sets the attention processor to use [feed forward
364
+ chunking](https://huggingface.co/blog/reformer#2-chunked-feed-forward-layers).
365
+
366
+ Parameters:
367
+ chunk_size (`int`, *optional*):
368
+ The chunk size of the feed-forward layers. If not specified, will run feed-forward layer individually
369
+ over each tensor of dim=`dim`.
370
+ dim (`int`, *optional*, defaults to `0`):
371
+ The dimension over which the feed-forward computation should be chunked. Choose between dim=0 (batch)
372
+ or dim=1 (sequence length).
373
+ """
374
+ if dim not in [0, 1]:
375
+ raise ValueError(f"Make sure to set `dim` to either 0 or 1, not {dim}")
376
+
377
+ # By default chunk size is 1
378
+ chunk_size = chunk_size or 1
379
+
380
+ def fn_recursive_feed_forward(module: torch.nn.Module, chunk_size: int, dim: int):
381
+ if hasattr(module, "set_chunk_feed_forward"):
382
+ module.set_chunk_feed_forward(chunk_size=chunk_size, dim=dim)
383
+
384
+ for child in module.children():
385
+ fn_recursive_feed_forward(child, chunk_size, dim)
386
+
387
+ for module in self.children():
388
+ fn_recursive_feed_forward(module, chunk_size, dim)
389
+
390
+ # Copied from diffusers.models.unets.unet_3d_condition.UNet3DConditionModel.disable_forward_chunking
391
+ def disable_forward_chunking(self):
392
+ def fn_recursive_feed_forward(module: torch.nn.Module, chunk_size: int, dim: int):
393
+ if hasattr(module, "set_chunk_feed_forward"):
394
+ module.set_chunk_feed_forward(chunk_size=chunk_size, dim=dim)
395
+
396
+ for child in module.children():
397
+ fn_recursive_feed_forward(child, chunk_size, dim)
398
+
399
+ for module in self.children():
400
+ fn_recursive_feed_forward(module, None, 0)
401
+
402
+ @property
403
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.attn_processors
404
+ def attn_processors(self) -> Dict[str, AttentionProcessor]:
405
+ r"""
406
+ Returns:
407
+ `dict` of attention processors: A dictionary containing all attention processors used in the model with
408
+ indexed by its weight name.
409
+ """
410
+ # set recursively
411
+ processors = {}
412
+
413
+ def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
414
+ if hasattr(module, "get_processor"):
415
+ processors[f"{name}.processor"] = module.get_processor()
416
+
417
+ for sub_name, child in module.named_children():
418
+ fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
419
+
420
+ return processors
421
+
422
+ for name, module in self.named_children():
423
+ fn_recursive_add_processors(name, module, processors)
424
+
425
+ return processors
426
+
427
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.set_attn_processor
428
+ def set_attn_processor(self, processor: Union[AttentionProcessor, Dict[str, AttentionProcessor]]):
429
+ r"""
430
+ Sets the attention processor to use to compute attention.
431
+
432
+ Parameters:
433
+ processor (`dict` of `AttentionProcessor` or only `AttentionProcessor`):
434
+ The instantiated processor class or a dictionary of processor classes that will be set as the processor
435
+ for **all** `Attention` layers.
436
+
437
+ If `processor` is a dict, the key needs to define the path to the corresponding cross attention
438
+ processor. This is strongly recommended when setting trainable attention processors.
439
+
440
+ """
441
+ count = len(self.attn_processors.keys())
442
+
443
+ if isinstance(processor, dict) and len(processor) != count:
444
+ raise ValueError(
445
+ f"A dict of processors was passed, but the number of processors {len(processor)} does not match the"
446
+ f" number of attention layers: {count}. Please make sure to pass {count} processor classes."
447
+ )
448
+
449
+ def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor):
450
+ if hasattr(module, "set_processor"):
451
+ if not isinstance(processor, dict):
452
+ module.set_processor(processor)
453
+ else:
454
+ module.set_processor(processor.pop(f"{name}.processor"))
455
+
456
+ for sub_name, child in module.named_children():
457
+ fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor)
458
+
459
+ for name, module in self.named_children():
460
+ fn_recursive_attn_processor(name, module, processor)
461
+
462
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.fuse_qkv_projections with FusedAttnProcessor2_0->FusedJointAttnProcessor2_0
463
+ def fuse_qkv_projections(self):
464
+ """
465
+ Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query, key, value)
466
+ are fused. For cross-attention modules, key and value projection matrices are fused.
467
+
468
+ <Tip warning={true}>
469
+
470
+ This API is 🧪 experimental.
471
+
472
+ </Tip>
473
+ """
474
+ self.original_attn_processors = None
475
+
476
+ for _, attn_processor in self.attn_processors.items():
477
+ if "Added" in str(attn_processor.__class__.__name__):
478
+ raise ValueError("`fuse_qkv_projections()` is not supported for models having added KV projections.")
479
+
480
+ self.original_attn_processors = self.attn_processors
481
+
482
+ for module in self.modules():
483
+ if isinstance(module, Attention):
484
+ module.fuse_projections(fuse=True)
485
+
486
+ self.set_attn_processor(FusedJointAttnProcessor2_0())
487
+
488
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.unfuse_qkv_projections
489
+ def unfuse_qkv_projections(self):
490
+ """Disables the fused QKV projection if enabled.
491
+
492
+ <Tip warning={true}>
493
+
494
+ This API is 🧪 experimental.
495
+
496
+ </Tip>
497
+
498
+ """
499
+ if self.original_attn_processors is not None:
500
+ self.set_attn_processor(self.original_attn_processors)
501
+
502
+ def forward(
503
+ self,
504
+ hidden_states: torch.Tensor,
505
+ encoder_hidden_states: torch.Tensor = None,
506
+ cond_hidden_states: torch.Tensor = None,
507
+ pooled_projections: torch.Tensor = None,
508
+ timestep: torch.LongTensor = None,
509
+ block_controlnet_hidden_states: List = None,
510
+ joint_attention_kwargs: Optional[Dict[str, Any]] = None,
511
+ return_dict: bool = True,
512
+ skip_layers: Optional[List[int]] = None,
513
+ ) -> Union[torch.Tensor, Transformer2DModelOutput]:
514
+ """
515
+ The [`SD3Transformer2DModel`] forward method.
516
+
517
+ Args:
518
+ hidden_states (`torch.Tensor` of shape `(batch size, channel, height, width)`):
519
+ Input `hidden_states`.
520
+ encoder_hidden_states (`torch.Tensor` of shape `(batch size, sequence_len, embed_dims)`):
521
+ Conditional embeddings (embeddings computed from the input conditions such as prompts) to use.
522
+ pooled_projections (`torch.Tensor` of shape `(batch_size, projection_dim)`):
523
+ Embeddings projected from the embeddings of input conditions.
524
+ timestep (`torch.LongTensor`):
525
+ Used to indicate denoising step.
526
+ block_controlnet_hidden_states (`list` of `torch.Tensor`):
527
+ A list of tensors that if specified are added to the residuals of transformer blocks.
528
+ joint_attention_kwargs (`dict`, *optional*):
529
+ A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
530
+ `self.processor` in
531
+ [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
532
+ return_dict (`bool`, *optional*, defaults to `True`):
533
+ Whether or not to return a [`~models.transformer_2d.Transformer2DModelOutput`] instead of a plain
534
+ tuple.
535
+ skip_layers (`list` of `int`, *optional*):
536
+ A list of layer indices to skip during the forward pass.
537
+
538
+ Returns:
539
+ If `return_dict` is True, an [`~models.transformer_2d.Transformer2DModelOutput`] is returned, otherwise a
540
+ `tuple` where the first element is the sample tensor.
541
+ """
542
+ if joint_attention_kwargs is not None:
543
+ joint_attention_kwargs = joint_attention_kwargs.copy()
544
+ lora_scale = joint_attention_kwargs.pop("scale", 1.0)
545
+ else:
546
+ lora_scale = 1.0
547
+
548
+ if USE_PEFT_BACKEND:
549
+ # weight the lora layers by setting `lora_scale` for each PEFT layer
550
+ scale_lora_layers(self, lora_scale)
551
+ else:
552
+ if joint_attention_kwargs is not None and joint_attention_kwargs.get("scale", None) is not None:
553
+ logger.warning(
554
+ "Passing `scale` via `joint_attention_kwargs` when not using the PEFT backend is ineffective."
555
+ )
556
+
557
+ latent_sizes = [hs.shape[-2:] for hs in hidden_states]
558
+ bsz = len(hidden_states)
559
+
560
+ hidden_states_list = []
561
+ for idx in range(bsz):
562
+ hidden_states_per_sample = self.pos_embed(hidden_states[idx][None])[0]
563
+ if cond_hidden_states is not None:
564
+ for ref in cond_hidden_states[idx]:
565
+ hidden_states_per_sample = torch.cat(
566
+ [hidden_states_per_sample, self.pos_embed(ref[None])[0]])
567
+
568
+ hidden_states_list.append(hidden_states_per_sample)
569
+
570
+ max_len = max([len(hs) for hs in hidden_states_list])
571
+ attention_mask = torch.zeros(bsz, max_len, dtype=torch.bool, device=self.device)
572
+ for i, hs in enumerate(hidden_states_list):
573
+ attention_mask[i, :len(hs)] = True # right padding
574
+ # import pdb; pdb.set_trace()
575
+ hidden_states = pad_sequence(hidden_states_list, batch_first=True, padding_value=0.0, padding_side='right')
576
+
577
+ temb = self.time_text_embed(timestep, pooled_projections)
578
+ encoder_hidden_states = self.context_embedder(encoder_hidden_states)
579
+
580
+ if joint_attention_kwargs is not None and "ip_adapter_image_embeds" in joint_attention_kwargs:
581
+ ip_adapter_image_embeds = joint_attention_kwargs.pop("ip_adapter_image_embeds")
582
+ ip_hidden_states, ip_temb = self.image_proj(ip_adapter_image_embeds, timestep)
583
+
584
+ joint_attention_kwargs.update(ip_hidden_states=ip_hidden_states, temb=ip_temb)
585
+
586
+ for index_block, block in enumerate(self.transformer_blocks):
587
+ # Skip specified layers
588
+ is_skip = True if skip_layers is not None and index_block in skip_layers else False
589
+
590
+ if torch.is_grad_enabled() and self.gradient_checkpointing and not is_skip:
591
+ encoder_hidden_states, hidden_states = self._gradient_checkpointing_func(
592
+ block,
593
+ hidden_states,
594
+ encoder_hidden_states,
595
+ temb,
596
+ attention_mask,
597
+ joint_attention_kwargs,
598
+ )
599
+ elif not is_skip:
600
+ encoder_hidden_states, hidden_states = block(
601
+ hidden_states=hidden_states,
602
+ encoder_hidden_states=encoder_hidden_states,
603
+ temb=temb,
604
+ attention_mask=attention_mask,
605
+ joint_attention_kwargs=joint_attention_kwargs,
606
+ )
607
+
608
+ # controlnet residual
609
+ if block_controlnet_hidden_states is not None and block.context_pre_only is False:
610
+ interval_control = len(self.transformer_blocks) / len(block_controlnet_hidden_states)
611
+ hidden_states = hidden_states + block_controlnet_hidden_states[int(index_block / interval_control)]
612
+
613
+ hidden_states = self.norm_out(hidden_states, temb)
614
+ hidden_states = self.proj_out(hidden_states)
615
+
616
+ patch_size = self.config.patch_size
617
+ latent_sizes = [(latent_size[0] // patch_size, latent_size[1] // patch_size)
618
+ for latent_size in latent_sizes]
619
+
620
+ # import pdb; pdb.set_trace()
621
+ # unpatchify
622
+ output = [rearrange(hs[:math.prod(latent_size)], '(h w) (p q c) -> c (h p) (w q)',
623
+ h=latent_size[0], w=latent_size[1], p=patch_size, q=patch_size)
624
+ for hs, latent_size in zip(hidden_states, latent_sizes)]
625
+
626
+ try:
627
+ output = torch.stack(output) # can be staked if all have the save shape
628
+ except:
629
+ # cannot be stacked
630
+ pass
631
+
632
+ if USE_PEFT_BACKEND:
633
+ # remove `lora_scale` from each PEFT layer
634
+ unscale_lora_layers(self, lora_scale)
635
+
636
+ if not return_dict:
637
+ return (output,)
638
+
639
+ return Transformer2DModelOutput(sample=output)
src/optimisers/custom_adamw.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from torch.optim import AdamW
2
+
3
+
4
+ class CustomAdamW(AdamW):
5
+ def __init__(self, params, weight_decay, *args, **kwargs):
6
+ # import pdb; pdb.set_trace()
7
+ if isinstance(params, dict):
8
+ params = [p for p in params.values() if p.requires_grad]
9
+ else:
10
+ params = [p for p in params if p.requires_grad]
11
+
12
+ # create optim groups. Any parameters that is 2D will be weight decayed, otherwise no.
13
+ # i.e. all weight tensors in matmuls + embeddings decay, all biases and layernorms don't.
14
+ decay_params = [p for p in params if p.dim() >= 2]
15
+ nodecay_params = [p for p in params if p.dim() < 2]
16
+ optim_groups = [
17
+ {'params': decay_params, 'weight_decay': weight_decay},
18
+ {'params': nodecay_params, 'weight_decay': 0.0}
19
+ ]
20
+ num_decay_params = sum(p.numel() for p in decay_params)
21
+ num_nodecay_params = sum(p.numel() for p in nodecay_params)
22
+ print(f"num decayed parameter tensors: {len(decay_params)}, with {num_decay_params:,} parameters")
23
+ print(f"num non-decayed parameter tensors: {len(nodecay_params)}, with {num_nodecay_params:,} parameters")
24
+ # Create AdamW optimizer and use the fused version if it is available
25
+ # fused_available = 'fused' in inspect.signature(AdamW).parameters
26
+ # extra_args = dict(fused=True) if fused_available else dict()
27
+ # print(f"using fused AdamW: {fused_available}")
28
+
29
+ # kwargs.update(extra_args)
30
+
31
+ super().__init__(params=optim_groups, *args, **kwargs)
32
+
33
+
34
+ class ParamWiseAdamW(AdamW):
35
+ def __init__(self, params, *args, **kwargs):
36
+ assert isinstance(params, list)
37
+ for param in params:
38
+ assert isinstance(param, dict)
39
+ assert isinstance(param['params'], list)
40
+ assert len(param['params']) == 1
41
+
42
+ if param['params'][0].ndim == 1:
43
+ param['weight_decay'] = 0.0
44
+
45
+ super().__init__(params=params, *args, **kwargs)
src/runners/custom_runner.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import logging
3
+ import inspect
4
+
5
+ from torch.utils.data import DataLoader
6
+ from functools import partial
7
+ from typing import Callable, Dict, List, Optional, Union
8
+
9
+ from mmengine.logging import print_log
10
+ from mmengine.dist import get_rank
11
+ from mmengine.dataset import worker_init_fn as default_worker_init_fn
12
+ from mmengine.utils import digit_version
13
+ from mmengine.utils.dl_utils import TORCH_VERSION
14
+ from mmengine.runner import FlexibleRunner
15
+ from mmengine.registry import (
16
+ DATA_SAMPLERS,
17
+ DATASETS,
18
+ FUNCTIONS,
19
+ )
20
+ from xtuner.registry import BUILDER
21
+
22
+
23
+ class CustomRunner(FlexibleRunner):
24
+ def __init__(
25
+ self,
26
+ **kwargs,
27
+ ):
28
+ super().__init__(**kwargs)
29
+
30
+ @staticmethod
31
+ def build_dataloader(
32
+ dataloader: Union[DataLoader, Dict],
33
+ seed: Optional[int] = None,
34
+ diff_rank_seed: bool = False,
35
+ ) -> DataLoader:
36
+ """Build dataloader.
37
+
38
+ The method builds three components:
39
+
40
+ - Dataset
41
+ - Sampler
42
+ - Dataloader
43
+
44
+ An example of ``dataloader``::
45
+
46
+ dataloader = dict(
47
+ dataset=dict(type='ToyDataset'),
48
+ sampler=dict(type='DefaultSampler', shuffle=True),
49
+ batch_size=1,
50
+ num_workers=9
51
+ )
52
+
53
+ Args:
54
+ dataloader (DataLoader or dict): A Dataloader object or a dict to
55
+ build Dataloader object. If ``dataloader`` is a Dataloader
56
+ object, just returns itself.
57
+ seed (int, optional): Random seed. Defaults to None.
58
+ diff_rank_seed (bool): Whether or not set different seeds to
59
+ different ranks. If True, the seed passed to sampler is set
60
+ to None, in order to synchronize the seeds used in samplers
61
+ across different ranks. Defaults to False.
62
+
63
+ Returns:
64
+ Dataloader: DataLoader build from ``dataloader_cfg``.
65
+ """
66
+ if isinstance(dataloader, DataLoader):
67
+ return dataloader
68
+
69
+ dataloader_cfg = copy.deepcopy(dataloader)
70
+
71
+ # build dataset
72
+ dataset_cfg = dataloader_cfg.pop('dataset')
73
+ if isinstance(dataset_cfg, dict):
74
+ dataset = DATASETS.build(dataset_cfg)
75
+ if hasattr(dataset, 'full_init'):
76
+ dataset.full_init()
77
+ else:
78
+ # fallback to raise error in dataloader
79
+ # if `dataset_cfg` is not a valid type
80
+ dataset = dataset_cfg
81
+
82
+ # build sampler
83
+ sampler_cfg = dataloader_cfg.pop('sampler')
84
+ if isinstance(sampler_cfg, dict):
85
+ sampler_seed = None if diff_rank_seed else seed
86
+ sampler = DATA_SAMPLERS.build(
87
+ sampler_cfg,
88
+ default_args=dict(dataset=dataset, seed=sampler_seed))
89
+ else:
90
+ # fallback to raise error in dataloader
91
+ # if `sampler_cfg` is not a valid type
92
+ sampler = sampler_cfg
93
+
94
+ # build batch sampler
95
+ batch_sampler_cfg = dataloader_cfg.pop('batch_sampler', None)
96
+ if batch_sampler_cfg is None:
97
+ batch_sampler = None
98
+ elif isinstance(batch_sampler_cfg, dict):
99
+ batch_sampler = DATA_SAMPLERS.build(
100
+ batch_sampler_cfg,
101
+ default_args=dict(
102
+ dataset=dataset,
103
+ sampler=sampler,
104
+ batch_size=dataloader_cfg.pop('batch_size')))
105
+ else:
106
+ # fallback to raise error in dataloader
107
+ # if `batch_sampler_cfg` is not a valid type
108
+ batch_sampler = batch_sampler_cfg
109
+
110
+ # build dataloader
111
+ init_fn: Optional[partial]
112
+ if 'worker_init_fn' in dataloader_cfg:
113
+ worker_init_fn_cfg = dataloader_cfg.pop('worker_init_fn')
114
+ worker_init_fn_type = worker_init_fn_cfg.pop('type')
115
+ worker_init_fn = FUNCTIONS.get(worker_init_fn_type)
116
+ assert callable(worker_init_fn)
117
+ init_fn = partial(worker_init_fn,
118
+ **worker_init_fn_cfg) # type: ignore
119
+ else:
120
+ if seed is not None:
121
+ disable_subprocess_warning = dataloader_cfg.pop(
122
+ 'disable_subprocess_warning', False)
123
+ assert isinstance(disable_subprocess_warning, bool), (
124
+ 'disable_subprocess_warning should be a bool, but got '
125
+ f'{type(disable_subprocess_warning)}')
126
+ init_fn = partial(
127
+ default_worker_init_fn,
128
+ num_workers=dataloader_cfg.get('num_workers'),
129
+ rank=get_rank(),
130
+ seed=seed,
131
+ disable_subprocess_warning=disable_subprocess_warning)
132
+ else:
133
+ init_fn = None
134
+
135
+ # `persistent_workers` requires pytorch version >= 1.7
136
+ if ('persistent_workers' in dataloader_cfg
137
+ and digit_version(TORCH_VERSION) < digit_version('1.7.0')):
138
+ print_log(
139
+ '`persistent_workers` is only available when '
140
+ 'pytorch version >= 1.7',
141
+ logger='current',
142
+ level=logging.WARNING)
143
+ dataloader_cfg.pop('persistent_workers')
144
+
145
+ # The default behavior of `collat_fn` in dataloader is to
146
+ # merge a list of samples to form a mini-batch of Tensor(s).
147
+ # However, in mmengine, if `collate_fn` is not defined in
148
+ # dataloader_cfg, `pseudo_collate` will only convert the list of
149
+ # samples into a dict without stacking the batch tensor.
150
+ collate_fn_cfg = dataloader_cfg.pop('collate_fn',
151
+ dict(type='pseudo_collate'))
152
+ if isinstance(collate_fn_cfg, dict):
153
+ collate_fn_type = collate_fn_cfg.pop('type')
154
+ if isinstance(collate_fn_type, str):
155
+ collate_fn = FUNCTIONS.get(collate_fn_type)
156
+ elif inspect.isclass(collate_fn_type):
157
+ collate_fn_cfg['type'] = collate_fn_type
158
+ collate_fn = BUILDER.build(collate_fn_cfg)
159
+ else:
160
+ collate_fn = collate_fn_type
161
+ if not inspect.isclass(collate_fn_type):
162
+ collate_fn = partial(collate_fn, **collate_fn_cfg) # type: ignore
163
+ elif callable(collate_fn_cfg):
164
+ collate_fn = collate_fn_cfg
165
+ else:
166
+ raise TypeError(
167
+ 'collate_fn should be a dict or callable object, but got '
168
+ f'{collate_fn_cfg}')
169
+ data_loader = DataLoader(
170
+ dataset=dataset,
171
+ sampler=sampler if batch_sampler is None else None,
172
+ batch_sampler=batch_sampler,
173
+ collate_fn=collate_fn,
174
+ worker_init_fn=init_fn,
175
+ **dataloader_cfg)
176
+
177
+ return data_loader