데이터셋 사양 정의하기
수집 전에 시나리오, 할당량, 변동 요인, 엣지 케이스, 배경 이미지를 계획하고 Ultralytics의 목표 규모에 맞춰 계획을 수립하세요.
사양 없이 진행하는 팀은 조명이 밝은 낮에 한 대의 카메라로 찍은 사진 5,000장만 확보한 뒤, 첫 운영일 해 질 무렵에 실패하는 모델을 만들게 됩니다. 데이터셋 사양은 이미지 쇼핑 목록입니다. 모델이 처리해야 할 모든 시나리오와 각 시나리오에 필요한 이미지 수, 과대표집할 희귀 사례를 정합니다. 작성에는 한 시간이 걸리지만, 수집 작업을 어디에 집중할지 결정합니다.
실습형
사양의 역할#
사양은 목표와 카메라를 연결하는 다리입니다. 데이터 수집을 이끌며, 첫 모델이 목표를 달성하지 못했을 때 다시 확인할 문서입니다.
학습 설정이 아니라 사양으로 돌아가는 순환 구조입니다. 목표를 달성하지 못하는 이유는 대개 사양에 포함되지 않은 시나리오 때문입니다. CV 프로젝트 단계 가이드에서도 동일한 라이프사이클을 설명합니다.
시나리오를 행으로 작성하세요#
시나리오는 모델이 처리해야 하는 캡처 조건의 조합 하나를 뜻합니다. 할당량이 포함된 행을 사용하면 누군가 카메라를 들기 전에 데이터 공백을 파악할 수 있습니다.
| 시나리오 | 카메라 | 위치 | 시간대 | 조건 | 할당량 | 참고 사항 |
|---|---|---|---|---|---|---|
| 주간 도크 | cam-1~cam-4 | A~C 구역 | 08:00–17:00 | 상관없음 | 1,200 | 가장 혼잡한 시간대 |
| 야간 도크 | cam-1~cam-4 | A~C 구역 | 22:00–04:00 | 상관없음 | 400 | 나트륨등 조명, 적은 인원 |
| 비 오는 날 하역 | cam-2, cam-3 | B 구역 | 상관없음 | 비가 오는 경우에만 | 200 | 드물지만 중요함 |
| 적재된 | 상관없음 | 상관없음 | 상관없음 | 팔레트 높이 2개 초과 | 150 | 뒤에 있는 사람을 가림 |
| 배경 | 상관없음 | 상관없음 | 상관없음 | 빈 도크 | 150 | 객체 없음, 라벨 없음 |
문서로 작성할 수 없는 시나리오는 모델이 학습 중에 보지 못하며, 운영 환경에서 실패하는 원인이 됩니다.
목표 규모에 맞춰 사양을 설정하세요#
Ultralytics의 최고의 학습 결과를 위한 팁에서 운영 환경에 적용할 수 있는 일반적인 기준을 제시합니다.
| 목표 | 권장 사항 |
|---|---|
| 클래스당 이미지 수 | ≥ 1,500 |
| 클래스당 라벨링된 인스턴스 수 | ≥ 10,000 |
| 배경 이미지(라벨 없음) | 데이터셋의 0~10%; COCO는 약 1%입니다 |
| 다양성 | 다양한 시간대, 계절, 날씨, 조명, 각도, 소스 및 카메라 |
이는 운영 모델을 위한 기준입니다. 클래스당 라벨이 지정된 객체 수백 개면 실험을 시작하기에 충분하며, 첫 모델을 통해 데이터가 더 필요한 시나리오를 가장 빠르게 파악할 수 있습니다. 데이터셋이 기준에 얼마나 못 미치는지 확인하려면 수량을 세어 보세요. 이 스크립트는 자동으로 다운로드되는 COCO의 128개 이미지 하위 집합인 COCO128에서 실행됩니다.
from collections import Counter
from pathlib import Path
from ultralytics.data.utils import IMG_FORMATS, check_det_dataset, img2label_paths
IMAGES_PER_CLASS, INSTANCES_PER_CLASS = 1500, 10_000 # Ultralytics production targets
data = check_det_dataset("coco128.yaml") # downloads COCO128 (~7 MB) on first run; swap in your data YAML
images = [p for p in Path(data["train"]).rglob("*.*") if p.suffix[1:].lower() in IMG_FORMATS]
image_count, instance_count, background = Counter(), Counter(), 0
for label in map(Path, img2label_paths([str(p) for p in images])):
classes = [int(r.split()[0]) for r in label.read_text().splitlines() if r.strip()] if label.exists() else []
instance_count.update(classes)
image_count.update(set(classes))
background += not classes
print(f"{len(images)} images, {background} background ({background / len(images):.0%}, target 0-10%)")
print(f"{'class':>12} {'images':>7} {'instances':>10} {'of target':>10}")
ranked = instance_count.most_common()
for cls, n in ranked[:3] + ranked[-3:]: # best and worst covered classes
coverage = min(image_count[cls] / IMAGES_PER_CLASS, n / INSTANCES_PER_CLASS)
print(f"{data['names'][cls]:>12} {image_count[cls]:>7} {n:>10} {coverage:>10.1%}")
never_labeled = [name for i, name in data["names"].items() if i not in instance_count]
print(f"{len(never_labeled)} classes in names with zero labels: {never_labeled[:4]} ...")128 images, 2 background (2%, target 0-10%)
class images instances of target
person 61 254 2.5%
car 12 46 0.5%
cup 10 36 0.4%
bear 1 1 0.0%
banana 1 1 0.0%
skis 1 1 0.0%
9 classes in names with zero labels: ['fire hydrant', 'parking meter', 'sheep', 'cow'] ...결과 읽는 방법:
- 커버리지는 이미지 비율과 인스턴스 비율 중 더 낮은 값입니다. 클래스에는 두 가지가 모두 필요하기 때문입니다. 커버리지가 가장 높은 클래스인
person조차 운영 기준의 2.5%에 불과합니다. - 인스턴스가 하나뿐인 클래스(
bear,banana,skis)는 학습하거나 측정할 수 없습니다. 자체 데이터셋에서는 해당 클래스를 수집하거나 v1에서 제외하세요. - 라벨이 하나도 없는 클래스는 YAML에 나열되어 있지만 실제로 그려진 적은 없습니다. 그래도 모델은 해당 클래스를 예측하는 데 용량을 할당합니다.
names에서 해당 클래스를 제거하거나 학습 전에 데이터를 수집하세요.
변동 요인을 다루세요#

한 프레임에도 가까이 있거나 멀리 있는 사람, 자전거, 개, 자동차가 함께 나타납니다. 실제 사양은 이러한 범위를 의도적으로 계획해야 합니다. 각 변동 요인에 대해 사양이 이를 다루는지, 아니면 적용되지 않는 이유가 무엇인지 기록하세요. 비어 있는 행은 운영 환경에서 발견하게 될 시나리오입니다.
| 차원 | 예시 |
|---|---|
| 시간 | 낮, 밤, 새벽, 해 질 녘 |
| 날씨 | 맑음, 흐림, 비, 안개, 눈 |
| 조명 | 밝음, 어두움, 눈부심, 짙은 그림자, 여러 광원 혼합 |
| 카메라 | 모델, 렌즈, 설치 높이, 시야각 |
| 지리 | 현장, 도시, 국가 |
| 사람 및 작업자 | 교대조, 팀, 유니폼, 보호 장비 |
| 제품 변형 | SKU, 패키징 개정판, 색상 |
| 객체 크기 | 가깝고 큰 객체, 중거리 객체, 멀고 작은 객체 |
| 가림 | 가림 없음, 부분 가림, 심한 가림, 유리 뒤 |
| 장비 상태 | 더럽거나 김 서린 렌즈, 손상된 팔레트, 마모된 바닥 표시 |
수량보다 변동성이 중요합니다. 이 행들에 걸쳐 분산된 이미지 1,500장이 정오에 한 대의 카메라로 촬영한 이미지 5,000장보다 가치가 높습니다.
배경 이미지와 엣지 케이스 계획하기#
배경 이미지는 빈 하역장처럼 라벨링된 객체가 없는 프레임입니다. 이러한 이미지는 모델이 아무것도 없는 상황을 학습하도록 하여 오탐을 줄입니다.
Ultralytics YOLO 형식에서 배경 이미지는 라벨 파일이 비어 있거나 라벨 파일이 없는 이미지입니다. 학습 과정에서 스캔 로그(… 1200 images, 90 backgrounds, 0 corrupt)에 두 유형 모두 집계됩니다. 첫 에포크를 시작하기 전에 이 수치를 목표인 0~10%와 비교하세요.
엣지 케이스에는 심한 가림, 프레임 가장자리에서 잘린 객체, 특이한 방향, 쌓인 객체, 지게차 마스트 옆의 철제 기둥처럼 비슷하게 보이는 객체가 포함됩니다. 각 엣지 케이스에 별도의 할당량 행을 지정하세요.
팔레트 더미 뒤에 사람이 나타나는 프레임이 200개 중 1개라면, 자연 발생 빈도로 수집한 데이터셋 1,500개에는 그런 프레임이 약 7개뿐이며, 학습하기에는 턱없이 부족합니다. 엣지 케이스를 의도적으로 오버샘플링하세요. 해당 케이스의 할당량은 실제 영상에서 차지하는 비중보다 훨씬 커야 합니다.
문서만으로 모든 시나리오를 떠올릴 수는 없습니다. 사양을 대체로 올바르게 작성하고, 첫 모델을 빠르게 학습한 다음 모델의 실수로 놓친 시나리오를 찾아내세요.
정답 보기
할당량은 이미지 목표치뿐 아니라 인스턴스 목표치에서도 산출해야 합니다. 주차장 차량 계수기에서 두 개의 클래스를 사용하고, 파일럿 영상에서 이미지당 자동차는 평균 3대, 트럭은 0.5대라고 가정해 보겠습니다.
| 클래스 | 이미지당 인스턴스 수 | 인스턴스 10,000개에 필요한 이미지 수 | 최소 이미지 수 1,500장을 충족하는 데 필요한 이미지 수 | 계획 |
|---|---|---|---|---|
car | 3.0 | ≈ 3,300 | 1,500 | 이미지 3,300장 |
truck | 0.5 | ≈ 20,000 | 1,500 | 트럭이 10배 더 자주 나타나는 하역장 입구에서 집중 촬영 |
일반 영상으로 자동차는 충분히 확보됩니다. 트럭은 무작위로 이미지 20,000장을 촬영해야 하므로, 모든 이미지 수집량을 6배로 늘리는 대신 명세에 전용 시나리오 행(평일 06:00~10:00 하역장 입구)을 추가합니다. 배경 행은 약 5%(밤의 빈 주차장)로 설정하고, 눈에 덮인 장면과 프레임 밖으로 일부 잘린 트럭을 엣지 케이스 행으로 추가하세요.