Ultralytics YOLO27: Встать в лист ожидания.
Get Started
Создание решений на Ultralytics Platform·Создай датасет·Урок 4/10
Уроксредний

Очистка, дедупликация и разбиение

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

Оценка на валидации настолько достоверна, насколько корректно сформирован сплит. В популярном демонстрационном наборе данных COCO128 одни и те же 128 изображений намеренно используются для обучения и валидации, а случайное повторение этой схемы в твоих данных даст «отличную» модель, которая провалится при работе со следующей камерой. В этом уроке выполняются две проверки на реальных данных: поиск дубликатов и проверка классов в каждом сплите; затем данные разбиваются по камерам, а версия, прошедшая обе проверки, фиксируется.

Результат

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

Ускоренное прохождение
Если ты уже освоился, вот краткая версия.
  1. Вычисли хеши изображений в каждом сплите: хеш, встречающийся в двух сплитах, означает утечку.

  2. Проверь экспорт NDJSON: для каждого класса нужны обучающие примеры и достаточное количество экземпляров в val для измерения.

  3. Запусти Clustering с раскраской по сплитам, чтобы найти близкие дубликаты, которые не обнаруживаются по хешам.

  4. Назначай сплиты по камерам, площадкам или сессиям; никогда не распределяй видеокадры случайным образом.

  5. Создай пронумерованную версию с описанием и обучайся только на версиях, прошедших проверку.

Практические занятия

Пример: найди изображения, общие для разных сплитов#

Платформа хранит по одной копии изображения в каждом сплите и при загрузке предлагает обработать конфликты, но это не мешает одному и тому же изображению или сцене попасть и в train, и в val. Проверь данные перед загрузкой и ещё раз после объединения наборов данных:

import hashlib
from pathlib import Path

from ultralytics.data.utils import check_det_dataset

data = check_det_dataset("coco128.yaml")  # any local YOLO dataset YAML before you upload it

def hashes(split_dir):
    return {hashlib.md5(p.read_bytes()).hexdigest(): p.name for p in Path(split_dir).rglob("*.jpg")}

train, val = hashes(data["train"]), hashes(data["val"])
shared = train.keys() & val.keys()
print(f"train {len(train)} images, val {len(val)} images, identical in both: {len(shared)}")
print("examples:", sorted(val[h] for h in shared)[:3])
train 128 images, val 128 images, identical in both: 128
examples: ['000000000009.jpg', '000000000025.jpg', '000000000030.jpg']

Каждое изображение для валидации из COCO128 также входит в обучающую выборку. Для набора данных, предназначенного для быстрой проверки, это сделано намеренно, и именно поэтому его mAP на валидации ничего не говорит об обобщающей способности модели. В твоих данных любое ненулевое количество таких изображений — утечка: перед обучением перемести или удали по одной копии каждого изображения.

Найди близкие дубликаты с помощью Clustering#

Хеши обнаруживают только побайтно идентичные файлы. Соседние видеокадры, снимки из серии и перекодированные копии отличаются на несколько пикселей. Открой значок диаграммы рассеяния на панели инструментов галереи и нажми Analyze Dataset (от 20 до 200 000 изображений, только загруженные наборы данных):

Галерея медицинских таблеток в Ultralytics Platform с открытой панелью Clustering: каждое изображение показано точкой на двумерной диаграмме рассеяния, где визуально похожие изображения расположены близко друг к другу

  • Раскраска по сплитам: компактная группа точек, в которой перемешаны train и val, указывает на утечку из-за близких дубликатов.
  • Раскраска по кластерам или классам: огромная группа одного класса показывает, чему набор данных уделяет слишком много внимания; отдельные точки — это выбросы (неправильная камера, повреждённый кадр, неверно размеченное изображение).
  • Выдели область лассо или щёлкни по ней, чтобы отфильтровать галерею и оставить в ней эти изображения (до 1 000), а затем удали, перемести или переименуй их.

Те же эмбеддинги используются для функции Поиск похожих изображений в контекстном меню изображения: она добавляет похожие изображения из общедоступных и командных наборов данных в train как неразмеченные. Это самый быстрый способ заполнить пробел, обнаруженный проверкой ниже.

Пример: проверь классы в каждом сплите#

Скачай экспорт NDJSON из заголовка набора данных или на вкладке Versions. Каждая строка после заголовка содержит одно изображение, его сплит и метки, поэтому несколько строк Python позволяют подсчитать каждый класс в каждом сплите. Здесь скрипт выполняется на общедоступном экспорте COCO8:

import json
from collections import Counter, defaultdict

counts = defaultdict(Counter)  # split -> class name -> instances
images = Counter()
with open("coco8.ndjson") as f:  # the NDJSON export from the dataset header or Versions tab
    header = json.loads(f.readline())
    names = header["class_names"]
    for line in f:
        record = json.loads(line)
        images[record["split"]] += 1
        for box in (record.get("annotations") or {}).get("boxes", []):
            counts[record["split"]][names[str(box[0])]] += 1

print("images per split:", dict(images))
for name in sorted(set(counts["train"]) | set(counts["val"])):
    train, val = counts["train"][name], counts["val"][name]
    flag = "missing in train" if not train else "too few in val" if val < 10 else ""
    print(f"{name:<14} train {train:>3}  val {val:>3}  {flag}")
images per split: {'train': 4, 'val': 4}
bowl           train   3  val   0  too few in val
broccoli       train   1  val   0  too few in val
dog            train   0  val   1  missing in train
elephant       train   0  val   2  missing in train
giraffe        train   2  val   0  too few in val
horse          train   0  val   2  missing in train
orange         train   4  val   0  too few in val
person         train   0  val  10  missing in train
potted plant   train   1  val   1  too few in val
umbrella       train   0  val   1  missing in train
vase           train   1  val   0  too few in val
zebra          train   1  val   0  too few in val

При чтении учитывай: строка заголовка содержит class_names; каждая рамка обнаружения — это [class, x, y, w, h]. Нет в train означает, что модель вообще не сможет обучиться распознавать этот класс, а его AP на val будет равен нулю. Слишком мало в val означает, что AP класса будет сильно меняться от запуска к запуску: если в val 7 паллет, пропуск одной паллеты сдвигает AP для паллет примерно на 14 пунктов. COCO8 — это проверочный набор из 8 изображений, поэтому проверку не проходит каждый класс; в твоей рабочей версии не должно быть ни одного предупреждения. Устраняй пробелы, собирая дополнительные примеры, а не подбирая параметры.

Вкладка Classes в Ultralytics Platform для набора данных medical-pills: гистограмма распределения классов и таблица классов с количеством аннотаций и изображений

На вкладке Classes отображаются те же подсчёты для текущего набора данных; для несбалансированных данных можно включить логарифмическую шкалу. Функция Объединить в один сводит дубликаты, например car, automobile и vehicle, в один класс без потери аннотаций; Удалить удаляет класс и все его аннотации.

Разбивай данные по единице, для которой важно обобщение модели#

Кадры с одной камеры, из одной сессии или за один день коррелируют друг с другом. Если они попадают и в train, и в val, модель оценивается на сценах, которые уже видела. Откладывай целиком те единицы, которые меняются при эксплуатации: если меняются камеры — отложи камеры; если те же камеры снимают в другие дни — отложи дни.

Инструменты разбиения на платформе различаются именно в этом:

ИнструментКак выполняется назначениеИспользуется для
Папки train/, val/, test/ в загружаемом набореСохраняет назначенные тобой сплитыГрупповое разбиение, подготовленное офлайн
Фильтр сплита, установленный перед загрузкойВсе файлы из этой загрузки попадают в выбранный сплитЗагрузка данных с одной камеры в каждый сплит
Переместить в сплит в режиме TableПеремещает выбранные изображенияИсправление отдельных изображений
Панель сплитов, Redistribute SplitsСлучайным образом перераспределяет все изображенияТолько для независимых фотографий

Диалог Redistribute Splits в Ultralytics Platform для набора данных medical-pills: панель сплитов и поля с процентами — 80 для train, 20 для validation и 0 для test

В уроке Как правильно разбить набор данных рассказывается, как выбрать единицу разбиения; для очень маленьких наборов данных больше информации поможет извлечь кросс-валидация k-fold. Чтобы проверить группы после загрузки, добавь ключ группы в проверку классов (например, record["file"].split("_")[0] для файлов с именами cam07_000123.jpg или поле camera в метаданных изображения) и выведи все группы, встречающиеся в двух сплитах.

Зафиксируй версию, прошедшую проверку#

Для обучения нужен фиксированный снимок данных. На вкладке Versions введи описание («v5: cam07 отложена, +80 изображений паллет») и нажми New Version. Каждая версия — это неизменяемый пронумерованный снимок NDJSON, который можно скачать, сравнить или восстановить; при запуске то же самое делает диалог обучения с помощью Save Dataset Version и повторно использует совпадающую версию, если ничего не изменилось. Версии недоступны для подключённых облачных наборов данных и наборов данных On Premise.

Подводные камни#

Перераспределение разделений для видеокадров

Симптом: после нажатия Авто для разделения 80/20 mAP на валидации резко растёт, а точность в продакшене — нет. Решение: функция «Перераспределить разделения» случайным образом назначает каждое изображение (и по пути удаляет точные дубликаты), из-за чего соседние кадры попадают в обучающую и валидационную выборки. Для сгруппированных данных используй папки для загрузки или Переместить в раздел.

Объединение или удаление класса меняет ID всех классов

Симптом: в старом экспорте или предсказаниях модели внезапно появляются неправильные названия классов. Решение: ID классов задаются их позициями, поэтому при объединении или удалении класса индекс каждого класса выше него уменьшается на единицу. Создавай новую версию перед любым изменением классов и переобучай модель вместо повторного использования старых файлов разметки.

Изменение разделения в середине проекта

Симптом: результат нового запуска на 5 пунктов ниже, и больше ничего не изменилось. Решение: метрики можно сравнивать только на одном и том же разбиении. Изменив разбиение, укажи это в описании версии и повторно запусти базовый эксперимент на новой версии, прежде чем что-либо сравнивать.

Попробовать

Запусти cross_split_dupes.py на локальном наборе данных и class_audit.py на экспорте NDJSON из Platform, указав ключ группы, соответствующий именам камер или сессий. Устрани все обнаруженные утечки и пробелы (Переместить в сплит, Поиск похожих изображений, дополнительные данные), повторно запускай обе проверки, пока они не пройдут без ошибок, проверь Clustering с раскраской по сплитам и создай версию с описанием.

Критерии готовности
Урок считается завершённым, если выполнены все условия ниже.
  • Проверка на дубликаты не обнаруживает identical in both: 0, а камеры и сессии не распределены между двумя сплитами.

  • Проверка классов не выдаёт предупреждений missing in train, и ты решил, что делать с каждым случаем too few in val.

  • Создана пронумерованная версия, описание которой содержит правило разбиения и список изменений.

Показать решение

Пример решения для набора данных с погрузчиками и 8 камерами. Проверка дубликатов находит 41 идентичное изображение в train и val — все из повторно загруженной папки; удали копии из val. Расширенная с помощью group = record['file'].split('_')[0] проверка показывает, что cam02 есть и в train, и в val, поэтому выбери её изображения из val в режиме Table и нажми Переместить в сплит > Train. Проверка также сообщает о pallet: train 63, val 7, too few in val: воспользуйся функцией Поиск похожих изображений для двух изображений паллет, чтобы добавить 40 похожих неразмеченных изображений, разметь их и перемести 15 изображений паллет с cam07 в val, чтобы cam07 целиком оставалась в одном сплите. Повтори проверку: 0 дубликатов, 0 утечек между группами, 22 изображения паллет в val. При раскраске Clustering по сплитам смешанных групп не обнаружено. Сохрани v5: cam07 val, cam08 test, +40 pallets.

Что дальше

Данные очищены и сохранены в версии. Теперь ты обучишь на них модель, изучишь результаты запуска и правильно выполнишь дообучение.

Войди, чтобы сохранять прогресс