YOLOv8教程系列:三、K折交叉验证——让你的每一份标注数据都物尽其用(yolov8目标检测+k折交叉验证法)

YOLOv8教程系列:三、K折交叉验证——让你的每一份标注数据都物尽其用(yolov8目标检测+k折交叉验证法)

0.引言

k折交叉验证(K-Fold
Cross-Validation)是一种在机器学习中常用的模型评估技术,用于估计模型的性能和泛化能力。它的主要作用是在有限的数据集上对模型进行评估,以便更准确地了解模型在新数据上的表现。

K折交叉验证的基本思想是将原始数据集分成K个子集(折),然后依次将每个子集作为验证集,其他K-1个子集作为训练集,进行K次训练和验证。每次验证后,计算模型在验证集上的性能指标,如准确率、精确率、召回率等。最后,将K次验证的性能指标平均,作为模型在整个数据集上的性能估计。

K折交叉验证的作用包括:

  1. 模型性能评估: K折交叉验证可以更准确地评估模型在数据集上的性能,避免因数据分布不均匀而导致评估结果不准确的问题。
  2. 泛化能力估计: 通过在不同的训练集和验证集上进行多次评估,可以更好地估计模型的泛化能力,即模型在新数据上的表现。
  3. 减少过拟合: K折交叉验证可以帮助检测模型是否出现过拟合问题。如果模型在训练集上表现很好,但在验证集上表现较差,可能存在过拟合。
  4. 参数调优: 在每一轮交叉验证中,可以使用不同的参数设置来训练模型,以找到在验证集上表现最好的参数组合。
  5. 数据利用率: K折交叉验证充分利用了数据集中的所有样本,因为每个样本都会在不同的折中被用作训练和验证。

总之,K折交叉验证是一种有助于评估和改进模型性能的重要技术,尤其在数据有限的情况下,它能更准确地估计模型在新数据上的表现。
在这里插入图片描述

1.数据准备

使用交叉验证前,需要把数据准备为yolo格式,不知道如何数据准备的朋友可以看下这篇文章:YOLOv8教程系列:一、使用自定义数据集训练YOLOv8模型(详细版教程,你只看一篇->调参攻略),包含环境搭建/数据准备/模型训练/预测/验证/导出等
.
├── ./data
│ ├── ./data/Annotations
│ │ ├── ./data/Annotations/fall_0.xml
│ │ ├── ./data/Annotations/fall_1000.xml
│ │ ├── ./data/Annotations/fall_1001.xml
│ │ ├── ./data/Annotations/fall_1002.xml
│ │ ├── ./data/Annotations/fall_1003.xml
│ │ ├── ./data/Annotations/fall_1004.xml
│ │ ├── …
│ ├── ./data/images
│ │ ├── ./data/images/fall_0.jpg
│ │ ├── ./data/images/fall_1000.jpg
│ │ ├── ./data/images/fall_1001.jpg
│ │ ├── ./data/images/fall_1002.jpg
│ │ ├── ./data/images/fall_1003.jpg
│ │ ├── ./data/images/fall_1004.jpg
│ │ ├── …
│ ├── ./data/ImageSets
│ └── ./data/labels
│ │ ├── ./data/images/fall_0.txt
│ │ ├── ./data/images/fall_1000.txt
│ │ ├── ./data/images/fall_1001.txt
│ │ ├── ./data/images/fall_1002.txt
│ │ ├── ./data/images/fall_1003.txt
│ │ ├── ./data/images/fall_1004.txt
│ ├── ./data/classes.yaml
其中,特别要注意的一点是,需要新建个classes.yaml的文件,然后将自己的标签按序填写,如下所示:

names:
  0: your_label_1
  1: your_label_2

2.代码准备

下面代码可以什么都不用改直接运行,前提是按我的数据格式,这个代码放在data的上层目录中

import datetime
import shutil
from pathlib import Path
from collections import Counter
import os

import yaml
import numpy as np
import pandas as pd
from ultralytics import YOLO
from sklearn.model_selection import KFold

# 定义数据集路径
dataset_path = Path('./data')  # 替换成你的数据集路径

# 获取所有标签文件的列表
labels = sorted(dataset_path.rglob("*labels/*.txt"))  # 所有标签文件在'labels'目录中

# 获取当前文件的绝对路径
current_file_path = os.path.abspath(__file__)

# 获取当前文件所在的文件夹路径(即当前文件的根目录)
root_directory = os.path.dirname(current_file_path)

print("当前文件运行根目录:", root_directory)

# 从YAML文件加载类名
yaml_file = 'data/classes.yaml'
with open(yaml_file, 'r', encoding="utf8") as y:
    classes = yaml.safe_load(y)['names']
cls_idx = sorted(classes.keys())

# 创建DataFrame来存储每张图像的标签计数
indx = [l.stem for l in labels]  # 使用基本文件名作为ID(无扩展名)
labels_df = pd.DataFrame([], columns=cls_idx, index=indx)

# 计算每张图像的标签计数
for label in labels:
    lbl_counter = Counter()

    with open(label, 'r') as lf:
        lines = lf.readlines()

    for l in lines:
        # YOLO标签使用每行的第一个位置的整数作为类别
        lbl_counter[int(l.split(' ')[0])] += 1

    labels_df.loc[label.stem] = lbl_counter

# 用0.0替换NaN值
labels_df = labels_df.fillna(0.0)

# 使用K-Fold交叉验证拆分数据集
ksplit = 5
kf = KFold(n_splits=ksplit, shuffle=True, random_state=20)  # 设置random_state以获得可重复的结果
kfolds = list(kf.split(labels_df))
folds = [f'split_{n}' for n in range(1, ksplit + 1)]
folds_df = pd.DataFrame(index=indx, columns=folds)

# 为每个折叠分配图像到训练集或验证集
for idx, (train, val) in enumerate(kfolds, start=1):
    folds_df[f'split_{idx}'].loc[labels_df.iloc[train].index] = 'train'
    folds_df[f'split_{idx}'].loc[labels_df.iloc[val].index] = 'val'

# 计算每个折叠的标签分布比例
fold_lbl_distrb = pd.DataFrame(index=folds, columns=cls_idx)
for n, (train_indices, val_indices) in enumerate(kfolds, start=1):
    train_totals = labels_df.iloc[train_indices].sum()
    val_totals = labels_df.iloc[val_indices].sum()

    # 为避免分母为零,向分母添加一个小值(1E-7)
    ratio = val_totals / (train_totals + 1E-7)
    fold_lbl_distrb.loc[f'split_{n}'] = ratio

# 创建目录以保存分割后的数据集
save_path = Path(dataset_path / f'{datetime.date.today().isoformat()}_{ksplit}-Fold_Cross-val')
save_path.mkdir(parents=True, exist_ok=True)

# 获取图像文件列表
images = sorted((dataset_path / 'images').rglob("*.jpg"))  # 更改文件扩展名以匹配你的数据
ds_yamls = []

# 循环遍历每个折叠并复制图像和标签
for split in folds_df.columns:
    # 为每个折叠创建目录
    split_dir = save_path / split
    split_dir.mkdir(parents=True, exist_ok=True)
    (split_dir / 'train' / 'images').mkdir(parents=True, exist_ok=True)
    (split_dir / 'train' / 'labels').mkdir(parents=True, exist_ok=True)
    (split_dir / 'val' / 'images').mkdir(parents=True, exist_ok=True)
    (split_dir / 'val' / 'labels').mkdir(parents=True, exist_ok=True)



    # 创建数据集的YAML文件
    dataset_yaml = split_dir / f'{split}_dataset.yaml'
    ds_yamls.append(dataset_yaml.as_posix())
    split_dir = os.path.join(root_directory, split_dir.as_posix())

    with open(dataset_yaml, 'w') as ds_y:
        yaml.safe_dump({
            'path': split_dir,
            'train': 'train',
            'val': 'val',
            'names': classes
        }, ds_y)
print(ds_yamls)

# 将文件路径保存到一个txt文件中
with open('data/file_paths.txt', 'w') as f:
    for path in ds_yamls:
        f.write(path + '\n')

# 为每个折叠复制图像和标签到相应的目录
for image, label in zip(images, labels):
    for split, k_split in folds_df.loc[image.stem].items():
        # 目标目录
        img_to_path = save_path / split / k_split / 'images'
        lbl_to_path = save_path / split / k_split / 'labels'

        # 将图像和标签文件复制到新目录中
        # 如果文件已存在,可能会抛出SamefileError
        shutil.copy(image, img_to_path / image.name)
        shutil.copy(label, lbl_to_path / label.name)

运行代码后,会在data目录下生成一个文件夹,里面有5种不同划分的数据集

3.开始训练

下面的代码放在和上面代码的同级目录中,训练参数可以根据自己情况进行调整

from ultralytics import YOLO

weights_path = 'checkpoints/yolov8s.pt'
model = YOLO(weights_path, task='train')
ksplit = 5
# 从文本文件中加载内容并存储到一个列表中
ds_yamls = []
with open('data/file_paths.txt', 'r') as f:
    for line in f:
        # 去除每行末尾的换行符
        line = line.strip()
        ds_yamls.append(line)

# 打印加载的文件路径列表
print(ds_yamls)


results = {}
for k in range(ksplit):
    dataset_yaml = ds_yamls[k]
    model.train(data=dataset_yaml, batch=6, epochs=2, imgsz=1280, device=0, workers=8, single_cls=False, ) 

在这里插入图片描述

<think>好的,我现在需要详细解释YOLOv11如何使用交叉验证来训练模型,特别是具体的实现过程。首先,我需要确认YOLOv11是否真的存在,因为截至我的知识更新日期(2023年10月),YOLO系列的最新版本是YOLOv8,而YOLOv11可能是一个笔误或者社区的自定义版本。不过,用户提到YOLOv11,所以假设它是一个基于YOLO架构的改进模型,或者可能是指YOLOX或其他变体。无论如何,交叉验证(Cross-Validation)在目标检测中的应用流程应该是类似的,所以我需要根据通用的交叉验证结合YOLO的训练流程来回答。 首先,交叉验证的基本概念。交叉验证是一种统计方,用于评估模型的泛化能力,通常将数据集分成k个子集,轮流使用其中k-1个子集训练,剩下的1个作为验证集,重复k次。常见的有k交叉验证。但目标检测任务中,尤其是像YOLO这样的模型,通常数据集较大,可能更倾向于使用较小的k值,比如5,或者甚至只做一次训练-验证-测试分割。不过用户明确提到使用交叉验证来训练模型,所以需要具体说明如何在YOLOv11中实现。 接下来,我需要考虑在目标检测任务中使用交叉验证的具体步骤。通常,目标检测数据集包含图像和对应的标注文件(如YOLO格式的.txt文件,每行包含类别和边界框坐标)。交叉验证需要将数据集分成多个子集,并确保每个子集的数据分布一致,避免同一张图像出现在多个子集中。 实现交叉验证的步骤可能包括: 1. 数据集准备:确保数据集的标注正确,并按一定结构组织,比如images和labels文件夹。 2. 划分数据集为k:将整个数据集随机分成k个互不重叠的子集(folds)。 3. 训练和验证循环:对于每一,选择其中一个作为验证集,其余作为训练集。然后配置模型参数,进行训练,并在验证集上评估。 4. 结果汇总:计算各评估指标的平均值,以评估模型性能。 5. 最终模型训练:可选步骤,使用全部数据重新训练模型,以用于实际应用。 现在,结合YOLO的训练流程,具体到YOLOv11的实现细节可能包括: - 数据集的划分可能需要生成对应的训练和验证的.txt文件,列出每个fold的图像路径。 - YOLO的训练脚本通常需要指定训练和验证的数据路径,所以对于每一,需要生成对应的路径文件,并调整配置文件(如data.yaml中的train和val路径)。 - 每一训练完成后保存模型,并记录评估指标,如mAP(平均精度)等。 - 可能需要自动化脚本来自动进行k次训练过程,每次更换数据划分。 需要注意的问题包括: - 如何确保划分时保持类别分布的平衡,特别是在目标检测中,某些类别的样本可能较少,需要分层抽样。 - 处理数据增强时的参数设置,避免在训练过程中数据泄露。 - 存储和管理多个模型的问题,因为每一都会生成一个模型,可能需要较大的存储空间。 - 计算资源消耗,k交叉验证需要进行k次训练,时间成本较高。 现在,我需要验证这些步骤是否合理,并检查是否有遗漏的细节。例如,YOLO的训练通常使用train.py和val.py脚本,可能需要在每次训练时动态修改数据配置。例如,在Ultralytics的YOLOv5或v8中,数据配置是通过一个YAML文件指定的,其中包含训练和验证图像的路径。因此,实现k交叉验证需要为每一生成不同的训练和验证路径文件,并相应地更新数据配置。 具体实现步骤可能如下: 1. 准备数据集,确保结构符合YOLO的要求,如: ``` dataset/ images/ train/ img1.jpg ... val/ ... labels/ train/ img1.txt ... val/ ... ``` 但可能更常见的是将所有图像放在images文件夹,标签在labels文件夹,然后通过划分生成索引文件。 2. 使用脚本将数据集划分为k。例如,对于5交叉验证,将图像文件名列表随机打乱后分成5个部分。需要确保对应的标签文件也存在。这里可以使用Python的sklearn库中的KFold或StratifiedKFold来辅助划分。不过,在目标检测中,StratifiedKFold可能不太适用,因为每个图像可能有多个类别,分层抽样较为复杂,所以可能采用简单的随机划分。 3. 对于每个fold,生成对应的train和val的.txt文件,其中包含对应图像文件的路径。例如,fold1_train.txt和fold1_val.txt,每一行是图像的绝对或相对路径,如: ``` dataset/images/img1.jpg dataset/images/img2.jpg ... ``` 4. 创建对应的data.yaml文件,或在每次训练时动态修改数据配置。例如,对于每个fold,data.yaml中的train和val字段分别指向当前fold的train.txt和val.txt。 5. 循环训练每个fold,使用训练脚本指定数据配置、模型配置等参数。例如,使用类似于以下命令: ``` python train.py --data fold_i_data.yaml --cfg yolov11.yaml --weights '' --batch-size 64 --epochs 100 ``` 这里假设每个fold的data.yaml文件已经正确配置。 6. 在每次训练完成后,记录模型在验证集上的性能指标,如mAP@0.5、mAP@0.5:0.95等。 7. 所有fold训练完成后,分析各fold的结果,计算平均指标以评估模型性能。 8. 如果需要最终模型,可以将全部数据用于训练,但此时不再有独立的验证集,所以可能使用交叉验证中得到的超参数进行训练。 另外,可能需要注意的细节: - 数据泄漏问题:在划分数据集时,要确保同一图像的不同增强版本不会出现在训练和验证集中,但通常在训练时数据增强是动态应用的,因此原始图像只要不在同一fold的train和val中即可。 - 文件路径的处理:在生成.txt文件时,路径需要正确,否则YOLO训练时会找不到图像或标签。 - 模型保存:每个fold训练后保存最佳模型,方便后续集成或分析。 - 计算资源:k交叉验证需要k倍的训练时间,对于大型模型和数据集可能成本较高。 可能的挑战包括自动化脚本的编写,如何高效管理多个fold的数据配置和训练过程。可能需要编写Shell脚本或Python脚本来自动生成每个fold的数据配置,并依次启动训练任务。 总结来说,YOLOv11(假设其训练流程类似YOLOv5或v8)实现交叉验证的步骤大致为数据集划分、生成各fold的数据索引文件、循环训练并验证、汇总结果。需要处理的主要是数据划分和训练配置的动态调整。</think>YOLOv11使用交叉验证训练模型的实现过程可分为以下步骤,结合目标检测任务特性和YOLO框架的工程实践进行说明: --- ### **1. 数据集准备与格式规范** - **数据结构**:按YOLO格式组织数据集,包含`images`(存放图片)和`labels`(存放对应标注文件)文件夹,标注文件为`.txt`格式,每行格式为:`类别索引 x_center y_center width height`(归一化坐标)。 - **示例目录**: ``` dataset/ images/ img1.jpg img2.jpg ... labels/ img1.txt img2.txt ... ``` --- ### **2. 划分K子集** - **随机打乱**:将图片文件名列表随机打乱,确保分布均匀。 - **K分割**:使用工具(如Python的`KFold`)将数据划分为$K$个互斥子集。 ```python from sklearn.model_selection import KFold import os image_files = [f for f in os.listdir("dataset/images") if f.endswith(".jpg")] kfold = KFold(n_splits=5, shuffle=True, random_state=42) for fold, (train_idx, val_idx) in enumerate(kfold.split(image_files)): train_files = [image_files[i] for i in train_idx] val_files = [image_files[i] for i in val_idx] # 生成各fold的train.txt和val.txt ``` --- ### **3. 生成数据索引文件** - 为每生成训练集和验证集的索引文件(如`fold1_train.txt`),内容为对应图片的**相对路径**: ``` dataset/images/img1.jpg dataset/images/img3.jpg ... ``` --- ### **4. 配置数据加载文件** - 创建`data_fold1.yaml`,动态指定当前数据路径: ```yaml train: fold1_train.txt val: fold1_val.txt names: 0: cat 1: dog ``` --- ### **5. 训练与验证循环** - **启动训练**:对每分别执行训练命令,指定对应的数据配置和超参数。 ```bash python train.py --data data_fold1.yaml --cfg yolov11.yaml --weights '' --batch-size 64 --epochs 100 ``` - **关键参数**: - 使用早停(Early Stopping)防止过拟合。 - 保存每的最佳模型(如`yolov11_fold1_best.pt`)。 --- ### **6. 评估与结果聚合** - **单评估**:每训练完成后,在验证集上计算评估指标(如mAP@0.5、Recall)。 - **全局统计**:汇总$K$结果,计算均值与方差,评估模型泛化能力: $$ \text{mAP}_{\text{avg}} = \frac{1}{K} \sum_{i=1}^{K} \text{mAP}_i $$ --- ### **7. 最终模型训练(可选)** - **全量训练**:若模型性能达标,合并所有数据重新训练最终模型,提升泛化性。 --- ### **关键注意事项** - **数据分布**:确保每中各类别样本比例均衡,避免偏差。 - **路径一致性**:索引文件中的路径需与实际存储一致,避免加载失败。 - **资源管理**:交叉验证需$K$倍计算资源,建议分布式训练或调整数(如$K=5$)。 --- ### **代码示例:自动化K训练** ```python import subprocess for fold in range(1, 6): # 生成data_fold{i}.yaml config = f"data_fold{fold}.yaml" # 启动训练 cmd = f"python train.py --data {config} --cfg yolov11.yaml --name fold{fold}" subprocess.run(cmd, shell=True) ``` --- 通过上述流程,YOLOv11可充分利用有限数据,通过交叉验证提升模型鲁棒性,尤其适用于小规模数据集或对泛化能力要求较高的场景。
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Zhijun.li@Studio

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

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

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

打赏作者

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

抵扣说明:

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

余额充值