Собирай качественные и репрезентативные данные
Бери изображения из реальных условий внедрения, указывай метаданные съёмки в каждом имени файла, отслеживай покрытие по спецификации и выбирай кадры из видео так, чтобы не перегружать датасет.
Большинство проблем с датасетами связано со сбором данных. Команда снимает чистые примеры одной камерой за одну неделю, проверка показывает отличные результаты, а на втором объекте модель работает из рук вон плохо. Сбор данных задаёт верхнюю планку: ни качество разметки, ни аугментация, ни гиперпараметры не научат модель работать в сцене, которой не было в датасете.
Практические занятия
Разнообразие важнее объёма#
Сравним два датасета, предназначенных для одного и того же внедрения:
| Датасет | Результат проверки | В производственной среде |
|---|---|---|
| 10 000 почти одинаковых кадров, одна камера, только полдень | Высокий: кадры val похожи на кадры train | Результаты хуже при съёмке другой камерой и в сумерках |
| 1 500 кадров, распределённых по 12 сценариям из спецификации | Скромнее | Работает стабильно, потому что производственные условия похожи на данные |
Высокая оценка проверки первого датасета объясняется тем, что проверка охватывает только одно представленное в нём условие. В производственной среде качество модели определяется тем сценарием, который представлен хуже всего. В первую очередь собирай данные так, чтобы охватить спецификацию, и лишь затем увеличивай их объём.
Выбирай источники в правильном порядке#
Большая часть датасета должна поступать из реальных производственных условий. В руководстве по сбору и аннотированию данных каждый источник описан подробнее:
| Источник | Когда полезен | Когда вредит |
|---|---|---|
| Текущие данные с производственных камер | Максимально соответствуют условиям внедрения | Собирать медленно; могут потребоваться соглашения на обработку данных |
| Архивные данные с производственных камер | Позволяют быстро получить большое разнообразие | Старые записи могут не соответствовать текущим условиям |
| Общедоступные датасеты (COCO, Open Images, VOC) | Первичное обучение на распространённых классах | Сдвиг домена относительно твоих сцен |
| Постановочная съёмка | Сбор конкретных граничных случаев по запросу | Слишком чистые данные; легко начать чрезмерно на них полагаться |
| Синтетические или сгенерированные изображения | Редкие опасные ситуации и неисправности, которых нельзя ждать | Модель может перенять стиль генератора |
| Сбор изображений из интернета | Разнообразие с низкими затратами | Нестабильное качество и риск нарушения лицензий |
Используй постановочные, общедоступные и синтетические изображения для заполнения конкретных пробелов, указанных в спецификации, и отслеживай их долю в датасете, чтобы оценить их пользу. На платформе Ultralytics функция Generate similar images добавляет в датасет сгенерированные вариации существующего изображения — без разметки, готовые к аннотированию.
Указывай метаданные в имени файла#
Для каждого изображения нужны как минимум ID камеры, день съёмки и номер кадра или клипа. Без них позже ты не сможешь разделить данные по группам, отследить источник регрессии или собрать дополнительные данные для недостаточно представленного сценария. Самый надёжный способ хранения этих данных — имя файла: cam2_2026-05-01_000124.jpg сохраняется при работе в любых инструментах, экспорте и загрузке. Поиск в датасетах платформы Ultralytics выполняется по именам файлов и пользовательским метаданным, поэтому после загрузки эти ключи останутся доступными для поиска.
Этот скрипт создаёт учебный датасет, который используется в остальной части курса. Он копирует размеченные изображения COCO128 и переименовывает их так, будто четыре камеры снимали в течение четырёх дней; при этом данные камеры cam1 представлены с избытком, как это часто бывает с удобной для съёмки камерой:
import shutil
from pathlib import Path
from ultralytics.data.utils import check_det_dataset, img2label_paths
data = check_det_dataset("coco128.yaml") # 128 COCO images stand in for your camera captures
out = Path("datasets/practice")
(out / "images").mkdir(parents=True, exist_ok=True)
(out / "labels").mkdir(parents=True, exist_ok=True)
for i, image in enumerate(sorted(Path(data["train"]).glob("*.jpg"))):
camera = ("cam1", "cam1", "cam2", "cam3", "cam4")[i % 5] # cam1 over-collected, like a real site
name = f"{camera}_2026-05-0{i // 32 + 1}_{i:06d}" # camera, capture day, frame number
shutil.copy(image, out / "images" / f"{name}.jpg")
label = Path(img2label_paths([str(image)])[0])
if label.exists():
shutil.copy(label, out / "labels" / f"{name}.txt")
print(len(list((out / "images").glob("*.jpg"))), "images written to", out)128 images written to datasets/practice
При загрузке на платформу Ultralytics имена файлов сохраняются. В диалоге загрузки также можно добавить видео и целые датасеты, а среди настроек есть размытие лиц для защиты конфиденциальности.
Отслеживай покрытие по спецификации#
Если метаданные указаны в именах файлов, покрытие можно посчитать. Задай квоты по спецификации, и скрипт подскажет, что собирать дальше:
from collections import Counter
from pathlib import Path
QUOTA = {"cam1": 30, "cam2": 30, "cam3": 30, "cam4": 30} # images per camera, from your spec
images = sorted(Path("datasets/practice/images").glob("*.jpg"))
cameras = Counter(p.stem.split("_")[0] for p in images)
days = Counter(p.stem.split("_")[1] for p in images)
for camera, quota in QUOTA.items():
gap = quota - cameras[camera]
print(f"{camera}: {cameras[camera]:3d} / {quota} {'ok' if gap <= 0 else f'collect {gap} more'}")
print("days:", dict(sorted(days.items())))cam1: 52 / 30 ok
cam2: 26 / 30 collect 4 more
cam3: 25 / 30 collect 5 more
cam4: 25 / 30 collect 5 more
days: {'2026-05-01': 32, '2026-05-02': 32, '2026-05-03': 32, '2026-05-04': 32}Правило принятия решения: прекрати сбор из любого источника, где квота уже превышена, и сосредоточь усилия на пробелах. Здесь на cam1 уже приходится 40% изображений. Ещё одна неделя съёмки этой камерой увеличит объём, но не улучшит покрытие.
Отбирай кадры из видео, не перегружая датасет#
Видео — самый дешёвый источник данных и источник, который проще всего использовать неправильно. При 30 кадрах в секунду за минуту получается 1 800 почти одинаковых кадров, а повторная разметка одного и того же момента стоит денег и завышает метрики. Оставляй кадр, только если сцена изменилась по сравнению с последним сохранённым кадром:
from pathlib import Path
import cv2
from ultralytics.utils.downloads import safe_download
video = "solutions_ci_demo.mp4" # a short public demo clip; swap in your own footage
safe_download(f"https://github.com/ultralytics/assets/releases/download/v0.0.0/{video}")
Path("frames").mkdir(exist_ok=True)
cap = cv2.VideoCapture(video)
fps = cap.get(cv2.CAP_PROP_FPS)
read = kept = 0
last = None
while True:
ok, frame = cap.read()
if not ok:
break
thumb = cv2.cvtColor(cv2.resize(frame, (64, 64)), cv2.COLOR_BGR2GRAY).astype(float)
if last is None or abs(thumb - last).mean() > 10: # keep a frame only when the scene has changed
cv2.imwrite(f"frames/clip01_{read:06d}.jpg", frame) # the clip ID stays in the filename
kept, last = kept + 1, thumb
read += 1
print(f"{read} frames at {fps:.0f} FPS -> {kept} kept")62 frames at 30 FPS -> 6 keptВ двухсекундном клипе 62 кадра, но лишь 6 визуально различающихся моментов. Скрипт сравнивает крошечные изображения в оттенках серого, поэтому быстро обрабатывает даже многочасовые записи. Увеличь порог 10, чтобы сохранять меньше кадров со статичных камер; уменьши его для быстро меняющихся сцен. Распределяй бюджет и между клипами: 20 кадров из каждого из 50 клипов лучше, чем 1 000 кадров из одного.
Два кадра с интервалом в секунду имеют одинаковое освещение и фон и обычно содержат те же объекты. Если они попадут и в обучающую, и в валидационную выборку, модель будет оцениваться на изображениях, которые фактически уже видела. Указывай ID видео в каждом имени файла, чтобы разделить набор данных правильно и не разрывать видео.
Избегай ошибок при сборе данных#
| Ошибка | Симптом | Решение |
|---|---|---|
| Сбор данных в течение одной недели | Высокий mAP на валидации, но ошибки в другие сезоны или при смене условий | Распредели сбор данных на несколько недель или месяцев |
| Одна камера | Модель не работает со второй камерой | Собирай данные со всех типов камер, которые используешь |
| Только постановочные данные | Модель не справляется с реальным беспорядком | Оставь постановочные изображения небольшой отслеживаемой долей данных |
| Дублирующиеся кадры | Завышенные метрики и напрасная разметка | Отбирай данные по смене сцены; Ultralytics Platform также пропускает точные копии при загрузке |
| Один оператор или смена | Незапланированное разнообразие внешнего вида и дисбаланс классов | Собирай данные в разные смены, на разных объектах и с разными людьми |
| Недостаточно редких случаев | Модель пропускает самые важные события, связанные с безопасностью | Целевой сбор данных, постановка сцен или дополнение синтетическими данными |
Смещение и согласие#
Смещение — это проблема сбора данных, а не моделирования: никакой гиперпараметр не исправит отсутствие в данных целой группы. В руководстве по сбору данных перечислены четыре способа решения: разнообразные источники, сбалансированное представление, регулярная повторная проверка охвата и меры по снижению смещения, например увеличение выборки недостаточно представленных групп.
Если на изображениях есть лица, номерные знаки или другие персональные данные, до начала сбора данных нужно получить согласие и определить политику хранения. С глоссария по теме конфиденциальности данных удобно начать, а в разделе «Конфиденциальность, регионы и соответствие требованиям» описано, как хранить данные в нужной юрисдикции с помощью dataRegion.
Показать решение
Для учебного набора отчет об охвате дает четкий план:
| Источник | Количество / квота | Действие |
|---|---|---|
cam1 | 52 / 30 | Прекрати сбор: здесь уже 40% всех изображений |
cam2 | 26 / 30 | Собери еще 4 |
cam3 | 25 / 30 | Собери еще 5 |
cam4 | 25 / 30 | Собери еще 5 |
Каждая камера охватывает все четыре дня, поэтому пробелы относятся к камерам, а не к дням. Распредели 14 новых изображений как минимум по двум новым дням, чтобы добавить новые условия, а не еще больше материалов за тот же день. Если для камеры невозможно восполнить пробел (например, она снимает только ночью), добавь в спецификацию строку для недостающего условия и зафиксируй, будут ли его покрывать постановочные или сгенерированные изображения, чтобы позже можно было оценить их влияние.