【YOLO】yolov5训练自己的数据集

news2024/12/23 17:49:05

文章目录

  • 0 前期教程
  • 1 前言
  • 2 准备数据集
    • 2.1 数据集来源
    • 2.2 数据集结构介绍
    • 2.3 标签格式的转换
  • 3 训练以及训练结果
    • 3.1 训练
    • 3.2 测试
  • 4 数据标注
  • 5 后续教程

0 前期教程

  • 【Python】朴实无华的yolov5环境配置

1 前言

  上面前期教程中,大致介绍了yolov5开发环境的配置方法和yolov5项目的基本结构,下一步就是基于yolov5预训练模型来训练自己的数据集,这对于只是想要使用yolov5这个工具的人,还是想要深入研究yolov5类似的目标识别算法的人,都是绕不开的入门操作,本文将根据查找的资料以及自己的经验来简单介绍这个过程。

2 准备数据集

2.1 数据集来源

  由于数据标注这个过程过于繁琐,耗时长,所以个人觉得可以先拿网上公开数据集来尝试,学会之后再尝试自己标注数据然后训练,只是后者多了一个标注的步骤,其他都是完全一样的。
  这里推荐的数据集是PASCAL VOC2012数据集,它是一个世界级计算机视觉比赛专用的数据集,包含了生活中常见的20种目标,其数据类别分布如下图所示。
在这里插入图片描述

官网下载链接

  打开之后,如下图所示,直接点击第一个下载即可。
在这里插入图片描述

2.2 数据集结构介绍

  下载完得到的是一个压缩包,它文件夹结构如下所示

├───Annotations       //标注文件
├───ImageSets         //图像数据
│   ├───Action        //人物动作
│   ├───Layout        //人各个部位数据集
│   ├───Main          //主要数据集(含训练集和测试集)
│   └───Segmentation  //语义分割训练和测试集
├───JPEGImages        //所有图片
├───SegmentationClass //语义分割类别
└───SegmentationObject//语义分割物体

其中,Main文件夹下的数据集分布非常有规律:

在这里插入图片描述

每一种目标都对应三个txt文件,以train结尾的为训练集,以val结尾的为测试集,以trainval结尾的为训练集和测试集之和,这里以bottle该目标的数据集为例:
在这里插入图片描述
其中,前面一列为JPEGImages文件夹下对应的图片文件名,后一列表示目标信息:-1代表没有该目标;1表示有该目标;0表示该目标检测有困难。

2.3 标签格式的转换

  Annotations文件夹下存放着所有图片的标签信息,标签文件名和图片文件名是一一对应的,使用时根据文件名查找即可。
  VOC数据集中的标签是xml格式,但yolov5训练时要使用的是yolo格式,因此需要先进行转换。以其中一个标签为例:
在这里插入图片描述

对于目标识别任务来说,需要用到的主要是两个标签,分别是<size>标签和<object>标签。其中<size>标签主要是描述图片的大小信息;<object>标签描述图片中的目标信息,一个object标签对应一个目标,其中<name>为目标信息,<truncated>表示目标是否截断(1为截断);<difficult>表示目标是否难以检测(1为难检测);<bndbox>表示目标的左上角和右下角坐标。

  而yolo格式的标签如下所示
在这里插入图片描述

分别表示:[目标类别(一般用数字表示) x_center y_center width height],因此,在训练该数据集之前,还需要进行标签格式的转换,主要使用到的就是xml这个库来对xml文件进行解析。

示例代码如下所示:

import xml.etree.ElementTree as ET
import os
import shutil
import tqdm

def convert(size, box):
    '''	@func: 将box的坐标转换为yolo需要的格式
        @para	size: 图片的尺寸, eg:[500, 200]
        @para	box: box的坐标, [xmin, xmax, ymin, ymax]
        @return: 转换后的yolo格式坐标[x_center, y_center, width, height]
    '''
    dw = 1. / (size[0])
    dh = 1. / (size[1])
    x = (box[0] + box[1]) / 2.0 - 1
    y = (box[2] + box[3]) / 2.0 - 1
    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 transfer(xmlfile:str, txtfile:str, classes:list[str]=['bottle']):
    '''	@func: 将xml文件转换为txt文件
        @para	xmlfile: xml文件的路径
        @para	txtfile: txt文件的路径
        @return: None
    '''
    in_file = open(xmlfile, encoding='utf-8')
    out_file = open(txtfile, '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'):
        cls = obj.find('name').text
        if cls not in classes: continue
        num_id = classes.index(cls) #找到该类别的序号
        xmlbox = obj.find('bndbox')
        # 坐标转换
        box = [float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text),
               float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text)]
        bb = convert((w, h), box)
        out_file.write(str(num_id) + ' ' + " ".join([str(a) for a in bb]) + '\n')


def construct(train_set, val_set, JPEG_Path, Ano_Path, dataset_path='./dataset', classes:list[str]=['bottle']):
    '''	@func: 将VOC数据集构建成yolo训练所需的数据集格式
        @para	train_set: 训练集的路径txt
        @para	val_set: 验证集的路径txt
        @para	JPEG_Path: VOC数据集图片文件的路径
        @para	Ano_Path: VOC数据集标注文件所在路径
        @para	dataset_path: 构造好的数据集所在路径,可以不存在, 默认为当前路径
        @para	classes: 类别列表
        @return: None
    '''
    os.makedirs(dataset_path, exist_ok=True)
    img_path = os.path.join(dataset_path, 'images'); os.makedirs(img_path, exist_ok=True)
    img_train_path = os.path.join(img_path, 'train'); os.makedirs(img_train_path, exist_ok=True)
    img_val_path = os.path.join(img_path, 'val'); os.makedirs(img_val_path, exist_ok=True)
    label_path = os.path.join(dataset_path, 'labels'); os.makedirs(label_path, exist_ok=True)
    label_train_path = os.path.join(label_path, 'train'); os.makedirs(label_train_path, exist_ok=True)
    label_val_path = os.path.join(label_path, 'val'); os.makedirs(label_val_path, exist_ok=True)

    with open(train_set, 'r') as f:
        print('Start converting train set...')
        for line in tqdm.tqdm(f.readlines()):
            num = line.strip().split()[1]
            filename = line.strip().split()[0]
            shutil.copy(os.path.join(JPEG_Path, filename + '.jpg'), img_train_path)
            if num == '-1':
                with open(os.path.join(label_train_path, filename + '.txt'), 'w') as f:
                    f.write("") # 写入空值
            else:
                transfer(os.path.join(Ano_Path, filename + '.xml'),
                         os.path.join(label_train_path, filename + '.txt'), classes)

    with open(val_set, 'r') as f:
        print('Start converting val set...')
        for line in tqdm.tqdm(f.readlines()):
            num = line.strip().split()[1]
            filename = line.strip().split()[0]
            shutil.copy(os.path.join(JPEG_Path, filename + '.jpg'), img_val_path)
            if num == '-1':
                with open(os.path.join(label_val_path, filename + '.txt'), 'w') as f:
                    f.write("")
            else:
                transfer(os.path.join(Ano_Path, filename + '.xml'),
                         os.path.join(label_val_path, filename + '.txt'), classes)

    print('Done!')

    # 写入yaml文件
    with open(os.path.join(dataset_path, 'dataset.yaml'), 'w') as f:
        f.write('path: {}\n'.format(dataset_path))
        f.write('train: images/train\n')
        f.write('val: images/val\n\n')
        f.write('nc: {}\n'.format(len(classes)))
        f.write('names: {}'.format(classes))

if __name__ ==  "__main__":
    dataset = r'C:\Users\Zeoy\Desktop\dataset' # 构造好的数据集所在路径
    train_set = r'C:\Users\Zeoy\Desktop\VOC2012\ImageSets\Main\bottle_train.txt'
    val_set = r'C:\Users\Zeoy\Desktop\VOC2012\ImageSets\Main\bottle_val.txt'
    JPEG_Path = r'C:\Users\Zeoy\Desktop\VOC2012\JPEGImages'
    Ano_Path = r'C:\Users\Zeoy\Desktop\VOC2012\Annotations'
    classes = ['bottle'] # 如果类别较多,可以用用numpy读取txt
    construct(train_set, val_set, JPEG_Path, Ano_Path, dataset, classes)

使用时,注意替换main部分的train_set, val_set, JPEG_Path, Ano_Path

这个代码需要注意的是,在使用yolov5进行模型训练时,填入的参数并不是数据集的路径,而是yaml文件,而在yaml文件中,只有图片所在路径,而没有标注文件所在路径,如下图所示:
在这里插入图片描述
这是因为yolov5默认读取的文件夹路径是固定的,要求文件夹名字和下面保持一致:

├───images
│   ├───train
│   └───val
└───labels
    ├───train
    └───val

3 训练以及训练结果

3.1 训练

  上一步得到了数据集和yaml文件,下一步就是利用该数据集进行训练得到pt模型。这里使用的是最外层文件夹的train.py文件,使用方法参考文件开头的注释即可。
在这里插入图片描述

直接在命令行输入:

python train.py --data "C:\Users\Zeoy\Desktop\dataset\dataset.yaml" --weights yolov5s.pt --img 640 --batch-size -1

其中,--data参数为刚刚构建好的数据集所在的路径;--batch-size参数为一次性读取图片的数量,设置为-1程序将根据显卡的显存大小自动分配。

进入到如下界面,就可以耐心等待了,默认的epoch数为100,如果觉得不合适可以添加参数--epochs xxxx来设置。

在这里插入图片描述

3.2 测试

  训练完毕后,在run/train/exp?文件夹下有一个weights文件夹,里面即是训练得到的权值模型,以pt结尾,一般会有两个:best.pt 和 last.pt,一般选择前者。接下来就是利用detect.py文件读取模型并对测试图片进行处理。

  方法和运行train.py差不多,首先看一下文件开始的注释部分,就知道怎么使用了
在这里插入图片描述

可以看出,yolov5测试时的输入数据可以是图片,视频,包含图片或视频的文件夹,甚至是摄像头和网络视频流,使用非常方便。而使用的模型格式也可以是多种深度学习框架下的结果。

  复制一下pt文件的路径,然后在命令行再次输入:

python detect.py --weights 'C:\Users\Zeoy\Desktop\Code\Python\yolov5-master\runs\train\exp19\weights\best.pt' --source 'C:\Users\Zeoy\Desktop\img.png'

--weights参数即刚刚训练得到的pt文件路径,--source参数即需要测试的数据来源。

4 数据标注

  以上就是基于VOC数据集并利用yolov5训练并测试的全部过程了,前面也提到,使用开源数据集还是自己数据集的差别就是标注的这个过程,因此,再简单介绍一下数据集标注的方法。

  目前网上绝大多数标注图片数据的方法都是使用labelImg这个工具,它是python的一个第三方库,可以直接使用pip或conda安装:

pip install labelImg

  不过,尤为需要注意的是,这个库对于新版的python支持似乎不够好,在运行时会经常出现卡退的情况,需要修改该库的内部代码,我比较懒,这里是直接使用python3.8安装(恰好电脑上也有python3.8),运行是没有问题的。

  labelImg使用较为简单,参考下图即可。

在这里插入图片描述

5 后续教程

  • 【YOLO】目标识别模型的导出和opencv部署

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/683178.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows 10 安装 Redis

安装 Redis 1&#xff1a;下载 下载 Windows 版本的 Redis&#xff0c;点击这里 下载redis 2&#xff1a;解压 解压下载的 zip 包到任意目录&#xff0c;如我的目录&#xff1a; 3&#xff1a;启动 命令行进入刚才解压文件的根目录下&#xff0c;然后执行如下命令即可&a…

跌倒检测 关节点角度数学计算

参考&#xff1a; https://github.com/GitGudwl/MediapipePoseEstimationForFallDetection/tree/main https://blog.csdn.net/weixin_45824067/article/details/130646962 1、mediapipe 根据关节点角度计算 1、11与12取中间点&#xff0c;记为center_up; 23 与24取中间点记为c…

为什么自学Python会从入门到放弃?

前言 Python现在非常火&#xff0c;语法简单而且功能强大&#xff0c;很多同学都想学Python&#xff01;所以蛋糕给各位看官们准备了高价值Python学习视频教程及相关电子版书籍&#xff0c;欢迎前来领取&#xff01; 下面小编与大家分享一下自学Python的人&#xff0c;放弃的…

【unity造轮子】Unity ShaderGraph使用教程与各种特效案例(持续更新)

文章目录 一、前言二、ShaderGraph1.什么是ShaderGraph2.在使用ShaderGraph时需要注意以下几点&#xff1a;3.优势4.项目 三、实例效果外发光进阶&#xff1a;带方向的菲涅尔边缘光效果裁剪进阶 带边缘色的裁剪溶解进阶 带边缘色溶解卡通阴影水波纹积雪效果不锈钢效果UV抖动水波…

使用编码工具

本文主要介绍了对句子编码的过程&#xff0c;以及如何使用PyTorch中自带的编码工具&#xff0c;包括基本编码encode()、增强编码encode_plus()和批量编码batch_encode_plus()。 一.对一个句子编码例子 假设想在要对句子’the quick brown fox jumps over a lazy dog’进行编码…

【K8S系列】深入解析K8S存储

序言 做一件事并不难&#xff0c;难的是在于坚持。坚持一下也不难&#xff0c;难的是坚持到底。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标记二级论点 Kubernetes (k8s) 是一…

ppp协议,一文带你了解

一、PPP协议简介 PPP&#xff08;Point-to-Point Protocol&#xff09;是一种数据链路层协议&#xff0c;用于在两个节点之间建立点对点的数据通信连接。PPP协议是TCP/IP协议族中的一员&#xff0c;它可以在串行通信线路上传输IP数据包&#xff0c;支持多种网络层协议&#xff…

C++ Primer 第11章关联容器

11.1 使用关联容器 map类型通常被常被称为关联数组。关联数组与正常数组类似&#xff0c;不同之处在于其下标不必是整数set就是关键字的简单集合&#xff0c;当想知道一个值是否存在时&#xff0c;set是最有用的 使用map #include<iostream> #include<string> #…

智慧水务物联网数据采集平台和营收管理平台建设

平台概述 智慧水务物联网数据采集平台是以物联感知技术、大数据、智能控制、云计算、人工智能、数字孪生、AI算法、虚拟现实技术为核心&#xff0c;以监测仪表、通讯网络、数据库系统、数据中台、模型软件、前台展示、智慧运维等产品体系为支撑&#xff0c;以城市水资源、水生…

MySQL - 第10节 - MySQL索引特性

1.索引的概念 索引的概念&#xff1a; • 数据库表中存储的数据都是以记录为单位的&#xff0c;如果在查询数据时直接一条条遍历表中的数据记录&#xff0c;那么查询的时间复杂度将会是O(N)。 • 索引的价值在于提高海量数据的检索速度&#xff0c;只要执行了正确的创建索引的操…

B049-cms04-浏览次数 富文本 轮播图 上传

目录 浏览次数页面加载发送请求后台处理请求前台展示 展示日期富文本编辑static下引入富文本资源文件夹模态框文本域替换成如下内容底部引入相关文件调整模态框样式把富文本选项移到模态框前面上传表情或图片等富文本添加操作手动清空富文本编辑器内容修改操作手动回显富文本编…

postman接口测试—Restful接口开发与测试

开发完接口&#xff0c;接下来我们需要对我们开发的接口进行测试。接口测试的方法比较多&#xff0c;使用接口工具或者Python来测试都可以&#xff0c;工具方面比如之前我们学习过的Postman或者Jmeter &#xff0c;Python脚本测试可以使用Requests unittest来测试。 测试思路…

抖音短视频矩阵系统源码:技术开发与实践

目录 一.短视频账号矩阵管理系统囊括的技术 1.开发必备的开发文档说明&#xff1a; 二.技术文档分享&#xff1a; 1.底层框架系统架构&#xff1a; 2.数据库接口设计 1.技术开发必备的开发文档说明&#xff1a; 1.1系统架构&#xff1a; 抖音SEO排名系统主要由以下几个模…

PHP 对PDF文件实现数字签名

PHP通过TCPDF库对生成的PDF文件进行数字签名。 效果如下&#xff1a; 这个是因为签名证书不在可信任证书列表中。 目录 准备数字证书 1.申请数字证书 2.自签名证书 安装TCPDF 证书签名 设置证书路径 设置证书信息 设置文档签名 设置签名外观 图像签名外观 空签名外观…

git使用命令技巧

文章目录 前言查看提交用户名更改提交用户名查看文件的diff查看提交记录Git 本地分支管理查看、切换、创建和删除分支 前言 我们在使用git的时候&#xff0c;提交后会看到如下记录&#xff1a; 经常会遇到提交后&#xff0c;这个作者的名字和自己设置的名字不一致&#xff0…

Python文件操作指南:编码、读取、写入和异常处理

文章目录 文件的编码文件的读取使用 read 方法读取整个文件内容&#xff1a;使用 readlines 方法按行读取文件内容并存储到列表中&#xff1a;使用迭代器遍历文件内容&#xff1a; 文件的写入文件的追加文件操作的综合案例文件的关闭文件的存在性检查异常处理文件操作的更多方法…

如何下载外文文献,PubMed中的文献怎么获取

查找外文文献常用数据库有&#xff1a;PubMed、ScienceDirect、Wiley、Web of Science、EI等等。今天单独讲一下PubMed数据库文献的获取方法。 PubMed是生物医药领域使用最广泛的免费文献检索系统。但PubMed 的资讯并不包括期刊论文的全文&#xff0c;只是提供了指向全文提供者…

Meta Quest v55系统推送,浏览器支持多点触摸

6月25日青亭网报道&#xff0c;此前我们报道了Quest v55公测版系统更新解锁了GPU和CPU频率限制&#xff0c;以及动态分辨率渲染功能。 现在v55系统正式向所有人开启推送&#xff0c;并且加入了更多功能&#xff1a; 1&#xff0c;解锁GPU和CPU限制&#xff0c;支持动态分辨率渲…

Linux进程间通信——管道(上)

目录 前文 一&#xff0c;进程间通信介绍 二&#xff0c;什么是管道&#xff1f; 三&#xff0c;管道的基本原理 3.1 匿名管道 3.2 管道基本原理 四&#xff0c;样例代码 五&#xff0c;管道的读写规则 六&#xff0c;管道的特点 总结 前文 本文主要是讲解一下进程间…

一文告诉你低代码的价值

随着数字化时代的到来&#xff0c;软件开发已经成为企业不可或缺的一部分。然而&#xff0c;传统的软件开发过程往往缓慢而昂贵。由此&#xff0c;低代码开发平台的出现给企业带来了一种新的软件开发方式。在本文中&#xff0c;我们将探讨低代码开发平台的用处和发展史&#xf…