K-Fold 交叉验证Ultralytics
导言
本综合指南说明了如何在Ultralytics 生态系统中对对象检测数据集实施 K 折交叉验证。我们将利用YOLO 检测格式和关键的Python 库(如 sklearn、pandas 和 PyYaml),指导您完成必要的设置、生成特征向量的过程以及 K-Fold 数据集拆分的执行。
无论您的项目涉及水果检测数据集还是自定义数据源,本教程都将帮助您理解和应用 K 折交叉验证,以增强机器学习模型的可靠性和稳健性。当我们应用 k=5
请记住,最佳折叠次数可能因数据集和项目的具体情况而异。
话不多说,让我们开始吧!
设置
类别标签 | 实例计数 |
---|---|
苹果 | 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
.
- Ultralytics 图书馆:
-
验证您的注释是否采用YOLO 检测格式。
- 在本教程中,所有注释文件都在
Fruit-Detection/labels
目录。
- 在本教程中,所有注释文件都在
生成物体检测数据集的特征向量
-
首先创建一个新的Python 文件,然后导入所需的库。
-
继续检索数据集的所有标签文件。
-
现在,读取数据集 YAML 文件的内容并提取类标签的索引。
-
初始化一个空的
pandas
DataFrame. -
计算注释文件中每个类别标签的实例数。
for label in labels: lbl_counter = Counter() with open(label,'r') as lf: lines = lf.readlines() for l in lines: # classes for YOLO label uses integer at first position of each line lbl_counter[int(l.split(' ')[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 倍数据集拆分
-
现在,我们将使用
KFold
从sklearn.model_selection
以产生k
对数据集进行分割。- 重要:
- 设置
shuffle=True
确保了分班中班级的随机分布。 - 通过设置
random_state=M
其中M
是一个选定的整数,这样就可以得到可重复的结果。
- 设置
- 重要:
-
数据集现已分为
k
折叠,每个折叠都有一个train
和val
指数。我们将构建一个 DataFrame 来更清晰地显示这些结果。 -
现在,我们将计算每个褶皱的类别标签分布,并将其作为褶皱中出现的类别的比率。
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 文件。
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)
-
最后,将图像和标签复制到每个分割的相应目录("train "或 "val")中。
- 注意:这部分代码所需的时间将根据数据集的大小和系统硬件而有所不同。
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
-
首先,加载YOLO 模型。
-
接下来,遍历数据集 YAML 文件以运行训练。结果将保存到由
project
和name
参数。默认情况下,该目录为 "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.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 ,但这些步骤大多可用于其他机器学习模型。了解了这些步骤,你就能在自己的机器学习项目中有效地应用交叉验证。祝你编码愉快