Text-to-Image
Diffusers
Safetensors
recoilme commited on
Commit
efa99e4
·
1 Parent(s): a45ddb3
dataset_sample.ipynb ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:76ee79200e6b42a95c9ec06059a7d37061324ed356eac772d2929588eea8f9da
3
+ size 47263813
girl.jpg CHANGED

Git LFS Details

  • SHA256: fd706646e8a05a5603898739224f4408cfcbd555eac5b6ea472dad1f0cc727ef
  • Pointer size: 130 Bytes
  • Size of remote file: 60.6 kB

Git LFS Details

  • SHA256: 5041aa7590e32aeecf7c60899df04c48e5984bc54908019c18299264abf3584c
  • Pointer size: 130 Bytes
  • Size of remote file: 46.1 kB
model_index.json CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:eff4428f96497a4c7d58ec805016420c0b33523c8a7ee0544e94d53a6cc9c2ab
3
- size 560
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dba75f85251de1820fc58c9446edc54a1e48523e5a813fe6ea0971df7faace5d
3
+ size 555
pipeline_sdxs.py CHANGED
@@ -26,7 +26,12 @@ class SdxsPipeline(DiffusionPipeline):
26
  unet=unet,
27
  scheduler=scheduler
28
  )
29
- self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
 
 
 
 
 
30
 
31
  def preprocess_image(self, image: Image.Image, width: int, height: int):
32
  """Ресайз и центрированный кроп изображения для асимметричного VAE."""
@@ -52,53 +57,6 @@ class SdxsPipeline(DiffusionPipeline):
52
  image = torch.from_numpy(image)
53
  return 2.0 * image - 1.0 # [-1, 1]
54
 
55
- @staticmethod
56
- def _patchify_latents(latents):
57
- batch_size, num_channels_latents, height, width = latents.shape
58
- latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
59
- latents = latents.permute(0, 1, 3, 5, 2, 4)
60
- latents = latents.reshape(batch_size, num_channels_latents * 4, height // 2, width // 2)
61
- return latents
62
-
63
- @staticmethod
64
- def _unpatchify_latents(latents):
65
- batch_size, num_channels_latents, height, width = latents.shape
66
- latents = latents.reshape(batch_size, num_channels_latents // (2 * 2), 2, 2, height, width)
67
- latents = latents.permute(0, 1, 4, 2, 5, 3)
68
- latents = latents.reshape(batch_size, num_channels_latents // (2 * 2), height * 2, width * 2)
69
- return latents
70
-
71
- def flux_encode(self, latents):
72
- # 1. Patchify
73
- image_latents = self._patchify_latents(latents)
74
-
75
- # 2. Normalization
76
- bn_mean = self.vae.bn.running_mean.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
77
- bn_var = self.vae.bn.running_var.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
78
- eps = getattr(self.vae.config, "batch_norm_eps", 1e-5)
79
-
80
- latents_bn_std = torch.sqrt(bn_var + eps)
81
- latents = (image_latents - bn_mean) / latents_bn_std
82
-
83
- # 3. Unpatchify
84
- latents = self._unpatchify_latents(latents)
85
- return latents
86
-
87
- def flux_decode(self, latents):
88
- # 1. Patchify
89
- image_latents = self._patchify_latents(latents)
90
-
91
- # 2. De-normalization
92
- bn_mean = self.vae.bn.running_mean.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
93
- bn_var = self.vae.bn.running_var.view(1, -1, 1, 1).to(image_latents.device, image_latents.dtype)
94
- eps = getattr(self.vae.config, "batch_norm_eps", 1e-5)
95
-
96
- latents_bn_std = torch.sqrt(bn_var + eps)
97
- latents = image_latents * latents_bn_std + bn_mean
98
-
99
- # 3. Unpatchify
100
- latents = self._unpatchify_latents(latents)
101
- return latents
102
 
103
  def encode_prompt(self, prompt, negative_prompt, device, dtype):
104
  def get_single_encode(texts):
@@ -301,9 +259,7 @@ class SdxsPipeline(DiffusionPipeline):
301
 
302
  # --- Кодируем в latent ---
303
  latents_clean = self.vae.encode(image_tensor).latent_dist.sample(generator=generator)
304
- vae_scaling_factor = getattr(self.vae.config, "scaling_factor", 1.0)
305
- vae_shift_factor = getattr(self.vae.config, "shift_factor", 0.0)
306
- latents_clean = (latents_clean - vae_shift_factor) / vae_scaling_factor
307
  latents_clean = latents_clean.to(dtype)
308
 
309
  # --- Добавляем шум по Rectified Flow формуле ---
@@ -322,9 +278,7 @@ class SdxsPipeline(DiffusionPipeline):
322
  timesteps = timesteps[t_start:]
323
 
324
  else:
325
- # txt2img — оставляем как было
326
- vae_scaling_factor = getattr(self.vae.config, "scaling_factor", 1.0)
327
- vae_shift_factor = getattr(self.vae.config, "shift_factor", 0.0)
328
  latent_h = height // self.vae_scale_factor
329
  latent_w = width // self.vae_scale_factor
330
 
@@ -359,9 +313,7 @@ class SdxsPipeline(DiffusionPipeline):
359
  if not return_dict: return (latents, prompt)
360
  return SdxsPipelineOutput(images=latents, prompt=prompt)
361
 
362
- latents = latents * vae_scaling_factor + vae_shift_factor
363
- latents = self.flux_decode(latents)
364
-
365
  image_output = self.vae.decode(latents.to(self.vae.dtype), return_dict=False)[0]
366
 
367
  image_output = (image_output.clamp(-1, 1) + 1) / 2
 
26
  unet=unet,
27
  scheduler=scheduler
28
  )
29
+ self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
30
+ mean = getattr(self.vae.config, "latents_mean", None)
31
+ std = getattr(self.vae.config, "latents_std", None)
32
+ if mean is not None and std is not None:
33
+ self.vae_latents_std = torch.tensor(std, device=self.unet.device, dtype=self.unet.dtype).view(1, len(std), 1, 1)
34
+ self.vae_latents_mean = torch.tensor(mean, device=self.unet.device, dtype=self.unet.dtype).view(1, len(mean), 1, 1)
35
 
36
  def preprocess_image(self, image: Image.Image, width: int, height: int):
37
  """Ресайз и центрированный кроп изображения для асимметричного VAE."""
 
57
  image = torch.from_numpy(image)
58
  return 2.0 * image - 1.0 # [-1, 1]
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  def encode_prompt(self, prompt, negative_prompt, device, dtype):
62
  def get_single_encode(texts):
 
259
 
260
  # --- Кодируем в latent ---
261
  latents_clean = self.vae.encode(image_tensor).latent_dist.sample(generator=generator)
262
+ latents_clean = (latents_clean - self.vae_latents_mean.to(device, self.vae.dtype)) / self.vae_latents_std.to(device, self.vae.dtype)
 
 
263
  latents_clean = latents_clean.to(dtype)
264
 
265
  # --- Добавляем шум по Rectified Flow формуле ---
 
278
  timesteps = timesteps[t_start:]
279
 
280
  else:
281
+ # txt2img
 
 
282
  latent_h = height // self.vae_scale_factor
283
  latent_w = width // self.vae_scale_factor
284
 
 
313
  if not return_dict: return (latents, prompt)
314
  return SdxsPipelineOutput(images=latents, prompt=prompt)
315
 
316
+ latents = latents * self.vae_latents_std.to(device, self.vae.dtype) + self.vae_latents_mean.to(device, self.vae.dtype)
 
 
317
  image_output = self.vae.decode(latents.to(self.vae.dtype), return_dict=False)[0]
318
 
319
  image_output = (image_output.clamp(-1, 1) + 1) / 2
samples/unet_1024x1280_0.jpg ADDED

Git LFS Details

  • SHA256: 416dcd4b4632798a086cde75c9bbbd872a3f4b5c3cf46f03464ebd8a4c1ff2ce
  • Pointer size: 131 Bytes
  • Size of remote file: 348 kB
samples/unet_1088x1280_0.jpg ADDED

Git LFS Details

  • SHA256: e5f764b29038bf9e3aa63fd1471839ee6151c60a387d3134222c7d63d439e1bc
  • Pointer size: 131 Bytes
  • Size of remote file: 216 kB
samples/unet_1152x1280_0.jpg ADDED

Git LFS Details

  • SHA256: 6c3460991b856160de5af6d5b98aa6d3d1231f894277d5b77521a086d3d48538
  • Pointer size: 131 Bytes
  • Size of remote file: 310 kB
samples/unet_1216x1280_0.jpg ADDED

Git LFS Details

  • SHA256: b7cfe2bec544adf6645a0278abd20174a9e0da23b4bf8091a4b460600eaeccde
  • Pointer size: 131 Bytes
  • Size of remote file: 385 kB
samples/unet_1280x1024_0.jpg ADDED

Git LFS Details

  • SHA256: 317d79942eb724628d94b9de6793f911cabbe0a304a0797e57446907f25a6695
  • Pointer size: 131 Bytes
  • Size of remote file: 455 kB
samples/unet_1280x1088_0.jpg ADDED

Git LFS Details

  • SHA256: 82de7ce76f8da8bb5d42d5fa310f53e6658892693167eb7cf51756c9e54cc46c
  • Pointer size: 131 Bytes
  • Size of remote file: 439 kB
samples/unet_1280x1152_0.jpg ADDED

Git LFS Details

  • SHA256: 82292d5f2f5b2e993231aafaa835abf5422d05eca22a32b752206447bb45421e
  • Pointer size: 131 Bytes
  • Size of remote file: 515 kB
samples/unet_1280x1216_0.jpg ADDED

Git LFS Details

  • SHA256: 5d5b07e74d18c0c7e6d21c8f9478cd6d93be03d7a52842e6f54031456107910e
  • Pointer size: 131 Bytes
  • Size of remote file: 917 kB
samples/unet_1280x1280_0.jpg ADDED

Git LFS Details

  • SHA256: fb409b1e70fdcc8317a0ddcc2d7b4ca8f655cc031d30892b90f3802766158245
  • Pointer size: 131 Bytes
  • Size of remote file: 296 kB
samples/unet_1280x640_0.jpg ADDED

Git LFS Details

  • SHA256: 451a6e8d12a13115d0feb583beacad2e31fb2e97a2b8678755ab46702cd27380
  • Pointer size: 131 Bytes
  • Size of remote file: 171 kB
samples/unet_1280x704_0.jpg ADDED

Git LFS Details

  • SHA256: 3c4cb0fa46a390c54a9ca2d6dd7b88c61f9e00abe128fc40ae892a81c2f65422
  • Pointer size: 131 Bytes
  • Size of remote file: 619 kB
samples/unet_1280x768_0.jpg ADDED

Git LFS Details

  • SHA256: 5962250a3912312ab3c7221e36d5b9f716d76892fb3bb0815301ed30baa66cfa
  • Pointer size: 131 Bytes
  • Size of remote file: 223 kB
samples/unet_1280x832_0.jpg ADDED

Git LFS Details

  • SHA256: ef44d854a0c36733053782c013e6e638d3b838adb84cccc91c3f09a854731753
  • Pointer size: 131 Bytes
  • Size of remote file: 408 kB
samples/unet_1280x896_0.jpg ADDED

Git LFS Details

  • SHA256: a3dacea502f042a22dbfe930478d9eb16a18038ff1530827825cb31618175be6
  • Pointer size: 131 Bytes
  • Size of remote file: 286 kB
samples/unet_1280x960_0.jpg ADDED

Git LFS Details

  • SHA256: 91646aead6bccf9dc66d7d5b08afccefaae9fb95029343780ea674c710503e0e
  • Pointer size: 131 Bytes
  • Size of remote file: 432 kB
samples/unet_640x1280_0.jpg ADDED

Git LFS Details

  • SHA256: 9ab48356d3f2fdb33bf1e9a51626ddfe510045170b2ec66a5521bdce9572c425
  • Pointer size: 131 Bytes
  • Size of remote file: 211 kB
samples/unet_704x1280_0.jpg ADDED

Git LFS Details

  • SHA256: 8f8f14d1177a55925787facedd5fffef1298e667c68dcdfc92287a21f951e4c2
  • Pointer size: 131 Bytes
  • Size of remote file: 229 kB
samples/unet_768x1280_0.jpg ADDED

Git LFS Details

  • SHA256: 79f5f1a0dd3fa9a4bc8523f0fda5b39f989df21f8dc9f63c2bf786b73cfdbd12
  • Pointer size: 131 Bytes
  • Size of remote file: 302 kB
samples/unet_832x1280_0.jpg ADDED

Git LFS Details

  • SHA256: a5fb6786b054505a9d2ca6e9b8d734a65e93dfcb168c01873d9abca040c2ec6c
  • Pointer size: 131 Bytes
  • Size of remote file: 314 kB
samples/unet_896x1280_0.jpg ADDED

Git LFS Details

  • SHA256: 0c8cd568663a9c284693182fa3814141f8f64de8d42eb1501d3e429acaa3318a
  • Pointer size: 131 Bytes
  • Size of remote file: 278 kB
samples/unet_960x1280_0.jpg ADDED

Git LFS Details

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