recoilme commited on
Commit
8d9d082
·
1 Parent(s): 7e448d9
dataset.py CHANGED
@@ -20,15 +20,15 @@ from datetime import timedelta
20
  # ---------------- 1️⃣ Настройки ----------------
21
  dtype = torch.float32
22
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
23
- batch_size = 5
24
  min_size = 192 #384 #320 #192 #256 #192
25
  max_size = 320 #768 #640 #384 #256 #384
26
  step = 64 #64
27
  empty_share = 0.0
28
  limit = 0
29
  # Основная процедура обработки
30
- folder_path = "/workspace/butterfly" #alchemist"
31
- save_path = "/workspace/sdxs/datasets/butterfly" #"alchemist"
32
  os.makedirs(save_path, exist_ok=True)
33
 
34
  # Функция для очистки CUDA памяти
 
20
  # ---------------- 1️⃣ Настройки ----------------
21
  dtype = torch.float32
22
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
23
+ batch_size = 10
24
  min_size = 192 #384 #320 #192 #256 #192
25
  max_size = 320 #768 #640 #384 #256 #384
26
  step = 64 #64
27
  empty_share = 0.0
28
  limit = 0
29
  # Основная процедура обработки
30
+ folder_path = "/workspace/mjnj" #alchemist"
31
+ save_path = "/workspace/sdxs/datasets/mjnj" #"alchemist"
32
  os.makedirs(save_path, exist_ok=True)
33
 
34
  # Функция для очистки CUDA памяти
samples/sdxs_08b_192x320_0.jpg ADDED

Git LFS Details

  • SHA256: c137dc9ae273d5ad50b5bf4775cebc5647a9ce068af9fb84d8780d245999878f
  • Pointer size: 130 Bytes
  • Size of remote file: 34.1 kB
samples/sdxs_08b_256x320_0.jpg ADDED

Git LFS Details

  • SHA256: d5bf5d75e7fa1d41589c11d2293a61b3233d5b9225af356101c322b9afeb6b7c
  • Pointer size: 130 Bytes
  • Size of remote file: 13.2 kB
samples/sdxs_08b_320x192_0.jpg ADDED

Git LFS Details

  • SHA256: 1e79df0301aec73ea63a248d8820d99dd10330fa4990820ebc641421796bb49f
  • Pointer size: 130 Bytes
  • Size of remote file: 32.4 kB
samples/sdxs_08b_320x256_0.jpg ADDED

Git LFS Details

  • SHA256: 6420857630c8cc49b6a8827cb1beab3a9a16d46a95af3acc4bc4823a2bbd3b43
  • Pointer size: 130 Bytes
  • Size of remote file: 29 kB
samples/sdxs_08b_320x320_0.jpg ADDED

Git LFS Details

  • SHA256: fd00d15ae13cf563b9f510e7aa3c0b03d1f9e7bd127c13163fa87f88e4adb55d
  • Pointer size: 130 Bytes
  • Size of remote file: 52.3 kB
{sdxs_07b → sdxs_08b}/config.json RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:df4c40dd0e8901aa712ac7e2553961071eb3435a75e5b25626b2a9e0599bfa37
3
- size 1863
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f4c96435f2980db8743704e9361889fb5df8c50443518f76cfe966e8dfc9dc53
3
+ size 1803
{sdxs_07b → sdxs_08b}/diffusion_pytorch_model.safetensors RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:65cc370e3b4294985fb11e1e1f2714754d290aec93d86e94260faa72b832a2ee
3
- size 3195105928
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f74021d28806f03df406d8e9e5a9e78d3ecbc3dc4e508a8f594282f04e17a964
3
+ size 3376002424
sdxs_08b/train.py ADDED
@@ -0,0 +1,798 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #from comet_ml import Experiment
2
+ import os
3
+ import math
4
+ import torch
5
+ import numpy as np
6
+ import matplotlib.pyplot as plt
7
+ from torch.utils.data import DataLoader, Sampler
8
+ from torch.utils.data.distributed import DistributedSampler
9
+ from torch.optim.lr_scheduler import LambdaLR
10
+ from collections import defaultdict
11
+ from diffusers import UNet2DConditionModel, AutoencoderKL,AutoencoderKLFlux2
12
+ from accelerate import Accelerator
13
+ from datasets import load_from_disk
14
+ from tqdm import tqdm
15
+ from PIL import Image, ImageOps
16
+ import wandb
17
+ import random
18
+ import gc
19
+ from accelerate.state import DistributedType
20
+ from torch.distributed import broadcast_object_list
21
+ from torch.utils.checkpoint import checkpoint
22
+ from diffusers.models.attention_processor import AttnProcessor2_0
23
+ from datetime import datetime
24
+ import bitsandbytes as bnb
25
+ import torch.nn.functional as F
26
+ from collections import deque
27
+ from transformers import AutoTokenizer, AutoModel
28
+
29
+ # --------------------------- Параметры ---------------------------
30
+ ds_path = "/workspace/sdxs/datasets/mjnj"
31
+ project = "sdxs_08b"
32
+ batch_size = 128
33
+ base_learning_rate = 4e-5 #2.7e-5
34
+ min_learning_rate = 1e-5 #2.7e-5
35
+ num_epochs = 15
36
+ sample_interval_share = 3
37
+ cfg_dropout = 0.25
38
+ max_length = 192
39
+ use_wandb = True
40
+ use_comet_ml = False
41
+ save_model = True
42
+ use_decay = True
43
+ fbp = False
44
+ optimizer_type = "adam8bit"
45
+ torch_compile = False
46
+ unet_gradient = True
47
+ fixed_seed = False
48
+ shuffle = True
49
+ comet_ml_api_key = "Agctp26mbqnoYrrlvQuKSTk6r"
50
+ comet_ml_workspace = "recoilme"
51
+ torch.backends.cuda.matmul.allow_tf32 = True
52
+ torch.backends.cudnn.allow_tf32 = True
53
+ torch.backends.cuda.enable_mem_efficient_sdp(False)
54
+ dtype = torch.float32
55
+ save_barrier = 1.01
56
+ warmup_percent = 0.01
57
+ percentile_clipping = 95 #96 #97
58
+ betta2 = 0.995
59
+ eps = 1e-7
60
+ clip_grad_norm = 1.0
61
+ limit = 0
62
+ checkpoints_folder = ""
63
+ mixed_precision = "no"
64
+ gradient_accumulation_steps = 1
65
+
66
+ accelerator = Accelerator(
67
+ mixed_precision=mixed_precision,
68
+ gradient_accumulation_steps=gradient_accumulation_steps
69
+ )
70
+ device = accelerator.device
71
+
72
+ # Параметры для диффузии
73
+ n_diffusion_steps = 40
74
+ samples_to_generate = 12
75
+ guidance_scale = 4
76
+
77
+ # Папки для сохранения результатов
78
+ generated_folder = "samples"
79
+ os.makedirs(generated_folder, exist_ok=True)
80
+
81
+ # Настройка seed
82
+ current_date = datetime.now()
83
+ seed = int(current_date.strftime("%Y%m%d"))
84
+ if fixed_seed:
85
+ torch.manual_seed(seed)
86
+ np.random.seed(seed)
87
+ random.seed(seed)
88
+ if torch.cuda.is_available():
89
+ torch.cuda.manual_seed_all(seed)
90
+
91
+ # --------------------------- Параметры LoRA ---------------------------
92
+ lora_name = ""
93
+ lora_rank = 32
94
+ lora_alpha = 64
95
+
96
+ print("init")
97
+
98
+ loss_ratios = {
99
+ "mse": 1.5,
100
+ "mae": 0.5,
101
+ }
102
+ median_coeff_steps = 256
103
+
104
+ # Нормализация лоссов по медианам: считаем КОЭФФИЦИЕНТЫ
105
+ class MedianLossNormalizer:
106
+ def __init__(self, desired_ratios: dict, window_steps: int):
107
+ # нормируем доли на случай, если сумма != 1
108
+ #s = sum(desired_ratios.values())
109
+ #self.ratios = {k: (v / s) for k, v in desired_ratios.items()}
110
+ self.ratios = {k: float(v) for k, v in desired_ratios.items()}
111
+ self.buffers = {k: deque(maxlen=window_steps) for k in self.ratios.keys()}
112
+ self.window = window_steps
113
+
114
+ def update_and_total(self, losses: dict):
115
+ """
116
+ losses: dict ключ->тензор (значения лоссов)
117
+ Поведение:
118
+ - буферим ABS(l) только для активных (ratio>0) лоссов
119
+ - coeff = ratio / median(abs(loss))
120
+ - total = sum(coeff * loss) по активным лоссам
121
+ CHANGED: буферим abs() — чтобы медиана была положительной и не ломала деление.
122
+ """
123
+ # буферим только активные лоссы
124
+ for k, v in losses.items():
125
+ if k in self.buffers and self.ratios.get(k, 0) > 0:
126
+ val = v.detach().abs().mean().cpu().item() # .item() лучше float() для тензоров
127
+ self.buffers[k].append(val)
128
+ #self.buffers[k].append(float(v.detach().abs().cpu()))
129
+
130
+ meds = {k: (np.median(self.buffers[k]) if len(self.buffers[k]) > 0 else 1.0) for k in self.buffers}
131
+ coeffs = {k: (self.ratios[k] / max(meds[k], 1e-12)) for k in self.ratios}
132
+
133
+ # суммируем только по активным (ratio>0)
134
+ total = sum(coeffs[k] * losses[k] for k in coeffs if self.ratios.get(k, 0) > 0)
135
+ return total, coeffs, meds
136
+
137
+ # создаём normalizer после определения loss_ratios
138
+ normalizer = MedianLossNormalizer(loss_ratios, median_coeff_steps)
139
+
140
+ # --------------------------- Инициализация WandB ---------------------------
141
+ if accelerator.is_main_process:
142
+ if use_wandb:
143
+ wandb.init(project=project+lora_name, config={
144
+ "batch_size": batch_size,
145
+ "base_learning_rate": base_learning_rate,
146
+ "num_epochs": num_epochs,
147
+ "optimizer_type": optimizer_type,
148
+ })
149
+ if use_comet_ml:
150
+ from comet_ml import Experiment
151
+ comet_experiment = Experiment(
152
+ api_key=comet_ml_api_key,
153
+ project_name=project,
154
+ workspace=comet_ml_workspace
155
+ )
156
+ hyper_params = {
157
+ "batch_size": batch_size,
158
+ "base_learning_rate": base_learning_rate,
159
+ "num_epochs": num_epochs,
160
+ }
161
+ comet_experiment.log_parameters(hyper_params)
162
+
163
+ # Включение Flash Attention 2/SDPA
164
+ torch.backends.cuda.enable_flash_sdp(True)
165
+
166
+ # --------------------------- Загрузка моделей ---------------------------
167
+ vae = AutoencoderKL.from_pretrained("vae1x", torch_dtype=dtype).to("cpu").eval()
168
+ #vae = AutoencoderKLFlux2.from_pretrained("black-forest-labs/FLUX.2-dev",subfolder="vae",torch_dtype=dtype).to(device).eval()
169
+ tokenizer = AutoTokenizer.from_pretrained("tokenizer")
170
+ text_model = AutoModel.from_pretrained("text_encoder").to(device).eval()
171
+
172
+ # --- [UPDATED] Функция кодирования текста (с маской и пулингом) ---
173
+ def encode_texts(texts, max_length=max_length):
174
+ # Если тексты пустые (для unconditional), создаем заглушки
175
+ if texts is None:
176
+ # В случае None возвращаем нули (логика для get_negative_embedding)
177
+ # Но здесь мы обычно ожидаем список строк.
178
+ pass
179
+
180
+ with torch.no_grad():
181
+ if isinstance(texts, str):
182
+ texts = [texts]
183
+
184
+ for i, prompt_item in enumerate(texts):
185
+ messages = [
186
+ {"role": "user", "content": prompt_item},
187
+ ]
188
+ prompt_item = tokenizer.apply_chat_template(
189
+ messages,
190
+ tokenize=False,
191
+ add_generation_prompt=True,
192
+ #enable_thinking=True,
193
+ )
194
+ #print(prompt_item+"\n")
195
+ texts[i] = prompt_item
196
+
197
+ toks = tokenizer(
198
+ texts,
199
+ return_tensors="pt",
200
+ padding="max_length",
201
+ truncation=True,
202
+ max_length=max_length
203
+ ).to(device)
204
+
205
+ outs = text_model(**toks, output_hidden_states=True, return_dict=True)
206
+
207
+ # Используем last_hidden_state или hidden_states[-1] (если Qwen, лучше last_hidden_state - прим человека: ХУЙ)
208
+ hidden = outs.hidden_states[-2]
209
+
210
+ # 2. Маска внимания
211
+ attention_mask = toks["attention_mask"]
212
+
213
+ # 3. Пулинг-эмбеддинг (Последний токен)
214
+ sequence_lengths = attention_mask.sum(dim=1) - 1
215
+ batch_size = hidden.shape[0]
216
+ pooled = hidden[torch.arange(batch_size, device=hidden.device), sequence_lengths]
217
+
218
+ #return hidden, attention_mask
219
+ # --- НОВАЯ ЛОГИКА: ОБЪЕДИНЕНИЕ ДЛЯ КРОСС-ВНИМАНИЯ ---
220
+ # 1. Расширяем пулинг-вектор до последовательности [B, 1, emb]
221
+ pooled_expanded = pooled.unsqueeze(1)
222
+
223
+ # 2. Объединяем последовательность токенов и пулинг-вектор
224
+ # !!! ИЗМЕНЕНИЕ ЗДЕСЬ !!!: Пулинг идет ПЕРВЫМ
225
+ # Теперь: [B, 1 + L, emb]. Пулинг стал токеном в НАЧАЛЕ.
226
+ new_encoder_hidden_states = torch.cat([pooled_expanded, hidden], dim=1)
227
+
228
+ # 3. Обновляем маску внимания для нового токена
229
+ # Маска внимания: [B, 1 + L]. Добавляем 1 в НАЧАЛО.
230
+ # torch.ones((batch_size, 1), device=device) создает маску [B, 1] со значениями 1.
231
+ new_attention_mask = torch.cat([torch.ones((batch_size, 1), device=device), attention_mask], dim=1)
232
+
233
+ return new_encoder_hidden_states, new_attention_mask
234
+
235
+ shift_factor = getattr(vae.config, "shift_factor", 0.0)
236
+ if shift_factor is None: shift_factor = 0.0
237
+ scaling_factor = getattr(vae.config, "scaling_factor", 1.0)
238
+ if scaling_factor is None: scaling_factor = 1.0
239
+
240
+ from diffusers import FlowMatchEulerDiscreteScheduler
241
+ num_train_timesteps = 1000
242
+ scheduler = FlowMatchEulerDiscreteScheduler(num_train_timesteps=num_train_timesteps)
243
+
244
+ class DistributedResolutionBatchSampler(Sampler):
245
+ def __init__(self, dataset, batch_size, num_replicas, rank, shuffle=True, drop_last=True):
246
+ self.dataset = dataset
247
+ self.batch_size = max(1, batch_size // num_replicas)
248
+ self.num_replicas = num_replicas
249
+ self.rank = rank
250
+ self.shuffle = shuffle
251
+ self.drop_last = drop_last
252
+ self.epoch = 0
253
+
254
+ try:
255
+ widths = np.array(dataset["width"])
256
+ heights = np.array(dataset["height"])
257
+ except KeyError:
258
+ widths = np.zeros(len(dataset))
259
+ heights = np.zeros(len(dataset))
260
+
261
+ self.size_keys = np.unique(np.stack([widths, heights], axis=1), axis=0)
262
+ self.size_groups = {}
263
+ for w, h in self.size_keys:
264
+ mask = (widths == w) & (heights == h)
265
+ self.size_groups[(w, h)] = np.where(mask)[0]
266
+
267
+ self.group_num_batches = {}
268
+ total_batches = 0
269
+ for size, indices in self.size_groups.items():
270
+ num_full_batches = len(indices) // (self.batch_size * self.num_replicas)
271
+ self.group_num_batches[size] = num_full_batches
272
+ total_batches += num_full_batches
273
+
274
+ self.num_batches = (total_batches // self.num_replicas) * self.num_replicas
275
+
276
+ def __iter__(self):
277
+ if torch.cuda.is_available():
278
+ torch.cuda.empty_cache()
279
+ all_batches = []
280
+ rng = np.random.RandomState(self.epoch)
281
+
282
+ for size, indices in self.size_groups.items():
283
+ indices = indices.copy()
284
+ if self.shuffle:
285
+ rng.shuffle(indices)
286
+ num_full_batches = self.group_num_batches[size]
287
+ if num_full_batches == 0:
288
+ continue
289
+ valid_indices = indices[:num_full_batches * self.batch_size * self.num_replicas]
290
+ batches = valid_indices.reshape(-1, self.batch_size * self.num_replicas)
291
+ start_idx = self.rank * self.batch_size
292
+ end_idx = start_idx + self.batch_size
293
+ gpu_batches = batches[:, start_idx:end_idx]
294
+ all_batches.extend(gpu_batches)
295
+
296
+ if self.shuffle:
297
+ rng.shuffle(all_batches)
298
+ accelerator.wait_for_everyone()
299
+ return iter(all_batches)
300
+
301
+ def __len__(self):
302
+ return self.num_batches
303
+
304
+ def set_epoch(self, epoch):
305
+ self.epoch = epoch
306
+
307
+ # --- [UPDATED] Функция для фиксированных семплов ---
308
+ def get_fixed_samples_by_resolution(dataset, samples_per_group=1):
309
+ size_groups = defaultdict(list)
310
+ try:
311
+ widths = dataset["width"]
312
+ heights = dataset["height"]
313
+ except KeyError:
314
+ widths = [0] * len(dataset)
315
+ heights = [0] * len(dataset)
316
+ for i, (w, h) in enumerate(zip(widths, heights)):
317
+ size = (w, h)
318
+ size_groups[size].append(i)
319
+
320
+ fixed_samples = {}
321
+ for size, indices in size_groups.items():
322
+ n_samples = min(samples_per_group, len(indices))
323
+ if len(size_groups)==1:
324
+ n_samples = samples_to_generate
325
+ if n_samples == 0:
326
+ continue
327
+ sample_indices = random.sample(indices, n_samples)
328
+ samples_data = [dataset[idx] for idx in sample_indices]
329
+
330
+ latents = torch.tensor(np.array([item["vae"] for item in samples_data])).to(device=device, dtype=dtype)
331
+ texts = [item["text"] for item in samples_data]
332
+
333
+ # Кодируем тексты на лету, чтобы получить маски и пулинг
334
+ embeddings, masks = encode_texts(texts)
335
+
336
+ fixed_samples[size] = (latents, embeddings, masks, texts)
337
+
338
+ print(f"Создано {len(fixed_samples)} групп фиксированных семплов по разрешениям")
339
+ return fixed_samples
340
+
341
+ if limit > 0:
342
+ dataset = load_from_disk(ds_path).select(range(limit))
343
+ else:
344
+ dataset = load_from_disk(ds_path)
345
+
346
+ dataset = dataset.filter(
347
+ lambda x: [not (path.startswith("/workspace/ds/animesfw") or path.startswith("/workspace/ds/d4/animesfw")) for path in x["image_path"]],
348
+ batched=True,
349
+ batch_size=10000, # обрабатываем по 10к строк за раз
350
+ num_proc=8
351
+ )
352
+ print(f"Осталось примеров после фильтрации: {len(dataset)}")
353
+
354
+ # --- [UPDATED] Collate Function ---
355
+ def collate_fn_simple(batch):
356
+ # 1. Латенты (VAE)
357
+ latents = torch.tensor(np.array([item["vae"] for item in batch])).to(device, dtype=dtype)
358
+
359
+ # 2. Текст берем сырой из датасета
360
+ raw_texts = [item["text"] for item in batch]
361
+ texts = [
362
+ "" if t.lower().startswith("zero")
363
+ else "" if random.random() < cfg_dropout
364
+ else t[1:].lstrip() if t.startswith(".")
365
+ else t.replace("The image shows ", "").replace("The image is ", "").replace("This image captures ","").strip()
366
+ for t in raw_texts
367
+ ]
368
+
369
+ # 3. Кодируем на лету
370
+ # Возвращает: hidden (B, L, D), mask (B, L)
371
+ embeddings, attention_mask = encode_texts(texts)
372
+
373
+ # attention_mask от токенизатора уже имеет нужный формат, но на всякий случай приведем к long
374
+ attention_mask = attention_mask.to(dtype=torch.int64)
375
+
376
+ return latents, embeddings, attention_mask
377
+
378
+ batch_sampler = DistributedResolutionBatchSampler(
379
+ dataset=dataset,
380
+ batch_size=batch_size,
381
+ num_replicas=accelerator.num_processes,
382
+ rank=accelerator.process_index,
383
+ shuffle=shuffle
384
+ )
385
+
386
+ dataloader = DataLoader(dataset, batch_sampler=batch_sampler, collate_fn=collate_fn_simple)
387
+ if accelerator.is_main_process:
388
+ print("Total samples", len(dataloader))
389
+ dataloader = accelerator.prepare(dataloader)
390
+
391
+ start_epoch = 0
392
+ global_step = 0
393
+ total_training_steps = (len(dataloader) * num_epochs)
394
+ world_size = accelerator.state.num_processes
395
+
396
+ # Загрузка UNet
397
+ latest_checkpoint = os.path.join(checkpoints_folder, project)
398
+ if os.path.isdir(latest_checkpoint):
399
+ print("Загружаем UNet из чекпоинта:", latest_checkpoint)
400
+ unet = UNet2DConditionModel.from_pretrained(latest_checkpoint).to(device=device, dtype=dtype)
401
+ if unet_gradient:
402
+ unet.enable_gradient_checkpointing()
403
+ unet.set_use_memory_efficient_attention_xformers(False)
404
+ try:
405
+ unet.set_attn_processor(AttnProcessor2_0())
406
+ except Exception as e:
407
+ print(f"Ошибка при включении SDPA: {e}")
408
+ unet.set_use_memory_efficient_attention_xformers(True)
409
+ else:
410
+ raise FileNotFoundError(f"UNet checkpoint not found at {latest_checkpoint}")
411
+
412
+ if lora_name:
413
+ # ... (Код LoRA без изменений, опущен для краткости, если не используется, иначе раскомментируйте оригинальный блок) ...
414
+ pass
415
+
416
+ # Оптимизатор
417
+ if lora_name:
418
+ trainable_params = [p for p in unet.parameters() if p.requires_grad]
419
+ else:
420
+ if fbp:
421
+ trainable_params = list(unet.parameters())
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
+ elif name == "muon":
434
+ from muon import MuonWithAuxAdam
435
+ trainable_params = [p for p in params if p.requires_grad]
436
+ hidden_weights = [p for p in trainable_params if p.ndim >= 2]
437
+ hidden_gains_biases = [p for p in trainable_params if p.ndim < 2]
438
+
439
+ param_groups = [
440
+ dict(params=hidden_weights, use_muon=True,
441
+ lr=1e-3, weight_decay=1e-4),
442
+ dict(params=hidden_gains_biases, use_muon=False,
443
+ lr=1e-4, betas=(0.9, 0.95), weight_decay=1e-4),
444
+ ]
445
+ optimizer = MuonWithAuxAdam(param_groups)
446
+ from snooc import SnooC
447
+ return SnooC(optimizer)
448
+ else:
449
+ raise ValueError(f"Unknown optimizer: {name}")
450
+
451
+ if fbp:
452
+ optimizer_dict = {p: create_optimizer(optimizer_type, [p]) for p in trainable_params}
453
+ def optimizer_hook(param):
454
+ optimizer_dict[param].step()
455
+ optimizer_dict[param].zero_grad(set_to_none=True)
456
+ for param in trainable_params:
457
+ param.register_post_accumulate_grad_hook(optimizer_hook)
458
+ unet, optimizer = accelerator.prepare(unet, optimizer_dict)
459
+ else:
460
+ optimizer = create_optimizer(optimizer_type, unet.parameters())
461
+ def lr_schedule(step):
462
+ x = step / (total_training_steps * world_size)
463
+ warmup = warmup_percent
464
+ if not use_decay:
465
+ return base_learning_rate
466
+ if x < warmup:
467
+ return min_learning_rate + (base_learning_rate - min_learning_rate) * (x / warmup)
468
+ decay_ratio = (x - warmup) / (1 - warmup)
469
+ return min_learning_rate + 0.5 * (base_learning_rate - min_learning_rate) * \
470
+ (1 + math.cos(math.pi * decay_ratio))
471
+ lr_scheduler = LambdaLR(optimizer, lambda step: lr_schedule(step) / base_learning_rate)
472
+ unet, optimizer, lr_scheduler = accelerator.prepare(unet, optimizer, lr_scheduler)
473
+
474
+ if torch_compile:
475
+ print("compiling")
476
+ unet = torch.compile(unet)
477
+ print("compiling - ok")
478
+
479
+ # Фиксированные семплы
480
+ fixed_samples = get_fixed_samples_by_resolution(dataset)
481
+
482
+ # --- [UPDATED] Функция для негативного эмбеддинга (возвращает 3 элемента) ---
483
+ def get_negative_embedding(neg_prompt="", batch_size=1):
484
+ if not neg_prompt:
485
+ hidden_dim = 2048
486
+ seq_len = max_length
487
+ empty_emb = torch.zeros((batch_size, seq_len, hidden_dim), dtype=dtype, device=device)
488
+ empty_mask = torch.ones((batch_size, seq_len), dtype=torch.int64, device=device)
489
+ return empty_emb, empty_mask
490
+
491
+ uncond_emb, uncond_mask = encode_texts([neg_prompt])
492
+ uncond_emb = uncond_emb.to(dtype=dtype, device=device).repeat(batch_size, 1, 1)
493
+ uncond_mask = uncond_mask.to(device=device).repeat(batch_size, 1)
494
+
495
+ return uncond_emb, uncond_mask
496
+
497
+ # Получаем негативные (пустые) усл��вия для валидации
498
+ uncond_emb, uncond_mask = get_negative_embedding("low quality")
499
+
500
+ # --- Функция генерации семплов ---
501
+ @torch.compiler.disable()
502
+ @torch.no_grad()
503
+ def generate_and_save_samples(fixed_samples_cpu, uncond_data, step):
504
+ uncond_emb, uncond_mask = uncond_data
505
+
506
+ original_model = None
507
+ try:
508
+ if not torch_compile:
509
+ original_model = accelerator.unwrap_model(unet, keep_torch_compile=True).eval()
510
+ else:
511
+ original_model = unet.eval()
512
+
513
+ vae.to(device=device).eval()
514
+
515
+ all_generated_images = []
516
+ all_captions = []
517
+
518
+ # Распаковываем 5 элементов (добавились mask)
519
+ for size, (sample_latents, sample_text_embeddings, sample_mask, sample_text) in fixed_samples_cpu.items():
520
+ width, height = size
521
+ sample_latents = sample_latents.to(dtype=dtype, device=device)
522
+ sample_text_embeddings = sample_text_embeddings.to(dtype=dtype, device=device)
523
+ sample_mask = sample_mask.to(device=device)
524
+
525
+ latents = torch.randn(
526
+ sample_latents.shape,
527
+ device=device,
528
+ dtype=sample_latents.dtype,
529
+ generator=torch.Generator(device=device).manual_seed(seed)
530
+ )
531
+
532
+ scheduler.set_timesteps(n_diffusion_steps, device=device)
533
+
534
+ for t in scheduler.timesteps:
535
+ if guidance_scale != 1:
536
+ latent_model_input = torch.cat([latents, latents], dim=0)
537
+
538
+ # Подготовка батчей для CFG (Negative + Positive)
539
+ # 1. Embeddings
540
+ curr_batch_size = sample_text_embeddings.shape[0]
541
+ seq_len = sample_text_embeddings.shape[1]
542
+ hidden_dim = sample_text_embeddings.shape[2]
543
+
544
+ neg_emb_batch = uncond_emb[0:1].expand(curr_batch_size, -1, -1)
545
+ text_embeddings_batch = torch.cat([neg_emb_batch, sample_text_embeddings], dim=0)
546
+
547
+ # 2. Masks
548
+ neg_mask_batch = uncond_mask[0:1].expand(curr_batch_size, -1)
549
+ attention_mask_batch = torch.cat([neg_mask_batch, sample_mask], dim=0)
550
+
551
+ else:
552
+ latent_model_input = latents
553
+ text_embeddings_batch = sample_text_embeddings
554
+ attention_mask_batch = sample_mask
555
+
556
+ # Предсказание с передачей всех условий
557
+ model_out = original_model(
558
+ latent_model_input,
559
+ t,
560
+ encoder_hidden_states=text_embeddings_batch,
561
+ encoder_attention_mask=attention_mask_batch,
562
+ )
563
+ flow = getattr(model_out, "sample", model_out)
564
+
565
+ if guidance_scale != 1:
566
+ flow_uncond, flow_cond = flow.chunk(2)
567
+ flow = flow_uncond + guidance_scale * (flow_cond - flow_uncond)
568
+
569
+ latents = scheduler.step(flow, t, latents).prev_sample
570
+
571
+ current_latents = latents
572
+ if step==0:
573
+ current_latents = sample_latents
574
+
575
+ latent_for_vae = current_latents.detach() / scaling_factor + shift_factor
576
+ decoded = vae.decode(latent_for_vae.to(torch.float32)).sample
577
+ decoded_fp32 = decoded.to(torch.float32)
578
+
579
+ for img_idx, img_tensor in enumerate(decoded_fp32):
580
+ img = (img_tensor / 2 + 0.5).clamp(0, 1).cpu().numpy()
581
+ img = img.transpose(1, 2, 0)
582
+
583
+ if np.isnan(img).any():
584
+ print("NaNs found, saving stopped! Step:", step)
585
+ pil_img = Image.fromarray((img * 255).astype("uint8"))
586
+
587
+ max_w_overall = max(s[0] for s in fixed_samples_cpu.keys())
588
+ max_h_overall = max(s[1] for s in fixed_samples_cpu.keys())
589
+ max_w_overall = max(255, max_w_overall)
590
+ max_h_overall = max(255, max_h_overall)
591
+
592
+ padded_img = ImageOps.pad(pil_img, (max_w_overall, max_h_overall), color='white')
593
+ all_generated_images.append(padded_img)
594
+
595
+ caption_text = sample_text[img_idx][:300] if img_idx < len(sample_text) else ""
596
+ all_captions.append(caption_text)
597
+
598
+ sample_path = f"{generated_folder}/{project}_{width}x{height}_{img_idx}.jpg"
599
+ pil_img.save(sample_path, "JPEG", quality=96)
600
+
601
+ if use_wandb and accelerator.is_main_process:
602
+ wandb_images = [
603
+ wandb.Image(img, caption=f"{all_captions[i]}")
604
+ for i, img in enumerate(all_generated_images)
605
+ ]
606
+ wandb.log({"generated_images": wandb_images})
607
+ if use_comet_ml and accelerator.is_main_process:
608
+ for i, img in enumerate(all_generated_images):
609
+ comet_experiment.log_image(
610
+ image_data=img,
611
+ name=f"step_{step}_img_{i}",
612
+ step=step,
613
+ metadata={"caption": all_captions[i]}
614
+ )
615
+ finally:
616
+ vae.to("cpu")
617
+ torch.cuda.empty_cache()
618
+ gc.collect()
619
+
620
+ # --------------------------- Генерация сэмплов перед обучением ---------------------------
621
+ if accelerator.is_main_process:
622
+ if save_model:
623
+ print("Генерация сэмплов до старта обучения...")
624
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), 0)
625
+ accelerator.wait_for_everyone()
626
+
627
+ def save_checkpoint(unet, variant=""):
628
+ if accelerator.is_main_process:
629
+ if lora_name:
630
+ save_lora_checkpoint(unet)
631
+ else:
632
+ model_to_save = None
633
+ if not torch_compile:
634
+ model_to_save = accelerator.unwrap_model(unet)
635
+ else:
636
+ model_to_save = unet
637
+
638
+ if variant != "":
639
+ model_to_save.to(dtype=torch.float16).save_pretrained(
640
+ os.path.join(checkpoints_folder, f"{project}"), variant=variant
641
+ )
642
+ else:
643
+ model_to_save.save_pretrained(os.path.join(checkpoints_folder, f"{project}"))
644
+
645
+ unet = unet.to(dtype=dtype)
646
+
647
+ # --------------------------- Тренировочный цикл ---------------------------
648
+ if accelerator.is_main_process:
649
+ print(f"Total steps per GPU: {total_training_steps}")
650
+
651
+ epoch_loss_points = []
652
+ progress_bar = tqdm(total=total_training_steps, disable=not accelerator.is_local_main_process, desc="Training", unit="step")
653
+
654
+ steps_per_epoch = len(dataloader)
655
+ sample_interval = max(1, steps_per_epoch // sample_interval_share)
656
+ min_loss = 4.
657
+
658
+ for epoch in range(start_epoch, start_epoch + num_epochs):
659
+ batch_losses = []
660
+ batch_grads = []
661
+ batch_sampler.set_epoch(epoch)
662
+ accelerator.wait_for_everyone()
663
+ unet.train()
664
+
665
+ for step, (latents, embeddings, attention_mask) in enumerate(dataloader):
666
+ with accelerator.accumulate(unet):
667
+ if save_model == False and epoch == 0 and step == 5 :
668
+ used_gb = torch.cuda.max_memory_allocated() / 1024**3
669
+ print(f"Шаг {step}: {used_gb:.2f} GB")
670
+
671
+ # шум
672
+ noise = torch.randn_like(latents, dtype=latents.dtype)
673
+ # берём t из [0, 1]
674
+ t = torch.rand(latents.shape[0], device=latents.device, dtype=latents.dtype)
675
+ #u = torch.rand(latents.shape[0], device=latents.device, dtype=latents.dtype)
676
+ #t = torch.sigmoid(torch.randn_like(u))
677
+
678
+ # интерполяция между x0 и шумом
679
+ noisy_latents = (1.0 - t.view(-1, 1, 1, 1)) * latents + t.view(-1, 1, 1, 1) * noise
680
+ # делаем integer timesteps для UNet
681
+ timesteps = (t * scheduler.config.num_train_timesteps).long()
682
+
683
+ # --- Вызов UNet с маской ---
684
+ model_pred = unet(
685
+ noisy_latents,
686
+ timesteps,
687
+ encoder_hidden_states=embeddings,
688
+ encoder_attention_mask=attention_mask
689
+ ).sample
690
+
691
+ target = noise - latents
692
+
693
+ mse_loss = F.mse_loss(model_pred.float(), target.float())
694
+ mae_loss = F.l1_loss(model_pred.float(), target.float())
695
+ batch_losses.append(mse_loss.detach().item())
696
+
697
+ if (global_step % 100 == 0) or (global_step % sample_interval == 0):
698
+ accelerator.wait_for_everyone()
699
+
700
+ losses_dict = {}
701
+ losses_dict["mse"] = mse_loss
702
+ losses_dict["mae"] = mae_loss
703
+
704
+ # === Нормализация всех лоссов ===
705
+ abs_for_norm = {k: losses_dict.get(k, torch.tensor(0.0, device=device)) for k in normalizer.ratios.keys()}
706
+ total_loss, coeffs, meds = normalizer.update_and_total(abs_for_norm)
707
+
708
+ if (global_step % 100 == 0) or (global_step % sample_interval == 0):
709
+ accelerator.wait_for_everyone()
710
+
711
+ accelerator.backward(total_loss)
712
+
713
+ if (global_step % 100 == 0) or (global_step % sample_interval == 0):
714
+ accelerator.wait_for_everyone()
715
+
716
+ grad = 0.0
717
+ if not fbp:
718
+ if accelerator.sync_gradients:
719
+ #with torch.amp.autocast('cuda', enabled=False):
720
+ grad_val = accelerator.clip_grad_norm_(unet.parameters(), clip_grad_norm)
721
+ grad = float(grad_val)
722
+ optimizer.step()
723
+ lr_scheduler.step()
724
+ optimizer.zero_grad(set_to_none=True)
725
+
726
+ if accelerator.sync_gradients:
727
+ global_step += 1
728
+ progress_bar.update(1)
729
+ if accelerator.is_main_process:
730
+ if fbp:
731
+ current_lr = base_learning_rate
732
+ else:
733
+ current_lr = lr_scheduler.get_last_lr()[0]
734
+ batch_grads.append(grad)
735
+
736
+ log_data = {}
737
+ log_data["loss_mse"] = mse_loss.detach().item()
738
+ log_data["loss_mae"] = mae_loss.detach().item()
739
+ log_data["lr"] = current_lr
740
+ log_data["grad"] = grad
741
+ log_data["loss_norm"] = float(total_loss.item())
742
+ for k, c in coeffs.items():
743
+ log_data[f"coeff_{k}"] = float(c)
744
+ if accelerator.sync_gradients:
745
+ if use_wandb:
746
+ wandb.log(log_data, step=global_step)
747
+ if use_comet_ml:
748
+ comet_experiment.log_metrics(log_data, step=global_step)
749
+
750
+ if global_step % sample_interval == 0:
751
+ # Передаем tuple (emb, mask) для негатива
752
+ if save_model:
753
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), global_step)
754
+ elif epoch % 10 == 0:
755
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), global_step)
756
+ last_n = sample_interval
757
+
758
+ if save_model:
759
+ has_losses = len(batch_losses) > 0
760
+ avg_sample_loss = np.mean(batch_losses[-sample_interval:]) if has_losses else 0.0
761
+ last_loss = batch_losses[-1] if has_losses else 0.0
762
+ max_loss = max(avg_sample_loss, last_loss)
763
+ should_save = max_loss < min_loss * save_barrier
764
+ print(
765
+ f"Saving: {should_save} | Max: {max_loss:.4f} | "
766
+ f"Last: {last_loss:.4f} | Avg: {avg_sample_loss:.4f}"
767
+ )
768
+ # 6. Сохранение и обновление
769
+ if should_save:
770
+ min_loss = max_loss
771
+ save_checkpoint(unet)
772
+
773
+ if accelerator.is_main_process:
774
+ avg_epoch_loss = np.mean(batch_losses) if len(batch_losses) > 0 else 0.0
775
+ avg_epoch_grad = np.mean(batch_grads) if len(batch_grads) > 0 else 0.0
776
+
777
+ print(f"\nЭпоха {epoch} завершена. Средний лосс: {avg_epoch_loss:.6f}")
778
+ log_data_ep = {
779
+ "epoch_loss": avg_epoch_loss,
780
+ "epoch_grad": avg_epoch_grad,
781
+ "epoch": epoch + 1,
782
+ }
783
+ if use_wandb:
784
+ wandb.log(log_data_ep)
785
+ if use_comet_ml:
786
+ comet_experiment.log_metrics(log_data_ep)
787
+
788
+ if accelerator.is_main_process:
789
+ print("Обучение завершено! Сохраняем финальную модель...")
790
+ #if save_model:
791
+ save_checkpoint(unet,"fp16")
792
+ if use_comet_ml:
793
+ comet_experiment.end()
794
+ accelerator.free_memory()
795
+ if torch.distributed.is_initialized():
796
+ torch.distributed.destroy_process_group()
797
+
798
+ print("Готово!")
sdxs_flux/diffusion_pytorch_model.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:5604baf577450654b1d025e8c31e476e42ebcb7d535eeb100917de6a31bf02fe
3
- size 3195253456
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3cc8895c1677f0976a7a8bc86252ad85fca95dab8bf68ecb836f24ff10a29c20
3
+ size 1597690928
sdxs_flux/train_flux.py ADDED
@@ -0,0 +1,798 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #from comet_ml import Experiment
2
+ import os
3
+ import math
4
+ import torch
5
+ import numpy as np
6
+ import matplotlib.pyplot as plt
7
+ from torch.utils.data import DataLoader, Sampler
8
+ from torch.utils.data.distributed import DistributedSampler
9
+ from torch.optim.lr_scheduler import LambdaLR
10
+ from collections import defaultdict
11
+ from diffusers import UNet2DConditionModel, AutoencoderKL,AutoencoderKLFlux2
12
+ from accelerate import Accelerator
13
+ from datasets import load_from_disk
14
+ from tqdm import tqdm
15
+ from PIL import Image, ImageOps
16
+ import wandb
17
+ import random
18
+ import gc
19
+ from accelerate.state import DistributedType
20
+ from torch.distributed import broadcast_object_list
21
+ from torch.utils.checkpoint import checkpoint
22
+ from diffusers.models.attention_processor import AttnProcessor2_0
23
+ from datetime import datetime
24
+ import bitsandbytes as bnb
25
+ import torch.nn.functional as F
26
+ from collections import deque
27
+ from transformers import AutoTokenizer, AutoModel
28
+
29
+ # --------------------------- Параметры ---------------------------
30
+ ds_path = "/workspace/sdxs/datasets/mjnj"
31
+ project = "sdxs_flux"
32
+ batch_size = 256
33
+ base_learning_rate = 4e-5 #2.7e-5
34
+ min_learning_rate = 9e-6 #2.7e-5
35
+ num_epochs = 20
36
+ sample_interval_share = 3
37
+ cfg_dropout = 0.5
38
+ max_length = 192
39
+ use_wandb = True
40
+ use_comet_ml = False
41
+ save_model = True
42
+ use_decay = True
43
+ fbp = False
44
+ optimizer_type = "adam8bit"
45
+ torch_compile = False
46
+ unet_gradient = True
47
+ fixed_seed = False
48
+ shuffle = True
49
+ comet_ml_api_key = "Agctp26mbqnoYrrlvQuKSTk6r"
50
+ comet_ml_workspace = "recoilme"
51
+ torch.backends.cuda.matmul.allow_tf32 = True
52
+ torch.backends.cudnn.allow_tf32 = True
53
+ torch.backends.cuda.enable_mem_efficient_sdp(False)
54
+ dtype = torch.float32
55
+ save_barrier = 1.01
56
+ warmup_percent = 0.01
57
+ percentile_clipping = 95 #96 #97
58
+ betta2 = 0.995
59
+ eps = 1e-7
60
+ clip_grad_norm = 1.0
61
+ limit = 0
62
+ checkpoints_folder = ""
63
+ mixed_precision = "no"
64
+ gradient_accumulation_steps = 1
65
+
66
+ accelerator = Accelerator(
67
+ mixed_precision=mixed_precision,
68
+ gradient_accumulation_steps=gradient_accumulation_steps
69
+ )
70
+ device = accelerator.device
71
+
72
+ # Параметры для диффузии
73
+ n_diffusion_steps = 40
74
+ samples_to_generate = 12
75
+ guidance_scale = 4
76
+
77
+ # Папки для сохранения результатов
78
+ generated_folder = "samples"
79
+ os.makedirs(generated_folder, exist_ok=True)
80
+
81
+ # Настройка seed
82
+ current_date = datetime.now()
83
+ seed = int(current_date.strftime("%Y%m%d"))
84
+ if fixed_seed:
85
+ torch.manual_seed(seed)
86
+ np.random.seed(seed)
87
+ random.seed(seed)
88
+ if torch.cuda.is_available():
89
+ torch.cuda.manual_seed_all(seed)
90
+
91
+ # --------------------------- Параметры LoRA ---------------------------
92
+ lora_name = ""
93
+ lora_rank = 32
94
+ lora_alpha = 64
95
+
96
+ print("init")
97
+
98
+ loss_ratios = {
99
+ "mse": 1.8,
100
+ "mae": 0.2,
101
+ }
102
+ median_coeff_steps = 256
103
+
104
+ # Нормализация лоссов по медианам: считаем КОЭФФИЦИЕНТЫ
105
+ class MedianLossNormalizer:
106
+ def __init__(self, desired_ratios: dict, window_steps: int):
107
+ # нормируем доли на случай, если сумма != 1
108
+ #s = sum(desired_ratios.values())
109
+ #self.ratios = {k: (v / s) for k, v in desired_ratios.items()}
110
+ self.ratios = {k: float(v) for k, v in desired_ratios.items()}
111
+ self.buffers = {k: deque(maxlen=window_steps) for k in self.ratios.keys()}
112
+ self.window = window_steps
113
+
114
+ def update_and_total(self, losses: dict):
115
+ """
116
+ losses: dict ключ->тензор (значения лоссов)
117
+ Поведение:
118
+ - буферим ABS(l) только для активных (ratio>0) лоссов
119
+ - coeff = ratio / median(abs(loss))
120
+ - total = sum(coeff * loss) по активным лоссам
121
+ CHANGED: буферим abs() — чтобы медиана была положительной и не ломала деление.
122
+ """
123
+ # буферим только активные лоссы
124
+ for k, v in losses.items():
125
+ if k in self.buffers and self.ratios.get(k, 0) > 0:
126
+ val = v.detach().abs().mean().cpu().item() # .item() лучше float() для тензоров
127
+ self.buffers[k].append(val)
128
+ #self.buffers[k].append(float(v.detach().abs().cpu()))
129
+
130
+ meds = {k: (np.median(self.buffers[k]) if len(self.buffers[k]) > 0 else 1.0) for k in self.buffers}
131
+ coeffs = {k: (self.ratios[k] / max(meds[k], 1e-12)) for k in self.ratios}
132
+
133
+ # суммируем только по активным (ratio>0)
134
+ total = sum(coeffs[k] * losses[k] for k in coeffs if self.ratios.get(k, 0) > 0)
135
+ return total, coeffs, meds
136
+
137
+ # создаём normalizer после определения loss_ratios
138
+ normalizer = MedianLossNormalizer(loss_ratios, median_coeff_steps)
139
+
140
+ # --------------------------- Инициализация WandB ---------------------------
141
+ if accelerator.is_main_process:
142
+ if use_wandb:
143
+ wandb.init(project=project+lora_name, config={
144
+ "batch_size": batch_size,
145
+ "base_learning_rate": base_learning_rate,
146
+ "num_epochs": num_epochs,
147
+ "optimizer_type": optimizer_type,
148
+ })
149
+ if use_comet_ml:
150
+ from comet_ml import Experiment
151
+ comet_experiment = Experiment(
152
+ api_key=comet_ml_api_key,
153
+ project_name=project,
154
+ workspace=comet_ml_workspace
155
+ )
156
+ hyper_params = {
157
+ "batch_size": batch_size,
158
+ "base_learning_rate": base_learning_rate,
159
+ "num_epochs": num_epochs,
160
+ }
161
+ comet_experiment.log_parameters(hyper_params)
162
+
163
+ # Включение Flash Attention 2/SDPA
164
+ torch.backends.cuda.enable_flash_sdp(True)
165
+
166
+ # --------------------------- Загрузка моделей ---------------------------
167
+ #vae = AutoencoderKL.from_pretrained("vae1x", torch_dtype=dtype).to("cpu").eval()
168
+ vae = AutoencoderKLFlux2.from_pretrained("black-forest-labs/FLUX.2-dev",subfolder="vae",torch_dtype=dtype).to(device).eval()
169
+ tokenizer = AutoTokenizer.from_pretrained("tokenizer")
170
+ text_model = AutoModel.from_pretrained("text_encoder").to(device).eval()
171
+
172
+ # --- [UPDATED] Функция кодирования текста (с маской и пулингом) ---
173
+ def encode_texts(texts, max_length=max_length):
174
+ # Если тексты пустые (для unconditional), создаем заглушки
175
+ if texts is None:
176
+ # В случае None возвращаем нули (логика для get_negative_embedding)
177
+ # Но здесь мы обычно ожидаем список строк.
178
+ pass
179
+
180
+ with torch.no_grad():
181
+ if isinstance(texts, str):
182
+ texts = [texts]
183
+
184
+ for i, prompt_item in enumerate(texts):
185
+ messages = [
186
+ {"role": "user", "content": prompt_item},
187
+ ]
188
+ prompt_item = tokenizer.apply_chat_template(
189
+ messages,
190
+ tokenize=False,
191
+ add_generation_prompt=True,
192
+ #enable_thinking=True,
193
+ )
194
+ #print(prompt_item+"\n")
195
+ texts[i] = prompt_item
196
+
197
+ toks = tokenizer(
198
+ texts,
199
+ return_tensors="pt",
200
+ padding="max_length",
201
+ truncation=True,
202
+ max_length=max_length
203
+ ).to(device)
204
+
205
+ outs = text_model(**toks, output_hidden_states=True, return_dict=True)
206
+
207
+ # Используем last_hidden_state или hidden_states[-1] (если Qwen, лучше last_hidden_state - прим человека: ХУЙ)
208
+ hidden = outs.hidden_states[-2]
209
+
210
+ # 2. Маска внимания
211
+ attention_mask = toks["attention_mask"]
212
+
213
+ # 3. Пулинг-эмбеддинг (Последний токен)
214
+ sequence_lengths = attention_mask.sum(dim=1) - 1
215
+ batch_size = hidden.shape[0]
216
+ pooled = hidden[torch.arange(batch_size, device=hidden.device), sequence_lengths]
217
+
218
+ #return hidden, attention_mask
219
+ # --- НОВАЯ ЛОГИКА: ОБЪЕДИНЕНИЕ ДЛЯ КРОСС-ВНИМАНИЯ ---
220
+ # 1. Расширяем пулинг-вектор до последовательности [B, 1, emb]
221
+ pooled_expanded = pooled.unsqueeze(1)
222
+
223
+ # 2. Объединяем последовательность токенов и пулинг-вектор
224
+ # !!! ИЗМЕНЕНИЕ ЗДЕСЬ !!!: Пулинг идет ПЕРВЫМ
225
+ # Теперь: [B, 1 + L, emb]. Пулинг стал токеном в НАЧАЛЕ.
226
+ new_encoder_hidden_states = torch.cat([pooled_expanded, hidden], dim=1)
227
+
228
+ # 3. Обновляем маску внимания для нового токена
229
+ # Маска внимания: [B, 1 + L]. Добавляем 1 в НАЧАЛО.
230
+ # torch.ones((batch_size, 1), device=device) создает маску [B, 1] со значениями 1.
231
+ new_attention_mask = torch.cat([torch.ones((batch_size, 1), device=device), attention_mask], dim=1)
232
+
233
+ return new_encoder_hidden_states, new_attention_mask
234
+
235
+ shift_factor = getattr(vae.config, "shift_factor", 0.0)
236
+ if shift_factor is None: shift_factor = 0.0
237
+ scaling_factor = getattr(vae.config, "scaling_factor", 1.0)
238
+ if scaling_factor is None: scaling_factor = 1.0
239
+
240
+ from diffusers import FlowMatchEulerDiscreteScheduler
241
+ num_train_timesteps = 1000
242
+ scheduler = FlowMatchEulerDiscreteScheduler(num_train_timesteps=num_train_timesteps)
243
+
244
+ class DistributedResolutionBatchSampler(Sampler):
245
+ def __init__(self, dataset, batch_size, num_replicas, rank, shuffle=True, drop_last=True):
246
+ self.dataset = dataset
247
+ self.batch_size = max(1, batch_size // num_replicas)
248
+ self.num_replicas = num_replicas
249
+ self.rank = rank
250
+ self.shuffle = shuffle
251
+ self.drop_last = drop_last
252
+ self.epoch = 0
253
+
254
+ try:
255
+ widths = np.array(dataset["width"])
256
+ heights = np.array(dataset["height"])
257
+ except KeyError:
258
+ widths = np.zeros(len(dataset))
259
+ heights = np.zeros(len(dataset))
260
+
261
+ self.size_keys = np.unique(np.stack([widths, heights], axis=1), axis=0)
262
+ self.size_groups = {}
263
+ for w, h in self.size_keys:
264
+ mask = (widths == w) & (heights == h)
265
+ self.size_groups[(w, h)] = np.where(mask)[0]
266
+
267
+ self.group_num_batches = {}
268
+ total_batches = 0
269
+ for size, indices in self.size_groups.items():
270
+ num_full_batches = len(indices) // (self.batch_size * self.num_replicas)
271
+ self.group_num_batches[size] = num_full_batches
272
+ total_batches += num_full_batches
273
+
274
+ self.num_batches = (total_batches // self.num_replicas) * self.num_replicas
275
+
276
+ def __iter__(self):
277
+ if torch.cuda.is_available():
278
+ torch.cuda.empty_cache()
279
+ all_batches = []
280
+ rng = np.random.RandomState(self.epoch)
281
+
282
+ for size, indices in self.size_groups.items():
283
+ indices = indices.copy()
284
+ if self.shuffle:
285
+ rng.shuffle(indices)
286
+ num_full_batches = self.group_num_batches[size]
287
+ if num_full_batches == 0:
288
+ continue
289
+ valid_indices = indices[:num_full_batches * self.batch_size * self.num_replicas]
290
+ batches = valid_indices.reshape(-1, self.batch_size * self.num_replicas)
291
+ start_idx = self.rank * self.batch_size
292
+ end_idx = start_idx + self.batch_size
293
+ gpu_batches = batches[:, start_idx:end_idx]
294
+ all_batches.extend(gpu_batches)
295
+
296
+ if self.shuffle:
297
+ rng.shuffle(all_batches)
298
+ accelerator.wait_for_everyone()
299
+ return iter(all_batches)
300
+
301
+ def __len__(self):
302
+ return self.num_batches
303
+
304
+ def set_epoch(self, epoch):
305
+ self.epoch = epoch
306
+
307
+ # --- [UPDATED] Функция для фиксированных семплов ---
308
+ def get_fixed_samples_by_resolution(dataset, samples_per_group=1):
309
+ size_groups = defaultdict(list)
310
+ try:
311
+ widths = dataset["width"]
312
+ heights = dataset["height"]
313
+ except KeyError:
314
+ widths = [0] * len(dataset)
315
+ heights = [0] * len(dataset)
316
+ for i, (w, h) in enumerate(zip(widths, heights)):
317
+ size = (w, h)
318
+ size_groups[size].append(i)
319
+
320
+ fixed_samples = {}
321
+ for size, indices in size_groups.items():
322
+ n_samples = min(samples_per_group, len(indices))
323
+ if len(size_groups)==1:
324
+ n_samples = samples_to_generate
325
+ if n_samples == 0:
326
+ continue
327
+ sample_indices = random.sample(indices, n_samples)
328
+ samples_data = [dataset[idx] for idx in sample_indices]
329
+
330
+ latents = torch.tensor(np.array([item["vae"] for item in samples_data])).to(device=device, dtype=dtype)
331
+ texts = [item["text"] for item in samples_data]
332
+
333
+ # Кодируем тексты на лету, чтобы получить маски и пулинг
334
+ embeddings, masks = encode_texts(texts)
335
+
336
+ fixed_samples[size] = (latents, embeddings, masks, texts)
337
+
338
+ print(f"Создано {len(fixed_samples)} групп фиксированных семплов по разрешениям")
339
+ return fixed_samples
340
+
341
+ if limit > 0:
342
+ dataset = load_from_disk(ds_path).select(range(limit))
343
+ else:
344
+ dataset = load_from_disk(ds_path)
345
+
346
+ dataset = dataset.filter(
347
+ lambda x: [not (path.startswith("/workspace/ds/animesfw") or path.startswith("/workspace/ds/d4/animesfw")) for path in x["image_path"]],
348
+ batched=True,
349
+ batch_size=10000, # обрабатываем по 10к строк за раз
350
+ num_proc=8
351
+ )
352
+ print(f"Осталось примеров после фильтрации: {len(dataset)}")
353
+
354
+ # --- [UPDATED] Collate Function ---
355
+ def collate_fn_simple(batch):
356
+ # 1. Латенты (VAE)
357
+ latents = torch.tensor(np.array([item["vae"] for item in batch])).to(device, dtype=dtype)
358
+
359
+ # 2. Текст берем сырой из датасета
360
+ raw_texts = [item["text"] for item in batch]
361
+ texts = [
362
+ "" if t.lower().startswith("zero")
363
+ else "" if random.random() < cfg_dropout
364
+ else t[1:].lstrip() if t.startswith(".")
365
+ else t.replace("The image shows ", "").replace("The image is ", "").replace("This image captures ","").strip()
366
+ for t in raw_texts
367
+ ]
368
+
369
+ # 3. Кодируем на лету
370
+ # Возвращает: hidden (B, L, D), mask (B, L)
371
+ embeddings, attention_mask = encode_texts(texts)
372
+
373
+ # attention_mask от токенизатора уже имеет нужный формат, но на всякий случай приведем к long
374
+ attention_mask = attention_mask.to(dtype=torch.int64)
375
+
376
+ return latents, embeddings, attention_mask
377
+
378
+ batch_sampler = DistributedResolutionBatchSampler(
379
+ dataset=dataset,
380
+ batch_size=batch_size,
381
+ num_replicas=accelerator.num_processes,
382
+ rank=accelerator.process_index,
383
+ shuffle=shuffle
384
+ )
385
+
386
+ dataloader = DataLoader(dataset, batch_sampler=batch_sampler, collate_fn=collate_fn_simple)
387
+ if accelerator.is_main_process:
388
+ print("Total samples", len(dataloader))
389
+ dataloader = accelerator.prepare(dataloader)
390
+
391
+ start_epoch = 0
392
+ global_step = 0
393
+ total_training_steps = (len(dataloader) * num_epochs)
394
+ world_size = accelerator.state.num_processes
395
+
396
+ # Загрузка UNet
397
+ latest_checkpoint = os.path.join(checkpoints_folder, project)
398
+ if os.path.isdir(latest_checkpoint):
399
+ print("Загружаем UNet из чекпоинта:", latest_checkpoint)
400
+ unet = UNet2DConditionModel.from_pretrained(latest_checkpoint).to(device=device, dtype=dtype)
401
+ if unet_gradient:
402
+ unet.enable_gradient_checkpointing()
403
+ unet.set_use_memory_efficient_attention_xformers(False)
404
+ try:
405
+ unet.set_attn_processor(AttnProcessor2_0())
406
+ except Exception as e:
407
+ print(f"Ошибка при включении SDPA: {e}")
408
+ unet.set_use_memory_efficient_attention_xformers(True)
409
+ else:
410
+ raise FileNotFoundError(f"UNet checkpoint not found at {latest_checkpoint}")
411
+
412
+ if lora_name:
413
+ # ... (Код LoRA без изменений, опущен для краткости, если не используется, иначе раскомментируйте оригинальный блок) ...
414
+ pass
415
+
416
+ # Оптимизатор
417
+ if lora_name:
418
+ trainable_params = [p for p in unet.parameters() if p.requires_grad]
419
+ else:
420
+ if fbp:
421
+ trainable_params = list(unet.parameters())
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
+ elif name == "muon":
434
+ from muon import MuonWithAuxAdam
435
+ trainable_params = [p for p in params if p.requires_grad]
436
+ hidden_weights = [p for p in trainable_params if p.ndim >= 2]
437
+ hidden_gains_biases = [p for p in trainable_params if p.ndim < 2]
438
+
439
+ param_groups = [
440
+ dict(params=hidden_weights, use_muon=True,
441
+ lr=1e-3, weight_decay=1e-4),
442
+ dict(params=hidden_gains_biases, use_muon=False,
443
+ lr=1e-4, betas=(0.9, 0.95), weight_decay=1e-4),
444
+ ]
445
+ optimizer = MuonWithAuxAdam(param_groups)
446
+ from snooc import SnooC
447
+ return SnooC(optimizer)
448
+ else:
449
+ raise ValueError(f"Unknown optimizer: {name}")
450
+
451
+ if fbp:
452
+ optimizer_dict = {p: create_optimizer(optimizer_type, [p]) for p in trainable_params}
453
+ def optimizer_hook(param):
454
+ optimizer_dict[param].step()
455
+ optimizer_dict[param].zero_grad(set_to_none=True)
456
+ for param in trainable_params:
457
+ param.register_post_accumulate_grad_hook(optimizer_hook)
458
+ unet, optimizer = accelerator.prepare(unet, optimizer_dict)
459
+ else:
460
+ optimizer = create_optimizer(optimizer_type, unet.parameters())
461
+ def lr_schedule(step):
462
+ x = step / (total_training_steps * world_size)
463
+ warmup = warmup_percent
464
+ if not use_decay:
465
+ return base_learning_rate
466
+ if x < warmup:
467
+ return min_learning_rate + (base_learning_rate - min_learning_rate) * (x / warmup)
468
+ decay_ratio = (x - warmup) / (1 - warmup)
469
+ return min_learning_rate + 0.5 * (base_learning_rate - min_learning_rate) * \
470
+ (1 + math.cos(math.pi * decay_ratio))
471
+ lr_scheduler = LambdaLR(optimizer, lambda step: lr_schedule(step) / base_learning_rate)
472
+ unet, optimizer, lr_scheduler = accelerator.prepare(unet, optimizer, lr_scheduler)
473
+
474
+ if torch_compile:
475
+ print("compiling")
476
+ unet = torch.compile(unet)
477
+ print("compiling - ok")
478
+
479
+ # Фиксированные семплы
480
+ fixed_samples = get_fixed_samples_by_resolution(dataset)
481
+
482
+ # --- [UPDATED] Функция для негативного эмбеддинга (возвращает 3 элемента) ---
483
+ def get_negative_embedding(neg_prompt="", batch_size=1):
484
+ if not neg_prompt:
485
+ hidden_dim = 2048
486
+ seq_len = max_length
487
+ empty_emb = torch.zeros((batch_size, seq_len, hidden_dim), dtype=dtype, device=device)
488
+ empty_mask = torch.ones((batch_size, seq_len), dtype=torch.int64, device=device)
489
+ return empty_emb, empty_mask
490
+
491
+ uncond_emb, uncond_mask = encode_texts([neg_prompt])
492
+ uncond_emb = uncond_emb.to(dtype=dtype, device=device).repeat(batch_size, 1, 1)
493
+ uncond_mask = uncond_mask.to(device=device).repeat(batch_size, 1)
494
+
495
+ return uncond_emb, uncond_mask
496
+
497
+ # Получаем негативные (пустые) усл��вия для валидации
498
+ uncond_emb, uncond_mask = get_negative_embedding("low quality")
499
+
500
+ # --- Функция генерации семплов ---
501
+ @torch.compiler.disable()
502
+ @torch.no_grad()
503
+ def generate_and_save_samples(fixed_samples_cpu, uncond_data, step):
504
+ uncond_emb, uncond_mask = uncond_data
505
+
506
+ original_model = None
507
+ try:
508
+ if not torch_compile:
509
+ original_model = accelerator.unwrap_model(unet, keep_torch_compile=True).eval()
510
+ else:
511
+ original_model = unet.eval()
512
+
513
+ vae.to(device=device).eval()
514
+
515
+ all_generated_images = []
516
+ all_captions = []
517
+
518
+ # Распаковываем 5 элементов (добавились mask)
519
+ for size, (sample_latents, sample_text_embeddings, sample_mask, sample_text) in fixed_samples_cpu.items():
520
+ width, height = size
521
+ sample_latents = sample_latents.to(dtype=dtype, device=device)
522
+ sample_text_embeddings = sample_text_embeddings.to(dtype=dtype, device=device)
523
+ sample_mask = sample_mask.to(device=device)
524
+
525
+ latents = torch.randn(
526
+ sample_latents.shape,
527
+ device=device,
528
+ dtype=sample_latents.dtype,
529
+ generator=torch.Generator(device=device).manual_seed(seed)
530
+ )
531
+
532
+ scheduler.set_timesteps(n_diffusion_steps, device=device)
533
+
534
+ for t in scheduler.timesteps:
535
+ if guidance_scale != 1:
536
+ latent_model_input = torch.cat([latents, latents], dim=0)
537
+
538
+ # Подготовка батчей для CFG (Negative + Positive)
539
+ # 1. Embeddings
540
+ curr_batch_size = sample_text_embeddings.shape[0]
541
+ seq_len = sample_text_embeddings.shape[1]
542
+ hidden_dim = sample_text_embeddings.shape[2]
543
+
544
+ neg_emb_batch = uncond_emb[0:1].expand(curr_batch_size, -1, -1)
545
+ text_embeddings_batch = torch.cat([neg_emb_batch, sample_text_embeddings], dim=0)
546
+
547
+ # 2. Masks
548
+ neg_mask_batch = uncond_mask[0:1].expand(curr_batch_size, -1)
549
+ attention_mask_batch = torch.cat([neg_mask_batch, sample_mask], dim=0)
550
+
551
+ else:
552
+ latent_model_input = latents
553
+ text_embeddings_batch = sample_text_embeddings
554
+ attention_mask_batch = sample_mask
555
+
556
+ # Предсказание с передачей всех условий
557
+ model_out = original_model(
558
+ latent_model_input,
559
+ t,
560
+ encoder_hidden_states=text_embeddings_batch,
561
+ encoder_attention_mask=attention_mask_batch,
562
+ )
563
+ flow = getattr(model_out, "sample", model_out)
564
+
565
+ if guidance_scale != 1:
566
+ flow_uncond, flow_cond = flow.chunk(2)
567
+ flow = flow_uncond + guidance_scale * (flow_cond - flow_uncond)
568
+
569
+ latents = scheduler.step(flow, t, latents).prev_sample
570
+
571
+ current_latents = latents
572
+ if step==0:
573
+ current_latents = sample_latents
574
+
575
+ latent_for_vae = current_latents.detach() / scaling_factor + shift_factor
576
+ decoded = vae.decode(latent_for_vae.to(torch.float32)).sample
577
+ decoded_fp32 = decoded.to(torch.float32)
578
+
579
+ for img_idx, img_tensor in enumerate(decoded_fp32):
580
+ img = (img_tensor / 2 + 0.5).clamp(0, 1).cpu().numpy()
581
+ img = img.transpose(1, 2, 0)
582
+
583
+ if np.isnan(img).any():
584
+ print("NaNs found, saving stopped! Step:", step)
585
+ pil_img = Image.fromarray((img * 255).astype("uint8"))
586
+
587
+ max_w_overall = max(s[0] for s in fixed_samples_cpu.keys())
588
+ max_h_overall = max(s[1] for s in fixed_samples_cpu.keys())
589
+ max_w_overall = max(255, max_w_overall)
590
+ max_h_overall = max(255, max_h_overall)
591
+
592
+ padded_img = ImageOps.pad(pil_img, (max_w_overall, max_h_overall), color='white')
593
+ all_generated_images.append(padded_img)
594
+
595
+ caption_text = sample_text[img_idx][:300] if img_idx < len(sample_text) else ""
596
+ all_captions.append(caption_text)
597
+
598
+ sample_path = f"{generated_folder}/{project}_{width}x{height}_{img_idx}.jpg"
599
+ pil_img.save(sample_path, "JPEG", quality=96)
600
+
601
+ if use_wandb and accelerator.is_main_process:
602
+ wandb_images = [
603
+ wandb.Image(img, caption=f"{all_captions[i]}")
604
+ for i, img in enumerate(all_generated_images)
605
+ ]
606
+ wandb.log({"generated_images": wandb_images})
607
+ if use_comet_ml and accelerator.is_main_process:
608
+ for i, img in enumerate(all_generated_images):
609
+ comet_experiment.log_image(
610
+ image_data=img,
611
+ name=f"step_{step}_img_{i}",
612
+ step=step,
613
+ metadata={"caption": all_captions[i]}
614
+ )
615
+ finally:
616
+ vae.to("cpu")
617
+ torch.cuda.empty_cache()
618
+ gc.collect()
619
+
620
+ # --------------------------- Генерация сэмплов перед обучением ---------------------------
621
+ if accelerator.is_main_process:
622
+ if save_model:
623
+ print("Генерация сэмплов до старта обучения...")
624
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), 0)
625
+ accelerator.wait_for_everyone()
626
+
627
+ def save_checkpoint(unet, variant=""):
628
+ if accelerator.is_main_process:
629
+ if lora_name:
630
+ save_lora_checkpoint(unet)
631
+ else:
632
+ model_to_save = None
633
+ if not torch_compile:
634
+ model_to_save = accelerator.unwrap_model(unet)
635
+ else:
636
+ model_to_save = unet
637
+
638
+ if variant != "":
639
+ model_to_save.to(dtype=torch.float16).save_pretrained(
640
+ os.path.join(checkpoints_folder, f"{project}"), variant=variant
641
+ )
642
+ else:
643
+ model_to_save.save_pretrained(os.path.join(checkpoints_folder, f"{project}"))
644
+
645
+ unet = unet.to(dtype=dtype)
646
+
647
+ # --------------------------- Тренировочный цикл ---------------------------
648
+ if accelerator.is_main_process:
649
+ print(f"Total steps per GPU: {total_training_steps}")
650
+
651
+ epoch_loss_points = []
652
+ progress_bar = tqdm(total=total_training_steps, disable=not accelerator.is_local_main_process, desc="Training", unit="step")
653
+
654
+ steps_per_epoch = len(dataloader)
655
+ sample_interval = max(1, steps_per_epoch // sample_interval_share)
656
+ min_loss = 4.
657
+
658
+ for epoch in range(start_epoch, start_epoch + num_epochs):
659
+ batch_losses = []
660
+ batch_grads = []
661
+ batch_sampler.set_epoch(epoch)
662
+ accelerator.wait_for_everyone()
663
+ unet.train()
664
+
665
+ for step, (latents, embeddings, attention_mask) in enumerate(dataloader):
666
+ with accelerator.accumulate(unet):
667
+ if save_model == False and epoch == 0 and step == 5 :
668
+ used_gb = torch.cuda.max_memory_allocated() / 1024**3
669
+ print(f"Шаг {step}: {used_gb:.2f} GB")
670
+
671
+ # шум
672
+ noise = torch.randn_like(latents, dtype=latents.dtype)
673
+ # берём t из [0, 1]
674
+ t = torch.rand(latents.shape[0], device=latents.device, dtype=latents.dtype)
675
+ #u = torch.rand(latents.shape[0], device=latents.device, dtype=latents.dtype)
676
+ #t = torch.sigmoid(torch.randn_like(u))
677
+
678
+ # интерполяция между x0 и шумом
679
+ noisy_latents = (1.0 - t.view(-1, 1, 1, 1)) * latents + t.view(-1, 1, 1, 1) * noise
680
+ # делаем integer timesteps для UNet
681
+ timesteps = (t * scheduler.config.num_train_timesteps).long()
682
+
683
+ # --- Вызов UNet с маской ---
684
+ model_pred = unet(
685
+ noisy_latents,
686
+ timesteps,
687
+ encoder_hidden_states=embeddings,
688
+ encoder_attention_mask=attention_mask
689
+ ).sample
690
+
691
+ target = noise - latents
692
+
693
+ mse_loss = F.mse_loss(model_pred.float(), target.float())
694
+ mae_loss = F.l1_loss(model_pred.float(), target.float())
695
+ batch_losses.append(mse_loss.detach().item())
696
+
697
+ if (global_step % 100 == 0) or (global_step % sample_interval == 0):
698
+ accelerator.wait_for_everyone()
699
+
700
+ losses_dict = {}
701
+ losses_dict["mse"] = mse_loss
702
+ losses_dict["mae"] = mae_loss
703
+
704
+ # === Нормализация всех лоссов ===
705
+ abs_for_norm = {k: losses_dict.get(k, torch.tensor(0.0, device=device)) for k in normalizer.ratios.keys()}
706
+ total_loss, coeffs, meds = normalizer.update_and_total(abs_for_norm)
707
+
708
+ if (global_step % 100 == 0) or (global_step % sample_interval == 0):
709
+ accelerator.wait_for_everyone()
710
+
711
+ accelerator.backward(total_loss)
712
+
713
+ if (global_step % 100 == 0) or (global_step % sample_interval == 0):
714
+ accelerator.wait_for_everyone()
715
+
716
+ grad = 0.0
717
+ if not fbp:
718
+ if accelerator.sync_gradients:
719
+ #with torch.amp.autocast('cuda', enabled=False):
720
+ grad_val = accelerator.clip_grad_norm_(unet.parameters(), clip_grad_norm)
721
+ grad = float(grad_val)
722
+ optimizer.step()
723
+ lr_scheduler.step()
724
+ optimizer.zero_grad(set_to_none=True)
725
+
726
+ if accelerator.sync_gradients:
727
+ global_step += 1
728
+ progress_bar.update(1)
729
+ if accelerator.is_main_process:
730
+ if fbp:
731
+ current_lr = base_learning_rate
732
+ else:
733
+ current_lr = lr_scheduler.get_last_lr()[0]
734
+ batch_grads.append(grad)
735
+
736
+ log_data = {}
737
+ log_data["loss_mse"] = mse_loss.detach().item()
738
+ log_data["loss_mae"] = mae_loss.detach().item()
739
+ log_data["lr"] = current_lr
740
+ log_data["grad"] = grad
741
+ log_data["loss_norm"] = float(total_loss.item())
742
+ for k, c in coeffs.items():
743
+ log_data[f"coeff_{k}"] = float(c)
744
+ if accelerator.sync_gradients:
745
+ if use_wandb:
746
+ wandb.log(log_data, step=global_step)
747
+ if use_comet_ml:
748
+ comet_experiment.log_metrics(log_data, step=global_step)
749
+
750
+ if global_step % sample_interval == 0:
751
+ # Передаем tuple (emb, mask) для негатива
752
+ if save_model:
753
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), global_step)
754
+ elif epoch % 10 == 0:
755
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), global_step)
756
+ last_n = sample_interval
757
+
758
+ if save_model:
759
+ has_losses = len(batch_losses) > 0
760
+ avg_sample_loss = np.mean(batch_losses[-sample_interval:]) if has_losses else 0.0
761
+ last_loss = batch_losses[-1] if has_losses else 0.0
762
+ max_loss = max(avg_sample_loss, last_loss)
763
+ should_save = max_loss < min_loss * save_barrier
764
+ print(
765
+ f"Saving: {should_save} | Max: {max_loss:.4f} | "
766
+ f"Last: {last_loss:.4f} | Avg: {avg_sample_loss:.4f}"
767
+ )
768
+ # 6. Сохранение и обновление
769
+ if should_save:
770
+ min_loss = max_loss
771
+ save_checkpoint(unet)
772
+
773
+ if accelerator.is_main_process:
774
+ avg_epoch_loss = np.mean(batch_losses) if len(batch_losses) > 0 else 0.0
775
+ avg_epoch_grad = np.mean(batch_grads) if len(batch_grads) > 0 else 0.0
776
+
777
+ print(f"\nЭпоха {epoch} завершена. Средний лосс: {avg_epoch_loss:.6f}")
778
+ log_data_ep = {
779
+ "epoch_loss": avg_epoch_loss,
780
+ "epoch_grad": avg_epoch_grad,
781
+ "epoch": epoch + 1,
782
+ }
783
+ if use_wandb:
784
+ wandb.log(log_data_ep)
785
+ if use_comet_ml:
786
+ comet_experiment.log_metrics(log_data_ep)
787
+
788
+ if accelerator.is_main_process:
789
+ print("Обучение завершено! Сохраняем финальную модель...")
790
+ #if save_model:
791
+ save_checkpoint(unet,"fp16")
792
+ if use_comet_ml:
793
+ comet_experiment.end()
794
+ accelerator.free_memory()
795
+ if torch.distributed.is_initialized():
796
+ torch.distributed.destroy_process_group()
797
+
798
+ print("Готово!")
src/sdxs_create_07.ipynb CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:7b24d9182db53377431320496f8f5fba1a74c3c09dd011a2cf1d3ff32f8aba58
3
- size 6648
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4717730757140bf0502f5c7790c0c2f287ac6355f26502864695daaa14c91d53
3
+ size 7091
src/sdxs_create_08.ipynb ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d2c29dd1e51a663f920f9360c8a3be76f68a9f5ffd0489871647f6e7add6c5ea
3
+ size 7091
test.ipynb CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:897429f78ba29492ec501a0b5b6224884068de6fc90a3976bc7b64f965b2b8e5
3
- size 9241758
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e67581f20e3602620323170d8d549f2e9ce569c4661fcbce563f6862f38235be
3
+ size 6286758
text_encoder/config.json CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:b6026cadca04402d27495be77ff01ca4e1b418ea59047ad7079bf3bfd3517f11
3
  size 1354
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d3bc4fb7ed18b75101464c8e6b93846ad0e626fa566592083d18866743d4893d
3
  size 1354
text_encoder/model.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:2a86278726ecbda59caff65f49ec07c3ec5672b044ed523780990a8c08a87fd4
3
- size 1192133232
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fbbfc1bd7cdbaa8f0759d8d3c00d8a91bf9af50093fc3c7204c1e3e2c5fe3723
3
+ size 3441183752
train-Copy3.py ADDED
@@ -0,0 +1,799 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #from comet_ml import Experiment
2
+ import os
3
+ import math
4
+ import torch
5
+ import numpy as np
6
+ import matplotlib.pyplot as plt
7
+ from torch.utils.data import DataLoader, Sampler
8
+ from torch.utils.data.distributed import DistributedSampler
9
+ from torch.optim.lr_scheduler import LambdaLR
10
+ from collections import defaultdict
11
+ from diffusers import UNet2DConditionModel, AutoencoderKL#,AutoencoderKLFlux2
12
+ from accelerate import Accelerator
13
+ from datasets import load_from_disk
14
+ from tqdm import tqdm
15
+ from PIL import Image, ImageOps
16
+ import wandb
17
+ import random
18
+ import gc
19
+ from accelerate.state import DistributedType
20
+ from torch.distributed import broadcast_object_list
21
+ from torch.utils.checkpoint import checkpoint
22
+ from diffusers.models.attention_processor import AttnProcessor2_0
23
+ from datetime import datetime
24
+ import bitsandbytes as bnb
25
+ import torch.nn.functional as F
26
+ from collections import deque
27
+ from transformers import AutoTokenizer, AutoModel
28
+
29
+ # --------------------------- Параметры ---------------------------
30
+ ds_path = "/workspace/sdxs/datasets/768"
31
+ project = "sdxs_08b"
32
+ batch_size = 64
33
+ base_learning_rate = 4e-5 #2.7e-5
34
+ min_learning_rate = 9e-6 #2.7e-5
35
+ num_epochs = 1
36
+ sample_interval_share = 10
37
+ cfg_dropout = 0.75
38
+ max_length = 192
39
+ use_wandb = False
40
+ use_comet_ml = False
41
+ save_model = False
42
+ use_decay = True
43
+ fbp = True
44
+ optimizer_type = "adam8bit"
45
+ torch_compile = False
46
+ unet_gradient = True
47
+ fixed_seed = False
48
+ shuffle = True
49
+ comet_ml_api_key = "Agctp26mbqnoYrrlvQuKSTk6r"
50
+ comet_ml_workspace = "recoilme"
51
+ torch.backends.cuda.matmul.allow_tf32 = True
52
+ torch.backends.cudnn.allow_tf32 = True
53
+ torch.backends.cuda.enable_mem_efficient_sdp(False)
54
+ dtype = torch.float32
55
+ save_barrier = 1.01
56
+ warmup_percent = 0.01
57
+ percentile_clipping = 95 #96 #97
58
+ betta2 = 0.995
59
+ eps = 1e-7
60
+ clip_grad_norm = 1.0
61
+ limit = 0
62
+ checkpoints_folder = ""
63
+ mixed_precision = "no"
64
+ gradient_accumulation_steps = 1
65
+
66
+ accelerator = Accelerator(
67
+ mixed_precision=mixed_precision,
68
+ gradient_accumulation_steps=gradient_accumulation_steps
69
+ )
70
+ device = accelerator.device
71
+
72
+ # Параметры для диффузии
73
+ n_diffusion_steps = 40
74
+ samples_to_generate = 12
75
+ guidance_scale = 4
76
+
77
+ # Папки для сохранения результатов
78
+ generated_folder = "samples"
79
+ os.makedirs(generated_folder, exist_ok=True)
80
+
81
+ # Настройка seed
82
+ current_date = datetime.now()
83
+ seed = int(current_date.strftime("%Y%m%d"))
84
+ if fixed_seed:
85
+ torch.manual_seed(seed)
86
+ np.random.seed(seed)
87
+ random.seed(seed)
88
+ if torch.cuda.is_available():
89
+ torch.cuda.manual_seed_all(seed)
90
+
91
+ # --------------------------- Параметры LoRA ---------------------------
92
+ lora_name = ""
93
+ lora_rank = 32
94
+ lora_alpha = 64
95
+
96
+ print("init")
97
+
98
+ loss_ratios = {
99
+ "mse": 0.9,
100
+ "mae": 0.1,
101
+ }
102
+ median_coeff_steps = 256
103
+
104
+ # Нормализация лоссов по медианам: считаем КОЭФФИЦИЕНТЫ
105
+ class MedianLossNormalizer:
106
+ def __init__(self, desired_ratios: dict, window_steps: int):
107
+ # нормируем доли на случай, если сумма != 1
108
+ #s = sum(desired_ratios.values())
109
+ #self.ratios = {k: (v / s) for k, v in desired_ratios.items()}
110
+ self.ratios = {k: float(v) for k, v in desired_ratios.items()}
111
+ self.buffers = {k: deque(maxlen=window_steps) for k in self.ratios.keys()}
112
+ self.window = window_steps
113
+
114
+ def update_and_total(self, losses: dict):
115
+ """
116
+ losses: dict ключ->тензор (значения лоссов)
117
+ Поведение:
118
+ - буферим ABS(l) только для активных (ratio>0) лоссов
119
+ - coeff = ratio / median(abs(loss))
120
+ - total = sum(coeff * loss) по активным лоссам
121
+ CHANGED: буферим abs() — чтобы медиана была положительной и не ломала деление.
122
+ """
123
+ # буферим только активные лоссы
124
+ for k, v in losses.items():
125
+ if k in self.buffers and self.ratios.get(k, 0) > 0:
126
+ val = v.detach().abs().mean().cpu().item() # .item() лучше float() для тензоров
127
+ self.buffers[k].append(val)
128
+ #self.buffers[k].append(float(v.detach().abs().cpu()))
129
+
130
+ meds = {k: (np.median(self.buffers[k]) if len(self.buffers[k]) > 0 else 1.0) for k in self.buffers}
131
+ coeffs = {k: (self.ratios[k] / max(meds[k], 1e-12)) for k in self.ratios}
132
+
133
+ # суммируем только по активным (ratio>0)
134
+ total = sum(coeffs[k] * losses[k] for k in coeffs if self.ratios.get(k, 0) > 0)
135
+ return total, coeffs, meds
136
+
137
+ # создаём normalizer после определения loss_ratios
138
+ normalizer = MedianLossNormalizer(loss_ratios, median_coeff_steps)
139
+
140
+ # --------------------------- Инициализация WandB ---------------------------
141
+ if accelerator.is_main_process:
142
+ if use_wandb:
143
+ wandb.init(project=project+lora_name, config={
144
+ "batch_size": batch_size,
145
+ "base_learning_rate": base_learning_rate,
146
+ "num_epochs": num_epochs,
147
+ "optimizer_type": optimizer_type,
148
+ })
149
+ if use_comet_ml:
150
+ from comet_ml import Experiment
151
+ comet_experiment = Experiment(
152
+ api_key=comet_ml_api_key,
153
+ project_name=project,
154
+ workspace=comet_ml_workspace
155
+ )
156
+ hyper_params = {
157
+ "batch_size": batch_size,
158
+ "base_learning_rate": base_learning_rate,
159
+ "num_epochs": num_epochs,
160
+ }
161
+ comet_experiment.log_parameters(hyper_params)
162
+
163
+ # Включение Flash Attention 2/SDPA
164
+ torch.backends.cuda.enable_flash_sdp(True)
165
+
166
+ # --------------------------- Загрузка моделей ---------------------------
167
+ vae = AutoencoderKL.from_pretrained("vae1x", torch_dtype=dtype).to("cpu").eval()
168
+ #vae = AutoencoderKLFlux2.from_pretrained("black-forest-labs/FLUX.2-dev",subfolder="vae",torch_dtype=dtype).to(device).eval()
169
+ tokenizer = AutoTokenizer.from_pretrained("tokenizer")
170
+ text_model = AutoModel.from_pretrained("text_encoder").to(device).eval()
171
+
172
+ # --- [UPDATED] Функция кодирования текста (с маской и пулингом) ---
173
+ def encode_texts(texts, max_length=max_length):
174
+ # Если тексты пустые (для unconditional), создаем заглушки
175
+ if texts is None:
176
+ # В случае None возвращаем нули (логика для get_negative_embedding)
177
+ # Но здесь мы обычно ожидаем список строк.
178
+ pass
179
+
180
+ with torch.no_grad():
181
+ if isinstance(texts, str):
182
+ texts = [texts]
183
+
184
+ for i, prompt_item in enumerate(texts):
185
+ messages = [
186
+ {"role": "user", "content": prompt_item},
187
+ ]
188
+ prompt_item = tokenizer.apply_chat_template(
189
+ messages,
190
+ tokenize=False,
191
+ add_generation_prompt=True,
192
+ #enable_thinking=True,
193
+ )
194
+ #print(prompt_item+"\n")
195
+ texts[i] = prompt_item
196
+
197
+ toks = tokenizer(
198
+ texts,
199
+ return_tensors="pt",
200
+ padding="max_length",
201
+ truncation=True,
202
+ max_length=max_length
203
+ ).to(device)
204
+
205
+ outs = text_model(**toks, output_hidden_states=True, return_dict=True)
206
+
207
+ # Используем last_hidden_state или hidden_states[-1] (если Qwen, лучше last_hidden_state - прим человека: ХУЙ)
208
+ hidden = outs.hidden_states[-2]
209
+
210
+ # 2. Маска внимания
211
+ attention_mask = toks["attention_mask"]
212
+
213
+ # 3. Пулинг-эмбеддинг (Последний токен)
214
+ sequence_lengths = attention_mask.sum(dim=1) - 1
215
+ batch_size = hidden.shape[0]
216
+ pooled = hidden[torch.arange(batch_size, device=hidden.device), sequence_lengths]
217
+
218
+ #return hidden, attention_mask
219
+ # --- НОВАЯ ЛОГИКА: ОБЪЕДИНЕНИЕ ДЛЯ КРОСС-ВНИМАНИЯ ---
220
+ # 1. Расширяем пулинг-вектор до последовательности [B, 1, emb]
221
+ pooled_expanded = pooled.unsqueeze(1)
222
+
223
+ # 2. Объединяем последовательность токенов и пулинг-вектор
224
+ # !!! ИЗМЕНЕНИЕ ЗДЕСЬ !!!: Пулинг идет ПЕРВЫМ
225
+ # Теперь: [B, 1 + L, emb]. Пулинг стал токеном в НАЧАЛЕ.
226
+ new_encoder_hidden_states = torch.cat([pooled_expanded, hidden], dim=1)
227
+
228
+ # 3. Обновляем маску внимания для нового токена
229
+ # Маска внимания: [B, 1 + L]. Добавляем 1 в НАЧАЛО.
230
+ # torch.ones((batch_size, 1), device=device) создает маску [B, 1] со значениями 1.
231
+ new_attention_mask = torch.cat([torch.ones((batch_size, 1), device=device), attention_mask], dim=1)
232
+
233
+ return new_encoder_hidden_states, new_attention_mask
234
+
235
+ shift_factor = getattr(vae.config, "shift_factor", 0.0)
236
+ if shift_factor is None: shift_factor = 0.0
237
+ scaling_factor = getattr(vae.config, "scaling_factor", 1.0)
238
+ if scaling_factor is None: scaling_factor = 1.0
239
+
240
+ from diffusers import FlowMatchEulerDiscreteScheduler
241
+ num_train_timesteps = 1000
242
+ scheduler = FlowMatchEulerDiscreteScheduler(num_train_timesteps=num_train_timesteps)
243
+
244
+ class DistributedResolutionBatchSampler(Sampler):
245
+ def __init__(self, dataset, batch_size, num_replicas, rank, shuffle=True, drop_last=True):
246
+ self.dataset = dataset
247
+ self.batch_size = max(1, batch_size // num_replicas)
248
+ self.num_replicas = num_replicas
249
+ self.rank = rank
250
+ self.shuffle = shuffle
251
+ self.drop_last = drop_last
252
+ self.epoch = 0
253
+
254
+ try:
255
+ widths = np.array(dataset["width"])
256
+ heights = np.array(dataset["height"])
257
+ except KeyError:
258
+ widths = np.zeros(len(dataset))
259
+ heights = np.zeros(len(dataset))
260
+
261
+ self.size_keys = np.unique(np.stack([widths, heights], axis=1), axis=0)
262
+ self.size_groups = {}
263
+ for w, h in self.size_keys:
264
+ mask = (widths == w) & (heights == h)
265
+ self.size_groups[(w, h)] = np.where(mask)[0]
266
+
267
+ self.group_num_batches = {}
268
+ total_batches = 0
269
+ for size, indices in self.size_groups.items():
270
+ num_full_batches = len(indices) // (self.batch_size * self.num_replicas)
271
+ self.group_num_batches[size] = num_full_batches
272
+ total_batches += num_full_batches
273
+
274
+ self.num_batches = (total_batches // self.num_replicas) * self.num_replicas
275
+
276
+ def __iter__(self):
277
+ if torch.cuda.is_available():
278
+ torch.cuda.empty_cache()
279
+ all_batches = []
280
+ rng = np.random.RandomState(self.epoch)
281
+
282
+ for size, indices in self.size_groups.items():
283
+ indices = indices.copy()
284
+ if self.shuffle:
285
+ rng.shuffle(indices)
286
+ num_full_batches = self.group_num_batches[size]
287
+ if num_full_batches == 0:
288
+ continue
289
+ valid_indices = indices[:num_full_batches * self.batch_size * self.num_replicas]
290
+ batches = valid_indices.reshape(-1, self.batch_size * self.num_replicas)
291
+ start_idx = self.rank * self.batch_size
292
+ end_idx = start_idx + self.batch_size
293
+ gpu_batches = batches[:, start_idx:end_idx]
294
+ all_batches.extend(gpu_batches)
295
+
296
+ if self.shuffle:
297
+ rng.shuffle(all_batches)
298
+ accelerator.wait_for_everyone()
299
+ return iter(all_batches)
300
+
301
+ def __len__(self):
302
+ return self.num_batches
303
+
304
+ def set_epoch(self, epoch):
305
+ self.epoch = epoch
306
+
307
+ # --- [UPDATED] Функция для фиксированных семплов ---
308
+ def get_fixed_samples_by_resolution(dataset, samples_per_group=1):
309
+ size_groups = defaultdict(list)
310
+ try:
311
+ widths = dataset["width"]
312
+ heights = dataset["height"]
313
+ except KeyError:
314
+ widths = [0] * len(dataset)
315
+ heights = [0] * len(dataset)
316
+ for i, (w, h) in enumerate(zip(widths, heights)):
317
+ size = (w, h)
318
+ size_groups[size].append(i)
319
+
320
+ fixed_samples = {}
321
+ for size, indices in size_groups.items():
322
+ n_samples = min(samples_per_group, len(indices))
323
+ if len(size_groups)==1:
324
+ n_samples = samples_to_generate
325
+ if n_samples == 0:
326
+ continue
327
+ sample_indices = random.sample(indices, n_samples)
328
+ samples_data = [dataset[idx] for idx in sample_indices]
329
+
330
+ latents = torch.tensor(np.array([item["vae"] for item in samples_data])).to(device=device, dtype=dtype)
331
+ texts = [item["text"] for item in samples_data]
332
+
333
+ # Кодируем тексты на лету, чтобы получить маски и пулинг
334
+ embeddings, masks = encode_texts(texts)
335
+
336
+ fixed_samples[size] = (latents, embeddings, masks, texts)
337
+
338
+ print(f"Создано {len(fixed_samples)} групп фиксированных семплов по разрешениям")
339
+ return fixed_samples
340
+
341
+ if limit > 0:
342
+ dataset = load_from_disk(ds_path).select(range(limit))
343
+ else:
344
+ dataset = load_from_disk(ds_path)
345
+
346
+ dataset = dataset.filter(
347
+ lambda x: [not (path.startswith("/workspace/ds/animesfw") or path.startswith("/workspace/ds/d4/animesfw")) for path in x["image_path"]],
348
+ batched=True,
349
+ batch_size=10000, # обрабатываем по 10к строк за раз
350
+ num_proc=8
351
+ )
352
+ print(f"Осталось примеров после фильтрации: {len(dataset)}")
353
+
354
+ # --- [UPDATED] Collate Function ---
355
+ def collate_fn_simple(batch):
356
+ # 1. Латенты (VAE)
357
+ latents = torch.tensor(np.array([item["vae"] for item in batch])).to(device, dtype=dtype)
358
+
359
+ # 2. Текст берем сырой из датасета
360
+ raw_texts = [item["text"] for item in batch]
361
+ texts = [
362
+ "" if t.lower().startswith("zero")
363
+ else "" if random.random() < cfg_dropout
364
+ else t[1:].lstrip() if t.startswith(".")
365
+ else t.replace("The image shows ", "").replace("The image is ", "").replace("This image captures ","").strip()
366
+ for t in raw_texts
367
+ ]
368
+
369
+ # 3. Кодируем на лету
370
+ # Возвращает: hidden (B, L, D), mask (B, L)
371
+ embeddings, attention_mask = encode_texts(texts)
372
+
373
+ # attention_mask от токенизатора уже имеет нужный формат, но на всякий случай приведем к long
374
+ attention_mask = attention_mask.to(dtype=torch.int64)
375
+
376
+ return latents, embeddings, attention_mask
377
+
378
+ batch_sampler = DistributedResolutionBatchSampler(
379
+ dataset=dataset,
380
+ batch_size=batch_size,
381
+ num_replicas=accelerator.num_processes,
382
+ rank=accelerator.process_index,
383
+ shuffle=shuffle
384
+ )
385
+
386
+ dataloader = DataLoader(dataset, batch_sampler=batch_sampler, collate_fn=collate_fn_simple)
387
+ if accelerator.is_main_process:
388
+ print("Total samples", len(dataloader))
389
+ dataloader = accelerator.prepare(dataloader)
390
+
391
+ start_epoch = 0
392
+ global_step = 0
393
+ total_training_steps = (len(dataloader) * num_epochs)
394
+ world_size = accelerator.state.num_processes
395
+
396
+ # Загрузка UNet
397
+ latest_checkpoint = os.path.join(checkpoints_folder, project)
398
+ if os.path.isdir(latest_checkpoint):
399
+ print("Загружаем UNet из чекпоинта:", latest_checkpoint)
400
+ unet = UNet2DConditionModel.from_pretrained(latest_checkpoint).to(device=device, dtype=dtype)
401
+ if unet_gradient:
402
+ unet.enable_gradient_checkpointing()
403
+ unet.set_use_memory_efficient_attention_xformers(False)
404
+ try:
405
+ unet.set_attn_processor(AttnProcessor2_0())
406
+ except Exception as e:
407
+ print(f"Ошибка при включении SDPA: {e}")
408
+ unet.set_use_memory_efficient_attention_xformers(True)
409
+ else:
410
+ raise FileNotFoundError(f"UNet checkpoint not found at {latest_checkpoint}")
411
+
412
+ if lora_name:
413
+ # ... (Код LoRA без изменений, опущен для краткости, если не используется, иначе раскомментируйте оригинальный блок) ...
414
+ pass
415
+
416
+ # Оптимизатор
417
+ if lora_name:
418
+ trainable_params = [p for p in unet.parameters() if p.requires_grad]
419
+ else:
420
+ if fbp:
421
+ trainable_params = list(unet.parameters())
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
+ elif name == "muon":
434
+ from muon import MuonWithAuxAdam
435
+ trainable_params = [p for p in params if p.requires_grad]
436
+ hidden_weights = [p for p in trainable_params if p.ndim >= 2]
437
+ hidden_gains_biases = [p for p in trainable_params if p.ndim < 2]
438
+
439
+ param_groups = [
440
+ dict(params=hidden_weights, use_muon=True,
441
+ lr=1e-3, weight_decay=1e-4),
442
+ dict(params=hidden_gains_biases, use_muon=False,
443
+ lr=1e-4, betas=(0.9, 0.95), weight_decay=1e-4),
444
+ ]
445
+ optimizer = MuonWithAuxAdam(param_groups)
446
+ from snooc import SnooC
447
+ return SnooC(optimizer)
448
+ else:
449
+ raise ValueError(f"Unknown optimizer: {name}")
450
+
451
+ if fbp:
452
+ optimizer_dict = {p: create_optimizer(optimizer_type, [p]) for p in trainable_params}
453
+ def optimizer_hook(param):
454
+ optimizer_dict[param].step()
455
+ optimizer_dict[param].zero_grad(set_to_none=True)
456
+ for param in trainable_params:
457
+ param.register_post_accumulate_grad_hook(optimizer_hook)
458
+ unet, optimizer = accelerator.prepare(unet, optimizer_dict)
459
+ else:
460
+ optimizer = create_optimizer(optimizer_type, unet.parameters())
461
+ def lr_schedule(step):
462
+ x = step / (total_training_steps * world_size)
463
+ warmup = warmup_percent
464
+ if not use_decay:
465
+ return base_learning_rate
466
+ if x < warmup:
467
+ return min_learning_rate + (base_learning_rate - min_learning_rate) * (x / warmup)
468
+ decay_ratio = (x - warmup) / (1 - warmup)
469
+ return min_learning_rate + 0.5 * (base_learning_rate - min_learning_rate) * \
470
+ (1 + math.cos(math.pi * decay_ratio))
471
+ lr_scheduler = LambdaLR(optimizer, lambda step: lr_schedule(step) / base_learning_rate)
472
+ unet, optimizer, lr_scheduler = accelerator.prepare(unet, optimizer, lr_scheduler)
473
+
474
+ if torch_compile:
475
+ print("compiling")
476
+ unet = torch.compile(unet)
477
+ print("compiling - ok")
478
+
479
+ # Фиксированные семплы
480
+ fixed_samples = get_fixed_samples_by_resolution(dataset)
481
+
482
+ # --- [UPDATED] Функция для негативного эмбеддинга (возвращает 3 элемента) ---
483
+ def get_negative_embedding(neg_prompt="", batch_size=1):
484
+ if not neg_prompt:
485
+ hidden_dim = 1024
486
+ seq_len = max_length
487
+ empty_emb = torch.zeros((batch_size, seq_len, hidden_dim), dtype=dtype, device=device)
488
+ empty_mask = torch.ones((batch_size, seq_len), dtype=torch.int64, device=device)
489
+ return empty_emb, empty_mask
490
+
491
+ uncond_emb, uncond_mask = encode_texts([neg_prompt])
492
+ uncond_emb = uncond_emb.to(dtype=dtype, device=device).repeat(batch_size, 1, 1)
493
+ uncond_mask = uncond_mask.to(device=device).repeat(batch_size, 1)
494
+
495
+ return uncond_emb, uncond_mask
496
+
497
+ # Получаем негативные (пустые) усл��вия для валидации
498
+ uncond_emb, uncond_mask = get_negative_embedding("low quality")
499
+
500
+ # --- Функция генерации семплов ---
501
+ @torch.compiler.disable()
502
+ @torch.no_grad()
503
+ def generate_and_save_samples(fixed_samples_cpu, uncond_data, step):
504
+ uncond_emb, uncond_mask = uncond_data
505
+
506
+ original_model = None
507
+ try:
508
+ if not torch_compile:
509
+ original_model = accelerator.unwrap_model(unet, keep_torch_compile=True).eval()
510
+ else:
511
+ original_model = unet.eval()
512
+
513
+ vae.to(device=device).eval()
514
+
515
+ all_generated_images = []
516
+ all_captions = []
517
+
518
+ # Распаковываем 5 элементов (добавились mask)
519
+ for size, (sample_latents, sample_text_embeddings, sample_mask, sample_text) in fixed_samples_cpu.items():
520
+ width, height = size
521
+ sample_latents = sample_latents.to(dtype=dtype, device=device)
522
+ sample_text_embeddings = sample_text_embeddings.to(dtype=dtype, device=device)
523
+ sample_mask = sample_mask.to(device=device)
524
+
525
+ latents = torch.randn(
526
+ sample_latents.shape,
527
+ device=device,
528
+ dtype=sample_latents.dtype,
529
+ generator=torch.Generator(device=device).manual_seed(seed)
530
+ )
531
+
532
+ scheduler.set_timesteps(n_diffusion_steps, device=device)
533
+
534
+ for t in scheduler.timesteps:
535
+ if guidance_scale != 1:
536
+ latent_model_input = torch.cat([latents, latents], dim=0)
537
+
538
+ # Подготовка батчей для CFG (Negative + Positive)
539
+ # 1. Embeddings
540
+ curr_batch_size = sample_text_embeddings.shape[0]
541
+ seq_len = sample_text_embeddings.shape[1]
542
+ hidden_dim = sample_text_embeddings.shape[2]
543
+
544
+ neg_emb_batch = uncond_emb[0:1].expand(curr_batch_size, -1, -1)
545
+ text_embeddings_batch = torch.cat([neg_emb_batch, sample_text_embeddings], dim=0)
546
+
547
+ # 2. Masks
548
+ neg_mask_batch = uncond_mask[0:1].expand(curr_batch_size, -1)
549
+ attention_mask_batch = torch.cat([neg_mask_batch, sample_mask], dim=0)
550
+
551
+ else:
552
+ latent_model_input = latents
553
+ text_embeddings_batch = sample_text_embeddings
554
+ attention_mask_batch = sample_mask
555
+
556
+ # Предсказание с передачей всех условий
557
+ model_out = original_model(
558
+ latent_model_input,
559
+ t,
560
+ encoder_hidden_states=text_embeddings_batch,
561
+ encoder_attention_mask=attention_mask_batch,
562
+ )
563
+ flow = getattr(model_out, "sample", model_out)
564
+
565
+ if guidance_scale != 1:
566
+ flow_uncond, flow_cond = flow.chunk(2)
567
+ flow = flow_uncond + guidance_scale * (flow_cond - flow_uncond)
568
+
569
+ latents = scheduler.step(flow, t, latents).prev_sample
570
+
571
+ current_latents = latents
572
+ if step==0:
573
+ current_latents = sample_latents
574
+
575
+ latent_for_vae = current_latents.detach() / scaling_factor + shift_factor
576
+ decoded = vae.decode(latent_for_vae.to(torch.float32)).sample
577
+ decoded_fp32 = decoded.to(torch.float32)
578
+
579
+ for img_idx, img_tensor in enumerate(decoded_fp32):
580
+ img = (img_tensor / 2 + 0.5).clamp(0, 1).cpu().numpy()
581
+ img = img.transpose(1, 2, 0)
582
+
583
+ if np.isnan(img).any():
584
+ print("NaNs found, saving stopped! Step:", step)
585
+ pil_img = Image.fromarray((img * 255).astype("uint8"))
586
+
587
+ max_w_overall = max(s[0] for s in fixed_samples_cpu.keys())
588
+ max_h_overall = max(s[1] for s in fixed_samples_cpu.keys())
589
+ max_w_overall = max(255, max_w_overall)
590
+ max_h_overall = max(255, max_h_overall)
591
+
592
+ padded_img = ImageOps.pad(pil_img, (max_w_overall, max_h_overall), color='white')
593
+ all_generated_images.append(padded_img)
594
+
595
+ caption_text = sample_text[img_idx][:300] if img_idx < len(sample_text) else ""
596
+ all_captions.append(caption_text)
597
+
598
+ sample_path = f"{generated_folder}/{project}_{width}x{height}_{img_idx}.jpg"
599
+ pil_img.save(sample_path, "JPEG", quality=96)
600
+
601
+ if use_wandb and accelerator.is_main_process:
602
+ wandb_images = [
603
+ wandb.Image(img, caption=f"{all_captions[i]}")
604
+ for i, img in enumerate(all_generated_images)
605
+ ]
606
+ wandb.log({"generated_images": wandb_images})
607
+ if use_comet_ml and accelerator.is_main_process:
608
+ for i, img in enumerate(all_generated_images):
609
+ comet_experiment.log_image(
610
+ image_data=img,
611
+ name=f"step_{step}_img_{i}",
612
+ step=step,
613
+ metadata={"caption": all_captions[i]}
614
+ )
615
+ finally:
616
+ vae.to("cpu")
617
+ torch.cuda.empty_cache()
618
+ gc.collect()
619
+
620
+ # --------------------------- Генерация сэмплов перед обучением ---------------------------
621
+ if accelerator.is_main_process:
622
+ if save_model:
623
+ print("Генерация сэмплов до старта обучения...")
624
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), 0)
625
+ accelerator.wait_for_everyone()
626
+
627
+ def save_checkpoint(unet, variant=""):
628
+ if accelerator.is_main_process:
629
+ if lora_name:
630
+ save_lora_checkpoint(unet)
631
+ else:
632
+ model_to_save = None
633
+ if not torch_compile:
634
+ model_to_save = accelerator.unwrap_model(unet)
635
+ else:
636
+ model_to_save = unet
637
+
638
+ if variant != "":
639
+ model_to_save.to(dtype=torch.float16).save_pretrained(
640
+ os.path.join(checkpoints_folder, f"{project}"), variant=variant
641
+ )
642
+ else:
643
+ model_to_save.save_pretrained(os.path.join(checkpoints_folder, f"{project}"))
644
+
645
+ unet = unet.to(dtype=dtype)
646
+
647
+ # --------------------------- Тренировочный цикл ---------------------------
648
+ if accelerator.is_main_process:
649
+ print(f"Total steps per GPU: {total_training_steps}")
650
+
651
+ epoch_loss_points = []
652
+ progress_bar = tqdm(total=total_training_steps, disable=not accelerator.is_local_main_process, desc="Training", unit="step")
653
+
654
+ steps_per_epoch = len(dataloader)
655
+ sample_interval = max(1, steps_per_epoch // sample_interval_share)
656
+ min_loss = 2.
657
+
658
+ for epoch in range(start_epoch, start_epoch + num_epochs):
659
+ batch_losses = []
660
+ batch_grads = []
661
+ batch_sampler.set_epoch(epoch)
662
+ accelerator.wait_for_everyone()
663
+ unet.train()
664
+
665
+ for step, (latents, embeddings, attention_mask) in enumerate(dataloader):
666
+ with accelerator.accumulate(unet):
667
+ if save_model == False and epoch == 0 and step == 5 :
668
+ used_gb = torch.cuda.max_memory_allocated() / 1024**3
669
+ print(f"Шаг {step}: {used_gb:.2f} GB")
670
+
671
+ # шум
672
+ noise = torch.randn_like(latents, dtype=latents.dtype)
673
+ # берём t из [0, 1]
674
+ t = torch.rand(latents.shape[0], device=latents.device, dtype=latents.dtype)
675
+ #u = torch.rand(latents.shape[0], device=latents.device, dtype=latents.dtype)
676
+ #t = torch.sigmoid(torch.randn_like(u))
677
+
678
+ # интерполяция между x0 и шумом
679
+ noisy_latents = (1.0 - t.view(-1, 1, 1, 1)) * latents + t.view(-1, 1, 1, 1) * noise
680
+ # делаем integer timesteps для UNet
681
+ timesteps = (t * scheduler.config.num_train_timesteps).long()
682
+
683
+ # --- Вызов UNet с маской ---
684
+ model_pred = unet(
685
+ noisy_latents,
686
+ timesteps,
687
+ encoder_hidden_states=embeddings,
688
+ encoder_attention_mask=attention_mask
689
+ ).sample
690
+
691
+ target = noise - latents
692
+
693
+ mse_loss = F.mse_loss(model_pred.float(), target.float())
694
+ mae_loss = F.l1_loss(model_pred.float(), target.float())
695
+ batch_losses.append(mse_loss.detach().item())
696
+
697
+ if (global_step % 100 == 0) or (global_step % sample_interval == 0):
698
+ accelerator.wait_for_everyone()
699
+
700
+ losses_dict = {}
701
+ losses_dict["mse"] = mse_loss
702
+ losses_dict["mae"] = mae_loss
703
+
704
+ # === Нормализация всех лоссов ===
705
+ abs_for_norm = {k: losses_dict.get(k, torch.tensor(0.0, device=device)) for k in normalizer.ratios.keys()}
706
+ total_loss, coeffs, meds = normalizer.update_and_total(abs_for_norm)
707
+
708
+ if (global_step % 100 == 0) or (global_step % sample_interval == 0):
709
+ accelerator.wait_for_everyone()
710
+
711
+ accelerator.backward(total_loss)
712
+
713
+ if (global_step % 100 == 0) or (global_step % sample_interval == 0):
714
+ accelerator.wait_for_everyone()
715
+
716
+ grad = 0.0
717
+ if not fbp:
718
+ if accelerator.sync_gradients:
719
+ #with torch.amp.autocast('cuda', enabled=False):
720
+ grad_val = accelerator.clip_grad_norm_(unet.parameters(), clip_grad_norm)
721
+ grad = float(grad_val)
722
+ optimizer.step()
723
+ lr_scheduler.step()
724
+ optimizer.zero_grad(set_to_none=True)
725
+
726
+ if accelerator.sync_gradients:
727
+ global_step += 1
728
+ progress_bar.update(1)
729
+ if accelerator.is_main_process:
730
+ if fbp:
731
+ current_lr = base_learning_rate
732
+ else:
733
+ current_lr = lr_scheduler.get_last_lr()[0]
734
+ batch_grads.append(grad)
735
+
736
+ log_data = {}
737
+ log_data["loss_mse"] = mse_loss.detach().item()
738
+ log_data["loss_mae"] = mae_loss.detach().item()
739
+ log_data["lr"] = current_lr
740
+ if not fbp:
741
+ log_data["grad"] = grad
742
+ log_data["loss_norm"] = float(total_loss.item())
743
+ for k, c in coeffs.items():
744
+ log_data[f"coeff_{k}"] = float(c)
745
+ if accelerator.sync_gradients:
746
+ if use_wandb:
747
+ wandb.log(log_data, step=global_step)
748
+ if use_comet_ml:
749
+ comet_experiment.log_metrics(log_data, step=global_step)
750
+
751
+ if global_step % sample_interval == 0:
752
+ # Передаем tuple (emb, mask) для негатива
753
+ if save_model:
754
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), global_step)
755
+ elif epoch % 10 == 0:
756
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), global_step)
757
+ last_n = sample_interval
758
+
759
+ if save_model:
760
+ has_losses = len(batch_losses) > 0
761
+ avg_sample_loss = np.mean(batch_losses[-sample_interval:]) if has_losses else 0.0
762
+ last_loss = batch_losses[-1] if has_losses else 0.0
763
+ max_loss = max(avg_sample_loss, last_loss)
764
+ should_save = max_loss < min_loss * save_barrier
765
+ print(
766
+ f"Saving: {should_save} | Max: {max_loss:.4f} | "
767
+ f"Last: {last_loss:.4f} | Avg: {avg_sample_loss:.4f}"
768
+ )
769
+ # 6. Сохранение и обновление
770
+ if should_save:
771
+ min_loss = max_loss
772
+ save_checkpoint(unet)
773
+
774
+ if accelerator.is_main_process:
775
+ avg_epoch_loss = np.mean(batch_losses) if len(batch_losses) > 0 else 0.0
776
+ avg_epoch_grad = np.mean(batch_grads) if len(batch_grads) > 0 else 0.0
777
+
778
+ print(f"\nЭпоха {epoch} завершена. Средний лосс: {avg_epoch_loss:.6f}")
779
+ log_data_ep = {
780
+ "epoch_loss": avg_epoch_loss,
781
+ "epoch_grad": avg_epoch_grad,
782
+ "epoch": epoch + 1,
783
+ }
784
+ if use_wandb:
785
+ wandb.log(log_data_ep)
786
+ if use_comet_ml:
787
+ comet_experiment.log_metrics(log_data_ep)
788
+
789
+ if accelerator.is_main_process:
790
+ print("Обучение завершено! Сохраняем финальную модель...")
791
+ #if save_model:
792
+ save_checkpoint(unet,"fp16")
793
+ if use_comet_ml:
794
+ comet_experiment.end()
795
+ accelerator.free_memory()
796
+ if torch.distributed.is_initialized():
797
+ torch.distributed.destroy_process_group()
798
+
799
+ print("Готово!")
train.py CHANGED
@@ -8,7 +8,7 @@ from torch.utils.data import DataLoader, Sampler
8
  from torch.utils.data.distributed import DistributedSampler
9
  from torch.optim.lr_scheduler import LambdaLR
10
  from collections import defaultdict
11
- from diffusers import UNet2DConditionModel, AutoencoderKL#,AutoencoderKLFlux2
12
  from accelerate import Accelerator
13
  from datasets import load_from_disk
14
  from tqdm import tqdm
@@ -27,20 +27,20 @@ from collections import deque
27
  from transformers import AutoTokenizer, AutoModel
28
 
29
  # --------------------------- Параметры ---------------------------
30
- ds_path = "/workspace/sdxs/datasets/768"
31
- project = "sdxs_1b"
32
- batch_size = 32
33
  base_learning_rate = 4e-5 #2.7e-5
34
- min_learning_rate = 9e-6 #2.7e-5
35
- num_epochs = 10
36
- sample_interval_share = 20
37
- cfg_dropout = 0.5
38
  max_length = 192
39
  use_wandb = True
40
  use_comet_ml = False
41
  save_model = True
42
  use_decay = True
43
- fbp = True
44
  optimizer_type = "adam8bit"
45
  torch_compile = False
46
  unet_gradient = True
@@ -96,8 +96,8 @@ lora_alpha = 64
96
  print("init")
97
 
98
  loss_ratios = {
99
- "mse": 0.9,
100
- "mae": 0.1,
101
  }
102
  median_coeff_steps = 256
103
 
@@ -482,7 +482,7 @@ fixed_samples = get_fixed_samples_by_resolution(dataset)
482
  # --- [UPDATED] Функция для негативного эмбеддинга (возвращает 3 элемента) ---
483
  def get_negative_embedding(neg_prompt="", batch_size=1):
484
  if not neg_prompt:
485
- hidden_dim = 1024
486
  seq_len = max_length
487
  empty_emb = torch.zeros((batch_size, seq_len, hidden_dim), dtype=dtype, device=device)
488
  empty_mask = torch.ones((batch_size, seq_len), dtype=torch.int64, device=device)
@@ -653,7 +653,7 @@ progress_bar = tqdm(total=total_training_steps, disable=not accelerator.is_local
653
 
654
  steps_per_epoch = len(dataloader)
655
  sample_interval = max(1, steps_per_epoch // sample_interval_share)
656
- min_loss = 2.
657
 
658
  for epoch in range(start_epoch, start_epoch + num_epochs):
659
  batch_losses = []
@@ -737,8 +737,7 @@ for epoch in range(start_epoch, start_epoch + num_epochs):
737
  log_data["loss_mse"] = mse_loss.detach().item()
738
  log_data["loss_mae"] = mae_loss.detach().item()
739
  log_data["lr"] = current_lr
740
- if not fbp:
741
- log_data["grad"] = grad
742
  log_data["loss_norm"] = float(total_loss.item())
743
  for k, c in coeffs.items():
744
  log_data[f"coeff_{k}"] = float(c)
 
8
  from torch.utils.data.distributed import DistributedSampler
9
  from torch.optim.lr_scheduler import LambdaLR
10
  from collections import defaultdict
11
+ from diffusers import UNet2DConditionModel, AutoencoderKL,AutoencoderKLFlux2
12
  from accelerate import Accelerator
13
  from datasets import load_from_disk
14
  from tqdm import tqdm
 
27
  from transformers import AutoTokenizer, AutoModel
28
 
29
  # --------------------------- Параметры ---------------------------
30
+ ds_path = "/workspace/sdxs/datasets/mjnj"
31
+ project = "sdxs_08b"
32
+ batch_size = 100
33
  base_learning_rate = 4e-5 #2.7e-5
34
+ min_learning_rate = 1e-5 #2.7e-5
35
+ num_epochs = 15
36
+ sample_interval_share = 5
37
+ cfg_dropout = 0.25
38
  max_length = 192
39
  use_wandb = True
40
  use_comet_ml = False
41
  save_model = True
42
  use_decay = True
43
+ fbp = False
44
  optimizer_type = "adam8bit"
45
  torch_compile = False
46
  unet_gradient = True
 
96
  print("init")
97
 
98
  loss_ratios = {
99
+ "mse": 1.5,
100
+ "mae": 0.5,
101
  }
102
  median_coeff_steps = 256
103
 
 
482
  # --- [UPDATED] Функция для негативного эмбеддинга (возвращает 3 элемента) ---
483
  def get_negative_embedding(neg_prompt="", batch_size=1):
484
  if not neg_prompt:
485
+ hidden_dim = 2048
486
  seq_len = max_length
487
  empty_emb = torch.zeros((batch_size, seq_len, hidden_dim), dtype=dtype, device=device)
488
  empty_mask = torch.ones((batch_size, seq_len), dtype=torch.int64, device=device)
 
653
 
654
  steps_per_epoch = len(dataloader)
655
  sample_interval = max(1, steps_per_epoch // sample_interval_share)
656
+ min_loss = 4.
657
 
658
  for epoch in range(start_epoch, start_epoch + num_epochs):
659
  batch_losses = []
 
737
  log_data["loss_mse"] = mse_loss.detach().item()
738
  log_data["loss_mae"] = mae_loss.detach().item()
739
  log_data["lr"] = current_lr
740
+ log_data["grad"] = grad
 
741
  log_data["loss_norm"] = float(total_loss.item())
742
  for k, c in coeffs.items():
743
  log_data[f"coeff_{k}"] = float(c)