跳至内容

K-Fold 交叉验证Ultralytics

导言

This comprehensive guide illustrates the implementation of K-Fold Cross Validation for object detection datasets within the Ultralytics ecosystem. We'll leverage the YOLO detection format and key Python libraries such as sklearn, pandas, and PyYaml to guide you through the necessary setup, the process of generating feature vectors, and the execution of a K-Fold dataset split.

K 折交叉验证概述

Whether your project involves the Fruit Detection dataset or a custom data source, this tutorial aims to help you comprehend and apply K-Fold Cross Validation to bolster the reliability and robustness of your machine learning models. While we're applying 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.或者,您也可以克隆官方的 repo.
    • 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")  # replace with 'path/to/dataset' for your custom data
    labels = sorted(dataset_path.rglob("*labels/*.txt"))  # all data in 'labels'
    
  3. 现在,读取数据集 YAML 文件的内容并提取类标签的索引。

    yaml_file = "path/to/data.yaml"  # your data YAML with data directories and names dictionary
    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]  # uses base filename as ID (no extension)
    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:
            # classes for YOLO label uses integer at first position of each line
            lbl_counter[int(line.split(" ")[0])] += 1
    
        labels_df.loc[label.stem] = lbl_counter
    
    labels_df = labels_df.fillna(0.0)  # replace `nan` values with `0.0`
    
  6. 以下是已填充 DataFrame 的示例视图:

                                                           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. 现在,我们将使用 KFoldsklearn.model_selection 以产生 k 对数据集进行分割。

    • 重要:
      • 设置 shuffle=True 确保了分班中班级的随机分布。
      • 通过设置 random_state=M 其中 M 是一个选定的整数,这样就可以得到可重复的结果。
    from sklearn.model_selection import KFold
    
    ksplit = 5
    kf = KFold(n_splits=ksplit, shuffle=True, random_state=20)  # setting random_state for repeatable results
    
    kfolds = list(kf.split(labels_df))
    
  2. 数据集现已分为 k 折叠,每个折叠都有一个 trainval 指数。我们将构建一个 DataFrame 来更清晰地显示这些结果。

    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.

    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()
    
        # To avoid division by zero, we add a small value (1E-7) to the denominator
        ratio = val_totals / (train_totals + 1e-7)
        fold_lbl_distrb.loc[f"split_{n}"] = ratio
    

    最理想的情况是,每次分割和不同类别的所有类别比率都相当相似。不过,这取决于数据集的具体情况。

  4. 接下来,我们为每个分割创建目录和数据集 YAML 文件。

    import datetime
    
    supported_extensions = [".jpg", ".jpeg", ".png"]
    
    # Initialize an empty list to store image file paths
    images = []
    
    # Loop through supported extensions and gather image files
    for ext in supported_extensions:
        images.extend(sorted((dataset_path / "images").rglob(f"*{ext}")))
    
    # Create the necessary directories and dataset YAML files (unchanged)
    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:
        # Create directories
        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)
    
        # Create dataset YAML files
        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")中。

    • 注意:这部分代码所需的时间将根据数据集的大小和系统硬件而有所不同。
    import shutil
    
    for image, label in zip(images, labels):
        for split, k_split in folds_df.loc[image.stem].items():
            # Destination directory
            img_to_path = save_path / split / k_split / "images"
            lbl_to_path = save_path / split / k_split / "labels"
    
            # Copy image and label files to new directory (SamefileError if file already exists)
            shutil.copy(image, img_to_path / image.name)
            shutil.copy(label, lbl_to_path / label.name)
    

保存记录(可选)

您可以选择将 K 折分割和标签分布数据框的记录保存为 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 = {}
    
    # Define your additional arguments here
    batch = 16
    project = "kfold_demo"
    epochs = 100
    
    for k in range(ksplit):
        dataset_yaml = ds_yamls[k]
        model = YOLO(weights_path, task="detect")
        model.train(data=dataset_yaml, epochs=epochs, batch=batch, project=project)  # include any train arguments
        results[k] = model.metrics  # save output metrics for further analysis
    

结论

在本指南中,我们探索了使用 K 折交叉验证来训练YOLO 物体检测模型的过程。我们学习了如何将数据集分成 K 个分区,确保不同折叠中的类分布均衡。

我们还探索了创建报告 DataFrames 的程序,以可视化数据拆分和标签在这些拆分中的分布,让我们清楚地了解训练集和验证集的结构。

此外,我们还保存了我们的记录,以备将来参考,这在大型项目或排除模型性能故障时尤为有用。

最后,我们在一个循环中使用每个拆分来执行实际的模型训练,保存训练结果,以便进一步分析和比较。

这种 K 折交叉验证技术是充分利用可用数据的一种稳健方法,有助于确保模型在不同数据子集中的性能是可靠和一致的。这将产生一个更具通用性和可靠性的模型,从而减少对特定数据模式的过度拟合。

请记住,虽然我们在本指南中使用的是YOLO ,但这些步骤大多可用于其他机器学习模型。了解了这些步骤,你就能在自己的机器学习项目中有效地应用交叉验证。祝你编码愉快

常见问题

什么是 K 折交叉验证,为什么它在物体检测中很有用?

K-Fold Cross Validation is a technique where the dataset is divided into 'k' subsets (folds) to evaluate model performance more reliably. Each fold serves as both training and validation data. In the context of object detection, using K-Fold Cross Validation helps to ensure your Ultralytics YOLO model's performance is robust and generalizable across different data splits, enhancing its reliability. For detailed instructions on setting up K-Fold Cross Validation with Ultralytics YOLO, refer to K-Fold Cross Validation with Ultralytics.

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

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

  1. 验证注释是否符合YOLO 检测格式
  2. 使用Python 库,如 sklearn, pandaspyyaml.
  3. 根据数据集创建特征向量
  4. 使用 KFoldsklearn.model_selection.
  5. 在每个分割点上训练YOLO 模型。

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

为什么要使用Ultralytics YOLO 进行物体检测?

Ultralytics YOLO offers state-of-the-art, real-time object detection with high accuracy and efficiency. It's versatile, supporting multiple computer vision tasks such as detection, segmentation, and classification. Additionally, it integrates seamlessly with tools like Ultralytics HUB for no-code model training and deployment. For more details, explore the benefits and features on our Ultralytics YOLO page.

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

Your annotations should follow the YOLO detection format. Each annotation file must list the object class, alongside its bounding box coordinates in the image. The YOLO format ensures streamlined and standardized data processing for training object detection models. For more information on proper annotation formatting, visit the YOLO detection format guide.

除了水果检测之外,我还能对自定义数据集使用 K-Fold 交叉验证吗?

是的,只要注释是YOLO 检测格式,您就可以对任何自定义数据集使用 K-Fold 交叉验证。用您自定义数据集的特定路径和类标签代替数据集路径和类标签。这种灵活性确保任何对象检测项目都能受益于使用 K 折交叉验证进行的强大模型评估。有关实用示例,请查看我们的生成特征矢量部分。

📅 Created 1 year ago ✏️ Updated 6 days ago

评论