Text-to-Image
Diffusers
Safetensors
recoilme commited on
Commit
8d75436
·
1 Parent(s): 6fdd5cf
grid.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from diffusers import DiffusionPipeline
3
+ from pipeline_sdxs import SdxsPipeline
4
+
5
+ device = "cuda"
6
+ pipe_id = "/workspace/sdxs-1b"
7
+ pipeline = SdxsPipeline.from_pretrained(
8
+ pipe_id,
9
+ ).to(device=device, dtype=torch.float16)
10
+ print(pipeline)
11
+ prompts = [
12
+ "A digital illustration of a young male character with white hair, blue eyes, and a dark complexion. He is wearing a white jacket, brown pants, and a brown belt. His hands are in his pockets, and he is wearing a wristwatch. He has a pouch on his belt and is wearing glasses. The background is a simple"
13
+ ,"A young woman with striking blue eyes and pointed ears, adorned with a floral kimono and a tattoo. Her hair is styled in a braid, and she wears a pair of ears"
14
+ ,"In the center of a futuristic space station, a highly advanced N7 soldier stands at attention. The soldier, clad in a suit of power armor, is equipped with a helmet that features a visor and a rifle slung over the shoulder. The armor is a striking combination of gray and red"
15
+ ,"A muscular, topless male with tiger-like ears and a tail stands in a forest, holding a sword and wearing a blue outfit, gazing directly at the viewer"
16
+ ,"A young woman with striking features, a mix of black and gold hair floating around her head, vibrant yellow eyes half-closed, and a blue sweater contrasting the black background, framed in a simple yet striking composition."
17
+ ,'A fluffy domestic cat with piercing green eyes sits attentively in a sunlit room filled with natural light, its soft fur reflecting warm hues of orange through golden windows.'
18
+ ,"A fierce woman in black-and-white, wearing a spiked iron mask with sharp metallic spikes, sporting a high ponytail, her skin marked with battle-dirt, and eyes that reflect determination and strength. The mask's menacing aura contrasts with her intense expression, capturing her mysterious and intimidating nature."
19
+ ,"A close-up of an astronaut's helmet with frosted, opaque visor, reflecting space's cold, frozen texture, resting on the visor a butterfly with vibrant, intricately patterned wings, and distant stars' faint glow."
20
+ ,"A striking character with red eyes and black uniform wields a sword in a defensive stance, poised for battle amidst a stark white background with vibrant red accents."
21
+ ,"A bald woman with a tattooed upper body stands against a vibrant red background. She is adorned with a pair of dangling earrings and a dress with a dragon design"
22
+ ,"white cyborg knight riding cyber horse with wings, long white gown, holding scythe, skeleton horse, zombies cyberpunk armor, feathers"
23
+ ,"A radiant voluptuous woman in Arizona's Grand Canyon at twilight, her fiery ginger hair cascading over a halter top with intricate lace, plunging neckline, black thongs, silver rings, and stacked bangles, surrounded by deep purples and burnt oranges in the sky, her light-blue eyes glowing with wonder as she gazes at the luminous beauty of the night."
24
+ ,"A man with hair, a white suit and black scarf, anime-style elements, glowing pink horns, and cyberpunk manga art style, standing in a shadowy smoke-filled background, with glowing pink eyes and a side view portrait of his face, white skin, and ear piercings."
25
+ ,"There is a young male character standing against a vibrant, colorful graffiti wall. he is wearing a straw hat, a black jacket adorned with gold accents, and black shorts."
26
+ ,"A black BMW M3 sports car with black and yellow rims"
27
+ ,"A young girl in a flowing, vibrant dress, her glowing eyes capturing the warmth of the day, sits on a grassy field, surrounded by anime-style elements."
28
+ ,"A young woman with a messy bun and vibrant red hoodie stands ready to strike with a large axe, her intense gaze and dynamic pose evoking energy and anticipation."
29
+ ,"A small bluebird with a white breast and black stripe perches on a branch, facing right with a blurred background of trees and bushes."
30
+ ,"A stylized, anime-style illustration of a young male character with white hair, wearing a white suit and holding two guns. He is standing with his arms outstretched and a confident smile on his face."
31
+ ,"In the center of a white background, a fantasy creature with blue eyes and a white body stands on its hind legs. Its front legs are adorned with armor, and its tail is adorned with scales. The creature has a large, spiky crest on its head and sharp claws"
32
+ ,"In a serene garden, two young girls stand side by side, their youthful energy palpable. The girl on the left, adorned with a blue dress and a matching blue flower in her hair, gazes directly at the viewer, her eyes sparkling with curiosity."
33
+ ]
34
+ prompts2 = ["Космонавт на мотоцикле"]
35
+
36
+ negative_prompt="low quality, bad quality, photo, low details"
37
+ negative_prompt2 = "worst quality, low quality, photo, low details, blurry, jpeg artifacts, unfinished, sketch, sepia, missing limb, text, bad anatomy, bad proportions, bad hands, missing fingers"
38
+ negative_prompt = "bad quality grainy image with low details, incomplete text, despite numerous technical flaws and distorted figures"
39
+
40
+ all_images = []
41
+ # Обработка батчей с прогресс-баром
42
+ for i, prompt in enumerate(prompts):
43
+ image = pipeline(
44
+ prompt = prompt,
45
+ negative_prompt = negative_prompt,
46
+ guidance_scale = 5,
47
+ width = 1024,
48
+ height = 1408,
49
+ seed = 0,
50
+ batch_size = 1,
51
+ )[0]
52
+ all_images.extend(image)
53
+ import matplotlib.pyplot as plt
54
+ import math
55
+ import numpy as np
56
+ import textwrap
57
+
58
+ def display_image_grid(images, prompts, cols=3, save_path=None):
59
+ n = len(images)
60
+ if n == 0:
61
+ return
62
+
63
+ rows = math.ceil(n / cols)
64
+
65
+ # constrained_layout вместо tight_layout лучше держит одинаковую высоту ячеек
66
+ fig, axes = plt.subplots(rows, cols, figsize=(cols * 4, rows * 4.5), constrained_layout=True)
67
+
68
+ if isinstance(axes, np.ndarray):
69
+ axes = axes.flatten()
70
+ elif isinstance(axes, plt.Axes):
71
+ axes = [axes]
72
+
73
+ axes = list(axes)[:n]
74
+
75
+ for i, (img, prompt) in enumerate(zip(images, prompts)):
76
+ ax = axes[i]
77
+ ax.imshow(img)
78
+ ax.axis("off")
79
+ ax.set_aspect("equal")
80
+
81
+ text = (prompt[:200] + "…") if len(prompt) > 200 else prompt
82
+
83
+ # Разбиваем на строки
84
+ lines = textwrap.wrap(text, width=35)
85
+
86
+ while len(lines) < 4:
87
+ lines.append("")
88
+
89
+ ax.set_title("\n".join(lines), fontsize=9, pad=8)
90
+ # -----------------------------------
91
+
92
+ if save_path:
93
+ # Для сохранения constrained_layout работает автоматически
94
+ plt.savefig(save_path, bbox_inches="tight", dpi=600, format="jpeg")
95
+
96
+ #plt.show()
97
+
98
+
99
+ display_image_grid(all_images, prompts, save_path="media/result_grid.jpg")
media/girl1.jpg ADDED

Git LFS Details

  • SHA256: 4b03cfa26d165a249b477b90862f62fc3a6b45e39d3d3faddd381c7076928182
  • Pointer size: 131 Bytes
  • Size of remote file: 198 kB
test.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from diffusers import DiffusionPipeline
3
+
4
+ device = "cuda" if torch.cuda.is_available() else "cpu"
5
+ dtype = torch.float16 if torch.cuda.is_available() else torch.float32
6
+
7
+ pipe_id = "/workspace/sdxs-1b"
8
+ pipe = DiffusionPipeline.from_pretrained(
9
+ pipe_id,
10
+ torch_dtype=dtype,
11
+ trust_remote_code=True
12
+ ).to(device)
13
+
14
+ prompt="There is a young male character standing against a vibrant, colorful graffiti wall. he is wearing a hat, a jacket adorned with gold accents, and black shorts."
15
+ #prompt = "A stylized digital detailed illustration of a melancholy female character with white hair and distinct pointed ears, seated cross-legged, wearing a sleeveless top featuring a bold red and black design on her thighs, paired with black thigh-high boots."
16
+ #prompt = "A young woman with striking blue eyes and pointed ears, with a dragon at background, adorned with a kimono and a tattoo. Her hair is styled in a braid, and she wears a pair of ears"
17
+ refined = pipe.refine_prompts(prompt)[0]
18
+ negative_prompt = "worst quality, low quality, photo, low details, blurry, jpeg artifacts, unfinished, sketch, sepia, missing limb, text, bad anatomy, bad proportions, bad hands, missing fingers"
19
+
20
+ print(refined)
21
+ output = pipe(
22
+ prompt=refined,
23
+ negative_prompt=negative_prompt,
24
+ #seed=43,
25
+ )
26
+
27
+ # Берем нулевую картинку и сам промпт (он будет строкой, так как мы подавали строку)
28
+ image = output.images[0]
29
+ refined_prompt_text = output.prompt
30
+
31
+ image.show()
32
+ image.save("media/girl1.jpg")
33
+
34
+ print(f"✨ refined_prompt_text:\n{refined_prompt_text}")
train4080.py ADDED
@@ -0,0 +1,837 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import math
3
+ import torch
4
+ import numpy as np
5
+ import matplotlib.pyplot as plt
6
+ import wandb,comet_ml
7
+ import random,time
8
+ import gc
9
+ import bitsandbytes as bnb
10
+ import torch.nn.functional as F
11
+ import argparse
12
+
13
+ from diffusers import UNet2DConditionModel, AsymmetricAutoencoderKL, FlowMatchEulerDiscreteScheduler
14
+ from transformers import Qwen3_5Tokenizer, Qwen3_5ForConditionalGeneration
15
+ from torch.utils.data import DataLoader, Sampler
16
+ from torch.optim.lr_scheduler import LambdaLR
17
+ from collections import defaultdict
18
+ from accelerate import Accelerator
19
+ from datasets import load_from_disk
20
+ from tqdm import tqdm
21
+ from PIL import Image, ImageOps
22
+ from torch.utils.checkpoint import checkpoint
23
+ from diffusers.models.attention_processor import AttnProcessor2_0
24
+ from datetime import datetime
25
+
26
+ # Muon not tested! pip install git+https://github.com/recoilme/muon_adamw8bit.git
27
+ from muon_adamw8bit import MuonAdamW8bit
28
+ from transformers.optimization import Adafactor
29
+
30
+ os.environ["NCCL_P2P_DISABLE"] = "1"
31
+ os.environ["NCCL_IB_DISABLE"] = "1" # comment this on H100!
32
+ os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
33
+
34
+ # --------------------------- Параметры ---------------------------
35
+ ds_path = "datasets/alchemist_704_vae8x16x_imgpool"
36
+ project = "unet"
37
+
38
+ gpu_mem_gb = torch.cuda.get_device_properties(0).total_memory / 1e9
39
+ local_bs = max(1, int((gpu_mem_gb / 32) * 7))
40
+ num_gpus = torch.cuda.device_count()
41
+ batch_size = local_bs * num_gpus
42
+
43
+ base_learning_rate = 4e-5
44
+ min_learning_rate = 4e-6
45
+
46
+ # 0.5 - pretrain (base forms)
47
+ # 1 - base train (composition)
48
+ # 3 - finetuning (anatomy)
49
+ # 5 - small details (faces)
50
+ learning_rate_scale = 3
51
+ base_learning_rate = base_learning_rate / learning_rate_scale
52
+ min_learning_rate = min_learning_rate / learning_rate_scale
53
+ print(f"Calculated params max-lr:{base_learning_rate} min-lr:{min_learning_rate} GPUs: {num_gpus}, Global BS: {batch_size}")
54
+
55
+ num_epochs = num_gpus
56
+ sink_interval_share = 20
57
+ cfg_dropout = 0.10
58
+ max_length = 248
59
+ use_precomputed_embeddings = True
60
+ use_wandb = False
61
+ use_comet_ml = True
62
+ save_model = True
63
+ use_decay = True
64
+ fbp = True
65
+ torch_compile = False
66
+ unet_gradient = True
67
+ loss_normalize = False
68
+ fixed_seed = False
69
+ shuffle = True
70
+ optimizer_type = "adam8bit"
71
+ if optimizer_type == "muon_adam8bit":
72
+ batch_size = num_gpus * max(1, int((gpu_mem_gb / 32) * 3))
73
+ muon_lr_scale = 500
74
+ comet_ml_api_key = "Agctp26mbqnoYrrlvQuKSTk6r" # hardcoded for blind run, i don't care about key
75
+ comet_ml_workspace = "recoilme"
76
+ torch.backends.cuda.matmul.allow_tf32 = True
77
+ torch.backends.cudnn.allow_tf32 = True
78
+ # MAX_JOBS=4 pip install flash-attn --no-build-isolation
79
+ torch.backends.cuda.enable_flash_sdp(True)
80
+ torch.backends.cuda.enable_mem_efficient_sdp(True)
81
+ torch.backends.cuda.enable_math_sdp(False) # Отключаем медленный вариант
82
+ save_barrier = 1.25
83
+ warmup_percent = 0.0025
84
+ betta2 = 0.997
85
+ eps = 1e-7
86
+ clip_grad_norm = 1.0
87
+ limit = 0
88
+ checkpoints_folder = ""
89
+ gradient_accumulation_steps = 1
90
+ dtype = torch.float32
91
+ mixed_precision = "bf16"
92
+
93
+ # Параметры для диффузии
94
+ n_diffusion_steps = 40
95
+ samples_to_generate = 12
96
+ guidance_scale = 1
97
+
98
+ # Папки для сохранения результатов
99
+ generated_folder = "samples"
100
+ os.makedirs(generated_folder, exist_ok=True)
101
+
102
+ # Настройка seed
103
+ current_date = datetime.now()
104
+ seed = int(current_date.strftime("%Y%m%d")) + 42
105
+ if fixed_seed:
106
+ torch.manual_seed(seed)
107
+ np.random.seed(seed)
108
+ random.seed(seed)
109
+ if torch.cuda.is_available():
110
+ torch.cuda.manual_seed_all(seed)
111
+
112
+ accelerator = Accelerator(
113
+ mixed_precision=mixed_precision,
114
+ gradient_accumulation_steps=gradient_accumulation_steps
115
+ )
116
+ device = accelerator.device
117
+
118
+ print("init")
119
+ # Создаём объект ArgumentParser с рассчитанными значениями по умолчанию
120
+ parser = argparse.ArgumentParser(description='Train a model on a dataset.')
121
+ parser.add_argument('--ds-path', type=str, default=ds_path, help='Path to the dataset')
122
+ parser.add_argument('--ep', type=int, default=num_epochs, help='Number of epochs to train the model')
123
+ parser.add_argument('--batch', type=int, default=batch_size, help='Total batch size')
124
+ parser.add_argument('--min-lr', type=float, default=min_learning_rate, help='Minimum learning rate')
125
+ parser.add_argument('--max-lr', type=float, default=base_learning_rate, help='Maximum learning rate')
126
+ parser.add_argument('--dry-run', action='store_true',default=False, help='Dry run train without saving/sampling')
127
+ parser.add_argument('--lvl', type=float, default=0.0, help='Train level, from 0.5 to 5')
128
+
129
+ # Парсим аргументы командной строки
130
+ args = parser.parse_args()
131
+
132
+ # Используем значения из аргументов
133
+ batch_size = args.batch
134
+ ds_path = args.ds_path
135
+ base_learning_rate = args.max_lr
136
+ min_learning_rate = args.min_lr
137
+ num_epochs = args.ep
138
+ lvl = args.lvl
139
+ if args.dry_run:
140
+ save_model = False
141
+ if lvl >= 0.1:
142
+ base_learning_rate = base_learning_rate / lvl
143
+ min_learning_rate = min_learning_rate / lvl
144
+ print(f"max-lr:{base_learning_rate} min-lr:{min_learning_rate}")
145
+
146
+
147
+ # --------------------------- Инициализация WandB ---------------------------
148
+ if accelerator.is_main_process:
149
+ if use_wandb:
150
+ wandb.init(project=project, config={
151
+ "batch_size": batch_size,
152
+ "base_learning_rate": base_learning_rate,
153
+ "num_epochs": num_epochs,
154
+ "optimizer_type": optimizer_type,
155
+ })
156
+ if use_comet_ml:
157
+ from comet_ml import Experiment
158
+ comet_experiment = Experiment(
159
+ api_key=comet_ml_api_key,
160
+ project_name=project,
161
+ workspace=comet_ml_workspace
162
+ )
163
+ hyper_params = {
164
+ "batch_size": batch_size,
165
+ "base_learning_rate": base_learning_rate,
166
+ "num_epochs": num_epochs,
167
+ }
168
+ comet_experiment.log_parameters(hyper_params)
169
+
170
+ # --------------------------- Загрузка моделей ---------------------------
171
+ vae = AsymmetricAutoencoderKL.from_pretrained("vae", torch_dtype=dtype).to("cpu").eval()
172
+ scheduler = FlowMatchEulerDiscreteScheduler.from_pretrained("scheduler")
173
+ tokenizer = None
174
+ text_encoder = None
175
+
176
+ def load_text_encoder():
177
+ global tokenizer, text_encoder
178
+ if tokenizer is None:
179
+ tokenizer = Qwen3_5Tokenizer.from_pretrained("tokenizer")
180
+ if text_encoder is None:
181
+ text_encoder = Qwen3_5ForConditionalGeneration.from_pretrained(
182
+ "text_encoder",
183
+ torch_dtype=torch.float16
184
+ ).to(device).eval()
185
+
186
+ load_text_encoder()
187
+
188
+ @torch.no_grad()
189
+ def encode_texts(text, max_length=max_length):
190
+ if text is None: text = ""
191
+ if isinstance(text, str): text = [text]
192
+
193
+ formatted_prompts = []
194
+ for t in text:
195
+ messages = [{"role": "user", "content": [{"type": "text", "text": t}]}]
196
+ formatted_prompts.append(tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False))
197
+
198
+ toks = tokenizer(formatted_prompts, padding="max_length", max_length=max_length, truncation=True, return_tensors="pt").to(device)
199
+ outputs = text_encoder(input_ids=toks.input_ids, attention_mask=toks.attention_mask, output_hidden_states=True)
200
+
201
+ last_hidden = outputs.hidden_states[-2]
202
+
203
+ return last_hidden.to(dtype=torch.float16), toks.attention_mask.to(dtype=torch.bool)
204
+
205
+ shift_factor = getattr(vae.config, "shift_factor", 0.0)
206
+ if shift_factor is None:
207
+ shift_factor = 0.0
208
+
209
+ scaling_factor = getattr(vae.config, "scaling_factor", 1.0)
210
+ if scaling_factor is None:
211
+ scaling_factor = 1.0
212
+
213
+ mean = getattr(vae.config, "latents_mean", None)
214
+ std = getattr(vae.config, "latents_std", None)
215
+ if mean is not None and std is not None:
216
+ latents_std = torch.tensor(std, device=device, dtype=dtype).view(1, len(std), 1, 1)
217
+ latents_mean = torch.tensor(mean, device=device, dtype=dtype).view(1, len(mean), 1, 1)
218
+
219
+ import numpy as np
220
+ from torch.utils.data import Sampler
221
+
222
+
223
+ class DistributedResolutionBatchSampler(Sampler):
224
+ def __init__(self, dataset, batch_size, num_replicas, rank, drop_last=True, shuffle=True):
225
+ self.dataset = dataset
226
+ self.num_replicas = num_replicas
227
+ self.rank = rank
228
+ self.shuffle = shuffle
229
+ self.drop_last = drop_last
230
+ self.epoch = 0
231
+
232
+ # batch на одну GPU
233
+ self.batch_size = max(1, batch_size // num_replicas)
234
+ self.global_batch = self.batch_size * num_replicas
235
+
236
+ try:
237
+ widths = np.asarray(dataset["width"])
238
+ heights = np.asarray(dataset["height"])
239
+ except KeyError:
240
+ widths = np.zeros(len(dataset))
241
+ heights = np.zeros(len(dataset))
242
+
243
+ # --- группировка индексов ---
244
+ groups = {}
245
+ for i, (w, h) in enumerate(zip(widths, heights)):
246
+ groups.setdefault((w, h), []).append(i)
247
+
248
+ # --- создаём список всех глобальных батчей ---
249
+ all_batches = []
250
+
251
+ for indices in groups.values():
252
+
253
+ idx = np.asarray(indices, dtype=np.int64)
254
+
255
+ num_batches = len(idx) // self.global_batch
256
+ if num_batches == 0:
257
+ continue
258
+
259
+ idx = idx[: num_batches * self.global_batch]
260
+
261
+ batches = idx.reshape(num_batches, self.global_batch)
262
+
263
+ all_batches.append(batches)
264
+
265
+ if len(all_batches) > 0:
266
+ self.global_batches = np.concatenate(all_batches, axis=0)
267
+ else:
268
+ self.global_batches = np.empty((0, self.global_batch), dtype=np.int64)
269
+
270
+ self.num_batches = len(self.global_batches)
271
+
272
+ def __iter__(self):
273
+
274
+ rng = np.random.RandomState(self.epoch)
275
+
276
+ order = np.arange(self.num_batches)
277
+
278
+ if self.shuffle:
279
+ rng.shuffle(order)
280
+
281
+ start = self.rank * self.batch_size
282
+ end = start + self.batch_size
283
+
284
+ for i in order:
285
+ yield self.global_batches[i][start:end]
286
+
287
+ def __len__(self):
288
+ return self.num_batches
289
+
290
+ def set_epoch(self, epoch):
291
+ self.epoch = epoch
292
+
293
+
294
+
295
+ # --- [UPDATED] Функция для фиксированных семплов (ХРАНИМ НА CPU) ---
296
+ def get_fixed_samples_by_resolution(dataset, samples_per_group=1):
297
+ size_groups = defaultdict(list)
298
+ try:
299
+ widths = dataset["width"]
300
+ heights = dataset["height"]
301
+ except KeyError:
302
+ widths = [0] * len(dataset)
303
+ heights = [0] * len(dataset)
304
+ for i, (w, h) in enumerate(zip(widths, heights)):
305
+ size = (w, h)
306
+ size_groups[size].append(i)
307
+
308
+ fixed_samples = {}
309
+ for size, indices in size_groups.items():
310
+ n_samples = min(samples_per_group, len(indices))
311
+ if len(size_groups)==1:
312
+ n_samples = samples_to_generate
313
+ if n_samples == 0:
314
+ continue
315
+ sample_indices = random.sample(indices, n_samples)
316
+ samples_data = [dataset[idx] for idx in sample_indices]
317
+
318
+ # Латенты лежат строго на CPU (без .to(device))
319
+ latents = torch.tensor(np.array([item["vae"] for item in samples_data]), dtype=dtype)
320
+ texts = [item["text"] for item in samples_data]
321
+
322
+ if use_precomputed_embeddings:
323
+ # Эмбеддинги лежат строго на CPU
324
+ embeddings = torch.tensor(
325
+ np.array([item["embeddings"] for item in samples_data]),
326
+ dtype=torch.float16
327
+ )
328
+ masks = torch.tensor(
329
+ np.array([item["attention_mask"] for item in samples_data]),
330
+ dtype=torch.bool
331
+ )
332
+ else:
333
+ # encode_texts возвращает GPU тензоры, поэтому принудительно возвращаем их на CPU для хранения
334
+ embeddings, masks = encode_texts(texts)
335
+ embeddings = embeddings.cpu()
336
+ masks = masks.cpu()
337
+
338
+ fixed_samples[size] = (latents, embeddings, masks, texts)
339
+
340
+ print(f"Создано {len(fixed_samples)} групп фиксированных семплов по разрешениям (хранятся в RAM на CPU)")
341
+ return fixed_samples
342
+
343
+ if limit > 0:
344
+ dataset = load_from_disk(ds_path).select(range(limit))
345
+ else:
346
+ dataset = load_from_disk(ds_path)
347
+
348
+
349
+ print(f"images: {len(dataset)}")
350
+
351
+ def collate_fn_simple(batch):
352
+
353
+ latents = torch.from_numpy(
354
+ np.array([item["vae"] for item in batch], dtype=np.float16)
355
+ ).to(device, dtype=torch.float16)
356
+
357
+ if use_precomputed_embeddings:
358
+ embeddings = torch.from_numpy(
359
+ np.array([item["embeddings"] for item in batch], dtype=np.float16)
360
+ ).to(device, dtype=torch.float16)
361
+
362
+ attention_mask = torch.from_numpy(
363
+ np.array([item["attention_mask"] for item in batch], dtype=np.int64)
364
+ ).to(device)
365
+
366
+ return latents, embeddings, attention_mask
367
+
368
+ raw_texts = [item["text"] for item in batch]
369
+
370
+ texts = [
371
+ "" if t.lower().startswith("zero")
372
+ else "" if random.random() < cfg_dropout
373
+ else t[1:].lstrip() if t.startswith(".")
374
+ else t.replace("The image shows ", "").replace("The image is ", "").replace("This image captures ","").strip()
375
+ for t in raw_texts
376
+ ]
377
+
378
+ embeddings, attention_mask = encode_texts(texts)
379
+ attention_mask = attention_mask.to(dtype=torch.bool)
380
+
381
+ return latents, embeddings, attention_mask
382
+
383
+ batch_sampler = DistributedResolutionBatchSampler(
384
+ dataset=dataset,
385
+ batch_size=batch_size,
386
+ num_replicas=accelerator.num_processes,
387
+ rank=accelerator.process_index,
388
+ shuffle = shuffle
389
+ )
390
+
391
+ dataloader = DataLoader(dataset, batch_sampler=batch_sampler, collate_fn=collate_fn_simple)
392
+
393
+ if accelerator.is_main_process:
394
+ print("Total samples", len(dataloader))
395
+ dataloader = accelerator.prepare(dataloader)
396
+
397
+ start_epoch = 0
398
+ global_step = 0
399
+ total_training_steps = (len(dataloader) * num_epochs)
400
+ world_size = accelerator.state.num_processes
401
+
402
+ # Загрузка UNet
403
+ latest_checkpoint = os.path.join(checkpoints_folder, project)
404
+ if os.path.isdir(latest_checkpoint):
405
+ print("Загружаем UNet из чекпоинта:", latest_checkpoint)
406
+ unet = UNet2DConditionModel.from_pretrained(latest_checkpoint).to(device=device, dtype=dtype)
407
+ if unet_gradient:
408
+ unet.enable_gradient_checkpointing()
409
+ unet.set_use_memory_efficient_attention_xformers(False)
410
+ try:
411
+ unet.set_attn_processor(AttnProcessor2_0())
412
+ except Exception as e:
413
+ print(f"Ошибка при включении SDPA: {e}")
414
+ unet.set_use_memory_efficient_attention_xformers(True)
415
+ else:
416
+ raise FileNotFoundError(f"UNet checkpoint not found at {latest_checkpoint}")
417
+
418
+ def create_optimizer(name, params):
419
+ if name == "adam8bit":
420
+ return bnb.optim.AdamW8bit(
421
+ params, lr=base_learning_rate, betas=(0.9, betta2), eps=eps, weight_decay=0.01
422
+ )
423
+ elif name == "adam":
424
+ return torch.optim.AdamW(
425
+ params, lr=base_learning_rate, betas=(0.9, betta2), eps=eps, weight_decay=0.01
426
+ )
427
+ elif name == "muon_adam8bit":
428
+ return MuonAdamW8bit(
429
+ params,
430
+ lr=base_learning_rate,
431
+ betas=(0.9, betta2),
432
+ eps=eps,
433
+ weight_decay=0.01,
434
+ muon_lr_mult=muon_lr_scale,
435
+ )
436
+ elif name == "adafactor":
437
+ return Adafactor(
438
+ params,
439
+ lr=base_learning_rate,
440
+ scale_parameter=False, # ВАЖНО (иначе авто lr)
441
+ relative_step=False, # ВАЖНО
442
+ warmup_init=False,
443
+ beta1=0.9, # как Adam
444
+ clip_threshold=1.0,
445
+ decay_rate=betta2, # аналог beta2
446
+ weight_decay=0.01,
447
+ eps=(1e-30, eps) # стандарт
448
+ )
449
+ else:
450
+ raise ValueError(f"Unknown optimizer: {name}")
451
+
452
+ if fbp:
453
+ trainable_params = list(unet.parameters())
454
+ optimizer_dict = {p: create_optimizer(optimizer_type, [p]) for p in trainable_params}
455
+ def optimizer_hook(param):
456
+ optimizer_dict[param].step()
457
+ optimizer_dict[param].zero_grad(set_to_none=True)
458
+ for param in trainable_params:
459
+ param.register_post_accumulate_grad_hook(optimizer_hook)
460
+ unet, optimizer = accelerator.prepare(unet, optimizer_dict)
461
+ else:
462
+ unet.requires_grad_(True)
463
+ optimizer = create_optimizer(optimizer_type, unet.parameters())
464
+
465
+ def lr_schedule(step):
466
+ x = step / (total_training_steps * world_size)
467
+ warmup = warmup_percent
468
+ if not use_decay:
469
+ return base_learning_rate
470
+ if x < warmup:
471
+ return min_learning_rate + (base_learning_rate - min_learning_rate) * (x / warmup)
472
+ decay_ratio = (x - warmup) / (1 - warmup)
473
+ return min_learning_rate + 0.5 * (base_learning_rate - min_learning_rate) * \
474
+ (1 + math.cos(math.pi * decay_ratio))
475
+ lr_scheduler = LambdaLR(optimizer, lambda step: lr_schedule(step) / base_learning_rate)
476
+ unet, optimizer, lr_scheduler = accelerator.prepare(unet, optimizer, lr_scheduler)
477
+
478
+ if torch_compile:
479
+ print("compiling")
480
+ unet = torch.compile(unet)
481
+ print("compiling - ok")
482
+
483
+ # Фиксированные семплы (теперь они создаются и лежат на CPU)
484
+ fixed_samples = get_fixed_samples_by_resolution(dataset)
485
+
486
+ # --- Функция для негативного эмбеддинга ---
487
+ def get_negative_embedding(neg_prompt="", batch_size=1):
488
+ if not neg_prompt:
489
+ hidden_dim = 2048
490
+ seq_len = 1063 #max_length
491
+ empty_emb = torch.zeros((batch_size, seq_len, hidden_dim), dtype=torch.float16, device=device)
492
+ empty_mask = torch.ones((batch_size, seq_len), dtype=torch.bool, device=device)
493
+ return empty_emb, empty_mask
494
+
495
+ uncond_emb, uncond_mask = encode_texts([neg_prompt])
496
+ uncond_emb = uncond_emb.to(dtype=dtype, device=device).repeat(batch_size, 1, 1)
497
+ uncond_mask = uncond_mask.to(device=device).repeat(batch_size, 1)
498
+
499
+ return uncond_emb, uncond_mask
500
+
501
+ # Получаем негативные (пустые) условия для валидации
502
+ if use_precomputed_embeddings:
503
+ # 1. грузим encoder ВРЕМЕННО
504
+ load_text_encoder()
505
+
506
+ # 2. считаем negative
507
+ uncond_emb, uncond_mask = get_negative_embedding()
508
+
509
+ # 3. уносим на CPU (очень важно)
510
+ uncond_emb = uncond_emb.to("cpu")
511
+ uncond_mask = uncond_mask.to("cpu")
512
+
513
+ # 4. выгружаем encoder с GPU
514
+ del text_encoder
515
+ torch.cuda.empty_cache()
516
+ gc.collect()
517
+
518
+ text_encoder = None
519
+
520
+ else:
521
+ uncond_emb, uncond_mask = get_negative_embedding("low quality")
522
+ # Уносим негативные эмбеддинги на CPU, чтобы не занимали VRAM во время трейна
523
+ uncond_emb = uncond_emb.to("cpu")
524
+ uncond_mask = uncond_mask.to("cpu")
525
+
526
+ # --- Функция генерации семплов ---
527
+ @torch.compiler.disable()
528
+ @torch.no_grad()
529
+ def generate_and_save_samples(fixed_samples_cpu, uncond_data, step):
530
+ torch.cuda.synchronize()
531
+ torch.cuda.empty_cache()
532
+ gc.collect()
533
+ # 1. Временно переносим негативные эмбеддинги на GPU
534
+ uncond_emb, uncond_mask = uncond_data
535
+ uncond_emb = uncond_emb.to(device)
536
+ uncond_mask = uncond_mask.to(device)
537
+
538
+ original_model = None
539
+ try:
540
+ if not torch_compile:
541
+ original_model = accelerator.unwrap_model(unet, keep_torch_compile=True).eval()
542
+ else:
543
+ original_model = unet.eval()
544
+
545
+ vae.to(dtype=torch.float16,device=device).eval()
546
+
547
+ all_generated_images = []
548
+ all_captions = []
549
+
550
+ # 2. Проходим по группам, дергая CPU тензоры и перенося их на GPU только здесь
551
+ for size, (sample_latents, sample_text_embeddings, sample_mask, sample_text) in fixed_samples_cpu.items():
552
+ width, height = size
553
+ # Перенос на GPU происходит в момент вызова .to()
554
+ sample_latents = sample_latents.to(dtype=dtype, device=device)
555
+ sample_text_embeddings = sample_text_embeddings.to(dtype=dtype, device=device)
556
+ sample_mask = sample_mask.to(device=device)
557
+
558
+ latents = torch.randn(
559
+ sample_latents.shape,
560
+ device=device,
561
+ dtype=sample_latents.dtype,
562
+ generator=torch.Generator(device=device).manual_seed(seed)
563
+ )
564
+
565
+ scheduler.set_timesteps(n_diffusion_steps, device=device)
566
+
567
+ for t in scheduler.timesteps:
568
+ if guidance_scale != 1:
569
+ latent_model_input = torch.cat([latents, latents], dim=0)
570
+
571
+ curr_batch_size = sample_text_embeddings.shape[0]
572
+ seq_len = sample_text_embeddings.shape[1]
573
+ hidden_dim = sample_text_embeddings.shape[2]
574
+
575
+ neg_emb_batch = uncond_emb[0:1].expand(curr_batch_size, -1, -1)
576
+ text_embeddings_batch = torch.cat([neg_emb_batch, sample_text_embeddings], dim=0)
577
+
578
+ neg_mask_batch = uncond_mask[0:1].expand(curr_batch_size, -1)
579
+ attention_mask_batch = torch.cat([neg_mask_batch, sample_mask], dim=0)
580
+
581
+ else:
582
+ latent_model_input = latents
583
+ text_embeddings_batch = sample_text_embeddings
584
+ attention_mask_batch = sample_mask
585
+
586
+ model_out = original_model(
587
+ latent_model_input,
588
+ t,
589
+ encoder_hidden_states=text_embeddings_batch,
590
+ encoder_attention_mask=attention_mask_batch,
591
+ )
592
+
593
+ flow = getattr(model_out, "sample", model_out)
594
+
595
+ if guidance_scale != 1:
596
+ flow_uncond, flow_cond = flow.chunk(2)
597
+ flow = flow_uncond + guidance_scale * (flow_cond - flow_uncond)
598
+
599
+ latents = scheduler.step(flow, t, latents).prev_sample
600
+
601
+ current_latents = latents
602
+ if step==0:
603
+ current_latents = sample_latents
604
+
605
+ if latents_mean is not None and latents_std is not None:
606
+ latents = current_latents * latents_std + latents_mean
607
+
608
+ decoded = vae.decode(latents.to(torch.float16)).sample
609
+ decoded_fp32 = decoded.to(torch.float32)
610
+
611
+ for img_idx, img_tensor in enumerate(decoded_fp32):
612
+ img = (img_tensor / 2 + 0.5).clamp(0, 1).cpu().numpy()
613
+ img = img.transpose(1, 2, 0)
614
+
615
+ if np.isnan(img).any():
616
+ print("NaNs found, saving stopped! Step:", step)
617
+ pil_img = Image.fromarray((img * 255).astype("uint8"))
618
+
619
+ max_w_overall = max(s[0] for s in fixed_samples_cpu.keys())
620
+ max_h_overall = max(s[1] for s in fixed_samples_cpu.keys())
621
+ max_w_overall = max(255, max_w_overall)
622
+ max_h_overall = max(255, max_h_overall)
623
+
624
+ padded_img = ImageOps.pad(pil_img, (max_w_overall, max_h_overall), color='white')
625
+ all_generated_images.append(padded_img)
626
+
627
+ caption_text = sample_text[img_idx][:300] if img_idx < len(sample_text) else ""
628
+ all_captions.append(caption_text)
629
+
630
+ sample_path = f"{generated_folder}/{project}_{width}x{height}_{img_idx}.jpg"
631
+ pil_img.save(sample_path, "JPEG", quality=95)
632
+
633
+ if use_wandb and accelerator.is_main_process:
634
+ wandb_images = [
635
+ wandb.Image(img, caption=f"{all_captions[i]}")
636
+ for i, img in enumerate(all_generated_images)
637
+ ]
638
+ wandb.log({"generated_images": wandb_images})
639
+ if use_comet_ml and accelerator.is_main_process:
640
+ for i, img in enumerate(all_generated_images):
641
+ comet_experiment.log_image(
642
+ image_data=img,
643
+ name=f"step_{step}_img_{i}",
644
+ step=step,
645
+ metadata={"caption": all_captions[i]}
646
+ )
647
+ finally:
648
+ vae.to("cpu")
649
+ # Возвращаем негативные эмбеддинги обратно на CPU
650
+ uncond_emb = uncond_emb.to("cpu")
651
+ uncond_mask = uncond_mask.to("cpu")
652
+ try:
653
+ all_generated_images.clear()
654
+ all_captions.clear()
655
+ del all_generated_images, all_captions
656
+ del latents, current_latents, latent_model_input, flow
657
+ del decoded, decoded_fp32
658
+ # Временные копии семплов на GPU удалятся здесь.
659
+ # Оригиналы в словаре fixed_samples_cpu останутся нетронутыми на CPU.
660
+ del sample_latents, sample_text_embeddings, sample_mask
661
+ del model_out
662
+ except UnboundLocalError:
663
+ pass
664
+
665
+ torch.cuda.synchronize()
666
+ torch.cuda.empty_cache()
667
+ gc.collect()
668
+
669
+ # --------------------------- Генерация сэмплов перед обучением ---------------------------
670
+ if accelerator.is_main_process:
671
+ if save_model:
672
+ print("Генерация сэмплов до старта обучения...")
673
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), 0)
674
+ accelerator.wait_for_everyone()
675
+
676
+ def save_checkpoint(unet, variant=""):
677
+ if accelerator.is_main_process:
678
+ model_to_save = None
679
+ if not torch_compile:
680
+ model_to_save = accelerator.unwrap_model(unet)
681
+ else:
682
+ model_to_save = unet
683
+
684
+ if variant != "":
685
+ model_to_save.to(dtype=torch.float16).save_pretrained(
686
+ os.path.join(checkpoints_folder, f"{project}"), variant=variant
687
+ )
688
+ else:
689
+ model_to_save.save_pretrained(os.path.join(checkpoints_folder, f"{project}"))
690
+
691
+ torch.cuda.synchronize()
692
+ torch.cuda.empty_cache()
693
+ gc.collect()
694
+
695
+ # --------------------------- Тренировочный цикл ---------------------------
696
+ if accelerator.is_main_process:
697
+ print(f"Total steps per GPU: {total_training_steps}")
698
+
699
+ epoch_loss_points = []
700
+ progress_bar = tqdm(total=total_training_steps, disable=not accelerator.is_local_main_process, desc="Training", unit="step")
701
+
702
+ steps_per_epoch = len(dataloader)
703
+ sink_interval = max(1, steps_per_epoch // sink_interval_share)
704
+ min_loss = 4.
705
+ last_sample_time = time.time()
706
+ sample_interval_seconds = 10 * 60 # 60 минут
707
+
708
+ for epoch in range(start_epoch, start_epoch + num_epochs):
709
+ batch_losses = []
710
+ batch_grads = []
711
+ batch_sampler.set_epoch(epoch)
712
+ accelerator.wait_for_everyone()
713
+ unet.train()
714
+
715
+ for step, (latents, embeddings, attention_mask) in enumerate(dataloader):
716
+ with accelerator.accumulate(unet):
717
+ with accelerator.autocast():
718
+ if save_model == False and epoch == 0 and step == 5 :
719
+ used_gb = torch.cuda.max_memory_allocated() / 1024**3
720
+ print(f"Шаг {step}: {used_gb:.2f} GB")
721
+
722
+ noise = torch.randn_like(latents, dtype=latents.dtype)
723
+
724
+ # 3. Время t, bias = -0.5 (Фокус на Деталях ~300) bias = 0.5 (Фокус на структуре) bias = 0 (колокол/ равномерно)
725
+ bias = 0.1
726
+ t = torch.sigmoid(torch.randn(latents.shape[0], device=latents.device, dtype=latents.dtype) + bias)
727
+
728
+ # интерполяция между x0 и шумом
729
+ noisy_latents = (1.0 - t.view(-1, 1, 1, 1)) * latents + t.view(-1, 1, 1, 1) * noise
730
+ # делаем integer timesteps для UNet
731
+ timesteps = t.to(torch.float32).mul(999.0)
732
+ timesteps = timesteps.clamp(0, scheduler.config.num_train_timesteps - 1)
733
+
734
+ model_pred = unet(
735
+ noisy_latents,
736
+ timesteps,
737
+ encoder_hidden_states=embeddings,
738
+ encoder_attention_mask=attention_mask,
739
+ ).sample
740
+
741
+ target = noise - latents
742
+
743
+ mse_loss = F.mse_loss(model_pred.float(), target.float())
744
+ batch_losses.append(mse_loss.detach().item())
745
+
746
+ if (global_step % 100 == 0) or (global_step % sink_interval == 0):
747
+ accelerator.wait_for_everyone()
748
+
749
+ losses_dict = {}
750
+ losses_dict["mse"] = mse_loss
751
+
752
+ if (global_step % 100 == 0) or (global_step % sink_interval == 0):
753
+ accelerator.wait_for_everyone()
754
+
755
+ accelerator.backward(mse_loss)
756
+
757
+ if (global_step % 100 == 0) or (global_step % sink_interval == 0):
758
+ accelerator.wait_for_everyone()
759
+
760
+ grad = 0.0
761
+ if not fbp:
762
+ if accelerator.sync_gradients:
763
+ grad = 0.
764
+ optimizer.step()
765
+ lr_scheduler.step()
766
+ optimizer.zero_grad(set_to_none=True)
767
+
768
+ if accelerator.sync_gradients:
769
+ global_step += 1
770
+ progress_bar.update(1)
771
+ if accelerator.is_main_process:
772
+ if fbp:
773
+ current_lr = base_learning_rate
774
+ else:
775
+ current_lr = lr_scheduler.get_last_lr()[0]
776
+ batch_grads.append(grad)
777
+
778
+ log_data = {}
779
+ log_data["loss_mse"] = mse_loss.detach().item()
780
+ log_data["lr"] = current_lr
781
+ log_data["grad"] = grad
782
+ if accelerator.sync_gradients:
783
+ if use_wandb:
784
+ wandb.log(log_data, step=global_step)
785
+ if use_comet_ml:
786
+ comet_experiment.log_metrics(log_data, step=global_step)
787
+
788
+ current_time = time.time()
789
+ is_time_to_sample = (current_time - last_sample_time) >= sample_interval_seconds
790
+ if is_time_to_sample or global_step == 10:
791
+ if save_model:
792
+ generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), global_step)
793
+ #elif epoch % 10 == 0:
794
+ # generate_and_save_samples(fixed_samples, (uncond_emb, uncond_mask), global_step)
795
+ last_n = sink_interval
796
+
797
+ if save_model:
798
+ has_losses = len(batch_losses) > 0
799
+ avg_sample_loss = np.mean(batch_losses[-sink_interval:]) if has_losses else 0.0
800
+ last_loss = batch_losses[-1] if has_losses else 0.0
801
+ max_loss = max(avg_sample_loss, last_loss)
802
+ should_save = max_loss < min_loss * save_barrier
803
+ print(
804
+ f"Saving: {should_save} | Max: {max_loss:.4f} | "
805
+ f"Last: {last_loss:.4f} | Avg: {avg_sample_loss:.4f}"
806
+ )
807
+ if should_save:
808
+ min_loss = max_loss
809
+ save_checkpoint(unet)
810
+ last_sample_time = current_time
811
+ unet.train()
812
+
813
+ if accelerator.is_main_process:
814
+ avg_epoch_loss = np.mean(batch_losses) if len(batch_losses) > 0 else 0.0
815
+ avg_epoch_grad = np.mean(batch_grads) if len(batch_grads) > 0 else 0.0
816
+
817
+ print(f"\nЭпоха {epoch} завершена. Средний лосс: {avg_epoch_loss:.6f}")
818
+ log_data_ep = {
819
+ "epoch_loss": avg_epoch_loss,
820
+ "epoch_grad": avg_epoch_grad,
821
+ "epoch": epoch + 1,
822
+ }
823
+ if use_wandb:
824
+ wandb.log(log_data_ep)
825
+ if use_comet_ml:
826
+ comet_experiment.log_metrics(log_data_ep)
827
+
828
+ if accelerator.is_main_process:
829
+ print("Обучение завершено! Сохраняем финальную модель...")
830
+ save_checkpoint(unet,"fp16")
831
+ if use_comet_ml:
832
+ comet_experiment.end()
833
+ accelerator.free_memory()
834
+ if torch.distributed.is_initialized():
835
+ torch.distributed.destroy_process_group()
836
+
837
+ print("Готово!")
unet/diffusion_pytorch_model.fp16.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:39ae783bc49d90ed176212edae361b3ebf33e4f8518a32ca89679a15682f6ac8
3
+ size 3210307232
unet/diffusion_pytorch_model.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:62aa0e71e311b62009ceff8bd7c22f38c6d43a93c792057648b8b5200ab5e014
3
- size 3210307232
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3e1f8d2466f1a28d6363358e81d3ac4097aaaa5414480d39ae14470cf1faf141
3
+ size 6420443856