概述
yolov5使用自己的数据集训练
- 一、环境与配置
- 1.1 安装anaconda并创建虚拟环境
- 1.2 安装CUDA和cudnn
- 1.3 安装pytorch
- 二、源码测试
- 2.1 下载源码
- 2.2 安装依赖项
- 2.3 测试
- 2.3.1 下载权重文件
- 2.3.2 测试
- 三、训练自己的数据集
- 3.1 数据集的制作
- 3.2 修改配置文件
- 3.2.1 修改数据集方面的yaml文件
- 3.2.2 聚类获得先验框
- 3.2.3 修改网络参数方面的yaml文件
- 3.2.4 修改train.py中的一些参数
- 3.3 训练
- 3.3.1 在预训练模型基础上进行迁移训练
- 3.3.2 从头开始训练
- 3.3.3 tensorboard可视化训练过程
- 3.4 测试
- 3.4.1 计算mAP
- 3.4.2 图片或视频测试
一、环境与配置
1.1 安装anaconda并创建虚拟环境
参考 安装anaconda并创建虚拟环境
1.2 安装CUDA和cudnn
参考 安装CUDA 和cudnn
1.3 安装pytorch
参考 安装pytorch
二、源码测试
2.1 下载源码
在github上下载源码:yolov5源码下载地址
2.2 安装依赖项
下载源码并解压后,源码根目录下有一个requirements.txt,这里面就是需要安装的各种依赖项了,安装方法是,从根目录打开命令提示符:在文件夹上方的框里输入“cmd”,然后按回车:
输入命令:
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
更换镜像源,可以提高下载速度。
2.3 测试
2.3.1 下载权重文件
作者在GitHub中给出了他们训练出来的权重文件:
yolov5权重文件下载地址
并给出了各种权重文件的检测效果,我们可以随意下载。
将下载的权重文件放在./weight文件夹下。
2.3.2 测试
还是在源码的根目录下打开cmd,然后激活pytorch环境。
图片测试
输入命令:
python detect.py --source ./data/images/ --weights ./weights/yolov5x6.pt --conf 0.4
打开根目录下多出来的runs文件夹,可以看到测试结果。
在这里插入图片描述
视频测试
准备一个视频文件test.mp4,放在/data/video路径下,输入命令:
python detect.py --source ./data/video/test.mp4 --weights ./weights/yolov5x6.pt --conf 0.4
三、训练自己的数据集
3.1 数据集的制作
参考这里制作VOC数据集。
第一步:将之做好的数据集放到yolov5-master根目录下,数据集文件夹结构为:
第二步:将数据集转换到yolo数据集格式。在VOCdata中新建一个voc_label.py文件,输入如下代码:
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
import shutil
sets=[('TrainVal', 'train'), ('TrainVal', 'val'), ('Test', 'test')]
classes = ['类别1', '类别2', '类别3', '类别4']
def convert(size, box):
dw = 1./size[0]
dh = 1./size[1]
x = (box[0] + box[1])/2.0
y = (box[2] + box[3])/2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x*dw
w = w*dw
y = y*dh
h = h*dh
return (x,y,w,h)
def convert_annotation(year, image_set, image_id):
in_file = open('VOC%s/Annotations/%s.xml'%(year, image_id))
out_file = open('VOC%s/labels/%s_%s/%s.txt'%(year, year, image_set, image_id), 'w')
tree=ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult) == 1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
bb = convert((w,h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + 'n')
def copy_images(year,image_set, image_id):
in_file = 'VOC%s/JPEGImages/%s.jpg'%(year, image_id)
out_flie = 'VOC%s/images/%s_%s/%s.jpg'%(year, year, image_set, image_id)
shutil.copy(in_file, out_flie)
wd = getcwd()
for year, image_set in sets:
if not os.path.exists('VOC%s/labels/%s_%s'%(year,year, image_set)):
os.makedirs('VOC%s/labels/%s_%s'%(year,year, image_set))
if not os.path.exists('VOC%s/images/%s_%s'%(year,year, image_set)):
os.makedirs('VOC%s/images/%s_%s'%(year,year, image_set))
image_ids = open('VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()
list_file = open('VOC%s/%s_%s.txt'%(year, year, image_set), 'w')
for image_id in image_ids:
list_file.write('%s/VOC%s/images/%s_%s/%s.jpgn'%(wd, year, year, image_set, image_id))
convert_annotation(year, image_set, image_id)
copy_images(year, image_set, image_id)
list_file.close()
转换后可以看到VOCData/VOCTrainval/和VOCData/VOCTest下生成了两个新的文件夹images和labels,以及相应的训练、验证、或测试的txt文件。
其中images和label文件夹下又有以相应的txt文件名命名的文件夹,里面存放了对应txt文件内容中的图片和标签信息。
现在整个数据集文件夹的结构如下:
3.2 修改配置文件
3.2.1 修改数据集方面的yaml文件
在准备完数据后,v5创新性的省略了.data和.names文件的配置,而是将二者合二为一到yaml中,在data文件夹下创建myvoc.yaml文件,输入以下信息
3.2.2 聚类获得先验框
在VOCData目录下创建程序两个程序 kmeans.py 以及 clauculate_anchors.py
不需要运行 kmeans.py,运行 clauculate_anchors.py 即可。
kmeans.py 程序如下:这不需要运行,也不需要更改
import numpy as np
def iou(box, clusters):
"""
Calculates the Intersection over Union (IoU) between a box and k clusters.
:param box: tuple or array, shifted to the origin (i. e. width and height)
:param clusters: numpy array of shape (k, 2) where k is the number of clusters
:return: numpy array of shape (k, 0) where k is the number of clusters
"""
x = np.minimum(clusters[:, 0], box[0])
y = np.minimum(clusters[:, 1], box[1])
if np.count_nonzero(x == 0) > 0 or np.count_nonzero(y == 0) > 0:
raise ValueError("Box has no area") # 如果报这个错,可以把这行改成pass即可
intersection = x * y
box_area = box[0] * box[1]
cluster_area = clusters[:, 0] * clusters[:, 1]
iou_ = intersection / (box_area + cluster_area - intersection)
return iou_
def avg_iou(boxes, clusters):
"""
Calculates the average Intersection over Union (IoU) between a numpy array of boxes and k clusters.
:param boxes: numpy array of shape (r, 2), where r is the number of rows
:param clusters: numpy array of shape (k, 2) where k is the number of clusters
:return: average IoU as a single float
"""
return np.mean([np.max(iou(boxes[i], clusters)) for i in range(boxes.shape[0])])
def translate_boxes(boxes):
"""
Translates all the boxes to the origin.
:param boxes: numpy array of shape (r, 4)
:return: numpy array of shape (r, 2)
"""
new_boxes = boxes.copy()
for row in range(new_boxes.shape[0]):
new_boxes[row][2] = np.abs(new_boxes[row][2] - new_boxes[row][0])
new_boxes[row][3] = np.abs(new_boxes[row][3] - new_boxes[row][1])
return np.delete(new_boxes, [0, 1], axis=1)
def kmeans(boxes, k, dist=np.median):
"""
Calculates k-means clustering with the Intersection over Union (IoU) metric.
:param boxes: numpy array of shape (r, 2), where r is the number of rows
:param k: number of clusters
:param dist: distance function
:return: numpy array of shape (k, 2)
"""
rows = boxes.shape[0]
distances = np.empty((rows, k))
last_clusters = np.zeros((rows,))
np.random.seed()
# the Forgy method will fail if the whole array contains the same rows
clusters = boxes[np.random.choice(rows, k, replace=False)]
while True:
for row in range(rows):
distances[row] = 1 - iou(boxes[row], clusters)
nearest_clusters = np.argmin(distances, axis=1)
if (last_clusters == nearest_clusters).all():
break
for cluster in range(k):
clusters[cluster] = dist(boxes[nearest_clusters == cluster], axis=0)
last_clusters = nearest_clusters
return clusters
if __name__ == '__main__':
a = np.array([[1, 2, 3, 4], [5, 7, 6, 8]])
print(translate_boxes(a))
运行:clauculate_anchors.py
会调用 kmeans.py 聚类生成新anchors的文件
程序如下:
需要更改第 9 、13行文件路径 以及 第 16 行标注类别名称
# -*- coding: utf-8 -*-
# 根据标签文件求先验框
import os
import numpy as np
import xml.etree.cElementTree as et
from kmeans import kmeans, avg_iou
FILE_ROOT = "D:/Yolov5/yolov5/VOCData/" # 根路径
ANNOTATION_ROOT = "Annotations" # 数据集标签文件夹路径
ANNOTATION_PATH = FILE_ROOT + ANNOTATION_ROOT
ANCHORS_TXT_PATH = "D:/Yolov5/yolov5/VOCData/anchors.txt" #anchors文件保存位置
CLUSTERS = 9
CLASS_NAMES = ['light', 'post'] #类别名称
def load_data(anno_dir, class_names):
xml_names = os.listdir(anno_dir)
boxes = []
for xml_name in xml_names:
xml_pth = os.path.join(anno_dir, xml_name)
tree = et.parse(xml_pth)
width = float(tree.findtext("./size/width"))
height = float(tree.findtext("./size/height"))
for obj in tree.findall("./object"):
cls_name = obj.findtext("name")
if cls_name in class_names:
xmin = float(obj.findtext("bndbox/xmin")) / width
ymin = float(obj.findtext("bndbox/ymin")) / height
xmax = float(obj.findtext("bndbox/xmax")) / width
ymax = float(obj.findtext("bndbox/ymax")) / height
box = [xmax - xmin, ymax - ymin]
boxes.append(box)
else:
continue
return np.array(boxes)
if __name__ == '__main__':
anchors_txt = open(ANCHORS_TXT_PATH, "w")
train_boxes = load_data(ANNOTATION_PATH, CLASS_NAMES)
count = 1
best_accuracy = 0
best_anchors = []
best_ratios = []
for i in range(10): ##### 可以修改,不要太大,否则时间很长
anchors_tmp = []
clusters = kmeans(train_boxes, k=CLUSTERS)
idx = clusters[:, 0].argsort()
clusters = clusters[idx]
# print(clusters)
for j in range(CLUSTERS):
anchor = [round(clusters[j][0] * 640, 2), round(clusters[j][1] * 640, 2)]
anchors_tmp.append(anchor)
print(f"Anchors:{anchor}")
temp_accuracy = avg_iou(train_boxes, clusters) * 100
print("Train_Accuracy:{:.2f}%".format(temp_accuracy))
ratios = np.around(clusters[:, 0] / clusters[:, 1], decimals=2).tolist()
ratios.sort()
print("Ratios:{}".format(ratios))
print(20 * "*" + " {} ".format(count) + 20 * "*")
count += 1
if temp_accuracy > best_accuracy:
best_accuracy = temp_accuracy
best_anchors = anchors_tmp
best_ratios = ratios
anchors_txt.write("Best Accuracy = " + str(round(best_accuracy, 2)) + '%' + "rn")
anchors_txt.write("Best Anchors = " + str(best_anchors) + "rn")
anchors_txt.write("Best Ratios = " + str(best_ratios))
anchors_txt.close()
会生成anchors文件。如果生成文件为空,重新运行即可。
第二行 Best Anchors 后面需要用到。
3.2.3 修改网络参数方面的yaml文件
这个相当于以前版本的.cfg文件,在models/yolov5x.yaml【当然,你想用哪个模型就去修改对应的yaml文件】,就修改一下类别数量;
修改anchors,根据 anchors.txt 中的 Best Anchors 修改,需要取整(四舍五入、向上、向下都可以)。
保持yaml中的anchors格式不变,按顺序一对一即可。
3.2.4 修改train.py中的一些参数
在train.py中修改一下训练参数,也可以直接在训练语句中重写,修改的话只是修改默认值。
parser.add_argument('--epochs', type=int, default=200) # 根据需要自行调节训练的epoch
parser.add_argument('--batch-size', type=int, default=8) # 根据自己的显卡调节,显卡不好的话,就调小点
parser.add_argument('--cfg', type=str, default='models/yolov5s.yaml', help='*.cfg path') # 根据需要,自行选择模型
parser.add_argument('--data', type=str, default='data/myvoc.yaml', help='*.data path') # data设置为前两步中我们新建的myvoc.yaml
parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='train,test sizes') # 可调可不调
parser.add_argument('--device', default='0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') # 使用CPU还是GPU训练
3.3 训练
3.3.1 在预训练模型基础上进行迁移训练
在yolov5-master目录中打开命令提示符,再激活torch环境,然后输入下列命令:
python train.py --img 640 --batch 8 --epoch 200 --data ./data/myvoc.yaml --cfg ./models/yolov5x.yaml --weights ./weights/yolov5x6.pt --device 0 --workers 8
参数解释:
--img:输入图片尺寸
--batch:batch_size大小
--epoch:训练周期
--data:上面修改过的数据集方面的信息文件
--cfg:模型的配置文件,自行选择模型,当然这里选的是哪个模型,就要在3.2.2中修改相应的配置文件中的类别数
--weights:预训练模型,之前下载好的,这里选择的预训练模型需要跟–cfg中相同
--device:CUDA设备,如果使用CPU训练就写cpu,如果用一块GPU训练就写0,如果有多块GPU就写 0,1,2,3
--workers:线程数
其他可修改参数:
--rect:rectangular training,布尔值
--resume:是否从最新的last.pt中恢复训练,布尔值
--nosave:仅仅保存最后的checkpoint,布尔值
--notest:仅仅在最后的epoch上测试,布尔值
--evolve:进化超参数(evolve hyperparameters),布尔值
--bucket:gsutil bucket,默认值’’
--cache-images:缓存图片可以更快的开始训练,布尔值
--name:如果提供,将results.txt重命名为results_name.txt
--adam:使用adam优化器,布尔值
--multi-scale:改变图片尺寸img-size +/0- 50%,布尔值
--single-cls:训练单个类别的数据集,布尔值
当然这些参数可以直接在train.py文件中直接修改默认值,然后直接运行train.py文件。
3.3.2 从头开始训练
将train.py中的第458行–weights的默认参数删掉
在上面命令的基础上,去掉–weight,在yolov5-master目录中打开命令提示符,再激活torch环境,然后输入下列命令:
python train.py --img 640 --batch 8 --epoch 200 --data ./data/myvoc.yaml --cfg ./models/yolov5x.yaml --device 0 --workers 8
当然如果其他参数也修改为适合自己训练的参数了,也可以直接运行train.py文件。
3.3.3 tensorboard可视化训练过程
在yolov5-master目录中打开命令提示符,再激活torch环境,然后输入下列命令:
tensorboard --logdir=runs/train/exp
参数解释:
tensorboard:可视化tensorboard命令
–logdir:训练日志文件所在目录(通常在训练的时候会自动建立runs/train/expn这么一个路径,其训练过程中的所有信息都保存在这个文件夹下,包括训练的日志文件。其中expn不是一个固定的文件夹名字,这里面的n是根据训练的次数依次进行排序的,在进行可视化的时候注意找对文件夹)
然后复制下面的地址,用任意一个浏览器打开
得到训练过程中的各种指标的变化信息:
可能遇到的问题:
若在浏览器中无法打开改地址,可能的原因是6006端口号被占用,这时候可以指定从其他端口打开,命令如下:
tensorboard --logdir=runs/train/exp --port=6008
3.4 测试
3.4.1 计算mAP
主要使用根目录下的test.py文件,有两种方法,建议使用方法二,因为test.py文件在训练过程中也会使用到,第一种方法修改了默认的参数,可能会导致以后再次训练时出错。
方法一:修改test.py文件中的相关参数,主要是修改–weights模型地址、–data数据集配置文件地址,–task当前进行的任务,这三个参数,其他的都可以看情况自行修改。
各种可修改参数的解释:
--weights :模型路径,
--data:数据集的配置文件
--batch-size:默认值32
--img-size:图片大小,默认640
--conf-thres:目标置信度阈值
--iou-thres:NMS的IOU阈值
--save-json:把结果保存为cocoapi-compatible的json文件
--task:当前进行的任务,改为test,可选其他值:val, test, study
--device:cuda设备,例如:0或0,1,2,3或cpu,默认’’
--half:半精度的FP16推理
--single-cls:将其视为单类别,布尔值
--augment:增强推理,布尔值
--verbose:显示类别的mAP,布尔值
修改完之后,直接运行test.py就能够得到测试的结果了,输出内容:
并且在runstestexp目录下有更多测试的细节信息:
使用该方法进行测试后,在下一次训练之前一定要将–task参数的默认值改回val
方法二:使用命令给定各个参数值进行测试,不修改test.py文件中的参数。
在yolov5-master目录中打开命令提示符,再激活torch环境,然后输入下列命令:
python test.py --weights ./runs/train/exp/weights/best.pt --data ./data/myvoc.yaml --batch 32 --img 640 --task test --device 0
其他可修改的参数和运行结果同上。
3.4.2 图片或视频测试
同样也有两种方式可以
在yolov5-master目录中打开命令提示符,再激活torch环境,然后输入下列命令:
python detect.py --source ./data/images/ --weights ./runs/train/exp14/weights/best.pt --conf 0.4
参数解释:
--weights :模型路径,根据自己模型的保存位置填写
--source:输入的数据源,可以是:图片、目录、视频、网络摄像头、http和rtsp流。
--output: 输出检测结果的路径,默认值为:inference/output
--img-size :图片的大小(pixels),默认值为:640
--conf-thres:对象的置信度阈值(object confidence threshold),默认值为:0.4
--iou-thres :NMS的IOU阈值( IOU threshold for NMS),默认值为:0.5
--device:cuda设备,例如:0或0,1,2,3或cpu,默认’’
--view-img :显示结果,‘布尔值,默认为true’
--save-txt :把结果保存到*.txt文件中
--classes:过滤类别 CLASSES [CLASSES …],filter by class
--agnostic-nms:类不可知 NMS
--augment:增强推理(augmented inference)
对–source参数的特别说明:
(1)检测单张图片,直接写该图片的路径加上图片名,如–source ./data/images/test.jpg;
(2)检测整个文件夹下所有的图片,就不用具体些到图片名,路径写到该文件夹就成,例如:–source ./data/images/,表示检测images文件夹下所有的图片。
(3)检测视频文件:直接写路径加视频的名称,如:–source ./data/images/test.mp4
(4)网络摄像头传输视频,使用电脑自带的摄像头检测:将source参数设置为0–source 0(我使用的台式机,没有自带的摄像头,暂时还没测试这一条)
(5)rtsp传输视频,使用外部摄像头,source参数设置为:–source + rtsp连接命令,不同厂家的摄像头使用rtsp连接的方式有些许差别,如海康威视的摄像头连接命令为–source rtsp://admin:密码@IP地址:554/STD_H265/ch1/main/av_stream,其中验证码为摄像头上贴的验证码,一个设备一个验证码;IP地址为当前摄像头的IP地址,摄像头通过网络进行连接,将会被分配一个IP,通过海康威视的官方APP能够查看该设备的IP。
参考(感谢)
https://blog.csdn.net/qq_40927867/article/details/115768888
https://blog.csdn.net/qq_45945548/article/details/121701492
最后
以上就是唠叨糖豆为你收集整理的yolov5使用自己的数据集训练一、环境与配置二、源码测试三、训练自己的数据集的全部内容,希望文章能够帮你解决yolov5使用自己的数据集训练一、环境与配置二、源码测试三、训练自己的数据集所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复