YOLO入门教程(一)——训练自己的模型【含教程源码 + 故障排查】

news2024/11/13 13:12:11

目录

  • 引言
  • 前期准备
  • Step1 打标训练
  • Step2 格式转换
  • Step3 整理训练集
  • Step4 训练数据集
    • 4.1创建yaml文件
    • 4.2训练
    • 4.3故障排查
      • 4.3.1OpenCV版本故障,把OpenCV版本升级到4.0以上
      • 4.3.2NumPy版本故障,把NumPy降低版本到1.26.4
      • 4.3.3没有安装ultralytics模块
      • 4.3.4Arial.ttf下载超时
      • 4.3.5其他报错
  • 参考博客

引言

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

前期准备

安装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 训练数据集

下载YOLOv10训练源码

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=10, batch=8, name=save_model_name)

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

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‘ 解决方案

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

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

相关文章

自闭症儿童上学指南:帮助孩子适应校园生活

在自闭症儿童成长的道路上&#xff0c;校园生活是他们融入社会、学习新知、发展社交技能的重要一步。作为星启帆自闭症儿童康复机构&#xff0c;我们深知这一过程对于孩子及其家庭而言既充满挑战也极具意义。 一、前期准备&#xff1a;建立坚实的支持体系 1. 深入了解孩子需求 …

【机器学习】梯度下降函数如何判断其收敛、学习率的调整以及特征缩放的Z-分数标准化

#引言 在机器学习中&#xff0c;特征缩放和学习率是两个非常重要的概念&#xff0c;它们对模型的性能和训练速度有显著影响。 特征缩放是指将数据集中的特征值缩放到一个固定的范围内&#xff0c;通常是在0到1之间或者标准化到均值为0、方差为1。特征缩放对于模型的训练至关重要…

Vmware安装openstack

安装虚拟机 创建完成后&#xff0c;点击开启虚拟机 稍等执行成功后 上传压缩包到指定目录。将yoga_patch.tar.gz包上传至/root目录下&#xff0c;将stack3_without_data.tar.gz包使用WinSCP上传至/opt目录下 vim run_yoga.sh #/bin/bash cd /root sudo apt-get update tar -xzv…

java面向对象总结

java面向对象篇到这里就已经结束了&#xff0c;有什么不懂的地方可以逐一进行重新观看。希望大家能够从入门到起飞。 Java面向对象基础篇综合训练&#xff08;附带全套源代码及逐语句分析&#xff09;-&#xff1e;基于javabeen Java面向对象进阶篇综合训练&#xff08;附带全…

如何跨专业通过软件设计师考试

链接&#xff1a;如何跨专业通过软件设计师-软件中级职称考试 (qq.com)

经济下行,企业还在“裁员至上”?

最近小红书、B站崩溃&#xff0c;又延伸到某云服务厂商问题频发&#xff0c;让人忍不住戏谑&#xff1a;“这算不算裁员裁到大动脉&#xff1f;” 在阿道看来&#xff0c;各大企业的裁员动作&#xff0c;绕不开的依旧是“人月神话”&#xff1a;盲目加人带来的是成本的倍增和效…

STKMATLAB 卫星编队覆盖分析纯代码实现

任务描述 设置卫星编队&#xff08;沿航迹编队&#xff0c;大斜视角&#xff0c;幅宽100km&#xff0c;下视角30&#xff0c;斜视角26&#xff09;&#xff0c;设置分析区域&#xff08;中国全境&#xff09;&#xff0c;设置FigureOfMerit&#xff08;展示覆盖率&#xff09;…

skynet 入门篇

文章目录 概述1.实现了actor模型2.实现了服务器的基础组件 环境准备centosubuntumac编译安装 ActorActor模型定义组成 Actor调度工作线程流程工作线程权重工作线程执行规则 小结 概述 skynet 是一个轻量级服务器框架&#xff0c;而不仅仅用于游戏&#xff1b; 轻量级有以下几…

16现代循环神经网络—深度循环与双向循环

目录 1.深度循环神经网络回顾:循环神经网络总结简洁代码实现2.双向循环神经网络隐马尔可夫模型中的动态规划双向模型模型的计算代价及其应用总结代码实现1.深度循环神经网络 回顾:循环神经网络 如何将循环神经网络变深,以获得更多的非线性? 通过添加多个隐藏层的方式来实现…

Transformer中的Multi-head与Self-Attention机制解析——从单一关注到多元自省的效益最大化

Transformer中的Multi-head与Self-Attention机制解析——从单一关注到多元自省的效益最大化 Multi-head Attention与Self-Attention的核心思想 想象一下&#xff0c;你在读一本书&#xff0c;你想要同时关注到书中的多个角色和情节。多头注意力机制就是让你能够同时关注到多个不…

day14:01函数参数的使用

一、形参与实参介绍 【形参】&#xff1a;在定义函数阶段定义的参数称之为形式参数&#xff0c;简称形参,相当于变量名 def func(x, y): # x1&#xff0c;y2print(x, y)【实参】&#xff1a;在调用函数阶段传入的值称之为实际参数&#xff0c;简称实参&#xff0c;相当于变量…

OpenEuler系统下——k8s离线安装部署

简介 K8S是目前已经是业界最为流行的开源技术框架&#xff0c;但是苦于其学习难度较大&#xff0c;并且初学者在开始的时候需要自己进行安装搭建部署&#xff0c;以供后续的学习使用&#xff0c;但是国内经常会出现无法访问外网的官方网站&#xff0c;导致很多镜像和依赖包无法…

uCore2020 lab1练习一作业

在线环境&#xff08;无需搭建环境即可复现&#xff09; 在线实验指导书uCore2020&#xff0c;有些章节无法访问 文章目录 lab1练习一1、操作系统镜像文件ucore.img生成过程init.o等文件的生成过程bin/kernal的生成过程bin/sign的生成过程bin/bootblock的生成过程bin/ucore.i…

搭建AI无人直播插件

在当今的数字时代&#xff0c;直播已成为一种极为流行的内容传播方式&#xff0c;而人工智能(AI)技术的飞速发展更是为直播行业注入了新的活力。 AI无人直播插件&#xff0c;作为这一趋势下的产物&#xff0c;不仅能够实现24小时不间断的直播内容生成&#xff0c;还能根据观众…

SpringCloud Alibaba 微服务(四):Sentinel

目录 前言 一、什么是Sentinel&#xff1f; Sentinel 的主要特性 Sentinel 的开源生态 二、Sentinel的核心功能 三、Sentinel 的主要优势与特性 1、丰富的流控规则 2、完善的熔断降级机制 3、实时监控和控制台 4、多数据源支持 5、扩展性强 四、Sentinel 与 Hystrix…

Redis 序列化 GenericJackson2JsonRedisSerializer和Jackson2JsonRedisSerializer的区别

GenericJackson2JsonRedisSerializer 和 Jackson2JsonRedisSerializer 是 Spring Data Redis 提供的两种基于 Jackson 的 Redis 序列化器。 它们的主要区别在于序列化和反序列化的方式以及适用的场景。 GenericJackson2JsonRedisSerializer 序列化方式&#xff1a;在序列化对…

为Mac配置Alfred

参考资料&#xff1a; Alfred神器使用手册 | louis blogMacOS神器之Alfred workflow概览GitHub - arpir/Alfred-Workflows-Collection: 一些好用的 Alfred Workflow 一、修改快捷键 Spotlight的默认快捷键是Command Space Alfred的默认快捷键是Option Space 可以将Alfred和…

[网络编程】网络编程的基础使用

系列文章目录 1、 初识网络 网络编程套接字 系列文章目录前言一、TCP和UDP协议的引入二、UDP网络编程1.Java中的UDP2.UDP回显代码案例3.UDP网络编程的注意事项 三、TCP网络编程1.TCP回显代码案例2.TCP多线程使用 总结 前言 在学习完基础的网络知识后&#xff0c;完成跨主机通…

关于 OSPF LSA 序列号范围 0x80000001-0x7FFFFFFF 释疑正本清源

注&#xff1a;机翻&#xff0c;未校对。 正本&#xff1a;RFC 2328 OSPF Version 2 中相关解释 April 1998 12.1.6. LS sequence number 12.1.6. 序列号 The sequence number field is a signed 32-bit integer. It is used to detect old and duplicate LSAs. The space …

set,map(java)

前言&#xff1a;要了解set和map&#xff0c;首先需要对搜索树和哈希有一定的了解&#xff0c;才能进一步深入的了解set和map。 1.搜索树 &#xff08;1&#xff09;性质&#xff1a; 若它的左子树不为空&#xff0c;则左子树上所有节点值都小于根节点的值。 若它的右子树不…