val.py refactor (#4053)
* val.py refactor * cleanup * cleanup * cleanup * cleanup * save after eval * opt.imgsz bug fix * wandb refactor * dataloader to train_loader * capitalize global variables * runs/hub/exp to runs/detect/exp * refactor wandb logging * Refactor wandb operations (#4061) Co-authored-by: Ayush Chaurasia <ayush.chaurarsia@gmail.com>
This commit is contained in:
1 parent
9dd33fd20f
commit
f7d8562060
8 files changed
+219
-202
No files matched your search
@@ -47,7 +47,7 @@ from utils.torch_utils import ModelEMA, select_device, intersect_dicts, torch_di
|
||||
from utils.wandb_logging.wandb_utils import WandbLogger, check_wandb_resume
|
||||
from utils.metrics import fitness
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html
|
||||
RANK = int(os.getenv('RANK', -1))
|
||||
WORLD_SIZE = int(os.getenv('WORLD_SIZE', 1))
|
||||
@@ -73,7 +73,7 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
if isinstance(hyp, str):
|
||||
with open(hyp) as f:
|
||||
hyp = yaml.safe_load(f) # load hyps dict
|
||||
logger.info(colorstr('hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in hyp.items()))
|
||||
LOGGER.info(colorstr('hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in hyp.items()))
|
||||
|
||||
# Save run settings
|
||||
with open(save_dir / 'hyp.yaml', 'w') as f:
|
||||
@@ -94,7 +94,7 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
# TensorBoard
|
||||
if not evolve:
|
||||
prefix = colorstr('tensorboard: ')
|
||||
logger.info(f"{prefix}Start with 'tensorboard --logdir {opt.project}', view at http://localhost:6006/")
|
||||
LOGGER.info(f"{prefix}Start with 'tensorboard --logdir {opt.project}', view at http://localhost:6006/")
|
||||
loggers['tb'] = SummaryWriter(str(save_dir))
|
||||
|
||||
# W&B
|
||||
@@ -123,7 +123,7 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
state_dict = ckpt['model'].float().state_dict() # to FP32
|
||||
state_dict = intersect_dicts(state_dict, model.state_dict(), exclude=exclude) # intersect
|
||||
model.load_state_dict(state_dict, strict=False) # load
|
||||
logger.info('Transferred %g/%g items from %s' % (len(state_dict), len(model.state_dict()), weights)) # report
|
||||
LOGGER.info('Transferred %g/%g items from %s' % (len(state_dict), len(model.state_dict()), weights)) # report
|
||||
else:
|
||||
model = Model(cfg, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
|
||||
with torch_distributed_zero_first(RANK):
|
||||
@@ -143,7 +143,7 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
nbs = 64 # nominal batch size
|
||||
accumulate = max(round(nbs / batch_size), 1) # accumulate loss before optimizing
|
||||
hyp['weight_decay'] *= batch_size * accumulate / nbs # scale weight_decay
|
||||
logger.info(f"Scaled weight_decay = {hyp['weight_decay']}")
|
||||
LOGGER.info(f"Scaled weight_decay = {hyp['weight_decay']}")
|
||||
|
||||
pg0, pg1, pg2 = [], [], [] # optimizer parameter groups
|
||||
for k, v in model.named_modules():
|
||||
@@ -161,7 +161,7 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
|
||||
optimizer.add_param_group({'params': pg1, 'weight_decay': hyp['weight_decay']}) # add pg1 with weight_decay
|
||||
optimizer.add_param_group({'params': pg2}) # add pg2 (biases)
|
||||
logger.info('Optimizer groups: %g .bias, %g conv.weight, %g other' % (len(pg2), len(pg1), len(pg0)))
|
||||
LOGGER.info('Optimizer groups: %g .bias, %g conv.weight, %g other' % (len(pg2), len(pg1), len(pg0)))
|
||||
del pg0, pg1, pg2
|
||||
|
||||
# Scheduler https://arxiv.org/pdf/1812.01187.pdf
|
||||
@@ -198,7 +198,7 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
if resume:
|
||||
assert start_epoch > 0, '%s training to %g epochs is finished, nothing to resume.' % (weights, epochs)
|
||||
if epochs < start_epoch:
|
||||
logger.info('%s has been trained for %g epochs. Fine-tuning for %g additional epochs.' %
|
||||
LOGGER.info('%s has been trained for %g epochs. Fine-tuning for %g additional epochs.' %
|
||||
(weights, ckpt['epoch'], epochs))
|
||||
epochs += ckpt['epoch'] # finetune additional epochs
|
||||
|
||||
@@ -207,7 +207,7 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
# Image sizes
|
||||
gs = max(int(model.stride.max()), 32) # grid size (max stride)
|
||||
nl = model.model[-1].nl # number of detection layers (used for scaling hyp['obj'])
|
||||
imgsz, imgsz_val = [check_img_size(x, gs) for x in opt.img_size] # verify imgsz are gs-multiples
|
||||
imgsz = check_img_size(opt.imgsz, gs) # verify imgsz is gs-multiple
|
||||
|
||||
# DP mode
|
||||
if cuda and RANK == -1 and torch.cuda.device_count() > 1:
|
||||
@@ -219,33 +219,31 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
if opt.sync_bn and cuda and RANK != -1:
|
||||
raise Exception('can not train with --sync-bn, known issue https://github.com/ultralytics/yolov5/issues/3998')
|
||||
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model).to(device)
|
||||
logger.info('Using SyncBatchNorm()')
|
||||
LOGGER.info('Using SyncBatchNorm()')
|
||||
|
||||
# Trainloader
|
||||
dataloader, dataset = create_dataloader(train_path, imgsz, batch_size // WORLD_SIZE, gs, single_cls,
|
||||
hyp=hyp, augment=True, cache=opt.cache_images, rect=opt.rect, rank=RANK,
|
||||
workers=workers,
|
||||
image_weights=opt.image_weights, quad=opt.quad, prefix=colorstr('train: '))
|
||||
train_loader, dataset = create_dataloader(train_path, imgsz, batch_size // WORLD_SIZE, gs, single_cls,
|
||||
hyp=hyp, augment=True, cache=opt.cache_images, rect=opt.rect, rank=RANK,
|
||||
workers=workers, image_weights=opt.image_weights, quad=opt.quad,
|
||||
prefix=colorstr('train: '))
|
||||
mlc = np.concatenate(dataset.labels, 0)[:, 0].max() # max label class
|
||||
nb = len(dataloader) # number of batches
|
||||
nb = len(train_loader) # number of batches
|
||||
assert mlc < nc, 'Label class %g exceeds nc=%g in %s. Possible class labels are 0-%g' % (mlc, nc, data, nc - 1)
|
||||
|
||||
# Process 0
|
||||
if RANK in [-1, 0]:
|
||||
valloader = create_dataloader(val_path, imgsz_val, batch_size // WORLD_SIZE * 2, gs, single_cls,
|
||||
hyp=hyp, cache=opt.cache_images and not noval, rect=True, rank=-1,
|
||||
workers=workers,
|
||||
pad=0.5, prefix=colorstr('val: '))[0]
|
||||
val_loader = create_dataloader(val_path, imgsz, batch_size // WORLD_SIZE * 2, gs, single_cls,
|
||||
hyp=hyp, cache=opt.cache_images and not noval, rect=True, rank=-1,
|
||||
workers=workers, pad=0.5,
|
||||
prefix=colorstr('val: '))[0]
|
||||
|
||||
if not resume:
|
||||
labels = np.concatenate(dataset.labels, 0)
|
||||
c = torch.tensor(labels[:, 0]) # classes
|
||||
# c = torch.tensor(labels[:, 0]) # classes
|
||||
# cf = torch.bincount(c.long(), minlength=nc) + 1. # frequency
|
||||
# model._initialize_biases(cf.to(device))
|
||||
if plots:
|
||||
plot_labels(labels, names, save_dir, loggers)
|
||||
if loggers['tb']:
|
||||
loggers['tb'].add_histogram('classes', c, 0) # TensorBoard
|
||||
|
||||
# Anchors
|
||||
if not opt.noautoanchor:
|
||||
@@ -277,8 +275,8 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
scheduler.last_epoch = start_epoch - 1 # do not move
|
||||
scaler = amp.GradScaler(enabled=cuda)
|
||||
compute_loss = ComputeLoss(model) # init loss class
|
||||
logger.info(f'Image sizes {imgsz} train, {imgsz_val} val\n'
|
||||
f'Using {dataloader.num_workers} dataloader workers\n'
|
||||
LOGGER.info(f'Image sizes {imgsz} train, {imgsz} val\n'
|
||||
f'Using {train_loader.num_workers} dataloader workers\n'
|
||||
f'Logging results to {save_dir}\n'
|
||||
f'Starting training for {epochs} epochs...')
|
||||
for epoch in range(start_epoch, epochs): # epoch ------------------------------------------------------------------
|
||||
@@ -304,9 +302,9 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
|
||||
mloss = torch.zeros(4, device=device) # mean losses
|
||||
if RANK != -1:
|
||||
dataloader.sampler.set_epoch(epoch)
|
||||
pbar = enumerate(dataloader)
|
||||
logger.info(('\n' + '%10s' * 8) % ('Epoch', 'gpu_mem', 'box', 'obj', 'cls', 'total', 'labels', 'img_size'))
|
||||
train_loader.sampler.set_epoch(epoch)
|
||||
pbar = enumerate(train_loader)
|
||||
LOGGER.info(('\n' + '%10s' * 8) % ('Epoch', 'gpu_mem', 'box', 'obj', 'cls', 'total', 'labels', 'img_size'))
|
||||
if RANK in [-1, 0]:
|
||||
pbar = tqdm(pbar, total=nb) # progress bar
|
||||
optimizer.zero_grad()
|
||||
@@ -389,10 +387,10 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
wandb_logger.current_epoch = epoch + 1
|
||||
results, maps, _ = val.run(data_dict,
|
||||
batch_size=batch_size // WORLD_SIZE * 2,
|
||||
imgsz=imgsz_val,
|
||||
imgsz=imgsz,
|
||||
model=ema.ema,
|
||||
single_cls=single_cls,
|
||||
dataloader=valloader,
|
||||
dataloader=val_loader,
|
||||
save_dir=save_dir,
|
||||
save_json=is_coco and final_epoch,
|
||||
verbose=nc < 50 and final_epoch,
|
||||
@@ -444,7 +442,7 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
# end epoch ----------------------------------------------------------------------------------------------------
|
||||
# end training -----------------------------------------------------------------------------------------------------
|
||||
if RANK in [-1, 0]:
|
||||
logger.info(f'{epoch - start_epoch + 1} epochs completed in {(time.time() - t0) / 3600:.3f} hours.\n')
|
||||
LOGGER.info(f'{epoch - start_epoch + 1} epochs completed in {(time.time() - t0) / 3600:.3f} hours.\n')
|
||||
if plots:
|
||||
plot_results(save_dir=save_dir) # save as results.png
|
||||
if loggers['wandb']:
|
||||
@@ -457,10 +455,10 @@ def train(hyp, # path/to/hyp.yaml or hyp dictionary
|
||||
for m in [last, best] if best.exists() else [last]: # speed, mAP tests
|
||||
results, _, _ = val.run(data_dict,
|
||||
batch_size=batch_size // WORLD_SIZE * 2,
|
||||
imgsz=imgsz_val,
|
||||
imgsz=imgsz,
|
||||
model=attempt_load(m, device).half(),
|
||||
single_cls=single_cls,
|
||||
dataloader=valloader,
|
||||
dataloader=val_loader,
|
||||
save_dir=save_dir,
|
||||
save_json=True,
|
||||
plots=False)
|
||||
@@ -487,7 +485,7 @@ def parse_opt(known=False):
|
||||
parser.add_argument('--hyp', type=str, default='data/hyps/hyp.scratch.yaml', help='hyperparameters path')
|
||||
parser.add_argument('--epochs', type=int, default=300)
|
||||
parser.add_argument('--batch-size', type=int, default=16, help='total batch size for all GPUs')
|
||||
parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, val] image sizes')
|
||||
parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='train, val image size (pixels)')
|
||||
parser.add_argument('--rect', action='store_true', help='rectangular training')
|
||||
parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')
|
||||
parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
|
||||
@@ -534,12 +532,11 @@ def main(opt):
|
||||
with open(Path(ckpt).parent.parent / 'opt.yaml') as f:
|
||||
opt = argparse.Namespace(**yaml.safe_load(f)) # replace
|
||||
opt.cfg, opt.weights, opt.resume = '', ckpt, True # reinstate
|
||||
logger.info('Resuming training from %s' % ckpt)
|
||||
LOGGER.info(f'Resuming training from {ckpt}')
|
||||
else:
|
||||
# opt.hyp = opt.hyp or ('hyp.finetune.yaml' if opt.weights else 'hyp.scratch.yaml')
|
||||
opt.data, opt.cfg, opt.hyp = check_file(opt.data), check_file(opt.cfg), check_file(opt.hyp) # check files
|
||||
assert len(opt.cfg) or len(opt.weights), 'either --cfg or --weights must be specified'
|
||||
opt.img_size.extend([opt.img_size[-1]] * (2 - len(opt.img_size))) # extend to 2 sizes (train, val)
|
||||
opt.name = 'evolve' if opt.evolve else opt.name
|
||||
opt.save_dir = str(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok or opt.evolve))
|
||||
|
||||
@@ -602,7 +599,7 @@ def main(opt):
|
||||
# ei = [isinstance(x, (int, float)) for x in hyp.values()] # evolvable indices
|
||||
yaml_file = Path(opt.save_dir) / 'hyp_evolved.yaml' # save best result here
|
||||
if opt.bucket:
|
||||
os.system('gsutil cp gs://%s/evolve.txt .' % opt.bucket) # download evolve.txt if exists
|
||||
os.system(f'gsutil cp gs://{opt.bucket}/evolve.txt .') # download evolve.txt if exists
|
||||
|
||||
for _ in range(opt.evolve): # generations to evolve
|
||||
if Path('evolve.txt').exists(): # if evolve.txt exists: select best hyps and mutate
|
||||
|
||||
Reference in new issue
Block a user