comdoleger commited on
Commit
a9e38ba
·
verified ·
1 Parent(s): 0ec6ccc

Upload jobs/process/TrainVAEProcess.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. jobs/process/TrainVAEProcess.py +1142 -0
jobs/process/TrainVAEProcess.py ADDED
@@ -0,0 +1,1142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import glob
3
+ import os
4
+ import shutil
5
+ import time
6
+ from collections import OrderedDict
7
+
8
+ from PIL import Image
9
+ from PIL.ImageOps import exif_transpose
10
+ from einops import rearrange
11
+ from safetensors.torch import save_file, load_file
12
+ from torch.utils.data import DataLoader, ConcatDataset
13
+ import torch
14
+ from torch import nn
15
+ from torchvision.transforms import transforms
16
+
17
+ from jobs.process import BaseTrainProcess
18
+ from toolkit.image_utils import show_tensors
19
+ from toolkit.kohya_model_util import load_vae, convert_diffusers_back_to_ldm
20
+ from toolkit.data_loader import ImageDataset
21
+ from toolkit.losses import ComparativeTotalVariation, get_gradient_penalty, PatternLoss, total_variation, total_variation_deltas
22
+ from toolkit.metadata import get_meta_for_safetensors
23
+ from toolkit.optimizer import get_optimizer
24
+ from toolkit.style import get_style_model_and_losses
25
+ from toolkit.train_tools import get_torch_dtype
26
+ from diffusers import AutoencoderKL, AutoencoderTiny
27
+ from toolkit.models.autoencoder_tiny_with_pooled_exits import AutoencoderTinyWithPooledExits
28
+ from tqdm import tqdm
29
+ import math
30
+ import torchvision.utils
31
+ import time
32
+ import numpy as np
33
+ from .models.critic import Critic
34
+ from torchvision.transforms import Resize
35
+ import lpips
36
+ import random
37
+ import traceback
38
+ from transformers import SiglipImageProcessor, SiglipVisionModel
39
+ import torch.nn.functional as F
40
+
41
+ IMAGE_TRANSFORMS = transforms.Compose(
42
+ [
43
+ transforms.ToTensor(),
44
+ transforms.Normalize([0.5], [0.5]),
45
+ ]
46
+ )
47
+
48
+
49
+ def unnormalize(tensor):
50
+ return (tensor / 2 + 0.5).clamp(0, 1)
51
+
52
+
53
+ def channel_dropout(x, p=0.5):
54
+ keep_prob = 1 - p
55
+ mask = torch.rand(x.size(0), x.size(1), 1, 1, device=x.device, dtype=x.dtype) < keep_prob
56
+ mask = mask / keep_prob # scale
57
+ return x * mask
58
+
59
+
60
+ def sharpen_image(images: torch.Tensor) -> torch.Tensor:
61
+ # Define sharpening kernel
62
+ kernel = torch.tensor([
63
+ [ 0, -1, 0],
64
+ [-1, 5, -1],
65
+ [ 0, -1, 0]
66
+ ], dtype=images.dtype, device=images.device).view(1, 1, 3, 3)
67
+
68
+ # Repeat kernel for each channel
69
+ kernel = kernel.repeat(3, 1, 1, 1) # (out_channels, in_channels/groups, kH, kW)
70
+
71
+ # Apply the filter
72
+ sharpened = F.conv2d(images, kernel, padding=1, groups=3)
73
+
74
+ return sharpened
75
+
76
+ class TrainVAEProcess(BaseTrainProcess):
77
+ def __init__(self, process_id: int, job, config: OrderedDict):
78
+ super().__init__(process_id, job, config)
79
+ self.data_loader = None
80
+ self.vae = None
81
+ self.target_latent_vae = None
82
+ self.device = self.get_conf('device', self.job.device)
83
+ self.vae_path = self.get_conf('vae_path', None)
84
+ self.target_latent_vae_path = self.get_conf('target_latent_vae_path', None)
85
+ self.eq_vae = self.get_conf('eq_vae', False)
86
+ self.datasets_objects = self.get_conf('datasets', required=True)
87
+ self.batch_size = self.get_conf('batch_size', 1, as_type=int)
88
+ self.resolution = self.get_conf('resolution', 256, as_type=int)
89
+ self.sample_resolution = self.get_conf('sample_resolution', self.resolution, as_type=int)
90
+ self.learning_rate = self.get_conf('learning_rate', 1e-6, as_type=float)
91
+ self.sample_every = self.get_conf('sample_every', None)
92
+ self.optimizer_type = self.get_conf('optimizer', 'adam')
93
+ self.epochs = self.get_conf('epochs', None, as_type=int)
94
+ self.max_steps = self.get_conf('max_steps', None, as_type=int)
95
+ self.save_every = self.get_conf('save_every', None)
96
+ self.dtype = self.get_conf('dtype', 'float32')
97
+ self.sample_sources = self.get_conf('sample_sources', None)
98
+ self.log_every = self.get_conf('log_every', 100, as_type=int)
99
+ self.style_weight = self.get_conf('style_weight', 0, as_type=float)
100
+ self.content_weight = self.get_conf('content_weight', 0, as_type=float)
101
+ self.kld_weight = self.get_conf('kld_weight', 0, as_type=float)
102
+ self.clip_weight = self.get_conf('clip_weight', 0, as_type=float)
103
+ self.mse_weight = self.get_conf('mse_weight', 1e0, as_type=float)
104
+ self.mae_weight = self.get_conf('mae_weight', 0, as_type=float)
105
+ self.mv_loss_weight = self.get_conf('mv_loss_weight', 0, as_type=float)
106
+ self.tv_weight = self.get_conf('tv_weight', 0, as_type=float)
107
+ self.ltv_weight = self.get_conf('ltv_weight', 0, as_type=float)
108
+ self.lpm_weight = self.get_conf('lpm_weight', 0, as_type=float) # latent pixel matching
109
+ self.lpips_weight = self.get_conf('lpips_weight', 0, as_type=float)
110
+ self.critic_weight = self.get_conf('critic_weight', 1, as_type=float)
111
+ self.pattern_weight = self.get_conf('pattern_weight', 0, as_type=float)
112
+ self.optimizer_params = self.get_conf('optimizer_params', {})
113
+ self.vae_config = self.get_conf('vae_config', None)
114
+ self.dropout = self.get_conf('dropout', 0.0, as_type=float)
115
+ self.train_encoder = self.get_conf('train_encoder', False, as_type=bool)
116
+ self.random_scaling = self.get_conf('random_scaling', False, as_type=bool)
117
+ self.vae_type = self.get_conf('vae_type', 'AutoencoderKL', as_type=str) # AutoencoderKL or AutoencoderTiny
118
+ self.only_if_contains = self.get_conf('only_if_contains', None)
119
+
120
+ self.do_pooled_exits = False
121
+ self.VaeClass = AutoencoderKL
122
+ if self.vae_type == 'AutoencoderTiny':
123
+ self.VaeClass = AutoencoderTiny
124
+ if self.vae_type == 'AutoencoderTinyWithPooledExits':
125
+ self.VaeClass = AutoencoderTinyWithPooledExits
126
+ self.do_pooled_exits = True
127
+
128
+ if not self.train_encoder:
129
+ # remove losses that only target encoder
130
+ self.kld_weight = 0
131
+ self.mv_loss_weight = 0
132
+ self.ltv_weight = 0
133
+ self.lpm_weight = 0
134
+
135
+ self.blocks_to_train = self.get_conf('blocks_to_train', ['all'])
136
+ self.torch_dtype = get_torch_dtype(self.dtype)
137
+ self.vgg_19 = None
138
+ self.clip = None
139
+ self.clip_image_processor = None
140
+ self.clip_image_size = 256
141
+ self.style_weight_scalers = []
142
+ self.content_weight_scalers = []
143
+ self.lpips_loss:lpips.LPIPS = None
144
+
145
+ self.vae_scale_factor = 8
146
+ self.target_vae_scale_factor = 8
147
+
148
+ self.step_num = 0
149
+ self.epoch_num = 0
150
+
151
+ self.use_critic = self.get_conf('use_critic', False, as_type=bool)
152
+ self.critic = None
153
+
154
+ if self.use_critic:
155
+ self.critic = Critic(
156
+ device=self.device,
157
+ dtype=self.dtype,
158
+ process=self,
159
+ **self.get_conf('critic', {}) # pass any other params
160
+ )
161
+
162
+ if self.sample_every is not None and self.sample_sources is None:
163
+ raise ValueError('sample_every is specified but sample_sources is not')
164
+
165
+ if self.epochs is None and self.max_steps is None:
166
+ raise ValueError('epochs or max_steps must be specified')
167
+
168
+ self.data_loaders = []
169
+ # check datasets
170
+ assert isinstance(self.datasets_objects, list)
171
+ for dataset in self.datasets_objects:
172
+ if 'path' not in dataset:
173
+ raise ValueError('dataset must have a path')
174
+ # check if is dir
175
+ if not os.path.isdir(dataset['path']):
176
+ raise ValueError(f"dataset path does is not a directory: {dataset['path']}")
177
+
178
+ # make training folder
179
+ if not os.path.exists(self.save_root):
180
+ os.makedirs(self.save_root, exist_ok=True)
181
+
182
+ self._pattern_loss = None
183
+
184
+ def update_training_metadata(self):
185
+ self.add_meta(OrderedDict({"training_info": self.get_training_info()}))
186
+
187
+ def get_training_info(self):
188
+ info = OrderedDict({
189
+ 'step': self.step_num,
190
+ 'epoch': self.epoch_num,
191
+ })
192
+ return info
193
+
194
+ def load_datasets(self):
195
+ if self.data_loader is None:
196
+ print(f"Loading datasets")
197
+ datasets = []
198
+ for dataset in self.datasets_objects:
199
+ print(f" - Dataset: {dataset['path']}")
200
+ ds = copy.copy(dataset)
201
+ dataset_res = self.resolution
202
+ if self.random_scaling:
203
+ # scale 2x to allow for random scaling
204
+ dataset_res = int(dataset_res * 2)
205
+ ds['resolution'] = dataset_res
206
+ image_dataset = ImageDataset(ds)
207
+ datasets.append(image_dataset)
208
+
209
+ concatenated_dataset = ConcatDataset(datasets)
210
+ self.data_loader = DataLoader(
211
+ concatenated_dataset,
212
+ batch_size=self.batch_size,
213
+ shuffle=True,
214
+ num_workers=16
215
+ )
216
+
217
+ def remove_oldest_checkpoint(self):
218
+ max_to_keep = 4
219
+ folders = glob.glob(os.path.join(self.save_root, f"{self.job.name}*_diffusers"))
220
+ if len(folders) > max_to_keep:
221
+ folders.sort(key=os.path.getmtime)
222
+ for folder in folders[:-max_to_keep]:
223
+ print(f"Removing {folder}")
224
+ shutil.rmtree(folder)
225
+ # also handle CRITIC_vae_42_000000500.safetensors format for critic
226
+ critic_files = glob.glob(os.path.join(self.save_root, f"CRITIC_{self.job.name}*.safetensors"))
227
+ if len(critic_files) > max_to_keep:
228
+ critic_files.sort(key=os.path.getmtime)
229
+ for file in critic_files[:-max_to_keep]:
230
+ print(f"Removing {file}")
231
+ os.remove(file)
232
+
233
+ def setup_vgg19(self):
234
+ if self.vgg_19 is None:
235
+ self.vgg_19, self.style_losses, self.content_losses, self.vgg19_pool_4 = get_style_model_and_losses(
236
+ single_target=True,
237
+ device=self.device,
238
+ output_layer_name='pool_4',
239
+ dtype=self.torch_dtype
240
+ )
241
+ self.vgg_19.to(self.device, dtype=self.torch_dtype)
242
+ self.vgg_19.requires_grad_(False)
243
+
244
+ # we run random noise through first to get layer scalers to normalize the loss per layer
245
+ # bs of 2 because we run pred and target through stacked
246
+ noise = torch.randn((2, 3, self.resolution, self.resolution), device=self.device, dtype=self.torch_dtype)
247
+ self.vgg_19(noise)
248
+ for style_loss in self.style_losses:
249
+ # get a scaler to normalize to 1
250
+ scaler = 1 / torch.mean(style_loss.loss).item()
251
+ self.style_weight_scalers.append(scaler)
252
+ for content_loss in self.content_losses:
253
+ # get a scaler to normalize to 1
254
+ scaler = 1 / torch.mean(content_loss.loss).item()
255
+ self.content_weight_scalers.append(scaler)
256
+
257
+ self.print(f"Style weight scalers: {self.style_weight_scalers}")
258
+ self.print(f"Content weight scalers: {self.content_weight_scalers}")
259
+
260
+ def setup_clip(self):
261
+ ckpt = 'google/siglip2-base-patch16-256'
262
+ if self.resolution == 512:
263
+ ckpt = 'google/siglip2-so400m-patch16-512'
264
+ # ckpt = 'google/siglip2-base-patch16-512'
265
+ self.clip_image_size = 512
266
+ self.print(f"Loading CLIP model from {ckpt}")
267
+ vision_encoder = SiglipVisionModel.from_pretrained(ckpt, device_map="auto", torch_dtype=torch.bfloat16).eval()
268
+ processor = SiglipImageProcessor.from_pretrained(ckpt)
269
+ self.clip = vision_encoder
270
+ self.clip_image_processor = processor
271
+
272
+ def get_clip_embeddings(self, image_n1p1):
273
+ tensors_0_1 = (image_n1p1 + 1) / 2
274
+ # sharpen images
275
+ tensors_0_1 = sharpen_image(tensors_0_1)
276
+
277
+ tensors_0_1 = tensors_0_1.clamp(0, 1)
278
+
279
+ # resize if needed
280
+ if tensors_0_1.shape[-2:] != (self.clip_image_size, self.clip_image_size):
281
+ tensors_0_1 = torch.nn.functional.interpolate(tensors_0_1, size=(self.clip_image_size, self.clip_image_size), mode='bilinear', align_corners=False)
282
+
283
+ mean = torch.tensor([0.5, 0.5, 0.5]).to(
284
+ tensors_0_1.device, dtype=tensors_0_1.dtype
285
+ ).view([1, 3, 1, 1]).detach()
286
+ std = torch.tensor([0.5, 0.5, 0.5]).to(
287
+ tensors_0_1.device, dtype=tensors_0_1.dtype
288
+ ).view([1, 3, 1, 1]).detach()
289
+
290
+ # tensors_0_1 = torch.clip((255. * tensors_0_1), 0, 255).round() / 255.0
291
+ clip_image = (tensors_0_1 - mean) / std
292
+
293
+ id_embeds = self.clip(
294
+ clip_image.to(self.clip.device, dtype=torch.bfloat16),
295
+ output_hidden_states=True,
296
+ )
297
+ last_hidden_state = id_embeds['last_hidden_state']
298
+ return last_hidden_state
299
+
300
+ def get_clip_loss(self, pred, target):
301
+ # pred and target come in as -1 to 1.
302
+ with torch.no_grad():
303
+ target_embeddings = self.get_clip_embeddings(target).float()
304
+ pred_embeddings = self.get_clip_embeddings(pred).float()
305
+ return torch.nn.functional.mse_loss(pred_embeddings, target_embeddings)
306
+
307
+ def get_pooled_output_loss(self, pooled_outputs, target):
308
+ if pooled_outputs is None:
309
+ return torch.tensor(0.0, device=self.device)
310
+
311
+ # pooled_outputs is a list of tensors, each with shape (batch_size, 3, h, w)
312
+ # target is a tensor with shape (batch_size, 3, h, w)
313
+ loss = 0.0
314
+ for pooled_output in pooled_outputs:
315
+ with torch.no_grad():
316
+ # resize target to match pooled_output size
317
+ target_resized = torch.nn.functional.interpolate(target, size=pooled_output.shape[2:], mode='bilinear', align_corners=False)
318
+ loss += torch.nn.functional.mse_loss(pooled_output.float(), target_resized.float())
319
+ return loss / len(pooled_outputs) if len(pooled_outputs) > 0 else torch.tensor(0.0, device=self.device)
320
+
321
+ def get_style_loss(self):
322
+ if self.style_weight > 0:
323
+ # scale all losses with loss scalers
324
+ loss = torch.sum(
325
+ torch.stack([loss.loss * scaler for loss, scaler in zip(self.style_losses, self.style_weight_scalers)]))
326
+ return loss
327
+ else:
328
+ return torch.tensor(0.0, device=self.device)
329
+
330
+ def get_content_loss(self):
331
+ if self.content_weight > 0:
332
+ # scale all losses with loss scalers
333
+ loss = torch.sum(torch.stack(
334
+ [loss.loss * scaler for loss, scaler in zip(self.content_losses, self.content_weight_scalers)]))
335
+ return loss
336
+ else:
337
+ return torch.tensor(0.0, device=self.device)
338
+
339
+ def get_mse_loss(self, pred, target):
340
+ if self.mse_weight > 0:
341
+ loss_fn = nn.MSELoss()
342
+ loss_normal = loss_fn(pred, target)
343
+
344
+ pred_sharp = sharpen_image(pred)
345
+ with torch.no_grad():
346
+ target_sharp = sharpen_image(target)
347
+
348
+ loss_sharp = loss_fn(pred_sharp, target_sharp)
349
+
350
+ return (loss_sharp + loss_normal) / 2
351
+ else:
352
+ return torch.tensor(0.0, device=self.device)
353
+
354
+ def get_mae_loss(self, pred, target):
355
+ if self.mae_weight > 0:
356
+ loss_fn = nn.L1Loss()
357
+ loss_normal = loss_fn(pred, target)
358
+
359
+ pred_sharp = sharpen_image(pred)
360
+ with torch.no_grad():
361
+ target_sharp = sharpen_image(target)
362
+ loss_sharp = loss_fn(pred_sharp, target_sharp)
363
+ return (loss_sharp + loss_normal) / 2
364
+ else:
365
+ return torch.tensor(0.0, device=self.device)
366
+
367
+ def get_kld_loss(self, mu, log_var):
368
+ if self.kld_weight > 0:
369
+ # Kullback-Leibler divergence
370
+ # added here for full training (not implemented). Not needed for only decoder
371
+ # as we are not changing the distribution of the latent space
372
+ # normally it would help keep a normal distribution for latents
373
+ KLD = -0.5 * torch.sum(1 + log_var - mu.pow(2) - log_var.exp()) # KL divergence
374
+ return KLD
375
+ else:
376
+ return torch.tensor(0.0, device=self.device)
377
+
378
+ def get_mean_variance_loss(self, latents: torch.Tensor):
379
+ if self.mv_loss_weight > 0:
380
+ # collapse rows into channels
381
+ latents_col = rearrange(latents, 'b c h (gw w) -> b (c gw) h w', gw=latents.shape[-1])
382
+ mean_col = latents_col.mean(dim=(2, 3), keepdim=True)
383
+ std_col = latents_col.std(dim=(2, 3), keepdim=True, unbiased=False)
384
+ mean_loss_col = (mean_col ** 2).mean()
385
+ std_loss_col = ((std_col - 1) ** 2).mean()
386
+
387
+ # collapse columns into channels
388
+ latents_row = rearrange(latents, 'b c (gh h) w -> b (c gh) h w', gh=latents.shape[-2])
389
+ mean_row = latents_row.mean(dim=(2, 3), keepdim=True)
390
+ std_row = latents_row.std(dim=(2, 3), keepdim=True, unbiased=False)
391
+ mean_loss_row = (mean_row ** 2).mean()
392
+ std_loss_row = ((std_row - 1) ** 2).mean()
393
+
394
+ # do a global one
395
+
396
+ mean = latents.mean(dim=(2, 3), keepdim=True)
397
+ std = latents.std(dim=(2, 3), keepdim=True, unbiased=False)
398
+ mean_loss_global = (mean ** 2).mean()
399
+ std_loss_global = ((std - 1) ** 2).mean()
400
+
401
+ return (mean_loss_col + std_loss_col + mean_loss_row + std_loss_row + mean_loss_global + std_loss_global) / 3
402
+ else:
403
+ return torch.tensor(0.0, device=self.device)
404
+
405
+ def get_ltv_loss(self, latent, images):
406
+ # loss to reduce the latent space variance
407
+ if self.ltv_weight > 0:
408
+ with torch.no_grad():
409
+ images = images.to(latent.device, dtype=latent.dtype)
410
+ # resize down to latent size
411
+ images = torch.nn.functional.interpolate(images, size=(latent.shape[2], latent.shape[3]), mode='bilinear', align_corners=False)
412
+
413
+ # mean the color channel and then expand to latent size
414
+ images = images.mean(dim=1, keepdim=True)
415
+ images = images.repeat(1, latent.shape[1], 1, 1)
416
+
417
+ # normalize to a mean of 0 and std of 1
418
+ images_mean = images.mean(dim=(2, 3), keepdim=True)
419
+ images_std = images.std(dim=(2, 3), keepdim=True)
420
+ images = (images - images_mean) / (images_std + 1e-6)
421
+
422
+ # now we target the same std of the image for the latent space as to not reduce to 0
423
+
424
+ latent_tv = torch.abs(total_variation_deltas(latent))
425
+ images_tv = torch.abs(total_variation_deltas(images))
426
+ loss = torch.abs(latent_tv - images_tv) # keep it spatially aware
427
+ loss = loss.mean(dim=2, keepdim=True)
428
+ loss = loss.mean(dim=3, keepdim=True) # mean over height and width
429
+ loss = loss.mean(dim=1, keepdim=True) # mean over channels
430
+ loss = loss.mean()
431
+ return loss
432
+ else:
433
+ return torch.tensor(0.0, device=self.device)
434
+
435
+ def get_latent_pixel_matching_loss(self, latent, pixels):
436
+ if self.lpm_weight > 0:
437
+ with torch.no_grad():
438
+ pixels = pixels.to(latent.device, dtype=latent.dtype)
439
+ # resize down to latent size
440
+ pixels = torch.nn.functional.interpolate(pixels, size=(latent.shape[2], latent.shape[3]), mode='bilinear', align_corners=False)
441
+
442
+ # mean the color channel and then expand to latent size
443
+ pixels = pixels.mean(dim=1, keepdim=True)
444
+ pixels = pixels.repeat(1, latent.shape[1], 1, 1)
445
+ # match the mean std of latent
446
+ latent_mean = latent.mean(dim=(2, 3), keepdim=True)
447
+ latent_std = latent.std(dim=(2, 3), keepdim=True)
448
+ pixels_mean = pixels.mean(dim=(2, 3), keepdim=True)
449
+ pixels_std = pixels.std(dim=(2, 3), keepdim=True)
450
+ pixels = (pixels - pixels_mean) / (pixels_std + 1e-6) * latent_std + latent_mean
451
+
452
+ return torch.nn.functional.mse_loss(latent.float(), pixels.float())
453
+
454
+ else:
455
+ return torch.tensor(0.0, device=self.device)
456
+
457
+ def get_tv_loss(self, pred, target):
458
+ if self.tv_weight > 0:
459
+ get_tv_loss = ComparativeTotalVariation()
460
+ loss = get_tv_loss(pred, target)
461
+ return loss
462
+ else:
463
+ return torch.tensor(0.0, device=self.device)
464
+
465
+ def get_pattern_loss(self, pred, target):
466
+ if self._pattern_loss is None:
467
+ self._pattern_loss = PatternLoss(pattern_size=16, dtype=self.torch_dtype).to(self.device,
468
+ dtype=self.torch_dtype)
469
+ loss = torch.mean(self._pattern_loss(pred, target))
470
+ return loss
471
+
472
+ def save(self, step=None):
473
+ if not os.path.exists(self.save_root):
474
+ os.makedirs(self.save_root, exist_ok=True)
475
+
476
+ step_num = ''
477
+ if step is not None:
478
+ # zeropad 9 digits
479
+ step_num = f"_{str(step).zfill(9)}"
480
+
481
+ self.update_training_metadata()
482
+ filename = f'{self.job.name}{step_num}_diffusers'
483
+
484
+ self.vae = self.vae.to("cpu", dtype=torch.float16)
485
+ self.vae.save_pretrained(
486
+ save_directory=os.path.join(self.save_root, filename)
487
+ )
488
+ self.vae = self.vae.to(self.device, dtype=self.torch_dtype)
489
+
490
+ self.print(f"Saved to {os.path.join(self.save_root, filename)}")
491
+
492
+ if self.use_critic:
493
+ self.critic.save(step)
494
+
495
+ self.remove_oldest_checkpoint()
496
+
497
+ def sample(self, step=None):
498
+ sample_folder = os.path.join(self.save_root, 'samples')
499
+ if not os.path.exists(sample_folder):
500
+ os.makedirs(sample_folder, exist_ok=True)
501
+
502
+ with torch.no_grad():
503
+ for i, img_url in enumerate(self.sample_sources):
504
+ img = exif_transpose(Image.open(img_url))
505
+ img = img.convert('RGB')
506
+ # crop if not square
507
+ if img.width != img.height:
508
+ min_dim = min(img.width, img.height)
509
+ img = img.crop((0, 0, min_dim, min_dim))
510
+ # resize
511
+ img = img.resize((self.sample_resolution, self.sample_resolution))
512
+
513
+ input_img = img
514
+ img = IMAGE_TRANSFORMS(img).unsqueeze(0).to(self.device, dtype=self.torch_dtype)
515
+ img = img
516
+ # latent = self.vae.encode(img).latent_dist.sample()
517
+
518
+ target_latent = None
519
+ if self.target_latent_vae is not None:
520
+ target_input_scale = self.target_vae_scale_factor / self.vae_scale_factor
521
+ target_input_size = (int(img.shape[2] * target_input_scale), int(img.shape[3] * target_input_scale))
522
+ # resize to target input size
523
+ target_input_batch = Resize(target_input_size)(img).to(self.device, dtype=torch.float32)
524
+ target_latent = self.target_latent_vae.encode(target_input_batch).latent_dist.sample().detach()
525
+ shift = self.target_latent_vae.config['shift_factor'] if self.target_latent_vae.config['shift_factor'] is not None else 0
526
+ target_latent = self.target_latent_vae.config['scaling_factor'] * (target_latent - shift)
527
+ target_latent = target_latent.to(self.device, dtype=self.torch_dtype)
528
+ latent = self.vae.encode(img, return_dict=False)[0]
529
+
530
+ if hasattr(latent, 'sample'):
531
+ latent = latent.sample()
532
+
533
+ shift = self.vae.config['shift_factor'] if self.vae.config['shift_factor'] is not None else 0
534
+ latent = self.vae.config['scaling_factor'] * (latent - shift)
535
+
536
+ latent_img = latent.clone()
537
+ bs, ch, h, w = latent_img.shape
538
+ grid_size = math.ceil(math.sqrt(ch))
539
+ pad = grid_size * grid_size - ch
540
+
541
+ # take first item in batch
542
+ latent_img = latent_img[0] # shape: (ch, h, w)
543
+
544
+ if pad > 0:
545
+ padding = torch.zeros((pad, h, w), dtype=latent_img.dtype, device=latent_img.device)
546
+ latent_img = torch.cat([latent_img, padding], dim=0)
547
+
548
+ # make grid
549
+ new_img = torch.zeros((1, grid_size * h, grid_size * w), dtype=latent_img.dtype, device=latent_img.device)
550
+ for x in range(grid_size):
551
+ for y in range(grid_size):
552
+ if x * grid_size + y < ch:
553
+ new_img[0, x * h:(x + 1) * h, y * w:(y + 1) * w] = latent_img[x * grid_size + y]
554
+ latent_img = new_img
555
+ # make rgb
556
+ latent_img = latent_img.repeat(3, 1, 1).unsqueeze(0)
557
+ latent_img = (latent_img / 2 + 0.5).clamp(0, 1)
558
+
559
+ # resize to 256x256
560
+ latent_img = torch.nn.functional.interpolate(latent_img, size=(self.sample_resolution, self.sample_resolution), mode='nearest')
561
+ latent_img = latent_img.squeeze(0).cpu().permute(1, 2, 0).float().numpy()
562
+ latent_img = (latent_img * 255).astype(np.uint8)
563
+ # convert to pillow image
564
+ latent_img = Image.fromarray(latent_img)
565
+
566
+ if target_latent is not None:
567
+ latent = target_latent.to(latent.device, dtype=latent.dtype)
568
+
569
+ shift = self.vae.config['shift_factor'] if self.vae.config['shift_factor'] is not None else 0
570
+ latent = latent / self.vae.config['scaling_factor'] + shift
571
+
572
+ decoded = self.vae.decode(latent).sample
573
+ decoded = (decoded / 2 + 0.5).clamp(0, 1)
574
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
575
+ decoded = decoded.cpu().permute(0, 2, 3, 1).squeeze(0).float().numpy()
576
+
577
+ # convert to pillow image
578
+ decoded = Image.fromarray((decoded * 255).astype(np.uint8))
579
+
580
+ # stack input image and decoded image
581
+ input_img = input_img.resize((self.sample_resolution, self.sample_resolution))
582
+ decoded = decoded.resize((self.sample_resolution, self.sample_resolution))
583
+
584
+ output_img = Image.new('RGB', (self.sample_resolution * 3, self.sample_resolution))
585
+ output_img.paste(input_img, (0, 0))
586
+ output_img.paste(decoded, (self.sample_resolution, 0))
587
+ output_img.paste(latent_img, (self.sample_resolution * 2, 0))
588
+
589
+ scale_up = 2
590
+ if output_img.height <= 300:
591
+ scale_up = 4
592
+ if output_img.height >= 1000:
593
+ scale_up = 1
594
+
595
+ # scale up using nearest neighbor
596
+ output_img = output_img.resize((output_img.width * scale_up, output_img.height * scale_up), Image.NEAREST)
597
+
598
+ step_num = ''
599
+ if step is not None:
600
+ # zero-pad 9 digits
601
+ step_num = f"_{str(step).zfill(9)}"
602
+ seconds_since_epoch = int(time.time())
603
+ # zero-pad 2 digits
604
+ i_str = str(i).zfill(2)
605
+ filename = f"{seconds_since_epoch}{step_num}_{i_str}.png"
606
+ output_img.save(os.path.join(sample_folder, filename))
607
+
608
+ def load_vae(self):
609
+ path_to_load = self.vae_path
610
+ # see if we have a checkpoint in out output to resume from
611
+ self.print(f"Looking for latest checkpoint in {self.save_root}")
612
+ files = glob.glob(os.path.join(self.save_root, f"{self.job.name}*_diffusers"))
613
+ if files and len(files) > 0:
614
+ latest_file = max(files, key=os.path.getmtime)
615
+ print(f" - Latest checkpoint is: {latest_file}")
616
+ path_to_load = latest_file
617
+ # todo update step and epoch count
618
+ else:
619
+ self.print(f" - No checkpoint found, starting from scratch")
620
+ # load vae
621
+ self.print(f"Loading VAE")
622
+ self.print(f" - Loading VAE: {path_to_load}")
623
+ if self.vae is None:
624
+ if path_to_load is not None:
625
+ self.vae = self.VaeClass.from_pretrained(path_to_load)
626
+ elif self.vae_config is not None:
627
+ self.vae = self.VaeClass(**self.vae_config)
628
+ else:
629
+ raise ValueError('vae_path or ae_config must be specified')
630
+
631
+ # set decoder to train
632
+ self.vae.to(self.device, dtype=self.torch_dtype)
633
+ if self.eq_vae:
634
+ self.vae.encoder.train()
635
+ else:
636
+ self.vae.requires_grad_(False)
637
+ self.vae.eval()
638
+ self.vae.decoder.train()
639
+ self.vae_scale_factor = 2 ** (len(self.vae.config['block_out_channels']) - 1)
640
+
641
+ if self.target_latent_vae_path is not None:
642
+ self.print(f"Loading target latent VAE from {self.target_latent_vae_path}")
643
+ self.target_latent_vae = AutoencoderKL.from_pretrained(self.target_latent_vae_path)
644
+ self.target_latent_vae.to(self.device, dtype=torch.float32)
645
+ self.target_latent_vae.eval()
646
+ self.target_vae_scale_factor = 2 ** (len(self.target_latent_vae.config['block_out_channels']) - 1)
647
+ else:
648
+ self.target_latent_vae = None
649
+ self.target_vae_scale_factor = self.vae_scale_factor
650
+
651
+ def run(self):
652
+ super().run()
653
+ self.load_datasets()
654
+
655
+ max_step_epochs = self.max_steps // len(self.data_loader)
656
+ num_epochs = self.epochs
657
+ if num_epochs is None or num_epochs > max_step_epochs:
658
+ num_epochs = max_step_epochs
659
+
660
+ max_epoch_steps = len(self.data_loader) * num_epochs
661
+ num_steps = self.max_steps
662
+ if num_steps is None or num_steps > max_epoch_steps:
663
+ num_steps = max_epoch_steps
664
+ self.max_steps = num_steps
665
+ self.epochs = num_epochs
666
+ start_step = self.step_num
667
+ self.first_step = start_step
668
+
669
+ self.print(f"Training VAE")
670
+ self.print(f" - Training folder: {self.training_folder}")
671
+ self.print(f" - Batch size: {self.batch_size}")
672
+ self.print(f" - Learning rate: {self.learning_rate}")
673
+ self.print(f" - Epochs: {num_epochs}")
674
+ self.print(f" - Max steps: {self.max_steps}")
675
+
676
+ # load vae
677
+ self.load_vae()
678
+
679
+ params = []
680
+
681
+ # only set last 2 layers to trainable
682
+ for param in self.vae.decoder.parameters():
683
+ param.requires_grad = False
684
+
685
+ train_all = 'all' in self.blocks_to_train
686
+
687
+ if train_all:
688
+ params = list(self.vae.decoder.named_parameters())
689
+ self.vae.decoder.requires_grad_(True)
690
+ if self.train_encoder:
691
+ # encoder
692
+ params += list(self.vae.encoder.named_parameters())
693
+ self.vae.encoder.requires_grad_(True)
694
+ else:
695
+ # mid_block
696
+ if train_all or 'mid_block' in self.blocks_to_train:
697
+ params += list(self.vae.decoder.mid_block.named_parameters())
698
+ self.vae.decoder.mid_block.requires_grad_(True)
699
+ # up_blocks
700
+ if train_all or 'up_blocks' in self.blocks_to_train:
701
+ params += list(self.vae.decoder.up_blocks.named_parameters())
702
+ self.vae.decoder.up_blocks.requires_grad_(True)
703
+ # conv_out (single conv layer output)
704
+ if train_all or 'conv_out' in self.blocks_to_train:
705
+ params += list(self.vae.decoder.conv_out.named_parameters())
706
+ self.vae.decoder.conv_out.requires_grad_(True)
707
+
708
+ if self.style_weight > 0 or self.content_weight > 0:
709
+ self.setup_vgg19()
710
+ # self.vgg_19.requires_grad_(False)
711
+ self.vgg_19.eval()
712
+
713
+ if self.use_critic:
714
+ self.critic.setup()
715
+
716
+ if self.clip_weight > 0:
717
+ self.setup_clip()
718
+
719
+ if self.lpips_weight > 0 and self.lpips_loss is None:
720
+ # self.lpips_loss = lpips.LPIPS(net='vgg')
721
+ self.lpips_loss = lpips.LPIPS(net='vgg').to(self.device, dtype=torch.bfloat16)
722
+
723
+ if self.only_if_contains is not None:
724
+ orig_params = params
725
+ params = []
726
+ for name, param in orig_params:
727
+ for contains in self.only_if_contains:
728
+ if contains in name:
729
+ params.append(param)
730
+ break
731
+
732
+ optimizer = get_optimizer(params, self.optimizer_type, self.learning_rate,
733
+ optimizer_params=self.optimizer_params)
734
+
735
+ # setup scheduler
736
+ # todo allow other schedulers
737
+ scheduler = torch.optim.lr_scheduler.ConstantLR(
738
+ optimizer,
739
+ total_iters=num_steps,
740
+ factor=1,
741
+ # verbose=False
742
+ )
743
+
744
+ # setup tqdm progress bar
745
+ self.progress_bar = tqdm(
746
+ total=num_steps,
747
+ desc='Training VAE',
748
+ leave=True
749
+ )
750
+
751
+ # sample first
752
+ self.sample()
753
+ blank_losses = OrderedDict({
754
+ "total": [],
755
+ "lpips": [],
756
+ "style": [],
757
+ "content": [],
758
+ "mse": [],
759
+ "mae": [],
760
+ "lat_mse": [],
761
+ "mvl": [],
762
+ "ltv": [],
763
+ "lpm": [],
764
+ "kl": [],
765
+ "tv": [],
766
+ "clip": [],
767
+ "pool": [],
768
+ "ptn": [],
769
+ "crD": [],
770
+ "crG": [],
771
+ })
772
+ epoch_losses = copy.deepcopy(blank_losses)
773
+ log_losses = copy.deepcopy(blank_losses)
774
+ # range start at self.epoch_num go to self.epochs
775
+
776
+ latent_size = self.resolution // self.vae_scale_factor
777
+
778
+ for epoch in range(self.epoch_num, self.epochs, 1):
779
+ if self.step_num >= self.max_steps:
780
+ break
781
+ for batch in self.data_loader:
782
+ if self.step_num >= self.max_steps:
783
+ break
784
+ with torch.no_grad():
785
+ batch = batch.to(self.device, dtype=self.torch_dtype)
786
+
787
+ if self.random_scaling:
788
+ # only random scale 0.5 of the time
789
+ if random.random() < 0.5:
790
+ # random scale the batch
791
+ scale_factor = 0.25
792
+ else:
793
+ scale_factor = 0.5
794
+ new_size = (int(batch.shape[2] * scale_factor), int(batch.shape[3] * scale_factor))
795
+ # make sure it is vae divisible
796
+ new_size = (new_size[0] // self.vae_scale_factor * self.vae_scale_factor,
797
+ new_size[1] // self.vae_scale_factor * self.vae_scale_factor)
798
+
799
+
800
+ # resize so it matches size of vae evenly
801
+ if batch.shape[2] % self.vae_scale_factor != 0 or batch.shape[3] % self.vae_scale_factor != 0:
802
+ batch = Resize((batch.shape[2] // self.vae_scale_factor * self.vae_scale_factor,
803
+ batch.shape[3] // self.vae_scale_factor * self.vae_scale_factor))(batch)
804
+
805
+ target_latent = None
806
+ lat_mse_loss = torch.tensor(0.0, device=self.device)
807
+ if self.target_latent_vae is not None:
808
+ target_input_scale = self.target_vae_scale_factor / self.vae_scale_factor
809
+ target_input_size = (int(batch.shape[2] * target_input_scale), int(batch.shape[3] * target_input_scale))
810
+ # resize to target input size
811
+ target_input_batch = Resize(target_input_size)(batch).to(self.device, dtype=torch.float32)
812
+ target_latent = self.target_latent_vae.encode(target_input_batch).latent_dist.sample().detach()
813
+ # shift scale it
814
+ shift = self.target_latent_vae.config['shift_factor'] if self.target_latent_vae.config['shift_factor'] is not None else 0
815
+ target_latent = self.target_latent_vae.config['scaling_factor'] * (target_latent - shift)
816
+ target_latent = target_latent.to(self.device, dtype=self.torch_dtype)
817
+
818
+
819
+ # forward pass
820
+ # grad only if eq_vae
821
+ with torch.set_grad_enabled(self.train_encoder):
822
+ if self.vae_type != 'AutoencoderKL':
823
+ # AutoencoderTiny cannot do latent distribution sampling
824
+ latents = self.vae.encode(batch, return_dict=False)[0]
825
+ mu, logvar = None, None
826
+ else:
827
+ dgd = self.vae.encode(batch).latent_dist
828
+ mu, logvar = dgd.mean, dgd.logvar
829
+ latents = dgd.sample()
830
+
831
+ # scale shift latent to config
832
+ shift = self.vae.config['shift_factor'] if self.vae.config['shift_factor'] is not None else 0
833
+ latents = self.vae.config['scaling_factor'] * (latents - shift)
834
+
835
+ if target_latent is not None and self.train_encoder:
836
+ # forward_latents = target_latent.detach()
837
+ lat_mse_loss = torch.nn.MSELoss()(target_latent.float(), latents.float())
838
+ latents = target_latent.detach()
839
+ forward_latents = target_latent.detach()
840
+
841
+ elif self.eq_vae:
842
+ # process flips, rotate, scale
843
+ latent_chunks = list(latents.chunk(latents.shape[0], dim=0))
844
+ batch_chunks = list(batch.chunk(batch.shape[0], dim=0))
845
+ out_chunks = []
846
+ for i in range(len(latent_chunks)):
847
+ try:
848
+ do_rotate = random.randint(0, 3)
849
+ do_flip_x = random.randint(0, 1)
850
+ do_flip_y = random.randint(0, 1)
851
+ do_scale = random.randint(0, 1)
852
+ if do_rotate > 0:
853
+ latent_chunks[i] = torch.rot90(latent_chunks[i], do_rotate, (2, 3))
854
+ batch_chunks[i] = torch.rot90(batch_chunks[i], do_rotate, (2, 3))
855
+ if do_flip_x > 0:
856
+ latent_chunks[i] = torch.flip(latent_chunks[i], [2])
857
+ batch_chunks[i] = torch.flip(batch_chunks[i], [2])
858
+ if do_flip_y > 0:
859
+ latent_chunks[i] = torch.flip(latent_chunks[i], [3])
860
+ batch_chunks[i] = torch.flip(batch_chunks[i], [3])
861
+
862
+ # resize latent to fit
863
+ if latent_chunks[i].shape[2] != latent_size or latent_chunks[i].shape[3] != latent_size:
864
+ latent_chunks[i] = torch.nn.functional.interpolate(latent_chunks[i], size=(latent_size, latent_size), mode='bilinear', align_corners=False)
865
+
866
+ # if do_scale > 0:
867
+ # scale = 2
868
+ # start_latent_h = latent_chunks[i].shape[2]
869
+ # start_latent_w = latent_chunks[i].shape[3]
870
+ # start_batch_h = batch_chunks[i].shape[2]
871
+ # start_batch_w = batch_chunks[i].shape[3]
872
+ # latent_chunks[i] = torch.nn.functional.interpolate(latent_chunks[i], scale_factor=scale, mode='bilinear', align_corners=False)
873
+ # batch_chunks[i] = torch.nn.functional.interpolate(batch_chunks[i], scale_factor=scale, mode='bilinear', align_corners=False)
874
+ # # random crop. latent is smaller than match but crops need to match
875
+ # latent_x = random.randint(0, latent_chunks[i].shape[2] - start_latent_h)
876
+ # latent_y = random.randint(0, latent_chunks[i].shape[3] - start_latent_w)
877
+ # batch_x = latent_x * self.vae_scale_factor
878
+ # batch_y = latent_y * self.vae_scale_factor
879
+
880
+ # # crop
881
+ # latent_chunks[i] = latent_chunks[i][:, :, latent_x:latent_x + start_latent_h, latent_y:latent_y + start_latent_w]
882
+ # batch_chunks[i] = batch_chunks[i][:, :, batch_x:batch_x + start_batch_h, batch_y:batch_y + start_batch_w]
883
+ except Exception as e:
884
+ print(f"Error processing image {i}: {e}")
885
+ traceback.print_exc()
886
+ raise e
887
+ out_chunks.append(latent_chunks[i])
888
+ latents = torch.cat(out_chunks, dim=0)
889
+ # do dropout
890
+ if self.dropout > 0:
891
+ forward_latents = channel_dropout(latents, self.dropout)
892
+ else:
893
+ forward_latents = latents
894
+
895
+ # resize batch to resolution if needed
896
+ if batch_chunks[0].shape[2] != self.resolution or batch_chunks[0].shape[3] != self.resolution:
897
+ batch_chunks = [torch.nn.functional.interpolate(b, size=(self.resolution, self.resolution), mode='bilinear', align_corners=False) for b in batch_chunks]
898
+ batch = torch.cat(batch_chunks, dim=0)
899
+
900
+ else:
901
+ # latents.detach().requires_grad_(True)
902
+ forward_latents = latents
903
+
904
+ forward_latents = forward_latents.to(self.device, dtype=self.torch_dtype)
905
+
906
+ if not self.train_encoder:
907
+ # detach latents if not training encoder
908
+ forward_latents = forward_latents.detach()
909
+
910
+ # shift latents to match vae config
911
+ shift = self.vae.config['shift_factor'] if self.vae.config['shift_factor'] is not None else 0
912
+ forward_latents = forward_latents / self.vae.config['scaling_factor'] + shift
913
+
914
+ pooled_outputs = None
915
+ if self.do_pooled_exits:
916
+ pred, pooled_outputs = self.vae.decode_with_pooled_exits(forward_latents)
917
+ else:
918
+ pred = self.vae.decode(forward_latents).sample
919
+
920
+ # Run through VGG19
921
+ if self.style_weight > 0 or self.content_weight > 0:
922
+ stacked = torch.cat([pred, batch], dim=0)
923
+ stacked = (stacked / 2 + 0.5).clamp(0, 1)
924
+ self.vgg_19(stacked)
925
+
926
+ if self.use_critic:
927
+ stacked = torch.cat([pred, batch], dim=0)
928
+ critic_d_loss = self.critic.step(stacked.detach())
929
+ else:
930
+ critic_d_loss = 0.0
931
+
932
+ style_loss = self.get_style_loss() * self.style_weight
933
+ content_loss = self.get_content_loss() * self.content_weight
934
+ kld_loss = self.get_kld_loss(mu, logvar) * self.kld_weight
935
+ mse_loss = self.get_mse_loss(pred, batch) * self.mse_weight
936
+ mae_loss = self.get_mae_loss(pred, batch) * self.mae_weight
937
+ pool_loss = self.get_pooled_output_loss(pooled_outputs, batch)
938
+ if self.clip_weight > 0:
939
+ clip_loss = self.get_clip_loss(pred, batch) * self.clip_weight
940
+ else:
941
+ clip_loss = torch.tensor(0.0, device=self.device, dtype=self.torch_dtype)
942
+ if self.lpips_weight > 0:
943
+ lpips_loss = self.lpips_loss(
944
+ pred.clamp(-1, 1).to(self.device, dtype=torch.bfloat16),
945
+ batch.clamp(-1, 1).to(self.device, dtype=torch.bfloat16)
946
+ ).float().mean() * self.lpips_weight
947
+ else:
948
+ lpips_loss = torch.tensor(0.0, device=self.device, dtype=self.torch_dtype)
949
+ tv_loss = self.get_tv_loss(pred, batch) * self.tv_weight
950
+ pattern_loss = self.get_pattern_loss(pred, batch) * self.pattern_weight
951
+ if self.use_critic:
952
+ stacked = torch.cat([pred, batch], dim=0)
953
+ critic_gen_loss = self.critic.get_critic_loss(stacked) * self.critic_weight
954
+
955
+ # do not let abs critic gen loss be higher than abs lpips * 0.1 if using it
956
+ if self.lpips_weight > 0:
957
+ max_target = lpips_loss.abs() * 0.1
958
+ with torch.no_grad():
959
+ crit_g_scaler = 1.0
960
+ if critic_gen_loss.abs() > max_target:
961
+ crit_g_scaler = max_target / critic_gen_loss.abs()
962
+
963
+ critic_gen_loss *= crit_g_scaler
964
+ else:
965
+ critic_gen_loss = torch.tensor(0.0, device=self.device, dtype=self.torch_dtype)
966
+
967
+ if self.mv_loss_weight > 0:
968
+ mv_loss = self.get_mean_variance_loss(latents) * self.mv_loss_weight
969
+ else:
970
+ mv_loss = torch.tensor(0.0, device=self.device, dtype=self.torch_dtype)
971
+
972
+ if self.ltv_weight > 0:
973
+ ltv_loss = self.get_ltv_loss(latents, batch) * self.ltv_weight
974
+ else:
975
+ ltv_loss = torch.tensor(0.0, device=self.device, dtype=self.torch_dtype)
976
+
977
+ if self.lpm_weight > 0:
978
+ lpm_loss = self.get_latent_pixel_matching_loss(latents, batch) * self.lpm_weight
979
+ else:
980
+ lpm_loss = torch.tensor(0.0, device=self.device, dtype=self.torch_dtype)
981
+
982
+ loss = style_loss + content_loss + kld_loss + mse_loss + tv_loss + critic_gen_loss + pattern_loss + lpips_loss + mv_loss + ltv_loss + mae_loss + lat_mse_loss + clip_loss + pool_loss
983
+
984
+ # check if loss is NaN or Inf
985
+ if torch.isnan(loss) or torch.isinf(loss):
986
+ self.print(f"Loss is NaN or Inf, stopping at step {self.step_num}")
987
+ self.print(f" - Style loss: {style_loss.item()}")
988
+ self.print(f" - Content loss: {content_loss.item()}")
989
+ self.print(f" - KLD loss: {kld_loss.item()}")
990
+ self.print(f" - MSE loss: {mse_loss.item()}")
991
+ self.print(f" - MAE loss: {mae_loss.item()}")
992
+ self.print(f" - Latent MSE loss: {lat_mse_loss.item()}")
993
+ self.print(f" - LPIPS loss: {lpips_loss.item()}")
994
+ self.print(f" - TV loss: {tv_loss.item()}")
995
+ self.print(f" - Pattern loss: {pattern_loss.item()}")
996
+ self.print(f" - CLIP loss: {clip_loss.item()}")
997
+ self.print(f" - Pooled output loss: {pool_loss.item()}")
998
+ self.print(f" - Critic gen loss: {critic_gen_loss.item()}")
999
+ self.print(f" - Critic D loss: {critic_d_loss}")
1000
+ self.print(f" - Mean variance loss: {mv_loss.item()}")
1001
+ self.print(f" - Latent TV loss: {ltv_loss.item()}")
1002
+ self.print(f" - Latent pixel matching loss: {lpm_loss.item()}")
1003
+ self.print(f" - Total loss: {loss.item()}")
1004
+ self.print(f" - Stopping training")
1005
+ exit(1)
1006
+
1007
+ # Backward pass and optimization
1008
+ optimizer.zero_grad()
1009
+ loss.backward()
1010
+ optimizer.step()
1011
+ scheduler.step()
1012
+
1013
+ # update progress bar
1014
+ loss_value = loss.item()
1015
+ # get exponent like 3.54e-4
1016
+ loss_string = f"loss: {loss_value:.2e}"
1017
+ if self.lpips_weight > 0:
1018
+ loss_string += f" lpips: {lpips_loss.item():.2e}"
1019
+ if self.content_weight > 0:
1020
+ loss_string += f" cnt: {content_loss.item():.2e}"
1021
+ if self.style_weight > 0:
1022
+ loss_string += f" sty: {style_loss.item():.2e}"
1023
+ if self.kld_weight > 0:
1024
+ loss_string += f" kld: {kld_loss.item():.2e}"
1025
+ if self.mse_weight > 0:
1026
+ loss_string += f" mse: {mse_loss.item():.2e}"
1027
+ if self.mae_weight > 0:
1028
+ loss_string += f" mae: {mae_loss.item():.2e}"
1029
+ if self.target_latent_vae:
1030
+ loss_string += f" lat_mse: {lat_mse_loss.item():.2e}"
1031
+ if self.tv_weight > 0:
1032
+ loss_string += f" tv: {tv_loss.item():.2e}"
1033
+ if self.pattern_weight > 0:
1034
+ loss_string += f" ptn: {pattern_loss.item():.2e}"
1035
+ if self.clip_weight > 0:
1036
+ loss_string += f" clip: {clip_loss.item():.2e}"
1037
+ if self.do_pooled_exits:
1038
+ loss_string += f" pool: {pool_loss.item():.2e}"
1039
+ if self.use_critic and self.critic_weight > 0:
1040
+ loss_string += f" crG: {critic_gen_loss.item():.2e}"
1041
+ if self.use_critic:
1042
+ loss_string += f" crD: {critic_d_loss:.2e}"
1043
+ if self.mv_loss_weight > 0:
1044
+ loss_string += f" mvl: {mv_loss:.2e}"
1045
+ if self.ltv_weight > 0:
1046
+ loss_string += f" ltv: {ltv_loss:.2e}"
1047
+ if self.lpm_weight > 0:
1048
+ loss_string += f" lpm: {lpm_loss:.2e}"
1049
+
1050
+
1051
+ if hasattr(optimizer, 'get_avg_learning_rate'):
1052
+ learning_rate = optimizer.get_avg_learning_rate()
1053
+ elif self.optimizer_type.startswith('dadaptation') or \
1054
+ self.optimizer_type.lower().startswith('prodigy'):
1055
+ learning_rate = (
1056
+ optimizer.param_groups[0]["d"] *
1057
+ optimizer.param_groups[0]["lr"]
1058
+ )
1059
+ else:
1060
+ learning_rate = optimizer.param_groups[0]['lr']
1061
+
1062
+ lr_critic_string = ''
1063
+ if self.use_critic:
1064
+ lr_critic = self.critic.get_lr()
1065
+ lr_critic_string = f" lrC: {lr_critic:.1e}"
1066
+
1067
+ self.progress_bar.set_postfix_str(f"lr: {learning_rate:.1e}{lr_critic_string} {loss_string}")
1068
+ self.progress_bar.set_description(f"E: {epoch}")
1069
+ self.progress_bar.update(1)
1070
+
1071
+ epoch_losses["total"].append(loss_value)
1072
+ epoch_losses["lpips"].append(lpips_loss.item())
1073
+ epoch_losses["style"].append(style_loss.item())
1074
+ epoch_losses["content"].append(content_loss.item())
1075
+ epoch_losses["mse"].append(mse_loss.item())
1076
+ epoch_losses["mae"].append(mae_loss.item())
1077
+ epoch_losses["lat_mse"].append(lat_mse_loss.item())
1078
+ epoch_losses["kl"].append(kld_loss.item())
1079
+ epoch_losses["tv"].append(tv_loss.item())
1080
+ epoch_losses["ptn"].append(pattern_loss.item())
1081
+ epoch_losses["clip"].append(clip_loss.item())
1082
+ epoch_losses["pool"].append(pool_loss.item())
1083
+ epoch_losses["crG"].append(critic_gen_loss.item())
1084
+ epoch_losses["crD"].append(critic_d_loss)
1085
+ epoch_losses["mvl"].append(mv_loss.item())
1086
+ epoch_losses["ltv"].append(ltv_loss.item())
1087
+ epoch_losses["lpm"].append(lpm_loss.item())
1088
+
1089
+ log_losses["total"].append(loss_value)
1090
+ log_losses["lpips"].append(lpips_loss.item())
1091
+ log_losses["style"].append(style_loss.item())
1092
+ log_losses["content"].append(content_loss.item())
1093
+ log_losses["mse"].append(mse_loss.item())
1094
+ log_losses["mae"].append(mae_loss.item())
1095
+ log_losses["lat_mse"].append(lat_mse_loss.item())
1096
+ log_losses["kl"].append(kld_loss.item())
1097
+ log_losses["tv"].append(tv_loss.item())
1098
+ log_losses["ptn"].append(pattern_loss.item())
1099
+ log_losses["clip"].append(clip_loss.item())
1100
+ log_losses["pool"].append(pool_loss.item())
1101
+ log_losses["crG"].append(critic_gen_loss.item())
1102
+ log_losses["crD"].append(critic_d_loss)
1103
+ log_losses["mvl"].append(mv_loss.item())
1104
+ log_losses["ltv"].append(ltv_loss.item())
1105
+ log_losses["lpm"].append(lpm_loss.item())
1106
+
1107
+ # don't do on first step
1108
+ if self.step_num != start_step:
1109
+ if self.sample_every and self.step_num % self.sample_every == 0:
1110
+ # print above the progress bar
1111
+ self.print(f"Sampling at step {self.step_num}")
1112
+ self.sample(self.step_num)
1113
+
1114
+ if self.save_every and self.step_num % self.save_every == 0:
1115
+ # print above the progress bar
1116
+ self.print(f"Saving at step {self.step_num}")
1117
+ self.save(self.step_num)
1118
+
1119
+ if self.log_every and self.step_num % self.log_every == 0:
1120
+ # log to tensorboard
1121
+ if self.writer is not None:
1122
+ # get avg loss
1123
+ for key in log_losses:
1124
+ log_losses[key] = sum(log_losses[key]) / (len(log_losses[key]) + 1e-6)
1125
+ # if log_losses[key] > 0:
1126
+ self.writer.add_scalar(f"loss/{key}", log_losses[key], self.step_num)
1127
+ # reset log losses
1128
+ log_losses = copy.deepcopy(blank_losses)
1129
+
1130
+ self.step_num += 1
1131
+ # end epoch
1132
+ if self.writer is not None:
1133
+ eps = 1e-6
1134
+ # get avg loss
1135
+ for key in epoch_losses:
1136
+ epoch_losses[key] = sum(log_losses[key]) / (len(log_losses[key]) + eps)
1137
+ if epoch_losses[key] > 0:
1138
+ self.writer.add_scalar(f"epoch loss/{key}", epoch_losses[key], epoch)
1139
+ # reset epoch losses
1140
+ epoch_losses = copy.deepcopy(blank_losses)
1141
+
1142
+ self.save()