classifier.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. import os
  2. import torch
  3. import pytorch_lightning as pl
  4. from omegaconf import OmegaConf
  5. from torch.nn import functional as F
  6. from torch.optim import AdamW
  7. from torch.optim.lr_scheduler import LambdaLR
  8. from copy import deepcopy
  9. from einops import rearrange
  10. from glob import glob
  11. from natsort import natsorted
  12. from ldm.modules.diffusionmodules.openaimodel import EncoderUNetModel, UNetModel
  13. from ldm.util import log_txt_as_img, default, ismap, instantiate_from_config
  14. __models__ = {
  15. 'class_label': EncoderUNetModel,
  16. 'segmentation': UNetModel
  17. }
  18. def disabled_train(self, mode=True):
  19. """Overwrite model.train with this function to make sure train/eval mode
  20. does not change anymore."""
  21. return self
  22. class NoisyLatentImageClassifier(pl.LightningModule):
  23. def __init__(self,
  24. diffusion_path,
  25. num_classes,
  26. ckpt_path=None,
  27. pool='attention',
  28. label_key=None,
  29. diffusion_ckpt_path=None,
  30. scheduler_config=None,
  31. weight_decay=1.e-2,
  32. log_steps=10,
  33. monitor='val/loss',
  34. *args,
  35. **kwargs):
  36. super().__init__(*args, **kwargs)
  37. self.num_classes = num_classes
  38. # get latest config of diffusion model
  39. diffusion_config = natsorted(glob(os.path.join(diffusion_path, 'configs', '*-project.yaml')))[-1]
  40. self.diffusion_config = OmegaConf.load(diffusion_config).model
  41. self.diffusion_config.params.ckpt_path = diffusion_ckpt_path
  42. self.load_diffusion()
  43. self.monitor = monitor
  44. self.numd = self.diffusion_model.first_stage_model.encoder.num_resolutions - 1
  45. self.log_time_interval = self.diffusion_model.num_timesteps // log_steps
  46. self.log_steps = log_steps
  47. self.label_key = label_key if not hasattr(self.diffusion_model, 'cond_stage_key') \
  48. else self.diffusion_model.cond_stage_key
  49. assert self.label_key is not None, 'label_key neither in diffusion model nor in model.params'
  50. if self.label_key not in __models__:
  51. raise NotImplementedError()
  52. self.load_classifier(ckpt_path, pool)
  53. self.scheduler_config = scheduler_config
  54. self.use_scheduler = self.scheduler_config is not None
  55. self.weight_decay = weight_decay
  56. def init_from_ckpt(self, path, ignore_keys=list(), only_model=False):
  57. sd = torch.load(path, map_location="cpu")
  58. if "state_dict" in list(sd.keys()):
  59. sd = sd["state_dict"]
  60. keys = list(sd.keys())
  61. for k in keys:
  62. for ik in ignore_keys:
  63. if k.startswith(ik):
  64. print("Deleting key {} from state_dict.".format(k))
  65. del sd[k]
  66. missing, unexpected = self.load_state_dict(sd, strict=False) if not only_model else self.model.load_state_dict(
  67. sd, strict=False)
  68. print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys")
  69. if len(missing) > 0:
  70. print(f"Missing Keys: {missing}")
  71. if len(unexpected) > 0:
  72. print(f"Unexpected Keys: {unexpected}")
  73. def load_diffusion(self):
  74. model = instantiate_from_config(self.diffusion_config)
  75. self.diffusion_model = model.eval()
  76. self.diffusion_model.train = disabled_train
  77. for param in self.diffusion_model.parameters():
  78. param.requires_grad = False
  79. def load_classifier(self, ckpt_path, pool):
  80. model_config = deepcopy(self.diffusion_config.params.unet_config.params)
  81. model_config.in_channels = self.diffusion_config.params.unet_config.params.out_channels
  82. model_config.out_channels = self.num_classes
  83. if self.label_key == 'class_label':
  84. model_config.pool = pool
  85. self.model = __models__[self.label_key](**model_config)
  86. if ckpt_path is not None:
  87. print('#####################################################################')
  88. print(f'load from ckpt "{ckpt_path}"')
  89. print('#####################################################################')
  90. self.init_from_ckpt(ckpt_path)
  91. @torch.no_grad()
  92. def get_x_noisy(self, x, t, noise=None):
  93. noise = default(noise, lambda: torch.randn_like(x))
  94. continuous_sqrt_alpha_cumprod = None
  95. if self.diffusion_model.use_continuous_noise:
  96. continuous_sqrt_alpha_cumprod = self.diffusion_model.sample_continuous_noise_level(x.shape[0], t + 1)
  97. # todo: make sure t+1 is correct here
  98. return self.diffusion_model.q_sample(x_start=x, t=t, noise=noise,
  99. continuous_sqrt_alpha_cumprod=continuous_sqrt_alpha_cumprod)
  100. def forward(self, x_noisy, t, *args, **kwargs):
  101. return self.model(x_noisy, t)
  102. @torch.no_grad()
  103. def get_input(self, batch, k):
  104. x = batch[k]
  105. if len(x.shape) == 3:
  106. x = x[..., None]
  107. x = rearrange(x, 'b h w c -> b c h w')
  108. x = x.to(memory_format=torch.contiguous_format).float()
  109. return x
  110. @torch.no_grad()
  111. def get_conditioning(self, batch, k=None):
  112. if k is None:
  113. k = self.label_key
  114. assert k is not None, 'Needs to provide label key'
  115. targets = batch[k].to(self.device)
  116. if self.label_key == 'segmentation':
  117. targets = rearrange(targets, 'b h w c -> b c h w')
  118. for down in range(self.numd):
  119. h, w = targets.shape[-2:]
  120. targets = F.interpolate(targets, size=(h // 2, w // 2), mode='nearest')
  121. # targets = rearrange(targets,'b c h w -> b h w c')
  122. return targets
  123. def compute_top_k(self, logits, labels, k, reduction="mean"):
  124. _, top_ks = torch.topk(logits, k, dim=1)
  125. if reduction == "mean":
  126. return (top_ks == labels[:, None]).float().sum(dim=-1).mean().item()
  127. elif reduction == "none":
  128. return (top_ks == labels[:, None]).float().sum(dim=-1)
  129. def on_train_epoch_start(self):
  130. # save some memory
  131. self.diffusion_model.model.to('cpu')
  132. @torch.no_grad()
  133. def write_logs(self, loss, logits, targets):
  134. log_prefix = 'train' if self.training else 'val'
  135. log = {}
  136. log[f"{log_prefix}/loss"] = loss.mean()
  137. log[f"{log_prefix}/acc@1"] = self.compute_top_k(
  138. logits, targets, k=1, reduction="mean"
  139. )
  140. log[f"{log_prefix}/acc@5"] = self.compute_top_k(
  141. logits, targets, k=5, reduction="mean"
  142. )
  143. self.log_dict(log, prog_bar=False, logger=True, on_step=self.training, on_epoch=True)
  144. self.log('loss', log[f"{log_prefix}/loss"], prog_bar=True, logger=False)
  145. self.log('global_step', self.global_step, logger=False, on_epoch=False, prog_bar=True)
  146. lr = self.optimizers().param_groups[0]['lr']
  147. self.log('lr_abs', lr, on_step=True, logger=True, on_epoch=False, prog_bar=True)
  148. def shared_step(self, batch, t=None):
  149. x, *_ = self.diffusion_model.get_input(batch, k=self.diffusion_model.first_stage_key)
  150. targets = self.get_conditioning(batch)
  151. if targets.dim() == 4:
  152. targets = targets.argmax(dim=1)
  153. if t is None:
  154. t = torch.randint(0, self.diffusion_model.num_timesteps, (x.shape[0],), device=self.device).long()
  155. else:
  156. t = torch.full(size=(x.shape[0],), fill_value=t, device=self.device).long()
  157. x_noisy = self.get_x_noisy(x, t)
  158. logits = self(x_noisy, t)
  159. loss = F.cross_entropy(logits, targets, reduction='none')
  160. self.write_logs(loss.detach(), logits.detach(), targets.detach())
  161. loss = loss.mean()
  162. return loss, logits, x_noisy, targets
  163. def training_step(self, batch, batch_idx):
  164. loss, *_ = self.shared_step(batch)
  165. return loss
  166. def reset_noise_accs(self):
  167. self.noisy_acc = {t: {'acc@1': [], 'acc@5': []} for t in
  168. range(0, self.diffusion_model.num_timesteps, self.diffusion_model.log_every_t)}
  169. def on_validation_start(self):
  170. self.reset_noise_accs()
  171. @torch.no_grad()
  172. def validation_step(self, batch, batch_idx):
  173. loss, *_ = self.shared_step(batch)
  174. for t in self.noisy_acc:
  175. _, logits, _, targets = self.shared_step(batch, t)
  176. self.noisy_acc[t]['acc@1'].append(self.compute_top_k(logits, targets, k=1, reduction='mean'))
  177. self.noisy_acc[t]['acc@5'].append(self.compute_top_k(logits, targets, k=5, reduction='mean'))
  178. return loss
  179. def configure_optimizers(self):
  180. optimizer = AdamW(self.model.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
  181. if self.use_scheduler:
  182. scheduler = instantiate_from_config(self.scheduler_config)
  183. print("Setting up LambdaLR scheduler...")
  184. scheduler = [
  185. {
  186. 'scheduler': LambdaLR(optimizer, lr_lambda=scheduler.schedule),
  187. 'interval': 'step',
  188. 'frequency': 1
  189. }]
  190. return [optimizer], scheduler
  191. return optimizer
  192. @torch.no_grad()
  193. def log_images(self, batch, N=8, *args, **kwargs):
  194. log = dict()
  195. x = self.get_input(batch, self.diffusion_model.first_stage_key)
  196. log['inputs'] = x
  197. y = self.get_conditioning(batch)
  198. if self.label_key == 'class_label':
  199. y = log_txt_as_img((x.shape[2], x.shape[3]), batch["human_label"])
  200. log['labels'] = y
  201. if ismap(y):
  202. log['labels'] = self.diffusion_model.to_rgb(y)
  203. for step in range(self.log_steps):
  204. current_time = step * self.log_time_interval
  205. _, logits, x_noisy, _ = self.shared_step(batch, t=current_time)
  206. log[f'inputs@t{current_time}'] = x_noisy
  207. pred = F.one_hot(logits.argmax(dim=1), num_classes=self.num_classes)
  208. pred = rearrange(pred, 'b h w c -> b c h w')
  209. log[f'pred@t{current_time}'] = self.diffusion_model.to_rgb(pred)
  210. for key in log:
  211. log[key] = log[key][:N]
  212. return log