|
|
|
@ -21,6 +21,7 @@ import time
|
|
|
|
|
from collections import OrderedDict
|
|
|
|
|
from contextlib import suppress
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from functools import partial
|
|
|
|
|
|
|
|
|
|
import torch
|
|
|
|
|
import torch.nn as nn
|
|
|
|
@ -66,7 +67,6 @@ except ImportError as e:
|
|
|
|
|
has_functorch = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
torch.backends.cudnn.benchmark = True
|
|
|
|
|
_logger = logging.getLogger('train')
|
|
|
|
|
|
|
|
|
|
# The first arg parser parses out only the --config argument, this argument is used to
|
|
|
|
@ -349,33 +349,27 @@ def main():
|
|
|
|
|
utils.setup_default_logging()
|
|
|
|
|
args, args_text = _parse_args()
|
|
|
|
|
|
|
|
|
|
if torch.cuda.is_available():
|
|
|
|
|
torch.backends.cuda.matmul.allow_tf32 = True
|
|
|
|
|
torch.backends.cudnn.benchmark = True
|
|
|
|
|
|
|
|
|
|
args.prefetcher = not args.no_prefetcher
|
|
|
|
|
args.distributed = False
|
|
|
|
|
if 'WORLD_SIZE' in os.environ:
|
|
|
|
|
args.distributed = int(os.environ['WORLD_SIZE']) > 1
|
|
|
|
|
args.device = 'cuda:0'
|
|
|
|
|
args.world_size = 1
|
|
|
|
|
args.rank = 0 # global rank
|
|
|
|
|
device = utils.init_distributed_device(args)
|
|
|
|
|
if args.distributed:
|
|
|
|
|
if 'LOCAL_RANK' in os.environ:
|
|
|
|
|
args.local_rank = int(os.getenv('LOCAL_RANK'))
|
|
|
|
|
args.device = 'cuda:%d' % args.local_rank
|
|
|
|
|
torch.cuda.set_device(args.local_rank)
|
|
|
|
|
torch.distributed.init_process_group(backend='nccl', init_method='env://')
|
|
|
|
|
args.world_size = torch.distributed.get_world_size()
|
|
|
|
|
args.rank = torch.distributed.get_rank()
|
|
|
|
|
_logger.info('Training in distributed mode with multiple processes, 1 GPU per process. Process %d, total %d.'
|
|
|
|
|
% (args.rank, args.world_size))
|
|
|
|
|
_logger.info(
|
|
|
|
|
'Training in distributed mode with multiple processes, 1 device per process.'
|
|
|
|
|
f'Process {args.rank}, total {args.world_size}, device {args.device}.')
|
|
|
|
|
else:
|
|
|
|
|
_logger.info('Training with a single process on 1 GPUs.')
|
|
|
|
|
_logger.info(f'Training with a single process on 1 device ({args.device}).')
|
|
|
|
|
assert args.rank >= 0
|
|
|
|
|
|
|
|
|
|
if args.rank == 0 and args.log_wandb:
|
|
|
|
|
if utils.is_primary(args) and args.log_wandb:
|
|
|
|
|
if has_wandb:
|
|
|
|
|
wandb.init(project=args.experiment, config=args)
|
|
|
|
|
else:
|
|
|
|
|
_logger.warning("You've requested to log metrics to wandb but package not found. "
|
|
|
|
|
"Metrics not being logged to wandb, try `pip install wandb`")
|
|
|
|
|
_logger.warning(
|
|
|
|
|
"You've requested to log metrics to wandb but package not found. "
|
|
|
|
|
"Metrics not being logged to wandb, try `pip install wandb`")
|
|
|
|
|
|
|
|
|
|
# resolve AMP arguments based on PyTorch / Apex availability
|
|
|
|
|
use_amp = None
|
|
|
|
@ -405,14 +399,14 @@ def main():
|
|
|
|
|
pretrained=args.pretrained,
|
|
|
|
|
num_classes=args.num_classes,
|
|
|
|
|
drop_rate=args.drop,
|
|
|
|
|
drop_connect_rate=args.drop_connect, # DEPRECATED, use drop_path
|
|
|
|
|
drop_path_rate=args.drop_path,
|
|
|
|
|
drop_block_rate=args.drop_block,
|
|
|
|
|
global_pool=args.gp,
|
|
|
|
|
bn_momentum=args.bn_momentum,
|
|
|
|
|
bn_eps=args.bn_eps,
|
|
|
|
|
scriptable=args.torchscript,
|
|
|
|
|
checkpoint_path=args.initial_checkpoint)
|
|
|
|
|
checkpoint_path=args.initial_checkpoint,
|
|
|
|
|
)
|
|
|
|
|
if args.num_classes is None:
|
|
|
|
|
assert hasattr(model, 'num_classes'), 'Model must have `num_classes` attr if not set on cmd line/config.'
|
|
|
|
|
args.num_classes = model.num_classes # FIXME handle model default vs config num_classes more elegantly
|
|
|
|
@ -420,11 +414,11 @@ def main():
|
|
|
|
|
if args.grad_checkpointing:
|
|
|
|
|
model.set_grad_checkpointing(enable=True)
|
|
|
|
|
|
|
|
|
|
if args.local_rank == 0:
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
_logger.info(
|
|
|
|
|
f'Model {safe_model_name(args.model)} created, param count:{sum([m.numel() for m in model.parameters()])}')
|
|
|
|
|
|
|
|
|
|
data_config = resolve_data_config(vars(args), model=model, verbose=args.local_rank == 0)
|
|
|
|
|
data_config = resolve_data_config(vars(args), model=model, verbose=utils.is_primary(args))
|
|
|
|
|
|
|
|
|
|
# setup augmentation batch splits for contrastive loss or split bn
|
|
|
|
|
num_aug_splits = 0
|
|
|
|
@ -438,9 +432,9 @@ def main():
|
|
|
|
|
model = convert_splitbn_model(model, max(num_aug_splits, 2))
|
|
|
|
|
|
|
|
|
|
# move model to GPU, enable channels last layout if set
|
|
|
|
|
model.cuda()
|
|
|
|
|
model.to(device=device)
|
|
|
|
|
if args.channels_last:
|
|
|
|
|
model = model.to(memory_format=torch.channels_last)
|
|
|
|
|
model.to(memory_format=torch.channels_last)
|
|
|
|
|
|
|
|
|
|
# setup synchronized BatchNorm for distributed training
|
|
|
|
|
if args.distributed and args.sync_bn:
|
|
|
|
@ -452,7 +446,7 @@ def main():
|
|
|
|
|
model = convert_syncbn_model(model)
|
|
|
|
|
else:
|
|
|
|
|
model = convert_sync_batchnorm(model)
|
|
|
|
|
if args.local_rank == 0:
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
_logger.info(
|
|
|
|
|
'Converted model to use Synchronized BatchNorm. WARNING: You may have issues if using '
|
|
|
|
|
'zero initialized BN layers (enabled by default for ResNets) while sync-bn enabled.')
|
|
|
|
@ -461,6 +455,7 @@ def main():
|
|
|
|
|
assert not use_amp == 'apex', 'Cannot use APEX AMP with torchscripted model'
|
|
|
|
|
assert not args.sync_bn, 'Cannot use SyncBatchNorm with torchscripted model'
|
|
|
|
|
model = torch.jit.script(model)
|
|
|
|
|
|
|
|
|
|
if args.aot_autograd:
|
|
|
|
|
assert has_functorch, "functorch is needed for --aot-autograd"
|
|
|
|
|
model = memory_efficient_fusion(model)
|
|
|
|
@ -471,28 +466,31 @@ def main():
|
|
|
|
|
amp_autocast = suppress # do nothing
|
|
|
|
|
loss_scaler = None
|
|
|
|
|
if use_amp == 'apex':
|
|
|
|
|
assert device.type == 'cuda'
|
|
|
|
|
model, optimizer = amp.initialize(model, optimizer, opt_level='O1')
|
|
|
|
|
loss_scaler = ApexScaler()
|
|
|
|
|
if args.local_rank == 0:
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
_logger.info('Using NVIDIA APEX AMP. Training in mixed precision.')
|
|
|
|
|
elif use_amp == 'native':
|
|
|
|
|
amp_autocast = torch.cuda.amp.autocast
|
|
|
|
|
loss_scaler = NativeScaler()
|
|
|
|
|
if args.local_rank == 0:
|
|
|
|
|
amp_autocast = partial(torch.autocast, device_type=device.type)
|
|
|
|
|
if device.type == 'cuda':
|
|
|
|
|
loss_scaler = NativeScaler()
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
_logger.info('Using native Torch AMP. Training in mixed precision.')
|
|
|
|
|
else:
|
|
|
|
|
if args.local_rank == 0:
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
_logger.info('AMP not enabled. Training in float32.')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# optionally resume from a checkpoint
|
|
|
|
|
resume_epoch = None
|
|
|
|
|
if args.resume:
|
|
|
|
|
resume_epoch = resume_checkpoint(
|
|
|
|
|
model, args.resume,
|
|
|
|
|
model,
|
|
|
|
|
args.resume,
|
|
|
|
|
optimizer=None if args.no_resume_opt else optimizer,
|
|
|
|
|
loss_scaler=None if args.no_resume_opt else loss_scaler,
|
|
|
|
|
log_info=args.local_rank == 0)
|
|
|
|
|
log_info=utils.is_primary(args),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# setup exponential moving average of model weights, SWA could be used here too
|
|
|
|
|
model_ema = None
|
|
|
|
@ -507,13 +505,13 @@ def main():
|
|
|
|
|
if args.distributed:
|
|
|
|
|
if has_apex and use_amp == 'apex':
|
|
|
|
|
# Apex DDP preferred unless native amp is activated
|
|
|
|
|
if args.local_rank == 0:
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
_logger.info("Using NVIDIA APEX DistributedDataParallel.")
|
|
|
|
|
model = ApexDDP(model, delay_allreduce=True)
|
|
|
|
|
else:
|
|
|
|
|
if args.local_rank == 0:
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
_logger.info("Using native Torch DistributedDataParallel.")
|
|
|
|
|
model = NativeDDP(model, device_ids=[args.local_rank], broadcast_buffers=not args.no_ddp_bb)
|
|
|
|
|
model = NativeDDP(model, device_ids=[device], broadcast_buffers=not args.no_ddp_bb)
|
|
|
|
|
# NOTE: EMA model does not need to be wrapped by DDP
|
|
|
|
|
|
|
|
|
|
# setup learning rate schedule and starting epoch
|
|
|
|
@ -527,21 +525,30 @@ def main():
|
|
|
|
|
if lr_scheduler is not None and start_epoch > 0:
|
|
|
|
|
lr_scheduler.step(start_epoch)
|
|
|
|
|
|
|
|
|
|
if args.local_rank == 0:
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
_logger.info('Scheduled epochs: {}'.format(num_epochs))
|
|
|
|
|
|
|
|
|
|
# create the train and eval datasets
|
|
|
|
|
dataset_train = create_dataset(
|
|
|
|
|
args.dataset, root=args.data_dir, split=args.train_split, is_training=True,
|
|
|
|
|
args.dataset,
|
|
|
|
|
root=args.data_dir,
|
|
|
|
|
split=args.train_split,
|
|
|
|
|
is_training=True,
|
|
|
|
|
class_map=args.class_map,
|
|
|
|
|
download=args.dataset_download,
|
|
|
|
|
batch_size=args.batch_size,
|
|
|
|
|
repeats=args.epoch_repeats)
|
|
|
|
|
repeats=args.epoch_repeats
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
dataset_eval = create_dataset(
|
|
|
|
|
args.dataset, root=args.data_dir, split=args.val_split, is_training=False,
|
|
|
|
|
args.dataset,
|
|
|
|
|
root=args.data_dir,
|
|
|
|
|
split=args.val_split,
|
|
|
|
|
is_training=False,
|
|
|
|
|
class_map=args.class_map,
|
|
|
|
|
download=args.dataset_download,
|
|
|
|
|
batch_size=args.batch_size)
|
|
|
|
|
batch_size=args.batch_size
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# setup mixup / cutmix
|
|
|
|
|
collate_fn = None
|
|
|
|
@ -549,9 +556,15 @@ def main():
|
|
|
|
|
mixup_active = args.mixup > 0 or args.cutmix > 0. or args.cutmix_minmax is not None
|
|
|
|
|
if mixup_active:
|
|
|
|
|
mixup_args = dict(
|
|
|
|
|
mixup_alpha=args.mixup, cutmix_alpha=args.cutmix, cutmix_minmax=args.cutmix_minmax,
|
|
|
|
|
prob=args.mixup_prob, switch_prob=args.mixup_switch_prob, mode=args.mixup_mode,
|
|
|
|
|
label_smoothing=args.smoothing, num_classes=args.num_classes)
|
|
|
|
|
mixup_alpha=args.mixup,
|
|
|
|
|
cutmix_alpha=args.cutmix,
|
|
|
|
|
cutmix_minmax=args.cutmix_minmax,
|
|
|
|
|
prob=args.mixup_prob,
|
|
|
|
|
switch_prob=args.mixup_switch_prob,
|
|
|
|
|
mode=args.mixup_mode,
|
|
|
|
|
label_smoothing=args.smoothing,
|
|
|
|
|
num_classes=args.num_classes
|
|
|
|
|
)
|
|
|
|
|
if args.prefetcher:
|
|
|
|
|
assert not num_aug_splits # collate conflict (need to support deinterleaving in collate mixup)
|
|
|
|
|
collate_fn = FastCollateMixup(**mixup_args)
|
|
|
|
@ -592,6 +605,7 @@ def main():
|
|
|
|
|
distributed=args.distributed,
|
|
|
|
|
collate_fn=collate_fn,
|
|
|
|
|
pin_memory=args.pin_mem,
|
|
|
|
|
device=device,
|
|
|
|
|
use_multi_epochs_loader=args.use_multi_epochs_loader,
|
|
|
|
|
worker_seeding=args.worker_seeding,
|
|
|
|
|
)
|
|
|
|
@ -609,6 +623,7 @@ def main():
|
|
|
|
|
distributed=args.distributed,
|
|
|
|
|
crop_pct=data_config['crop_pct'],
|
|
|
|
|
pin_memory=args.pin_mem,
|
|
|
|
|
device=device,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# setup loss function
|
|
|
|
@ -628,8 +643,8 @@ def main():
|
|
|
|
|
train_loss_fn = LabelSmoothingCrossEntropy(smoothing=args.smoothing)
|
|
|
|
|
else:
|
|
|
|
|
train_loss_fn = nn.CrossEntropyLoss()
|
|
|
|
|
train_loss_fn = train_loss_fn.cuda()
|
|
|
|
|
validate_loss_fn = nn.CrossEntropyLoss().cuda()
|
|
|
|
|
train_loss_fn = train_loss_fn.to(device=device)
|
|
|
|
|
validate_loss_fn = nn.CrossEntropyLoss().to(device=device)
|
|
|
|
|
|
|
|
|
|
# setup checkpoint saver and eval metric tracking
|
|
|
|
|
eval_metric = args.eval_metric
|
|
|
|
@ -637,7 +652,7 @@ def main():
|
|
|
|
|
best_epoch = None
|
|
|
|
|
saver = None
|
|
|
|
|
output_dir = None
|
|
|
|
|
if args.rank == 0:
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
if args.experiment:
|
|
|
|
|
exp_name = args.experiment
|
|
|
|
|
else:
|
|
|
|
@ -649,8 +664,16 @@ def main():
|
|
|
|
|
output_dir = utils.get_outdir(args.output if args.output else './output/train', exp_name)
|
|
|
|
|
decreasing = True if eval_metric == 'loss' else False
|
|
|
|
|
saver = utils.CheckpointSaver(
|
|
|
|
|
model=model, optimizer=optimizer, args=args, model_ema=model_ema, amp_scaler=loss_scaler,
|
|
|
|
|
checkpoint_dir=output_dir, recovery_dir=output_dir, decreasing=decreasing, max_history=args.checkpoint_hist)
|
|
|
|
|
model=model,
|
|
|
|
|
optimizer=optimizer,
|
|
|
|
|
args=args,
|
|
|
|
|
model_ema=model_ema,
|
|
|
|
|
amp_scaler=loss_scaler,
|
|
|
|
|
checkpoint_dir=output_dir,
|
|
|
|
|
recovery_dir=output_dir,
|
|
|
|
|
decreasing=decreasing,
|
|
|
|
|
max_history=args.checkpoint_hist
|
|
|
|
|
)
|
|
|
|
|
with open(os.path.join(output_dir, 'args.yaml'), 'w') as f:
|
|
|
|
|
f.write(args_text)
|
|
|
|
|
|
|
|
|
@ -660,22 +683,46 @@ def main():
|
|
|
|
|
loader_train.sampler.set_epoch(epoch)
|
|
|
|
|
|
|
|
|
|
train_metrics = train_one_epoch(
|
|
|
|
|
epoch, model, loader_train, optimizer, train_loss_fn, args,
|
|
|
|
|
lr_scheduler=lr_scheduler, saver=saver, output_dir=output_dir,
|
|
|
|
|
amp_autocast=amp_autocast, loss_scaler=loss_scaler, model_ema=model_ema, mixup_fn=mixup_fn)
|
|
|
|
|
epoch,
|
|
|
|
|
model,
|
|
|
|
|
loader_train,
|
|
|
|
|
optimizer,
|
|
|
|
|
train_loss_fn,
|
|
|
|
|
args,
|
|
|
|
|
lr_scheduler=lr_scheduler,
|
|
|
|
|
saver=saver,
|
|
|
|
|
output_dir=output_dir,
|
|
|
|
|
amp_autocast=amp_autocast,
|
|
|
|
|
loss_scaler=loss_scaler,
|
|
|
|
|
model_ema=model_ema,
|
|
|
|
|
mixup_fn=mixup_fn,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if args.distributed and args.dist_bn in ('broadcast', 'reduce'):
|
|
|
|
|
if args.local_rank == 0:
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
_logger.info("Distributing BatchNorm running means and vars")
|
|
|
|
|
utils.distribute_bn(model, args.world_size, args.dist_bn == 'reduce')
|
|
|
|
|
|
|
|
|
|
eval_metrics = validate(model, loader_eval, validate_loss_fn, args, amp_autocast=amp_autocast)
|
|
|
|
|
eval_metrics = validate(
|
|
|
|
|
model,
|
|
|
|
|
loader_eval,
|
|
|
|
|
validate_loss_fn,
|
|
|
|
|
args,
|
|
|
|
|
amp_autocast=amp_autocast,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if model_ema is not None and not args.model_ema_force_cpu:
|
|
|
|
|
if args.distributed and args.dist_bn in ('broadcast', 'reduce'):
|
|
|
|
|
utils.distribute_bn(model_ema, args.world_size, args.dist_bn == 'reduce')
|
|
|
|
|
|
|
|
|
|
ema_eval_metrics = validate(
|
|
|
|
|
model_ema.module, loader_eval, validate_loss_fn, args, amp_autocast=amp_autocast, log_suffix=' (EMA)')
|
|
|
|
|
model_ema.module,
|
|
|
|
|
loader_eval,
|
|
|
|
|
validate_loss_fn,
|
|
|
|
|
args,
|
|
|
|
|
amp_autocast=amp_autocast,
|
|
|
|
|
log_suffix=' (EMA)',
|
|
|
|
|
)
|
|
|
|
|
eval_metrics = ema_eval_metrics
|
|
|
|
|
|
|
|
|
|
if lr_scheduler is not None:
|
|
|
|
@ -684,8 +731,13 @@ def main():
|
|
|
|
|
|
|
|
|
|
if output_dir is not None:
|
|
|
|
|
utils.update_summary(
|
|
|
|
|
epoch, train_metrics, eval_metrics, os.path.join(output_dir, 'summary.csv'),
|
|
|
|
|
write_header=best_metric is None, log_wandb=args.log_wandb and has_wandb)
|
|
|
|
|
epoch,
|
|
|
|
|
train_metrics,
|
|
|
|
|
eval_metrics,
|
|
|
|
|
os.path.join(output_dir, 'summary.csv'),
|
|
|
|
|
write_header=best_metric is None,
|
|
|
|
|
log_wandb=args.log_wandb and has_wandb,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if saver is not None:
|
|
|
|
|
# save proper checkpoint with eval metric
|
|
|
|
@ -699,10 +751,21 @@ def main():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def train_one_epoch(
|
|
|
|
|
epoch, model, loader, optimizer, loss_fn, args,
|
|
|
|
|
lr_scheduler=None, saver=None, output_dir=None, amp_autocast=suppress,
|
|
|
|
|
loss_scaler=None, model_ema=None, mixup_fn=None):
|
|
|
|
|
|
|
|
|
|
epoch,
|
|
|
|
|
model,
|
|
|
|
|
loader,
|
|
|
|
|
optimizer,
|
|
|
|
|
loss_fn,
|
|
|
|
|
args,
|
|
|
|
|
device=torch.device('cuda'),
|
|
|
|
|
lr_scheduler=None,
|
|
|
|
|
saver=None,
|
|
|
|
|
output_dir=None,
|
|
|
|
|
amp_autocast=suppress,
|
|
|
|
|
loss_scaler=None,
|
|
|
|
|
model_ema=None,
|
|
|
|
|
mixup_fn=None
|
|
|
|
|
):
|
|
|
|
|
if args.mixup_off_epoch and epoch >= args.mixup_off_epoch:
|
|
|
|
|
if args.prefetcher and loader.mixup_enabled:
|
|
|
|
|
loader.mixup_enabled = False
|
|
|
|
@ -723,7 +786,7 @@ def train_one_epoch(
|
|
|
|
|
last_batch = batch_idx == last_idx
|
|
|
|
|
data_time_m.update(time.time() - end)
|
|
|
|
|
if not args.prefetcher:
|
|
|
|
|
input, target = input.cuda(), target.cuda()
|
|
|
|
|
input, target = input.to(device), target.to(device)
|
|
|
|
|
if mixup_fn is not None:
|
|
|
|
|
input, target = mixup_fn(input, target)
|
|
|
|
|
if args.channels_last:
|
|
|
|
@ -740,21 +803,26 @@ def train_one_epoch(
|
|
|
|
|
if loss_scaler is not None:
|
|
|
|
|
loss_scaler(
|
|
|
|
|
loss, optimizer,
|
|
|
|
|
clip_grad=args.clip_grad, clip_mode=args.clip_mode,
|
|
|
|
|
clip_grad=args.clip_grad,
|
|
|
|
|
clip_mode=args.clip_mode,
|
|
|
|
|
parameters=model_parameters(model, exclude_head='agc' in args.clip_mode),
|
|
|
|
|
create_graph=second_order)
|
|
|
|
|
create_graph=second_order
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
loss.backward(create_graph=second_order)
|
|
|
|
|
if args.clip_grad is not None:
|
|
|
|
|
utils.dispatch_clip_grad(
|
|
|
|
|
model_parameters(model, exclude_head='agc' in args.clip_mode),
|
|
|
|
|
value=args.clip_grad, mode=args.clip_mode)
|
|
|
|
|
value=args.clip_grad,
|
|
|
|
|
mode=args.clip_mode
|
|
|
|
|
)
|
|
|
|
|
optimizer.step()
|
|
|
|
|
|
|
|
|
|
if model_ema is not None:
|
|
|
|
|
model_ema.update(model)
|
|
|
|
|
|
|
|
|
|
torch.cuda.synchronize()
|
|
|
|
|
|
|
|
|
|
num_updates += 1
|
|
|
|
|
batch_time_m.update(time.time() - end)
|
|
|
|
|
if last_batch or batch_idx % args.log_interval == 0:
|
|
|
|
@ -765,7 +833,7 @@ def train_one_epoch(
|
|
|
|
|
reduced_loss = utils.reduce_tensor(loss.data, args.world_size)
|
|
|
|
|
losses_m.update(reduced_loss.item(), input.size(0))
|
|
|
|
|
|
|
|
|
|
if args.local_rank == 0:
|
|
|
|
|
if utils.is_primary(args):
|
|
|
|
|
_logger.info(
|
|
|
|
|
'Train: {} [{:>4d}/{} ({:>3.0f}%)] '
|
|
|
|
|
'Loss: {loss.val:#.4g} ({loss.avg:#.3g}) '
|
|
|
|
@ -781,14 +849,16 @@ def train_one_epoch(
|
|
|
|
|
rate=input.size(0) * args.world_size / batch_time_m.val,
|
|
|
|
|
rate_avg=input.size(0) * args.world_size / batch_time_m.avg,
|
|
|
|
|
lr=lr,
|
|
|
|
|
data_time=data_time_m))
|
|
|
|
|
data_time=data_time_m)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if args.save_images and output_dir:
|
|
|
|
|
torchvision.utils.save_image(
|
|
|
|
|
input,
|
|
|
|
|
os.path.join(output_dir, 'train-batch-%d.jpg' % batch_idx),
|
|
|
|
|
padding=0,
|
|
|
|
|
normalize=True)
|
|
|
|
|
normalize=True
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if saver is not None and args.recovery_interval and (
|
|
|
|
|
last_batch or (batch_idx + 1) % args.recovery_interval == 0):
|
|
|
|
@ -806,7 +876,15 @@ def train_one_epoch(
|
|
|
|
|
return OrderedDict([('loss', losses_m.avg)])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate(model, loader, loss_fn, args, amp_autocast=suppress, log_suffix=''):
|
|
|
|
|
def validate(
|
|
|
|
|
model,
|
|
|
|
|
loader,
|
|
|
|
|
loss_fn,
|
|
|
|
|
args,
|
|
|
|
|
device=torch.device('cuda'),
|
|
|
|
|
amp_autocast=suppress,
|
|
|
|
|
log_suffix=''
|
|
|
|
|
):
|
|
|
|
|
batch_time_m = utils.AverageMeter()
|
|
|
|
|
losses_m = utils.AverageMeter()
|
|
|
|
|
top1_m = utils.AverageMeter()
|
|
|
|
@ -820,8 +898,8 @@ def validate(model, loader, loss_fn, args, amp_autocast=suppress, log_suffix='')
|
|
|
|
|
for batch_idx, (input, target) in enumerate(loader):
|
|
|
|
|
last_batch = batch_idx == last_idx
|
|
|
|
|
if not args.prefetcher:
|
|
|
|
|
input = input.cuda()
|
|
|
|
|
target = target.cuda()
|
|
|
|
|
input = input.to(device)
|
|
|
|
|
target = target.to(device)
|
|
|
|
|
if args.channels_last:
|
|
|
|
|
input = input.contiguous(memory_format=torch.channels_last)
|
|
|
|
|
|
|
|
|
@ -846,7 +924,8 @@ def validate(model, loader, loss_fn, args, amp_autocast=suppress, log_suffix='')
|
|
|
|
|
else:
|
|
|
|
|
reduced_loss = loss.data
|
|
|
|
|
|
|
|
|
|
torch.cuda.synchronize()
|
|
|
|
|
if device.type == 'cuda':
|
|
|
|
|
torch.cuda.synchronize()
|
|
|
|
|
|
|
|
|
|
losses_m.update(reduced_loss.item(), input.size(0))
|
|
|
|
|
top1_m.update(acc1.item(), output.size(0))
|
|
|
|
@ -854,7 +933,7 @@ def validate(model, loader, loss_fn, args, amp_autocast=suppress, log_suffix='')
|
|
|
|
|
|
|
|
|
|
batch_time_m.update(time.time() - end)
|
|
|
|
|
end = time.time()
|
|
|
|
|
if args.local_rank == 0 and (last_batch or batch_idx % args.log_interval == 0):
|
|
|
|
|
if utils.is_primary(args) and (last_batch or batch_idx % args.log_interval == 0):
|
|
|
|
|
log_name = 'Test' + log_suffix
|
|
|
|
|
_logger.info(
|
|
|
|
|
'{0}: [{1:>4d}/{2}] '
|
|
|
|
@ -862,8 +941,12 @@ def validate(model, loader, loss_fn, args, amp_autocast=suppress, log_suffix='')
|
|
|
|
|
'Loss: {loss.val:>7.4f} ({loss.avg:>6.4f}) '
|
|
|
|
|
'Acc@1: {top1.val:>7.4f} ({top1.avg:>7.4f}) '
|
|
|
|
|
'Acc@5: {top5.val:>7.4f} ({top5.avg:>7.4f})'.format(
|
|
|
|
|
log_name, batch_idx, last_idx, batch_time=batch_time_m,
|
|
|
|
|
loss=losses_m, top1=top1_m, top5=top5_m))
|
|
|
|
|
log_name, batch_idx, last_idx,
|
|
|
|
|
batch_time=batch_time_m,
|
|
|
|
|
loss=losses_m,
|
|
|
|
|
top1=top1_m,
|
|
|
|
|
top5=top5_m)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
metrics = OrderedDict([('loss', losses_m.avg), ('top1', top1_m.avg), ('top5', top5_m.avg)])
|
|
|
|
|
|
|
|
|
|