目录
0 前言
1、从githab上克隆yolov5代码
1.1 yolov5网络project克隆
1.2 项目代码结构的整体介绍
1.3 深度学习环境的配置和安装yolov5所需要的库
2、数据集和预训练权重的准备
2.1 利用labelimg对数据进行标注和划分
2.2 下载预训练权重
3、训练自己的模型
3.1 修改一些文件配置
3.2 训练自己的模型
3.3 启用tensorbord查看参数
4 、利用自己训练的模型检测识别
5 、常见一些报错问题
这段时间正在学习深度学习,通过自己的摸索以及观看一些视频教程和一些前辈的博客,实现了深度学习目标检测--使用yolov5网络训练自己的数据集并实现了对目标的识别。这篇博客我也是参考别人的博客教程,复现了使用yolov5训练自己的数据集,我呢主要是想记录下我自己的操作流程以及遇到的一些问题,方便以后自己学习查看。我也是刚刚接触深度学习不久,目前正处于学习阶段。希望也对大家有所帮助,同时也欢迎大家来一起学习讨论,有什么不足的地方还恳请大家指正。
YOLOV5的代码作者在GitHub上开源,打开yolov5的GitHub官网,目前已经更新了6个分支了,这里我使用的是yolov5-5.0版本,如下图所示,点击克隆下载即可。具体如下图。
将yolov5代码下载后进行解压,接着用pycharm IDE软件打开,打开后整个代码项目如下图所示。
其中:
(1)data:主要是存放一些超参数的配置文件(这些文件(yaml文件)是用来配置训练集和测试集还有验证集的路径的,其中还包括目标检测的种类数和种类的名称);还有一些官方提供测试的图片。
(2)models:里面主要是一些网络构建的配置文件和函数,其中包含了该项目的四个不同的版本,分别为是s、m、l、x。从名字就可以看出,这几个版本的大小。他们的检测测度分别都是从快到慢,但是精确度分别是从低到高。
(3)utils:存放的是工具类的函数,里面有loss函数,metrics函数,plots函数等等。
(4)weights:放置训练好的权重参数。
(5)detect.py:利用训练好的权重参数进行目标检测,可以进行图像、视频和摄像头的检测。
(6)train.py:训练自己的数据集的函数。
(7)test.py:测试训练的结果的函数。
(8)requirements.txt:这是一个文本文件,里面写着使用yolov5项目的环境依赖包的一些版本,可以利用该文本导入相应版本的包。
关于深度学习环境的搭建具体可以查看我另一篇博客(window系统下利用Anaconda安装pytorch+cuda搭建深度学习环境),里面有详细的介绍,接着打开yolov5目录下的requirements.txt文件,文件里可以看到我们需要安装的依赖库和对应其版本要求。
接着打开pycharm终端命令控制台,输入以下命令对这些依赖库进行安装。
pip install -r requirements.txt
(1)因为目标检测是监督学习,需要对数据集进行标注打标签,所以我们使用labelimg对数据进行标注,具体步骤可以参考我另一篇的博客(深度学习目标检测---使用labelimg对自己的数据集进行标记(windows系统)),那里有详细的介绍。
(2)yolov5训练过程中需要的数据集是yolo(txt)格式,同时使用yolov5在训练自己的数据集模型的时候,需要将数据集划分为训练集和验证集。然后我们需要对标注好的数据集进行格式转换,还有将数据集划分为训练集和验证集来训练我们自己的yolov5模型,具体步骤可以参考我另一篇的博客(深度学习目标检测---数据集的格式转换及训练集、验证集的划分),那里有详细的介绍。
(3)将我们划分好的数据集整个文件(VOCdevkit)放到yolov5-5.0项目下面,具体如下图所示。
YOLOV5-5.0给我们提供了一些预训练权重,可以根据自己的需求选择不同的版本的预训练权重(预训练权重越大,训练出来的精度就会相对来说越高,但是其检测的速度就会越慢)。预训练权重可以通过这个网址进行下载,找到对应的v5版本进行下载,我这里使用的预训练权重是yolov5x.pt。
预训练模型下载和数据集格式转换、划分好后,就可以开始训练自己的数据集模型了。在训练目标检测模型前还需要修改两个yaml文件的参数。一个是data目录下的yaml文件,一个是model目录下的yaml文件。
(1)修改data目录下的yaml文件。找到data目录下的voc.yaml文件,将此文件复制一份,并将文件重命名,可以将文件命名为与你项目相关的名称。我这里修改的命名为weed.yaml。然后粘贴在同一个目录(data)下,如下图所示。
打开我们我们复制的文件(weed.yaml),对里面的参数进行修改,第一,将箭头1中的代码注释掉;第二,将箭头2中改成你的数据集(需要训练和测试的数据集)的路径,这里我填的是相对路径,也可以填绝对路径;第三,将箭头3处改成需要检测的类别个数,这里我的项目需要识别7种类别,我就将其改成7;第四,将箭头四填写成需要识别的类别名称(必须是英文)。到这里,我们就修改好了data目录下的yaml文件。
(2)修改models目录下的yaml模型配置参数,在此项目中,我选用的是yolov5x.pt这个预训练权重,所以我们需要使用models目录下的yolov5x.pt文件中的对应参数。像上面步骤(1)修改data目录下的yaml文件一样,将models目录下的yolov5x.pt文件复制一份,对其重命名后粘贴到同一目录下(models),我这里将其重命名为yolov5x_weed.yaml。
打开yolov5x_weed.yaml文件,将红色方框中的数字改成需要检测的类别个数(这里我的识别的类别是7,就将其改成7),如下方图片所示。修改之后,我们的参数配置就修改好了。
完成上面的操作后,就可以开始yolov5的训练了。在这里我们打开train.py这个文件。
点击进入文件后,找到主函数,这里有模型的一些主要参数,如下代码所示
if __name__ == '__main__':
"""
weights: 权重文件
cfg: 模型配置文件 包括nc、depth_multiple、width_multiple、anchors、backbone、head等
data: 数据集配置文件 包括path、train、val、test、nc、names、download等
hyp: 初始超参文件
epochs: 训练轮次
batch-size: 训练批次大小
img-size: 输入网络的图片分辨率大小
resume: 断点续训, 从上次打断的训练结果处接着训练 默认False
nosave: 不保存模型 默认False(保存) True: only test final epoch
notest: 是否只测试最后一轮 默认False True: 只测试最后一轮 False: 每轮训练完都测试mAP
workers: dataloader中的最大work数(线程个数)
device: 训练的设备
single-cls: 数据集是否只有一个类别 默认False
rect: 训练集是否采用矩形训练 默认False
noautoanchor: 不自动调整anchor 默认False(自动调整anchor)
evolve: 是否进行超参进化 默认False
multi-scale: 是否使用多尺度训练 默认False
label-smoothing: 标签平滑增强 默认0.0不增强 要增强一般就设为0.1
adam: 是否使用adam优化器 默认False(使用SGD)
sync-bn: 是否使用跨卡同步bn操作,再DDP中使用 默认False
linear-lr: 是否使用linear lr 线性学习率 默认False 使用cosine lr
cache-image: 是否提前缓存图片到内存cache,以加速训练 默认False
image-weights: 是否使用图片采用策略(selection img to training by class weights) 默认False 不使用
bucket: 谷歌云盘bucket 一般用不到
project: 训练结果保存的根目录 默认是runs/train
name: 训练结果保存的目录 默认是exp 最终: runs/train/exp
exist-ok: 如果文件存在就ok不存在就新建或increment name 默认False(默认文件都是不存在的)
quad: dataloader取数据时, 是否使用collate_fn4代替collate_fn 默认False
save_period: Log model after every "save_period" epoch 默认-1 不需要log model 信息
artifact_alias: which version of dataset artifact to be stripped 默认lastest 貌似没用到这个参数?
local_rank: rank为进程编号 -1且gpu=1时不进行分布式 -1且多块gpu使用DataParallel模式
entity: wandb entity 默认None
upload_dataset: 是否上传dataset到wandb tabel(将数据集作为交互式 dsviz表 在浏览器中查看、查询、筛选和分析数据集) 默认False
bbox_interval: 设置界框图像记录间隔 Set bounding-box image logging interval for W&B 默认-1 opt.epochs // 10
"""
parser = argparse.ArgumentParser()
# --------------------------------------------------- 常用参数 ---------------------------------------------
parser.add_argument('--weights', type=str, default='weights/yolov5x.pt', help='initial weights path')
parser.add_argument('--cfg', type=str, default='models/yolov5x_weed.yaml', help='model.yaml path')
parser.add_argument('--data', type=str, default='data/weed.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=150)
parser.add_argument('--batch-size', type=int, default=3, 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('--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('--workers', type=int, default=0, help='maximum number of dataloader workers')
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')
# --------------------------------------------------- 数据增强参数 ---------------------------------------------
parser.add_argument('--rect', action='store_true', help='rectangular training')
parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')
parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')
parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')
parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')
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('--linear-lr', action='store_true', help='linear LR')
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('--bucket', type=str, default='', help='gsutil bucket')
parser.add_argument('--project', default='runs/train', help='save to project/name')
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('--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')
parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')
# --------------------------------------------------- 三个W&B(wandb)参数 ---------------------------------------------
parser.add_argument('--entity', default=None, help='W&B entity')
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')
opt = parser.parse_args()
在训练自己的模型时需要修改以下几个参数。如下图:第一步,将weights权重的路径填写到对应的参数里面;第二步,将修改好的models模型的yolov5x_weed.yaml文件路径填写到相应的参数里面;第三步,将data数据的weed.yaml文件路径填写到相对于的参数里面。这几个参数就必须要修改的参数。如下图所示,上面步骤对应下图中的1,2,3。
其中,训练轮次可以根据的自己的需求更改,我这里将其改成150轮。
修改完成后,就可以运行train.py文件进行训练自己的模型了,训练需要一段时间,需耐心等待。这里,若训练过程中出现报错,可以参考我下面⬇⬇⬇标题5写常见的一些报错问题(请跳转至标题5),那里有解决方法。
YOLOV5里面有写好的tensorbord函数,训练结束后通过运行命令就可以调用tensorbord,然后查看tensorbord了,具体流程如下:打开pycharm的命令控制终端,输入如下命令,就会出现像下图红色框一样的网址,将下图红色框中的网址复制下来到浏览器打开就可以看到训练的过程了。
tensorboard --logdir=runs/train
打开对应的网址,如下图所示。
如果模型已经训练好了,但是我们还想用tensorbord查看此模型的训练过程,就需要输入如下的命令。就可以看到模型的训练结果了。
tensorboard --logdir=runs
完成上面步骤后进行训练,会在主目录下生成一个run文件,在run/train/exp/weights目录下会产生两个权重文件,一个是最后一轮的权重文件,一个是最好的权重文件,我们将利用这个最好的权重文件来做检测推理测试。除此以外还会产生一些验证文件的图片等一些文件。
找到主目录下的detect.py文件,打开该文件。
点击进入文件后,找到主函数,这里有模型的一些主要参数,如下代码所示
if __name__ == '__main__':
"""
opt参数解析
weights: 模型的权重地址 默认 weights/best.pt
source: 测试数据文件(图片或视频)的保存路径 默认data/images
imgsz: 网络输入图片的大小 默认640
conf-thres: object置信度阈值 默认0.25
iou-thres: 做nms的iou阈值 默认0.45
max-det: 每张图片最大的目标个数 默认1000
device: 设置代码执行的设备 cuda device, i.e. 0 or 0,1,2,3 or cpu
view-img: 是否展示预测之后的图片或视频 默认False
save-txt: 是否将预测的框坐标以txt文件格式保存 默认True 会在runs/detect/expn/labels下生成每张图片预测的txt文件
save-conf: 是否保存预测每个目标的置信度到预测tx文件中 默认True
save-crop: 是否需要将预测到的目标从原图中扣出来 剪切好 并保存 会在runs/detect/expn下生成crops文件,将剪切的图片保存在里面 默认False
nosave: 是否不要保存预测后的图片 默认False 就是默认要保存预测后的图片
classes: 在nms中是否是只保留某些特定的类 默认是None 就是所有类只要满足条件都可以保留
agnostic-nms: 进行nms是否也除去不同类别之间的框 默认False
augment: 预测是否也要采用数据增强 TTA
update: 是否将optimizer从ckpt中删除 更新模型 默认False
project: 当前测试结果放在哪个主文件夹下 默认runs/detect
name: 当前测试结果放在run/detect下的文件名 默认是exp
exist-ok: 是否存在当前文件 默认False 一般是 no exist-ok 连用 所以一般都要重新创建文件夹
line-thickness: 画框的框框的线宽 默认是 3
hide-labels: 画出的框框是否需要隐藏label信息 默认False
hide-conf: 画出的框框是否需要隐藏conf信息 默认False
half: 是否使用半精度 Float16 推理 可以缩短推理时间 但是默认是False
"""
parser = argparse.ArgumentParser()
parser.add_argument('--weights', nargs='+', type=str, default='weights/best1.pt', help='model.pt path(s)')
parser.add_argument('--source', type=str, default='data/images', help='source') # file/folder, 0 for webcam
parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')
parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
parser.add_argument('--view-img', action='store_true', help='display results')
parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
parser.add_argument('--augment', action='store_true', help='augmented inference')
parser.add_argument('--update', action='store_true', help='update all models')
parser.add_argument('--project', default='runs/detect', help='save results to project/name')
parser.add_argument('--name', default='exp', help='save results to project/name')
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
opt = parser.parse_args()
print(opt)
这里需要将刚刚训练好的最好的权重传入到推理函数中去。然后就可以对图像视频进行检测推理了。
将需要训练的图片/视频放在data目录的的images目录下(也可以在data目录下新建一个文件夹存放视频),如下图所示
将如下参数修改成图片/视频的路径(这里若设置为0,则是打开摄像头),然后运行detect.py就可以进行测试了。
检测推理测试结束以后,在run目录下面会生成一个detect目录,检测推理结果会保存在exp目录下。如图所示。
下图是的检测推理的图片结果:
综上,我们就完成了利用yolov5训练自己的模型和使用模型进行检测识别了。
(1)训练时,根据自己个人的电脑配置,若配置低点的话可能会出现GPU显存溢出的报错,报错信息如下:
解决方法:这里就要调小这两个参数了,每个人的电脑配置不一样,所以可以根据自己的电脑配置来修改参数。我这里将其改成3和0。
(2)有些用户可能会出现以下报错,这是说明虚拟内存不够。
解决方法:可以根据如下的操作来修改,在utils路径下找到datasets.py这个文件,将里面的第81行里面的参数nw改完0就可以了。
(3)在detect测试时,利用摄像头进行测试只需将如图路径改写为0就好了。但是好像还是会报错。
报错信息:
解决方法如下:找到datasets.py文件,如下图
打开文件,找到第279行代码,给两个url参数加上str就可以了,如图所示,就可以运行电脑的摄像头了。
(4)yolov5运行时,报错问题---assert img0 is not None, ‘Image Not Found ‘ + pathAssertionError: Image Not Found。
解决方法:将utils/datasets.py文件中的125行代码的p = str(Path(path).absolute()) # os-agnostic absolute path改为p = str(Path(path)),重新运行代码即可,如下图所示