数据集的 train/val/test 划分:解读 Ultralytics `data/split.py` 数据集切分工具

数据集的 train/val/test 划分:解读 Ultralytics data/split.py 数据集切分工具

【免费下载链接】ultralytics Ultralytics YOLO26, YOLO11, YOLOv8 — object detection, instance segmentation, semantic segmentation, image classification, pose estimation, object tracking 【免费下载链接】ultralytics 项目地址: https://gitcode.com/GitHub_Trending/ul/ultralytics

数据准备是训练一切 YOLO 模型的第一步,而把一份原始图片合理划分成 train / val / test 三个子集则是数据准备中最常被低估的环节。本文以 Ultralytics 仓库中的 数据切分模块 为对象,深入讲解它提供的两大数据集切分工具——面向图像分类任务的 split_classify_dataset 与面向检测 / 分割 / 姿态等任务、按路径清单切分的 autosplit,覆盖其设计动机、目录结构约定、函数参数、底层实现与真实数据集(如 xView)中的落地用法。读完本文,你将掌握 Ultralytics 官方数据切分的全部机制,并能结合自己的数据规模正确选择切分方案。

模块全景:一个文件解决两类切分需求

ultralytics/data/split.py 是 Ultralytics 工具链中专门负责"把原始数据划分成训练可用子集"的模块,其公开 API 由两个函数组成(对应 API 参考文档 中自动生成的 ultralytics.data.split.split_classify_dataset 与 ultralytics.data.split.autosplit 两节):

函数适用任务切分产物是否复制文件
split_classify_dataset(source_dir, train_ratio=0.8)图像分类新建 {source}_split/train、{source}_split/val 目录并复制图片是(shutil.copy2)
autosplit(path, weights=(0.9, 0.1, 0.0), annotated_only=False)检测 / 实例分割 / 姿态等(YOLO 标签体系)在 path.parent 生成 autosplit_train.txt / autosplit_val.txt / autosplit_test.txt 路径清单否(仅写文本清单)

之所以需要两套方案,是因为两类任务的数据组织方式不同:图像分类数据集按 class_dir/图片 的目录结构组织,类别由目录名隐含,必须物理拆分目录才能得到独立子集;而 YOLO 检测类数据集通过一个数据集 YAML 指向 train / val 图片路径(可为目录或清单文件),标签则通过"把 images 换成 labels"的路径约定与图片一一对应,因此切分只要**生成图片路径清单(.txt)**即可,无需复制任何文件。

分类数据集切分:split_classify_dataset

split_classify_dataset 专门服务于以"子目录 = 类别"组织的图像分类数据集(如 Caltech101、ImageNet 的简化目录结构),它会在原目录旁新建一个 {source_dir}_split 目录,在其中按比例重建 train / val 两级子目录,并将图片按类别复制进去。

函数签名与参数

从 split.py 源码 可确认完整签名:

def split_classify_dataset(source_dir: str | Path, train_ratio: float = 0.8) -> Path:
参数类型默认值说明
source_dirstr \| Path必填分类数据集根目录路径,其下每个子目录代表一个类别,子目录内为图片文件
train_ratiofloat0.8分配给 train 集的比例,取值区间 (0, 1);val 集比例为 1 - train_ratio
返回值Path—新建的切分目录 {source_dir}_split 的路径

目录结构约定

源码 docstring 中给出了切分前后的标准结构对照(split.py):

切分前:                         切分后:
caltech/                        caltech_split/
├── class1/                     ├── train/
│   ├── img1.jpg                │   ├── class1/
│   ├── img2.jpg                │   │   ├── img1.jpg
│   └── ...                     │   │   └── ...
├── class2/                     │   ├── class2/
│   ├── img1.jpg                │   │   └── ...
│   └── ...                     └── val/
└── ...                             ├── class1/
                                    │   ├── img2.jpg
                                    │   └── ...
                                    ├── class2/
                                    └── ...

底层行为与实现细节

对照源码可以梳理出该函数的完整执行流程(split.py):

  1. 目录创建:以 Path(f"{source_path}_split") 构造输出根目录,并依次创建 train、val 子目录(exist_ok=True 保证可重复执行不会报错)。
  2. 统计与日志:枚举 source_dir 下所有子目录作为类别目录,统计类别数与图片总数,并通过 LOGGER.info 输出形如 Splitting caltech (2 classes, 100 images) into 80% train, 20% val... 的进度信息。
  3. 逐类别切分:对每个类别目录,仅收集后缀匹配图片格式白名单的文件(见下文),调用 random.shuffle 打乱后,按 int(len(image_files) * train_ratio) 计算切分点,前段复制到 train、后段复制到 val。
  4. 复制而非移动:使用 shutil.copy2,会保留文件的修改时间等元数据,且原始数据不受任何影响,可放心重复实验。
  5. 返回结果:日志输出 Split complete in {split_path} ✅ 并返回切分目录路径。

需要注意的一个实现细节:这里使用了 Python 全局 random 模块且没有固定随机种子,因此每次调用划分结果是随机的;若需要可复现的划分,可在调用前自行 random.seed(...)。

只挑选合法图片:IMG_FORMATS 白名单

函数只处理扩展名(小写后)位于 IMG_FORMATS 集合内的文件。该集合定义于 data/utils.py,共 13 种常见图像格式:

avif, bmp, dng, heic, heif, jp2, jpeg, jpg, mpo, png, tif, tiff, webp

因此即使类别目录中混有 README.txt、缩略图缓存等无关文件,也不会被复制进 train / val 子集,天然实现了数据清洗。

实际调用示例

from ultralytics.data.split import split_classify_dataset

# 默认 80/20 划分,返回 Path("path/to/caltech_split")
split_path = split_classify_dataset("path/to/caltech")

# 自定义 75/25 划分
split_classify_dataset("path/to/caltech", 0.75)

一个自动触发场景:缺失 train 目录时的隐式切分

split_classify_dataset 并非只能手动调用——它在 data/utils.py 的 check_cls_dataset 中被自动触发,构成一个很实用的兜底逻辑:

  1. 当校验分类数据集时发现 data_dir / "train" 目录不存在;
  2. 若递归扫描 data_dir 仍能找到合法的图片文件(image_files := [f for f in data_dir.rglob("*.*") if ...]),则日志输出 Dataset 'split=train' not found...Attempting to split...;
  3. 自动以 train_ratio=0.8 调用 split_classify_dataset,并重新指向新生成的 train 子目录。

这意味着:当你手头只有一个"全部类别平铺在一起"的原始分类图片目录、直接把它作为 data 传给分类训练入口时,Ultralytics 会主动帮你完成 80/20 的 train/val 划分,无需手工准备目录。这一自动行为也从侧面印证了上述切分函数在设计上"对原始目录零破坏、只新增 _split 目录"的保守策略是刻意为之的。

检测类数据集的自动切分:autosplit

autosplit 面向 YOLO 家族最常见的标签体系(images/ 与 labels/ 同级、同名 txt 标签),核心思路是:不复制图片,而是在图片目录的父目录下生成 3 个持久化的路径清单文件 autosplit_train.txt / autosplit_val.txt / autosplit_test.txt,之后在数据集 YAML 中用这些清单作为 train / val 的来源。

函数签名与参数

从 split.py 源码 可确认完整签名:

def autosplit(
    path: Path = DATASETS_DIR / "coco8/images",
    weights: tuple[float, float, float] = (0.9, 0.1, 0.0),
    annotated_only: bool = False,
) -> None:
参数类型默认值说明
pathPathDATASETS_DIR / "coco8/images"待切分图片所在目录,递归收集其下所有合法格式图片
weightstuple[float, float, float](0.9, 0.1, 0.0)依次对应 train / val / test 三个子集被抽中的权重,即 (训练, 验证, 测试) 的期望比例
annotated_onlyboolFalse为 True 时仅收录存在对应 txt 标签文件的图片
返回值None—结果以 3 个 autosplit_*.txt 文件落盘

底层实现要点

对照源码可以梳理其执行逻辑(split.py):

  1. 递归收集图片:files = sorted(x for x in path.rglob("*.*") if x.suffix[1:].lower() in IMG_FORMATS),即对 path 做递归展开并同样以 IMG_FORMATS 白名单过滤,保证子目录嵌套也能被覆盖。
  2. 固定随机种子:random.seed(0) 被显式调用,意味着多次运行同一数据集会得到完全一致的划分结果,这对可复现实验至关重要。
  3. 按权重抽样分桶:indices = random.choices([0, 1, 2], weights=weights, k=n),对每一张图片独立地按权重从 0(train)/1(val)/2(test) 中抽取一个归属,即每个子集的图片数只是期望比例而非精确比例。
  4. 清理旧产物:若 path.parent 下已存在同名清单文件,先 unlink 删除再追加写入,避免重复运行产生脏数据。
  5. 逐图片落盘:遍历(带 TQDM 进度条)时,若 annotated_only=True 则通过 img2label_paths 检查标签是否存在,只有通过检查的图片才会被追加写入对应的 txt[i]。
  6. 写入相对路径:清单内每行是相对 path.parent 的 POSIX 风格相对路径并带 ./ 前缀,例如 ./images/img1.jpg(split.py)。写 ./ 前缀是因为 data/base.py 在解析数据集 YAML 的 train/val 条目时,会把 ./ 开头的相对路径再拼接到数据集根目录上,形成"本地 → 全局"的路径转换。

标签路径换算:img2label_paths

autosplit 的 annotated_only 过滤依赖 data/utils.py 的 img2label_paths,它实现 YOLO 标准的路径映射规则:把路径中的 {sep}images{sep} 段替换为 {sep}labels{sep},同时把扩展名替换为 .txt。例如:

  • 输入 .../coco8/images/train/0001.jpg
  • 输出 .../coco8/labels/train/0001.txt

因此在使用 autosplit(..., annotated_only=True) 前,请务必保证标签目录采用 images ↔ labels 同级替换的默认布局;若自定义了标签目录名,可通过 img2label_paths(..., label_dir=...) 换算确认后再决定是否启用该开关。

典型用法示例

from ultralytics.data.split import autosplit

# 默认 90/10/0 划分(无测试集),作用于默认 coco8 图片目录
autosplit()

# 常用:80/15/5,且仅收录带标签的图片
autosplit(path="path/to/images", weights=(0.8, 0.15, 0.05), annotated_only=True)

运行完成后,会在 path 的父目录(例如 datasets/coco8/)中生成三个清单文件,可直接供数据集 YAML 引用:

# coco8 风格的 autosplit 引用写法
path: ../datasets/coco8
train: images/autosplit_train.txt
val: images/autosplit_val.txt

真实落地:xView 数据集的官方下载流程即用 autosplit

autosplit 不是孤立的工具函数——Ultralytics 官方维护的 xView 遥感检测数据集配置 就把它内嵌进 download: 脚本作为数据准备流水线的一环(xView.yaml):

from ultralytics.data.split import autosplit
...
convert_labels(dir / "xView_train.geojson")      # 1. GeoJSON → YOLO 标签
Path(dir / "train_images").rename(dir / "images" / "train")  # 2. 整理图片目录
Path(dir / "val_images").rename(dir / "images" / "val")
autosplit(dir / "images" / "train")              # 3. 对训练图片做 90/10 自动切分

对应地,该数据集的 YAML 中 train / val 直接指向生成的清单(xView.yaml):

train: images/autosplit_train.txt  # 约 90% 的 847 张标注图片
val: images/autosplit_val.txt      # 约 10% 的 847 张标注图片

从该数据集的官方文档说明可以看出这套流程的最终效果:autosplit_train.txt 列出约 90% 的带标注训练图,autosplit_val.txt 列出约 10% 用于验证。值得注意的是 xView 的 847 张训练图片本身就有官方标注,而官方单独的 val 图片无标签,所以这里切分的是"标注过的训练图"这一子集——这正是 autosplit 在真实数据集流水线中的典型定位。

与 fraction 训练参数的区别

很多开发者会把 autosplit 与训练时的 fraction 参数混为一谈,Ultralytics 官方在 simple-utilities 文档 中明确区分了两者:

  • autosplit(本模块):一次性生成持久化的 autosplit_*.txt 清单文件,划分结果固化在磁盘上,多次训练跨进程共享同一划分;
  • fraction 训练参数:在单次训练启动时按比例随机抽取子集用于本次 run,不落盘、每次运行可变化,适合快速试跑与消融实验。

两者可以组合使用:先用 autosplit 得到稳定的 train/val 清单,再配合 fraction 对训练子集做进一步的临时采样。此外,split_classify_dataset 是"物理复制文件"型划分,而 autosplit 是"生成路径清单"型划分,前者适用于目录即类别的分类任务,后者适用于 images/labels 布局的检测类任务——请根据任务类型选择,不要混用。

进阶阅读与延伸

  • 若想进行更严谨的、按类别均衡的 K 折交叉验证而非简单随机切分,仓库提供了完整指南 kfold-cross-validation.md,其中也明确推荐了 autosplit 作为自动切分手段,并与基于 YOLODataset 的 kfold_split 方案做了对比。
  • 更多实用的数据集与标签工具(如 check_cls_dataset 的自动兜底切分、图片压缩、路径换算)集中在 simple-utilities.md。
  • 两个函数的完整 API 文档见 split.md 参考页。

小结

Ultralytics 通过 data/split.py 一个模块优雅地覆盖了两种主流数据组织方式下的切分需求:split_classify_dataset 负责"按类别目录物理拆分、复制出 train/val 两套目录",并在分类数据缺失 train 目录时被 check_cls_dataset 自动触发;autosplit 负责"在 images/labels 布局下按权重随机生成持久化的路径清单",以固定种子保证可复现,其可靠性经过了 xView 等官方数据集下载流程的验证。理解这两个函数的分工与内部约定,是把自己的原始数据快速、正确地送入 Ultralytics 训练管线的前提。

【免费下载链接】ultralytics Ultralytics YOLO26, YOLO11, YOLOv8 — object detection, instance segmentation, semantic segmentation, image classification, pose estimation, object tracking 【免费下载链接】ultralytics 项目地址: https://gitcode.com/GitHub_Trending/ul/ultralytics

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值