经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 大数据/云/AI » 人工智能基础 » 查看文章
训练一个目标检测模型
来源:cnblogs  作者:zylyehuo  时间:2023/12/15 8:50:42  对本文有异议

博客地址:https://www.cnblogs.com/zylyehuo/

(一)识别背景/目的

第十八届全国大学生智能汽车竞赛室外 ROS 无人车赛(高教组)

无人车在室外运行中, 需要探索未知环境, 识别障碍物, 停车标志牌、红绿灯等标志物。

比赛场地为不规则环形场地, 由红蓝两色锥桶搭建而成, 整体赛道由直线区域、 "S"弯、 直角区域、 圆形区域等部分元素或全部元素构成

(二)识别/运行场地

① 一食堂二楼

② 室外网球场地

(三)实现效果

(四)技术栈

  • 识别模型:yolov5
  • 标注工具:labelmaster
  • 运行环境:Ubuntu20.04
  • 加速方式:使用onnx及tensorrt将模型进行推理加速

(五)识别类别

  • 0: red 红色锥桶
  • 1: blue 蓝色锥桶
  • 2: stop 红绿灯(红灯)
  • 3: wait 停车牌

(六)yolov5目标检测模型及其环境配置过程

第一步:下载yolov5源码

https://github.com/ultralytics/yolov5

第二步:解压源码压缩包

第三步:在代码编辑器 pycharm/vscode 中打开源码文件夹(配置完成)

(七)训练数据、测试数据采集

第一步:打开 ROS 智能车摄像头

终端输入 cheese

第二步:点击拍摄按键,采集数据集图片

第三步:将无人车上的照片拷贝到电脑上,为数据标注做准备

(八)训练数据、测试数据标注、整理

第一步:安装 labelmaster 库

  1. pip install labelImg

第二步:启动 labelmaster

  1. labelImg

第三步:打开采集图片的保存路径

第四步:鼠标右键图片,创建区块标注

左侧选择创建区块

鼠标移至目标的左上角

点击鼠标并拉直右下角

在出现的框里面选择自己标注目标的分类

键入新的分类,则会自动生成一个新的分类

这边选择wait的红绿灯分类

左键单击 ok 键

完成单个目标的标注

其他类别同样操作,只是分类时选择不同分类

依次按照识别类别对其余区块进行标注

例如下图选择red的红色锥桶分类

全部完成后左上角点击“改变保存目录

选择保存的目标文件夹

第五步:保存标注参数,保存为 .txt 文件后缀

点击保存下方的按键,改变保存的文件格式,保存为 .txt 文件后缀

yolo”对应的是“txt”文件

PascalVOC”对应的是“xml”文件

CreatelML”对应的是“json”文件

最后单击保存完成标注

此时,保存的文件夹中会自动生成一个 class.txt 文件

(九)模型训练过程

为了减小最终模型保存的大小,保证无人车整体运行的流程性,我们选用 yolov5n.yaml 的参数作为样本

源码:yolov5n.yaml

  1. # Parameters
  2. nc: 80 # number of classes
  3. depth_multiple: 0.33 # model depth multiple
  4. width_multiple: 0.25 # layer channel multiple
  5. anchors:
  6. - [10,13, 16,30, 33,23] # P3/8
  7. - [30,61, 62,45, 59,119] # P4/16
  8. - [116,90, 156,198, 373,326] # P5/32
  9. # YOLOv5 v6.0 backbone
  10. backbone:
  11. # [from, number, module, args]
  12. [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
  13. [-1, 1, Conv, [128, 3, 2]], # 1-P2/4
  14. [-1, 3, C3, [128]],
  15. [-1, 1, Conv, [256, 3, 2]], # 3-P3/8
  16. [-1, 6, C3, [256]],
  17. [-1, 1, Conv, [512, 3, 2]], # 5-P4/16
  18. [-1, 9, C3, [512]],
  19. [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
  20. [-1, 3, C3, [1024]],
  21. [-1, 1, SPPF, [1024, 5]], # 9
  22. ]
  23. # YOLOv5 v6.0 head
  24. head:
  25. [[-1, 1, Conv, [512, 1, 1]],
  26. [-1, 1, nn.Upsample, [None, 2, 'nearest']],
  27. [[-1, 6], 1, Concat, [1]], # cat backbone P4
  28. [-1, 3, C3, [512, False]], # 13
  29. [-1, 1, Conv, [256, 1, 1]],
  30. [-1, 1, nn.Upsample, [None, 2, 'nearest']],
  31. [[-1, 4], 1, Concat, [1]], # cat backbone P3
  32. [-1, 3, C3, [256, False]], # 17 (P3/8-small)
  33. [-1, 1, Conv, [256, 3, 2]],
  34. [[-1, 14], 1, Concat, [1]], # cat head P4
  35. [-1, 3, C3, [512, False]], # 20 (P4/16-medium)
  36. [-1, 1, Conv, [512, 3, 2]],
  37. [[-1, 10], 1, Concat, [1]], # cat head P5
  38. [-1, 3, C3, [1024, False]], # 23 (P5/32-large)
  39. [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
  40. ]

第一步:配置识别模型参数

① IP_model.yaml(在 yolov5n.yaml 基础上进行修改)

  1. # Parameters
  2. nc: 4 # number of classes
  3. depth_multiple: 0.33 # model depth multiple
  4. width_multiple: 0.25 # layer channel multiple
  5. anchors:
  6. - [10,13, 16,30, 33,23] # P3/8
  7. - [30,61, 62,45, 59,119] # P4/16
  8. - [116,90, 156,198, 373,326] # P5/32
  9. # YOLOv5 v6.0 backbone
  10. backbone:
  11. # [from, number, module, args]
  12. [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
  13. [-1, 1, Conv, [128, 3, 2]], # 1-P2/4
  14. [-1, 3, C3, [128]],
  15. [-1, 1, Conv, [256, 3, 2]], # 3-P3/8
  16. [-1, 6, C3, [256]],
  17. [-1, 1, Conv, [512, 3, 2]], # 5-P4/16
  18. [-1, 9, C3, [512]],
  19. [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
  20. [-1, 3, C3, [1024]],
  21. [-1, 1, SPPF, [1024, 5]], # 9
  22. ]
  23. # YOLOv5 v6.0 head
  24. head:
  25. [[-1, 1, Conv, [512, 1, 1]],
  26. [-1, 1, nn.Upsample, [None, 2, 'nearest']],
  27. [[-1, 6], 1, Concat, [1]], # cat backbone P4
  28. [-1, 3, C3, [512, False]], # 13
  29. [-1, 1, Conv, [256, 1, 1]],
  30. [-1, 1, nn.Upsample, [None, 2, 'nearest']],
  31. [[-1, 4], 1, Concat, [1]], # cat backbone P3
  32. [-1, 3, C3, [256, False]], # 17 (P3/8-small)
  33. [-1, 1, Conv, [256, 3, 2]],
  34. [[-1, 14], 1, Concat, [1]], # cat head P4
  35. [-1, 3, C3, [512, False]], # 20 (P4/16-medium)
  36. [-1, 1, Conv, [512, 3, 2]],
  37. [[-1, 10], 1, Concat, [1]], # cat head P5
  38. [-1, 3, C3, [1024, False]], # 23 (P5/32-large)
  39. [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
  40. ]

② IP_parameter.yaml(模型训练以及模型保存路径等参数设置)

  1. path: D:\BaiduNetdiskWorkspace\licenses\machine_learning\yolov5\yolov5-master\V2\datasets # dataset root dir
  2. train: images/train # train images (relative to 'path') 128 images
  3. val: images/train # val images (relative to 'path') 128 images
  4. test: # test images (optional)
  5. # Classes
  6. names:
  7. 0: red
  8. 1: blue
  9. 2: stop
  10. 3: wait
  11. # Download script/URL (optional)
  12. download: https://ultralytics.com/assets/coco128.zip

第二步:编写模型训练代码

train.py

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

(十)模型推断过程的程序编写

第一部分:编写 detect.py

  1. import argparse
  2. import os
  3. import platform
  4. import sys
  5. from pathlib import Path
  6. import torch
  7. FILE = Path(__file__).resolve()
  8. ROOT = FILE.parents[0] # YOLOv5 root directory
  9. if str(ROOT) not in sys.path:
  10. sys.path.append(str(ROOT)) # add ROOT to PATH
  11. ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
  12. from models.common import DetectMultiBackend
  13. from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams
  14. from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2,
  15. increment_path, non_max_suppression, print_args, scale_boxes, strip_optimizer, xyxy2xywh)
  16. from utils.plots import Annotator, colors, save_one_box
  17. from utils.torch_utils import select_device, smart_inference_mode
  18. @smart_inference_mode()
  19. def run(
  20. weights=ROOT / r'D:\BaiduNetdiskWorkspace\licenses\machine_learning\yolov5\yolov5-master\runs\train\exp15\weights\best.pt',
  21. # model path or triton URL
  22. # source=ROOT / 'data/images', # file/dir/URL/glob/screen/0(webcam)
  23. source=ROOT / '0', # file/dir/URL/glob/screen/0(webcam)
  24. data=ROOT / 'car-parts/IP_parameter.yaml', # dataset.yaml path
  25. imgsz=(640, 640), # inference size (height, width)
  26. conf_thres=0.25, # confidence threshold
  27. iou_thres=0.45, # NMS IOU threshold
  28. max_det=1000, # maximum detections per image
  29. device='', # cuda device, i.e. 0 or 0,1,2,3 or cpu
  30. view_img=False, # show results
  31. save_txt=False, # save results to *.txt
  32. save_conf=False, # save confidences in --save-txt labels
  33. save_crop=False, # save cropped prediction boxes
  34. nosave=False, # do not save images/videos
  35. classes=None, # filter by class: --class 0, or --class 0 2 3
  36. agnostic_nms=False, # class-agnostic NMS
  37. augment=False, # augmented inference
  38. visualize=False, # visualize features
  39. update=False, # update all models
  40. project=ROOT / 'runs/detect', # save results to project/name
  41. name='exp', # save results to project/name
  42. exist_ok=False, # existing project/name ok, do not increment
  43. line_thickness=3, # bounding box thickness (pixels)
  44. hide_labels=False, # hide labels
  45. hide_conf=False, # hide confidences
  46. half=False, # use FP16 half-precision inference
  47. dnn=False, # use OpenCV DNN for ONNX inference
  48. vid_stride=1, # video frame-rate stride
  49. ):
  50. source = str(source)
  51. save_img = not nosave and not source.endswith('.txt') # save inference images
  52. is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS)
  53. is_url = source.lower().startswith(('rtsp://', 'rtmp://', 'http://', 'https://'))
  54. webcam = source.isnumeric() or source.endswith('.streams') or (is_url and not is_file)
  55. screenshot = source.lower().startswith('screen')
  56. if is_url and is_file:
  57. source = check_file(source) # download
  58. # Directories
  59. save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run
  60. (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir
  61. # Load model
  62. device = select_device(device)
  63. model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half)
  64. stride, names, pt = model.stride, model.names, model.pt
  65. imgsz = check_img_size(imgsz, s=stride) # check image size
  66. # Dataloader
  67. bs = 1 # batch_size
  68. if webcam:
  69. view_img = check_imshow(warn=True)
  70. dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride)
  71. bs = len(dataset)
  72. elif screenshot:
  73. dataset = LoadScreenshots(source, img_size=imgsz, stride=stride, auto=pt)
  74. else:
  75. dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride)
  76. vid_path, vid_writer = [None] * bs, [None] * bs
  77. # Run inference
  78. model.warmup(imgsz=(1 if pt or model.triton else bs, 3, *imgsz)) # warmup
  79. seen, windows, dt = 0, [], (Profile(), Profile(), Profile())
  80. for path, im, im0s, vid_cap, s in dataset:
  81. with dt[0]:
  82. im = torch.from_numpy(im).to(model.device)
  83. im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
  84. im /= 255 # 0 - 255 to 0.0 - 1.0
  85. if len(im.shape) == 3:
  86. im = im[None] # expand for batch dim
  87. # Inference
  88. with dt[1]:
  89. visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False
  90. pred = model(im, augment=augment, visualize=visualize)
  91. # NMS
  92. with dt[2]:
  93. pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)
  94. # Second-stage classifier (optional)
  95. # pred = utils.general.apply_classifier(pred, classifier_model, im, im0s)
  96. # Process predictions
  97. for i, det in enumerate(pred): # per image
  98. seen += 1
  99. if webcam: # batch_size >= 1
  100. p, im0, frame = path[i], im0s[i].copy(), dataset.count
  101. s += f'{i}: '
  102. else:
  103. p, im0, frame = path, im0s.copy(), getattr(dataset, 'frame', 0)
  104. p = Path(p) # to Path
  105. save_path = str(save_dir / p.name) # im.jpg
  106. txt_path = str(save_dir / 'labels' / p.stem) + ('' if dataset.mode == 'image' else f'_{frame}') # im.txt
  107. s += '%gx%g ' % im.shape[2:] # print string
  108. #####################################################################################################
  109. location_center_dir = str(save_dir) + '/location_center'
  110. if not os.path.exists(location_center_dir):
  111. os.makedirs(location_center_dir)
  112. location_center_path = location_center_dir + '\\' + str(p.stem) + (
  113. '' if dataset.mode == 'image' else f'_{frame}') # location_center.txt
  114. flocation = open(location_center_path + '.txt', 'w') # 保存检测框中点
  115. #####################################################################################################
  116. gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh
  117. imc = im0.copy() if save_crop else im0 # for save_crop
  118. annotator = Annotator(im0, line_width=line_thickness, example=str(names))
  119. if len(det):
  120. # Rescale boxes from img_size to im0 size
  121. det[:, :4] = scale_boxes(im.shape[2:], det[:, :4], im0.shape).round()
  122. # Print results
  123. for c in det[:, 5].unique():
  124. n = (det[:, 5] == c).sum() # detections per class
  125. s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string
  126. # Write results
  127. for *xyxy, conf, cls in reversed(det):
  128. #####################################################################################################
  129. x0 = (int(xyxy[0].item()) + int(xyxy[2].item())) / 2
  130. y0 = (int(xyxy[1].item()) + int(xyxy[3].item())) / 2 # 中心点坐标(x0, y0)
  131. class_index = cls # 获取属性
  132. object_name = names[int(cls)] # 获取标签名
  133. flocation.write(object_name + ': ' + str(x0) + ', ' + str(y0) + '\n')
  134. #####################################################################################################
  135. if save_txt: # Write to file
  136. xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh
  137. line = (cls, *xywh, conf) if save_conf else (cls, *xywh) # label format
  138. with open(f'{txt_path}.txt', 'a') as f:
  139. f.write(('%g ' * len(line)).rstrip() % line + '\n')
  140. if save_img or save_crop or view_img: # Add bbox to image
  141. c = int(cls) # integer class
  142. label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}')
  143. annotator.box_label(xyxy, label, color=colors(c, True))
  144. if save_crop:
  145. save_one_box(xyxy, imc, file=save_dir / 'crops' / names[c] / f'{p.stem}.jpg', BGR=True)
  146. #####################################################################################################
  147. flocation.close()
  148. #####################################################################################################
  149. # Stream results
  150. im0 = annotator.result()
  151. if view_img:
  152. if platform.system() == 'Linux' and p not in windows:
  153. windows.append(p)
  154. cv2.namedWindow(str(p), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO) # allow window resize (Linux)
  155. cv2.resizeWindow(str(p), im0.shape[1], im0.shape[0])
  156. cv2.imshow(str(p), im0)
  157. cv2.waitKey(1) # 1 millisecond
  158. # Save results (image with detections)
  159. if save_img:
  160. if dataset.mode == 'image':
  161. cv2.imwrite(save_path, im0)
  162. else: # 'video' or 'stream'
  163. if vid_path[i] != save_path: # new video
  164. vid_path[i] = save_path
  165. if isinstance(vid_writer[i], cv2.VideoWriter):
  166. vid_writer[i].release() # release previous video writer
  167. if vid_cap: # video
  168. fps = vid_cap.get(cv2.CAP_PROP_FPS)
  169. w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
  170. h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
  171. else: # stream
  172. fps, w, h = 30, im0.shape[1], im0.shape[0]
  173. save_path = str(Path(save_path).with_suffix('.mp4')) # force *.mp4 suffix on results videos
  174. vid_writer[i] = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
  175. vid_writer[i].write(im0)
  176. # Print time (inference-only)
  177. LOGGER.info(f"{s}{'' if len(det) else '(no detections), '}{dt[1].dt * 1E3:.1f}ms")
  178. # Print results
  179. t = tuple(x.t / seen * 1E3 for x in dt) # speeds per image
  180. LOGGER.info(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {(1, 3, *imgsz)}' % t)
  181. if save_txt or save_img:
  182. s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ''
  183. LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}")
  184. if update:
  185. strip_optimizer(weights[0]) # update model (to fix SourceChangeWarning)
  186. def parse_opt():
  187. parser = argparse.ArgumentParser()
  188. # parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolov5s.pt', help='model path or triton URL')
  189. parser.add_argument('--weights', nargs='+', type=str,
  190. default=ROOT / r'D:\BaiduNetdiskWorkspace\licenses\machine_learning\yolov5\yolov5-master\runs\train\exp3\weights\best.pt',
  191. help='model path or triton URL')
  192. # parser.add_argument('--source', type=str, default=ROOT / 'data/images', help='file/dir/URL/glob/screen/0(webcam)')
  193. parser.add_argument('--source', type=str,
  194. default=ROOT / r'D:\BaiduNetdiskWorkspace\licenses\machine_learning\yolov5\yolov5-master\V2\datasets\images\train',
  195. help='file/dir/URL/glob/screen/0(webcam)')
  196. parser.add_argument('--data', type=str, default=ROOT / 'data/coco128.yaml', help='(optional) dataset.yaml path')
  197. parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[640], help='inference size h,w')
  198. parser.add_argument('--conf-thres', type=float, default=0.25, help='confidence threshold')
  199. parser.add_argument('--iou-thres', type=float, default=0, help='NMS IoU threshold')
  200. parser.add_argument('--max-det', type=int, default=10, help='maximum detections per image')
  201. parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
  202. parser.add_argument('--view-img', action='store_true', help='show results')
  203. parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
  204. parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
  205. parser.add_argument('--save-crop', action='store_true', help='save cropped prediction boxes')
  206. parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
  207. parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --classes 0, or --classes 0 2 3')
  208. parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
  209. parser.add_argument('--augment', action='store_true', help='augmented inference')
  210. parser.add_argument('--visualize', action='store_true', help='visualize features')
  211. parser.add_argument('--update', action='store_true', help='update all models')
  212. parser.add_argument('--project', default=ROOT / 'runs/detect', help='save results to project/name')
  213. parser.add_argument('--name', default='exp', help='save results to project/name')
  214. parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
  215. parser.add_argument('--line-thickness', default=3, type=int, help='bounding box thickness (pixels)')
  216. parser.add_argument('--hide-labels', default=False, action='store_true', help='hide labels')
  217. parser.add_argument('--hide-conf', default=False, action='store_true', help='hide confidences')
  218. parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference')
  219. parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference')
  220. parser.add_argument('--vid-stride', type=int, default=1, help='video frame-rate stride')
  221. opt = parser.parse_args()
  222. opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1 # expand
  223. print_args(vars(opt))
  224. return opt
  225. def main(opt):
  226. check_requirements(exclude=('tensorboard', 'thop'))
  227. run(**vars(opt))
  228. if __name__ == '__main__':
  229. opt = parse_opt()
  230. main(opt)

第二部分:在无人车终端运行代码,调用物理摄像头,查看具体效果

(十一)使用onnx及TensorRT将模型进行推理加速

第一步:将pt模型转化为onnx模型

① 安装onnx

  1. pip install onnx
  1. pip install onnxruntime

② 使用 export.py 导出模型为ONNX

  1. python export.py --weights D:\BaiduNetdiskWorkspace\licenses\machine_learning\yolov5\yolov5-master\runs\train\exp3\weights\best.pt --img-size 640 --batch-size 1 --include onnx

第二步:TensorRT环境安装及配置

① 下载对应TensorRT版本

  1. https://developer.nvidia.com/nvidia-tensorrt-8x-download

② 解压 TensorRT

③ 配置环境变量

将TensorRT解压位置\lib 加入系统环境变量

将TensorRT解压位置\lib下的dll文件复制到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.7\bin目录下

④ 测试示例代码

用VS2019打开sampleOnnxMNIST示例(D:\TensorRT\TensorRT-8.6.1.6.Windows10.x86_64.cuda-11.8\TensorRT-8.6.1.6\samples\sampleOnnxMNIST)

将D:\TensorRT\TensorRT-8.6.1.6.Windows10.x86_64.cuda-11.8\TensorRT-8.6.1.6\lib加入 VC++目录–>可执行文件目录

将D:\TensorRT\TensorRT-8.6.1.6.Windows10.x86_64.cuda-11.8\TensorRT-8.6.1.6\include加入C/C++ --> 常规 --> 附加包含目录

将D:\TensorRT\TensorRT-8.6.1.6.Windows10.x86_64.cuda-11.8\TensorRT-8.6.1.6\lib加入 VC++目录–>库目录

将nvinfer.lib、nvinfer_plugin.lib、nvonnxparser.lib和nvparsers.lib加入链接器–>输入–>附加依赖项

⑤ 安装 pycuda

https://www.lfd.uci.edu/~gohlke/pythonlibs/?cm_mc_uid=08085305845514542921829&cm_mc_sid_50200000=1456395916&cm_mc_uid=08085305845514542921829&cm_mc_sid_50200000=1456395916#pycuda

  1. pip install "D:\pycuda\pycuda-2022.1+cuda116-cp38-cp38-win_amd64.whl"

⑥ python 环境配置 TensorRT

  1. pip install "D:\TensorRT\TensorRT-8.6.1.6.Windows10.x86_64.cuda-11.8\TensorRT-8.6.1.6\python\tensorrt-8.6.1-cp38-none-win_amd64.whl"

⑦ python 环境测试

  1. python "D:\TensorRT\TensorRT-8.6.1.6.Windows10.x86_64.cuda-11.8\TensorRT-8.6.1.6\samples\python\network_api_pytorch_mnist\sample.py"

第三步:使用 TensorRT 编译 onnx 文件,转换成 .trt 后缀文件

D:\TensorRT\TensorRT-8.6.1.6.Windows10.x86_64.cuda-11.8\TensorRT-8.6.1.6\bin\trtexec.exe --onnx=D:\BaiduNetdiskWorkspace\licenses\machine_learning\yolov5\yolov5-master\runs\train\exp3\weights\best.onnx --saveEngine=D:\BaiduNetdiskWorkspace\licenses\machine_learning\yolov5\yolov5-master\runs\train\exp3\weights\best.trt --buildOnly

第四步:查看训练图片的维度

check_picture.py

  1. import cv2
  2. # 读取图像
  3. image_path = r'D:\BaiduNetdiskWorkspace\licenses\machine_learning\yolov5\yolov5-master\V2\datasets\images\train\2023-09-18-160239.jpg '
  4. image = cv2.imread(image_path)
  5. # 检查图像形状
  6. image_shape = image.shape
  7. # 打印图像形状
  8. print("图像形状:", image_shape)
  9. # 如果您只关心图像的高度,宽度和通道数,可以使用以下方式获取
  10. height, width, channels = image_shape
  11. print("高度:", height)
  12. print("宽度:", width)
  13. print("通道数:", channels)

第五步:编写加速推理脚本

compare.py

  1. import numpy as np
  2. import pycuda.driver as cuda
  3. import pycuda.autoinit
  4. import tensorrt as trt
  5. import cv2
  6. import time
  7. # 加载.trt文件
  8. trt_file_path = r'D:\BaiduNetdiskWorkspace\licenses\machine_learning\yolov5\yolov5-master\runs\train\exp3\weights\best.trt'
  9. with open(trt_file_path, 'rb') as f, trt.Runtime(trt.Logger(trt.Logger.WARNING)) as runtime:
  10. engine = runtime.deserialize_cuda_engine(f.read())
  11. # 创建执行上下文
  12. context = engine.create_execution_context()
  13. # 分配输入和输出内存
  14. input_size = trt.volume(context.get_binding_shape(0))
  15. output_size = trt.volume(context.get_binding_shape(1))
  16. # 在GPU上分配内存
  17. input_host = cuda.pagelocked_empty(input_size, dtype=np.float32)
  18. output_host = cuda.pagelocked_empty(output_size, dtype=np.float32)
  19. input_device = cuda.mem_alloc(input_host.nbytes)
  20. output_device = cuda.mem_alloc(output_host.nbytes)
  21. # 准备输入数据
  22. # 读取彩色图像
  23. image_path = r'D:\BaiduNetdiskWorkspace\licenses\machine_learning\yolov5\yolov5-master\V2\datasets\images\train\2023-09-18-160239.jpg '
  24. image = cv2.imread(image_path)
  25. # 调整数据形状以匹配模型期望的输入形状
  26. input_data = image.astype(np.float32) / 255.0 # 归一化(假设模型期望的输入范围是 [0, 1])
  27. # 使用cv2.resize调整图像大小
  28. resized_image = cv2.resize(input_data, (640, 640))
  29. input_data = np.transpose(resized_image, (2, 0, 1)) # 将通道移到正确的位置
  30. input_data = np.expand_dims(input_data, axis=0) # 添加批处理维度
  31. # 确保输入数据的长度与模型期望的输入大小一致
  32. if input_data.size != input_size:
  33. raise ValueError(f"Input data size ({input_data.size}) does not match the expected input size ({input_size})")
  34. np.copyto(input_host, input_data.ravel())
  35. cuda.memcpy_htod(input_device, input_host)
  36. # 计时开始
  37. start_time = time.time()
  38. # 执行推理
  39. context.execute_v2(bindings=[int(input_device), int(output_device)])
  40. # 计时结束
  41. end_time = time.time()
  42. # 获取输出
  43. cuda.memcpy_dtoh(output_host, output_device)
  44. # 处理输出数据
  45. result = output_host.reshape(context.get_binding_shape(1))
  46. # 打印结果
  47. print(result)
  48. # 打印推理时间
  49. inference_time = end_time - start_time
  50. print(f"Inference time: {inference_time} seconds")

第六步:对比测试

加速前

加速后

(十二)问题汇总

(1)error MSB8036:找不到 Windows SDK 版本......

参考链接

error MSB8036:找不到 Windows SDK 版本···_microsoft.cpp.windowssdk.targe ts(46,5): error msb-CSDN博客

(2)too many values to unpack

参考链接

too many values to unpack (expected 4)_UC_Gundam的博客-CSDN博客

(3)运行示例时,提示找不到 MNIST数据

参考链接

TensorRT之安装与测试(Windows和Linux环境下安装TensorRT)_判断tensorrt是否可以正常使用-CSDN博客

(4)加载 libnvinfer.so.7报错

参考链接

TensorRT之安装与测试(Windows和Linux环境下安装TensorRT)_判断tensorrt是否可以正常使用-CSDN博客

原文链接:https://www.cnblogs.com/zylyehuo/p/17832064.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号