【YOLOv8系列】(四)YOLOv8使用自己的数据集进行模型训练,成就感满满

目录

一.数据集获取

1.使用开源数据集

2.自定义数据图片

二.数据集标注

1.标注工具介绍

2.labelme安装

3.数据标注

1.选择要标注的数据集文件夹

2.设置自动保存:

3.创建多边形标注

4.格式转换

1.安装labelme2yolo 

2.格式转换

3.效果查看

4.其他格式转换 

5.划分数据集

三.模型训练

1.模型训练

2.参数解读

1.训练参数

2.结果参数

3.模型验证

四.总结 


随着YOLO(You Only Look Once)系列的不断进化,YOLOv8以其显著的精度提升和优化的架构设计,再次吸引了众多研究者和开发者的眼球。在本系列的第四篇文章中,我们将深入探讨如何在自定义数据集上训练YOLOv8模型,让你从零开始掌握目标检测的实战技能。

本系列其他文章

【YOLOv8系列】(一)YOLOv8介绍:实时目标检测的最新突破-CSDN博客

【YOLOv8系列】(二)YOLOv8环境配置,手把手嘴对嘴保姆教学-CSDN博客

【YOLOv8系列】(三)YOLOv8应用实践:从识别到分类再到分割的全方位视觉解决方案-CSDN博客

一.数据集获取

在机器学习和深度学习中,数据集是训练模型的基础。尤其在计算机视觉领域,数据集的重要性更为突出。以下是获取数据集的几个途径:

1.使用开源数据集

有许多公开的目标检测数据集可供使用。以下是一些常见的数据集:

  • COCO (Common Objects in Context):一个大规模的图像数据集,包含80种类别的物体。可以从 COCO官网 下载。
  • Pascal VOC:一个经典的目标检测数据集,包含20种类别的物体。可以从Pascal VOC官网 下载。
  • Open Images:一个包含约900万张图像和600种类别的物体数据集。可以从 Open Images官网下载。

2.自定义数据图片

通过爬虫程序批量下载网络上的图片数据集,或者使用相机或其他来源收集您需要的图片

二.数据集标注

数据标注是通过人工把需要识别和分辨的数据贴上标签。深度神经网络学习这些标注数据的特征,最终实现自主识别的功能。

1.标注工具介绍

使用标注工具对图片中的目标进行标注。常用的标注工具包括:

  • LabelImg:一个开源的图像标注工具,支持VOC和YOLO格式。可以从 LabelImg官网 下载。
  • LabelMe:一个在线的图像标注工具,支持多种标注格式。可以从 LabelMe官网 使用。
  • Roboflow:一个在线平台,提供数据集管理和标注工具。可以从 Roboflow官网 使用。

2.labelme安装

我个人比较推荐使用labelme,因为可以进行多边形标注。

pip安装

pip install labelme

终端输入labelme打开软件

labelme

 界面如下所示

3.数据标注

1.选择要标注的数据集文件夹

2.设置自动保存:

文件—>自动保存

3.创建多边形标注

标注的数据会自动保存

4.格式转换

将标注文件转换为YOLO格式。YOLO格式的标注文件内容如下:

<class_id> <x_center> <y_center> <width> <height>

但我们标注好的json数据格式是这样的,需要将json格式转换成需要的txt

1.安装labelme2yolo 

pip install labelme2yolo

2.格式转换

labelme2yolo --json_dir /path/to/labelme_json_dir/ # 将所有 LabelMe JSON 文件放在 labelme_json_dir 下

--json_dirLabelMe JSON 文件文件夹路径。

--val_size(可选)验证数据集大小,例如 0.2 表示 20% 用于验证。

--test_size(可选)测试数据集大小,例如 0.1 表示 Test 的 10%。

--json_name(可选)转换单个LabelMe JSON文件。

--output_format(可选)标签的输出格式。

--label_list(可选)预先分配的类别标签。

3.效果查看

运行后会出现一个名为YOLODataset的文件夹

 项目结构如下所示:

YOLODataset/
├── images/
│   ├── train/
│   ├── val/
├── labels/
│   ├── train/
│   ├── val/
├── dataset.yaml

yaml文件如下 

转换后的txt文件如下

可以看到,除了格式转换,labelme2yolo模块已经帮我们划分好了数据集以及验证集并生成好了yaml文件。一劳永逸,灰常好用!!!!!

4.其他格式转换 

除了json文件,最常见的就是voc格式的xml文件,例如安全帽检测数据集。以下脚本可以将xml转成txt文件。

import xml.etree.ElementTree as ET
import os
 
 
def convert(size, box):
    x_center = (box[0] + box[1]) / 2.0
    y_center = (box[2] + box[3]) / 2.0
    #分别计算纵坐标和横坐标的中心点
    x = x_center / size[0]
    y = y_center / size[1]
 
    w = (box[1] - box[0]) / size[0]
    h = (box[3] - box[2]) / size[1]
 
    # print(x, y, w, h)
    return (x, y, w, h)
 
 
def convert_annotation(xml_files_path, save_txt_files_path, classes):
    xml_files = os.listdir(xml_files_path)
    print(xml_files)
    for xml_name in xml_files:
        print(xml_name)
        xml_file = os.path.join(xml_files_path, xml_name)
        out_txt_path = os.path.join(save_txt_files_path, xml_name.split('.')[0] + '.txt')
        out_txt_f = open(out_txt_path, 'w')
        tree = ET.parse(xml_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))
            # b=(xmin, xmax, ymin, ymax)
            print(w, h, b)
            bb = convert((w, h), b)
            out_txt_f.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
 
 
if __name__ == "__main__":
    # 1、指定yolo类别
    classes1 = ["class1","class2","class3"]
    # 2、voc格式的xml标签文件路径
    xml_files1 = r'/path/to/your/xml/folder'
    # 3、转化为yolo格式的txt标签文件存储路径
    save_txt_files1 = r'/path/to/your/txt/save/folder'
 
    convert_annotation(xml_files1, save_txt_files1, classes1)
    with open(save_txt_files1 + '/classes.txt', 'w') as file:
        for class_name in classes1:
            file.write(class_name + '\n')
 

5.划分数据集

如果你是通过labelme按照以上步骤标注自己数据集,这一步可以跳过。但如果是下载的公开已标注好的数据集,那么需要通过脚本进行数据集的划分。

import os
import random
import shutil
import time
import yaml
 
 
 
class YOLOTrainDataSetGenerator:
    def __init__(self, origin_dataset_dir, train_dataset_dir, train_ratio=0.7, val_ratio=0.15, test_ratio=0.15,
                 clear_train_dir=False):
        # 设置随机数种子
        random.seed(1233)
 
        self.origin_dataset_dir = origin_dataset_dir
        self.train_dataset_dir = train_dataset_dir
        self.train_ratio = train_ratio
        self.val_ratio = val_ratio
        self.test_ratio = test_ratio
        self.clear_train_dir = clear_train_dir
 
        assert self.train_ratio > 0.5, 'train_ratio must larger than 0.5'
        assert self.val_ratio > 0.01, 'train_ratio must larger than 0.01'
        assert self.test_ratio > 0.01, 'test_ratio must larger than 0.01'
        total_ratio = round(self.train_ratio + self.val_ratio + self.test_ratio)
        assert total_ratio == 1.0, 'train_ratio + val_ratio + test_ratio must equal 1.0'
 
    def generate(self):
        time_start = time.time()
        # 原始数据集的图像目录,标签目录,和类别文件路径
        origin_image_dir = os.path.join(self.origin_dataset_dir, 'images')
        origin_label_dir = os.path.join(self.origin_dataset_dir, 'labels')
        origin_classes_file = os.path.join(self.origin_dataset_dir, 'classes.txt')
        if not os.path.exists(origin_classes_file):
            return
        else:
            origin_classes = {}
            with open(origin_classes_file, mode='r') as f:
                for cls_id, cls_name in enumerate(f.readlines()):
                    cls_name = cls_name.strip()
                    if cls_name != '':
                        origin_classes[cls_id] = cls_name
 
        # 获取所有原始图像文件名(包括后缀名)
        origin_image_filenames = os.listdir(origin_image_dir)
 
        # 随机打乱文件名列表
        random.shuffle(origin_image_filenames)
 
        # 计算训练集、验证集和测试集的数量
        total_count = len(origin_image_filenames)
        train_count = int(total_count * self.train_ratio)
        val_count = int(total_count * self.val_ratio)
        test_count = total_count - train_count - val_count
 
        # 定义训练集文件夹路径
        if self.clear_train_dir and os.path.exists(self.train_dataset_dir):
            shutil.rmtree(self.train_dataset_dir, ignore_errors=True)
        train_dir = os.path.join(self.train_dataset_dir, 'train')
        val_dir = os.path.join(self.train_dataset_dir, 'val')
        test_dir = os.path.join(self.train_dataset_dir, 'test')
        train_image_dir = os.path.join(train_dir, 'images')
        train_label_dir = os.path.join(train_dir, 'labels')
        val_image_dir = os.path.join(val_dir, 'images')
        val_label_dir = os.path.join(val_dir, 'labels')
        test_image_dir = os.path.join(test_dir, 'images')
        test_label_dir = os.path.join(test_dir, 'labels')
 
        # 创建训练集输出文件夹
        os.makedirs(train_image_dir, exist_ok=True)
        os.makedirs(train_label_dir, exist_ok=True)
        os.makedirs(val_image_dir, exist_ok=True)
        os.makedirs(val_label_dir, exist_ok=True)
        os.makedirs(test_image_dir, exist_ok=True)
        os.makedirs(test_label_dir, exist_ok=True)
 
        # 将图像和标签文件按设定的ratio划分到训练集,验证集,测试集中
        for i, filename in enumerate(origin_image_filenames):
            if i < train_count:
                output_image_dir = train_image_dir
                output_label_dir = train_label_dir
            elif i < train_count + val_count:
                output_image_dir = val_image_dir
                output_label_dir = val_label_dir
            else:
                output_image_dir = test_image_dir
                output_label_dir = test_label_dir
            src_img_name_no_ext = os.path.splitext(filename)[0]
            src_image_path = os.path.join(origin_image_dir, filename)
            src_label_path = os.path.join(origin_label_dir, src_img_name_no_ext + '.txt')
            if os.path.exists(src_label_path):
                # 复制图像文件
                dst_image_path = os.path.join(output_image_dir, filename)
                shutil.copy(src_image_path, dst_image_path)
                # 复制标签文件
                src_label_path = os.path.join(origin_label_dir, src_img_name_no_ext + '.txt')
                dst_label_path = os.path.join(output_label_dir, src_img_name_no_ext + '.txt')
                shutil.copy(src_label_path, dst_label_path)
            else:
                pass
        train_dir = os.path.normpath(train_dir)
        val_dir = os.path.normpath(val_dir)
        test_dir = os.path.normpath(test_dir)
        data_dict = {
            'train': train_dir,
            'val': val_dir,
            'test': test_dir,
            'nc': len(origin_classes),
            'names': origin_classes
        }
 
        yaml_file_path = os.path.normpath(os.path.join(self.train_dataset_dir, 'data.yaml'))
        with open(yaml_file_path, mode='w') as f:
            yaml.safe_dump(data_dict, f, default_flow_style=False, allow_unicode=True)
    
 
if __name__ == '__main__':
    g_origin_dataset_dir = '原始数据集'
    g_train_dataset_dir = '生成的文件地址'
    g_train_ratio = 0.7
    g_val_ratio = 0.15
    g_test_ratio = 0.15
    yolo_generator = YOLOTrainDataSetGenerator(g_origin_dataset_dir, g_train_dataset_dir, g_train_ratio, g_val_ratio,
                                               g_test_ratio, True)
    yolo_generator.generate()

原始YOLO数据集:images包含被标注的图像,labels包含对应图像的标注文件,classes.txt包含标注的类别。

生成用于YOLOv8训练用的数据集,如下图所示:

三.模型训练

当以上步骤都完成后就可以开始漫长的模型训练过程了。

1.模型训练

#coding:utf-8
from ultralytics import YOLO
# 加载预训练模型
model = YOLO("./model/yolov8n.pt")
# Use the model
if __name__ == '__main__':
    # Use the model
    results = model.train(data='dataset.yaml', epochs=300, batch=4)  # 训练模型

 训练ing~~~

如果训练出现意外中断,例如说电脑爆炸,世界末日之类的,可以使用下面命令或python代码恢复训练

# Resume an interrupted training
yolo train resume model=path/to/last.pt
from ultralytics import YOLO

# Load a model
model = YOLO("path/to/last.pt")  # load a partially trained model

# Resume training
results = model.train(resume=True)

2.参数解读

1.训练参数

  • Epoch:训练过程中的迭代次数
  • GPU_mem:GPU内存使用情况,通常是以MB或GB为单位的数字
  • box_loss:模型预测的边界框与真实边界框之间的平均损失值
  • cls_loss:模型预测的对象类别与真实类别之间的平均损失值
  • dfl_loss:分布式焦距损失,用于提高边界框回归的精度
  • Instances:检测任务中每个图像包含的目标实例
  • Size:输入模型的图像的大小,通常是以像素为单位的宽度和高度。
  • Class:检测的目标类别
  • Images:测试集中包含该类别的图像数量
  • Box(P:该类别的预测精确度(precision),即正确预测的物体数量占所有预测的物体数量的比例
  • R:该类别的召回率(recall),即正确预测的物体数量占所有真实物体数量的比例
  • mAP50:在 50% IoU(Intersection over Union)阈值下的平均精度(mean Average Precision)
  • mAP50-95):在 IoU 阈值从 0.50 到 0.95(步长为 0.05)的范围内,逐步计算的平均精度的平均值

2.结果参数

Yolov8在训练完成之后,会在runs/detect/train目录下把训练的过程一些参数与结果示意图保存下来,这里面包含是目标检测性能指标。

  • weights:包含best.pt以及last.pt,分布代表模型损失最低的结果以及最后一次训练完成后的结果
  • args.yaml:训练时的保存的超参数
  • 混淆矩阵(confusion_matrix):

                其中:

                        True Positive (TP):预测为正类且实际为正类的样本数。

                        False Positive (FP):预测为正类但实际为负类的样本数(误报)。

                        False Negative (FN):预测为负类但实际为正类的样本数(漏报)。

                        True Negative (TN):预测为负类且实际为负类的样本数。        

  •  混淆矩阵归一化(confusion_matrix_normalized):将每个单元格的值除以该类别实际样本数,从而得到表示分类准确率的百分比

  • F1曲线(F1_curve): 指在不同阈值下计算的 F1 分数与阈值之间的关系曲线。

  • labels :代表每个检测到的目标的类别和边界框信息。

                从左往右,从上到下按顺序排列:

                1.训练集的数据量,显示每个类别包含的样本数量

                2.框的尺寸和数量,展示了训练集中边界框的大小分布以及相应数量

                3.中心点相对于整幅图的位置,描述了边界框中心点在图像中的位置分布情况

                4.图中目标相对于整幅图的高宽比例,反映了训练集中目标高宽比例的分布状况

  • labels_correlogram:展示了在训练过程中对标签之间相关性的建模情况。每个矩阵单元代表模型训练时使用的标签,而单元格的颜色深浅反映了对应标签之间的相关性。 

  • P_curve: 关于的是PrecisionConfidence之间的关系。

  • PR_curve :关于的是PrecisionRecall之间的关系

  • R_curve :召回率曲线。在目标检测中,召回率是衡量模型检测到所有正样本中有多少被正确检测出来的指标。

  • results.csv :模型训练时每次迭代结果,记录了一些我们训练过程中的参数信息,包括损失和学习率等。

  • results :

        损失函数在目标检测任务中扮演关键角色,它用于衡量模型的预测值与真实值之间的差异,直接影响模型性能。以下是一些与目标检测相关的损失函数和性能评价指标的解释:

        1. 定位损失(box_loss):

                定义: 衡量预测框与标注框之间的误差,通常使用 GIoU(Generalized Intersection over Union)来度量,其值越小表示定位越准确。
                目的: 通过最小化定位损失,使模型能够准确地定位目标。
        2. 置信度损失(obj_loss):

                定义: 计算网络对目标的置信度,通常使用二元交叉熵损失函数,其值越小表示模型判断目标的能力越准确。
                目的: 通过最小化置信度损失,使模型能够准确判断目标是否存在。
        3. 分类损失(cls_loss):

                定义: 计算锚框对应的分类是否正确,通常使用交叉熵损失函数,其值越小表示分类越准确。
                目的: 通过最小化分类损失,使模型能够准确分类目标。
        4. Precision(精度):

                定义: 正确预测为正类别的样本数量占所有预测为正类别的样本数量的比例。
                目的: 衡量模型在所有预测为正例的样本中有多少是正确的。
        5. Recall(召回率):

                定义: 正确预测为正类别的样本数量占所有真实正类别的样本数量的比例。
                目的: 衡量模型能够找出真实正例的能力。
        6. mAP(平均精度):

                定义: 使用 Precision-Recall 曲线计算的面积,mAP@[.5:.95] 表示在不同 IoU 阈值下的平均 mAP。
                目的: 综合考虑了模型在不同精度和召回率条件下的性能,是目标检测任务中常用的评价指标。

        在训练过程中,通常需要关注精度和召回率的波动情况,以及 mAP50 mAP50-95) 评估训练结果。这些指标可以提供关于模型性能和泛化能力的有用信息。 

3.模型验证

当模型训练完成,我们使用以下命令行预测

yolo task=detect mode=predict model=best.pt source=YOLODataset/images/val

结果如下所示:

置信度都在90%左右,效果还是蛮不错的!!! 

四.总结 

本文详细介绍了如何利用YOLOv8模型进行目标检测任务的训练过程,包括数据集获取、数据集标注、模型训练、以及最终的部署和应用。通过本文的指导,读者可以快速掌握训练YOLOv8模型的关键步骤,并在实际项目中应用和优化模型。

如果以上内容对您有帮助,可以三连打赏订阅本专栏哦, 谢谢~

Yolov8是一种高效的目标检测算法,具有较高的准确率和实时性。在使用Yolov8进行目标检测时,我们通常需要使用自定义数据集进行模型训练。本教程将详细介绍如何使用自定义数据集训练Yolov8模型。 首先,我们需要准备自定义数据集。自定义数据集应包含目标类别的图像及其对应的标注文件。标注文件通常采用Pascal VOC或COCO数据集的格式,包含目标的坐标、类别等信息。 接下来,我们需要将数据集划分为训练集和验证集。通常,我们将数据集的大部分用于训练,一小部分用于验证模型的性能。训练集和验证集的划分比例可以根据具体情况进行调整。 然后,我们需要下载Yolov8训练代码和预训练权重,并进行相应的配置。可以通过GitHub等渠道获取Yolov8的代码和权重文件,并根据需要进行配置,如设置类别数量、学习率等超参数。 接着,我们需要将自定义数据集转换为Yolov8所需的格式。Yolov8接受Darknet格式的数据集,我们可以使用相应的脚本将自定义数据集转换为Darknet格式,并生成对应的配置文件。 在数据集准备就绪后,我们可以使用Yolov8训练脚本开始训练模型。在训练过程中,我们可以调整学习率、批大小等参数来优化模型的性能。此外,可以使用训练权重来加速收敛和提高模型的准确率。 训练完成后,我们可以使用训练得到的模型进行目标检测。可以使用Yolov8提供的脚本加载模型进行目标检测,同时可以进行后处理操作,如非极大值抑制来去除冗余的检测结果。 总之,本教程详细介绍了如何使用自定义数据集训练Yolov8模型,包括数据集准备、模型配置、训练目标检测等步骤。通过按照教程进行操作,你可以轻松地训练出自己的Yolov8模型,并应用于实际的目标检测任务中。
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

到点就困告

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

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

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

打赏作者

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

抵扣说明:

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

余额充值