train_qt.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. # YOLOv5 🚀 by Ultralytics, AGPL-3.0 license
  2. """
  3. Train a YOLOv5 model on a custom dataset.
  4. Models and datasets download automatically from the latest YOLOv5 release.
  5. Usage - Single-GPU training:
  6. $ python train.py --data coco128.yaml --weights yolov5s.pt --img 640 # from pretrained (recommended)
  7. $ python train.py --data coco128.yaml --weights '' --cfg yolov5s.yaml --img 640 # from scratch
  8. Usage - Multi-GPU DDP training:
  9. $ python -m torch.distributed.run --nproc_per_node 4 --master_port 1 train.py --data coco128.yaml --weights yolov5s.pt --img 640 --device 0,1,2,3
  10. Models: https://github.com/ultralytics/yolov5/tree/master/models
  11. Datasets: https://github.com/ultralytics/yolov5/tree/master/data
  12. Tutorial: https://docs.ultralytics.com/yolov5/tutorials/train_custom_data
  13. """
  14. import argparse
  15. import math
  16. import os
  17. import random
  18. import subprocess
  19. import sys
  20. import time
  21. from copy import deepcopy
  22. from datetime import datetime
  23. from pathlib import Path
  24. try:
  25. import comet_ml # must be imported before torch (if installed)
  26. except ImportError:
  27. comet_ml = None
  28. import numpy as np
  29. import torch
  30. import torch.distributed as dist
  31. import torch.nn as nn
  32. import yaml
  33. from torch.optim import lr_scheduler
  34. from tqdm import tqdm
  35. FILE = Path(__file__).resolve()
  36. ROOT = FILE.parents[0] # YOLOv5 root directory
  37. if str(ROOT) not in sys.path:
  38. sys.path.append(str(ROOT)) # add ROOT to PATH
  39. ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
  40. import val as validate # for end-of-epoch mAP
  41. from models.experimental import attempt_load
  42. from models.yolo import Model
  43. from utils.autoanchor import check_anchors
  44. from utils.autobatch import check_train_batch_size
  45. from utils.callbacks import Callbacks
  46. from utils.dataloaders import create_dataloader
  47. from utils.downloads import attempt_download, is_url
  48. from utils.general import (LOGGER, TQDM_BAR_FORMAT, check_amp, check_dataset, check_file, check_git_info,
  49. check_git_status, check_img_size, check_requirements, check_suffix, check_yaml, colorstr,
  50. get_latest_run, increment_path, init_seeds, intersect_dicts, labels_to_class_weights,
  51. labels_to_image_weights, methods, one_cycle, print_args, print_mutation, strip_optimizer,
  52. yaml_save)
  53. from utils.loggers import Loggers
  54. from utils.loggers.comet.comet_utils import check_comet_resume
  55. from utils.loss import ComputeLoss
  56. from utils.metrics import fitness
  57. from utils.plots import plot_evolve
  58. from utils.torch_utils import (EarlyStopping, ModelEMA, de_parallel, select_device, smart_DDP, smart_optimizer,
  59. smart_resume, torch_distributed_zero_first)
  60. LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html
  61. RANK = int(os.getenv('RANK', -1))
  62. WORLD_SIZE = int(os.getenv('WORLD_SIZE', 1))
  63. GIT_INFO = check_git_info()
  64. def train(hyp, opt, device, callbacks): # hyp is path/to/hyp.yaml or hyp dictionary
  65. save_dir, epochs, batch_size, weights, single_cls, evolve, data, cfg, resume, noval, nosave, workers, freeze = \
  66. Path(opt.save_dir), opt.epochs, opt.batch_size, opt.weights, opt.single_cls, opt.evolve, opt.data, opt.cfg, \
  67. opt.resume, opt.noval, opt.nosave, opt.workers, opt.freeze
  68. callbacks.run('on_pretrain_routine_start')
  69. # Directories
  70. w = save_dir / 'weights' # weights dir
  71. (w.parent if evolve else w).mkdir(parents=True, exist_ok=True) # make dir
  72. last, best = w / 'last.pt', w / 'best.pt'
  73. # Hyperparameters
  74. if isinstance(hyp, str):
  75. with open(hyp, errors='ignore') as f:
  76. hyp = yaml.safe_load(f) # load hyps dict
  77. LOGGER.info(colorstr('hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in hyp.items()))
  78. opt.hyp = hyp.copy() # for saving hyps to checkpoints
  79. # Save run settings
  80. if not evolve:
  81. yaml_save(save_dir / 'hyp.yaml', hyp)
  82. yaml_save(save_dir / 'opt.yaml', vars(opt))
  83. # Loggers
  84. data_dict = None
  85. if RANK in {-1, 0}:
  86. loggers = Loggers(save_dir, weights, opt, hyp, LOGGER) # loggers instance
  87. # Register actions
  88. for k in methods(loggers):
  89. callbacks.register_action(k, callback=getattr(loggers, k))
  90. # Process custom dataset artifact link
  91. data_dict = loggers.remote_dataset
  92. if resume: # If resuming runs from remote artifact
  93. weights, epochs, hyp, batch_size = opt.weights, opt.epochs, opt.hyp, opt.batch_size
  94. # Config
  95. plots = not evolve and not opt.noplots # create plots
  96. cuda = device.type != 'cpu'
  97. init_seeds(opt.seed + 1 + RANK, deterministic=True)
  98. with torch_distributed_zero_first(LOCAL_RANK):
  99. data_dict = data_dict or check_dataset(data) # check if None
  100. train_path, val_path = data_dict['train'], data_dict['val']
  101. nc = 1 if single_cls else int(data_dict['nc']) # number of classes
  102. names = {0: 'item'} if single_cls and len(data_dict['names']) != 1 else data_dict['names'] # class names
  103. is_coco = isinstance(val_path, str) and val_path.endswith('coco/val2017.txt') # COCO dataset
  104. # Model
  105. check_suffix(weights, '.pt') # check weights
  106. pretrained = weights.endswith('.pt')
  107. if pretrained:
  108. with torch_distributed_zero_first(LOCAL_RANK):
  109. weights = attempt_download(weights) # download if not found locally
  110. ckpt = torch.load(weights, map_location='cpu') # load checkpoint to CPU to avoid CUDA memory leak
  111. model = Model(cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
  112. exclude = ['anchor'] if (cfg or hyp.get('anchors')) and not resume else [] # exclude keys
  113. csd = ckpt['model'].float().state_dict() # checkpoint state_dict as FP32
  114. csd = intersect_dicts(csd, model.state_dict(), exclude=exclude) # intersect
  115. model.load_state_dict(csd, strict=False) # load
  116. LOGGER.info(f'Transferred {len(csd)}/{len(model.state_dict())} items from {weights}') # report
  117. else:
  118. model = Model(cfg, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
  119. amp = check_amp(model) # check AMP
  120. # Freeze
  121. freeze = [f'model.{x}.' for x in (freeze if len(freeze) > 1 else range(freeze[0]))] # layers to freeze
  122. for k, v in model.named_parameters():
  123. v.requires_grad = True # train all layers
  124. # v.register_hook(lambda x: torch.nan_to_num(x)) # NaN to 0 (commented for erratic training results)
  125. if any(x in k for x in freeze):
  126. LOGGER.info(f'freezing {k}')
  127. v.requires_grad = False
  128. # Image size
  129. gs = max(int(model.stride.max()), 32) # grid size (max stride)
  130. imgsz = check_img_size(opt.imgsz, gs, floor=gs * 2) # verify imgsz is gs-multiple
  131. # Batch size
  132. if RANK == -1 and batch_size == -1: # single-GPU only, estimate best batch size
  133. batch_size = check_train_batch_size(model, imgsz, amp)
  134. loggers.on_params_update({'batch_size': batch_size})
  135. # Optimizer
  136. nbs = 64 # nominal batch size
  137. accumulate = max(round(nbs / batch_size), 1) # accumulate loss before optimizing
  138. hyp['weight_decay'] *= batch_size * accumulate / nbs # scale weight_decay
  139. optimizer = smart_optimizer(model, opt.optimizer, hyp['lr0'], hyp['momentum'], hyp['weight_decay'])
  140. # Scheduler
  141. if opt.cos_lr:
  142. lf = one_cycle(1, hyp['lrf'], epochs) # cosine 1->hyp['lrf']
  143. else:
  144. lf = lambda x: (1 - x / epochs) * (1.0 - hyp['lrf']) + hyp['lrf'] # linear
  145. scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) # plot_lr_scheduler(optimizer, scheduler, epochs)
  146. # EMA
  147. ema = ModelEMA(model) if RANK in {-1, 0} else None
  148. # Resume
  149. best_fitness, start_epoch = 0.0, 0
  150. if pretrained:
  151. if resume:
  152. best_fitness, start_epoch, epochs = smart_resume(ckpt, optimizer, ema, weights, epochs, resume)
  153. del ckpt, csd
  154. # DP mode
  155. if cuda and RANK == -1 and torch.cuda.device_count() > 1:
  156. LOGGER.warning(
  157. 'WARNING ⚠️ DP not recommended, use torch.distributed.run for best DDP Multi-GPU results.\n'
  158. 'See Multi-GPU Tutorial at https://docs.ultralytics.com/yolov5/tutorials/multi_gpu_training to get started.'
  159. )
  160. model = torch.nn.DataParallel(model)
  161. # SyncBatchNorm
  162. if opt.sync_bn and cuda and RANK != -1:
  163. model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model).to(device)
  164. LOGGER.info('Using SyncBatchNorm()')
  165. # Trainloader
  166. train_loader, dataset = create_dataloader(train_path,
  167. imgsz,
  168. batch_size // WORLD_SIZE,
  169. gs,
  170. single_cls,
  171. hyp=hyp,
  172. augment=True,
  173. cache=None if opt.cache == 'val' else opt.cache,
  174. rect=opt.rect,
  175. rank=LOCAL_RANK,
  176. workers=workers,
  177. image_weights=opt.image_weights,
  178. quad=opt.quad,
  179. prefix=colorstr('train: '),
  180. shuffle=True,
  181. seed=opt.seed)
  182. labels = np.concatenate(dataset.labels, 0)
  183. mlc = int(labels[:, 0].max()) # max label class
  184. assert mlc < nc, f'Label class {mlc} exceeds nc={nc} in {data}. Possible class labels are 0-{nc - 1}'
  185. # Process 0
  186. if RANK in {-1, 0}:
  187. val_loader = create_dataloader(val_path,
  188. imgsz,
  189. batch_size // WORLD_SIZE * 2,
  190. gs,
  191. single_cls,
  192. hyp=hyp,
  193. cache=None if noval else opt.cache,
  194. rect=True,
  195. rank=-1,
  196. workers=workers * 2,
  197. pad=0.5,
  198. prefix=colorstr('val: '))[0]
  199. if not resume:
  200. if not opt.noautoanchor:
  201. check_anchors(dataset, model=model, thr=hyp['anchor_t'], imgsz=imgsz) # run AutoAnchor
  202. model.half().float() # pre-reduce anchor precision
  203. callbacks.run('on_pretrain_routine_end', labels, names)
  204. # DDP mode
  205. if cuda and RANK != -1:
  206. model = smart_DDP(model)
  207. # Model attributes
  208. nl = de_parallel(model).model[-1].nl # number of detection layers (to scale hyps)
  209. hyp['box'] *= 3 / nl # scale to layers
  210. hyp['cls'] *= nc / 80 * 3 / nl # scale to classes and layers
  211. hyp['obj'] *= (imgsz / 640) ** 2 * 3 / nl # scale to image size and layers
  212. hyp['label_smoothing'] = opt.label_smoothing
  213. model.nc = nc # attach number of classes to model
  214. model.hyp = hyp # attach hyperparameters to model
  215. model.class_weights = labels_to_class_weights(dataset.labels, nc).to(device) * nc # attach class weights
  216. model.names = names
  217. # Start training
  218. t0 = time.time()
  219. nb = len(train_loader) # number of batches
  220. nw = max(round(hyp['warmup_epochs'] * nb), 100) # number of warmup iterations, max(3 epochs, 100 iterations)
  221. # nw = min(nw, (epochs - start_epoch) / 2 * nb) # limit warmup to < 1/2 of training
  222. last_opt_step = -1
  223. maps = np.zeros(nc) # mAP per class
  224. results = (0, 0, 0, 0, 0, 0, 0) # P, R, mAP@.5, mAP@.5-.95, val_loss(box, obj, cls)
  225. scheduler.last_epoch = start_epoch - 1 # do not move
  226. scaler = torch.cuda.amp.GradScaler(enabled=amp)
  227. stopper, stop = EarlyStopping(patience=opt.patience), False
  228. compute_loss = ComputeLoss(model) # init loss class
  229. callbacks.run('on_train_start')
  230. LOGGER.info(f'Image sizes {imgsz} train, {imgsz} val\n'
  231. f'Using {train_loader.num_workers * WORLD_SIZE} dataloader workers\n'
  232. f"Logging results to {colorstr('bold', save_dir)}\n"
  233. f'Starting training for {epochs} epochs...')
  234. for epoch in range(start_epoch, epochs): # epoch ------------------------------------------------------------------
  235. callbacks.run('on_train_epoch_start')
  236. model.train()
  237. # Update image weights (optional, single-GPU only)
  238. if opt.image_weights:
  239. cw = model.class_weights.cpu().numpy() * (1 - maps) ** 2 / nc # class weights
  240. iw = labels_to_image_weights(dataset.labels, nc=nc, class_weights=cw) # image weights
  241. dataset.indices = random.choices(range(dataset.n), weights=iw, k=dataset.n) # rand weighted idx
  242. # Update mosaic border (optional)
  243. # b = int(random.uniform(0.25 * imgsz, 0.75 * imgsz + gs) // gs * gs)
  244. # dataset.mosaic_border = [b - imgsz, -b] # height, width borders
  245. mloss = torch.zeros(3, device=device) # mean losses
  246. if RANK != -1:
  247. train_loader.sampler.set_epoch(epoch)
  248. pbar = enumerate(train_loader)
  249. LOGGER.info(('\n' + '%11s' * 7) % ('Epoch', 'GPU_mem', 'box_loss', 'obj_loss', 'cls_loss', 'Instances', 'Size'))
  250. if RANK in {-1, 0}:
  251. pbar = tqdm(pbar, total=nb, bar_format=TQDM_BAR_FORMAT) # progress bar
  252. optimizer.zero_grad()
  253. for i, (imgs, targets, paths, _) in pbar: # batch -------------------------------------------------------------
  254. callbacks.run('on_train_batch_start')
  255. ni = i + nb * epoch # number integrated batches (since train start)
  256. imgs = imgs.to(device, non_blocking=True).float() / 255 # uint8 to float32, 0-255 to 0.0-1.0
  257. # Warmup
  258. if ni <= nw:
  259. xi = [0, nw] # x interp
  260. # compute_loss.gr = np.interp(ni, xi, [0.0, 1.0]) # iou loss ratio (obj_loss = 1.0 or iou)
  261. accumulate = max(1, np.interp(ni, xi, [1, nbs / batch_size]).round())
  262. for j, x in enumerate(optimizer.param_groups):
  263. # bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0
  264. x['lr'] = np.interp(ni, xi, [hyp['warmup_bias_lr'] if j == 0 else 0.0, x['initial_lr'] * lf(epoch)])
  265. if 'momentum' in x:
  266. x['momentum'] = np.interp(ni, xi, [hyp['warmup_momentum'], hyp['momentum']])
  267. # Multi-scale
  268. if opt.multi_scale:
  269. sz = random.randrange(int(imgsz * 0.5), int(imgsz * 1.5) + gs) // gs * gs # size
  270. sf = sz / max(imgs.shape[2:]) # scale factor
  271. if sf != 1:
  272. ns = [math.ceil(x * sf / gs) * gs for x in imgs.shape[2:]] # new shape (stretched to gs-multiple)
  273. imgs = nn.functional.interpolate(imgs, size=ns, mode='bilinear', align_corners=False)
  274. # Forward
  275. with torch.cuda.amp.autocast(amp):
  276. pred = model(imgs) # forward
  277. loss, loss_items = compute_loss(pred, targets.to(device)) # loss scaled by batch_size
  278. if RANK != -1:
  279. loss *= WORLD_SIZE # gradient averaged between devices in DDP mode
  280. if opt.quad:
  281. loss *= 4.
  282. # Backward
  283. scaler.scale(loss).backward()
  284. # Optimize - https://pytorch.org/docs/master/notes/amp_examples.html
  285. if ni - last_opt_step >= accumulate:
  286. scaler.unscale_(optimizer) # unscale gradients
  287. torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=10.0) # clip gradients
  288. scaler.step(optimizer) # optimizer.step
  289. scaler.update()
  290. optimizer.zero_grad()
  291. if ema:
  292. ema.update(model)
  293. last_opt_step = ni
  294. # Log
  295. if RANK in {-1, 0}:
  296. mloss = (mloss * i + loss_items) / (i + 1) # update mean losses
  297. mem = f'{torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0:.3g}G' # (GB)
  298. pbar.set_description(('%11s' * 2 + '%11.4g' * 5) %
  299. (f'{epoch}/{epochs - 1}', mem, *mloss, targets.shape[0], imgs.shape[-1]))
  300. callbacks.run('on_train_batch_end', model, ni, imgs, targets, paths, list(mloss))
  301. if callbacks.stop_training:
  302. return
  303. # end batch ------------------------------------------------------------------------------------------------
  304. # Scheduler
  305. lr = [x['lr'] for x in optimizer.param_groups] # for loggers
  306. scheduler.step()
  307. if RANK in {-1, 0}:
  308. # mAP
  309. callbacks.run('on_train_epoch_end', epoch=epoch)
  310. ema.update_attr(model, include=['yaml', 'nc', 'hyp', 'names', 'stride', 'class_weights'])
  311. final_epoch = (epoch + 1 == epochs) or stopper.possible_stop
  312. if not noval or final_epoch: # Calculate mAP
  313. results, maps, _ = validate.run(data_dict,
  314. batch_size=batch_size // WORLD_SIZE * 2,
  315. imgsz=imgsz,
  316. half=amp,
  317. model=ema.ema,
  318. single_cls=single_cls,
  319. dataloader=val_loader,
  320. save_dir=save_dir,
  321. plots=False,
  322. callbacks=callbacks,
  323. compute_loss=compute_loss)
  324. # Update best mAP
  325. fi = fitness(np.array(results).reshape(1, -1)) # weighted combination of [P, R, mAP@.5, mAP@.5-.95]
  326. stop = stopper(epoch=epoch, fitness=fi) # early stop check
  327. if fi > best_fitness:
  328. best_fitness = fi
  329. log_vals = list(mloss) + list(results) + lr
  330. callbacks.run('on_fit_epoch_end', log_vals, epoch, best_fitness, fi)
  331. # Save model
  332. if (not nosave) or (final_epoch and not evolve): # if save
  333. ckpt = {
  334. 'epoch': epoch,
  335. 'best_fitness': best_fitness,
  336. 'model': deepcopy(de_parallel(model)).half(),
  337. 'ema': deepcopy(ema.ema).half(),
  338. 'updates': ema.updates,
  339. 'optimizer': optimizer.state_dict(),
  340. 'opt': vars(opt),
  341. 'git': GIT_INFO, # {remote, branch, commit} if a git repo
  342. 'date': datetime.now().isoformat()}
  343. # Save last, best and delete
  344. torch.save(ckpt, last)
  345. if best_fitness == fi:
  346. torch.save(ckpt, best)
  347. import shutil
  348. shutil.copyfile(best, opt.ckptname)
  349. if opt.save_period > 0 and epoch % opt.save_period == 0:
  350. torch.save(ckpt, w / f'epoch{epoch}.pt')
  351. del ckpt
  352. callbacks.run('on_model_save', last, epoch, final_epoch, best_fitness, fi)
  353. # EarlyStopping
  354. if RANK != -1: # if DDP training
  355. broadcast_list = [stop if RANK == 0 else None]
  356. dist.broadcast_object_list(broadcast_list, 0) # broadcast 'stop' to all ranks
  357. if RANK != 0:
  358. stop = broadcast_list[0]
  359. if stop:
  360. break # must break all DDP ranks
  361. # end epoch ----------------------------------------------------------------------------------------------------
  362. # end training -----------------------------------------------------------------------------------------------------
  363. if RANK in {-1, 0}:
  364. LOGGER.info(f'\n{epoch - start_epoch + 1} epochs completed in {(time.time() - t0) / 3600:.3f} hours.')
  365. for f in last, best:
  366. if f.exists():
  367. strip_optimizer(f) # strip optimizers
  368. if f is best:
  369. LOGGER.info(f'\nValidating {f}...')
  370. results, _, _ = validate.run(
  371. data_dict,
  372. batch_size=batch_size // WORLD_SIZE * 2,
  373. imgsz=imgsz,
  374. model=attempt_load(f, device).half(),
  375. iou_thres=0.65 if is_coco else 0.60, # best pycocotools at iou 0.65
  376. single_cls=single_cls,
  377. dataloader=val_loader,
  378. save_dir=save_dir,
  379. save_json=is_coco,
  380. verbose=True,
  381. plots=plots,
  382. callbacks=callbacks,
  383. compute_loss=compute_loss) # val best model with plots
  384. if is_coco:
  385. callbacks.run('on_fit_epoch_end', list(mloss) + list(results) + lr, epoch, best_fitness, fi)
  386. callbacks.run('on_train_end', last, best, epoch, results)
  387. torch.cuda.empty_cache()
  388. return results
  389. def parse_opt(known=False):
  390. parser = argparse.ArgumentParser()
  391. parser.add_argument('--weights', type=str, default=ROOT / 'yolov5s.pt', help='initial weights path')
  392. parser.add_argument('--cfg', type=str, default='', help='model.yaml path')
  393. parser.add_argument('--data', type=str, default=ROOT / 'data\HRSID_YOLO\dataset.yaml', help='dataset.yaml path')
  394. parser.add_argument('--hyp', type=str, default=ROOT / 'data/hyps/hyp.scratch-low.yaml', help='hyperparameters path')
  395. parser.add_argument('--epochs', type=int, default=100, help='total training epochs')
  396. parser.add_argument('--batch-size', type=int, default=3, help='total batch size for all GPUs, -1 for autobatch')
  397. parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='train, val image size (pixels)')
  398. parser.add_argument('--rect', action='store_true', help='rectangular training')
  399. parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')
  400. parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
  401. parser.add_argument('--noval', action='store_true', help='only validate final epoch')
  402. parser.add_argument('--noautoanchor', action='store_true', help='disable AutoAnchor')
  403. parser.add_argument('--noplots', action='store_true', help='save no plot files')
  404. parser.add_argument('--evolve', type=int, nargs='?', const=300, help='evolve hyperparameters for x generations')
  405. parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')
  406. parser.add_argument('--cache', type=str, nargs='?', const='ram', help='image --cache ram/disk')
  407. parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')
  408. parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
  409. parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')
  410. parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')
  411. parser.add_argument('--optimizer', type=str, choices=['SGD', 'Adam', 'AdamW'], default='SGD', help='optimizer')
  412. parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')
  413. parser.add_argument('--workers', type=int, default=8, help='max dataloader workers (per RANK in DDP mode)')
  414. parser.add_argument('--project', default=ROOT / 'runs/train', help='save to project/name')
  415. parser.add_argument('--name', default='exp', help='save to project/name')
  416. parser.add_argument('--ckptname', default='train_model', help='save to model/name') # 用户自己训练的模型
  417. parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
  418. parser.add_argument('--quad', action='store_true', help='quad dataloader')
  419. parser.add_argument('--cos-lr', action='store_true', help='cosine LR scheduler')
  420. parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')
  421. parser.add_argument('--patience', type=int, default=100, help='EarlyStopping patience (epochs without improvement)')
  422. parser.add_argument('--freeze', nargs='+', type=int, default=[0], help='Freeze layers: backbone=10, first3=0 1 2')
  423. parser.add_argument('--save-period', type=int, default=-1, help='Save checkpoint every x epochs (disabled if < 1)')
  424. parser.add_argument('--seed', type=int, default=0, help='Global training seed')
  425. parser.add_argument('--local_rank', type=int, default=-1, help='Automatic DDP Multi-GPU argument, do not modify')
  426. # Logger arguments
  427. parser.add_argument('--entity', default=None, help='Entity')
  428. parser.add_argument('--upload_dataset', nargs='?', const=True, default=False, help='Upload data, "val" option')
  429. parser.add_argument('--bbox_interval', type=int, default=-1, help='Set bounding-box image logging interval')
  430. parser.add_argument('--artifact_alias', type=str, default='latest', help='Version of dataset artifact to use')
  431. return parser.parse_known_args()[0] if known else parser.parse_args()
  432. def main(opt, callbacks=Callbacks()):
  433. # Checks
  434. if RANK in {-1, 0}:
  435. print_args(vars(opt))
  436. check_git_status()
  437. check_requirements(ROOT / 'requirements.txt')
  438. # Resume (from specified or most recent last.pt)
  439. if opt.resume and not check_comet_resume(opt) and not opt.evolve:
  440. last = Path(check_file(opt.resume) if isinstance(opt.resume, str) else get_latest_run())
  441. opt_yaml = last.parent.parent / 'opt.yaml' # train options yaml
  442. opt_data = opt.data # original dataset
  443. if opt_yaml.is_file():
  444. with open(opt_yaml, errors='ignore') as f:
  445. d = yaml.safe_load(f)
  446. else:
  447. d = torch.load(last, map_location='cpu')['opt']
  448. opt = argparse.Namespace(**d) # replace
  449. opt.cfg, opt.weights, opt.resume = '', str(last), True # reinstate
  450. if is_url(opt_data):
  451. opt.data = check_file(opt_data) # avoid HUB resume auth timeout
  452. else:
  453. opt.data, opt.cfg, opt.hyp, opt.weights, opt.project = \
  454. check_file(opt.data), check_yaml(opt.cfg), check_yaml(opt.hyp), str(opt.weights), str(opt.project) # checks
  455. assert len(opt.cfg) or len(opt.weights), 'either --cfg or --weights must be specified'
  456. if opt.evolve:
  457. if opt.project == str(ROOT / 'runs/train'): # if default project name, rename to runs/evolve
  458. opt.project = str(ROOT / 'runs/evolve')
  459. opt.exist_ok, opt.resume = opt.resume, False # pass resume to exist_ok and disable resume
  460. if opt.name == 'cfg':
  461. opt.name = Path(opt.cfg).stem # use model.yaml as name
  462. opt.save_dir = str(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok))
  463. # DDP mode
  464. device = select_device(opt.device, batch_size=opt.batch_size)
  465. if LOCAL_RANK != -1:
  466. msg = 'is not compatible with YOLOv5 Multi-GPU DDP training'
  467. assert not opt.image_weights, f'--image-weights {msg}'
  468. assert not opt.evolve, f'--evolve {msg}'
  469. assert opt.batch_size != -1, f'AutoBatch with --batch-size -1 {msg}, please pass a valid --batch-size'
  470. assert opt.batch_size % WORLD_SIZE == 0, f'--batch-size {opt.batch_size} must be multiple of WORLD_SIZE'
  471. assert torch.cuda.device_count() > LOCAL_RANK, 'insufficient CUDA devices for DDP command'
  472. torch.cuda.set_device(LOCAL_RANK)
  473. device = torch.device('cuda', LOCAL_RANK)
  474. dist.init_process_group(backend='nccl' if dist.is_nccl_available() else 'gloo')
  475. # Train
  476. if not opt.evolve:
  477. train(opt.hyp, opt, device, callbacks)
  478. # Evolve hyperparameters (optional)
  479. else:
  480. # Hyperparameter evolution metadata (mutation scale 0-1, lower_limit, upper_limit)
  481. meta = {
  482. 'lr0': (1, 1e-5, 1e-1), # initial learning rate (SGD=1E-2, Adam=1E-3)
  483. 'lrf': (1, 0.01, 1.0), # final OneCycleLR learning rate (lr0 * lrf)
  484. 'momentum': (0.3, 0.6, 0.98), # SGD momentum/Adam beta1
  485. 'weight_decay': (1, 0.0, 0.001), # optimizer weight decay
  486. 'warmup_epochs': (1, 0.0, 5.0), # warmup epochs (fractions ok)
  487. 'warmup_momentum': (1, 0.0, 0.95), # warmup initial momentum
  488. 'warmup_bias_lr': (1, 0.0, 0.2), # warmup initial bias lr
  489. 'box': (1, 0.02, 0.2), # box loss gain
  490. 'cls': (1, 0.2, 4.0), # cls loss gain
  491. 'cls_pw': (1, 0.5, 2.0), # cls BCELoss positive_weight
  492. 'obj': (1, 0.2, 4.0), # obj loss gain (scale with pixels)
  493. 'obj_pw': (1, 0.5, 2.0), # obj BCELoss positive_weight
  494. 'iou_t': (0, 0.1, 0.7), # IoU training threshold
  495. 'anchor_t': (1, 2.0, 8.0), # anchor-multiple threshold
  496. 'anchors': (2, 2.0, 10.0), # anchors per output grid (0 to ignore)
  497. 'fl_gamma': (0, 0.0, 2.0), # focal loss gamma (efficientDet default gamma=1.5)
  498. 'hsv_h': (1, 0.0, 0.1), # image HSV-Hue augmentation (fraction)
  499. 'hsv_s': (1, 0.0, 0.9), # image HSV-Saturation augmentation (fraction)
  500. 'hsv_v': (1, 0.0, 0.9), # image HSV-Value augmentation (fraction)
  501. 'degrees': (1, 0.0, 45.0), # image rotation (+/- deg)
  502. 'translate': (1, 0.0, 0.9), # image translation (+/- fraction)
  503. 'scale': (1, 0.0, 0.9), # image scale (+/- gain)
  504. 'shear': (1, 0.0, 10.0), # image shear (+/- deg)
  505. 'perspective': (0, 0.0, 0.001), # image perspective (+/- fraction), range 0-0.001
  506. 'flipud': (1, 0.0, 1.0), # image flip up-down (probability)
  507. 'fliplr': (0, 0.0, 1.0), # image flip left-right (probability)
  508. 'mosaic': (1, 0.0, 1.0), # image mixup (probability)
  509. 'mixup': (1, 0.0, 1.0), # image mixup (probability)
  510. 'copy_paste': (1, 0.0, 1.0)} # segment copy-paste (probability)
  511. with open(opt.hyp, errors='ignore') as f:
  512. hyp = yaml.safe_load(f) # load hyps dict
  513. if 'anchors' not in hyp: # anchors commented in hyp.yaml
  514. hyp['anchors'] = 3
  515. if opt.noautoanchor:
  516. del hyp['anchors'], meta['anchors']
  517. opt.noval, opt.nosave, save_dir = True, True, Path(opt.save_dir) # only val/save final epoch
  518. # ei = [isinstance(x, (int, float)) for x in hyp.values()] # evolvable indices
  519. evolve_yaml, evolve_csv = save_dir / 'hyp_evolve.yaml', save_dir / 'evolve.csv'
  520. if opt.bucket:
  521. # download evolve.csv if exists
  522. subprocess.run([
  523. 'gsutil',
  524. 'cp',
  525. f'gs://{opt.bucket}/evolve.csv',
  526. str(evolve_csv),])
  527. for _ in range(opt.evolve): # generations to evolve
  528. if evolve_csv.exists(): # if evolve.csv exists: select best hyps and mutate
  529. # Select parent(s)
  530. parent = 'single' # parent selection method: 'single' or 'weighted'
  531. x = np.loadtxt(evolve_csv, ndmin=2, delimiter=',', skiprows=1)
  532. n = min(5, len(x)) # number of previous results to consider
  533. x = x[np.argsort(-fitness(x))][:n] # top n mutations
  534. w = fitness(x) - fitness(x).min() + 1E-6 # weights (sum > 0)
  535. if parent == 'single' or len(x) == 1:
  536. # x = x[random.randint(0, n - 1)] # random selection
  537. x = x[random.choices(range(n), weights=w)[0]] # weighted selection
  538. elif parent == 'weighted':
  539. x = (x * w.reshape(n, 1)).sum(0) / w.sum() # weighted combination
  540. # Mutate
  541. mp, s = 0.8, 0.2 # mutation probability, sigma
  542. npr = np.random
  543. npr.seed(int(time.time()))
  544. g = np.array([meta[k][0] for k in hyp.keys()]) # gains 0-1
  545. ng = len(meta)
  546. v = np.ones(ng)
  547. while all(v == 1): # mutate until a change occurs (prevent duplicates)
  548. v = (g * (npr.random(ng) < mp) * npr.randn(ng) * npr.random() * s + 1).clip(0.3, 3.0)
  549. for i, k in enumerate(hyp.keys()): # plt.hist(v.ravel(), 300)
  550. hyp[k] = float(x[i + 7] * v[i]) # mutate
  551. # Constrain to limits
  552. for k, v in meta.items():
  553. hyp[k] = max(hyp[k], v[1]) # lower limit
  554. hyp[k] = min(hyp[k], v[2]) # upper limit
  555. hyp[k] = round(hyp[k], 5) # significant digits
  556. # Train mutation
  557. results = train(hyp.copy(), opt, device, callbacks)
  558. callbacks = Callbacks()
  559. # Write mutation results
  560. keys = ('metrics/precision', 'metrics/recall', 'metrics/mAP_0.5', 'metrics/mAP_0.5:0.95', 'val/box_loss',
  561. 'val/obj_loss', 'val/cls_loss')
  562. print_mutation(keys, results, hyp.copy(), save_dir, opt.bucket)
  563. # Plot results
  564. plot_evolve(evolve_csv)
  565. LOGGER.info(f'Hyperparameter evolution finished {opt.evolve} generations\n'
  566. f"Results saved to {colorstr('bold', save_dir)}\n"
  567. f'Usage example: $ python train.py --hyp {evolve_yaml}')
  568. def run(**kwargs):
  569. # Usage: import train; train.run(data='coco128.yaml', imgsz=320, weights='yolov5m.pt')
  570. opt = parse_opt(True)
  571. for k, v in kwargs.items():
  572. setattr(opt, k, v)
  573. main(opt)
  574. return opt
  575. if __name__ == '__main__':
  576. opt = parse_opt()
  577. main(opt)