44import torch
55import torch .nn .functional as F
66
7- import PIL .Image
87from accelerate import Accelerator
8+ from accelerate .logging import get_logger
99from datasets import load_dataset
10- from diffusers import DDPMPipeline , DDPMScheduler , UNetModel
10+ from diffusers import DDIMPipeline , DDIMScheduler , UNetModel
1111from diffusers .hub_utils import init_git_repo , push_to_hub
1212from diffusers .optimization import get_scheduler
1313from diffusers .training_utils import EMAModel
14- from diffusers .utils import logging
1514from torchvision .transforms import (
1615 CenterCrop ,
1716 Compose ,
2423from tqdm .auto import tqdm
2524
2625
27- logger = logging . get_logger (__name__ )
26+ logger = get_logger (__name__ )
2827
2928
3029def main (args ):
31- accelerator = Accelerator (mixed_precision = args .mixed_precision )
30+ logging_dir = os .path .join (args .output_dir , args .logging_dir )
31+ accelerator = Accelerator (mixed_precision = args .mixed_precision , log_with = "tensorboard" , logging_dir = logging_dir )
3232
3333 model = UNetModel (
3434 attn_resolutions = (16 ,),
@@ -39,8 +39,14 @@ def main(args):
3939 resamp_with_conv = True ,
4040 resolution = args .resolution ,
4141 )
42- noise_scheduler = DDPMScheduler (timesteps = 1000 , tensor_format = "pt" )
43- optimizer = torch .optim .Adam (model .parameters (), lr = args .lr )
42+ noise_scheduler = DDIMScheduler (timesteps = 1000 , tensor_format = "pt" )
43+ optimizer = torch .optim .AdamW (
44+ model .parameters (),
45+ lr = args .learning_rate ,
46+ betas = (args .adam_beta1 , args .adam_beta2 ),
47+ weight_decay = args .adam_weight_decay ,
48+ eps = args .adam_epsilon ,
49+ )
4450
4551 augmentations = Compose (
4652 [
@@ -58,12 +64,12 @@ def transforms(examples):
5864 return {"input" : images }
5965
6066 dataset .set_transform (transforms )
61- train_dataloader = torch .utils .data .DataLoader (dataset , batch_size = args .batch_size , shuffle = True )
67+ train_dataloader = torch .utils .data .DataLoader (dataset , batch_size = args .train_batch_size , shuffle = True )
6268
6369 lr_scheduler = get_scheduler (
64- "linear" ,
70+ args . lr_scheduler ,
6571 optimizer = optimizer ,
66- num_warmup_steps = args .warmup_steps ,
72+ num_warmup_steps = args .lr_warmup_steps ,
6773 num_training_steps = (len (train_dataloader ) * args .num_epochs ) // args .gradient_accumulation_steps ,
6874 )
6975
@@ -76,81 +82,91 @@ def transforms(examples):
7682 if args .push_to_hub :
7783 repo = init_git_repo (args , at_init = True )
7884
85+ if accelerator .is_main_process :
86+ run = os .path .split (__file__ )[- 1 ].split ("." )[0 ]
87+ accelerator .init_trackers (run )
88+
7989 # Train!
8090 is_distributed = torch .distributed .is_available () and torch .distributed .is_initialized ()
8191 world_size = torch .distributed .get_world_size () if is_distributed else 1
82- total_train_batch_size = args .batch_size * args .gradient_accumulation_steps * world_size
92+ total_train_batch_size = args .train_batch_size * args .gradient_accumulation_steps * world_size
8393 max_steps = len (train_dataloader ) // args .gradient_accumulation_steps * args .num_epochs
8494 logger .info ("***** Running training *****" )
8595 logger .info (f" Num examples = { len (train_dataloader .dataset )} " )
8696 logger .info (f" Num Epochs = { args .num_epochs } " )
87- logger .info (f" Instantaneous batch size per device = { args .batch_size } " )
97+ logger .info (f" Instantaneous batch size per device = { args .train_batch_size } " )
8898 logger .info (f" Total train batch size (w. parallel, distributed & accumulation) = { total_train_batch_size } " )
8999 logger .info (f" Gradient Accumulation steps = { args .gradient_accumulation_steps } " )
90100 logger .info (f" Total optimization steps = { max_steps } " )
91101
92102 global_step = 0
93103 for epoch in range (args .num_epochs ):
94104 model .train ()
95- with tqdm (total = len (train_dataloader ), unit = "ba" ) as pbar :
96- pbar .set_description (f"Epoch { epoch } " )
97- for step , batch in enumerate (train_dataloader ):
98- clean_images = batch ["input" ]
99- noise_samples = torch .randn (clean_images .shape ).to (clean_images .device )
100- bsz = clean_images .shape [0 ]
101- timesteps = torch .randint (0 , noise_scheduler .timesteps , (bsz ,), device = clean_images .device ).long ()
102-
103- # add noise onto the clean images according to the noise magnitude at each timestep
104- # (this is the forward diffusion process)
105- noisy_images = noise_scheduler .training_step (clean_images , noise_samples , timesteps )
106-
107- if step % args .gradient_accumulation_steps != 0 :
108- with accelerator .no_sync (model ):
109- output = model (noisy_images , timesteps )
110- # predict the noise residual
111- loss = F .mse_loss (output , noise_samples )
112- loss = loss / args .gradient_accumulation_steps
113- accelerator .backward (loss )
114- else :
105+ progress_bar = tqdm (total = len (train_dataloader ), disable = not accelerator .is_local_main_process )
106+ progress_bar .set_description (f"Epoch { epoch } " )
107+ for step , batch in enumerate (train_dataloader ):
108+ clean_images = batch ["input" ]
109+ noise_samples = torch .randn (clean_images .shape ).to (clean_images .device )
110+ bsz = clean_images .shape [0 ]
111+ timesteps = torch .randint (0 , noise_scheduler .timesteps , (bsz ,), device = clean_images .device ).long ()
112+
113+ # add noise onto the clean images according to the noise magnitude at each timestep
114+ # (this is the forward diffusion process)
115+ noisy_images = noise_scheduler .add_noise (clean_images , noise_samples , timesteps )
116+
117+ if step % args .gradient_accumulation_steps != 0 :
118+ with accelerator .no_sync (model ):
115119 output = model (noisy_images , timesteps )
116120 # predict the noise residual
117121 loss = F .mse_loss (output , noise_samples )
118122 loss = loss / args .gradient_accumulation_steps
119123 accelerator .backward (loss )
120- torch .nn .utils .clip_grad_norm_ (model .parameters (), 1.0 )
121- optimizer .step ()
122- lr_scheduler .step ()
123- ema_model .step (model , global_step )
124- optimizer .zero_grad ()
125- pbar .update (1 )
126- pbar .set_postfix (
127- loss = loss .detach ().item (), lr = optimizer .param_groups [0 ]["lr" ], ema_decay = ema_model .decay
128- )
129- global_step += 1
124+ else :
125+ output = model (noisy_images , timesteps )
126+ # predict the noise residual
127+ loss = F .mse_loss (output , noise_samples )
128+ loss = loss / args .gradient_accumulation_steps
129+ accelerator .backward (loss )
130+ torch .nn .utils .clip_grad_norm_ (model .parameters (), 1.0 )
131+ optimizer .step ()
132+ lr_scheduler .step ()
133+ ema_model .step (model , global_step )
134+ optimizer .zero_grad ()
135+ progress_bar .update (1 )
136+ progress_bar .set_postfix (
137+ loss = loss .detach ().item (), lr = optimizer .param_groups [0 ]["lr" ], ema_decay = ema_model .decay
138+ )
139+ accelerator .log (
140+ {
141+ "train_loss" : loss .detach ().item (),
142+ "epoch" : epoch ,
143+ "ema_decay" : ema_model .decay ,
144+ "step" : global_step ,
145+ },
146+ step = global_step ,
147+ )
148+ global_step += 1
149+ progress_bar .close ()
130150
131151 accelerator .wait_for_everyone ()
132152
133153 # Generate a sample image for visual inspection
134154 if accelerator .is_main_process :
135155 with torch .no_grad ():
136- pipeline = DDPMPipeline (
137- unet = accelerator .unwrap_model (ema_model .averaged_model ), noise_scheduler = noise_scheduler
156+ pipeline = DDIMPipeline (
157+ unet = accelerator .unwrap_model (ema_model .averaged_model ),
158+ noise_scheduler = noise_scheduler ,
138159 )
139160
140161 generator = torch .manual_seed (0 )
141162 # run pipeline in inference (sample random noise and denoise)
142- image = pipeline (generator = generator )
163+ images = pipeline (generator = generator , batch_size = args . eval_batch_size , num_inference_steps = 50 )
143164
144- # process image to PIL
145- image_processed = image .cpu ().permute (0 , 2 , 3 , 1 )
146- image_processed = (image_processed + 1.0 ) * 127.5
147- image_processed = image_processed .type (torch .uint8 ).numpy ()
148- image_pil = PIL .Image .fromarray (image_processed [0 ])
165+ # denormalize the images and save to tensorboard
166+ images_processed = (images .cpu () + 1.0 ) * 127.5
167+ images_processed = images_processed .clamp (0 , 255 ).type (torch .uint8 ).numpy ()
149168
150- # save image
151- test_dir = os .path .join (args .output_dir , "test_samples" )
152- os .makedirs (test_dir , exist_ok = True )
153- image_pil .save (f"{ test_dir } /{ epoch :04d} .png" )
169+ accelerator .trackers [0 ].writer .add_images ("test_samples" , images_processed , epoch )
154170
155171 # save the model
156172 if args .push_to_hub :
@@ -159,6 +175,8 @@ def transforms(examples):
159175 pipeline .save_pretrained (args .output_dir )
160176 accelerator .wait_for_everyone ()
161177
178+ accelerator .end_training ()
179+
162180
163181if __name__ == "__main__" :
164182 parser = argparse .ArgumentParser (description = "Simple example of a training script." )
@@ -167,18 +185,25 @@ def transforms(examples):
167185 parser .add_argument ("--output_dir" , type = str , default = "ddpm-model" )
168186 parser .add_argument ("--overwrite_output_dir" , action = "store_true" )
169187 parser .add_argument ("--resolution" , type = int , default = 64 )
170- parser .add_argument ("--batch_size" , type = int , default = 16 )
188+ parser .add_argument ("--train_batch_size" , type = int , default = 16 )
189+ parser .add_argument ("--eval_batch_size" , type = int , default = 16 )
171190 parser .add_argument ("--num_epochs" , type = int , default = 100 )
172191 parser .add_argument ("--gradient_accumulation_steps" , type = int , default = 1 )
173- parser .add_argument ("--lr" , type = float , default = 1e-4 )
174- parser .add_argument ("--warmup_steps" , type = int , default = 500 )
192+ parser .add_argument ("--learning_rate" , type = float , default = 1e-4 )
193+ parser .add_argument ("--lr_scheduler" , type = str , default = "cosine" )
194+ parser .add_argument ("--lr_warmup_steps" , type = int , default = 500 )
195+ parser .add_argument ("--adam_beta1" , type = float , default = 0.95 )
196+ parser .add_argument ("--adam_beta2" , type = float , default = 0.999 )
197+ parser .add_argument ("--adam_weight_decay" , type = float , default = 1e-6 )
198+ parser .add_argument ("--adam_epsilon" , type = float , default = 1e-3 )
175199 parser .add_argument ("--ema_inv_gamma" , type = float , default = 1.0 )
176200 parser .add_argument ("--ema_power" , type = float , default = 3 / 4 )
177- parser .add_argument ("--ema_max_decay" , type = float , default = 0.999 )
201+ parser .add_argument ("--ema_max_decay" , type = float , default = 0.9999 )
178202 parser .add_argument ("--push_to_hub" , action = "store_true" )
179203 parser .add_argument ("--hub_token" , type = str , default = None )
180204 parser .add_argument ("--hub_model_id" , type = str , default = None )
181205 parser .add_argument ("--hub_private_repo" , action = "store_true" )
206+ parser .add_argument ("--logging_dir" , type = str , default = "logs" )
182207 parser .add_argument (
183208 "--mixed_precision" ,
184209 type = str ,
0 commit comments