CoolFace
Apppublic

facebook/StyleNeRF

sourceHugging Faceupdated 4y agoView on Hugging Face
34likes
run_train.py399 linesDownload Raw Back to root
1# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved2 3 4from math import dist5import sys6import os7import click8import re9import json10import glob11import tempfile12import torch13import dnnlib14import hydra15 16from datetime import date17from training import training_loop18from metrics import metric_main19from torch_utils import training_stats, custom_ops, distributed_utils20from torch_utils.distributed_utils import get_init_file, get_shared_folder21from omegaconf import DictConfig, OmegaConf22 23#----------------------------------------------------------------------------24 25class UserError(Exception):26    pass27 28#----------------------------------------------------------------------------29 30def setup_training_loop_kwargs(cfg):31    args = OmegaConf.create({})32 33    # ------------------------------------------34    # General options: gpus, snap, metrics, seed35    # ------------------------------------------36    args.rank       = 037    args.gpu        = 038    args.num_gpus   = torch.cuda.device_count() if cfg.gpus is None else cfg.gpus39    args.nodes      = cfg.nodes if cfg.nodes is not None else 140    args.world_size = 141    42    args.dist_url   = 'env://'43    args.launcher   = cfg.launcher44    args.partition  = cfg.partition45    args.comment    = cfg.comment46    args.timeout    = 4320 if cfg.timeout is None else cfg.timeout47    args.job_dir    = ''48 49    if cfg.snap is None:50        cfg.snap = 5051    assert isinstance(cfg.snap, int)52    if cfg.snap < 1:53        raise UserError('snap must be at least 1')54    args.image_snapshot_ticks = cfg.imgsnap55    args.network_snapshot_ticks = cfg.snap56    if hasattr(cfg, 'ucp'):57        args.update_cam_prior_ticks = cfg.ucp58 59    if cfg.metrics is None:60        cfg.metrics = ['fid50k_full']61    cfg.metrics = list(cfg.metrics)62    if not all(metric_main.is_valid_metric(metric) for metric in cfg.metrics):63        raise UserError('\n'.join(['metrics can only contain the following values:'] + metric_main.list_valid_metrics()))64    args.metrics = cfg.metrics65 66    if cfg.seed is None:67        cfg.seed = 068    assert isinstance(cfg.seed, int)69    args.random_seed = cfg.seed70 71    # -----------------------------------72    # Dataset: data, cond, subset, mirror73    # ----------------------------------- 74 75    assert cfg.data is not None76    assert isinstance(cfg.data, str)77    args.update({"training_set_kwargs": dict(class_name='training.dataset.ImageFolderDataset', path=cfg.data, resolution=cfg.resolution, use_labels=True, max_size=None, xflip=False)})78    args.update({"data_loader_kwargs": dict(pin_memory=True, num_workers=3, prefetch_factor=2)})79    args.generation_with_image = getattr(cfg, 'generate_with_image', False)80    try:81        training_set = dnnlib.util.construct_class_by_name(**args.training_set_kwargs) # subclass of training.dataset.Dataset82        args.training_set_kwargs.resolution = training_set.resolution                  # be explicit about resolution83        args.training_set_kwargs.use_labels = training_set.has_labels                  # be explicit about labels84        args.training_set_kwargs.max_size = len(training_set)                          # be explicit about dataset size85        desc = training_set.name86        del training_set # conserve memory87    except IOError as err:88        raise UserError(f'data: {err}')89 90    if cfg.cond is None:91        cfg.cond = False92    assert isinstance(cfg.cond, bool)93    if cfg.cond:94        if not args.training_set_kwargs.use_labels:95            raise UserError('cond=True requires labels specified in dataset.json')96        desc += '-cond'97    else:98        args.training_set_kwargs.use_labels = False99 100    if cfg.subset is not None:101        assert isinstance(cfg.subset, int)102        if not 1 <= cfg.subset <= args.training_set_kwargs.max_size:103            raise UserError(f'subset must be between 1 and {args.training_set_kwargs.max_size}')104        desc += f'-subset{cfg.subset}'105        if cfg.subset < args.training_set_kwargs.max_size:106            args.training_set_kwargs.max_size = cfg.subset107            args.training_set_kwargs.random_seed = args.random_seed108 109    if cfg.mirror is None:110        cfg.mirror = False111    assert isinstance(cfg.mirror, bool)112    if cfg.mirror:113        desc += '-mirror'114        args.training_set_kwargs.xflip = True115 116    # ------------------------------------117    # Base config: cfg, model, gamma, kimg, batch118    # ------------------------------------119    if cfg.auto:120        cfg.spec.name = 'auto'121    desc += f'-{cfg.spec.name}'122    desc += f'-{cfg.model.name}'123    if cfg.spec.name == 'auto':124        res = args.training_set_kwargs.resolution125        cfg.spec.fmaps = 1 if res >= 512 else 0.5126        cfg.spec.lrate = 0.002 if res >= 1024 else 0.0025127        cfg.spec.gamma = 0.0002 * (res ** 2) / cfg.spec.mb # heuristic formula128        cfg.spec.ema = cfg.spec.mb * 10 / 32129    130    if getattr(cfg.spec, 'lrate_disc', None) is None:131        cfg.spec.lrate_disc = cfg.spec.lrate   # use the same learning rate for discriminator132 133    # model (generator, discriminator)134    args.update({"G_kwargs": dict(**cfg.model.G_kwargs)})135    args.update({"D_kwargs": dict(**cfg.model.D_kwargs)})136    args.update({"G_opt_kwargs": dict(class_name='torch.optim.Adam', lr=cfg.spec.lrate, betas=[0,0.99], eps=1e-8)})137    args.update({"D_opt_kwargs": dict(class_name='torch.optim.Adam', lr=cfg.spec.lrate_disc, betas=[0,0.99], eps=1e-8)})138    args.update({"loss_kwargs": dict(class_name='training.loss.StyleGAN2Loss', r1_gamma=cfg.spec.gamma, **cfg.model.loss_kwargs)})139    140    if cfg.spec.name == 'cifar':141        args.loss_kwargs.pl_weight = 0 # disable path length regularization142        args.loss_kwargs.style_mixing_prob = 0 # disable style mixing143        args.D_kwargs.architecture = 'orig' # disable residual skip connections144 145    # kimg data config146    args.spec = cfg.spec  # just keep the dict.147    args.total_kimg = cfg.spec.kimg148    args.batch_size = cfg.spec.mb149    args.batch_gpu = cfg.spec.mbstd150    args.ema_kimg = cfg.spec.ema151    args.ema_rampup = cfg.spec.ramp152    153    # ---------------------------------------------------154    # Discriminator augmentation: aug, p, target, augpipe155    # ---------------------------------------------------156    if cfg.aug is None:157        cfg.aug = 'ada'158    else:159        assert isinstance(cfg.aug, str)160        desc += f'-{cfg.aug}'161 162    if cfg.aug == 'ada':163        args.ada_target = 0.6164    elif cfg.aug == 'noaug':165        pass166    elif cfg.aug == 'fixed':167        if cfg.p is None:168            raise UserError(f'--aug={cfg.aug} requires specifying --p')169    else:170        raise UserError(f'--aug={cfg.aug} not supported')171 172    if cfg.p is not None:173        assert isinstance(cfg.p, float)174        if cfg.aug != 'fixed':175            raise UserError('--p can only be specified with --aug=fixed')176        if not 0 <= cfg.p <= 1:177            raise UserError('--p must be between 0 and 1')178        desc += f'-p{cfg.p:g}'179        args.augment_p = cfg.p180 181    if cfg.target is not None:182        assert isinstance(cfg.target, float)183        if cfg.aug != 'ada':184            raise UserError('--target can only be specified with --aug=ada')185        if not 0 <= cfg.target <= 1:186            raise UserError('--target must be between 0 and 1')187        desc += f'-target{cfg.target:g}'188        args.ada_target = cfg.target189 190    assert cfg.augpipe is None or isinstance(cfg.augpipe, str)191    if cfg.augpipe is None:192        cfg.augpipe = 'bgc'193    else:194        if cfg.aug == 'noaug':195            raise UserError('--augpipe cannot be specified with --aug=noaug')196        desc += f'-{cfg.augpipe}'197 198    augpipe_specs = {199        'blit':   dict(xflip=1, rotate90=1, xint=1),200        'geom':   dict(scale=1, rotate=1, aniso=1, xfrac=1),201        'color':  dict(brightness=1, contrast=1, lumaflip=1, hue=1, saturation=1),202        'filter': dict(imgfilter=1),203        'noise':  dict(noise=1),204        'cutout': dict(cutout=1),205        'bgc0':   dict(xint=1, scale=1, aniso=1, xfrac=1, brightness=1, contrast=1, lumaflip=1, hue=1, saturation=1),206        'bg':     dict(xflip=1, rotate90=1, xint=1, scale=1, rotate=1, aniso=1, xfrac=1),207        'bgc':    dict(xflip=1, rotate90=1, xint=1, scale=1, rotate=1, aniso=1, xfrac=1, brightness=1, contrast=1, lumaflip=1, hue=1, saturation=1),208        'bgcf':   dict(xflip=1, rotate90=1, xint=1, scale=1, rotate=1, aniso=1, xfrac=1, brightness=1, contrast=1, lumaflip=1, hue=1, saturation=1, imgfilter=1),209        'bgcfn':  dict(xflip=1, rotate90=1, xint=1, scale=1, rotate=1, aniso=1, xfrac=1, brightness=1, contrast=1, lumaflip=1, hue=1, saturation=1, imgfilter=1, noise=1),210        'bgcfnc': dict(xflip=1, rotate90=1, xint=1, scale=1, rotate=1, aniso=1, xfrac=1, brightness=1, contrast=1, lumaflip=1, hue=1, saturation=1, imgfilter=1, noise=1, cutout=1),211    }212    assert cfg.augpipe in augpipe_specs213    if cfg.aug != 'noaug':214        args.update({"augment_kwargs": dict(class_name='training.augment.AugmentPipe', **augpipe_specs[cfg.augpipe])})215 216    # ----------------------------------217    # Transfer learning: resume, freezed218    # ----------------------------------219 220    resume_specs = {221        'ffhq256':     'https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada-pytorch/pretrained/transfer-learning-source-nets/ffhq-res256-mirror-paper256-noaug.pkl',222        'ffhq512':     'https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada-pytorch/pretrained/transfer-learning-source-nets/ffhq-res512-mirror-stylegan2-noaug.pkl',223        'ffhq1024':    'https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada-pytorch/pretrained/transfer-learning-source-nets/ffhq-res1024-mirror-stylegan2-noaug.pkl',224        'celebahq256': 'https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada-pytorch/pretrained/transfer-learning-source-nets/celebahq-res256-mirror-paper256-kimg100000-ada-target0.5.pkl',225        'lsundog256':  'https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada-pytorch/pretrained/transfer-learning-source-nets/lsundog-res256-paper256-kimg100000-noaug.pkl',226    }227 228    assert cfg.resume is None or isinstance(cfg.resume, str)229    if cfg.resume is None:230        cfg.resume = 'noresume'231    elif cfg.resume == 'noresume':232        desc += '-noresume'233    elif cfg.resume in resume_specs:234        desc += f'-resume{cfg.resume}'235        args.resume_pkl = resume_specs[cfg.resume] # predefined url236    else:237        desc += '-resumecustom'238        args.resume_pkl = cfg.resume # custom path or url239 240    if cfg.resume != 'noresume':241        args.ada_kimg = 100 # make ADA react faster at the beginning242        args.ema_rampup = None # disable EMA rampup243 244    if cfg.freezed is not None:245        assert isinstance(cfg.freezed, int)246        if not cfg.freezed >= 0:247            raise UserError('--freezed must be non-negative')248        desc += f'-freezed{cfg.freezed:d}'249        args.D_kwargs.block_kwargs.freeze_layers = cfg.freezed250 251    # -------------------------------------------------252    # Performance options: fp32, nhwc, nobench, workers253    # -------------------------------------------------254    args.num_fp16_res = cfg.num_fp16_res255    if cfg.fp32 is None:256        cfg.fp32 = False257    assert isinstance(cfg.fp32, bool)258    if cfg.fp32:259        args.G_kwargs.synthesis_kwargs.num_fp16_res = args.D_kwargs.num_fp16_res = 0260        args.G_kwargs.synthesis_kwargs.conv_clamp = args.D_kwargs.conv_clamp = None261 262    if cfg.nhwc is None:263        cfg.nhwc = False264    assert isinstance(cfg.nhwc, bool)265    if cfg.nhwc:266        args.G_kwargs.synthesis_kwargs.fp16_channels_last = args.D_kwargs.block_kwargs.fp16_channels_last = True267 268    if cfg.nobench is None:269        cfg.nobench = False270    assert isinstance(cfg.nobench, bool)271    if cfg.nobench:272        args.cudnn_benchmark = False273 274    if cfg.allow_tf32 is None:275        cfg.allow_tf32 = False276    assert isinstance(cfg.allow_tf32, bool)277    args.allow_tf32 = cfg.allow_tf32278 279    if cfg.workers is not None:280        assert isinstance(cfg.workers, int)281        if not cfg.workers >= 1:282            raise UserError('--workers must be at least 1')283        args.data_loader_kwargs.num_workers = cfg.workers284 285    args.debug = cfg.debug286    if getattr(cfg, "prefix", None) is not None:287        desc = cfg.prefix + '-' + desc288    return desc, args289 290#----------------------------------------------------------------------------291 292def subprocess_fn(rank, args):293    if not args.debug:294        dnnlib.util.Logger(file_name=os.path.join(args.run_dir, 'log.txt'), file_mode='a', should_flush=True)295 296    # Init torch.distributed.297    distributed_utils.init_distributed_mode(rank, args)298    if args.rank != 0:299        custom_ops.verbosity = 'none'300    301    # Execute training loop.302    training_loop.training_loop(**args)303    304#----------------------------------------------------------------------------305 306class CommaSeparatedList(click.ParamType):307    name = 'list'308 309    def convert(self, value, param, ctx):310        _ = param, ctx311        if value is None or value.lower() == 'none' or value == '':312            return []313        return value.split(',')314 315 316@hydra.main(config_path="conf", config_name="config")317def main(cfg: DictConfig):318    319    outdir = cfg.outdir320 321    # Setup training options322    run_desc, args = setup_training_loop_kwargs(cfg)323    324    # Pick output directory.325    prev_run_dirs = []326    if os.path.isdir(outdir):327        prev_run_dirs = [x for x in os.listdir(outdir) if os.path.isdir(os.path.join(outdir, x))]328    329    if cfg.resume_run is None:330        prev_run_ids = [re.match(r'^\d+', x) for x in prev_run_dirs]331        prev_run_ids = [int(x.group()) for x in prev_run_ids if x is not None]332        cur_run_id = max(prev_run_ids, default=-1) + 1333    else:334        cur_run_id = cfg.resume_run335        336    args.run_dir = os.path.join(outdir, f'{cur_run_id:05d}-{run_desc}')337    print(outdir, args.run_dir)338 339    if cfg.resume_run is not None:340        pkls = sorted(glob.glob(args.run_dir + '/network*.pkl'))341        if len(pkls) > 0:342            args.resume_pkl = pkls[-1]343            args.resume_start = int(args.resume_pkl.split('-')[-1][:-4]) * 1000344        else:345            args.resume_start = 0346 347    # Print options.348    print()349    print('Training options:')350    print(OmegaConf.to_yaml(args))351    print()352    print(f'Output directory:   {args.run_dir}')353    print(f'Training data:      {args.training_set_kwargs.path}')354    print(f'Training duration:  {args.total_kimg} kimg')355    print(f'Number of images:   {args.training_set_kwargs.max_size}')356    print(f'Image resolution:   {args.training_set_kwargs.resolution}')357    print(f'Conditional model:  {args.training_set_kwargs.use_labels}')358    print(f'Dataset x-flips:    {args.training_set_kwargs.xflip}')359    print()360 361    # Dry run?362    if cfg.dry_run:363        print('Dry run; exiting.')364        return365 366    # Create output directory.367    print('Creating output directory...')368    if not os.path.exists(args.run_dir):369        os.makedirs(args.run_dir)370        with open(os.path.join(args.run_dir, 'training_options.yaml'), 'wt') as fp:371            OmegaConf.save(config=args, f=fp.name)372 373    # Launch processes.    374    print('Launching processes...')375    if (args.launcher == 'spawn') and (args.num_gpus > 1):376        args.dist_url = distributed_utils.get_init_file().as_uri()377        torch.multiprocessing.set_start_method('spawn')378        torch.multiprocessing.spawn(fn=subprocess_fn, args=(args,), nprocs=args.num_gpus)379    else:380        subprocess_fn(rank=0, args=args)381 382#----------------------------------------------------------------------------383 384if __name__ == "__main__":385    if os.getenv('SLURM_ARGS') is not None:386        # deparcated launcher for slurm jobs.387        slurm_arg = eval(os.getenv('SLURM_ARGS'))388        all_args = sys.argv[1:]389        print(slurm_arg)390        print(all_args)391 392        from launcher import launch393        launch(slurm_arg, all_args)394    395    else:396        main() # pylint: disable=no-value-for-parameter397 398#----------------------------------------------------------------------------399