Windows安装Yolov8训练、验证、推理自定义数据教程

       YOLOv8是Ultralytics 2023年的巨作,建立在深度学习和计算机视觉的前沿进步之上,在速度和准确性方面提供无与伦比的性能。 其流线型设计使其适用于各种应用程序,并可轻松适应从边缘设备到云 API 的不同硬件平台。

引言

YOLOv8本次升级主要更新了如下部分:

  • 更友好的安装/运行方式;
  • 速度更快、准确率更高;
  • 新的backbone,将YOLOv5中的C3更换为C2F;
  • YOLO系列第一次尝试使用anchor-free;
  • 新的损失函数。

yolov8的推理速度对比如上图,极大的提高了训练速度。

基础篇

1.环境准备

在这之前,需要先准备主机的环境,环境如下:

  • Windows11
  • cuda11.5
  • pytorch:1.11.0
  • torchvision:0.12.0

2.安装

        官方提供了两种形式的安装方法,这里如果只是玩玩的话建议使用第一种方法安装;如果为了扩展,开发的可以用第二种方法安装,这样可以更改源码并立即生效,如果不需要更改源码就直接采用第一种安装方法:pip install ultralytics也是可以的。(本文采用的是第二种方法,为了更好的管理项目)

(1)准备好环境后,先进入自己带pytorch虚拟环境,与之前的yolo系列安装都不太一样,yolov8仅需要安装ultralytics这一个库就ok了。

 pip install ultralytics

(2)另一种方法稍显麻烦,需要先克隆git仓库,再进行安装;二者取其一即可。Linux可以使用下面命令进行环境配置,当然如果是windows下,直接在官网https://github.com/ultralytics/ultralytics下载压缩包,解压即可。

git clone https://github.com/ultralytics/ultralytics  # clone repo
cd ultralytics
pip install -e .

2.1环境配置

其中requirements.txt 中包含了必要的配置环境,基本如下:

3.10>=Python>=3.7
torch>=1.7.0
torchvision>=0.8.1

创建虚拟环境:关于pytorch gpu版安下载和安装见博客:https://blog.csdn.net/lzdjlu/article/details/134772588?spm=1001.2014.3001.5502

#创建虚拟环境
conda create -n conda activate torch1-11_yolov8_py39gpu python=3.9
activate conda activate torch1-11_yolov8_py39gpu
conda info --envs

#安装gpu版本pytorch
pip install D:\pytorch_whl\torch-1.11.0+cu115-cp39-cp39-win_amd64.whl
 
pip install D:\pytorch_whl\torchaudio-0.11.0+cu115-cp39-cp39-win_amd64.whl
 
pip install D:\pytorch_whl\torchvision-0.12.0+cu115-cp39-cp39-win_amd64.whl

2.2 下载yolov8源码,在pycharm中进行配置

下载好yolov8源码(ultralytics-main.zip)解压之后(ultralytics-main)用pycharm进行打开,然后把interpreter设置为刚刚创建的虚拟环境:

2.3 在pycharm的终端terminal中安装requirements.txt中的相关包

注意此时虚拟环境的名字为所创建激活的虚拟环境名称,如下图红色框中的虚拟环境名称:

#安装其他依赖
pip install -r requirements.txt  -i https://pypi.tuna.tsinghua.edu.cn/simple/

#安装ultralytics,可以直接使用yolo
pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple/

pip install yolo

安装完成后得到如下提示表示所需环境安装完成:

测试环境是否配置成功,出现以下信息表示环境安装配置成功:

2.4 预训练权重的下载 

        源码下载界面中的readme文件中往下翻,找到yolovn8的目标检测预训练权重,点击下载,然后把下载好的文件放在项目代码的根目录下(n、s、m、l、x五种不同规模的预训练模型选择一个即可):

把下载好的预训练权重文件(yolov8n.pt)放在项目(ultralytics-main)根目录处。

2.5 验证环境配置是否成功

 复制下面的代码来验证一下是否可以运行:

yolo predict model=yolov8n.pt source='ultralytics/assets/bus.jpg'

在运行过程中遇到了Error:No such command 'predict'问题,解决方案如下,只需到项目根目录(ultralytics-main)下执行此命令:

python setup.py install

执行上述命令成功后再次执行下面的代码来验证一下是否可以运行:

yolo predict model=yolov8n.pt source='ultralytics/assets/bus.jpg'

成功运行上述命令得到如下结果,可以看到运行所占用的GPU资源信息、运行结果信息( 4 persons, 1 bus, 1 stop sign, 14.0ms)、结果保存信息(目标检测结果保存在runs\detect\predict目录下):

成功运行后在项目根目录下(ultralytics-main)可以看到新建了runs\detect\predict目录并将目标检测结果保存在该目录下!

提升篇

3.数据集的准备

3.1 coco128数据集下载

下载coco128数据集文件:

https://ultralytics.com/assets/coco128.zip

下载之后在项目根目录下新建一个datasets文件夹用于存放数据集,然后把coco128数据集放在下该文件夹中。

  • images:下面的子文件夹为train2017,存放所有的训练图片;
  • labels:下面的子文件夹为labels2017,存放所有的标注标签。

自定义数据集的命名和排列方式也要按这个格式来。

3.1.1coco数据集的yaml文件

       coco128的yaml文件在源码中是有的,如下所示路径ultralytics/cfg/datasets/coco128.yaml,可以看到给出了数据集的路径、训练集和验证集所在的位置以及类别列表,所以仿照该文件写一个我们自己数据集的yaml文件。

3.2 自建数据集 

3.2.1指定格式存放自定义数据集

        采用行人跌倒数据集fall_dataset存放到datasets目录下,新建Annotations, JPEGImages, ImageSets, labels 四个文件夹:

  • JPEGImages目录下存放数据集的图片文件;
  • Annotations目录下存放图片的xml文件(labelImg标注);

目录结构如下所示:

3.2.2将.xml格式的标签文件转换为.txt格式

将xml文件转换成YOLO系列标准读取的txt文件,在同级目录下再新建一个文件xml_2_txt.py
注意classes = ['…']一定需要填写自己数据集的类别,在这里我是一个类别'fall',因此classes = ['fall'],如果数据集中的类别比较多不想手敲类别的,可以使用(4)中的脚本直接获取类别,同时还能查看各个类别的数据量,如果不想可以直接跳过(4)。xml_2_txt.py代码如下所示:

# -*- coding: utf-8 -*-
# xml解析包
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join


sets = ['train', 'test', 'val']
classes = ['fall']


# 进行归一化操作
def convert(size, box): # size:(原图w,原图h) , box:(xmin,xmax,ymin,ymax)
    dw = 1./size[0]     # 1/w
    dh = 1./size[1]     # 1/h
    x = (box[0] + box[1])/2.0   # 物体在图中的中心点x坐标
    y = (box[2] + box[3])/2.0   # 物体在图中的中心点y坐标
    w = box[1] - box[0]         # 物体实际像素宽度
    h = box[3] - box[2]         # 物体实际像素高度
    x = x*dw    # 物体中心点x的坐标比(相当于 x/原图w)
    w = w*dw    # 物体宽度的宽度比(相当于 w/原图w)
    y = y*dh    # 物体中心点y的坐标比(相当于 y/原图h)
    h = h*dh    # 物体宽度的宽度比(相当于 h/原图h)
    return (x, y, w, h)    # 返回 相对于原图的物体中心点的x坐标比,y坐标比,宽度比,高度比,取值范围[0-1]


# year ='2012', 对应图片的id(文件名)
def convert_annotation(image_id):
    '''
    将对应文件名的xml文件转化为label文件,xml文件包含了对应的bunding框以及图片长款大小等信息,
    通过对其解析,然后进行归一化最终读到label文件中去,也就是说
    一张图片文件对应一个xml文件,然后通过解析和归一化,能够将对应的信息保存到唯一一个label文件中去
    labal文件中的格式:calss x y w h  同时,一张图片对应的类别有多个,所以对应的bunding的信息也有多个
    '''
    # 对应的通过year 找到相应的文件夹,并且打开相应image_id的xml文件,其对应bund文件
    in_file = open('data/Annotations/%s.xml' % (image_id), encoding='utf-8')
    # 准备在对应的image_id 中写入对应的label,分别为
    # <object-class> <x> <y> <width> <height>
    out_file = open('data/labels/%s.txt' % (image_id), 'w', encoding='utf-8')
    # 解析xml文件
    tree = ET.parse(in_file)
    # 获得对应的键值对
    root = tree.getroot()
    # 获得图片的尺寸大小
    size = root.find('size')
    # 如果xml内的标记为空,增加判断条件
    if size != None:
        # 获得宽
        w = int(size.find('width').text)
        # 获得高
        h = int(size.find('height').text)
        # 遍历目标obj
        for obj in root.iter('object'):
            # 获得difficult ??
            difficult = obj.find('difficult').text
            # 获得类别 =string 类型
            cls = obj.find('name').text
            # 如果类别不是对应在我们预定好的class文件中,或difficult==1则跳过
            if cls not in classes or int(difficult) == 1:
                continue
            # 通过类别名称找到id
            cls_id = classes.index(cls)
            # 找到bndbox 对象
            xmlbox = obj.find('bndbox')
            # 获取对应的bndbox的数组 = ['xmin','xmax','ymin','ymax']
            b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
                 float(xmlbox.find('ymax').text))
            print(image_id, cls, b)
            # 带入进行归一化操作
            # w = 宽, h = 高, b= bndbox的数组 = ['xmin','xmax','ymin','ymax']
            bb = convert((w, h), b)
            # bb 对应的是归一化后的(x,y,w,h)
            # 生成 calss x y w h 在label文件中
            out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')


# 返回当前工作目录
wd = getcwd()
print(wd)


for image_set in sets:
    '''
    对所有的文件数据集进行遍历
    做了两个工作:
    1.将所有图片文件都遍历一遍,并且将其所有的全路径都写在对应的txt文件中去,方便定位
    2.同时对所有的图片文件进行解析和转化,将其对应的bundingbox 以及类别的信息全部解析写到label 文件中去
         最后再通过直接读取文件,就能找到对应的label 信息
    '''
    # 先找labels文件夹如果不存在则创建
    if not os.path.exists('data/labels/'):
        os.makedirs('data/labels/')
    # 读取在ImageSets/Main 中的train、test..等文件的内容
    # 包含对应的文件名称
    image_ids = open('data/ImageSets/%s.txt' % (image_set)).read().strip().split()
    # 打开对应的2012_train.txt 文件对其进行写入准备
    list_file = open('data/%s.txt' % (image_set), 'w')
    # 将对应的文件_id以及全路径写进去并换行
    for image_id in image_ids:
        list_file.write('data/images/%s.jpg\n' % (image_id))
        # 调用  year = 年份  image_id = 对应的文件名_id
        convert_annotation(image_id)
    # 关闭文件
    list_file.close()

运行后会在datasets/fall_dataset/txt中存有转换后的.txt格式的标签文件。成功转换完后命令行中则会输出以下信息,这里的Dataset Classes:['fall']即为标签类别: 

3.2.3.划分训练集、验证集、测试集

在项目根目录中新建split_data.py文件,在split_data.py文件中放入以下代码并运行,该文件是划分训练、验证、测试集。其中支持修改train_percent、val_percent、test_percent,改变训练集、验证集和测试集比例。

# 将图片和标注数据按比例切分为 训练集和测试集
import shutil
import random
import os
import argparse


# 检查文件夹是否存在
def mkdir(path):
    if not os.path.exists(path):
        os.makedirs(path)


def main(image_dir, txt_dir, save_dir):
    # 创建文件夹
    mkdir(save_dir)
    images_dir = os.path.join(save_dir, 'images')
    labels_dir = os.path.join(save_dir, 'labels')

    img_train_path = os.path.join(images_dir, 'train')
    img_test_path = os.path.join(images_dir, 'test')
    img_val_path = os.path.join(images_dir, 'val')

    label_train_path = os.path.join(labels_dir, 'train')
    label_test_path = os.path.join(labels_dir, 'test')
    label_val_path = os.path.join(labels_dir, 'val')

    mkdir(images_dir);
    mkdir(labels_dir);
    mkdir(img_train_path);
    mkdir(img_test_path);
    mkdir(img_val_path);
    mkdir(label_train_path);
    mkdir(label_test_path);
    mkdir(label_val_path);

    # 数据集划分比例,训练集80%,验证集10%,测试集10%,按需修改
    train_percent = 0.8
    val_percent = 0.1
    test_percent = 0.1

    total_txt = os.listdir(txt_dir)
    num_txt = len(total_txt)
    list_all_txt = range(num_txt)  # 范围 range(0, num)

    num_train = int(num_txt * train_percent)
    num_val = int(num_txt * val_percent)
    num_test = num_txt - num_train - num_val

    train = random.sample(list_all_txt, num_train)
    # 在全部数据集中取出train
    val_test = [i for i in list_all_txt if not i in train]
    # 再从val_test取出num_val个元素,val_test剩下的元素就是test
    val = random.sample(val_test, num_val)

    print("训练集数目:{}, 验证集数目:{},测试集数目:{}".format(len(train), len(val), len(val_test) - len(val)))
    for i in list_all_txt:
        name = total_txt[i][:-4]

        srcImage = os.path.join(image_dir, name + '.jpg')
        srcLabel = os.path.join(txt_dir, name + '.txt')

        if i in train:
            dst_train_Image = os.path.join(img_train_path, name + '.jpg')
            dst_train_Label = os.path.join(label_train_path, name + '.txt')
            shutil.copyfile(srcImage, dst_train_Image)
            shutil.copyfile(srcLabel, dst_train_Label)
        elif i in val:
            dst_val_Image = os.path.join(img_val_path, name + '.jpg')
            dst_val_Label = os.path.join(label_val_path, name + '.txt')
            shutil.copyfile(srcImage, dst_val_Image)
            shutil.copyfile(srcLabel, dst_val_Label)
        else:
            dst_test_Image = os.path.join(img_test_path, name + '.jpg')
            dst_test_Label = os.path.join(label_test_path, name + '.txt')
            shutil.copyfile(srcImage, dst_test_Image)
            shutil.copyfile(srcLabel, dst_test_Label)


if __name__ == '__main__':
    """
    python split_datasets.py --image-dir my_datasets/color_rings/imgs --txt-dir my_datasets/color_rings/txts --save-dir my_datasets/color_rings/train_data
    """
    parser = argparse.ArgumentParser(description='split datasets to train,val,test params')
    parser.add_argument('--image-dir', type=str, default=r"VOCdevkit\images", help='image path dir')
    parser.add_argument('--txt-dir', type=str, default=r"VOCdevkit\txt", help='txt path dir')
    parser.add_argument('--save-dir', default=r"VOCdevkit\datsets", type=str, help='save dir')
    args = parser.parse_args()
    image_dir = args.image_dir
    txt_dir = args.txt_dir
    save_dir = args.save_dir

    main(image_dir, txt_dir, save_dir)

运行以上代码后将会在datasets/fall_dataset/文件夹中生成ImageSets和labels文件夹,其中分别存有训练集、验证集和测试集的图片及对应的标签文件。

至此训练所需的自定义数据集(训练集、验证集、测试集)准备完毕!

4.训练自己的模型

(1)在datasets/fall_dataset/文件夹下新建fall.yaml文件,在fall.yaml文件中放入以下代码,注意修改其中的三个训练数据文件夹路径以及对应数据集的nc(类别数)和class name(类别名):

# dataset path
train: E:\knowledge\YOLO v8\ultralytics-main\ultralytics\yolo\v8\detect\datasets\images\train
val: E:\knowledge\YOLO v8\ultralytics-main\ultralytics\yolo\v8\detect\datasets\images\val
test: E:\knowledge\YOLO v8\ultralytics-main\ultralytics\yolo\v8\detect\datasets\images\test

# number of classes
nc: 1

# class names
names: ['fall']

(2)找到YOLO v8\ultralytics-main\ultralytics/yolo/cfg文件路径下的default.yaml文件并打开

  • task设置为detect;
  • mode设置为train;
  • model后设置为预训练模型yolov8n.pt的文件路径;
  • data后设置为(1)中所述的fall.yaml文件的绝对路径;
  • epoch设置为自己所需的训练轮数;
  • batch根据自己电脑配置进行设置

(3)开始训练,在终端中输入以下命令回车,即可开始训练:

yolo cfg=ultralytics/yolo/cfg/default.yaml

运行部分过程如下:

WARNING ⚠️ no labels found in detect set, can not compute metrics without labels!!!

对训练集、验证集、测试集以及对应的labels放到一个文件夹下成功运行,不在提示WARNING ⚠️ no labels found in detect set, can not compute metrics without labels!

(4)训练结果如下:

5.进行predict预测

(1)将default.yaml文件中mode改为predict。

(2)将model由预训练模型更改为自己训练后的最优模型的绝对路径。

(3)将source改为待预测图片所在文件夹的路径。

(4)在终端中输入以下命令回车,即可开始预测:

yolo cfg=ultralytics/yolo/cfg/default.yaml

运行预测完毕后可以看到每张图片的预测结果和最终的预测结果保存路径如下,也可以直接预测视频:

有问题咨询qq:498609554 

微信:

  • 9
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lzdjlu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值