• 基于深度学习的监控场景下是否佩戴安全帽检测(yolov5)


    内容:基于目标检测对图像中的人员是否佩戴安全帽进行检测

    具体要求:1) 使用Python编程语言,建议使用深度学习框架PyTorch;

    2) 完成自定义数据集的制作,基于目标检测方法在数据集上完成训练和验证,可使用开源框架;

    3) 使用数据集外的图像数据进行验证,对图像中的行人是否佩戴安全帽进行检测,检测到有人员未佩戴安全帽时改变人员检测框颜色,起到告警作用;

    环境 

    PyTorch  1.8.1

    Python  3.8

    Cuda  11.1

    RTX 2080 Ti * 1

    显存:11GB

    首先先给大家推荐一个GPU租用平台,关于为什么笔者本人会知道这种类型的平台,那必然是笔者我见多识广(说多了都是泪),你看我这破电脑运行的有多慢!!!(要我连跑60小时,达咩)

    6c76cea5a21247e5bca82f07e1f1b1e6.png

    好了,言归正传,今天给大家推荐的平台是AutoDL

    附上链接:AutoDL-品质GPU租用平台-租GPU就上AutoDL 

    价钱低,关键是注册就有10元体验券,像笔者租了个RTX 2080的卡,一小时0.85元,跑个简单的深度学习还是绰绰有余的。

    eba4440a22d445de99a9e605985b1a0c.png

    还可在其后方进行环境的配置,如图(我用的是pytorch1.8.1,python3.8,cuda11.1)

    072952aa752a48d19807dece8affbd89.png

    进入控制台后,选择jupyterlab进入

    c5a70ddcf1654352bd13927e951060b3.png

    可以先看看pytorch是否安装成功,进入终端打开输入python

    1. python
    2. import torch
    3. torch.__version__

    fdc8bc5d825a45188a391d460b71150e.png

    bea4b81748c1435db58bf8ce0d612ace.png

    基于Yolov5训练模型

    先把yolov5的框架下载出来,可以去github直接下,也可以用我上传的网盘资源

    链接:https://pan.baidu.com/s/1KIIjNOf449ueD8OdRJUH9g 
    提取码:0329

    通过jupyter内的文件上传,即可将下载好的zip文件上传,在终端找到对应压缩包,执行下述指令,即可解压,随后进入yolov5文件夹

     unzip yolov5-5.0.zip
    cd yolov5-5.0

    790e99537bf447cea411be22bf7a352b.png

    右击新建文件夹VOCdevkit,随后进入文件夹

    cf9cbdfc88f7429e9a18e2e951154c2d.png

    老方法,将数据集上传至文件包内

    196788b7b8a64e5d9d1f0049577b85b4.png

    数据集已全部标注,可直接食用

    链接:https://pan.baidu.com/s/1L4t9MQj48pT71mGaQlO4Tw 
    提取码:0329

    标注方法(可跳过):

    (本机)使用labelimg进行标注

    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple labelimg
    

    8e4e5aad7cd24af4814b6914bc77037c.png

    输入labelimg,打开界面

    2f5d1ea381e24b728abb68d44729d68c.png

    点击 Open Dir 选择数据集的保存路径,即VOCdevkit文件夹下的JPEGImages文件

    点击Change Save Dir选择保存标注好的文件路径,选择VOCdevkit文件夹下的Annotations文件

    View中选中Auto Save mode,即可自动保存

    选中Create RectBox,框选出要识别的物体(如:hat),标注相应类别

    快捷键:A上一张,D下一张

    5cc0bfad5643409fbf9195f15bd232c5.png

    数据集处理(接着上两步标注过程前)

    同刚开始处理zip压缩包,unzip数据集即可

    unzip VOC2007.zip

    回退到yolov5主界面,进行环境配置

    pip install -r requirements.txt
    

    配置预训练权重,用yolov5s.pt即可

    链接:https://pan.baidu.com/s/1oopsiHUEclmO2U47FiuSJQ 
    提取码:0329

    下载完后,老方法上传至jupyter

    d1e2324545934aad8d050b816f21518c.png

    划分训练集和测试集

    新建prepare.py文件

    yolov5-5.0/prepare.py将VOC标签格式.xml转yolo格式.txt进行训练集和测试集的划分

    代码如下:

    1. import xml.etree.ElementTree as ET
    2. import pickle
    3. import os
    4. from os import listdir, getcwd
    5. from os.path import join
    6. import random
    7. from shutil import copyfile
    8. classes = ["hat","person"]
    9. TRAIN_RATIO = 80
    10. def clear_hidden_files(path):
    11. dir_list = os.listdir(path)
    12. for i in dir_list:
    13. abspath = os.path.join(os.path.abspath(path), i)
    14. if os.path.isfile(abspath):
    15. if i.startswith("._"):
    16. os.remove(abspath)
    17. else:
    18. clear_hidden_files(abspath)
    19. def convert(size, box):
    20. dw = 1./size[0]
    21. dh = 1./size[1]
    22. x = (box[0] + box[1])/2.0
    23. y = (box[2] + box[3])/2.0
    24. w = box[1] - box[0]
    25. h = box[3] - box[2]
    26. x = x*dw
    27. w = w*dw
    28. y = y*dh
    29. h = h*dh
    30. return (x,y,w,h)
    31. def convert_annotation(image_id):
    32. in_file = open('VOCdevkit/VOC2007/Annotations/%s.xml' %image_id)
    33. out_file = open('VOCdevkit/VOC2007/YOLOLabels/%s.txt' %image_id, 'w')
    34. tree=ET.parse(in_file)
    35. root = tree.getroot()
    36. size = root.find('size')
    37. w = int(size.find('width').text)
    38. h = int(size.find('height').text)
    39. for obj in root.iter('object'):
    40. difficult = obj.find('difficult').text
    41. cls = obj.find('name').text
    42. if cls not in classes or int(difficult) == 1:
    43. continue
    44. cls_id = classes.index(cls)
    45. xmlbox = obj.find('bndbox')
    46. b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
    47. bb = convert((w,h), b)
    48. out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
    49. in_file.close()
    50. out_file.close()
    51. wd = os.getcwd()
    52. wd = os.getcwd()
    53. data_base_dir = os.path.join(wd, "VOCdevkit/")
    54. if not os.path.isdir(data_base_dir):
    55. os.mkdir(data_base_dir)
    56. work_sapce_dir = os.path.join(data_base_dir, "VOC2007/")
    57. if not os.path.isdir(work_sapce_dir):
    58. os.mkdir(work_sapce_dir)
    59. annotation_dir = os.path.join(work_sapce_dir, "Annotations/")
    60. if not os.path.isdir(annotation_dir):
    61. os.mkdir(annotation_dir)
    62. clear_hidden_files(annotation_dir)
    63. image_dir = os.path.join(work_sapce_dir, "JPEGImages/")
    64. if not os.path.isdir(image_dir):
    65. os.mkdir(image_dir)
    66. clear_hidden_files(image_dir)
    67. yolo_labels_dir = os.path.join(work_sapce_dir, "YOLOLabels/")
    68. if not os.path.isdir(yolo_labels_dir):
    69. os.mkdir(yolo_labels_dir)
    70. clear_hidden_files(yolo_labels_dir)
    71. yolov5_images_dir = os.path.join(data_base_dir, "images/")
    72. if not os.path.isdir(yolov5_images_dir):
    73. os.mkdir(yolov5_images_dir)
    74. clear_hidden_files(yolov5_images_dir)
    75. yolov5_labels_dir = os.path.join(data_base_dir, "labels/")
    76. if not os.path.isdir(yolov5_labels_dir):
    77. os.mkdir(yolov5_labels_dir)
    78. clear_hidden_files(yolov5_labels_dir)
    79. yolov5_images_train_dir = os.path.join(yolov5_images_dir, "train/")
    80. if not os.path.isdir(yolov5_images_train_dir):
    81. os.mkdir(yolov5_images_train_dir)
    82. clear_hidden_files(yolov5_images_train_dir)
    83. yolov5_images_test_dir = os.path.join(yolov5_images_dir, "val/")
    84. if not os.path.isdir(yolov5_images_test_dir):
    85. os.mkdir(yolov5_images_test_dir)
    86. clear_hidden_files(yolov5_images_test_dir)
    87. yolov5_labels_train_dir = os.path.join(yolov5_labels_dir, "train/")
    88. if not os.path.isdir(yolov5_labels_train_dir):
    89. os.mkdir(yolov5_labels_train_dir)
    90. clear_hidden_files(yolov5_labels_train_dir)
    91. yolov5_labels_test_dir = os.path.join(yolov5_labels_dir, "val/")
    92. if not os.path.isdir(yolov5_labels_test_dir):
    93. os.mkdir(yolov5_labels_test_dir)
    94. clear_hidden_files(yolov5_labels_test_dir)
    95. train_file = open(os.path.join(wd, "yolov5_train.txt"), 'w')
    96. test_file = open(os.path.join(wd, "yolov5_val.txt"), 'w')
    97. train_file.close()
    98. test_file.close()
    99. train_file = open(os.path.join(wd, "yolov5_train.txt"), 'a')
    100. test_file = open(os.path.join(wd, "yolov5_val.txt"), 'a')
    101. list_imgs = os.listdir(image_dir) # list image files
    102. prob = random.randint(1, 100)
    103. print("Probability: %d" % prob)
    104. for i in range(0,len(list_imgs)):
    105. path = os.path.join(image_dir,list_imgs[i])
    106. if os.path.isfile(path):
    107. image_path = image_dir + list_imgs[i]
    108. voc_path = list_imgs[i]
    109. (nameWithoutExtention, extention) = os.path.splitext(os.path.basename(image_path))
    110. (voc_nameWithoutExtention, voc_extention) = os.path.splitext(os.path.basename(voc_path))
    111. annotation_name = nameWithoutExtention + '.xml'
    112. annotation_path = os.path.join(annotation_dir, annotation_name)
    113. label_name = nameWithoutExtention + '.txt'
    114. label_path = os.path.join(yolo_labels_dir, label_name)
    115. prob = random.randint(1, 100)
    116. print("Probability: %d" % prob)
    117. if(prob < TRAIN_RATIO): # train dataset
    118. if os.path.exists(annotation_path):
    119. train_file.write(image_path + '\n')
    120. convert_annotation(nameWithoutExtention) # convert label
    121. copyfile(image_path, yolov5_images_train_dir + voc_path)
    122. copyfile(label_path, yolov5_labels_train_dir + label_name)
    123. else: # test dataset
    124. if os.path.exists(annotation_path):
    125. test_file.write(image_path + '\n')
    126. convert_annotation(nameWithoutExtention) # convert label
    127. copyfile(image_path, yolov5_images_test_dir + voc_path)
    128. copyfile(label_path, yolov5_labels_test_dir + label_name)
    129. train_file.close()
    130. test_file.close()

    运行后产生文件夹:
    yolov5-5.0/VOCdevkit/images: 分为trainval两个文件夹,存放图片
    yolov5-5.0/VOCdevkit/labels: 分为trainval两个文件夹,存放.txt label

    4b7d4e3c95094c29a4de6c84e59db397.png

    配置文件的编写

    数据配置文件data/hat.yaml

    在data文件夹内新建一个hat.yaml 

    1. train: /root/yolov5-5.0/VOCdevkit/images/train/ # 16551 images
    2. val: /root/yolov5-5.0/VOCdevkit/images/val/ # 4952 images
    3. # Classes
    4. nc: 2 # number of classes
    5. names: ["hat","person"] # class names

    模型配置文件models/yolov5s_hat.yaml

    在models文件夹内,找到yolov5x.yaml文件夹,复制后重命名为yolov5s_hat.yaml

    修改nc即可

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

    训练模型

    先将yolov5-5.0/train.py(456行开始)中的部分内容配置

    一般是修改这些,也可直接复制下方代码替换

    d54201b018904af9b9e35dae8aa723fe.png

    1. import argparse
    2. import logging
    3. import math
    4. import os
    5. import random
    6. import time
    7. from copy import deepcopy
    8. from pathlib import Path
    9. from threading import Thread
    10. import numpy as np
    11. import torch.distributed as dist
    12. import torch.nn as nn
    13. import torch.nn.functional as F
    14. import torch.optim as optim
    15. import torch.optim.lr_scheduler as lr_scheduler
    16. import torch.utils.data
    17. import yaml
    18. from torch.cuda import amp
    19. from torch.nn.parallel import DistributedDataParallel as DDP
    20. from torch.utils.tensorboard import SummaryWriter
    21. from tqdm import tqdm
    22. import test # import test.py to get mAP after each epoch
    23. from models.experimental import attempt_load
    24. from models.yolo import Model
    25. from utils.autoanchor import check_anchors
    26. from utils.datasets import create_dataloader
    27. from utils.general import labels_to_class_weights, increment_path, labels_to_image_weights, init_seeds, \
    28. fitness, strip_optimizer, get_latest_run, check_dataset, check_file, check_git_status, check_img_size, \
    29. check_requirements, print_mutation, set_logging, one_cycle, colorstr
    30. from utils.google_utils import attempt_download
    31. from utils.loss import ComputeLoss
    32. from utils.plots import plot_images, plot_labels, plot_results, plot_evolution
    33. from utils.torch_utils import ModelEMA, select_device, intersect_dicts, torch_distributed_zero_first, is_parallel
    34. from utils.wandb_logging.wandb_utils import WandbLogger, check_wandb_resume
    35. logger = logging.getLogger(__name__)
    36. def train(hyp, opt, device, tb_writer=None):
    37. logger.info(colorstr('hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in hyp.items()))
    38. save_dir, epochs, batch_size, total_batch_size, weights, rank = \
    39. Path(opt.save_dir), opt.epochs, opt.batch_size, opt.total_batch_size, opt.weights, opt.global_rank
    40. # Directories
    41. wdir = save_dir / 'weights'
    42. wdir.mkdir(parents=True, exist_ok=True) # make dir
    43. last = wdir / 'last.pt'
    44. best = wdir / 'best.pt'
    45. results_file = save_dir / 'results.txt'
    46. # Save run settings
    47. with open(save_dir / 'hyp.yaml', 'w') as f:
    48. yaml.dump(hyp, f, sort_keys=False)
    49. with open(save_dir / 'opt.yaml', 'w') as f:
    50. yaml.dump(vars(opt), f, sort_keys=False)
    51. # Configure
    52. plots = not opt.evolve # create plots
    53. cuda = device.type != 'cpu'
    54. init_seeds(2 + rank)
    55. with open(opt.data) as f:
    56. data_dict = yaml.load(f, Loader=yaml.SafeLoader) # data dict
    57. is_coco = opt.data.endswith('coco.yaml')
    58. # Logging- Doing this before checking the dataset. Might update data_dict
    59. loggers = {'wandb': None} # loggers dict
    60. if rank in [-1, 0]:
    61. opt.hyp = hyp # add hyperparameters
    62. run_id = torch.load(weights).get('wandb_id') if weights.endswith('.pt') and os.path.isfile(weights) else None
    63. wandb_logger = WandbLogger(opt, Path(opt.save_dir).stem, run_id, data_dict)
    64. loggers['wandb'] = wandb_logger.wandb
    65. data_dict = wandb_logger.data_dict
    66. if wandb_logger.wandb:
    67. weights, epochs, hyp = opt.weights, opt.epochs, opt.hyp # WandbLogger might update weights, epochs if resuming
    68. nc = 1 if opt.single_cls else int(data_dict['nc']) # number of classes
    69. names = ['item'] if opt.single_cls and len(data_dict['names']) != 1 else data_dict['names'] # class names
    70. assert len(names) == nc, '%g names found for nc=%g dataset in %s' % (len(names), nc, opt.data) # check
    71. # Model
    72. pretrained = weights.endswith('.pt')
    73. if pretrained:
    74. with torch_distributed_zero_first(rank):
    75. attempt_download(weights) # download if not found locally
    76. ckpt = torch.load(weights, map_location=device) # load checkpoint
    77. model = Model(opt.cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
    78. exclude = ['anchor'] if (opt.cfg or hyp.get('anchors')) and not opt.resume else [] # exclude keys
    79. state_dict = ckpt['model'].float().state_dict() # to FP32
    80. state_dict = intersect_dicts(state_dict, model.state_dict(), exclude=exclude) # intersect
    81. model.load_state_dict(state_dict, strict=False) # load
    82. logger.info('Transferred %g/%g items from %s' % (len(state_dict), len(model.state_dict()), weights)) # report
    83. else:
    84. model = Model(opt.cfg, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
    85. with torch_distributed_zero_first(rank):
    86. check_dataset(data_dict) # check
    87. train_path = data_dict['train']
    88. test_path = data_dict['val']
    89. # Freeze
    90. freeze = [] # parameter names to freeze (full or partial)
    91. for k, v in model.named_parameters():
    92. v.requires_grad = True # train all layers
    93. if any(x in k for x in freeze):
    94. print('freezing %s' % k)
    95. v.requires_grad = False
    96. # Optimizer
    97. nbs = 64 # nominal batch size
    98. accumulate = max(round(nbs / total_batch_size), 1) # accumulate loss before optimizing
    99. hyp['weight_decay'] *= total_batch_size * accumulate / nbs # scale weight_decay
    100. logger.info(f"Scaled weight_decay = {hyp['weight_decay']}")
    101. pg0, pg1, pg2 = [], [], [] # optimizer parameter groups
    102. for k, v in model.named_modules():
    103. if hasattr(v, 'bias') and isinstance(v.bias, nn.Parameter):
    104. pg2.append(v.bias) # biases
    105. if isinstance(v, nn.BatchNorm2d):
    106. pg0.append(v.weight) # no decay
    107. elif hasattr(v, 'weight') and isinstance(v.weight, nn.Parameter):
    108. pg1.append(v.weight) # apply decay
    109. if opt.adam:
    110. optimizer = optim.Adam(pg0, lr=hyp['lr0'], betas=(hyp['momentum'], 0.999)) # adjust beta1 to momentum
    111. else:
    112. optimizer = optim.SGD(pg0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True)
    113. optimizer.add_param_group({'params': pg1, 'weight_decay': hyp['weight_decay']}) # add pg1 with weight_decay
    114. optimizer.add_param_group({'params': pg2}) # add pg2 (biases)
    115. logger.info('Optimizer groups: %g .bias, %g conv.weight, %g other' % (len(pg2), len(pg1), len(pg0)))
    116. del pg0, pg1, pg2
    117. # Scheduler https://arxiv.org/pdf/1812.01187.pdf
    118. # https://pytorch.org/docs/stable/_modules/torch/optim/lr_scheduler.html#OneCycleLR
    119. if opt.linear_lr:
    120. lf = lambda x: (1 - x / (epochs - 1)) * (1.0 - hyp['lrf']) + hyp['lrf'] # linear
    121. else:
    122. lf = one_cycle(1, hyp['lrf'], epochs) # cosine 1->hyp['lrf']
    123. scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)
    124. # plot_lr_scheduler(optimizer, scheduler, epochs)
    125. # EMA
    126. ema = ModelEMA(model) if rank in [-1, 0] else None
    127. # Resume
    128. start_epoch, best_fitness = 0, 0.0
    129. if pretrained:
    130. # Optimizer
    131. if ckpt['optimizer'] is not None:
    132. optimizer.load_state_dict(ckpt['optimizer'])
    133. best_fitness = ckpt['best_fitness']
    134. # EMA
    135. if ema and ckpt.get('ema'):
    136. ema.ema.load_state_dict(ckpt['ema'].float().state_dict())
    137. ema.updates = ckpt['updates']
    138. # Results
    139. if ckpt.get('training_results') is not None:
    140. results_file.write_text(ckpt['training_results']) # write results.txt
    141. # Epochs
    142. start_epoch = ckpt['epoch'] + 1
    143. if opt.resume:
    144. assert start_epoch > 0, '%s training to %g epochs is finished, nothing to resume.' % (weights, epochs)
    145. if epochs < start_epoch:
    146. logger.info('%s has been trained for %g epochs. Fine-tuning for %g additional epochs.' %
    147. (weights, ckpt['epoch'], epochs))
    148. epochs += ckpt['epoch'] # finetune additional epochs
    149. del ckpt, state_dict
    150. # Image sizes
    151. gs = max(int(model.stride.max()), 32) # grid size (max stride)
    152. nl = model.model[-1].nl # number of detection layers (used for scaling hyp['obj'])
    153. imgsz, imgsz_test = [check_img_size(x, gs) for x in opt.img_size] # verify imgsz are gs-multiples
    154. # DP mode
    155. if cuda and rank == -1 and torch.cuda.device_count() > 1:
    156. model = torch.nn.DataParallel(model)
    157. # SyncBatchNorm
    158. if opt.sync_bn and cuda and rank != -1:
    159. model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model).to(device)
    160. logger.info('Using SyncBatchNorm()')
    161. # Trainloader
    162. dataloader, dataset = create_dataloader(train_path, imgsz, batch_size, gs, opt,
    163. hyp=hyp, augment=True, cache=opt.cache_images, rect=opt.rect, rank=rank,
    164. world_size=opt.world_size, workers=opt.workers,
    165. image_weights=opt.image_weights, quad=opt.quad, prefix=colorstr('train: '))
    166. mlc = np.concatenate(dataset.labels, 0)[:, 0].max() # max label class
    167. nb = len(dataloader) # number of batches
    168. assert mlc < nc, 'Label class %g exceeds nc=%g in %s. Possible class labels are 0-%g' % (mlc, nc, opt.data, nc - 1)
    169. # Process 0
    170. if rank in [-1, 0]:
    171. testloader = create_dataloader(test_path, imgsz_test, batch_size * 2, gs, opt, # testloader
    172. hyp=hyp, cache=opt.cache_images and not opt.notest, rect=True, rank=-1,
    173. world_size=opt.world_size, workers=opt.workers,
    174. pad=0.5, prefix=colorstr('val: '))[0]
    175. if not opt.resume:
    176. labels = np.concatenate(dataset.labels, 0)
    177. c = torch.tensor(labels[:, 0]) # classes
    178. # cf = torch.bincount(c.long(), minlength=nc) + 1. # frequency
    179. # model._initialize_biases(cf.to(device))
    180. if plots:
    181. plot_labels(labels, names, save_dir, loggers)
    182. if tb_writer:
    183. tb_writer.add_histogram('classes', c, 0)
    184. # Anchors
    185. if not opt.noautoanchor:
    186. check_anchors(dataset, model=model, thr=hyp['anchor_t'], imgsz=imgsz)
    187. model.half().float() # pre-reduce anchor precision
    188. # DDP mode
    189. if cuda and rank != -1:
    190. model = DDP(model, device_ids=[opt.local_rank], output_device=opt.local_rank,
    191. # nn.MultiheadAttention incompatibility with DDP https://github.com/pytorch/pytorch/issues/26698
    192. find_unused_parameters=any(isinstance(layer, nn.MultiheadAttention) for layer in model.modules()))
    193. # Model parameters
    194. hyp['box'] *= 3. / nl # scale to layers
    195. hyp['cls'] *= nc / 80. * 3. / nl # scale to classes and layers
    196. hyp['obj'] *= (imgsz / 640) ** 2 * 3. / nl # scale to image size and layers
    197. hyp['label_smoothing'] = opt.label_smoothing
    198. model.nc = nc # attach number of classes to model
    199. model.hyp = hyp # attach hyperparameters to model
    200. model.gr = 1.0 # iou loss ratio (obj_loss = 1.0 or iou)
    201. model.class_weights = labels_to_class_weights(dataset.labels, nc).to(device) * nc # attach class weights
    202. model.names = names
    203. # Start training
    204. t0 = time.time()
    205. nw = max(round(hyp['warmup_epochs'] * nb), 1000) # number of warmup iterations, max(3 epochs, 1k iterations)
    206. # nw = min(nw, (epochs - start_epoch) / 2 * nb) # limit warmup to < 1/2 of training
    207. maps = np.zeros(nc) # mAP per class
    208. results = (0, 0, 0, 0, 0, 0, 0) # P, R, mAP@.5, mAP@.5-.95, val_loss(box, obj, cls)
    209. scheduler.last_epoch = start_epoch - 1 # do not move
    210. scaler = amp.GradScaler(enabled=cuda)
    211. compute_loss = ComputeLoss(model) # init loss class
    212. logger.info(f'Image sizes {imgsz} train, {imgsz_test} test\n'
    213. f'Using {dataloader.num_workers} dataloader workers\n'
    214. f'Logging results to {save_dir}\n'
    215. f'Starting training for {epochs} epochs...')
    216. for epoch in range(start_epoch, epochs): # epoch ------------------------------------------------------------------
    217. model.train()
    218. # Update image weights (optional)
    219. if opt.image_weights:
    220. # Generate indices
    221. if rank in [-1, 0]:
    222. cw = model.class_weights.cpu().numpy() * (1 - maps) ** 2 / nc # class weights
    223. iw = labels_to_image_weights(dataset.labels, nc=nc, class_weights=cw) # image weights
    224. dataset.indices = random.choices(range(dataset.n), weights=iw, k=dataset.n) # rand weighted idx
    225. # Broadcast if DDP
    226. if rank != -1:
    227. indices = (torch.tensor(dataset.indices) if rank == 0 else torch.zeros(dataset.n)).int()
    228. dist.broadcast(indices, 0)
    229. if rank != 0:
    230. dataset.indices = indices.cpu().numpy()
    231. # Update mosaic border
    232. # b = int(random.uniform(0.25 * imgsz, 0.75 * imgsz + gs) // gs * gs)
    233. # dataset.mosaic_border = [b - imgsz, -b] # height, width borders
    234. mloss = torch.zeros(4, device=device) # mean losses
    235. if rank != -1:
    236. dataloader.sampler.set_epoch(epoch)
    237. pbar = enumerate(dataloader)
    238. logger.info(('\n' + '%10s' * 8) % ('Epoch', 'gpu_mem', 'box', 'obj', 'cls', 'total', 'labels', 'img_size'))
    239. if rank in [-1, 0]:
    240. pbar = tqdm(pbar, total=nb) # progress bar
    241. optimizer.zero_grad()
    242. for i, (imgs, targets, paths, _) in pbar: # batch -------------------------------------------------------------
    243. ni = i + nb * epoch # number integrated batches (since train start)
    244. imgs = imgs.to(device, non_blocking=True).float() / 255.0 # uint8 to float32, 0-255 to 0.0-1.0
    245. # Warmup
    246. if ni <= nw:
    247. xi = [0, nw] # x interp
    248. # model.gr = np.interp(ni, xi, [0.0, 1.0]) # iou loss ratio (obj_loss = 1.0 or iou)
    249. accumulate = max(1, np.interp(ni, xi, [1, nbs / total_batch_size]).round())
    250. for j, x in enumerate(optimizer.param_groups):
    251. # bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0
    252. x['lr'] = np.interp(ni, xi, [hyp['warmup_bias_lr'] if j == 2 else 0.0, x['initial_lr'] * lf(epoch)])
    253. if 'momentum' in x:
    254. x['momentum'] = np.interp(ni, xi, [hyp['warmup_momentum'], hyp['momentum']])
    255. # Multi-scale
    256. if opt.multi_scale:
    257. sz = random.randrange(imgsz * 0.5, imgsz * 1.5 + gs) // gs * gs # size
    258. sf = sz / max(imgs.shape[2:]) # scale factor
    259. if sf != 1:
    260. ns = [math.ceil(x * sf / gs) * gs for x in imgs.shape[2:]] # new shape (stretched to gs-multiple)
    261. imgs = F.interpolate(imgs, size=ns, mode='bilinear', align_corners=False)
    262. # Forward
    263. with amp.autocast(enabled=cuda):
    264. pred = model(imgs) # forward
    265. loss, loss_items = compute_loss(pred, targets.to(device)) # loss scaled by batch_size
    266. if rank != -1:
    267. loss *= opt.world_size # gradient averaged between devices in DDP mode
    268. if opt.quad:
    269. loss *= 4.
    270. # Backward
    271. scaler.scale(loss).backward()
    272. # Optimize
    273. if ni % accumulate == 0:
    274. scaler.step(optimizer) # optimizer.step
    275. scaler.update()
    276. optimizer.zero_grad()
    277. if ema:
    278. ema.update(model)
    279. # Print
    280. if rank in [-1, 0]:
    281. mloss = (mloss * i + loss_items) / (i + 1) # update mean losses
    282. mem = '%.3gG' % (torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0) # (GB)
    283. s = ('%10s' * 2 + '%10.4g' * 6) % (
    284. '%g/%g' % (epoch, epochs - 1), mem, *mloss, targets.shape[0], imgs.shape[-1])
    285. pbar.set_description(s)
    286. # Plot
    287. if plots and ni < 3:
    288. f = save_dir / f'train_batch{ni}.jpg' # filename
    289. Thread(target=plot_images, args=(imgs, targets, paths, f), daemon=True).start()
    290. # if tb_writer:
    291. # tb_writer.add_image(f, result, dataformats='HWC', global_step=epoch)
    292. # tb_writer.add_graph(torch.jit.trace(model, imgs, strict=False), []) # add model graph
    293. elif plots and ni == 10 and wandb_logger.wandb:
    294. wandb_logger.log({"Mosaics": [wandb_logger.wandb.Image(str(x), caption=x.name) for x in
    295. save_dir.glob('train*.jpg') if x.exists()]})
    296. # end batch ------------------------------------------------------------------------------------------------
    297. # end epoch ----------------------------------------------------------------------------------------------------
    298. # Scheduler
    299. lr = [x['lr'] for x in optimizer.param_groups] # for tensorboard
    300. scheduler.step()
    301. # DDP process 0 or single-GPU
    302. if rank in [-1, 0]:
    303. # mAP
    304. ema.update_attr(model, include=['yaml', 'nc', 'hyp', 'gr', 'names', 'stride', 'class_weights'])
    305. final_epoch = epoch + 1 == epochs
    306. if not opt.notest or final_epoch: # Calculate mAP
    307. wandb_logger.current_epoch = epoch + 1
    308. results, maps, times = test.test(data_dict,
    309. batch_size=batch_size * 2,
    310. imgsz=imgsz_test,
    311. model=ema.ema,
    312. single_cls=opt.single_cls,
    313. dataloader=testloader,
    314. save_dir=save_dir,
    315. verbose=nc < 50 and final_epoch,
    316. plots=plots and final_epoch,
    317. wandb_logger=wandb_logger,
    318. compute_loss=compute_loss,
    319. is_coco=is_coco)
    320. # Write
    321. with open(results_file, 'a') as f:
    322. f.write(s + '%10.4g' * 7 % results + '\n') # append metrics, val_loss
    323. if len(opt.name) and opt.bucket:
    324. os.system('gsutil cp %s gs://%s/results/results%s.txt' % (results_file, opt.bucket, opt.name))
    325. # Log
    326. tags = ['train/box_loss', 'train/obj_loss', 'train/cls_loss', # train loss
    327. 'metrics/precision', 'metrics/recall', 'metrics/mAP_0.5', 'metrics/mAP_0.5:0.95',
    328. 'val/box_loss', 'val/obj_loss', 'val/cls_loss', # val loss
    329. 'x/lr0', 'x/lr1', 'x/lr2'] # params
    330. for x, tag in zip(list(mloss[:-1]) + list(results) + lr, tags):
    331. if tb_writer:
    332. tb_writer.add_scalar(tag, x, epoch) # tensorboard
    333. if wandb_logger.wandb:
    334. wandb_logger.log({tag: x}) # W&B
    335. # Update best mAP
    336. fi = fitness(np.array(results).reshape(1, -1)) # weighted combination of [P, R, mAP@.5, mAP@.5-.95]
    337. if fi > best_fitness:
    338. best_fitness = fi
    339. wandb_logger.end_epoch(best_result=best_fitness == fi)
    340. # Save model
    341. if (not opt.nosave) or (final_epoch and not opt.evolve): # if save
    342. ckpt = {'epoch': epoch,
    343. 'best_fitness': best_fitness,
    344. 'training_results': results_file.read_text(),
    345. 'model': deepcopy(model.module if is_parallel(model) else model).half(),
    346. 'ema': deepcopy(ema.ema).half(),
    347. 'updates': ema.updates,
    348. 'optimizer': optimizer.state_dict(),
    349. 'wandb_id': wandb_logger.wandb_run.id if wandb_logger.wandb else None}
    350. # Save last, best and delete
    351. torch.save(ckpt, last)
    352. if best_fitness == fi:
    353. torch.save(ckpt, best)
    354. if wandb_logger.wandb:
    355. if ((epoch + 1) % opt.save_period == 0 and not final_epoch) and opt.save_period != -1:
    356. wandb_logger.log_model(
    357. last.parent, opt, epoch, fi, best_model=best_fitness == fi)
    358. del ckpt
    359. # end epoch ----------------------------------------------------------------------------------------------------
    360. # end training
    361. if rank in [-1, 0]:
    362. # Plots
    363. if plots:
    364. plot_results(save_dir=save_dir) # save as results.png
    365. if wandb_logger.wandb:
    366. files = ['results.png', 'confusion_matrix.png', *[f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R')]]
    367. wandb_logger.log({"Results": [wandb_logger.wandb.Image(str(save_dir / f), caption=f) for f in files
    368. if (save_dir / f).exists()]})
    369. # Test best.pt
    370. logger.info('%g epochs completed in %.3f hours.\n' % (epoch - start_epoch + 1, (time.time() - t0) / 3600))
    371. if opt.data.endswith('coco.yaml') and nc == 80: # if COCO
    372. for m in (last, best) if best.exists() else (last): # speed, mAP tests
    373. results, _, _ = test.test(opt.data,
    374. batch_size=batch_size * 2,
    375. imgsz=imgsz_test,
    376. conf_thres=0.001,
    377. iou_thres=0.7,
    378. model=attempt_load(m, device).half(),
    379. single_cls=opt.single_cls,
    380. dataloader=testloader,
    381. save_dir=save_dir,
    382. save_json=True,
    383. plots=False,
    384. is_coco=is_coco)
    385. # Strip optimizers
    386. final = best if best.exists() else last # final model
    387. for f in last, best:
    388. if f.exists():
    389. strip_optimizer(f) # strip optimizers
    390. if opt.bucket:
    391. os.system(f'gsutil cp {final} gs://{opt.bucket}/weights') # upload
    392. if wandb_logger.wandb and not opt.evolve: # Log the stripped model
    393. wandb_logger.wandb.log_artifact(str(final), type='model',
    394. name='run_' + wandb_logger.wandb_run.id + '_model',
    395. aliases=['last', 'best', 'stripped'])
    396. wandb_logger.finish_run()
    397. else:
    398. dist.destroy_process_group()
    399. torch.cuda.empty_cache()
    400. return results
    401. if __name__ == '__main__':
    402. parser = argparse.ArgumentParser()
    403. parser.add_argument('--weights', type=str, default='yolov5s.pt', help='initial weights path')
    404. parser.add_argument('--cfg', type=str, default='models/yolov5s_hat.yaml', help='model.yaml path')
    405. parser.add_argument('--data', type=str, default='data/hat.yaml', help='data.yaml path')
    406. parser.add_argument('--hyp', type=str, default='data/hyp.scratch.yaml', help='hyperparameters path')
    407. parser.add_argument('--epochs', type=int, default=50)
    408. parser.add_argument('--batch-size', type=int, default=32, help='total batch size for all GPUs')
    409. parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes')
    410. parser.add_argument('--rect', action='store_true', help='rectangular training')
    411. parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')
    412. parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
    413. parser.add_argument('--notest', action='store_true', help='only test final epoch')
    414. parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')
    415. parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')
    416. parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')
    417. parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')
    418. parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')
    419. parser.add_argument('--device', default='0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
    420. parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')
    421. parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')
    422. parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')
    423. parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')
    424. parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')
    425. parser.add_argument('--workers', type=int, default=8, help='maximum number of dataloader workers')
    426. parser.add_argument('--project', default='runs/train', help='save to project/name')
    427. parser.add_argument('--entity', default=None, help='W&B entity')
    428. parser.add_argument('--name', default='exp', help='save to project/name')
    429. parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
    430. parser.add_argument('--quad', action='store_true', help='quad dataloader')
    431. parser.add_argument('--linear-lr', action='store_true', help='linear LR')
    432. parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')
    433. parser.add_argument('--upload_dataset', action='store_true', help='Upload dataset as W&B artifact table')
    434. parser.add_argument('--bbox_interval', type=int, default=-1, help='Set bounding-box image logging interval for W&B')
    435. parser.add_argument('--save_period', type=int, default=-1, help='Log model after every "save_period" epoch')
    436. parser.add_argument('--artifact_alias', type=str, default="latest", help='version of dataset artifact to be used')
    437. opt = parser.parse_args()
    438. # Set DDP variables
    439. opt.world_size = int(os.environ['WORLD_SIZE']) if 'WORLD_SIZE' in os.environ else 1
    440. opt.global_rank = int(os.environ['RANK']) if 'RANK' in os.environ else -1
    441. set_logging(opt.global_rank)
    442. if opt.global_rank in [-1, 0]:
    443. check_git_status()
    444. check_requirements()
    445. # Resume
    446. wandb_run = check_wandb_resume(opt)
    447. if opt.resume and not wandb_run: # resume an interrupted run
    448. ckpt = opt.resume if isinstance(opt.resume, str) else get_latest_run() # specified or most recent path
    449. assert os.path.isfile(ckpt), 'ERROR: --resume checkpoint does not exist'
    450. apriori = opt.global_rank, opt.local_rank
    451. with open(Path(ckpt).parent.parent / 'opt.yaml') as f:
    452. opt = argparse.Namespace(**yaml.load(f, Loader=yaml.SafeLoader)) # replace
    453. opt.cfg, opt.weights, opt.resume, opt.batch_size, opt.global_rank, opt.local_rank = '', ckpt, True, opt.total_batch_size, *apriori # reinstate
    454. logger.info('Resuming training from %s' % ckpt)
    455. else:
    456. # opt.hyp = opt.hyp or ('hyp.finetune.yaml' if opt.weights else 'hyp.scratch.yaml')
    457. opt.data, opt.cfg, opt.hyp = check_file(opt.data), check_file(opt.cfg), check_file(opt.hyp) # check files
    458. assert len(opt.cfg) or len(opt.weights), 'either --cfg or --weights must be specified'
    459. opt.img_size.extend([opt.img_size[-1]] * (2 - len(opt.img_size))) # extend to 2 sizes (train, test)
    460. opt.name = 'evolve' if opt.evolve else opt.name
    461. opt.save_dir = increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok | opt.evolve) # increment run
    462. # DDP mode
    463. opt.total_batch_size = opt.batch_size
    464. device = select_device(opt.device, batch_size=opt.batch_size)
    465. if opt.local_rank != -1:
    466. assert torch.cuda.device_count() > opt.local_rank
    467. torch.cuda.set_device(opt.local_rank)
    468. device = torch.device('cuda', opt.local_rank)
    469. dist.init_process_group(backend='nccl', init_method='env://') # distributed backend
    470. assert opt.batch_size % opt.world_size == 0, '--batch-size must be multiple of CUDA device count'
    471. opt.batch_size = opt.total_batch_size // opt.world_size
    472. # Hyperparameters
    473. with open(opt.hyp) as f:
    474. hyp = yaml.load(f, Loader=yaml.SafeLoader) # load hyps
    475. # Train
    476. logger.info(opt)
    477. if not opt.evolve:
    478. tb_writer = None # init loggers
    479. if opt.global_rank in [-1, 0]:
    480. prefix = colorstr('tensorboard: ')
    481. logger.info(f"{prefix}Start with 'tensorboard --logdir {opt.project}', view at http://localhost:6006/")
    482. tb_writer = SummaryWriter(opt.save_dir) # Tensorboard
    483. train(hyp, opt, device, tb_writer)
    484. # Evolve hyperparameters (optional)
    485. else:
    486. # Hyperparameter evolution metadata (mutation scale 0-1, lower_limit, upper_limit)
    487. meta = {'lr0': (1, 1e-5, 1e-1), # initial learning rate (SGD=1E-2, Adam=1E-3)
    488. 'lrf': (1, 0.01, 1.0), # final OneCycleLR learning rate (lr0 * lrf)
    489. 'momentum': (0.3, 0.6, 0.98), # SGD momentum/Adam beta1
    490. 'weight_decay': (1, 0.0, 0.001), # optimizer weight decay
    491. 'warmup_epochs': (1, 0.0, 5.0), # warmup epochs (fractions ok)
    492. 'warmup_momentum': (1, 0.0, 0.95), # warmup initial momentum
    493. 'warmup_bias_lr': (1, 0.0, 0.2), # warmup initial bias lr
    494. 'box': (1, 0.02, 0.2), # box loss gain
    495. 'cls': (1, 0.2, 4.0), # cls loss gain
    496. 'cls_pw': (1, 0.5, 2.0), # cls BCELoss positive_weight
    497. 'obj': (1, 0.2, 4.0), # obj loss gain (scale with pixels)
    498. 'obj_pw': (1, 0.5, 2.0), # obj BCELoss positive_weight
    499. 'iou_t': (0, 0.1, 0.7), # IoU training threshold
    500. 'anchor_t': (1, 2.0, 8.0), # anchor-multiple threshold
    501. 'anchors': (2, 2.0, 10.0), # anchors per output grid (0 to ignore)
    502. 'fl_gamma': (0, 0.0, 2.0), # focal loss gamma (efficientDet default gamma=1.5)
    503. 'hsv_h': (1, 0.0, 0.1), # image HSV-Hue augmentation (fraction)
    504. 'hsv_s': (1, 0.0, 0.9), # image HSV-Saturation augmentation (fraction)
    505. 'hsv_v': (1, 0.0, 0.9), # image HSV-Value augmentation (fraction)
    506. 'degrees': (1, 0.0, 45.0), # image rotation (+/- deg)
    507. 'translate': (1, 0.0, 0.9), # image translation (+/- fraction)
    508. 'scale': (1, 0.0, 0.9), # image scale (+/- gain)
    509. 'shear': (1, 0.0, 10.0), # image shear (+/- deg)
    510. 'perspective': (0, 0.0, 0.001), # image perspective (+/- fraction), range 0-0.001
    511. 'flipud': (1, 0.0, 1.0), # image flip up-down (probability)
    512. 'fliplr': (0, 0.0, 1.0), # image flip left-right (probability)
    513. 'mosaic': (1, 0.0, 1.0), # image mixup (probability)
    514. 'mixup': (1, 0.0, 1.0)} # image mixup (probability)
    515. assert opt.local_rank == -1, 'DDP mode not implemented for --evolve'
    516. opt.notest, opt.nosave = True, True # only test/save final epoch
    517. # ei = [isinstance(x, (int, float)) for x in hyp.values()] # evolvable indices
    518. yaml_file = Path(opt.save_dir) / 'hyp_evolved.yaml' # save best result here
    519. if opt.bucket:
    520. os.system('gsutil cp gs://%s/evolve.txt .' % opt.bucket) # download evolve.txt if exists
    521. for _ in range(300): # generations to evolve
    522. if Path('evolve.txt').exists(): # if evolve.txt exists: select best hyps and mutate
    523. # Select parent(s)
    524. parent = 'single' # parent selection method: 'single' or 'weighted'
    525. x = np.loadtxt('evolve.txt', ndmin=2)
    526. n = min(5, len(x)) # number of previous results to consider
    527. x = x[np.argsort(-fitness(x))][:n] # top n mutations
    528. w = fitness(x) - fitness(x).min() # weights
    529. if parent == 'single' or len(x) == 1:
    530. # x = x[random.randint(0, n - 1)] # random selection
    531. x = x[random.choices(range(n), weights=w)[0]] # weighted selection
    532. elif parent == 'weighted':
    533. x = (x * w.reshape(n, 1)).sum(0) / w.sum() # weighted combination
    534. # Mutate
    535. mp, s = 0.8, 0.2 # mutation probability, sigma
    536. npr = np.random
    537. npr.seed(int(time.time()))
    538. g = np.array([x[0] for x in meta.values()]) # gains 0-1
    539. ng = len(meta)
    540. v = np.ones(ng)
    541. while all(v == 1): # mutate until a change occurs (prevent duplicates)
    542. v = (g * (npr.random(ng) < mp) * npr.randn(ng) * npr.random() * s + 1).clip(0.3, 3.0)
    543. for i, k in enumerate(hyp.keys()): # plt.hist(v.ravel(), 300)
    544. hyp[k] = float(x[i + 7] * v[i]) # mutate
    545. # Constrain to limits
    546. for k, v in meta.items():
    547. hyp[k] = max(hyp[k], v[1]) # lower limit
    548. hyp[k] = min(hyp[k], v[2]) # upper limit
    549. hyp[k] = round(hyp[k], 5) # significant digits
    550. # Train mutation
    551. results = train(hyp.copy(), opt, device)
    552. # Write mutation results
    553. print_mutation(hyp.copy(), results, yaml_file, opt.bucket)
    554. # Plot results
    555. plot_evolution(yaml_file)
    556. print(f'Hyperparameter evolution complete. Best results saved as: {yaml_file}\n'
    557. f'Command to train a new model with these hyperparameters: $ python train.py --hyp {yaml_file}')

    终端执行train.py即可

    python train.py

    75b02cf52b334d0ea9fa3f8dd13e66d4.png

    训练好的模型是runs/train/exp3/best.pt

    最后一次模型是runs/train/exp3/last.pt

    训练好后检测图片

    先修改detect.py(149行开始)内的部分内容

    主要修改以下参数,也可直接复制下方代码替换

    6a900f4797854b218308a2121dbbdcc2.png

    1. import argparse
    2. import time
    3. from pathlib import Path
    4. import cv2
    5. import torch
    6. import torch.backends.cudnn as cudnn
    7. from numpy import random
    8. from models.experimental import attempt_load
    9. from utils.datasets import LoadStreams, LoadImages
    10. from utils.general import check_img_size, check_requirements, check_imshow, non_max_suppression, apply_classifier, \
    11. scale_coords, xyxy2xywh, strip_optimizer, set_logging, increment_path
    12. from utils.plots import plot_one_box
    13. from utils.torch_utils import select_device, load_classifier, time_synchronized
    14. def detect(save_img=False):
    15. source, weights, view_img, save_txt, imgsz = opt.source, opt.weights, opt.view_img, opt.save_txt, opt.img_size
    16. save_img = not opt.nosave and not source.endswith('.txt') # save inference images
    17. webcam = source.isnumeric() or source.endswith('.txt') or source.lower().startswith(
    18. ('rtsp://', 'rtmp://', 'http://', 'https://'))
    19. # Directories
    20. save_dir = Path(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok)) # increment run
    21. (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir
    22. # Initialize
    23. set_logging()
    24. device = select_device(opt.device)
    25. half = device.type != 'cpu' # half precision only supported on CUDA
    26. # Load model
    27. model = attempt_load(weights, map_location=device) # load FP32 model
    28. stride = int(model.stride.max()) # model stride
    29. imgsz = check_img_size(imgsz, s=stride) # check img_size
    30. if half:
    31. model.half() # to FP16
    32. # Second-stage classifier
    33. classify = False
    34. if classify:
    35. modelc = load_classifier(name='resnet101', n=2) # initialize
    36. modelc.load_state_dict(torch.load('weights/resnet101.pt', map_location=device)['model']).to(device).eval()
    37. # Set Dataloader
    38. vid_path, vid_writer = None, None
    39. if webcam:
    40. view_img = check_imshow()
    41. cudnn.benchmark = True # set True to speed up constant image size inference
    42. dataset = LoadStreams(source, img_size=imgsz, stride=stride)
    43. else:
    44. dataset = LoadImages(source, img_size=imgsz, stride=stride)
    45. # Get names and colors
    46. names = model.module.names if hasattr(model, 'module') else model.names
    47. colors = [[random.randint(0, 255) for _ in range(3)] for _ in names]
    48. # Run inference
    49. if device.type != 'cpu':
    50. model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters()))) # run once
    51. t0 = time.time()
    52. for path, img, im0s, vid_cap in dataset:
    53. img = torch.from_numpy(img).to(device)
    54. img = img.half() if half else img.float() # uint8 to fp16/32
    55. img /= 255.0 # 0 - 255 to 0.0 - 1.0
    56. if img.ndimension() == 3:
    57. img = img.unsqueeze(0)
    58. # Inference
    59. t1 = time_synchronized()
    60. pred = model(img, augment=opt.augment)[0]
    61. # Apply NMS
    62. pred = non_max_suppression(pred, opt.conf_thres, opt.iou_thres, classes=opt.classes, agnostic=opt.agnostic_nms)
    63. t2 = time_synchronized()
    64. # Apply Classifier
    65. if classify:
    66. pred = apply_classifier(pred, modelc, img, im0s)
    67. # Process detections
    68. for i, det in enumerate(pred): # detections per image
    69. if webcam: # batch_size >= 1
    70. p, s, im0, frame = path[i], '%g: ' % i, im0s[i].copy(), dataset.count
    71. else:
    72. p, s, im0, frame = path, '', im0s, getattr(dataset, 'frame', 0)
    73. p = Path(p) # to Path
    74. save_path = str(save_dir / p.name) # img.jpg
    75. txt_path = str(save_dir / 'labels' / p.stem) + ('' if dataset.mode == 'image' else f'_{frame}') # img.txt
    76. s += '%gx%g ' % img.shape[2:] # print string
    77. gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh
    78. if len(det):
    79. # Rescale boxes from img_size to im0 size
    80. det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()
    81. # Print results
    82. for c in det[:, -1].unique():
    83. n = (det[:, -1] == c).sum() # detections per class
    84. s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string
    85. # Write results
    86. for *xyxy, conf, cls in reversed(det):
    87. if save_txt: # Write to file
    88. xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh
    89. line = (cls, *xywh, conf) if opt.save_conf else (cls, *xywh) # label format
    90. with open(txt_path + '.txt', 'a') as f:
    91. f.write(('%g ' * len(line)).rstrip() % line + '\n')
    92. if save_img or view_img: # Add bbox to image
    93. label = f'{names[int(cls)]} {conf:.2f}'
    94. plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3)
    95. # Print time (inference + NMS)
    96. print(f'{s}Done. ({t2 - t1:.3f}s)')
    97. # Stream results
    98. if view_img:
    99. cv2.imshow(str(p), im0)
    100. cv2.waitKey(1) # 1 millisecond
    101. # Save results (image with detections)
    102. if save_img:
    103. if dataset.mode == 'image':
    104. cv2.imwrite(save_path, im0)
    105. else: # 'video' or 'stream'
    106. if vid_path != save_path: # new video
    107. vid_path = save_path
    108. if isinstance(vid_writer, cv2.VideoWriter):
    109. vid_writer.release() # release previous video writer
    110. if vid_cap: # video
    111. fps = vid_cap.get(cv2.CAP_PROP_FPS)
    112. w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    113. h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    114. else: # stream
    115. fps, w, h = 30, im0.shape[1], im0.shape[0]
    116. save_path += '.mp4'
    117. vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
    118. vid_writer.write(im0)
    119. if save_txt or save_img:
    120. s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ''
    121. print(f"Results saved to {save_dir}{s}")
    122. print(f'Done. ({time.time() - t0:.3f}s)')
    123. if __name__ == '__main__':
    124. parser = argparse.ArgumentParser()
    125. parser.add_argument('--weights', nargs='+', type=str, default='best.pt', help='model.pt path(s)')
    126. parser.add_argument('--source', type=str, default='data/images', help='source') # file/folder, 0 for webcam
    127. parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
    128. parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')
    129. parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')
    130. parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
    131. parser.add_argument('--view-img', action='store_true', help='display results')
    132. parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
    133. parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
    134. parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
    135. parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
    136. parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
    137. parser.add_argument('--augment', action='store_true', help='augmented inference')
    138. parser.add_argument('--update', action='store_true', help='update all models')
    139. parser.add_argument('--project', default='runs/detect', help='save results to project/name')
    140. parser.add_argument('--name', default='exp', help='save results to project/name')
    141. parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
    142. opt = parser.parse_args()
    143. print(opt)
    144. check_requirements(exclude=('pycocotools', 'thop'))
    145. with torch.no_grad():
    146. if opt.update: # update all models (to fix SourceChangeWarning)
    147. for opt.weights in ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt']:
    148. detect()
    149. strip_optimizer(opt.weights)
    150. else:
    151. detect()

    运行detect.py

    python detect.py --weight runs/train/exp3/weights/best.pt   --source  VOCdevkit/images/train/000002.jpg

    或者

    python detect.py

    755c2a7d34484dae8a60ab57d8cfe4fa.png

    识别出佩戴安全帽和未佩戴安全帽,完成简单案例的流程(第一次用yolo框架,如有不妥请见谅)! 

     

     

  • 相关阅读:
    六、回归与聚类算法 - K-means算法
    Oracle的listagg的用法和例子
    jvm 内存泄露、内存溢出、栈溢出区别
    Redis基础架构
    【四旋翼飞行器】模拟四旋翼飞行器的平移和旋转动力学(Simulink仿真实现)
    MySQL 8.0 新特性
    C++ 学习笔记(五)(泛型算法篇)
    Redis基础
    【ROOTFS】1-构建rootfs与nfs调试
    【通信工程笔记】【终端与业务-第十二章】市场营销计划、实施和控制
  • 原文地址:https://blog.csdn.net/yyfloveqcw/article/details/125628085