Skip to content

使用Ultralytics进行K折交叉验证

简介

本综合指南展示了在Ultralytics生态系统中为对象检测数据集实现K折交叉验证的过程。我们将利用YOLO检测格式以及sklearn、pandas和PyYaml等关键Python库,引导您完成必要的设置、生成特征向量的过程以及执行K折数据集分割的操作。

K折交叉验证概述

无论您的项目涉及水果检测数据集还是自定义数据源,本教程旨在帮助您理解和应用K折交叉验证,以增强机器学习模型的可靠性和鲁棒性。虽然我们在本教程中应用了k=5折,但请记住,最佳的折数可能会根据您的数据集和项目的具体情况而有所不同。

事不宜迟,让我们开始吧!

设置

  • 您的标注应采用YOLO检测格式

  • 本指南假设标注文件在本地可用。

  • 在我们的演示中,我们使用了水果检测数据集。

    • 该数据集总共包含8479张图像。
    • 它包括6个类别标签,每个类别标签的实例总数如下所示。
类别标签 实例数量
苹果 7049
葡萄 7202
菠萝 1613
橙子 15549
香蕉 3536
西瓜 1976
  • 必要的Python包包括:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • 本教程使用k=5折。然而,您应根据您的特定数据集确定最佳折数。

  • 为您的项目启动一个新的Python虚拟环境(venv)并激活它。使用pip(或您首选的包管理器)安装:

    • Ultralytics库:pip install -U ultralytics。或者,您可以克隆官方仓库
    • Scikit-learn、pandas和PyYAML:pip install -U scikit-learn pandas pyyaml
  • 验证您的标注是否采用YOLO检测格式

    • 在本教程中,所有标注文件位于Fruit-Detection/labels目录中。

为对象检测数据集生成特征向量

  1. 首先为以下步骤创建一个新的example.py Python文件。

  2. 继续获取数据集的所有标签文件。

    from pathlib import Path
    
    dataset_path = Path("./Fruit-detection")  # 替换为您的自定义数据路径'path/to/dataset'
    labels = sorted(dataset_path.rglob("*labels/*.txt"))  # 所有位于'labels'中的数据
    
  3. 现在,读取数据集YAML文件的内容并提取类别标签的索引。

    yaml_file = "path/to/data.yaml"  # 包含数据目录和名称字典的YAML文件
    with open(yaml_file, "r", encoding="utf8") as y:
        classes = yaml.safe_load(y)["names"]
    cls_idx = sorted(classes.keys())
    
  4. 初始化一个空的pandas DataFrame。

    import pandas as pd
    
    indx = [label.stem for label in labels]  # 使用基本文件名作为ID(无扩展名)
    labels_df = pd.DataFrame([], columns=cls_idx, index=indx)
    
  5. 统计标注文件中每个类别标签的实例数量。

    from collections import Counter
    
    for label in labels:
        lbl_counter = Counter()
    
        with open(label, "r") as lf:
            lines = lf.readlines()
    
        for line in lines:
            # YOLO标签的类别使用每行第一个位置的整数
            lbl_counter[int(line.split(" ")[0])] += 1
    
        labels_df.loc[label.stem] = lbl_counter
    
    labels_df = labels_df.fillna(0.0)  # 将`nan`值替换为`0.0`
    
  6. 以下是填充后的DataFrame的示例视图:

    pandas 0 1 2 3 4 5 '0000a16e4b057580_jpg.rf.00ab48988370f64f5ca8ea4...' 0.0 0.0 0.0 0.0 0.0 7.0 '0000a16e4b057580_jpg.rf.7e6dce029fb67f01eb19aa7...' 0.0 0.0 0.0 0.0 0.0 7.0 '0000a16e4b057580_jpg.rf.bc4d31cdcbe229dd022957a...' 0.0 0.0 0.0 0.0 0.0 7.0 '00020ebf74c4881c_jpg.rf.508192a0a97aa6c4a3b6882...' 0.0 0.0 0.0 1.0 0.0 0.0 '00020ebf74c4881c_jpg.rf.5af192a2254c8ecc4188a25...' 0.0 0.0 0.0 1.0 0.0 0.0 ... ... ... ... ... ... ... 'ff4cd45896de38be_jpg.rf.c4b5e967ca10c7ced3b9e97...' 0.0 0.0 0.0 0.0 0.0 2.0 'ff4cd45896de38be_jpg.rf.ea4c1d37d2884b3e3cbce08...' 0.0 0.0 0.0 0.0 0.0 2.0 'ff5fd9c3c624b7dc_jpg.rf.bb519feaa36fc4bf630a033...' 1.0 0.0 0.0 0.0 0.0 0.0 'ff5fd9c3c624b7dc_jpg.rf.f0751c9c3aa4519ea3c9d6a...' 1.0 0.0 0.0 0.0 0.0 0.0 'fffe28b31f2a70d4_jpg.rf.7ea16bd637ba0711c53b540...' 0.0 6.0 0.0 0.0 0.0 0.0

    行索引了标签文件,每个标签文件对应数据集中的一个图像,列对应于类标签索引。每一行代表一个伪特征向量,包含数据集中每个类标签的计数。这种数据结构使得可以将K折交叉验证应用于目标检测数据集。
    
    ## K折数据集分割
    
    1. 现在我们将使用`sklearn.model_selection`中的`KFold`类来生成数据集的`k`个分割。
    
        - 重要提示:
            - 设置`shuffle=True`确保类在分割中的随机分布。
            - 通过设置`random_state=M`,其中`M`是一个选定的整数,可以获得可重复的结果。
    
        ```python
        from sklearn.model_selection import KFold
    
        ksplit = 5
        kf = KFold(n_splits=ksplit, shuffle=True, random_state=20)  # 设置random_state以获得可重复的结果
    
        kfolds = list(kf.split(labels_df))
        ```
    
    2. 数据集现在已经被分割成`k`折,每折包含`train`和`val`索引的列表。我们将构建一个DataFrame来更清晰地显示这些结果。
    
        ```python
        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"
        ```
    
    3. 现在我们将计算每个折中类标签的分布,作为`val`中存在的类与`train`中存在的类的比率。
    
        ```python
        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
        ```
    
        理想情况下,所有类的比率在每个分割中都应该合理相似。然而,这将取决于您的数据集的具体情况。
    
    4. 接下来,我们为每个分割创建目录和数据集YAML文件。
    
        ```python
        import datetime
    
        supported_extensions = [".jpg", ".jpeg", ".png"]
    
        # 初始化一个空列表来存储图像文件路径
        images = []
    
        # 遍历支持的扩展名并收集图像文件
        for ext in supported_extensions:
            images.extend(sorted((dataset_path / "images").rglob(f"*{ext}")))
    
        # 创建必要的目录和数据集YAML文件(未更改)
        save_path = Path(dataset_path / f"{datetime.date.today().isoformat()}_{ksplit}-Fold_Cross-val")
        save_path.mkdir(parents=True, exist_ok=True)
        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)
    
            with open(dataset_yaml, "w") as ds_y:
                yaml.safe_dump(
                    {
                        "path": split_dir.as_posix(),
                        "train": "train",
                        "val": "val",
                        "names": classes,
                    },
                    ds_y,
                )
        ```
    
    5. 最后,将图像和标签复制到每个分割的相应目录('train'或'val')中。
    
        - **注意:** 这部分代码所需的时间将根据数据集的大小和系统硬件的不同而有所变化。
    
        ```python
        import shutil
    
        for image, label in zip(images, labels):
            for split, k_split in folds_df.loc[image.stem].items():
                # 目标目录
    ```python
    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)
    

保存记录(可选)

可选地,您可以将K折分割和标签分布DataFrame的记录保存为CSV文件,以供将来参考。

folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")

使用K折数据分割训练YOLO

  1. 首先,加载YOLO模型。

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"
    model = YOLO(weights_path, task="detect")
    
  2. 接下来,遍历数据集YAML文件以运行训练。结果将保存到由projectname参数指定的目录中。默认情况下,此目录为'exp/runs#',其中#是一个整数索引。

    results = {}
    
    # 在此处定义您的其他参数
    batch = 16
    project = "kfold_demo"
    epochs = 100
    
    for k in range(ksplit):
        dataset_yaml = ds_yamls[k]
        model.train(data=dataset_yaml, epochs=epochs, batch=batch, project=project)  # 包括任何训练参数
        results[k] = model.metrics  # 保存输出指标以供进一步分析
    

结论

在本指南中,我们探讨了使用K折交叉验证训练YOLO目标检测模型的过程。我们学习了如何将数据集分成K个分区,确保不同折之间的类别分布平衡。

我们还探讨了创建报告DataFrame以可视化数据分割和这些分割中的标签分布的过程,为我们提供了对训练和验证集结构的清晰洞察。

可选地,我们保存了记录以供将来参考,这在大型项目或调试模型性能时可能特别有用。

最后,我们通过循环使用每个分割来实现实际的模型训练,并保存训练结果以供进一步分析和比较。

这种K折交叉验证技术是充分利用可用数据的一种稳健方法,并有助于确保模型性能在不同数据子集上可靠且一致。这导致了一个更具泛化性和可靠性的模型,不太可能过度拟合特定数据模式。

请记住,尽管我们在本指南中使用了YOLO,但这些步骤大多可转移到其他机器学习模型。理解这些步骤使您能够有效地在您自己的机器学习项目中应用交叉验证。祝您编码愉快!

常见问题

什么是K折交叉验证,为什么它在目标检测中有用?

K折交叉验证是一种技术,其中数据集被分成'k'个子集(折),以更可靠地评估模型性能。每个折既作为训练数据,也作为验证数据。在目标检测的背景下,使用K折交叉验证有助于确保您的Ultralytics YOLO模型的性能在不同数据分割上稳健且可泛化,增强了其可靠性。有关使用Ultralytics YOLO设置K折交叉验证的详细说明,请参阅K折交叉验证与Ultralytics

如何使用Ultralytics YOLO实现K折交叉验证?

要使用Ultralytics YOLO实现K折交叉验证,您需要遵循以下步骤:

  1. 验证注释是否为YOLO检测格式
  2. 使用Python库如sklearnpandaspyyaml
  3. 从您的数据集中创建特征向量。
  4. 使用sklearn.model_selection中的KFold分割您的数据集。
  5. 在每个分割上训练YOLO模型。

有关全面的指南,请参阅我们的文档中的K折数据集分割部分。

为什么我应该使用Ultralytics YOLO进行目标检测?

Ultralytics YOLO提供了最先进的、实时的目标检测,具有高准确性和效率。它功能多样,支持多种计算机视觉任务,如检测、分割和分类。此外,它与Ultralytics HUB等工具无缝集成,用于无代码模型训练和部署。有关更多详细信息,请浏览我们的Ultralytics YOLO页面上的优势和功能。

如何确保我的注释格式正确以用于Ultralytics YOLO?

你的标注应遵循YOLO检测格式。每个标注文件必须列出对象类别及其在图像中的边界框坐标。YOLO格式确保了对象检测模型训练数据处理的简化和标准化。有关正确标注格式的更多信息,请访问YOLO检测格式指南

我可以在除水果检测外的自定义数据集上使用K折交叉验证吗?

是的,只要标注采用YOLO检测格式,你就可以在任何自定义数据集上使用K折交叉验证。将数据集路径和类别标签替换为你自定义数据集的特定内容。这种灵活性确保了任何对象检测项目都能通过K折交叉验证进行稳健的模型评估。如需实际示例,请查看我们的生成特征向量部分。


📅 Created 11 months ago ✏️ Updated 20 days ago

Comments