这篇文章介绍了如何完整的训练自己的模型,并介绍相关的推理过程和识别过程,使用的数据集是安全帽数据集,只有带安全帽和不带安全帽两类,在学习这篇文章之前可以先学习一下图片标注和数据集划分,传送站如下:
- labelimg图片标注工具
- YOLO数据集格式转化与划分
登录到github网站搜索yolov5,找到星级最高的的仓库,也可以点击该链接跳转:https://github.com/ultralytics/yolov5/tree/v7.0
在左上角可以选择yolov5版本,这里我建议使用v5.0版本,因为后面我在修改相关参数的时候都是基于5.0版本进行修改的,选择之后直接点击code,会出现下拉框,选择Download zip可以将代码和相关文件打包下载。
打开上面跳转链接之后,将安装包下载完毕可以将网站下拉,找到yolov5已经训练好的权重,后面训练需要使用,我们用的最多的就是yolov5s,我后面训练模型用的也是在yolov5s.pt的基础上进行训练的。通过下面这张表格可以看见他们训练图片的大小、精度、速度、以及所占内存大小,如果需要高精度的化可以使用yolov5x。
下图是不同权重的在GPU上面的速度对比效果图:
将下载好的zip压缩包解压之后用pycharm打开,yolov5项目结构如下图:
项目文件夹以及代码文件简单介绍:(后续出详细介绍,这里只需要知道每个文件是做什么的)
1.data文件夹
主要是存放一些超参数的配置文件(yaml文件)是用来配置训练集和测试集还有验证集的路径的,其中还包括目标检测的种类数和种类的名称;还有一些官方提供测试的图片。如果是训练自己的数据集的话,那么就需要修改其中的yaml文件,yaml文件中记录的是训练所用到数据集的路径地址,以及类别名称和类别数量,data文件目录如下图。(训练的话该文件夹内容需要修改)
我们训练自己数据集的时候需要修改yaml文件中的内容,我这里是将coco128.yaml复制之后进行修改,修改内容如下:
2.models文件夹
这个文件夹面主要是一些网络构建的配置文件和函数,其中包含了该项目网络的不同的版本,分别以l,m,s,x结尾,我们在使用不同权重训练模型的时候,需要选择对应的yaml文件,他们的检测测度分别都是从快到慢,但是精确度分别是从低到高文件目录如下:
因为我在训练的时候加载的是yolov5s.yaml,所以这里我们需要修改该文件中的内容(建议大家复制该文件之后重新命名),我这里命名为hat-yolov5s.yaml,这个文件需要修改部分内容,因为我们只有2个类别,所以将nc设置为2(原文件应该是nc=80).
3.runs文件夹
这个文件我们在下载源文件的时候是不存在的,当我们运行detect.py或者train.py文件的时候就会生成该文件夹,该文件夹储存的是训练过程数据和推理结果,目录结构如下:
train文件夹保存的是训练结果,包括训练好的权重、训练参数、损失函数曲线等内容。
detect文件夹保存的是识别好的数据文件,包括图片、视频。
4.utils文件夹
这个文件夹存放的是训练过程中所用到的工具函数,像损失函数loss,metrics函数,plots函数等等,训练过程中不需要修改此部分内容。
5.detect.py
推理文件,使用该代码可以对图片以及视频进行推理检测,同时也可以调用摄像头进行检测。在训练过程中一般只需要修改前面两行内容,主要代码结构如下:
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--weights', nargs='+', type=str, default='yolov5s.pt', help='model.pt path(s)')
# 需要加载权重文件地址,这里用我们已经训练好的权重,为了便于理解这里我填的是yolov5官网已经训练好的权重
parser.add_argument('--source', type=str, default='data/images', help='source') # file/folder, 0 for webcam
# 测试数据文件(图片或视频)的保存路径 默认data/images
parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
# inference size (height, width) 输入图片的大小 默认640(pixels)
parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')
# object置信度阈值 默认0.25
parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')
# 做nms的iou阈值 默认0.45
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
# 设置代码执行的设备,也就是选择用显卡进行推理或者cpu进行推理,可以选择第几块显卡或者cpu
parser.add_argument('--view-img', action='store_true', help='display results',default=True)
# 是否展示预测之后的图片或视频(如何设置为True的话,可以看到推理过程) 默认False直接看到推理之后的结果
parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
# 是否将预测的框坐标以txt文件格式保存 默认False
parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
# 是否保存预测每个目标的置信度到预测tx文件中 默认False
parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
# 是否不要保存预测后的图片 默认False 就是默认要保存预测后的图片
parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
# 在nms中是否是只保留某些特定的类 默认是None 就是所有类只要满足条件都可以保留
parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
# 进行nms是否也除去不同类别之间的框 默认False
parser.add_argument('--augment', action='store_true', help='augmented inference')
# 预测是否也要采用数据增强 TTA 默认False
parser.add_argument('--update', action='store_true', help='update all models')
parser.add_argument('--project', default='runs/detect', help='save results to project/name')
# 当前测试结果放在哪个主文件夹下 默认runs/detect
parser.add_argument('--name', default='exp', help='save results to project/name')
# 当前测试结果放在run/detect下的文件名 默认是exp => run/detect/exp
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
# 是否存在当前文件 默认False 一般是 no exist-ok 连用 所以一般都要重新创建文件夹
opt = parser.parse_args()
print(opt)
check_requirements(exclude=('pycocotools', 'thop'))
6.train.py
用来训练自己的数据集的函数。功能结构如下:
主要参数代码如下:
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--weights', type=str, default='runs/train/exp/weights/best.pt', help='initial weights path')
parser.add_argument('--cfg', type=str, default='yolov5s.pt', help='model.yaml path')
parser.add_argument('--data', type=str, default='data/hat.yaml', help='data.yaml path')
parser.add_argument('--hyp', type=str, default='data/hyp.scratch.yaml', help='hyperparameters path')
parser.add_argument('--epochs', type=int, default=100)
parser.add_argument('--batch-size', type=int, default=16, help='total batch size for all GPUs')
parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes')
parser.add_argument('--rect', action='store_true', help='rectangular training')
parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')
parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
parser.add_argument('--notest', action='store_true', help='only test final epoch')
parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')
parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')
parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')
parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')
parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')
parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')
parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')
parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')
parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')
parser.add_argument('--workers', type=int, default=8, help='maximum number of dataloader workers')
parser.add_argument('--project', default='runs/train', help='save to project/name')
parser.add_argument('--entity', default=None, help='W&B entity')
parser.add_argument('--name', default='exp', help='save to project/name')
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
parser.add_argument('--quad', action='store_true', help='quad dataloader')
parser.add_argument('--linear-lr', action='store_true', help='linear LR')
parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')
parser.add_argument('--upload_dataset', action='store_true', help='Upload dataset as W&B artifact table')
parser.add_argument('--bbox_interval', type=int, default=-1, help='Set bounding-box image logging interval for W&B')
parser.add_argument('--save_period', type=int, default=-1, help='Log model after every "save_period" epoch')
parser.add_argument('--artifact_alias', type=str, default="latest", help='version of dataset artifact to be used')
opt = parser.parse_args()
参数解读:
这行代码定义了一个 argparse.ArgumentParser 对象,用于解析命令行参数。argparse 是 Python 自带的一个命令行解析库,可以方便地将命令行参数解析成 Python 对象,并提供帮助信息等功能。
argparse解析了以下参数:
如果想要完整的运行代码,我们需要安装yolov5所需要的依赖包,我们从github上面现在下来的压缩包里面会有一个requirements.txt文件,我们打开pycharm最底下的Terminal输入下面命令:
pip install -r requirements.txt
我们需要对已经下载好的数据集进行打标签,来标注安全帽和人脸的位置,我们有两种打标签工具可以进行选择,第一种是在线打标签工具:https://www.makesense.ai/
第二种是安装本地打标签工具,有需要的可以观看这篇博客:YOLOV5目标检测—labelimg图片标注工具(1)
打好标签的数据集我们需要进行数据集与验证集的划分,我一般是按照8:2进行划分,或者5:1进行划分,我们可以手动进行数据集的划分,也可以使用代码进行随机划分,数据集预处理可以参考这篇博客:YOLOV5目标检测—数据集格式转化与划分(2)
划分好的文件夹格式如下:
----训练数据集文件夹
-----图片
-------训练集图片
-------验证集图片
-----标签
-------训练集标签
-------验证集标签
前面我们已经下载好训练需要的权重—yolov5s.pt,将这个权重拷贝到我们的项目文件夹里面,找到train.py代码,将权重路径复制到default里面(这里我们也可以不放权重,default也可以默认为空,但是我们从头开始训练的话可能需要训练我几千轮,所以我在yolov5s的基础上训练300轮就能达到一个不错的精度)
parser.add_argument('--weights', type=str, default='yolov5s.pt', help='initial weights path')
找到data文件夹,复制coco.yaml文件重新命名为hat.yaml,hat.yaml修改内容如下:
因为我们是在yolov5s.pt权重的基础上进行训练的,我们需要修改models/yolov5s.yaml中的内容,这里只需要将80类改成2类:
我们只需要将修改后的数据集配置文件和模型配置文件路径替换原来默认路径,修改内容如下:
parser.add_argument('--weights', type=str, default='yolov5s.pt', help='initial weights path')
parser.add_argument('--cfg', type=str, default='models/hat.yaml', help='model.yaml path')
parser.add_argument('--data', type=str, default='data/hat.yaml', help='data.yaml path')
修改后右键运行就能进行训练了:
训练之后的权重保存在runs/train/exp/weights中,目录结构如下:
weights里面会有一个best.pt和一个last.pt,一个是训练最好结果的权重,一个是最后一轮训练得到的权重,我们使用best.pt。
训练过程中我们想要查看损失值的情况,以及精度的变化,我们可以使用pytorch中自带的可视化工具tensboard,在pycharm下方的Terminal中输入如下命令:
tensorboard --logdir==runs/train
训练之后我们需要将我们训练好的权重路径复制到detect.py代码中的–weights中,同时–source中放我们待测图片路径如下:
parser.add_argument('--weights', nargs='+', type=str, default='weights/best.pt', help='model.pt path(s)')
parser.add_argument('--source', type=str, default='data/hat', help='source')
#这里的default放的是我们待识别图片文件夹路径
右键运行代码就能得到测试结果,检测到的结果保存,在runs/detect/exp
测试结果如下(这里我用到了图形化界面,后期会整理如何使用pyqt开发图形化界面):
识别视频只需要将–source中的图片路径改成视频路径就ok了,修改如下:
parser.add_argument('--source', type=str, default='1.mp4', help='source')
调用摄像头只需要将default修改为0,修改如下:
parser.add_argument('--source', type=str, default=0, help='source')
1.缺少SPPF模块
2.调用摄像头报错
3.yolov5训练时参数workers与batch-size的常见问题
4.训练过程中找不到图片问题