Text-to-Image
Diffusers
Safetensors
recoilme commited on
Commit
a45ddb3
·
1 Parent(s): a19a3a3
girl.jpg CHANGED

Git LFS Details

  • SHA256: 7f25c95ebab86739de1d13e2e93f04c1560f5211faac0aa938686bbfd3e50856
  • Pointer size: 130 Bytes
  • Size of remote file: 96 kB

Git LFS Details

  • SHA256: fd706646e8a05a5603898739224f4408cfcbd555eac5b6ea472dad1f0cc727ef
  • Pointer size: 130 Bytes
  • Size of remote file: 60.6 kB
media/result_grid.jpg CHANGED

Git LFS Details

  • SHA256: f3365f1b7b918cefc940f77ab36adc86e0fa37ea3458fb0be0702d208c01a7d6
  • Pointer size: 132 Bytes
  • Size of remote file: 3.79 MB

Git LFS Details

  • SHA256: eae7615befd8f92a8cd365b4cb84c210982ec7059d712ea82aa2e46413250b1e
  • Pointer size: 132 Bytes
  • Size of remote file: 2.79 MB
pipeline_sdxs-Copy1.py DELETED
@@ -1,369 +0,0 @@
1
- import torch
2
- import numpy as np
3
- from PIL import Image
4
- from typing import List, Union, Optional, Tuple
5
- from dataclasses import dataclass
6
-
7
- from diffusers import DiffusionPipeline
8
- from diffusers.utils import BaseOutput
9
- from tqdm import tqdm
10
- from transformers import Qwen3ForCausalLM, Qwen2Tokenizer
11
-
12
- @dataclass
13
- class SdxsPipelineOutput(BaseOutput):
14
- images: Union[List[Image.Image], np.ndarray]
15
- prompt: Optional[Union[str, List[str]]] = None # Возврат улучшенного промпта
16
-
17
- class SdxsPipeline(DiffusionPipeline):
18
- def __init__(self, vae, text_encoder, text_encoder2, tokenizer, tokenizer2, unet, scheduler):
19
- super().__init__()
20
- self.register_modules(
21
- vae=vae,
22
- text_encoder=text_encoder,
23
- text_encoder2=text_encoder2,
24
- tokenizer=tokenizer,
25
- tokenizer2=tokenizer2,
26
- unet=unet,
27
- scheduler=scheduler
28
- )
29
- self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
30
-
31
- def preprocess_image(self, image: Image.Image, width: int, height: int):
32
- """Ресайз и центрированный кроп изображения для асимметричного VAE."""
33
- # Для энкодера с масштабом 8
34
- target_height = ((height // self.vae_scale_factor) * self.vae_scale_factor)
35
- target_width = ((width // self.vae_scale_factor) * self.vae_scale_factor)
36
-
37
- w, h = image.size
38
- aspect_ratio = target_width / target_height
39
-
40
- if w / h > aspect_ratio:
41
- new_w = int(h * aspect_ratio)
42
- left = (w - new_w) // 2
43
- image = image.crop((left, 0, left + new_w, h))
44
- else:
45
- new_h = int(w / aspect_ratio)
46
- top = (h - new_h) // 2
47
- image = image.crop((0, top, w, top + new_h))
48
-
49
- image = image.resize((target_width, target_height), resample=Image.LANCZOS)
50
- image = np.array(image).astype(np.float32) / 255.0
51
- image = image[None].transpose(0, 3, 1, 2) # [1, C, H, W]
52
- image = torch.from_numpy(image)
53
- return 2.0 * image - 1.0 # [-1, 1]
54
-
55
- @staticmethod
56
- def _patchify_latents(latents):
57
- batch_size, num_channels_latents, height, width = latents.shape
58
- latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
59
- latents = latents.permute(0, 1, 3, 5, 2, 4)
60
- latents = latents.reshape(batch_size, num_channels_latents * 4, height // 2, width // 2)
61
- return latents
62
-
63
- @staticmethod
64
- def _unpatchify_latents(latents):
65
- batch_size, num_channels_latents, height, width = latents.shape
66
- latents = latents.reshape(batch_size, num_channels_latents // (2 * 2), 2, 2, height, width)
67
- latents = latents.permute(0, 1, 4, 2, 5, 3)
68
- latents = latents.reshape(batch_size, num_channels_latents // (2 * 2), height * 2, width * 2)
69
- return latents
70
-
71
- def flux_encode(self, latents):
72
- # 1. Patchify
73
- image_latents = self._patchify_latents(latents)
74
-
75
- # 2. Normalization
76
- bn_mean = self.vae.bn.running_mean.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
77
- bn_var = self.vae.bn.running_var.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
78
- eps = getattr(self.vae.config, "batch_norm_eps", 1e-5)
79
-
80
- latents_bn_std = torch.sqrt(bn_var + eps)
81
- latents = (image_latents - bn_mean) / latents_bn_std
82
-
83
- # 3. Unpatchify
84
- latents = self._unpatchify_latents(latents)
85
- return latents
86
-
87
- def flux_decode(self, latents):
88
- # 1. Patchify
89
- image_latents = self._patchify_latents(latents)
90
-
91
- # 2. De-normalization
92
- bn_mean = self.vae.bn.running_mean.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
93
- bn_var = self.vae.bn.running_var.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
94
- eps = getattr(self.vae.config, "batch_norm_eps", 1e-5)
95
-
96
- latents_bn_std = torch.sqrt(bn_var + eps)
97
- latents = image_latents * latents_bn_std + bn_mean
98
-
99
- # 3. Unpatchify
100
- latents = self._unpatchify_latents(latents)
101
- return latents
102
-
103
- def encode_prompt(self, prompt, negative_prompt, device, dtype):
104
- def get_single_encode(texts):
105
- if not texts:
106
- texts = [""]
107
- elif isinstance(texts, str):
108
- texts = [texts]
109
-
110
- with torch.no_grad():
111
- toks = self.tokenizer(
112
- texts,
113
- padding="max_length",
114
- max_length=self.text_encoder.config.max_position_embeddings,
115
- truncation=True,
116
- return_tensors="pt"
117
- ).to(device)
118
-
119
- outputs = self.text_encoder(
120
- input_ids=toks.input_ids,
121
- attention_mask=toks.attention_mask,
122
- output_hidden_states=True
123
- )
124
-
125
- # 1. Берем -2 слой [Batch, Seq, Dim]
126
- hidden = outputs.hidden_states[-2]
127
-
128
- # 2. Достаем pooled вектор (последний токен) [Batch, Dim]
129
- seq_lens = toks.attention_mask.sum(dim=1) - 1
130
- pooled = hidden[torch.arange(hidden.shape[0]), seq_lens.clamp(min=0)]
131
-
132
- # 3. Нормализация
133
- norm = self.text_encoder.text_model.final_layer_norm
134
- hidden = norm(hidden)
135
- pooled = norm(pooled)
136
-
137
- # 4. Объединяем в матрицу: Пулед (как 1-й токен) + остальные токены
138
- # pooled.unsqueeze(1) делает [Batch, 1, Dim]
139
- embeds = torch.cat([pooled.unsqueeze(1), hidden], dim=1)
140
-
141
- # 5. Расширяем маску для нового токена (добавляем единицы спереди)
142
- ones = torch.ones((toks.attention_mask.shape[0], 1), dtype=toks.attention_mask.dtype, device=device)
143
- mask = torch.cat([ones, toks.attention_mask], dim=1)
144
-
145
- return embeds, mask
146
-
147
- def get_pooled_encode(texts):
148
- if texts is None:
149
- texts = ""
150
-
151
- if isinstance(texts, str):
152
- texts = [texts]
153
-
154
- with torch.no_grad():
155
- # 1. Собираем текстовые промпты оборачивая их в Chat Template
156
- formatted_prompts = []
157
- for t in texts:
158
- messages = [{"role": "user", "content": [{"type": "text", "text": t}]}]
159
- res_text = self.tokenizer2.apply_chat_template(
160
- messages,
161
- add_generation_prompt=True,
162
- tokenize=False
163
- )
164
- formatted_prompts.append(res_text)
165
-
166
- # 2. Токенизируем, режем и добавляем паддинг за один раз
167
- toks = self.tokenizer2(
168
- formatted_prompts,
169
- padding="max_length",
170
- max_length=self.text_encoder.config.max_position_embeddings,
171
- truncation=True, # Не забываем обрезать, если вдруг длиннее
172
- return_tensors="pt"
173
- ).to(device)
174
-
175
- # 3. Прогоняем через модель
176
- outputs = self.text_encoder2(
177
- input_ids=toks.input_ids,
178
- attention_mask=toks.attention_mask,
179
- output_hidden_states=True
180
- )
181
-
182
- layer_index = -2
183
- last_hidden = outputs.hidden_states[layer_index]
184
- seq_len = toks.attention_mask.sum(dim=1) - 1
185
- pooled = last_hidden[torch.arange(len(last_hidden)), seq_len.clamp(min=0)]
186
-
187
- return pooled
188
-
189
- pos_embeds, pos_mask = get_single_encode(prompt)
190
- neg_embeds, neg_mask = get_single_encode(negative_prompt)
191
- pos_pooled = get_pooled_encode(prompt)
192
- neg_pooled = get_pooled_encode(negative_prompt)
193
-
194
- batch_size = pos_embeds.shape[0]
195
- if neg_embeds.shape[0] != batch_size:
196
- neg_embeds = neg_embeds.repeat(batch_size, 1, 1)
197
- neg_mask = neg_mask.repeat(batch_size, 1)
198
- neg_pooled = neg_pooled.repeat(batch_size, 1)
199
-
200
- if pos_pooled.shape[0] != batch_size:
201
- pos_pooled = pos_pooled.repeat(batch_size, 1)
202
-
203
- text_embeddings = torch.cat([neg_embeds, pos_embeds], dim=0)
204
- final_mask = torch.cat([neg_mask, pos_mask], dim=0)
205
- pooled_embeds = torch.cat([neg_pooled, pos_pooled], dim=0)
206
-
207
- return text_embeddings.to(dtype=dtype), final_mask.to(dtype=torch.int64), pooled_embeds.to(dtype=dtype)
208
-
209
- @torch.no_grad()
210
- def __call__(
211
- self,
212
- prompt: Union[str, List[str]],
213
- image: Optional[Union[Image.Image, List[Image.Image]]] = None,
214
- coef: float = 0.97, # ← strength (0.0 = оригинал, 1.0 = полный шум)
215
- negative_prompt: Optional[Union[str, List[str]]] = None,
216
- height: int = 1024,
217
- width: int = 1024,
218
- num_inference_steps: int = 40,
219
- guidance_scale: float = 4.0,
220
- generator: Optional[torch.Generator] = None,
221
- seed: Optional[int] = None,
222
- output_type: str = "pil",
223
- return_dict: bool = True,
224
- refine_prompt: bool = False, # Флаг рефайна!
225
- # structure_preservation оставляем для совместимости, но теперь он почти не нужен
226
- structure_preservation: float = 0.0, # 0.0 = стандартный линейный путь (лучше всего)
227
- **kwargs,
228
- ):
229
- device = self.device
230
- dtype = self.unet.dtype
231
-
232
- if generator is None and seed is not None:
233
- generator = torch.Generator(device=device).manual_seed(seed)
234
-
235
- # ==================== REFINE PROMPT (INLINE) ====================
236
- if refine_prompt and prompt:
237
- sys_msg = (
238
- "You are a skilled text-to-image prompt engineer whose sole function is to transform the user's input into an aesthetically optimized, detailed, and visually descriptive three-sentence output. "
239
- "**The primary subject (e.g., 'girl', 'dog', 'house') MUST be the main focus of the revised prompt and MUST be described in rich detail within the first sentence or two.** "
240
- "Output **only** the final revised prompt in **English**, with absolutely no commentary.\n Don't use cliches like warm,soft,vibrant, wildflowers. Be creative "
241
- "User input prompt: "
242
- )
243
- prompts_list = [prompt] if isinstance(prompt, str) else prompt
244
- refined_list = []
245
-
246
- for p in prompts_list:
247
- messages = [{"role": "user", "content": [{"type": "text", "text": sys_msg + p}]}]
248
-
249
- # Используем Qwen-Instruct формат (apply_chat_template сам подставит system/user/assistant токены)
250
- inputs = self.tokenizer2.apply_chat_template(
251
- messages,
252
- tokenize=True,
253
- add_generation_prompt=True,
254
- return_dict=True,
255
- return_tensors="pt"
256
- ).to(device)
257
-
258
- generated_ids = self.text_encoder2.generate(
259
- **inputs, max_new_tokens=self.text_encoder.config.max_position_embeddings, do_sample=True,temperature = 0.7
260
- )
261
-
262
- # Обрезаем входные токены из ответа
263
- generated_ids_trimmed = [
264
- out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
265
- ]
266
- output_text = self.tokenizer2.batch_decode(
267
- generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
268
- )
269
- refined_list.append(output_text)
270
-
271
- prompt = refined_list[0] if isinstance(prompt, str) else refined_list
272
-
273
- # ==================== ENCODE PROMPTS ====================
274
- text_embeddings, attention_mask, pooled_embeds = self.encode_prompt(
275
- prompt, negative_prompt, device, dtype
276
- )
277
- batch_size = 1 if isinstance(prompt, str) else len(prompt)
278
-
279
- # 2. Scheduler timesteps
280
- self.scheduler.set_timesteps(num_inference_steps, device=device)
281
- timesteps = self.scheduler.timesteps
282
-
283
- # ==================== IMG2IMG БЛОК (НОВАЯ ВЕРСИЯ) ====================
284
- if image is not None:
285
- # --- Подготовка изображения ---
286
- if isinstance(image, Image.Image):
287
- image_tensor = self.preprocess_image(image, width, height).to(device, self.vae.dtype)
288
- else:
289
- image_tensor = self.preprocess_image(image[0], width, height).to(device, self.vae.dtype)
290
-
291
- # --- Кодируем в latent ---
292
- latents_clean = self.vae.encode(image_tensor).latent_dist.sample(generator=generator)
293
- vae_scaling_factor = getattr(self.vae.config, "scaling_factor", 1.0)
294
- vae_shift_factor = getattr(self.vae.config, "shift_factor", 0.0)
295
- latents_clean = (latents_clean - vae_shift_factor) / vae_scaling_factor
296
- latents_clean = latents_clean.to(dtype)
297
-
298
- # --- Добавляем шум по Rectified Flow формуле ---
299
- noise = torch.randn_like(latents_clean)
300
-
301
- # coef = strength (0.0 → оригинал, 1.0 → чистый шум)
302
- sigma = coef # в Flow Matching sigma = t
303
- if hasattr(self.scheduler, "sigma_shift"): # если есть shift (Flux-style)
304
- sigma = self.scheduler.sigma_shift(sigma)
305
-
306
- latents = (1.0 - sigma) * latents_clean + sigma * noise
307
-
308
- # Обрезаем timesteps начиная с текущего sigma
309
- init_timestep = int(num_inference_steps * coef)
310
- t_start = max(num_inference_steps - init_timestep, 0)
311
- timesteps = timesteps[t_start:]
312
-
313
- else:
314
- # txt2img — оставляем как было
315
- vae_scaling_factor = getattr(self.vae.config, "scaling_factor", 1.0)
316
- vae_shift_factor = getattr(self.vae.config, "shift_factor", 0.0)
317
- latent_h = height // self.vae_scale_factor
318
- latent_w = width // self.vae_scale_factor
319
-
320
- latents = torch.randn(
321
- (batch_size, self.unet.config.in_channels, latent_h, latent_w),
322
- generator=generator, device=device, dtype=dtype
323
- )
324
-
325
- # ==================== DENOISING LOOP (одинаковый для txt2img и img2img) ====================
326
- for i, t in enumerate(tqdm(timesteps, desc="Sampling")):
327
- latent_model_input = torch.cat([latents] * 2) if guidance_scale > 1.0 else latents
328
-
329
- added_cond_kwargs = {
330
- "text_embeds": pooled_embeds,
331
- }
332
-
333
- model_out = self.unet(
334
- latent_model_input,
335
- t,
336
- encoder_hidden_states=text_embeddings,
337
- encoder_attention_mask=attention_mask,
338
- added_cond_kwargs=added_cond_kwargs,
339
- return_dict=False,
340
- )[0]
341
-
342
- if guidance_scale > 1.0:
343
- flow_uncond, flow_cond = model_out.chunk(2)
344
- model_out = flow_uncond + guidance_scale * (flow_cond - flow_uncond)
345
-
346
- # Важно: используем scheduler.step — он сам знает, что делать с velocity
347
- latents = self.scheduler.step(model_out, t, latents, return_dict=False)[0]
348
-
349
- # ==================== DECODE ====================
350
- if output_type == "latent":
351
- if not return_dict: return (latents, prompt)
352
- return SdxsPipelineOutput(images=latents, prompt=prompt)
353
-
354
- latents = latents * vae_scaling_factor + vae_shift_factor
355
- latents = self.flux_decode(latents)
356
-
357
- image_output = self.vae.decode(latents.to(self.vae.dtype), return_dict=False)[0]
358
-
359
- image_output = (image_output.clamp(-1, 1) + 1) / 2
360
- image_np = image_output.cpu().permute(0, 2, 3, 1).float().numpy()
361
-
362
- if output_type == "pil":
363
- images = [(Image.fromarray((img * 255).round().astype("uint8"))) for img in image_np]
364
- else:
365
- images = image_np
366
-
367
- if not return_dict:
368
- return (images, prompt)
369
- return SdxsPipelineOutput(images=images, prompt=prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pipeline_sdxs-Copy2.py DELETED
@@ -1,377 +0,0 @@
1
- import torch
2
- import numpy as np
3
- from PIL import Image
4
- from typing import List, Union, Optional, Tuple
5
- from dataclasses import dataclass
6
-
7
- from diffusers import DiffusionPipeline
8
- from diffusers.utils import BaseOutput
9
- from tqdm import tqdm
10
- from transformers import Qwen3ForCausalLM, Qwen2Tokenizer
11
-
12
- @dataclass
13
- class SdxsPipelineOutput(BaseOutput):
14
- images: Union[List[Image.Image], np.ndarray]
15
- prompt: Optional[Union[str, List[str]]] = None # Возврат улучшенного промпта
16
-
17
- class SdxsPipeline(DiffusionPipeline):
18
- def __init__(self, vae, text_encoder, text_encoder2, tokenizer, tokenizer2, unet, scheduler):
19
- super().__init__()
20
- self.register_modules(
21
- vae=vae,
22
- text_encoder=text_encoder,
23
- text_encoder2=text_encoder2,
24
- tokenizer=tokenizer,
25
- tokenizer2=tokenizer2,
26
- unet=unet,
27
- scheduler=scheduler
28
- )
29
- self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
30
-
31
- def preprocess_image(self, image: Image.Image, width: int, height: int):
32
- """Ресайз и центрированный кроп изображения для асимметричного VAE."""
33
- # Для энкодера с масштабом 8
34
- target_height = ((height // self.vae_scale_factor) * self.vae_scale_factor)
35
- target_width = ((width // self.vae_scale_factor) * self.vae_scale_factor)
36
-
37
- w, h = image.size
38
- aspect_ratio = target_width / target_height
39
-
40
- if w / h > aspect_ratio:
41
- new_w = int(h * aspect_ratio)
42
- left = (w - new_w) // 2
43
- image = image.crop((left, 0, left + new_w, h))
44
- else:
45
- new_h = int(w / aspect_ratio)
46
- top = (h - new_h) // 2
47
- image = image.crop((0, top, w, top + new_h))
48
-
49
- image = image.resize((target_width, target_height), resample=Image.LANCZOS)
50
- image = np.array(image).astype(np.float32) / 255.0
51
- image = image[None].transpose(0, 3, 1, 2) # [1, C, H, W]
52
- image = torch.from_numpy(image)
53
- return 2.0 * image - 1.0 # [-1, 1]
54
-
55
- @staticmethod
56
- def _patchify_latents(latents):
57
- batch_size, num_channels_latents, height, width = latents.shape
58
- latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
59
- latents = latents.permute(0, 1, 3, 5, 2, 4)
60
- latents = latents.reshape(batch_size, num_channels_latents * 4, height // 2, width // 2)
61
- return latents
62
-
63
- @staticmethod
64
- def _unpatchify_latents(latents):
65
- batch_size, num_channels_latents, height, width = latents.shape
66
- latents = latents.reshape(batch_size, num_channels_latents // (2 * 2), 2, 2, height, width)
67
- latents = latents.permute(0, 1, 4, 2, 5, 3)
68
- latents = latents.reshape(batch_size, num_channels_latents // (2 * 2), height * 2, width * 2)
69
- return latents
70
-
71
- def flux_encode(self, latents):
72
- # 1. Patchify
73
- image_latents = self._patchify_latents(latents)
74
-
75
- # 2. Normalization
76
- bn_mean = self.vae.bn.running_mean.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
77
- bn_var = self.vae.bn.running_var.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
78
- eps = getattr(self.vae.config, "batch_norm_eps", 1e-5)
79
-
80
- latents_bn_std = torch.sqrt(bn_var + eps)
81
- latents = (image_latents - bn_mean) / latents_bn_std
82
-
83
- # 3. Unpatchify
84
- latents = self._unpatchify_latents(latents)
85
- return latents
86
-
87
- def flux_decode(self, latents):
88
- # 1. Patchify
89
- image_latents = self._patchify_latents(latents)
90
-
91
- # 2. De-normalization
92
- bn_mean = self.vae.bn.running_mean.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
93
- bn_var = self.vae.bn.running_var.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
94
- eps = getattr(self.vae.config, "batch_norm_eps", 1e-5)
95
-
96
- latents_bn_std = torch.sqrt(bn_var + eps)
97
- latents = image_latents * latents_bn_std + bn_mean
98
-
99
- # 3. Unpatchify
100
- latents = self._unpatchify_latents(latents)
101
- return latents
102
-
103
- def encode_prompt(self, prompt, negative_prompt, device, dtype):
104
- def get_single_encode(texts):
105
- if not texts:
106
- texts = [""]
107
- elif isinstance(texts, str):
108
- texts = [texts]
109
-
110
- with torch.no_grad():
111
- toks = self.tokenizer(
112
- texts,
113
- padding="max_length",
114
- max_length=self.text_encoder.config.max_position_embeddings,
115
- truncation=True,
116
- return_tensors="pt"
117
- ).to(device)
118
-
119
- outputs = self.text_encoder(
120
- input_ids=toks.input_ids,
121
- attention_mask=toks.attention_mask,
122
- output_hidden_states=True
123
- )
124
-
125
- # 1. Берем -2 слой [Batch, Seq, Dim]
126
- hidden = outputs.hidden_states[-2]
127
-
128
- # 2. Достаем pooled вектор (последний токен) [Batch, Dim]
129
- seq_lens = toks.attention_mask.sum(dim=1) - 1
130
- pooled = hidden[torch.arange(hidden.shape[0]), seq_lens.clamp(min=0)]
131
-
132
- # 3. Нормализация
133
- norm = self.text_encoder.text_model.final_layer_norm
134
- hidden = norm(hidden)
135
- pooled = norm(pooled)
136
-
137
- # 4. Объединяем в матрицу: Пулед (как 1-й токен) + остальные токены
138
- # pooled.unsqueeze(1) делает [Batch, 1, Dim]
139
- embeds = torch.cat([pooled.unsqueeze(1), hidden], dim=1)
140
-
141
- # 5. Расширяем маску для нового токена (добавляем единицы спереди)
142
- ones = torch.ones((toks.attention_mask.shape[0], 1), dtype=toks.attention_mask.dtype, device=device)
143
- mask = torch.cat([ones, toks.attention_mask], dim=1)
144
-
145
- return embeds, mask, pooled
146
-
147
- def get_pooled_encode(texts):
148
- if texts is None:
149
- texts = ""
150
-
151
- if isinstance(texts, str):
152
- texts = [texts]
153
-
154
- with torch.no_grad():
155
- # 1. Собираем текстовые промпты оборачивая их в Chat Template
156
- formatted_prompts = []
157
- for t in texts:
158
- messages = [{"role": "user", "content": [{"type": "text", "text": t}]}]
159
- res_text = self.tokenizer2.apply_chat_template(
160
- messages,
161
- add_generation_prompt=True,
162
- tokenize=False
163
- )
164
- formatted_prompts.append(res_text)
165
-
166
- # 2. Токенизируем, режем и добавляем паддинг за один раз
167
- toks = self.tokenizer2(
168
- formatted_prompts,
169
- padding="max_length",
170
- max_length=self.text_encoder.config.max_position_embeddings,
171
- truncation=True, # Не забываем обрезать, если вдруг длиннее
172
- return_tensors="pt"
173
- ).to(device)
174
-
175
- # 3. Прогоняем через модель
176
- outputs = self.text_encoder2(
177
- input_ids=toks.input_ids,
178
- attention_mask=toks.attention_mask,
179
- output_hidden_states=True
180
- )
181
-
182
- layer_index = -2
183
- last_hidden = outputs.hidden_states[layer_index]
184
- seq_len = toks.attention_mask.sum(dim=1) - 1
185
- pooled = last_hidden[torch.arange(len(last_hidden)), seq_len.clamp(min=0)]
186
-
187
- return pooled
188
-
189
- pos_embeds, pos_mask, pooled_pos = get_single_encode(prompt)
190
- neg_embeds, neg_mask, pooled_neg = get_single_encode(negative_prompt)
191
- # 768 + 2048
192
- pos_pooled = torch.cat([pooled_pos, get_pooled_encode(prompt)], dim=1)
193
- neg_pooled = torch.cat([pooled_neg, get_pooled_encode(negative_prompt)], dim=1)
194
-
195
- batch_size = pos_embeds.shape[0]
196
- if neg_embeds.shape[0] != batch_size:
197
- neg_embeds = neg_embeds.repeat(batch_size, 1, 1)
198
- neg_mask = neg_mask.repeat(batch_size, 1)
199
- neg_pooled = neg_pooled.repeat(batch_size, 1)
200
-
201
- if pos_pooled.shape[0] != batch_size:
202
- pos_pooled = pos_pooled.repeat(batch_size, 1)
203
-
204
- text_embeddings = torch.cat([neg_embeds, pos_embeds], dim=0)
205
- final_mask = torch.cat([neg_mask, pos_mask], dim=0)
206
- pooled_embeds = torch.cat([neg_pooled, pos_pooled], dim=0)
207
-
208
- return text_embeddings.to(dtype=dtype), final_mask.to(dtype=torch.int64), pooled_embeds.to(dtype=dtype)
209
-
210
- @torch.no_grad()
211
- def __call__(
212
- self,
213
- prompt: Union[str, List[str]],
214
- image: Optional[Union[Image.Image, List[Image.Image]]] = None,
215
- coef: float = 0.97, # ← strength (0.0 = оригинал, 1.0 = полный шум)
216
- negative_prompt: Optional[Union[str, List[str]]] = None,
217
- height: int = 1024,
218
- width: int = 1024,
219
- num_inference_steps: int = 40,
220
- guidance_scale: float = 4.0,
221
- generator: Optional[torch.Generator] = None,
222
- seed: Optional[int] = None,
223
- output_type: str = "pil",
224
- return_dict: bool = True,
225
- refine_prompt: bool = False, # Флаг рефайна!
226
- # structure_preservation оставляем для совместимости, но теперь он почти не нужен
227
- structure_preservation: float = 0.0, # 0.0 = стандартный линейный путь (лучше всего)
228
- **kwargs,
229
- ):
230
- device = self.device
231
- dtype = self.unet.dtype
232
-
233
- if generator is None and seed is not None:
234
- generator = torch.Generator(device=device).manual_seed(seed)
235
-
236
- # ==================== REFINE PROMPT (INLINE) ====================
237
- if refine_prompt and prompt:
238
- sys_msg = (
239
- "You are a skilled text-to-image prompt engineer whose sole function is to transform the user's input into an aesthetically optimized, detailed, and visually descriptive three-sentence output. "
240
- "**The primary subject (e.g., 'girl', 'dog', 'house') MUST be the main focus of the revised prompt and MUST be described in rich detail within the first sentence or two.** "
241
- "Output **only** the final revised prompt in **English**, with absolutely no commentary.\n Don't use cliches like warm,soft,vibrant, wildflowers. Be creative "
242
- "User input prompt: "
243
- )
244
- prompts_list = [prompt] if isinstance(prompt, str) else prompt
245
- refined_list = []
246
-
247
- for p in prompts_list:
248
- messages = [{"role": "user", "content": [{"type": "text", "text": sys_msg + p}]}]
249
-
250
- # Используем Qwen-Instruct формат (apply_chat_template сам подставит system/user/assistant токены)
251
- inputs = self.tokenizer2.apply_chat_template(
252
- messages,
253
- tokenize=True,
254
- add_generation_prompt=True,
255
- return_dict=True,
256
- return_tensors="pt"
257
- ).to(device)
258
-
259
- generated_ids = self.text_encoder2.generate(
260
- **inputs, max_new_tokens=self.text_encoder.config.max_position_embeddings, do_sample=True,temperature = 0.7
261
- )
262
-
263
- # Обрезаем входные токены из ответа
264
- generated_ids_trimmed = [
265
- out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
266
- ]
267
- output_text = self.tokenizer2.batch_decode(
268
- generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
269
- )
270
- refined_list.append(output_text)
271
-
272
- prompt = refined_list[0] if isinstance(prompt, str) else refined_list
273
-
274
- # ==================== ENCODE PROMPTS ====================
275
- text_embeddings, attention_mask, pooled_embeds = self.encode_prompt(
276
- prompt, negative_prompt, device, dtype
277
- )
278
- batch_size = 1 if isinstance(prompt, str) else len(prompt)
279
-
280
- # 2. Scheduler timesteps
281
- self.scheduler.set_timesteps(num_inference_steps, device=device)
282
- timesteps = self.scheduler.timesteps
283
-
284
- # ==================== TIME IDS =======================================
285
- # time_ids должен иметь ТОТ ЖЕ batch-размер, что и pooled_embeds и text_embeddings
286
- # (в твоём encode_prompt они всегда удваиваются из-за CFG)
287
- time_ids = torch.zeros(
288
- pooled_embeds.shape[0], # ← вот это главное
289
- 6,
290
- device=device,
291
- dtype=torch.long
292
- )
293
-
294
- # ==================== IMG2IMG БЛОК (НОВАЯ ВЕРСИЯ) ====================
295
- if image is not None:
296
- # --- Подготовка изображения ---
297
- if isinstance(image, Image.Image):
298
- image_tensor = self.preprocess_image(image, width, height).to(device, self.vae.dtype)
299
- else:
300
- image_tensor = self.preprocess_image(image[0], width, height).to(device, self.vae.dtype)
301
-
302
- # --- Кодируем в latent ---
303
- latents_clean = self.vae.encode(image_tensor).latent_dist.sample(generator=generator)
304
- vae_scaling_factor = getattr(self.vae.config, "scaling_factor", 1.0)
305
- vae_shift_factor = getattr(self.vae.config, "shift_factor", 0.0)
306
- latents_clean = (latents_clean - vae_shift_factor) / vae_scaling_factor
307
- latents_clean = latents_clean.to(dtype)
308
-
309
- # --- Добавляем шум по Rectified Flow формуле ---
310
- noise = torch.randn_like(latents_clean)
311
-
312
- # coef = strength (0.0 → оригинал, 1.0 → чистый шум)
313
- sigma = coef # в Flow Matching sigma = t
314
- if hasattr(self.scheduler, "sigma_shift"): # если есть shift (Flux-style)
315
- sigma = self.scheduler.sigma_shift(sigma)
316
-
317
- latents = (1.0 - sigma) * latents_clean + sigma * noise
318
-
319
- # Обрезаем timesteps начиная с текущего sigma
320
- init_timestep = int(num_inference_steps * coef)
321
- t_start = max(num_inference_steps - init_timestep, 0)
322
- timesteps = timesteps[t_start:]
323
-
324
- else:
325
- # txt2img — оставляем как было
326
- vae_scaling_factor = getattr(self.vae.config, "scaling_factor", 1.0)
327
- vae_shift_factor = getattr(self.vae.config, "shift_factor", 0.0)
328
- latent_h = height // self.vae_scale_factor
329
- latent_w = width // self.vae_scale_factor
330
-
331
- latents = torch.randn(
332
- (batch_size, self.unet.config.in_channels, latent_h, latent_w),
333
- generator=generator, device=device, dtype=dtype
334
- )
335
-
336
- # ==================== DENOISING LOOP (одинаковый для txt2img и img2img) ====================
337
- for i, t in enumerate(tqdm(timesteps, desc="Sampling")):
338
- latent_model_input = torch.cat([latents] * 2) if guidance_scale > 1.0 else latents
339
-
340
- model_out = self.unet(
341
- latent_model_input,
342
- t,
343
- encoder_hidden_states=text_embeddings,
344
- encoder_attention_mask=attention_mask,
345
- #added_cond_kwargs=added_cond_kwargs,
346
- added_cond_kwargs={"text_embeds": pooled_embeds,"time_ids": time_ids},
347
- return_dict=False,
348
- )[0]
349
-
350
- if guidance_scale > 1.0:
351
- flow_uncond, flow_cond = model_out.chunk(2)
352
- model_out = flow_uncond + guidance_scale * (flow_cond - flow_uncond)
353
-
354
- # Важно: используем scheduler.step — он сам знает, что делать с velocity
355
- latents = self.scheduler.step(model_out, t, latents, return_dict=False)[0]
356
-
357
- # ==================== DECODE ====================
358
- if output_type == "latent":
359
- if not return_dict: return (latents, prompt)
360
- return SdxsPipelineOutput(images=latents, prompt=prompt)
361
-
362
- latents = latents * vae_scaling_factor + vae_shift_factor
363
- latents = self.flux_decode(latents)
364
-
365
- image_output = self.vae.decode(latents.to(self.vae.dtype), return_dict=False)[0]
366
-
367
- image_output = (image_output.clamp(-1, 1) + 1) / 2
368
- image_np = image_output.cpu().permute(0, 2, 3, 1).float().numpy()
369
-
370
- if output_type == "pil":
371
- images = [(Image.fromarray((img * 255).round().astype("uint8"))) for img in image_np]
372
- else:
373
- images = image_np
374
-
375
- if not return_dict:
376
- return (images, prompt)
377
- return SdxsPipelineOutput(images=images, prompt=prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test.ipynb CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:95269c824348ba2d5212db7d149efc938a519b90476a8f172936927d7867b7d6
3
- size 5599938
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1da01a71ae7db2ab06541fc09ddfd80a8b81f804ab2194f4defdabda9c060f60
3
+ size 4347682
train-Copy1.py DELETED
@@ -1,818 +0,0 @@
1
- #from comet_ml import Experiment
2
- import os
3
- os.environ["NCCL_P2P_DISABLE"] = "1"
4
- # disable this on old GPU?
5
- os.environ["NCCL_IB_DISABLE"] = "1"
6
- os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
7
- import math
8
- import torch
9
- import numpy as np
10
- import matplotlib.pyplot as plt
11
- from torch.utils.data import DataLoader, Sampler
12
- from torch.utils.data.distributed import DistributedSampler
13
- from torch.optim.lr_scheduler import LambdaLR
14
- from collections import defaultdict
15
- from diffusers import UNet2DConditionModel,AutoencoderKLFlux2,FlowMatchEulerDiscreteScheduler
16
- from accelerate import Accelerator, DeepSpeedPlugin
17
- from datasets import load_from_disk
18
- from tqdm import tqdm
19
- from PIL import Image, ImageOps
20
- import wandb
21
- import random
22
- import gc
23
- from accelerate.state import DistributedType
24
- from torch.distributed import broadcast_object_list
25
- from torch.utils.checkpoint import checkpoint
26
- from diffusers.models.attention_processor import AttnProcessor2_0
27
- from datetime import datetime
28
- import bitsandbytes as bnb
29
- import torch.nn.functional as F
30
- from collections import deque
31
- from transformers import AutoTokenizer, AutoModel, Qwen2Tokenizer,Qwen3VLForConditionalGeneration
32
-
33
- # --------------------------- Параметры ---------------------------
34
- ds_path = "/workspace/sdxs-1b/datasets/ds1234_flux32"
35
- project = "unet"
36
- ## total batch (split // num `GPU)
37
- batch_size = 48
38
- base_learning_rate = 6e-6
39
- min_learning_rate = 7e-7
40
- num_epochs = 8
41
- sample_interval_share = 5
42
- cfg_dropout = 0.10
43
- max_length = 248
44
- use_wandb = False
45
- use_comet_ml = False
46
- save_model = False
47
- use_decay = True
48
- fbp = False
49
- optimizer_type = "adam8bit"
50
- torch_compile = False
51
- unet_gradient = True
52
- loss_normalize = False
53
- fixed_seed = False
54
- shuffle = True
55
- comet_ml_api_key = "Agctp26mbqnoYrrlvQuKSTk6r"
56
- comet_ml_workspace = "recoilme"
57
- torch.backends.cuda.matmul.allow_tf32 = True
58
- torch.backends.cudnn.allow_tf32 = True
59
- # Включение Flash Attention 2/SDPA #MAX_JOBS=4 pip install flash-attn --no-build-isolation
60
- torch.backends.cuda.enable_flash_sdp(True)
61
- torch.backends.cuda.enable_mem_efficient_sdp(True)
62
- torch.backends.cuda.enable_math_sdp(False) # Отключаем медленный вариант
63
- save_barrier = 1.25
64
- warmup_percent = 0.03
65
- #percentile_clipping = 95
66
- betta2 = 0.995
67
- eps = 1e-7
68
- clip_grad_norm = 1.0
69
- limit = 0
70
- checkpoints_folder = ""
71
- gradient_accumulation_steps = 1
72
- dtype = torch.float32
73
- mixed_precision = "no"
74
-
75
- # Параметры для диффузии
76
- n_diffusion_steps = 40
77
- samples_to_generate = 12
78
- guidance_scale = 4
79
-
80
- # Папки для сохранения результатов
81
- generated_folder = "samples"
82
- os.makedirs(generated_folder, exist_ok=True)
83
-
84
- # Настройка seed
85
- current_date = datetime.now()
86
- seed = int(current_date.strftime("%Y%m%d")) + 10000001
87
- if fixed_seed:
88
- torch.manual_seed(seed)
89
- np.random.seed(seed)
90
- random.seed(seed)
91
- if torch.cuda.is_available():
92
- torch.cuda.manual_seed_all(seed)
93
-
94
- accelerator = Accelerator(
95
- mixed_precision=mixed_precision,
96
- gradient_accumulation_steps=gradient_accumulation_steps
97
- )
98
- device = accelerator.device
99
-
100
- print("init")
101
-
102
- # --------------------------- Инициализация WandB ---------------------------
103
- if accelerator.is_main_process:
104
- if use_wandb:
105
- wandb.init(project=project, config={
106
- "batch_size": batch_size,
107
- "base_learning_rate": base_learning_rate,
108
- "num_epochs": num_epochs,
109
- "optimizer_type": optimizer_type,
110
- })
111
- if use_comet_ml:
112
- from comet_ml import Experiment
113
- comet_experiment = Experiment(
114
- api_key=comet_ml_api_key,
115
- project_name=project,
116
- workspace=comet_ml_workspace
117
- )
118
- hyper_params = {
119
- "batch_size": batch_size,
120
- "base_learning_rate": base_learning_rate,
121
- "num_epochs": num_epochs,
122
- }
123
- comet_experiment.log_parameters(hyper_params)
124
-
125
- # --------------------------- Загрузка моделей ---------------------------
126
- #vae = AutoencoderKL.from_pretrained("vae", torch_dtype=dtype).to("cpu").eval()
127
- #vae = AutoencoderKLFlux2.from_pretrained("black-forest-labs/FLUX.2-dev",subfolder="vae",torch_dtype=dtype).to(device).eval()
128
- #vae = AsymmetricAutoencoderKL.from_pretrained("vae",torch_dtype=dtype).to(device).eval()
129
- vae = AutoencoderKLFlux2.from_pretrained("vae", torch_dtype=dtype).to(device).eval()
130
- tokenizer = AutoTokenizer.from_pretrained("tokenizer")
131
- text_encoder = AutoModel.from_pretrained("text_encoder", torch_dtype=torch.float16).to(device).eval()
132
- tokenizer2 = Qwen2Tokenizer.from_pretrained("tokenizer2")
133
- text_encoder2 = Qwen3VLForConditionalGeneration.from_pretrained("text_encoder2", torch_dtype=torch.float16).to(device).eval()
134
- scheduler = FlowMatchEulerDiscreteScheduler.from_pretrained("scheduler")
135
-
136
- def encode_texts(texts, max_length=max_length):
137
- if texts is None:
138
- texts = [""]
139
- if isinstance(texts, str):
140
- texts = [texts]
141
-
142
- with torch.no_grad():
143
- # --- 1. CLIP Энкодер ---
144
- toks = tokenizer(
145
- texts,
146
- padding="max_length",
147
- max_length=max_length,
148
- truncation=True,
149
- return_tensors="pt"
150
- ).to(device)
151
-
152
- outputs = text_encoder(
153
- input_ids=toks.input_ids,
154
- attention_mask=toks.attention_mask,
155
- output_hidden_states=True
156
- )
157
-
158
- # 1. Берем -2 слой [Batch, Seq, Dim]
159
- hidden = outputs.hidden_states[-2]
160
-
161
- # 2. Достаем pooled вектор (последний токен) [Batch, Dim]
162
- seq_lens = toks.attention_mask.sum(dim=1) - 1
163
- pooled_clip = hidden[torch.arange(hidden.shape[0]), seq_lens.clamp(min=0)]
164
-
165
- # 3. Нормализация
166
- norm = text_encoder.text_model.final_layer_norm
167
- hidden = norm(hidden)
168
- pooled_clip = norm(pooled_clip)
169
-
170
- # 4. Объединяем в матрицу: Пулед (как 1-й токен) + остальные токены
171
- # pooled.unsqueeze(1) делает [Batch, 1, Dim]
172
- prompt_embeds = torch.cat([pooled_clip.unsqueeze(1), hidden], dim=1)
173
-
174
- # 5. Расширяем маску для нового токена (добавляем единицы спереди)
175
- ones = torch.ones((toks.attention_mask.shape[0], 1), dtype=toks.attention_mask.dtype, device=device)
176
- mask = torch.cat([ones, toks.attention_mask], dim=1)
177
-
178
- # --- 2. QWEN Энкодер (через Chat Template) ---
179
- # 1. Собираем текстовые промпты оборачивая их в Chat Template
180
- formatted_prompts = []
181
- for t in texts:
182
- messages = [{"role": "user", "content": [{"type": "text", "text": t}]}]
183
- res_text = tokenizer2.apply_chat_template(
184
- messages,
185
- add_generation_prompt=True,
186
- tokenize=False
187
- )
188
- formatted_prompts.append(res_text)
189
-
190
- # 2. Токенизируем, режем и добавляем паддинг за один раз
191
- toks = tokenizer2(
192
- formatted_prompts,
193
- padding="max_length",
194
- max_length=max_length,
195
- truncation=True,
196
- return_tensors="pt"
197
- ).to(device)
198
-
199
- # 3. Прогоняем через модель
200
- outputs = text_encoder2(
201
- input_ids=toks.input_ids,
202
- attention_mask=toks.attention_mask,
203
- output_hidden_states=True
204
- )
205
-
206
- layer_index = -2
207
- last_hidden = outputs.hidden_states[layer_index]
208
- seq_len = toks.attention_mask.sum(dim=1) - 1
209
- pooled = last_hidden[torch.arange(len(last_hidden)), seq_len.clamp(min=0)]
210
- return prompt_embeds.to(dtype), mask, pooled.to(dtype)
211
-
212
-
213
- shift_factor = getattr(vae.config, "shift_factor", 0.0)
214
- if shift_factor is None: shift_factor = 0.0
215
- scaling_factor = getattr(vae.config, "scaling_factor", 1.0)
216
- if scaling_factor is None: scaling_factor = 1.0
217
-
218
- def _patchify_latents(latents):
219
- batch_size, num_channels_latents, height, width = latents.shape
220
- latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
221
- latents = latents.permute(0, 1, 3, 5, 2, 4)
222
- latents = latents.reshape(batch_size, num_channels_latents * 4, height // 2, width // 2)
223
- return latents
224
-
225
- @staticmethod
226
- def _unpatchify_latents(latents):
227
- batch_size, num_channels_latents, height, width = latents.shape
228
- latents = latents.reshape(batch_size, num_channels_latents // (2 * 2), 2, 2, height, width)
229
- latents = latents.permute(0, 1, 4, 2, 5, 3)
230
- latents = latents.reshape(batch_size, num_channels_latents // (2 * 2), height * 2, width * 2)
231
- return latents
232
-
233
- def flux_encode(vae,latents):
234
- # patch
235
- image_latents = _patchify_latents(latents)
236
- # norm
237
- latents_bn_mean = vae.bn.running_mean.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
238
- latents_bn_std = torch.sqrt(vae.bn.running_var.view(1, -1, 1, 1) + vae.config.batch_norm_eps)
239
- latents = (image_latents - latents_bn_mean) / latents_bn_std
240
- # unpatch
241
- latents = _unpatchify_latents(latents)
242
- return latents
243
-
244
- def flux_decode(vae,latents):
245
- # patch
246
- image_latents = _patchify_latents(latents)
247
- # norm
248
- latents_bn_mean = vae.bn.running_mean.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
249
- latents_bn_std = torch.sqrt(vae.bn.running_var.view(1, -1, 1, 1) + vae.config.batch_norm_eps)
250
- latents = image_latents * latents_bn_std + latents_bn_mean
251
- # unpatch
252
- latents = _unpatchify_latents(latents)
253
- return latents
254
-
255
- class DistributedResolutionBatchSampler(Sampler):
256
- def __init__(self, dataset, batch_size, num_replicas, rank, shuffle=True, drop_last=True):
257
- self.dataset = dataset
258
- self.batch_size = max(1, batch_size // num_replicas)
259
- self.num_replicas = num_replicas
260
- self.rank = rank
261
- self.shuffle = shuffle
262
- self.drop_last = drop_last
263
- self.epoch = 0
264
-
265
- try:
266
- widths = np.array(dataset["width"])
267
- heights = np.array(dataset["height"])
268
- except KeyError:
269
- widths = np.zeros(len(dataset))
270
- heights = np.zeros(len(dataset))
271
-
272
- self.size_keys = np.unique(np.stack([widths, heights], axis=1), axis=0)
273
- self.size_groups = {}
274
- for w, h in self.size_keys:
275
- mask = (widths == w) & (heights == h)
276
- self.size_groups[(w, h)] = np.where(mask)[0]
277
-
278
- self.group_num_batches = {}
279
- total_batches = 0
280
- for size, indices in self.size_groups.items():
281
- num_full_batches = len(indices) // (self.batch_size * self.num_replicas)
282
- self.group_num_batches[size] = num_full_batches
283
- total_batches += num_full_batches
284
-
285
- self.num_batches = (total_batches // self.num_replicas) * self.num_replicas
286
-
287
- def __iter__(self):
288
- if torch.cuda.is_available():
289
- torch.cuda.empty_cache()
290
- all_batches = []
291
- rng = np.random.RandomState(self.epoch)
292
-
293
- for size, indices in self.size_groups.items():
294
- indices = indices.copy()
295
- if self.shuffle:
296
- rng.shuffle(indices)
297
- num_full_batches = self.group_num_batches[size]
298
- if num_full_batches == 0:
299
- continue
300
- valid_indices = indices[:num_full_batches * self.batch_size * self.num_replicas]
301
- batches = valid_indices.reshape(-1, self.batch_size * self.num_replicas)
302
- start_idx = self.rank * self.batch_size
303
- end_idx = start_idx + self.batch_size
304
- gpu_batches = batches[:, start_idx:end_idx]
305
- all_batches.extend(gpu_batches)
306
-
307
- if self.shuffle:
308
- rng.shuffle(all_batches)
309
- accelerator.wait_for_everyone()
310
- return iter(all_batches)
311
-
312
- def __len__(self):
313
- return self.num_batches
314
-
315
- def set_epoch(self, epoch):
316
- self.epoch = epoch
317
-
318
- # --- [UPDATED] Функция для фиксированных семплов ---
319
- def get_fixed_samples_by_resolution(dataset, samples_per_group=1):
320
- size_groups = defaultdict(list)
321
- try:
322
- widths = dataset["width"]
323
- heights = dataset["height"]
324
- except KeyError:
325
- widths = [0] * len(dataset)
326
- heights = [0] * len(dataset)
327
- for i, (w, h) in enumerate(zip(widths, heights)):
328
- size = (w, h)
329
- size_groups[size].append(i)
330
-
331
- fixed_samples = {}
332
- for size, indices in size_groups.items():
333
- n_samples = min(samples_per_group, len(indices))
334
- if len(size_groups)==1:
335
- n_samples = samples_to_generate
336
- if n_samples == 0:
337
- continue
338
- sample_indices = random.sample(indices, n_samples)
339
- samples_data = [dataset[idx] for idx in sample_indices]
340
-
341
- latents = torch.tensor(np.array([item["vae"] for item in samples_data])).to(device=device, dtype=dtype)
342
- texts = [item["text"] for item in samples_data]
343
-
344
- # Кодируем тексты на лету, чтобы получить маски и пулинг
345
- embeddings, masks, pooled = encode_texts(texts)
346
-
347
- fixed_samples[size] = (latents, embeddings, masks, texts, pooled)
348
-
349
- print(f"Создано {len(fixed_samples)} групп фиксированных семплов по разрешениям")
350
- return fixed_samples
351
-
352
- if limit > 0:
353
- dataset = load_from_disk(ds_path).select(range(limit))
354
- else:
355
- dataset = load_from_disk(ds_path)
356
-
357
- dataset = dataset.filter(
358
- lambda x: [not (path.startswith("/workspace/dataset/animesfw") or path.startswith("/workspace/dataset/d4/animesfw")) for path in x["image_path"]],
359
- batched=True,
360
- batch_size=10000, # обрабатываем по 10к строк за раз
361
- num_proc=8
362
- )
363
- print(f"Осталось примеров после фильтрации: {len(dataset)}")
364
-
365
- # --- Collate Function ---
366
- def collate_fn_simple(batch):
367
- # 1. Латенты (VAE)
368
- latents = torch.tensor(np.array([item["vae"] for item in batch])).to(device, dtype=dtype)
369
-
370
- # 2. Текст берем сырой из датасета
371
- raw_texts = [item["text"] for item in batch]
372
- texts = [
373
- "" if t.lower().startswith("zero")
374
- else "" if random.random() < cfg_dropout
375
- else t[1:].lstrip() if t.startswith(".")
376
- else t.replace("The image shows ", "").replace("The image is ", "").replace("This image captures ","").strip()
377
- for t in raw_texts
378
- ]
379
- # 3. Кодируем на лету
380
- # Возвращает: hidden (B, L, D), mask (B, L)
381
- embeddings, attention_mask, pooled = encode_texts(texts)
382
-
383
- # attention_mask от токенизатора уже имеет нужный формат, но на всякий случай приведем к long
384
- attention_mask = attention_mask.to(dtype=torch.int64)
385
-
386
- return latents, embeddings, attention_mask, pooled
387
-
388
- batch_sampler = DistributedResolutionBatchSampler(
389
- dataset=dataset,
390
- batch_size=batch_size,
391
- num_replicas=accelerator.num_processes,
392
- rank=accelerator.process_index,
393
- shuffle=shuffle
394
- )
395
-
396
- dataloader = DataLoader(dataset, batch_sampler=batch_sampler, collate_fn=collate_fn_simple)
397
- if accelerator.is_main_process:
398
- print("Total samples", len(dataloader))
399
- dataloader = accelerator.prepare(dataloader)
400
-
401
- start_epoch = 0
402
- global_step = 0
403
- total_training_steps = (len(dataloader) * num_epochs)
404
- world_size = accelerator.state.num_processes
405
-
406
- # Загрузка UNet
407
- latest_checkpoint = os.path.join(checkpoints_folder, project)
408
- if os.path.isdir(latest_checkpoint):
409
- print("Загружаем UNet из чекпоинта:", latest_checkpoint)
410
- unet = UNet2DConditionModel.from_pretrained(latest_checkpoint).to(device=device, dtype=dtype)
411
- if unet_gradient:
412
- unet.enable_gradient_checkpointing()
413
- unet.set_use_memory_efficient_attention_xformers(False)
414
- try:
415
- unet.set_attn_processor(AttnProcessor2_0())
416
- except Exception as e:
417
- print(f"Ошибка при включении SDPA: {e}")
418
- unet.set_use_memory_efficient_attention_xformers(True)
419
- else:
420
- raise FileNotFoundError(f"UNet checkpoint not found at {latest_checkpoint}")
421
-
422
-
423
- def create_optimizer(name, params):
424
- if name == "adam8bit":
425
- return bnb.optim.AdamW8bit(
426
- params, lr=base_learning_rate, betas=(0.9, betta2), eps=eps, weight_decay=0.01,
427
- #percentile_clipping=percentile_clipping
428
- )
429
- elif name == "adam":
430
- return torch.optim.AdamW(
431
- params, lr=base_learning_rate, betas=(0.9, betta2), eps=1e-8, weight_decay=0.01
432
- )
433
- else:
434
- raise ValueError(f"Unknown optimizer: {name}")
435
-
436
- if fbp:
437
- trainable_params = list(unet.parameters())
438
- optimizer_dict = {p: create_optimizer(optimizer_type, [p]) for p in trainable_params}
439
- def optimizer_hook(param):
440
- optimizer_dict[param].step()
441
- optimizer_dict[param].zero_grad(set_to_none=True)
442
- for param in trainable_params:
443
- param.register_post_accumulate_grad_hook(optimizer_hook)
444
- unet, optimizer = accelerator.prepare(unet, optimizer_dict)
445
- else:
446
- # 1. Сначала замораживаем ВСЕ параметры UNet
447
- #unet.requires_grad_(False)
448
-
449
- # 2. Размораживаем только нужные
450
- #trainable_params_names = ["conv_in.weight", "conv_in.bias", "conv_out.weight", "conv_out.bias"]
451
- #train_params = []
452
-
453
- #for name, param in unet.named_parameters():
454
- # if any(target in name for target in trainable_params_names):
455
- # param.requires_grad = True
456
- # train_params.append(param)
457
- # print(f"Обучаемый слой: {name}")
458
-
459
- unet.requires_grad_(True)
460
- optimizer = create_optimizer(optimizer_type, unet.parameters())
461
-
462
- def lr_schedule(step):
463
- x = step / (total_training_steps * world_size)
464
- warmup = warmup_percent
465
- if not use_decay:
466
- return base_learning_rate
467
- if x < warmup:
468
- return min_learning_rate + (base_learning_rate - min_learning_rate) * (x / warmup)
469
- decay_ratio = (x - warmup) / (1 - warmup)
470
- return min_learning_rate + 0.5 * (base_learning_rate - min_learning_rate) * \
471
- (1 + math.cos(math.pi * decay_ratio))
472
- lr_scheduler = LambdaLR(optimizer, lambda step: lr_schedule(step) / base_learning_rate)
473
- unet, optimizer, lr_scheduler = accelerator.prepare(unet, optimizer, lr_scheduler)
474
-
475
- if torch_compile:
476
- print("compiling")
477
- unet = torch.compile(unet)
478
- print("compiling - ok")
479
-
480
- # Фиксированные семплы
481
- fixed_samples = get_fixed_samples_by_resolution(dataset)
482
-
483
- # --- [UPDATED] Функция для негативного эмбеддинга (возвращает 3 элемента) ---
484
- def get_negative_embedding(neg_prompt="", batch_size=1):
485
- if not neg_prompt:
486
- hidden_dim = 2048
487
- seq_len = max_length
488
- empty_emb = torch.zeros((batch_size, seq_len, hidden_dim), dtype=dtype, device=device)
489
- empty_mask = torch.ones((batch_size, seq_len), dtype=torch.int64, device=device)
490
- return empty_emb, empty_mask
491
-
492
- uncond_emb, uncond_mask, uncond_pooled = encode_texts([neg_prompt])
493
- uncond_emb = uncond_emb.to(dtype=dtype, device=device).repeat(batch_size, 1, 1)
494
- uncond_mask = uncond_mask.to(device=device).repeat(batch_size, 1)
495
- uncond_pooled = uncond_pooled.to(device=device).repeat(batch_size, 1)
496
-
497
- return uncond_emb, uncond_mask, uncond_pooled
498
-
499
- # Получаем негативные (пустые) условия для валидации
500
- uncond_emb, uncond_mask, uncond_pooled = get_negative_embedding("low quality")
501
-
502
- # --- Функция генерации семплов ---
503
- @torch.compiler.disable()
504
- @torch.no_grad()
505
- def generate_and_save_samples(fixed_samples_cpu, uncond_data, step):
506
- uncond_emb, uncond_mask, uncond_pooled = uncond_data
507
-
508
- original_model = None
509
- try:
510
- if not torch_compile:
511
- original_model = accelerator.unwrap_model(unet, keep_torch_compile=True).eval()
512
- else:
513
- original_model = unet.eval()
514
-
515
- vae.to(device=device).eval()
516
-
517
- all_generated_images = []
518
- all_captions = []
519
-
520
- # Распаковываем 5 элементов (добавились mask)
521
- for size, (sample_latents, sample_text_embeddings, sample_mask, sample_text, sample_pooled) in fixed_samples_cpu.items():
522
- width, height = size
523
- sample_latents = sample_latents.to(dtype=dtype, device=device)
524
- sample_text_embeddings = sample_text_embeddings.to(dtype=dtype, device=device)
525
- sample_mask = sample_mask.to(device=device)
526
- sample_pooled = sample_pooled.to(dtype=dtype, device=device)
527
-
528
- latents = torch.randn(
529
- sample_latents.shape,
530
- device=device,
531
- dtype=sample_latents.dtype,
532
- generator=torch.Generator(device=device).manual_seed(seed)
533
- )
534
-
535
- scheduler.set_timesteps(n_diffusion_steps, device=device)
536
-
537
- for t in scheduler.timesteps:
538
- if guidance_scale != 1:
539
- latent_model_input = torch.cat([latents, latents], dim=0)
540
-
541
- # Подготовка батчей для CFG (Negative + Positive)
542
- # 1. Embeddings
543
- curr_batch_size = sample_text_embeddings.shape[0]
544
- seq_len = sample_text_embeddings.shape[1]
545
- hidden_dim = sample_text_embeddings.shape[2]
546
-
547
- neg_emb_batch = uncond_emb[0:1].expand(curr_batch_size, -1, -1)
548
- text_embeddings_batch = torch.cat([neg_emb_batch, sample_text_embeddings], dim=0)
549
-
550
- # 2. Masks
551
- neg_mask_batch = uncond_mask[0:1].expand(curr_batch_size, -1)
552
- attention_mask_batch = torch.cat([neg_mask_batch, sample_mask], dim=0)
553
-
554
- neg_pooled_batch = uncond_pooled[0:1].expand(curr_batch_size, -1)
555
- attention_pooled_batch = torch.cat([neg_pooled_batch, sample_pooled], dim=0)
556
-
557
- else:
558
- latent_model_input = latents
559
- text_embeddings_batch = sample_text_embeddings
560
- attention_mask_batch = sample_mask
561
- attention_pooled_batch = sample_pooled
562
-
563
- added_cond_kwargs = {
564
- "text_embeds": attention_pooled_batch,
565
- }
566
- # Предсказание с передачей всех условий
567
- model_out = original_model(
568
- latent_model_input,
569
- t,
570
- encoder_hidden_states=text_embeddings_batch,
571
- encoder_attention_mask=attention_mask_batch,
572
- added_cond_kwargs=added_cond_kwargs,
573
- )
574
- flow = getattr(model_out, "sample", model_out)
575
-
576
- if guidance_scale != 1:
577
- flow_uncond, flow_cond = flow.chunk(2)
578
- flow = flow_uncond + guidance_scale * (flow_cond - flow_uncond)
579
-
580
- latents = scheduler.step(flow, t, latents).prev_sample
581
-
582
- current_latents = latents
583
- if step==0:
584
- current_latents = sample_latents
585
-
586
- latents = current_latents.detach() * scaling_factor + shift_factor
587
- latents = flux_decode(vae,latents)
588
- decoded = vae.decode(latents.to(torch.float32)).sample
589
- decoded_fp32 = decoded.to(torch.float32)
590
-
591
- for img_idx, img_tensor in enumerate(decoded_fp32):
592
- img = (img_tensor / 2 + 0.5).clamp(0, 1).cpu().numpy()
593
- img = img.transpose(1, 2, 0)
594
-
595
- if np.isnan(img).any():
596
- print("NaNs found, saving stopped! Step:", step)
597
- pil_img = Image.fromarray((img * 255).astype("uint8"))
598
-
599
- max_w_overall = max(s[0] for s in fixed_samples_cpu.keys())
600
- max_h_overall = max(s[1] for s in fixed_samples_cpu.keys())
601
- max_w_overall = max(255, max_w_overall)
602
- max_h_overall = max(255, max_h_overall)
603
-
604
- padded_img = ImageOps.pad(pil_img, (max_w_overall, max_h_overall), color='white')
605
- all_generated_images.append(padded_img)
606
-
607
- caption_text = sample_text[img_idx][:300] if img_idx < len(sample_text) else ""
608
- all_captions.append(caption_text)
609
-
610
- sample_path = f"{generated_folder}/{project}_{width}x{height}_{img_idx}.jpg"
611
- pil_img.save(sample_path, "JPEG", quality=96)
612
-
613
- if use_wandb and accelerator.is_main_process:
614
- wandb_images = [
615
- wandb.Image(img, caption=f"{all_captions[i]}")
616
- for i, img in enumerate(all_generated_images)
617
- ]
618
- wandb.log({"generated_images": wandb_images})
619
- if use_comet_ml and accelerator.is_main_process:
620
- for i, img in enumerate(all_generated_images):
621
- comet_experiment.log_image(
622
- image_data=img,
623
- name=f"step_{step}_img_{i}",
624
- step=step,
625
- metadata={"caption": all_captions[i]}
626
- )
627
- finally:
628
- vae.to("cpu")
629
- try:
630
- all_generated_images.clear()
631
- all_captions.clear()
632
- del all_generated_images, all_captions
633
- del latents, current_latents, latent_model_input, flow
634
- del decoded, decoded_fp32
635
- del sample_latents, sample_text_embeddings, sample_mask, sample_pooled # Копии на GPU
636
- del model_out
637
- except UnboundLocalError:
638
- pass
639
-
640
- # 3. Синхронизируем CUDA перед очисткой
641
- torch.cuda.synchronize()
642
- # 4. Теперь чистим кэш аллокатора и вызываем GC
643
- torch.cuda.empty_cache()
644
- gc.collect()
645
-
646
- # --------------------------- Генерация сэмплов перед обучением ---------------------------
647
- if accelerator.is_main_process:
648
- if save_model:
649
- print("Генерация сэмплов до старта обучения...")
650
- generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask,uncond_pooled), 0)
651
- accelerator.wait_for_everyone()
652
-
653
- def save_checkpoint(unet, variant=""):
654
- if accelerator.is_main_process:
655
- model_to_save = None
656
- if not torch_compile:
657
- model_to_save = accelerator.unwrap_model(unet)
658
- else:
659
- model_to_save = unet
660
-
661
- if variant != "":
662
- model_to_save.to(dtype=torch.float16).save_pretrained(
663
- os.path.join(checkpoints_folder, f"{project}"), variant=variant
664
- )
665
- else:
666
- model_to_save.save_pretrained(os.path.join(checkpoints_folder, f"{project}"))
667
-
668
- torch.cuda.synchronize()
669
- torch.cuda.empty_cache()
670
- gc.collect()
671
- #unet = unet.to(dtype=dtype) #TODO: wtf???
672
-
673
- # --------------------------- Тренировочный цикл ---------------------------
674
- if accelerator.is_main_process:
675
- print(f"Total steps per GPU: {total_training_steps}")
676
-
677
- epoch_loss_points = []
678
- progress_bar = tqdm(total=total_training_steps, disable=not accelerator.is_local_main_process, desc="Training", unit="step")
679
-
680
- steps_per_epoch = len(dataloader)
681
- sample_interval = max(1, steps_per_epoch // sample_interval_share)
682
- min_loss = 4.
683
-
684
- for epoch in range(start_epoch, start_epoch + num_epochs):
685
- batch_losses = []
686
- batch_grads = []
687
- batch_sampler.set_epoch(epoch)
688
- accelerator.wait_for_everyone()
689
- unet.train()
690
-
691
- for step, (latents, embeddings, attention_mask, pooled) in enumerate(dataloader):
692
- with accelerator.accumulate(unet):
693
- if save_model == False and epoch == 0 and step == 5 :
694
- used_gb = torch.cuda.max_memory_allocated() / 1024**3
695
- print(f"Шаг {step}: {used_gb:.2f} GB")
696
-
697
- # шум
698
- noise = torch.randn_like(latents, dtype=latents.dtype)
699
-
700
- # 3. Время t (сэмплим, как и раньше, но чуть сжимаем края)
701
- u = torch.rand(latents.shape[0], device=latents.device, dtype=latents.dtype)
702
- t = u * (1 - 2 * 1e-5) + 1e-5 # Теперь t строго в (0.00001 ... 0.99999)
703
- # интерполяция между x0 и шумом
704
- noisy_latents = (1.0 - t.view(-1, 1, 1, 1)) * latents + t.view(-1, 1, 1, 1) * noise
705
- # делаем integer timesteps для UNet
706
- timesteps = t.to(torch.float32).mul(999.0)
707
- timesteps = timesteps.clamp(0, scheduler.config.num_train_timesteps - 1)
708
-
709
- added_cond_kwargs = {
710
- "text_embeds": pooled,
711
- }
712
- # --- Вызов UNet с маской ---
713
- model_pred = unet(
714
- noisy_latents,
715
- timesteps,
716
- encoder_hidden_states=embeddings,
717
- encoder_attention_mask=attention_mask,
718
- added_cond_kwargs=added_cond_kwargs,
719
- ).sample
720
-
721
- target = noise - latents
722
-
723
- mse_loss = F.mse_loss(model_pred.float(), target.float())
724
- batch_losses.append(mse_loss.detach().item())
725
-
726
- if (global_step % 100 == 0) or (global_step % sample_interval == 0):
727
- accelerator.wait_for_everyone()
728
-
729
- losses_dict = {}
730
- losses_dict["mse"] = mse_loss
731
-
732
- if (global_step % 100 == 0) or (global_step % sample_interval == 0):
733
- accelerator.wait_for_everyone()
734
-
735
- accelerator.backward(mse_loss)
736
-
737
- if (global_step % 100 == 0) or (global_step % sample_interval == 0):
738
- accelerator.wait_for_everyone()
739
-
740
- grad = 0.0
741
- if not fbp:
742
- if accelerator.sync_gradients:
743
- grad_val = accelerator.clip_grad_norm_(unet.parameters(), clip_grad_norm)
744
- grad = grad_val.float().item() if torch.is_tensor(grad_val) else float(grad_val)
745
- optimizer.step()
746
- lr_scheduler.step()
747
- optimizer.zero_grad(set_to_none=True)
748
-
749
- if accelerator.sync_gradients:
750
- global_step += 1
751
- progress_bar.update(1)
752
- if accelerator.is_main_process:
753
- if fbp:
754
- current_lr = base_learning_rate
755
- else:
756
- current_lr = lr_scheduler.get_last_lr()[0]
757
- batch_grads.append(grad)
758
-
759
- log_data = {}
760
- log_data["loss_mse"] = mse_loss.detach().item()
761
- log_data["lr"] = current_lr
762
- log_data["grad"] = grad
763
- if accelerator.sync_gradients:
764
- if use_wandb:
765
- wandb.log(log_data, step=global_step)
766
- if use_comet_ml:
767
- comet_experiment.log_metrics(log_data, step=global_step)
768
-
769
- if global_step % sample_interval == 0 or global_step==50:
770
- # Передаем tuple (emb, mask) для негатива
771
- if save_model:
772
- generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask,uncond_pooled), global_step)
773
- elif epoch % 10 == 0:
774
- generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask,uncond_pooled), global_step)
775
- last_n = sample_interval
776
-
777
- if save_model:
778
- has_losses = len(batch_losses) > 0
779
- avg_sample_loss = np.mean(batch_losses[-sample_interval:]) if has_losses else 0.0
780
- last_loss = batch_losses[-1] if has_losses else 0.0
781
- max_loss = max(avg_sample_loss, last_loss)
782
- should_save = max_loss < min_loss * save_barrier
783
- print(
784
- f"Saving: {should_save} | Max: {max_loss:.4f} | "
785
- f"Last: {last_loss:.4f} | Avg: {avg_sample_loss:.4f}"
786
- )
787
- # 6. Сохранение и обновление
788
- if should_save:
789
- min_loss = max_loss
790
- save_checkpoint(unet)
791
- unet.train()
792
-
793
- if accelerator.is_main_process:
794
- avg_epoch_loss = np.mean(batch_losses) if len(batch_losses) > 0 else 0.0
795
- avg_epoch_grad = np.mean(batch_grads) if len(batch_grads) > 0 else 0.0
796
-
797
- print(f"\nЭпоха {epoch} завершена. Средний лосс: {avg_epoch_loss:.6f}")
798
- log_data_ep = {
799
- "epoch_loss": avg_epoch_loss,
800
- "epoch_grad": avg_epoch_grad,
801
- "epoch": epoch + 1,
802
- }
803
- if use_wandb:
804
- wandb.log(log_data_ep)
805
- if use_comet_ml:
806
- comet_experiment.log_metrics(log_data_ep)
807
-
808
- if accelerator.is_main_process:
809
- print("Обучение завершено! Сохраняем финальную модель...")
810
- #if save_model:
811
- save_checkpoint(unet,"fp16")
812
- if use_comet_ml:
813
- comet_experiment.end()
814
- accelerator.free_memory()
815
- if torch.distributed.is_initialized():
816
- torch.distributed.destroy_process_group()
817
-
818
- print("Готово!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
train-Copy2.py DELETED
@@ -1,843 +0,0 @@
1
- #from comet_ml import Experiment
2
- import os
3
- os.environ["NCCL_P2P_DISABLE"] = "1"
4
- # disable this on old GPU?
5
- os.environ["NCCL_IB_DISABLE"] = "1"
6
- os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
7
- import math
8
- import torch
9
- import numpy as np
10
- import matplotlib.pyplot as plt
11
- from torch.utils.data import DataLoader, Sampler
12
- from torch.utils.data.distributed import DistributedSampler
13
- from torch.optim.lr_scheduler import LambdaLR
14
- from collections import defaultdict
15
- from diffusers import UNet2DConditionModel,AutoencoderKLFlux2,FlowMatchEulerDiscreteScheduler
16
- from accelerate import Accelerator, DeepSpeedPlugin
17
- from datasets import load_from_disk
18
- from tqdm import tqdm
19
- from PIL import Image, ImageOps
20
- import wandb
21
- import random
22
- import gc
23
- from accelerate.state import DistributedType
24
- from torch.distributed import broadcast_object_list
25
- from torch.utils.checkpoint import checkpoint
26
- from diffusers.models.attention_processor import AttnProcessor2_0
27
- from datetime import datetime
28
- import bitsandbytes as bnb
29
- import torch.nn.functional as F
30
- from collections import deque
31
- from transformers import AutoTokenizer, AutoModel, Qwen2Tokenizer,Qwen3VLForConditionalGeneration
32
-
33
- # --------------------------- Параметры ---------------------------
34
- ds_path = "/workspace/sdxs-1b/datasets/ds1234_flux32"
35
- project = "unet"
36
- ## total batch (split // num `GPU)
37
- batch_size = 48
38
- base_learning_rate = 6e-6
39
- min_learning_rate = 7e-7
40
- num_epochs = 8
41
- sample_interval_share = 10
42
- cfg_dropout = 0.10
43
- max_length = 248
44
- use_wandb = False
45
- use_comet_ml = True
46
- save_model = True
47
- use_decay = True
48
- fbp = False
49
- optimizer_type = "adam8bit"
50
- torch_compile = False
51
- unet_gradient = True
52
- loss_normalize = False
53
- fixed_seed = False
54
- shuffle = True
55
- comet_ml_api_key = "Agctp26mbqnoYrrlvQuKSTk6r"
56
- comet_ml_workspace = "recoilme"
57
- torch.backends.cuda.matmul.allow_tf32 = True
58
- torch.backends.cudnn.allow_tf32 = True
59
- # Включение Flash Attention 2/SDPA #MAX_JOBS=4 pip install flash-attn --no-build-isolation
60
- torch.backends.cuda.enable_flash_sdp(True)
61
- torch.backends.cuda.enable_mem_efficient_sdp(True)
62
- torch.backends.cuda.enable_math_sdp(False) # Отключаем медленный вариант
63
- save_barrier = 1.25
64
- warmup_percent = 0.03
65
- #percentile_clipping = 95
66
- betta2 = 0.995
67
- eps = 1e-7
68
- clip_grad_norm = 1.0
69
- limit = 0
70
- checkpoints_folder = ""
71
- gradient_accumulation_steps = 1
72
- dtype = torch.float32
73
- mixed_precision = "no"
74
-
75
- # Параметры для диффузии
76
- n_diffusion_steps = 40
77
- samples_to_generate = 12
78
- guidance_scale = 4
79
-
80
- # Папки для сохранения результатов
81
- generated_folder = "samples"
82
- os.makedirs(generated_folder, exist_ok=True)
83
-
84
- # Настройка seed
85
- current_date = datetime.now()
86
- seed = int(current_date.strftime("%Y%m%d")) + 10000001
87
- if fixed_seed:
88
- torch.manual_seed(seed)
89
- np.random.seed(seed)
90
- random.seed(seed)
91
- if torch.cuda.is_available():
92
- torch.cuda.manual_seed_all(seed)
93
-
94
- accelerator = Accelerator(
95
- mixed_precision=mixed_precision,
96
- gradient_accumulation_steps=gradient_accumulation_steps
97
- )
98
- device = accelerator.device
99
-
100
- print("init")
101
-
102
- # --------------------------- Инициализация WandB ---------------------------
103
- if accelerator.is_main_process:
104
- if use_wandb:
105
- wandb.init(project=project, config={
106
- "batch_size": batch_size,
107
- "base_learning_rate": base_learning_rate,
108
- "num_epochs": num_epochs,
109
- "optimizer_type": optimizer_type,
110
- })
111
- if use_comet_ml:
112
- from comet_ml import Experiment
113
- comet_experiment = Experiment(
114
- api_key=comet_ml_api_key,
115
- project_name=project,
116
- workspace=comet_ml_workspace
117
- )
118
- hyper_params = {
119
- "batch_size": batch_size,
120
- "base_learning_rate": base_learning_rate,
121
- "num_epochs": num_epochs,
122
- }
123
- comet_experiment.log_parameters(hyper_params)
124
-
125
- # --------------------------- Загрузка моделей ---------------------------
126
- #vae = AutoencoderKL.from_pretrained("vae", torch_dtype=dtype).to("cpu").eval()
127
- #vae = AutoencoderKLFlux2.from_pretrained("black-forest-labs/FLUX.2-dev",subfolder="vae",torch_dtype=dtype).to(device).eval()
128
- #vae = AsymmetricAutoencoderKL.from_pretrained("vae",torch_dtype=dtype).to(device).eval()
129
- vae = AutoencoderKLFlux2.from_pretrained("vae", torch_dtype=dtype).to(device).eval()
130
- tokenizer = AutoTokenizer.from_pretrained("tokenizer")
131
- text_encoder = AutoModel.from_pretrained("text_encoder", torch_dtype=torch.float16).to(device).eval()
132
- tokenizer2 = Qwen2Tokenizer.from_pretrained("tokenizer2")
133
- text_encoder2 = Qwen3VLForConditionalGeneration.from_pretrained("text_encoder2", torch_dtype=torch.float16).to(device).eval()
134
- scheduler = FlowMatchEulerDiscreteScheduler.from_pretrained("scheduler")
135
-
136
- def encode_texts(texts, max_length=max_length):
137
- if texts is None:
138
- texts = [""]
139
- if isinstance(texts, str):
140
- texts = [texts]
141
-
142
- with torch.no_grad():
143
- # --- 1. CLIP Энкодер ---
144
- toks = tokenizer(
145
- texts,
146
- padding="max_length",
147
- max_length=max_length,
148
- truncation=True,
149
- return_tensors="pt"
150
- ).to(device)
151
-
152
- outputs = text_encoder(
153
- input_ids=toks.input_ids,
154
- attention_mask=toks.attention_mask,
155
- output_hidden_states=True
156
- )
157
-
158
- # 1. Берем -2 слой [Batch, Seq, Dim]
159
- hidden = outputs.hidden_states[-2]
160
-
161
- # 2. Достаем pooled вектор (последний токен) [Batch, Dim]
162
- seq_lens = toks.attention_mask.sum(dim=1) - 1
163
- pooled_clip = hidden[torch.arange(hidden.shape[0]), seq_lens.clamp(min=0)]
164
-
165
- # 3. Нормализация
166
- norm = text_encoder.text_model.final_layer_norm
167
- hidden = norm(hidden)
168
- pooled_clip = norm(pooled_clip)
169
-
170
- # 4. Объединяем в матрицу: Пулед (как 1-й токен) + остальные токены
171
- # pooled.unsqueeze(1) делает [Batch, 1, Dim]
172
- prompt_embeds = torch.cat([pooled_clip.unsqueeze(1), hidden], dim=1)
173
-
174
- # 5. Расширяем маску для нового токена (добавляем единицы спереди)
175
- ones = torch.ones((toks.attention_mask.shape[0], 1), dtype=toks.attention_mask.dtype, device=device)
176
- mask = torch.cat([ones, toks.attention_mask], dim=1)
177
-
178
- # --- 2. QWEN Энкодер (через Chat Template) ---
179
- # 1. Собираем текстовые промпты оборачивая их в Chat Template
180
- formatted_prompts = []
181
- for t in texts:
182
- messages = [{"role": "user", "content": [{"type": "text", "text": t}]}]
183
- res_text = tokenizer2.apply_chat_template(
184
- messages,
185
- add_generation_prompt=True,
186
- tokenize=False
187
- )
188
- formatted_prompts.append(res_text)
189
-
190
- # 2. Токенизируем, режем и добавляем паддинг за один раз
191
- toks = tokenizer2(
192
- formatted_prompts,
193
- padding="max_length",
194
- max_length=max_length,
195
- truncation=True,
196
- return_tensors="pt"
197
- ).to(device)
198
-
199
- # 3. Прогоняем через модель
200
- outputs = text_encoder2(
201
- input_ids=toks.input_ids,
202
- attention_mask=toks.attention_mask,
203
- output_hidden_states=True
204
- )
205
-
206
- layer_index = -2
207
- last_hidden = outputs.hidden_states[layer_index]
208
- seq_len = toks.attention_mask.sum(dim=1) - 1
209
- pooled = last_hidden[torch.arange(len(last_hidden)), seq_len.clamp(min=0)]
210
- pooled = torch.cat([pooled_clip, pooled], dim=1)
211
- return prompt_embeds.to(dtype), mask, pooled.to(dtype)
212
-
213
-
214
- shift_factor = getattr(vae.config, "shift_factor", 0.0)
215
- if shift_factor is None: shift_factor = 0.0
216
- scaling_factor = getattr(vae.config, "scaling_factor", 1.0)
217
- if scaling_factor is None: scaling_factor = 1.0
218
-
219
- def _patchify_latents(latents):
220
- batch_size, num_channels_latents, height, width = latents.shape
221
- latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
222
- latents = latents.permute(0, 1, 3, 5, 2, 4)
223
- latents = latents.reshape(batch_size, num_channels_latents * 4, height // 2, width // 2)
224
- return latents
225
-
226
- @staticmethod
227
- def _unpatchify_latents(latents):
228
- batch_size, num_channels_latents, height, width = latents.shape
229
- latents = latents.reshape(batch_size, num_channels_latents // (2 * 2), 2, 2, height, width)
230
- latents = latents.permute(0, 1, 4, 2, 5, 3)
231
- latents = latents.reshape(batch_size, num_channels_latents // (2 * 2), height * 2, width * 2)
232
- return latents
233
-
234
- def flux_encode(vae,latents):
235
- # patch
236
- image_latents = _patchify_latents(latents)
237
- # norm
238
- latents_bn_mean = vae.bn.running_mean.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
239
- latents_bn_std = torch.sqrt(vae.bn.running_var.view(1, -1, 1, 1) + vae.config.batch_norm_eps)
240
- latents = (image_latents - latents_bn_mean) / latents_bn_std
241
- # unpatch
242
- latents = _unpatchify_latents(latents)
243
- return latents
244
-
245
- def flux_decode(vae,latents):
246
- # patch
247
- image_latents = _patchify_latents(latents)
248
- # norm
249
- latents_bn_mean = vae.bn.running_mean.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
250
- latents_bn_std = torch.sqrt(vae.bn.running_var.view(1, -1, 1, 1) + vae.config.batch_norm_eps)
251
- latents = image_latents * latents_bn_std + latents_bn_mean
252
- # unpatch
253
- latents = _unpatchify_latents(latents)
254
- return latents
255
-
256
- class DistributedResolutionBatchSampler(Sampler):
257
- def __init__(self, dataset, batch_size, num_replicas, rank, shuffle=True, drop_last=True):
258
- self.dataset = dataset
259
- self.batch_size = max(1, batch_size // num_replicas)
260
- self.num_replicas = num_replicas
261
- self.rank = rank
262
- self.shuffle = shuffle
263
- self.drop_last = drop_last
264
- self.epoch = 0
265
-
266
- try:
267
- widths = np.array(dataset["width"])
268
- heights = np.array(dataset["height"])
269
- except KeyError:
270
- widths = np.zeros(len(dataset))
271
- heights = np.zeros(len(dataset))
272
-
273
- self.size_keys = np.unique(np.stack([widths, heights], axis=1), axis=0)
274
- self.size_groups = {}
275
- for w, h in self.size_keys:
276
- mask = (widths == w) & (heights == h)
277
- self.size_groups[(w, h)] = np.where(mask)[0]
278
-
279
- self.group_num_batches = {}
280
- total_batches = 0
281
- for size, indices in self.size_groups.items():
282
- num_full_batches = len(indices) // (self.batch_size * self.num_replicas)
283
- self.group_num_batches[size] = num_full_batches
284
- total_batches += num_full_batches
285
-
286
- self.num_batches = (total_batches // self.num_replicas) * self.num_replicas
287
-
288
- def __iter__(self):
289
- if torch.cuda.is_available():
290
- torch.cuda.empty_cache()
291
- all_batches = []
292
- rng = np.random.RandomState(self.epoch)
293
-
294
- for size, indices in self.size_groups.items():
295
- indices = indices.copy()
296
- if self.shuffle:
297
- rng.shuffle(indices)
298
- num_full_batches = self.group_num_batches[size]
299
- if num_full_batches == 0:
300
- continue
301
- valid_indices = indices[:num_full_batches * self.batch_size * self.num_replicas]
302
- batches = valid_indices.reshape(-1, self.batch_size * self.num_replicas)
303
- start_idx = self.rank * self.batch_size
304
- end_idx = start_idx + self.batch_size
305
- gpu_batches = batches[:, start_idx:end_idx]
306
- all_batches.extend(gpu_batches)
307
-
308
- if self.shuffle:
309
- rng.shuffle(all_batches)
310
- accelerator.wait_for_everyone()
311
- return iter(all_batches)
312
-
313
- def __len__(self):
314
- return self.num_batches
315
-
316
- def set_epoch(self, epoch):
317
- self.epoch = epoch
318
-
319
- # --- [UPDATED] Функция для фиксированных семплов ---
320
- def get_fixed_samples_by_resolution(dataset, samples_per_group=1):
321
- size_groups = defaultdict(list)
322
- try:
323
- widths = dataset["width"]
324
- heights = dataset["height"]
325
- except KeyError:
326
- widths = [0] * len(dataset)
327
- heights = [0] * len(dataset)
328
- for i, (w, h) in enumerate(zip(widths, heights)):
329
- size = (w, h)
330
- size_groups[size].append(i)
331
-
332
- fixed_samples = {}
333
- for size, indices in size_groups.items():
334
- n_samples = min(samples_per_group, len(indices))
335
- if len(size_groups)==1:
336
- n_samples = samples_to_generate
337
- if n_samples == 0:
338
- continue
339
- sample_indices = random.sample(indices, n_samples)
340
- samples_data = [dataset[idx] for idx in sample_indices]
341
-
342
- latents = torch.tensor(np.array([item["vae"] for item in samples_data])).to(device=device, dtype=dtype)
343
- texts = [item["text"] for item in samples_data]
344
-
345
- # Кодируем тексты на лету, чтобы получить маски и пулинг
346
- embeddings, masks, pooled = encode_texts(texts)
347
-
348
- fixed_samples[size] = (latents, embeddings, masks, texts, pooled)
349
-
350
- print(f"Создано {len(fixed_samples)} групп фиксированных семплов по разрешениям")
351
- return fixed_samples
352
-
353
- if limit > 0:
354
- dataset = load_from_disk(ds_path).select(range(limit))
355
- else:
356
- dataset = load_from_disk(ds_path)
357
-
358
- dataset = dataset.filter(
359
- lambda x: [not (path.startswith("/workspace/dataset/animesfw") or path.startswith("/workspace/dataset/d4/animesfw")) for path in x["image_path"]],
360
- batched=True,
361
- batch_size=10000, # обрабатываем по 10к строк за раз
362
- num_proc=8
363
- )
364
- print(f"Осталось примеров после фильтрации: {len(dataset)}")
365
-
366
- # --- Collate Function ---
367
- def collate_fn_simple(batch):
368
- # 1. Латенты (VAE)
369
- latents = torch.tensor(np.array([item["vae"] for item in batch])).to(device, dtype=dtype)
370
-
371
- # 2. Текст берем сырой из датасета
372
- raw_texts = [item["text"] for item in batch]
373
- texts = [
374
- "" if t.lower().startswith("zero")
375
- else "" if random.random() < cfg_dropout
376
- else t[1:].lstrip() if t.startswith(".")
377
- else t.replace("The image shows ", "").replace("The image is ", "").replace("This image captures ","").strip()
378
- for t in raw_texts
379
- ]
380
- # 3. Кодируем на лету
381
- # Возвращает: hidden (B, L, D), mask (B, L)
382
- embeddings, attention_mask, pooled = encode_texts(texts)
383
-
384
- # attention_mask от токенизатора уже имеет нужный формат, но на всякий случай приведем к long
385
- attention_mask = attention_mask.to(dtype=torch.int64)
386
-
387
- return latents, embeddings, attention_mask, pooled
388
-
389
- batch_sampler = DistributedResolutionBatchSampler(
390
- dataset=dataset,
391
- batch_size=batch_size,
392
- num_replicas=accelerator.num_processes,
393
- rank=accelerator.process_index,
394
- shuffle=shuffle
395
- )
396
-
397
- dataloader = DataLoader(dataset, batch_sampler=batch_sampler, collate_fn=collate_fn_simple)
398
- if accelerator.is_main_process:
399
- print("Total samples", len(dataloader))
400
- dataloader = accelerator.prepare(dataloader)
401
-
402
- start_epoch = 0
403
- global_step = 0
404
- total_training_steps = (len(dataloader) * num_epochs)
405
- world_size = accelerator.state.num_processes
406
-
407
- # Загрузка UNet
408
- latest_checkpoint = os.path.join(checkpoints_folder, project)
409
- if os.path.isdir(latest_checkpoint):
410
- print("Загружаем UNet из чекпоинта:", latest_checkpoint)
411
- unet = UNet2DConditionModel.from_pretrained(latest_checkpoint).to(device=device, dtype=dtype)
412
- if unet_gradient:
413
- unet.enable_gradient_checkpointing()
414
- unet.set_use_memory_efficient_attention_xformers(False)
415
- try:
416
- unet.set_attn_processor(AttnProcessor2_0())
417
- except Exception as e:
418
- print(f"Ошибка при включении SDPA: {e}")
419
- unet.set_use_memory_efficient_attention_xformers(True)
420
- else:
421
- raise FileNotFoundError(f"UNet checkpoint not found at {latest_checkpoint}")
422
-
423
-
424
- def create_optimizer(name, params):
425
- if name == "adam8bit":
426
- return bnb.optim.AdamW8bit(
427
- params, lr=base_learning_rate, betas=(0.9, betta2), eps=eps, weight_decay=0.01,
428
- #percentile_clipping=percentile_clipping
429
- )
430
- elif name == "adam":
431
- return torch.optim.AdamW(
432
- params, lr=base_learning_rate, betas=(0.9, betta2), eps=1e-8, weight_decay=0.01
433
- )
434
- else:
435
- raise ValueError(f"Unknown optimizer: {name}")
436
-
437
- if fbp:
438
- trainable_params = list(unet.parameters())
439
- optimizer_dict = {p: create_optimizer(optimizer_type, [p]) for p in trainable_params}
440
- def optimizer_hook(param):
441
- optimizer_dict[param].step()
442
- optimizer_dict[param].zero_grad(set_to_none=True)
443
- for param in trainable_params:
444
- param.register_post_accumulate_grad_hook(optimizer_hook)
445
- unet, optimizer = accelerator.prepare(unet, optimizer_dict)
446
- else:
447
- # 1. Сначала замораживаем ВСЕ параметры UNet
448
- #unet.requires_grad_(False)
449
-
450
- # 2. Размораживаем только нужные
451
- #trainable_params_names = ["conv_in.weight", "conv_in.bias", "conv_out.weight", "conv_out.bias"]
452
- #train_params = []
453
-
454
- #for name, param in unet.named_parameters():
455
- # if any(target in name for target in trainable_params_names):
456
- # param.requires_grad = True
457
- # train_params.append(param)
458
- # print(f"Обучаемый слой: {name}")
459
-
460
- unet.requires_grad_(True)
461
- optimizer = create_optimizer(optimizer_type, unet.parameters())
462
-
463
- def lr_schedule(step):
464
- x = step / (total_training_steps * world_size)
465
- warmup = warmup_percent
466
- if not use_decay:
467
- return base_learning_rate
468
- if x < warmup:
469
- return min_learning_rate + (base_learning_rate - min_learning_rate) * (x / warmup)
470
- decay_ratio = (x - warmup) / (1 - warmup)
471
- return min_learning_rate + 0.5 * (base_learning_rate - min_learning_rate) * \
472
- (1 + math.cos(math.pi * decay_ratio))
473
- lr_scheduler = LambdaLR(optimizer, lambda step: lr_schedule(step) / base_learning_rate)
474
- unet, optimizer, lr_scheduler = accelerator.prepare(unet, optimizer, lr_scheduler)
475
-
476
- if torch_compile:
477
- print("compiling")
478
- unet = torch.compile(unet)
479
- print("compiling - ok")
480
-
481
- # Фиксированные семплы
482
- fixed_samples = get_fixed_samples_by_resolution(dataset)
483
-
484
- # --- [UPDATED] Функция для негативного эмбеддинга (возвращает 3 элемента) ---
485
- def get_negative_embedding(neg_prompt="", batch_size=1):
486
- if not neg_prompt:
487
- hidden_dim = 2048
488
- seq_len = max_length
489
- empty_emb = torch.zeros((batch_size, seq_len, hidden_dim), dtype=dtype, device=device)
490
- empty_mask = torch.ones((batch_size, seq_len), dtype=torch.int64, device=device)
491
- return empty_emb, empty_mask
492
-
493
- uncond_emb, uncond_mask, uncond_pooled = encode_texts([neg_prompt])
494
- uncond_emb = uncond_emb.to(dtype=dtype, device=device).repeat(batch_size, 1, 1)
495
- uncond_mask = uncond_mask.to(device=device).repeat(batch_size, 1)
496
- uncond_pooled = uncond_pooled.to(device=device).repeat(batch_size, 1)
497
-
498
- return uncond_emb, uncond_mask, uncond_pooled
499
-
500
- # Получаем негативные (пустые) условия для валидации
501
- uncond_emb, uncond_mask, uncond_pooled = get_negative_embedding("low quality")
502
-
503
- # --- Функция генерации семплов ---
504
- @torch.compiler.disable()
505
- @torch.no_grad()
506
- def generate_and_save_samples(fixed_samples_cpu, uncond_data, step):
507
- uncond_emb, uncond_mask, uncond_pooled = uncond_data
508
-
509
- original_model = None
510
- try:
511
- if not torch_compile:
512
- original_model = accelerator.unwrap_model(unet, keep_torch_compile=True).eval()
513
- else:
514
- original_model = unet.eval()
515
-
516
- vae.to(device=device).eval()
517
-
518
- all_generated_images = []
519
- all_captions = []
520
-
521
- # Распаковываем 5 элементов (добавились mask)
522
- for size, (sample_latents, sample_text_embeddings, sample_mask, sample_text, sample_pooled) in fixed_samples_cpu.items():
523
- width, height = size
524
- sample_latents = sample_latents.to(dtype=dtype, device=device)
525
- sample_text_embeddings = sample_text_embeddings.to(dtype=dtype, device=device)
526
- sample_mask = sample_mask.to(device=device)
527
- sample_pooled = sample_pooled.to(dtype=dtype, device=device)
528
-
529
- latents = torch.randn(
530
- sample_latents.shape,
531
- device=device,
532
- dtype=sample_latents.dtype,
533
- generator=torch.Generator(device=device).manual_seed(seed)
534
- )
535
-
536
- scheduler.set_timesteps(n_diffusion_steps, device=device)
537
-
538
- time_ids = torch.zeros(
539
- sample_pooled.shape[0], # ← вот это главное
540
- 6,
541
- device=device,
542
- dtype=torch.long
543
- )
544
-
545
- for t in scheduler.timesteps:
546
- if guidance_scale != 1:
547
- latent_model_input = torch.cat([latents, latents], dim=0)
548
-
549
- curr_batch_size = sample_text_embeddings.shape[0]
550
- seq_len = sample_text_embeddings.shape[1]
551
- hidden_dim = sample_text_embeddings.shape[2]
552
-
553
- neg_emb_batch = uncond_emb[0:1].expand(curr_batch_size, -1, -1)
554
- text_embeddings_batch = torch.cat([neg_emb_batch, sample_text_embeddings], dim=0)
555
-
556
- neg_mask_batch = uncond_mask[0:1].expand(curr_batch_size, -1)
557
- attention_mask_batch = torch.cat([neg_mask_batch, sample_mask], dim=0)
558
-
559
- neg_pooled_batch = uncond_pooled[0:1].expand(curr_batch_size, -1)
560
- pooled_batch = torch.cat([neg_pooled_batch, sample_pooled], dim=0)
561
-
562
- # ← КЛЮЧЕВОЕ ИСПРАВЛЕНИЕ — time_ids под текущий удвоенный батч!
563
- time_ids = torch.zeros(
564
- pooled_batch.shape[0], # 2 * curr_batch_size при CFG
565
- 6,
566
- device=device,
567
- dtype=torch.long
568
- )
569
-
570
- else:
571
- latent_model_input = latents
572
- text_embeddings_batch = sample_text_embeddings
573
- attention_mask_batch = sample_mask
574
- pooled_batch = sample_pooled
575
-
576
- time_ids = torch.zeros(
577
- pooled_batch.shape[0],
578
- 6,
579
- device=device,
580
- dtype=torch.long
581
- )
582
-
583
- # Теперь всё имеет одинаковый batch size
584
- model_out = original_model(
585
- latent_model_input,
586
- t,
587
- encoder_hidden_states=text_embeddings_batch,
588
- encoder_attention_mask=attention_mask_batch,
589
- added_cond_kwargs={
590
- "text_embeds": pooled_batch,
591
- "time_ids": time_ids
592
- },
593
- )
594
-
595
- flow = getattr(model_out, "sample", model_out)
596
-
597
- if guidance_scale != 1:
598
- flow_uncond, flow_cond = flow.chunk(2)
599
- flow = flow_uncond + guidance_scale * (flow_cond - flow_uncond)
600
-
601
- latents = scheduler.step(flow, t, latents).prev_sample
602
-
603
- current_latents = latents
604
- if step==0:
605
- current_latents = sample_latents
606
-
607
- latents = current_latents.detach() * scaling_factor + shift_factor
608
- latents = flux_decode(vae,latents)
609
- decoded = vae.decode(latents.to(torch.float32)).sample
610
- decoded_fp32 = decoded.to(torch.float32)
611
-
612
- for img_idx, img_tensor in enumerate(decoded_fp32):
613
- img = (img_tensor / 2 + 0.5).clamp(0, 1).cpu().numpy()
614
- img = img.transpose(1, 2, 0)
615
-
616
- if np.isnan(img).any():
617
- print("NaNs found, saving stopped! Step:", step)
618
- pil_img = Image.fromarray((img * 255).astype("uint8"))
619
-
620
- max_w_overall = max(s[0] for s in fixed_samples_cpu.keys())
621
- max_h_overall = max(s[1] for s in fixed_samples_cpu.keys())
622
- max_w_overall = max(255, max_w_overall)
623
- max_h_overall = max(255, max_h_overall)
624
-
625
- padded_img = ImageOps.pad(pil_img, (max_w_overall, max_h_overall), color='white')
626
- all_generated_images.append(padded_img)
627
-
628
- caption_text = sample_text[img_idx][:300] if img_idx < len(sample_text) else ""
629
- all_captions.append(caption_text)
630
-
631
- sample_path = f"{generated_folder}/{project}_{width}x{height}_{img_idx}.jpg"
632
- pil_img.save(sample_path, "JPEG", quality=96)
633
-
634
- if use_wandb and accelerator.is_main_process:
635
- wandb_images = [
636
- wandb.Image(img, caption=f"{all_captions[i]}")
637
- for i, img in enumerate(all_generated_images)
638
- ]
639
- wandb.log({"generated_images": wandb_images})
640
- if use_comet_ml and accelerator.is_main_process:
641
- for i, img in enumerate(all_generated_images):
642
- comet_experiment.log_image(
643
- image_data=img,
644
- name=f"step_{step}_img_{i}",
645
- step=step,
646
- metadata={"caption": all_captions[i]}
647
- )
648
- finally:
649
- vae.to("cpu")
650
- try:
651
- all_generated_images.clear()
652
- all_captions.clear()
653
- del all_generated_images, all_captions
654
- del latents, current_latents, latent_model_input, flow
655
- del decoded, decoded_fp32
656
- del sample_latents, sample_text_embeddings, sample_mask, sample_pooled # Копии на GPU
657
- del model_out
658
- except UnboundLocalError:
659
- pass
660
-
661
- # 3. Синхронизируем CUDA перед очисткой
662
- torch.cuda.synchronize()
663
- # 4. Теперь чистим кэш аллокатора и вызываем GC
664
- torch.cuda.empty_cache()
665
- gc.collect()
666
-
667
- # --------------------------- Генерация сэмплов перед обучением ---------------------------
668
- if accelerator.is_main_process:
669
- if save_model:
670
- print("Генерация сэмплов до старта обучения...")
671
- generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask,uncond_pooled), 0)
672
- accelerator.wait_for_everyone()
673
-
674
- def save_checkpoint(unet, variant=""):
675
- if accelerator.is_main_process:
676
- model_to_save = None
677
- if not torch_compile:
678
- model_to_save = accelerator.unwrap_model(unet)
679
- else:
680
- model_to_save = unet
681
-
682
- if variant != "":
683
- model_to_save.to(dtype=torch.float16).save_pretrained(
684
- os.path.join(checkpoints_folder, f"{project}"), variant=variant
685
- )
686
- else:
687
- model_to_save.save_pretrained(os.path.join(checkpoints_folder, f"{project}"))
688
-
689
- torch.cuda.synchronize()
690
- torch.cuda.empty_cache()
691
- gc.collect()
692
- #unet = unet.to(dtype=dtype) #TODO: wtf???
693
-
694
- # --------------------------- Тренировочный цикл ---------------------------
695
- if accelerator.is_main_process:
696
- print(f"Total steps per GPU: {total_training_steps}")
697
-
698
- epoch_loss_points = []
699
- progress_bar = tqdm(total=total_training_steps, disable=not accelerator.is_local_main_process, desc="Training", unit="step")
700
-
701
- steps_per_epoch = len(dataloader)
702
- sample_interval = max(1, steps_per_epoch // sample_interval_share)
703
- min_loss = 4.
704
-
705
- for epoch in range(start_epoch, start_epoch + num_epochs):
706
- batch_losses = []
707
- batch_grads = []
708
- batch_sampler.set_epoch(epoch)
709
- accelerator.wait_for_everyone()
710
- unet.train()
711
-
712
- for step, (latents, embeddings, attention_mask, pooled) in enumerate(dataloader):
713
- with accelerator.accumulate(unet):
714
- if save_model == False and epoch == 0 and step == 5 :
715
- used_gb = torch.cuda.max_memory_allocated() / 1024**3
716
- print(f"Шаг {step}: {used_gb:.2f} GB")
717
-
718
- # шум
719
- noise = torch.randn_like(latents, dtype=latents.dtype)
720
-
721
- # 3. Время t (сэмплим, как и раньше, но чуть сжимаем края)
722
- u = torch.rand(latents.shape[0], device=latents.device, dtype=latents.dtype)
723
- t = u * (1 - 2 * 1e-5) + 1e-5 # Теперь t строго в (0.00001 ... 0.99999)
724
- # интерполяция между x0 и шумом
725
- noisy_latents = (1.0 - t.view(-1, 1, 1, 1)) * latents + t.view(-1, 1, 1, 1) * noise
726
- # делаем integer timesteps для UNet
727
- timesteps = t.to(torch.float32).mul(999.0)
728
- timesteps = timesteps.clamp(0, scheduler.config.num_train_timesteps - 1)
729
-
730
- time_ids = torch.zeros(
731
- pooled.shape[0], # ← вот это главное
732
- 6,
733
- device=device,
734
- dtype=torch.long
735
- )
736
-
737
- # --- Вызов UNet с маской ---
738
- model_pred = unet(
739
- noisy_latents,
740
- timesteps,
741
- encoder_hidden_states=embeddings,
742
- encoder_attention_mask=attention_mask,
743
- added_cond_kwargs={"text_embeds": pooled,"time_ids": time_ids},
744
- ).sample
745
-
746
- target = noise - latents
747
-
748
- mse_loss = F.mse_loss(model_pred.float(), target.float())
749
- batch_losses.append(mse_loss.detach().item())
750
-
751
- if (global_step % 100 == 0) or (global_step % sample_interval == 0):
752
- accelerator.wait_for_everyone()
753
-
754
- losses_dict = {}
755
- losses_dict["mse"] = mse_loss
756
-
757
- if (global_step % 100 == 0) or (global_step % sample_interval == 0):
758
- accelerator.wait_for_everyone()
759
-
760
- accelerator.backward(mse_loss)
761
-
762
- if (global_step % 100 == 0) or (global_step % sample_interval == 0):
763
- accelerator.wait_for_everyone()
764
-
765
- grad = 0.0
766
- if not fbp:
767
- if accelerator.sync_gradients:
768
- grad_val = accelerator.clip_grad_norm_(unet.parameters(), clip_grad_norm)
769
- grad = grad_val.float().item() if torch.is_tensor(grad_val) else float(grad_val)
770
- optimizer.step()
771
- lr_scheduler.step()
772
- optimizer.zero_grad(set_to_none=True)
773
-
774
- if accelerator.sync_gradients:
775
- global_step += 1
776
- progress_bar.update(1)
777
- if accelerator.is_main_process:
778
- if fbp:
779
- current_lr = base_learning_rate
780
- else:
781
- current_lr = lr_scheduler.get_last_lr()[0]
782
- batch_grads.append(grad)
783
-
784
- log_data = {}
785
- log_data["loss_mse"] = mse_loss.detach().item()
786
- log_data["lr"] = current_lr
787
- log_data["grad"] = grad
788
- if accelerator.sync_gradients:
789
- if use_wandb:
790
- wandb.log(log_data, step=global_step)
791
- if use_comet_ml:
792
- comet_experiment.log_metrics(log_data, step=global_step)
793
-
794
- if global_step % sample_interval == 0 or global_step==50:
795
- # Передаем tuple (emb, mask) для негатива
796
- if save_model:
797
- generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask,uncond_pooled), global_step)
798
- elif epoch % 10 == 0:
799
- generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask,uncond_pooled), global_step)
800
- last_n = sample_interval
801
-
802
- if save_model:
803
- has_losses = len(batch_losses) > 0
804
- avg_sample_loss = np.mean(batch_losses[-sample_interval:]) if has_losses else 0.0
805
- last_loss = batch_losses[-1] if has_losses else 0.0
806
- max_loss = max(avg_sample_loss, last_loss)
807
- should_save = max_loss < min_loss * save_barrier
808
- print(
809
- f"Saving: {should_save} | Max: {max_loss:.4f} | "
810
- f"Last: {last_loss:.4f} | Avg: {avg_sample_loss:.4f}"
811
- )
812
- # 6. Сохранение и обновление
813
- if should_save:
814
- min_loss = max_loss
815
- save_checkpoint(unet)
816
- unet.train()
817
-
818
- if accelerator.is_main_process:
819
- avg_epoch_loss = np.mean(batch_losses) if len(batch_losses) > 0 else 0.0
820
- avg_epoch_grad = np.mean(batch_grads) if len(batch_grads) > 0 else 0.0
821
-
822
- print(f"\nЭпоха {epoch} завершена. Средний лосс: {avg_epoch_loss:.6f}")
823
- log_data_ep = {
824
- "epoch_loss": avg_epoch_loss,
825
- "epoch_grad": avg_epoch_grad,
826
- "epoch": epoch + 1,
827
- }
828
- if use_wandb:
829
- wandb.log(log_data_ep)
830
- if use_comet_ml:
831
- comet_experiment.log_metrics(log_data_ep)
832
-
833
- if accelerator.is_main_process:
834
- print("Обучение завершено! Сохраняем финальную модель...")
835
- #if save_model:
836
- save_checkpoint(unet,"fp16")
837
- if use_comet_ml:
838
- comet_experiment.end()
839
- accelerator.free_memory()
840
- if torch.distributed.is_initialized():
841
- torch.distributed.destroy_process_group()
842
-
843
- print("Готово!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
{unet0 → unet}/diffusion_pytorch_model.fp16.safetensors RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:a06d8280ec3d97a35a0cb8bdad5f3d308d899f519ddf931a6af3573171f48a56
3
- size 2973375056
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:863dec221a0c36998de31664f313d0600c27ced6cbd742dae7bb8566008d1c0c
3
+ size 2980309336
unet/diffusion_pytorch_model.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:20df58d34704ec4bf5dd388944f0f799e0f758b6d9e48cc472ee473cdb736b88
3
- size 5960474736
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:863dec221a0c36998de31664f313d0600c27ced6cbd742dae7bb8566008d1c0c
3
+ size 2980309336
unet0/config.json DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:0c10e81c0737a03d3518c2d3034358b8aec858e40b021ae637fe3b8c44d26ec4
3
- size 1879
 
 
 
 
unet0/diffusion_pytorch_model.safetensors DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:baf0a91864ebdfbdb646c7317c32827fe6514197e89d05057427483760e569f0
3
- size 5946605448
 
 
 
 
unet1.5b-2TE-text-Copy1.ipynb DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:256e5ab08659d487f86da10282a658a1876ab12e703cbe8705e76b9abee8e0ac
3
- size 44131
 
 
 
 
unet1.5b-2TE-text-Copy2.ipynb DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:a50e57ca24db3eabcd9f3205c2e38d4d52e919b206146a6f86fc326849f9b15f
3
- size 47714