使用 Ultralytics 进行 K 折交叉验证#
简介#
本综合指南介绍如何在 Ultralytics 生态系统中为目标检测数据集实现 K 折交叉验证。我们将使用 YOLO 检测格式以及 sklearn、pandas 和 PyYAML 等关键 Python 库,指导你完成必要的设置、特征向量生成过程以及 K 折数据集划分的执行。
无论你的项目使用的是 Fruit Detection 数据集还是自定义数据源,本教程都旨在帮助你理解并应用 K 折交叉验证,从而提升机器学习模型的可靠性和稳健性。本教程使用 k=5 个折,但请注意,最佳折数可能因数据集和项目具体情况而异。当数据集较小、噪声较多或变化很大时,K 折交叉验证最有价值;对于规模较大且多样化的数据集,经过良好构建的训练/验证/测试集划分通常就足够了。
让我们开始吧。
设置#
-
你的标注应采用 YOLO 检测格式。
-
本指南假设标注文件已在本地准备就绪。
-
在我们的演示中,我们使用 Fruit Detection 数据集。
- 该数据集总共包含 8479 张图像。
- 其中包含 6 个类别标签,下面列出了每个类别的实例总数。
| 类别标签 | 实例数量 |
|---|---|
| 苹果 | 7049 |
| 葡萄 | 7202 |
| 菠萝 | 1613 |
| 橙子 | 15549 |
| 香蕉 | 3536 |
| 西瓜 | 1976 |
-
所需的 Python 软件包包括:
ultralyticssklearnpandaspyyaml
-
本教程使用
k=5个折。不过,你应根据具体数据集确定最佳折数。
-
为你的项目创建新的 Python 虚拟环境(
venv)并将其激活。使用pip(或你首选的软件包管理器)安装:- Ultralytics 库:
pip install -U ultralytics。或者,你也可以克隆官方 仓库。 - Scikit-learn、pandas 和 PyYAML:
pip install -U scikit-learn pandas pyyaml。
- Ultralytics 库:
-
确认你的标注采用 YOLO 检测格式。
- 在本教程中,所有标注文件都位于
Fruit-Detection/labels目录中。
- 在本教程中,所有标注文件都位于
为目标检测数据集生成特征向量#
-
首先,为下面的步骤创建一个新的
example.pyPython 文件。 -
通过软件包数据集所有者加载已配置的训练和验证图像,同时保持测试集划分不变。
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] -
现在,读取数据集 YAML 文件的内容并提取类别标签的索引。
classes = data["names"] cls_idx = sorted(classes.keys()) -
初始化一个空的
pandasDataFrame。import pandas as pd labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images) -
统计标注文件中每个类别标签的实例数量。
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()) -
以下是填充后的 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 折数据集划分#
-
现在,我们将使用
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) # setting random_state for repeatable results kfolds = list(kf.split(labels_df)) - 重要提示:
-
数据集现已划分为
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" -
现在,我们将计算每个折中类别标签的分布,作为
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理想情况下,每个划分内以及不同类别之间的所有类别比例都应大致相似。不过,这取决于你的数据集具体情况。
-
为每个划分写入图像列表和数据集 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#
-
首先,加载 YOLO 模型。
from ultralytics import YOLO weights_path = "path/to/weights.pt" # use yolo26n.pt for a small model model = YOLO(weights_path, task="detect") -
接下来,遍历数据集 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 -
你也可以使用 Ultralytics data.split.autosplit 函数自动划分数据集:
from ultralytics.data.split import autosplit # Automatically split dataset into 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 折交叉验证。
本指南中的工作流针对 YOLO 检测格式,但相同的方法也适用于每项 YOLO 任务——任务会改变折的构成方式,而不会改变交叉验证是否有帮助:
任务 折的设计 detect在图像级别进行划分,在各折之间平衡目标和类别分布。将相关图像(同一患者、视频序列、摄像头或地点)保留在同一个折中。 segment采用与检测相同的图像级策略,同时确保每个折都保留掩码和类别覆盖。 classify优先使用分层折,以便训练集和验证集之间的类别频率保持平衡。 pose按主体或序列进行划分,确保同一个人或动物不会出现在某个折的两侧。 obb在图像级别进行划分,将来自同一场景的图块或裁剪图放在一起——这对于航空影像尤其重要。 无论任务是什么,都应将近重复样本和相关样本排除在相对的折之外:这类数据泄漏会使验证指标远高于模型在生产环境中能够达到的水平。
Ultralytics YOLO 提供业界领先的实时目标检测能力,兼具高准确率和高效率。它具有很强的通用性,支持多种计算机视觉任务,例如检测、实例分割、语义分割和分类。此外,它还能与 Ultralytics Platform 等工具无缝集成,实现无需编码的模型训练和部署。如需了解更多信息,请参阅我们的 Ultralytics YOLO 页面以探索其优势和功能。
你的标注应遵循 YOLO 检测格式。每个标注文件都必须列出目标类别及其在图像中的边界框坐标。YOLO 格式可确保在训练目标检测模型时进行流畅且标准化的数据处理。如需了解正确的标注格式,请访问 YOLO 检测格式指南。
可以,只要标注采用 YOLO 检测格式,你就可以将 K 折交叉验证用于任何自定义数据集。将数据集路径和类别标签替换为自定义数据集对应的内容即可。这种灵活性确保任何目标检测项目都能通过 K 折交叉验证获得稳健的模型评估。如需查看实际示例,请参阅我们的生成特征向量部分。