Ultralytics YOLO27:

K-блочная кросс-валидация с Ultralytics#

Введение#

Это подробное руководство демонстрирует реализацию K-блочной кросс-валидации для наборов данных обнаружения объектов в экосистеме Ultralytics. Мы будем использовать формат обнаружения YOLO и такие ключевые библиотеки Python, как sklearn, pandas и PyYAML, чтобы провести тебя через необходимую настройку, процесс создания векторов признаков и выполнение разбиения набора данных на блоки K-блочной валидации.

K-fold cross validation data splitting

Если твой проект использует набор данных Fruit Detection или собственный источник данных, это руководство поможет тебе понять и применять K-блочную кросс-валидацию для повышения надежности и устойчивости твоих моделей машинного обучения. В этом руководстве мы используем k=5 блоков, но имей в виду, что оптимальное количество блоков может зависеть от твоего набора данных и особенностей проекта. K-блочная кросс-валидация наиболее полезна, когда набор данных небольшой, зашумленный или сильно варьируется; для больших и разнообразных наборов данных обычно достаточно правильно организованного разбиения train/val/test.

Начнем.

Настройка#

  • Твои аннотации должны быть в формате обнаружения YOLO.

  • Предполагается, что файлы аннотаций доступны локально.

  • Для демонстрации мы используем набор данных Fruit Detection.

    • Всего этот набор данных содержит 8479 изображений.
    • Он содержит 6 меток классов, а общее количество экземпляров каждого класса указано ниже.
Метка классаКоличество экземпляров
Яблоко7049
Виноград7202
Ананас1613
Апельсин15549
Банан3536
Арбуз1976
  • Необходимые пакеты Python включают:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • В этом руководстве используются k=5 блоков. Однако тебе следует определить оптимальное количество блоков для конкретного набора данных.

  1. Создай новую виртуальную среду Python (venv) для проекта и активируй ее. Используй pip (или предпочитаемый менеджер пакетов), чтобы установить:

    • Библиотека Ultralytics: pip install -U ultralytics. В качестве альтернативы можно клонировать официальный репозиторий.
    • Scikit-learn, pandas и PyYAML: pip install -U scikit-learn pandas pyyaml.
  2. Убедись, что аннотации находятся в формате обнаружения YOLO.

    • В этом руководстве все файлы аннотаций находятся в каталоге Fruit-Detection/labels.

Создание векторов признаков для набора данных обнаружения объектов#

  1. Сначала создай новый файл Python example.py для выполнения описанных ниже шагов.

  2. Загрузи настроенные изображения для обучения и валидации через владелец набора данных пакета, оставив тестовую выборку нетронутой.

    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]
  3. Теперь прочитай содержимое YAML-файла набора данных и извлеки индексы меток классов.

    classes = data["names"]
    cls_idx = sorted(classes.keys())
  4. Инициализируй пустой DataFrame pandas.

    import pandas as pd
    
    labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images)
  5. Подсчитай экземпляры каждого класса, представленного в файлах аннотаций.

    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())
  6. Ниже показан пример заполненного 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-блочную кросс-валидацию к набору данных обнаружения объектов.

Разбиение набора данных на блоки#

  1. Теперь мы используем класс 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)  # setting random_state for repeatable results
    
    kfolds = list(kf.split(labels_df))
  2. Теперь набор данных разделен на 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"
  3. Теперь мы вычислим распределение меток классов для каждого блока как соотношение классов, представленных в 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

    В идеальном случае соотношения всех классов должны быть достаточно близкими для каждого разбиения и между классами. Однако это зависит от особенностей твоего набора данных.

  4. Запиши списки изображений и 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")

Обучение YOLO на разбиениях данных K-блочной валидации#

  1. Сначала загрузи модель YOLO.

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"  # use yolo26n.pt for a small model
    model = YOLO(weights_path, task="detect")
  2. Затем перебери 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
  3. Также можно использовать функцию 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 на разных разбиениях данных, повышая ее надежность. Подробные инструкции по настройке K-блочной кросс-валидации с Ultralytics YOLO см. в разделе K-блочная кросс-валидация с Ultralytics.

  • Чтобы реализовать K-блочную кросс-валидацию с Ultralytics YOLO, выполни следующие шаги:

    1. Убедись, что аннотации находятся в формате обнаружения YOLO.
    2. Используй библиотеки Python, такие как sklearn, pandas и pyyaml.
    3. Создай векторы признаков из набора данных.
    4. Раздели набор данных с помощью KFold из sklearn.model_selection.
    5. Обучи модель YOLO на каждом разбиении.

    Подробное руководство см. в разделе Разбиение набора данных на блоки нашей документации.

  • Рабочий процесс в этом руководстве ориентирован на формат обнаружения YOLO, но тот же подход адаптируется к любой задаче YOLO — задача меняет способ формирования блоков, но не влияет на полезность кросс-валидации:

    ЗадачаПроектирование блоков
    detectРазделяй данные на уровне изображений, балансируя распределение объектов и классов между блоками. Связанные изображения (с одного пациента, из одной видеопоследовательности, с одной камеры или одной площадки) помещай в один блок.
    segmentИспользуй ту же стратегию на уровне изображений, что и для обнаружения, дополнительно сохраняя покрытие масок и классов в каждом блоке.
    classifyПредпочитай стратифицированные блоки, чтобы частоты классов оставались сбалансированными между обучением и валидацией.
    poseРазделяй данные по субъектам или последовательностям, чтобы один и тот же человек или животное никогда не попадали по разные стороны одного разбиения.
    obbРазделяй данные на уровне изображений, сохраняя тайлы или кропы одной сцены вместе — это особенно важно для аэрофотоснимков.

    Независимо от задачи, не помещай почти дублирующиеся и связанные образцы в противоположные блоки: такая утечка существенно завышает валидационные метрики по сравнению с результатами, которых модель достигнет в production.

  • Ultralytics YOLO обеспечивает современное обнаружение объектов в реальном времени с высокой точностью и эффективностью. Это универсальное решение, поддерживающее множество задач компьютерного зрения, включая обнаружение, сегментацию экземпляров, семантическую сегментацию и классификацию. Кроме того, оно легко интегрируется с такими инструментами, как Ultralytics Platform, для обучения и развертывания моделей без написания кода. Подробнее о преимуществах и возможностях см. на нашей странице Ultralytics YOLO.

  • Твои аннотации должны соответствовать формату обнаружения YOLO. Каждый файл аннотаций должен содержать класс объекта и координаты его ограничивающей рамки на изображении. Формат YOLO обеспечивает упорядоченную и стандартизированную обработку данных для обучения моделей обнаружения объектов. Дополнительную информацию о правильном форматировании аннотаций см. в руководстве по формату обнаружения YOLO.

  • Да, K-блочную кросс-валидацию можно использовать с любым пользовательским набором данных, если аннотации находятся в формате обнаружения YOLO. Замени пути к набору данных и метки классов на соответствующие твоему пользовательскому набору данных. Такая гибкость позволяет любому проекту по обнаружению объектов получить преимущества надежной оценки модели с помощью K-блочной кросс-валидации. Практический пример см. в разделе Создание векторов признаков.

Комментарии