使用 Ultralytics 进行 K 折交叉验证
简介
本综合指南展示了如何在 Ultralytics 生态系统中为 目标检测 数据集实现 K 折交叉验证。我们将利用 YOLO 检测格式以及诸如 sklearn、pandas 和 PyYAML 等关键 Python 库,引导你完成必要的设置、生成特征向量的过程以及执行 K 折数据集拆分。
无论你的项目涉及水果检测(Fruit Detection)数据集还是自定义数据源,本教程旨在帮助你理解并应用 K 折交叉验证,以提高你的 机器学习 模型的可靠性和稳健性。虽然我们在本教程中使用了 k=5 折,但请记住,最佳折数取决于你的数据集和项目的具体情况。
让我们开始吧。
设置
-
你的标注应采用 YOLO 检测格式。
-
本指南假定标注文件可在本地访问。
-
在我们的演示中,使用了 水果检测 数据集。
- 该数据集总共包含 8479 张图像。
- 它包含 6 个类别标签,每个标签的实例总数如下所示。
| 类别标签 | 实例计数 |
|---|---|
| Apple | 7049 |
| Grapes | 7202 |
| Pineapple | 1613 |
| Orange | 15549 |
| Banana | 3536 |
| Watermelon | 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 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' -
现在,读取数据集 YAML 文件的内容并提取类别标签的索引。
import yaml yaml_file = "path/to/data.yaml" # your data YAML with data directories and names dictionary with open(yaml_file, encoding="utf8") as y: classes = yaml.safe_load(y)["names"] cls_idx = sorted(classes.keys()) -
初始化一个空的
pandasDataFrame。import pandas as pd index = [label.stem for label in labels] # uses base filename as ID (no extension) labels_df = pd.DataFrame([], columns=cls_idx, index=index) -
统计标注文件中存在的每个类别标签的实例数。
from collections import Counter for label in labels: lbl_counter = Counter() with open(label) 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(" ", 1)[0])] += 1 labels_df.loc[label.stem] = lbl_counter labels_df = labels_df.fillna(0.0) # replace `nan` values with `0.0` -
以下是填充后的 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 折数据集拆分
-
Now we will use the
KFoldclass fromsklearn.model_selectionto generateksplits of the dataset.- 重要提示:
- 设置
shuffle=True可确保拆分中类别的随机分布。 - 通过设置
random_state=M(其中M为你选择的整数),你可以获得可重复的结果。
- 设置
import random from sklearn.model_selection import KFold random.seed(0) # for reproducibility 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=index, columns=folds) for i, (train, val) in enumerate(kfolds, start=1): folds_df[f"split_{i}"].loc[labels_df.iloc[train].index] = "train" folds_df[f"split_{i}"].loc[labels_df.iloc[val].index] = "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 文件。
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 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, ) -
最后,将图像和标签复制到每个拆分的相应目录('train' 或 'val')中。
- 注意: 这部分代码所需的时间取决于你的数据集大小和系统硬件。
import shutil from tqdm import tqdm for image, label in tqdm(zip(images, labels), total=len(images), desc="Copying files"): 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 折拆分和标签分布 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.utils.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,但这些步骤大多适用于其他机器学习模型。理解这些步骤能让你在自己的机器学习项目中有效地应用交叉验证。
常见问题 (FAQ)
什么是 K 折交叉验证,为什么它在目标检测中很有用?
K 折交叉验证是一种将数据集划分为 'k' 个子集(折)以更可靠地评估模型性能的技术。每一折既充当训练数据,也充当 验证数据。在目标检测中,使用 K 折交叉验证有助于确保你的 Ultralytics YOLO 模型在不同数据拆分上的性能稳健且具有通用性,从而增强其可靠性。有关使用 Ultralytics YOLO 设置 K 折交叉验证的详细说明,请参考 使用 Ultralytics 进行 K 折交叉验证。
如何使用 Ultralytics YOLO 实现 K 折交叉验证?
要使用 Ultralytics YOLO 实现 K 折交叉验证,你需要遵循以下步骤:
- 验证标注是否采用 YOLO 检测格式。
- 使用像
sklearn、pandas和pyyaml这样的 Python 库。 - 从你的数据集创建特征向量。
- Split your dataset using
KFoldfromsklearn.model_selection. - 在每个拆分上训练 YOLO 模型。
有关综合指南,请参阅我们文档中的 K 折数据集拆分 部分。
为什么我应该使用 Ultralytics YOLO 进行目标检测?
Ultralytics YOLO 提供具有高 准确率 和效率的尖端实时目标检测。它功能多样,支持多种 计算机视觉 任务,例如 检测、实例分割、语义分割 和 分类。此外,它还能与诸如 Ultralytics Platform 等工具无缝集成,进行无代码模型训练和部署。欲了解更多详情,请探索我们 Ultralytics YOLO 页面 上的优势和功能。
如何确保我的标注符合 Ultralytics YOLO 的正确格式?
你的标注应遵循 YOLO 检测格式。每个标注文件必须列出对象类别及其在图像中的 边界框 坐标。YOLO 格式可确保为训练目标检测模型提供简化且标准化的数据处理。有关正确标注格式的更多信息,请访问 YOLO 检测格式指南。
我可以使用 K 折交叉验证处理水果检测以外的自定义数据集吗?
可以,只要标注采用 YOLO 检测格式,你就可以将 K 折交叉验证用于任何自定义数据集。将数据集路径和类别标签替换为你自定义数据集特定的内容。这种灵活性确保任何目标检测项目都能从使用 K 折交叉验证进行的稳健模型评估中受益。有关实际示例,请查阅我们的 生成特征向量 部分。