YOLO入门教程(一)——训练自己YOLOv10模型【含教程源码+一键分类数据集 + 故障排查】

引言

YOLO(You Only Look Once)作为一个目标检测算法,主要是针对矩形边界框的标注设计的,通常情况下使用的是矩形边界框,常用的LabelImg可以只支持矩形标注,该软件标注的标签可以直接用于应用,本文教程主要介绍如何训练自己的YOLO模型,LabelMe多边形标注的标签如何训练。

前期准备

在开始前建议先下载YOLOv10训练源码,安装Anaconda,VSCode,LabelMe并配置好Python环境,OpenCV环境,PyTorch即可开始训练自己的数据集。

Step1 打标训练

使用LabelMe软件进行打标签,会在原图路径下生成.json标签文件。
在这里插入图片描述

Step2 格式转换

LabelMe生成的.json标签文件格式不适用于YOLO训练,需要转换格式为.txt标签文件,利用以下代码将.json标签文件归一化为YOLO可直接训练的.txt标签文件。

import json
import os
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np

#定义分类
name2id = {'background':0,'dot':1}
#通过相对路径索引到标签文件
label_floder_path = './source'

def convert(img_size, corners):
    # 输入的image_size[0]为宽,[1]为高
    # 输入的corners[0]和[1]左上角的xy坐标,[2][3]为右下角的xy坐标
    dw = 1./(img_size[0])
    dh = 1./(img_size[1])
    x = (corners[0] + corners[2]) / 2.0 - 1
    y = (corners[1] + corners[3]) / 2.0 - 1
    w = abs(corners[2] - corners[0])
    h = abs(corners[3] - corners[1])
    x = x * dw
    w = w * dw
    y = y * dh
    h = h * dh
    print("归一化后矩形中心坐标为(", x, ",", y, ")")
    print("归一化后矩形中心宽高为(", w, ",", h, ")")
    return (x, y, w, h)
 
def decode_json(label_floder_path, label_name):
 
    # txt文件名为:文件名.json转换为文件名.txt
    txt_name = label_floder_path + "/" + label_name[0:-5] + '.txt'
    # 打开txt后续可以进行写入,若不存在改文件会自动创建
    print("新建的txt文件名称为", txt_name)
    txt_file = open(txt_name, 'w')

    # 文件路径+文件名称组成完整路径
    label_path = os.path.join(label_floder_path, label_name)
    print("文件路径为", label_floder_path, "文件名称为", label_name)

    # 打开json后续可进行读取,编码方式为GBK
    data = json.load(open(label_path, 'r', encoding='GBK'))

    
    # 用索引的方式可以得到数据
    img_w = data['imageWidth']
    img_h = data['imageHeight']
 
    for i in data['shapes']:
        
        label_name = i['label']
        # 标签形状为矩形时的转换方式
        if (i['shape_type'] == 'rectangle'):
            print("标签形状为矩形进行转换")
 
            x1 = int(i['points'][0][0])
            y1 = int(i['points'][0][1])
            x2 = int(i['points'][1][0])
            y2 = int(i['points'][1][1])
 
            corners = (x1, y1, x2, y2)
            box = convert((img_w,img_h), corners)
            txt_file.write(str(name2id[label_name]) + " " + " ".join([str(xy) for xy in box]) + '\n')

        # 标签形状为多边形时的转换方式
        elif (i['shape_type'] == 'polygon'):
            print("标签形状为多边形进行转换")
            # 创建一个空的列表来存储所有点
            points = []

            # 从路径中获取多边形的坐标
            for point in i['points']:
                points.append(point)

            # 使用列表解析创建二维元组列表
            polygon_vertices = [(points[i][0], points[i][1]) for i in range(0, len(points), 1)]
            print("点的数量为", len(points), "分别为:", polygon_vertices)

            # 创建一个多边形对象
            polygon = Polygon(polygon_vertices, closed=True, edgecolor='r', fill=None)
            fig, ax = plt.subplots()
            ax.add_patch(polygon)

            # 获取多边形的路径对象
            path = polygon.get_path()
            
            # 获取路径的边界框
            extents = path.get_extents()

            corners = (extents.xmin, extents.ymax, extents.xmax, extents.ymin)
            box = convert((img_w,img_h),corners)
            txt_file.write(str(name2id[label_name]) + " " + " ".join([str(xy) for xy in box]) + '\n')

        # 标签形状为圆形时的转换方式
        else:
            # 圆形的信息
            circle_center = (3, 4)  # 圆心坐标 (x, y)
            circle_radius = 2       # 圆的半径

            # 计算外接矩形的位置
            # 外接矩形的左上角和右下角坐标
            rect_top_left = (circle_center[0] - circle_radius, circle_center[1] + circle_radius)
            rect_bottom_right = (circle_center[0] + circle_radius, circle_center[1] - circle_radius)
            
            corners = (rect_top_left[0], rect_top_left[1], rect_bottom_right[0], rect_bottom_right[1])
            box = convert((img_w,img_h), corners)
            txt_file.write(str(name2id[label_name]) + " " + " ".join([str(xy) for xy in box]) + '\n')

def create_classes(label_floder_path):
    # txt文件名为:文件名.json转换为文件名.txt
    classes_name = label_floder_path + "/classes.txt"
    # 打开txt后续可以进行写入,若不存在改文件会自动创建
    print("新建的txt文件名称为", classes_name)
    classes_file = open(classes_name, 'w')
    for name in name2id:
        classes_file.write(name + '\n')

if __name__ == "__main__":
    label_names = os.listdir(label_floder_path)
    print("转换的总文件数量为", len(label_names))

    create_classes(label_floder_path)

    for label_name in label_names:
        if(label_name[-5:] == '.json'):
            print("解析文件", label_name)
            decode_json(label_floder_path, label_name)

label_floder_path替换成标签和图片所在路径
name2id替换成标签
encoding='GBK’为编码格式

以上代码暂时只支持矩形和多边形的形状转换

备注:转换后可以在LabelImg上检验是否转换成功
在这里插入图片描述

Step3 整理训练集

YOLO有数据集要求放置的格式要求,具体要求如下:

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

利用以下代码将快速将训练集整理好,划分为训练集和验证集:

import random
import shutil
import os
import shutil

#通过相对路径索引到数据集
source_floder_path = './source'
# 图片目标路径
images_folder = source_floder_path + '/images'
# 标签目标路径
labels_folder = source_floder_path + '/labels'

def CollateDataset(image_dir,label_dir):  # image_dir:图片路径  label_dir:标签路径
    # 创建一个空列表来存储有效图片的路径
    valid_images = []
    # 创建一个空列表来存储有效 label 的路径
    valid_labels = []
    # 遍历 images 文件夹下的所有图片
    for image_name in os.listdir(image_dir):
        # 获取图片的完整路径
        image_path = os.path.join(image_dir, image_name)
        # 获取图片文件的扩展名
        ext = os.path.splitext(image_name)[-1]
        # 根据扩展名替换成对应的 label 文件名
        label_name = image_name.replace(ext, ".txt")
        # 获取对应 label 的完整路径
        label_path = os.path.join(label_dir, label_name)
        # 判断 label 是否存在
        if not os.path.exists(label_path):
            # # 删除图片
            # os.remove(image_path)
            print("there is no:", label_path)
        else:
            # 将图片路径添加到列表中
            valid_images.append(image_path)
            # 将 label 路径添加到列表中
            valid_labels.append(label_path)
    # 遍历每个有效图片路径
    for i in range(len(valid_images)):
        image_path = valid_images[i]
        label_path = valid_labels[i]
        # 随机生成一个概率
        r = random.random()
        # 判断图片应该移动到哪个文件夹
        # train:valid:test = 8:2:0
        if r < 0.0:
            # 移动到 test 文件夹
            destination = "./datasets/test"
        elif r < 0.2:
            # 移动到 valid 文件夹
            destination = "./datasets/valid"
        else:
            # 移动到 train 文件夹
            destination = "./datasets/train"
        # 创建目标文件夹中 images 和 labels 子文件夹
        os.makedirs(os.path.join(destination, "images"), exist_ok=True)
        os.makedirs(os.path.join(destination, "labels"), exist_ok=True)
        # 生成目标文件夹中图片的新路径
        image_destination_path = os.path.join(destination, "images", os.path.basename(image_path))
        # 移动图片到目标文件夹
        shutil.copy(image_path, image_destination_path)
        # 生成目标文件夹中 label 的新路径
        label_destination_path = os.path.join(destination, "labels", os.path.basename(label_path))
        # 移动 label 到目标文件夹
        shutil.copy(label_path, label_destination_path)

def CopyImagesAndLabels(path):
    # 定义图片尾缀
    image_suffix = ['.png']
    # 定义标签尾缀
    label_suffix = ['.txt']



    # 遍历源文件夹中的文件
    for filename in os.listdir(path):
        print(f"FileName is {filename}")
        # 将路径分隔符从 '\\' 替换为 '/'
        filename = filename.replace('\\', '/')
        # 检查文件是否为图片的后缀结尾
        for suffix in image_suffix:
            #图片移动到图片路径
            if filename.endswith(suffix):
                    # 拼接路径字符串
                    source_file = os.path.normpath(os.path.join(os.getcwd(), path, filename))
                    dest_file = os.path.normpath(os.path.join(os.getcwd(), images_folder, filename))

                    # 复制文件
                    if os.path.exists(source_file):
                        try:
                            # 复制文件
                             # 确保目标文件夹存在,递归创建目录
                            os.makedirs(os.path.dirname(dest_file), exist_ok=True)
                            print(f"Copying Image {source_file} to {dest_file}")
                            shutil.copy(source_file, dest_file)
                        except Exception as e:
                            print(f"Error: {e}") 
                            
        # 检查文件是否为标签的后缀结尾
        for suffix in label_suffix:
            #图片移动到图片路径
            if filename.endswith(suffix):
                    # 拼接路径字符串
                    source_file = os.path.normpath(os.path.join(os.getcwd(), path, filename))
                    dest_file = os.path.normpath(os.path.join(os.getcwd(), labels_folder, filename))

                    # 复制文件
                    print(f"Copy Label {source_file} to {dest_file}")
                    if os.path.exists(source_file):
                        try:
                            # 复制文件
                             # 确保目标文件夹存在,递归创建目录
                            os.makedirs(os.path.dirname(dest_file), exist_ok=True)
                            print(f"Copying Label {source_file} to {dest_file}")
                            shutil.copy(source_file, dest_file)
                            print("File copied successfully!")
                        except Exception as e:
                            print(f"Error: {e}")

    # 拷贝完成
    print("File copied successfully!")

if __name__ == '__main__':
    # 整理训练集
    CopyImagesAndLabels(source_floder_path)
    CollateDataset(images_folder, labels_folder)

source_floder_path替换成数据集所在路径
images_folder和labels_folder为图片集和标记集所在路径
image_suffix和label_suffix为图片集和标签集的尾缀
destination为最终训练集和验证集所在路径

以上代码只复制png和txt尾缀的数据集

备注:转换后可以在本地路径上检查是否整理成功
在这里插入图片描述

Step4 训练数据集

4.1创建yaml文件

在这里插入图片描述

Train/val替换成数据集所在绝对路径
Classes为标签数量与名称

4.2训练

from ultralytics import YOLOv10
import torch
import cv2
import os

# 模型配置文件
model_yaml_path = "./ultralytics/cfg/models/v10/yolov10n.yaml"
# 数据集配置文件
data_yaml_path = os.path.normpath(os.path.join(os.getcwd(), './yolov10-main/ultralytics/cfg/datasets/mytrain.yaml'))
# 预训练模型
pre_model_name = os.path.normpath(os.path.join(os.getcwd(), './yolov10-main/weights/yolov10n.pt'))
# 模型保存路径
save_model_name = os.path.normpath(os.path.join(os.getcwd(), './yolov10-main/runs/detect/mytrain_0729'))

if __name__ == '__main__':
    print(torch.__version__)  #注意,这里也是两个下划线
    print(cv2.__version__)
    # 加载预训练模型
    model = YOLOv10(model_yaml_path).load(pre_model_name)
    # model = YOLOv10(pre_model_name)

    # 训练模型
    results = model.train(data=data_yaml_path, epochs=100, batch=8, name=save_model_name)

data_yaml_path替换成4.1步骤下yaml的名称与路径
pre_model_name为预训练模型区别如下图
在这里插入图片描述

以下表格列出了训练参数解析参数及其参考含义
参数类型默认值说明
–datastrROOT/“data/coco128.yaml”数据集配置文件路径
–epochsint100训练总轮数
–batchint16所有 GPU 上的总批量大小,-1 表示自动批量大小

task:选择任务类型,可选[‘detect’, ‘segment’, ‘classify’, ‘init’]
mode: 选择是训练、验证还是预测的任务蕾西 可选[‘train’, ‘val’, ‘predict’]
model: 选择yolov8不同的模型配置文件,可选yolov8s.yaml、yolov8m.yaml、yolov8l.yaml、yolov8x.yaml
data: 选择生成的数据集配置文件
epochs:指的就是训练过程中整个数据集将被迭代多少次,显卡不行你就调小点。
batch:一次看完多少张图片才进行权重更新,梯度下降的mini-batch,显卡不行你就调小点。

4.3故障排查

4.3.1OpenCV版本故障,把OpenCV版本升级到4.0以上

ModuleNotFoundError: No module named ‘cv2’

下载所需版本的OpenCV包,将安装好的.whl文件拷贝到.\Anaconda3\Lib\site-packages文件夹中,并将原来的OpenCV卸载
pip uninstall opencv-python
pip uninstall opencv-contrib-python
cd .\Anaconda3\Lib\site-packages
pip install msgpack-python
pip install msgpack
pip install x.whl

备注:版本cp后的数字代表了适配Python的版本。如果你的Python版本是3.7.4请务必选择cp37,其它依此类推。对于我们Windows64位系统,应当选择win_amd64系列。

4.3.2NumPy版本故障,把NumPy降低版本到1.26.4

A module that was compiled using NumPy 1.x cannot be run in NumPy 2.0.1 as it may crash. To support both 1.x and 2.x versions of NumPy, modules must be compiled with NumPy 2.0. Some module may need to rebuild instead e.g. with ‘pybind11>=2.12’.

pip uninstall numpy
pip install numpy==1.26.4

4.3.3没有安装ultralytics模块

New https://pypi.org/project/ultralytics/8.2.66 available 😃 Update with ‘pip install -U ultralytics’

pip install -U ultralytics

4.3.4Arial.ttf下载超时

解决yolov5环境配置报错Arial.ttf下载超时Downloading

4.3.5其他报错

其他报错可以搜索或者在YOLO官方常见问题文档中查找

参考博客

1.深度学习之目标检测从入门到精通——json转yolo格式
2.模型训练篇 | yolov10来了!手把手教你如何用yolov10训练自己的数据集(含网络结构 + 模型训练 + 模型推理等)
3.在win10下安装Anaconda环境并配置OpenCV
4.ModuleNotFoundError: No module named ‘torch‘ 解决方案

要使用YOLO训练自己的模型,你需要按照以下步骤进行操作: 1. 数据准备:首先,你需要准备好训练所需的数据集数据集应包带有标签的图像,每个标签指定了图像中的对象位置和类别。确保你的数据集符合YOLO的格式要求,每个图像对应一个同名的.txt文件,其中包了对象的位置和类别信息。 2. 标注工具:使用标注工具(如LabelImg、RectLabel等)对图像进行标注,将对象位置和类别信息添加到每个图像中,并生成对应的标签文件。 3. 配置文件:创建YOLO的配置文件,其中包模型的相关参数,如网络结构、训练参数和路径等。确保配置文件与你的数据集和类别信息相匹配。 4. 下载预训练权重:从YOLO官方网站或其他可靠来源下载预训练的权重文件,这将作为模型的初始参数。 5. 训练模型:使用YOLO训练代码(如Darknet)加载配置文件和预训练权重,并开始训练模型。在训练过程中,模型将根据你提供的数据集进行学习和优化。 6. 模型评估和调优:训练完成后,使用测试集评估模型的性能,并根据需要进行调整和优化。你可以通过计算指标(如精确度、召回率和平均精确度均值(mAP))来评估模型。 7. 模型应用:训练完成的YOLO模型可以用于物体检测任务。你可以使用训练好的模型对新的图像或视频进行物体检测,并获取对象的位置和类别信息。 请注意,YOLO训练流程较为复杂,需要一定的计算资源和时间。如果你刚开始接触物体检测和深度学习,建议先阅读相关文档和教程,并尝试使用开源实现的YOLO模型进行学习和实验。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

吾门

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

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

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

打赏作者

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

抵扣说明:

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

余额充值