main.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. import argparse, os, sys, datetime, glob, importlib, csv
  2. import numpy as np
  3. import time
  4. import torch
  5. import torchvision
  6. import pytorch_lightning as pl
  7. from packaging import version
  8. from omegaconf import OmegaConf
  9. from torch.utils.data import random_split, DataLoader, Dataset, Subset
  10. from functools import partial
  11. from PIL import Image
  12. from pytorch_lightning import seed_everything
  13. from pytorch_lightning.trainer import Trainer
  14. from pytorch_lightning.callbacks import ModelCheckpoint, Callback, LearningRateMonitor
  15. from pytorch_lightning.utilities.distributed import rank_zero_only
  16. from pytorch_lightning.utilities import rank_zero_info
  17. from ldm.data.base import Txt2ImgIterableBaseDataset
  18. from ldm.util import instantiate_from_config
  19. def get_parser(**parser_kwargs):
  20. def str2bool(v):
  21. if isinstance(v, bool):
  22. return v
  23. if v.lower() in ("yes", "true", "t", "y", "1"):
  24. return True
  25. elif v.lower() in ("no", "false", "f", "n", "0"):
  26. return False
  27. else:
  28. raise argparse.ArgumentTypeError("Boolean value expected.")
  29. parser = argparse.ArgumentParser(**parser_kwargs)
  30. parser.add_argument(
  31. "-n",
  32. "--name",
  33. type=str,
  34. const=True,
  35. default="",
  36. nargs="?",
  37. help="postfix for logdir",
  38. )
  39. parser.add_argument(
  40. "-r",
  41. "--resume",
  42. type=str,
  43. const=True,
  44. default="",
  45. nargs="?",
  46. help="resume from logdir or checkpoint in logdir",
  47. )
  48. parser.add_argument(
  49. "-b",
  50. "--base",
  51. nargs="*",
  52. metavar="base_config.yaml",
  53. help="paths to base configs. Loaded from left-to-right. "
  54. "Parameters can be overwritten or added with command-line options of the form `--key value`.",
  55. default=list(),
  56. )
  57. parser.add_argument(
  58. "-t",
  59. "--train",
  60. type=str2bool,
  61. const=True,
  62. default=False,
  63. nargs="?",
  64. help="train",
  65. )
  66. parser.add_argument(
  67. "--no-test",
  68. type=str2bool,
  69. const=True,
  70. default=False,
  71. nargs="?",
  72. help="disable test",
  73. )
  74. parser.add_argument(
  75. "-p",
  76. "--project",
  77. help="name of new or path to existing project"
  78. )
  79. parser.add_argument(
  80. "-d",
  81. "--debug",
  82. type=str2bool,
  83. nargs="?",
  84. const=True,
  85. default=False,
  86. help="enable post-mortem debugging",
  87. )
  88. parser.add_argument(
  89. "-s",
  90. "--seed",
  91. type=int,
  92. default=23,
  93. help="seed for seed_everything",
  94. )
  95. parser.add_argument(
  96. "-f",
  97. "--postfix",
  98. type=str,
  99. default="",
  100. help="post-postfix for default name",
  101. )
  102. parser.add_argument(
  103. "-l",
  104. "--logdir",
  105. type=str,
  106. default="logs",
  107. help="directory for logging dat shit",
  108. )
  109. parser.add_argument(
  110. "--scale_lr",
  111. type=str2bool,
  112. nargs="?",
  113. const=True,
  114. default=True,
  115. help="scale base-lr by ngpu * batch_size * n_accumulate",
  116. )
  117. return parser
  118. def nondefault_trainer_args(opt):
  119. parser = argparse.ArgumentParser()
  120. parser = Trainer.add_argparse_args(parser)
  121. args = parser.parse_args([])
  122. return sorted(k for k in vars(args) if getattr(opt, k) != getattr(args, k))
  123. class WrappedDataset(Dataset):
  124. """Wraps an arbitrary object with __len__ and __getitem__ into a pytorch dataset"""
  125. def __init__(self, dataset):
  126. self.data = dataset
  127. def __len__(self):
  128. return len(self.data)
  129. def __getitem__(self, idx):
  130. return self.data[idx]
  131. def worker_init_fn(_):
  132. worker_info = torch.utils.data.get_worker_info()
  133. dataset = worker_info.dataset
  134. worker_id = worker_info.id
  135. if isinstance(dataset, Txt2ImgIterableBaseDataset):
  136. split_size = dataset.num_records // worker_info.num_workers
  137. # reset num_records to the true number to retain reliable length information
  138. dataset.sample_ids = dataset.valid_ids[worker_id * split_size:(worker_id + 1) * split_size]
  139. current_id = np.random.choice(len(np.random.get_state()[1]), 1)
  140. return np.random.seed(np.random.get_state()[1][current_id] + worker_id)
  141. else:
  142. return np.random.seed(np.random.get_state()[1][0] + worker_id)
  143. class DataModuleFromConfig(pl.LightningDataModule):
  144. def __init__(self, batch_size, train=None, validation=None, test=None, predict=None,
  145. wrap=False, num_workers=None, shuffle_test_loader=False, use_worker_init_fn=False,
  146. shuffle_val_dataloader=False):
  147. super().__init__()
  148. self.batch_size = batch_size
  149. self.dataset_configs = dict()
  150. self.num_workers = num_workers if num_workers is not None else batch_size * 2
  151. self.use_worker_init_fn = use_worker_init_fn
  152. if train is not None:
  153. self.dataset_configs["train"] = train
  154. self.train_dataloader = self._train_dataloader
  155. if validation is not None:
  156. self.dataset_configs["validation"] = validation
  157. self.val_dataloader = partial(self._val_dataloader, shuffle=shuffle_val_dataloader)
  158. if test is not None:
  159. self.dataset_configs["test"] = test
  160. self.test_dataloader = partial(self._test_dataloader, shuffle=shuffle_test_loader)
  161. if predict is not None:
  162. self.dataset_configs["predict"] = predict
  163. self.predict_dataloader = self._predict_dataloader
  164. self.wrap = wrap
  165. def prepare_data(self):
  166. for data_cfg in self.dataset_configs.values():
  167. instantiate_from_config(data_cfg)
  168. def setup(self, stage=None):
  169. self.datasets = dict(
  170. (k, instantiate_from_config(self.dataset_configs[k]))
  171. for k in self.dataset_configs)
  172. if self.wrap:
  173. for k in self.datasets:
  174. self.datasets[k] = WrappedDataset(self.datasets[k])
  175. def _train_dataloader(self):
  176. is_iterable_dataset = isinstance(self.datasets['train'], Txt2ImgIterableBaseDataset)
  177. if is_iterable_dataset or self.use_worker_init_fn:
  178. init_fn = worker_init_fn
  179. else:
  180. init_fn = None
  181. return DataLoader(self.datasets["train"], batch_size=self.batch_size,
  182. num_workers=self.num_workers, shuffle=False if is_iterable_dataset else True,
  183. worker_init_fn=init_fn)
  184. def _val_dataloader(self, shuffle=False):
  185. if isinstance(self.datasets['validation'], Txt2ImgIterableBaseDataset) or self.use_worker_init_fn:
  186. init_fn = worker_init_fn
  187. else:
  188. init_fn = None
  189. return DataLoader(self.datasets["validation"],
  190. batch_size=self.batch_size,
  191. num_workers=self.num_workers,
  192. worker_init_fn=init_fn,
  193. shuffle=shuffle)
  194. def _test_dataloader(self, shuffle=False):
  195. is_iterable_dataset = isinstance(self.datasets['train'], Txt2ImgIterableBaseDataset)
  196. if is_iterable_dataset or self.use_worker_init_fn:
  197. init_fn = worker_init_fn
  198. else:
  199. init_fn = None
  200. # do not shuffle dataloader for iterable dataset
  201. shuffle = shuffle and (not is_iterable_dataset)
  202. return DataLoader(self.datasets["test"], batch_size=self.batch_size,
  203. num_workers=self.num_workers, worker_init_fn=init_fn, shuffle=shuffle)
  204. def _predict_dataloader(self, shuffle=False):
  205. if isinstance(self.datasets['predict'], Txt2ImgIterableBaseDataset) or self.use_worker_init_fn:
  206. init_fn = worker_init_fn
  207. else:
  208. init_fn = None
  209. return DataLoader(self.datasets["predict"], batch_size=self.batch_size,
  210. num_workers=self.num_workers, worker_init_fn=init_fn)
  211. class SetupCallback(Callback):
  212. def __init__(self, resume, now, logdir, ckptdir, cfgdir, config, lightning_config):
  213. super().__init__()
  214. self.resume = resume
  215. self.now = now
  216. self.logdir = logdir
  217. self.ckptdir = ckptdir
  218. self.cfgdir = cfgdir
  219. self.config = config
  220. self.lightning_config = lightning_config
  221. def on_keyboard_interrupt(self, trainer, pl_module):
  222. if trainer.global_rank == 0:
  223. print("Summoning checkpoint.")
  224. ckpt_path = os.path.join(self.ckptdir, "last.ckpt")
  225. trainer.save_checkpoint(ckpt_path)
  226. def on_pretrain_routine_start(self, trainer, pl_module):
  227. if trainer.global_rank == 0:
  228. # Create logdirs and save configs
  229. os.makedirs(self.logdir, exist_ok=True)
  230. os.makedirs(self.ckptdir, exist_ok=True)
  231. os.makedirs(self.cfgdir, exist_ok=True)
  232. if "callbacks" in self.lightning_config:
  233. if 'metrics_over_trainsteps_checkpoint' in self.lightning_config['callbacks']:
  234. os.makedirs(os.path.join(self.ckptdir, 'trainstep_checkpoints'), exist_ok=True)
  235. print("Project config")
  236. print(OmegaConf.to_yaml(self.config))
  237. OmegaConf.save(self.config,
  238. os.path.join(self.cfgdir, "{}-project.yaml".format(self.now)))
  239. print("Lightning config")
  240. print(OmegaConf.to_yaml(self.lightning_config))
  241. OmegaConf.save(OmegaConf.create({"lightning": self.lightning_config}),
  242. os.path.join(self.cfgdir, "{}-lightning.yaml".format(self.now)))
  243. else:
  244. # ModelCheckpoint callback created log directory --- remove it
  245. if not self.resume and os.path.exists(self.logdir):
  246. dst, name = os.path.split(self.logdir)
  247. dst = os.path.join(dst, "child_runs", name)
  248. os.makedirs(os.path.split(dst)[0], exist_ok=True)
  249. try:
  250. os.rename(self.logdir, dst)
  251. except FileNotFoundError:
  252. pass
  253. class ImageLogger(Callback):
  254. def __init__(self, batch_frequency, max_images, clamp=True, increase_log_steps=True,
  255. rescale=True, disabled=False, log_on_batch_idx=False, log_first_step=False,
  256. log_images_kwargs=None):
  257. super().__init__()
  258. self.rescale = rescale
  259. self.batch_freq = batch_frequency
  260. self.max_images = max_images
  261. self.logger_log_images = {
  262. pl.loggers.TestTubeLogger: self._testtube,
  263. }
  264. self.log_steps = [2 ** n for n in range(int(np.log2(self.batch_freq)) + 1)]
  265. if not increase_log_steps:
  266. self.log_steps = [self.batch_freq]
  267. self.clamp = clamp
  268. self.disabled = disabled
  269. self.log_on_batch_idx = log_on_batch_idx
  270. self.log_images_kwargs = log_images_kwargs if log_images_kwargs else {}
  271. self.log_first_step = log_first_step
  272. @rank_zero_only
  273. def _testtube(self, pl_module, images, batch_idx, split):
  274. for k in images:
  275. grid = torchvision.utils.make_grid(images[k])
  276. grid = (grid + 1.0) / 2.0 # -1,1 -> 0,1; c,h,w
  277. tag = f"{split}/{k}"
  278. pl_module.logger.experiment.add_image(
  279. tag, grid,
  280. global_step=pl_module.global_step)
  281. @rank_zero_only
  282. def log_local(self, save_dir, split, images,
  283. global_step, current_epoch, batch_idx):
  284. root = os.path.join(save_dir, "images", split)
  285. for k in images:
  286. grid = torchvision.utils.make_grid(images[k], nrow=4)
  287. if self.rescale:
  288. grid = (grid + 1.0) / 2.0 # -1,1 -> 0,1; c,h,w
  289. grid = grid.transpose(0, 1).transpose(1, 2).squeeze(-1)
  290. grid = grid.numpy()
  291. grid = (grid * 255).astype(np.uint8)
  292. filename = "{}_gs-{:06}_e-{:06}_b-{:06}.png".format(
  293. k,
  294. global_step,
  295. current_epoch,
  296. batch_idx)
  297. path = os.path.join(root, filename)
  298. os.makedirs(os.path.split(path)[0], exist_ok=True)
  299. Image.fromarray(grid).save(path)
  300. def log_img(self, pl_module, batch, batch_idx, split="train"):
  301. check_idx = batch_idx if self.log_on_batch_idx else pl_module.global_step
  302. if (self.check_frequency(check_idx) and # batch_idx % self.batch_freq == 0
  303. hasattr(pl_module, "log_images") and
  304. callable(pl_module.log_images) and
  305. self.max_images > 0):
  306. logger = type(pl_module.logger)
  307. is_train = pl_module.training
  308. if is_train:
  309. pl_module.eval()
  310. with torch.no_grad():
  311. images = pl_module.log_images(batch, split=split, **self.log_images_kwargs)
  312. for k in images:
  313. N = min(images[k].shape[0], self.max_images)
  314. images[k] = images[k][:N]
  315. if isinstance(images[k], torch.Tensor):
  316. images[k] = images[k].detach().cpu()
  317. if self.clamp:
  318. images[k] = torch.clamp(images[k], -1., 1.)
  319. self.log_local(pl_module.logger.save_dir, split, images,
  320. pl_module.global_step, pl_module.current_epoch, batch_idx)
  321. logger_log_images = self.logger_log_images.get(logger, lambda *args, **kwargs: None)
  322. logger_log_images(pl_module, images, pl_module.global_step, split)
  323. if is_train:
  324. pl_module.train()
  325. def check_frequency(self, check_idx):
  326. if ((check_idx % self.batch_freq) == 0 or (check_idx in self.log_steps)) and (
  327. check_idx > 0 or self.log_first_step):
  328. try:
  329. self.log_steps.pop(0)
  330. except IndexError as e:
  331. print(e)
  332. pass
  333. return True
  334. return False
  335. def on_train_batch_end(self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx):
  336. if not self.disabled and (pl_module.global_step > 0 or self.log_first_step):
  337. self.log_img(pl_module, batch, batch_idx, split="train")
  338. def on_validation_batch_end(self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx):
  339. if not self.disabled and pl_module.global_step > 0:
  340. self.log_img(pl_module, batch, batch_idx, split="val")
  341. if hasattr(pl_module, 'calibrate_grad_norm'):
  342. if (pl_module.calibrate_grad_norm and batch_idx % 25 == 0) and batch_idx > 0:
  343. self.log_gradients(trainer, pl_module, batch_idx=batch_idx)
  344. class CUDACallback(Callback):
  345. # see https://github.com/SeanNaren/minGPT/blob/master/mingpt/callback.py
  346. def on_train_epoch_start(self, trainer, pl_module):
  347. # Reset the memory use counter
  348. torch.cuda.reset_peak_memory_stats(trainer.root_gpu)
  349. torch.cuda.synchronize(trainer.root_gpu)
  350. self.start_time = time.time()
  351. def on_train_epoch_end(self, trainer, pl_module, outputs):
  352. torch.cuda.synchronize(trainer.root_gpu)
  353. max_memory = torch.cuda.max_memory_allocated(trainer.root_gpu) / 2 ** 20
  354. epoch_time = time.time() - self.start_time
  355. try:
  356. max_memory = trainer.training_type_plugin.reduce(max_memory)
  357. epoch_time = trainer.training_type_plugin.reduce(epoch_time)
  358. rank_zero_info(f"Average Epoch time: {epoch_time:.2f} seconds")
  359. rank_zero_info(f"Average Peak memory {max_memory:.2f}MiB")
  360. except AttributeError:
  361. pass
  362. if __name__ == "__main__":
  363. # custom parser to specify config files, train, test and debug mode,
  364. # postfix, resume.
  365. # `--key value` arguments are interpreted as arguments to the trainer.
  366. # `nested.key=value` arguments are interpreted as config parameters.
  367. # configs are merged from left-to-right followed by command line parameters.
  368. # model:
  369. # base_learning_rate: float
  370. # target: path to lightning module
  371. # params:
  372. # key: value
  373. # data:
  374. # target: main.DataModuleFromConfig
  375. # params:
  376. # batch_size: int
  377. # wrap: bool
  378. # train:
  379. # target: path to train dataset
  380. # params:
  381. # key: value
  382. # validation:
  383. # target: path to validation dataset
  384. # params:
  385. # key: value
  386. # test:
  387. # target: path to test dataset
  388. # params:
  389. # key: value
  390. # lightning: (optional, has sane defaults and can be specified on cmdline)
  391. # trainer:
  392. # additional arguments to trainer
  393. # logger:
  394. # logger to instantiate
  395. # modelcheckpoint:
  396. # modelcheckpoint to instantiate
  397. # callbacks:
  398. # callback1:
  399. # target: importpath
  400. # params:
  401. # key: value
  402. now = datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")
  403. # add cwd for convenience and to make classes in this file available when
  404. # running as `python main.py`
  405. # (in particular `main.DataModuleFromConfig`)
  406. sys.path.append(os.getcwd())
  407. parser = get_parser()
  408. parser = Trainer.add_argparse_args(parser)
  409. opt, unknown = parser.parse_known_args()
  410. if opt.name and opt.resume:
  411. raise ValueError(
  412. "-n/--name and -r/--resume cannot be specified both."
  413. "If you want to resume training in a new log folder, "
  414. "use -n/--name in combination with --resume_from_checkpoint"
  415. )
  416. if opt.resume:
  417. if not os.path.exists(opt.resume):
  418. raise ValueError("Cannot find {}".format(opt.resume))
  419. if os.path.isfile(opt.resume):
  420. paths = opt.resume.split("/")
  421. # idx = len(paths)-paths[::-1].index("logs")+1
  422. # logdir = "/".join(paths[:idx])
  423. logdir = "/".join(paths[:-2])
  424. ckpt = opt.resume
  425. else:
  426. assert os.path.isdir(opt.resume), opt.resume
  427. logdir = opt.resume.rstrip("/")
  428. ckpt = os.path.join(logdir, "checkpoints", "last.ckpt")
  429. opt.resume_from_checkpoint = ckpt
  430. base_configs = sorted(glob.glob(os.path.join(logdir, "configs/*.yaml")))
  431. opt.base = base_configs + opt.base
  432. _tmp = logdir.split("/")
  433. nowname = _tmp[-1]
  434. else:
  435. if opt.name:
  436. name = "_" + opt.name
  437. elif opt.base:
  438. cfg_fname = os.path.split(opt.base[0])[-1]
  439. cfg_name = os.path.splitext(cfg_fname)[0]
  440. name = "_" + cfg_name
  441. else:
  442. name = ""
  443. nowname = now + name + opt.postfix
  444. logdir = os.path.join(opt.logdir, nowname)
  445. ckptdir = os.path.join(logdir, "checkpoints")
  446. cfgdir = os.path.join(logdir, "configs")
  447. seed_everything(opt.seed)
  448. try:
  449. # init and save configs
  450. configs = [OmegaConf.load(cfg) for cfg in opt.base]
  451. cli = OmegaConf.from_dotlist(unknown)
  452. config = OmegaConf.merge(*configs, cli)
  453. lightning_config = config.pop("lightning", OmegaConf.create())
  454. # merge trainer cli with config
  455. trainer_config = lightning_config.get("trainer", OmegaConf.create())
  456. # default to ddp
  457. trainer_config["accelerator"] = "ddp"
  458. for k in nondefault_trainer_args(opt):
  459. trainer_config[k] = getattr(opt, k)
  460. if not "gpus" in trainer_config:
  461. del trainer_config["accelerator"]
  462. cpu = True
  463. else:
  464. gpuinfo = trainer_config["gpus"]
  465. print(f"Running on GPUs {gpuinfo}")
  466. cpu = False
  467. trainer_opt = argparse.Namespace(**trainer_config)
  468. lightning_config.trainer = trainer_config
  469. # model
  470. model = instantiate_from_config(config.model)
  471. # trainer and callbacks
  472. trainer_kwargs = dict()
  473. # default logger configs
  474. default_logger_cfgs = {
  475. "wandb": {
  476. "target": "pytorch_lightning.loggers.WandbLogger",
  477. "params": {
  478. "name": nowname,
  479. "save_dir": logdir,
  480. "offline": opt.debug,
  481. "id": nowname,
  482. }
  483. },
  484. "testtube": {
  485. "target": "pytorch_lightning.loggers.TestTubeLogger",
  486. "params": {
  487. "name": "testtube",
  488. "save_dir": logdir,
  489. }
  490. },
  491. }
  492. default_logger_cfg = default_logger_cfgs["testtube"]
  493. if "logger" in lightning_config:
  494. logger_cfg = lightning_config.logger
  495. else:
  496. logger_cfg = OmegaConf.create()
  497. logger_cfg = OmegaConf.merge(default_logger_cfg, logger_cfg)
  498. trainer_kwargs["logger"] = instantiate_from_config(logger_cfg)
  499. # modelcheckpoint - use TrainResult/EvalResult(checkpoint_on=metric) to
  500. # specify which metric is used to determine best models
  501. default_modelckpt_cfg = {
  502. "target": "pytorch_lightning.callbacks.ModelCheckpoint",
  503. "params": {
  504. "dirpath": ckptdir,
  505. "filename": "{epoch:06}",
  506. "verbose": True,
  507. "save_last": True,
  508. }
  509. }
  510. if hasattr(model, "monitor"):
  511. print(f"Monitoring {model.monitor} as checkpoint metric.")
  512. default_modelckpt_cfg["params"]["monitor"] = model.monitor
  513. default_modelckpt_cfg["params"]["save_top_k"] = 3
  514. if "modelcheckpoint" in lightning_config:
  515. modelckpt_cfg = lightning_config.modelcheckpoint
  516. else:
  517. modelckpt_cfg = OmegaConf.create()
  518. modelckpt_cfg = OmegaConf.merge(default_modelckpt_cfg, modelckpt_cfg)
  519. print(f"Merged modelckpt-cfg: \n{modelckpt_cfg}")
  520. if version.parse(pl.__version__) < version.parse('1.4.0'):
  521. trainer_kwargs["checkpoint_callback"] = instantiate_from_config(modelckpt_cfg)
  522. # add callback which sets up log directory
  523. default_callbacks_cfg = {
  524. "setup_callback": {
  525. "target": "main.SetupCallback",
  526. "params": {
  527. "resume": opt.resume,
  528. "now": now,
  529. "logdir": logdir,
  530. "ckptdir": ckptdir,
  531. "cfgdir": cfgdir,
  532. "config": config,
  533. "lightning_config": lightning_config,
  534. }
  535. },
  536. "image_logger": {
  537. "target": "main.ImageLogger",
  538. "params": {
  539. "batch_frequency": 750,
  540. "max_images": 4,
  541. "clamp": True
  542. }
  543. },
  544. "learning_rate_logger": {
  545. "target": "main.LearningRateMonitor",
  546. "params": {
  547. "logging_interval": "step",
  548. # "log_momentum": True
  549. }
  550. },
  551. "cuda_callback": {
  552. "target": "main.CUDACallback"
  553. },
  554. }
  555. if version.parse(pl.__version__) >= version.parse('1.4.0'):
  556. default_callbacks_cfg.update({'checkpoint_callback': modelckpt_cfg})
  557. if "callbacks" in lightning_config:
  558. callbacks_cfg = lightning_config.callbacks
  559. else:
  560. callbacks_cfg = OmegaConf.create()
  561. if 'metrics_over_trainsteps_checkpoint' in callbacks_cfg:
  562. print(
  563. 'Caution: Saving checkpoints every n train steps without deleting. This might require some free space.')
  564. default_metrics_over_trainsteps_ckpt_dict = {
  565. 'metrics_over_trainsteps_checkpoint':
  566. {"target": 'pytorch_lightning.callbacks.ModelCheckpoint',
  567. 'params': {
  568. "dirpath": os.path.join(ckptdir, 'trainstep_checkpoints'),
  569. "filename": "{epoch:06}-{step:09}",
  570. "verbose": True,
  571. 'save_top_k': -1,
  572. 'every_n_train_steps': 10000,
  573. 'save_weights_only': True
  574. }
  575. }
  576. }
  577. default_callbacks_cfg.update(default_metrics_over_trainsteps_ckpt_dict)
  578. callbacks_cfg = OmegaConf.merge(default_callbacks_cfg, callbacks_cfg)
  579. if 'ignore_keys_callback' in callbacks_cfg and hasattr(trainer_opt, 'resume_from_checkpoint'):
  580. callbacks_cfg.ignore_keys_callback.params['ckpt_path'] = trainer_opt.resume_from_checkpoint
  581. elif 'ignore_keys_callback' in callbacks_cfg:
  582. del callbacks_cfg['ignore_keys_callback']
  583. trainer_kwargs["callbacks"] = [instantiate_from_config(callbacks_cfg[k]) for k in callbacks_cfg]
  584. trainer = Trainer.from_argparse_args(trainer_opt, **trainer_kwargs)
  585. trainer.logdir = logdir ###
  586. # data
  587. data = instantiate_from_config(config.data)
  588. # NOTE according to https://pytorch-lightning.readthedocs.io/en/latest/datamodules.html
  589. # calling these ourselves should not be necessary but it is.
  590. # lightning still takes care of proper multiprocessing though
  591. data.prepare_data()
  592. data.setup()
  593. print("#### Data #####")
  594. for k in data.datasets:
  595. print(f"{k}, {data.datasets[k].__class__.__name__}, {len(data.datasets[k])}")
  596. # configure learning rate
  597. bs, base_lr = config.data.params.batch_size, config.model.base_learning_rate
  598. if not cpu:
  599. ngpu = len(lightning_config.trainer.gpus.strip(",").split(','))
  600. else:
  601. ngpu = 1
  602. if 'accumulate_grad_batches' in lightning_config.trainer:
  603. accumulate_grad_batches = lightning_config.trainer.accumulate_grad_batches
  604. else:
  605. accumulate_grad_batches = 1
  606. print(f"accumulate_grad_batches = {accumulate_grad_batches}")
  607. lightning_config.trainer.accumulate_grad_batches = accumulate_grad_batches
  608. if opt.scale_lr:
  609. model.learning_rate = accumulate_grad_batches * ngpu * bs * base_lr
  610. print(
  611. "Setting learning rate to {:.2e} = {} (accumulate_grad_batches) * {} (num_gpus) * {} (batchsize) * {:.2e} (base_lr)".format(
  612. model.learning_rate, accumulate_grad_batches, ngpu, bs, base_lr))
  613. else:
  614. model.learning_rate = base_lr
  615. print("++++ NOT USING LR SCALING ++++")
  616. print(f"Setting learning rate to {model.learning_rate:.2e}")
  617. # allow checkpointing via USR1
  618. def melk(*args, **kwargs):
  619. # run all checkpoint hooks
  620. if trainer.global_rank == 0:
  621. print("Summoning checkpoint.")
  622. ckpt_path = os.path.join(ckptdir, "last.ckpt")
  623. trainer.save_checkpoint(ckpt_path)
  624. def divein(*args, **kwargs):
  625. if trainer.global_rank == 0:
  626. import pudb;
  627. pudb.set_trace()
  628. import signal
  629. signal.signal(signal.SIGUSR1, melk)
  630. signal.signal(signal.SIGUSR2, divein)
  631. # run
  632. if opt.train:
  633. try:
  634. trainer.fit(model, data)
  635. except Exception:
  636. melk()
  637. raise
  638. if not opt.no_test and not trainer.interrupted:
  639. trainer.test(model, data)
  640. except Exception:
  641. if opt.debug and trainer.global_rank == 0:
  642. try:
  643. import pudb as debugger
  644. except ImportError:
  645. import pdb as debugger
  646. debugger.post_mortem()
  647. raise
  648. finally:
  649. # move newly created debug project to debug_runs
  650. if opt.debug and not opt.resume and trainer.global_rank == 0:
  651. dst, name = os.path.split(logdir)
  652. dst = os.path.join(dst, "debug_runs", name)
  653. os.makedirs(os.path.split(dst)[0], exist_ok=True)
  654. os.rename(logdir, dst)
  655. if trainer.global_rank == 0:
  656. print(trainer.profiler.summary())