Ultralytics YOLO27:
Get Started

使用 Ultralytics 进行 K 折交叉验证#

简介#

本指南全面介绍如何在 Ultralytics 生态系统中为目标检测数据集实现 K 折交叉验证。我们将使用 YOLO 检测格式以及 sklearn、pandas 和 PyYAML 等关键 Python 库,指导你完成必要的设置、生成特征向量,以及执行 K 折数据集划分。

K-fold cross validation data splitting

无论你的项目使用的是 Fruit Detection 数据集还是自定义数据源,本教程都旨在帮助你理解并应用 K 折交叉验证,从而提高机器学习模型的可靠性和鲁棒性。本教程使用 k=5 折,但请记住,最佳折数会因数据集和项目具体情况而异。K 折交叉验证最适用于数据集较小、噪声较多或变化较大的情况;对于大型、多样化的数据集,通常只需合理划分训练集、验证集和测试集即可。

开始吧。

设置#

  • 你的标注应采用 YOLO detection format。

  • 本指南假设标注文件已保存在本地。

  • 在本示例中,我们使用 Fruit Detection 数据集。

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

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • 本教程使用 k=5 折。不过,你应根据自己的数据集确定最佳折数。

  1. 为你的项目新建一个 Python 虚拟环境(venv)并激活它。使用 pip(或你偏好的软件包管理器)安装:

    • Ultralytics 库:pip install -U ultralytics。或者,你也可以克隆官方 repo。
    • Scikit-learn、pandas 和 PyYAML:pip install -U scikit-learn pandas pyyaml。
  2. 确认你的标注采用 YOLO detection format。

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

为目标检测数据集生成特征向量#

  1. 首先,新建一个 example.py Python 文件,用于执行以下步骤。

  2. 通过软件包的数据集所有者加载已配置的训练图像和验证图像,并保持测试集划分不变。

    from pathlib import Path
    
    from ultralytics.data.dataset import YOLODataset
    from ultralytics.data.utils import check_det_dataset
    
    yaml_file = "path/to/data.yaml"
    data = check_det_dataset(yaml_file)
    dataset_path = Path(data["path"])
    sources = []
    for split in ("train", "val"):
        source = data.get(split)
        if source:
            sources.extend(source if isinstance(source, list) else [source])
    dataset = YOLODataset(sources, data=data, augment=False)
    images = [Path(p) for p in dataset.im_files]
  3. 现在,读取数据集 YAML 文件的内容,并提取类别标签的索引。

    classes = data["names"]
    cls_idx = sorted(classes.keys())
  4. 初始化一个空的 pandas DataFrame。

    import pandas as pd
    
    labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images)
  5. 统计标注文件中每个类别标签的实例数。

    from collections import Counter
    
    for image, label in zip(images, dataset.labels):
        lbl_counter = Counter(label["cls"].flatten().astype(int))
        labels_df.loc[image, list(lbl_counter)] = list(lbl_counter.values())
  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. 现在,我们将使用 sklearn.model_selection 中的 KFold 类,将数据集划分为 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)  # 设置 random_state 以获得可重复的结果
    
    kfolds = list(kf.split(labels_df))
  2. 现在,数据集已划分为 k 折,每折包含 train 和 val 索引列表。我们将构建一个 DataFrame,以便更清晰地展示这些结果。

    folds = [f"split_{n}" for n in range(1, ksplit + 1)]
    folds_df = pd.DataFrame(index=labels_df.index, columns=folds)
    
    for i, (train, val) in enumerate(kfolds, start=1):
        folds_df.loc[labels_df.index[train], f"split_{i}"] = "train"
        folds_df.loc[labels_df.index[val], f"split_{i}"] = "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 文件。使用文本列表可以避免复制数据集 k 次。

    import yaml
    
    save_path = dataset_path.parent / f"{ksplit}-Fold_Cross-val"
    save_path.mkdir(parents=True, exist_ok=True)
    ds_yamls = []
    
    for split in folds_df.columns:
        for partition in ("train", "val"):
            split_images = folds_df.index[folds_df[split] == partition]
            paths = "\n".join(map(str, split_images))
            (save_path / f"{split}_{partition}.txt").write_text(f"{paths}\n")
    
        dataset_yaml = save_path / f"{split}.yaml"
        ds_yamls.append(dataset_yaml)
        with open(dataset_yaml, "w") as ds_y:
            yaml.safe_dump(
                {
                    "path": save_path.as_posix(),
                    "train": f"{split}_train.txt",
                    "val": f"{split}_val.txt",
                    "names": classes,
                },
                ds_y,
            )

保存记录(可选)#

你也可以将 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"  # 小型模型请使用 yolo26n.pt
    model = YOLO(weights_path, task="detect")
  2. 接下来,遍历数据集 YAML 文件并运行训练。结果将保存到由 project 和 name 参数指定的目录中。默认情况下,该目录为 'runs/detect/train#',其中 # 是整数索引。

    results = {}
    
    # Define your additional arguments here
    batch = 16
    project = "kfold_demo"
    epochs = 100
    
    for k, dataset_yaml in enumerate(ds_yamls):
        model = YOLO(weights_path, task="detect")
        results[k] = model.train(
            data=dataset_yaml, epochs=epochs, batch=batch, project=project, name=f"fold_{k + 1}"
        )  # include any additional train arguments
  3. 你也可以使用 Ultralytics data.split.autosplit 函数自动划分数据集:

    from ultralytics.data.split import autosplit
    
    # 自动将数据集划分为 train/val/test
    autosplit(path="path/to/images", weights=(0.8, 0.2, 0.0), annotated_only=True)

结论#

本指南介绍了如何使用 K 折交叉验证训练 YOLO 目标检测模型。我们了解了如何将训练集和验证集划分为 K 个分区,以及如何使用生成的比例表检查随机划分后的类别平衡情况。

我们还介绍了如何创建报告 DataFrame,以直观呈现这些划分中的数据拆分和标签分布,让我们清楚了解训练集和验证集的结构。

我们还可以选择保存记录以供日后参考,这在大型项目或排查模型性能问题时尤其有用。

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

K 折交叉验证是一种充分利用现有数据的稳健方法,有助于确保模型在不同数据子集上的性能可靠且一致。这样可以得到泛化能力更强、可靠性更高的模型,降低模型对特定数据模式过拟合的可能性。

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

常见问题#

  • K 折交叉验证是一种将数据集划分为 'k' 个子集(折)来更可靠地评估模型性能的方法。每一折都会用作训练数据和验证数据。在目标检测中,使用 K 折交叉验证有助于确保 Ultralytics YOLO 模型在不同数据划分上的性能稳健且具有良好的泛化能力,从而提高模型的可靠性。有关使用 Ultralytics YOLO 设置 K 折交叉验证的详细说明,请参阅使用 Ultralytics 进行 K 折交叉验证。

  • 使用 Ultralytics YOLO 实现 K 折交叉验证,请按以下步骤操作:

    1. 确认标注采用 YOLO detection format。
    2. 使用 sklearn、pandas 和 pyyaml 等 Python 库。
    3. 根据数据集创建特征向量。
    4. 使用来自 sklearn.model_selection 的 KFold 划分数据集。
    5. 在每个划分上训练 YOLO 模型。

    如需完整指南,请参阅文档中的 K 折数据集划分部分。

  • 本指南介绍的工作流程针对 YOLO 检测格式,但同样的方法也适用于所有 YOLO 任务——任务会影响折的组成方式,而不会改变交叉验证是否有帮助:

    任务折的设计
    detect按图像划分,并平衡各折中的目标和类别分布。将相关图像(来自同一患者、视频序列、摄像头或地点的图像)放在同一折中。
    segment采用与检测任务相同的图像级策略,同时确保每折都涵盖相应的掩码和类别。
    classify优先使用分层划分,使训练集和验证集中的类别频率保持平衡。
    pose按个体或序列划分,确保同一个人或动物不会同时出现在某一折的两侧。
    obb按图像划分,并将来自同一场景的切片或裁剪图像放在一起——这对于航空影像尤其重要。

    无论任务是什么,都要避免将近似重复或相关样本分到相反的折中:这种数据泄漏会让验证指标虚高,远超模型在生产环境中的实际表现。

  • Ultralytics YOLO 可实现先进的实时目标检测,同时具有高准确率和高效率。它用途广泛,支持多种计算机视觉任务,例如检测、实例分割、语义分割和分类。此外,它还能与 Ultralytics Platform 等工具无缝集成,实现无需编写代码的模型训练和部署。有关更多详情,请访问 Ultralytics YOLO 页面,了解其优势和功能。

  • 你的标注应遵循 YOLO 检测格式。每个标注文件都必须列出目标类别及其在图像中的边界框坐标。YOLO 格式能为目标检测模型训练提供高效、标准化的数据处理流程。有关正确标注格式的更多信息,请参阅 YOLO 检测格式指南。

  • 可以,只要标注采用 YOLO 检测格式,你就能对任何自定义数据集使用 K 折交叉验证。将数据集路径和类别标签替换为适用于自定义数据集的内容即可。这种灵活性让任何目标检测项目都能通过 K 折交叉验证获得稳健的模型评估结果。如需查看实际示例,请参阅生成特征向量部分。

评论