Кросс-валидация K-Fold с Ultralytics#
Введение#
Это подробное руководство демонстрирует реализацию кросс-валидации K-Fold для наборов данных обнаружения объектов в экосистеме Ultralytics. Мы воспользуемся форматом детекции YOLO и ключевыми библиотеками Python, такими как sklearn, pandas и PyYAML, чтобы провести тебя через необходимую настройку, создание векторов признаков и разделение набора данных на фолды K-Fold.
Работаешь ли ты с набором данных Fruit Detection или собственным источником данных, это руководство поможет разобраться в кросс-валидации K-Fold и применять её для повышения надежности и устойчивости моделей машинного обучения. В этом руководстве мы используем k=5 фолдов, но имей в виду, что оптимальное количество фолдов зависит от набора данных и особенностей проекта. Кросс-валидация K-Fold особенно полезна, если набор данных небольшой, зашумленный или сильно варьируется; для больших и разнообразных наборов данных обычно достаточно хорошо составленного разделения на train/val/test.
Приступим.
Настройка#
-
Аннотации должны быть в формате детекции 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.
- В этом руководстве все файлы аннотаций находятся в каталоге
Создание векторов признаков для набора данных обнаружения объектов#
-
Для выполнения следующих шагов создай новый файл Python
example.py. -
Загрузи настроенные обучающие и валидационные изображения через владелец набора данных пакета, оставив тестовую выборку нетронутой.
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()) -
Инициализируй пустой DataFrame
pandas.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-Fold.
Разделение набора данных на фолды K-Fold#
-
Теперь воспользуемся классом
KFoldизsklearn.model_selection, чтобы создать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)) - Важно:
-
Теперь набор данных разделен на
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-Fold и DataFrame с распределением меток в виде CSV-файлов для дальнейшего использования.
folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")Обучение YOLO с разделением данных K-Fold#
-
Сначала загрузи модель YOLO.
from ultralytics import YOLO weights_path = "path/to/weights.pt" # используй yolo26n.pt для небольшой модели 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 # Автоматически разделить набор данных на train/val/test autosplit(path="path/to/images", weights=(0.8, 0.2, 0.0), annotated_only=True)
Заключение#
В этом руководстве мы рассмотрели применение кросс-валидации K-Fold для обучения модели YOLO обнаружения объектов. Мы научились разделять совокупность обучающих и валидационных данных на K частей и использовать полученную таблицу соотношений для проверки баланса классов после случайного разделения.
Мы также рассмотрели, как создавать отчетные DataFrame для визуализации разделения данных и распределения меток по фолдам. Это позволяет наглядно оценить структуру обучающих и валидационных выборок.
При желании мы сохранили записи для дальнейшего использования — это может быть особенно полезно в крупных проектах или при поиске причин проблем с производительностью модели.
Наконец, мы реализовали обучение модели в цикле на каждой части и сохранили результаты обучения для дальнейшего анализа и сравнения.
Кросс-валидация K-Fold — надежный способ эффективнее использовать доступные данные; она помогает убедиться, что производительность модели остается стабильной на разных подмножествах данных. В результате получается более обобщающая и надежная модель, которая с меньшей вероятностью будет переобучаться на конкретных закономерностях данных.
Имей в виду, что хотя в этом руководстве мы использовали YOLO, эти шаги в основном применимы и к другим моделям машинного обучения. Понимание описанных шагов позволит эффективно применять кросс-валидацию в собственных проектах машинного обучения.
Часто задаваемые вопросы#
Кросс-валидация K-Fold — это метод, при котором набор данных делится на «k» подмножеств (фолдов) для более надежной оценки производительности модели. Каждый фолд поочередно используется и для обучения, и как валидационные данные. В обнаружении объектов кросс-валидация K-Fold помогает убедиться, что модель Ultralytics YOLO надежно работает и хорошо обобщает данные при разных вариантах их разделения. Это повышает надежность модели. Подробные инструкции по настройке кросс-валидации K-Fold с Ultralytics YOLO см. в разделе Кросс-валидация K-Fold с Ultralytics.
Чтобы реализовать кросс-валидацию K-Fold с Ultralytics YOLO, выполни следующие шаги:
- Убедись, что аннотации соответствуют формату детекции YOLO.
- Используй библиотеки Python, такие как
sklearn,pandasиpyyaml. - Создай векторы признаков на основе набора данных.
- Раздели набор данных с помощью
KFoldизsklearn.model_selection. - Обучи модель YOLO на каждой части.
Подробное руководство см. в разделе документации Разделение набора данных на фолды K-Fold.
В этом руководстве рассматривается формат детекции YOLO, но тот же подход подходит для всех задач YOLO: задача влияет на то, как формировать фолды, но не на пользу кросс-валидации:
Задача Формирование фолдов detectРазделяй данные на уровне изображений, балансируя распределение объектов и классов между фолдами. Связанные изображения (один пациент, видеопоследовательность, камера или объект съемки) оставляй в одном фолде. segmentИспользуй ту же стратегию разделения на уровне изображений, что и для детекции, дополнительно обеспечив наличие масок и всех классов в каждом фолде. classifyПо возможности используй стратифицированные фолды, чтобы частоты классов оставались сбалансированными в обучающей и валидационной выборках. poseРазделяй данные по субъектам или последовательностям, чтобы один и тот же человек или животное ни в коем случае не попадали в обе части фолда. obbРазделяй данные на уровне изображений, объединяя тайлы или фрагменты одной сцены в одном фолде. Это особенно важно для аэрофотоснимков. Независимо от задачи не допускай попадания почти идентичных и связанных примеров в противоположные фолды: такая утечка существенно завышает метрики валидации по сравнению с результатами, которых модель достигнет в рабочей среде.
Ultralytics YOLO обеспечивает современное обнаружение объектов в реальном времени с высокой точностью и эффективностью. Эта универсальная модель поддерживает множество задач компьютерного зрения, включая обнаружение объектов, сегментацию экземпляров, семантическую сегментацию и классификацию. Кроме того, она легко интегрируется с такими инструментами, как Ultralytics Platform, для обучения и развертывания моделей без кода. Подробнее о преимуществах и возможностях читай на странице Ultralytics YOLO.
Аннотации должны соответствовать формату детекции YOLO. В каждом файле аннотаций нужно указать класс объекта и координаты его ограничивающей рамки на изображении. Формат YOLO упрощает и стандартизирует обработку данных для обучения моделей обнаружения объектов. Подробнее о правильном формате аннотаций см. в руководстве по формату детекции YOLO.
Да, кросс-валидацию K-Fold можно использовать с любым пользовательским набором данных, если аннотации соответствуют формату детекции YOLO. Замени пути к данным и метки классов на соответствующие своему набору данных. Благодаря такой гибкости оценка модели с помощью кросс-валидации K-Fold может повысить надежность любого проекта по обнаружению объектов. Практический пример см. в разделе Создание векторов признаков.