Ultralytics YOLO27: 대기자 명단에 등록하세요.
Get Started
강의중급

인스턴스 세그멘테이션 라벨 지정 및 학습

폴리곤 라벨을 작성하고 코드에서 확인한 다음, COCO8-seg에서 yolo26n-seg.pt를 학습하고 박스 메트릭과 마스크 메트릭 간 격차를 바탕으로 모델을 진단합니다.

인스턴스 세그멘테이션은 각 객체의 윤곽을 개별적으로 제공하므로, 면적, 겹침 또는 경계가 의사 결정의 기준일 때 필요합니다. 폴리곤은 대부분의 팀이 구매하는 라벨 중 비용이 가장 많이 들며, 형식이 잘못된 행이 있어도 학습이 중단되는 대신 잘못된 형태로 조용히 학습됩니다. 이 강의에서는 행 형식을 올바르게 작성하고, 코드로 라벨 세트를 확인한 뒤 첫 모델을 학습하여 추가 라벨링의 이점을 살펴봅니다. 즉, 위치뿐 아니라 윤곽도 평가하는 두 번째 메트릭을 확인합니다.

학습 성과

형식이 잘못된 행이 있는지 폴리곤 라벨 세트를 확인하고, yolo26n-seg.pt을 coco8-seg.yaml에서 학습한 다음, 박스 mAP50-95와 마스크 mAP50-95 간 격차를 바탕으로 수정할 항목을 판단합니다.

속성 과정
이미 사용법을 알고 계신다면, 간단히 요약해 드리겠습니다.
  1. 객체마다 한 행을 작성합니다. 클래스 인덱스 뒤에 정규화된 x y 꼭짓점 쌍을 적고, 꼭짓점은 최소 세 개여야 합니다.

  2. 값 개수가 홀수인지, 좌표가 범위를 벗어났는지, 폴리곤 채움 비율이 의심스러울 정도로 낮은지 라벨 검사를 실행합니다.

  3. yolo segment train data=coco8-seg.yaml model=yolo26n-seg.pt epochs=1 imgsz=320.

  4. 마스크 mAP50-95(metrics.seg.map)를 기준으로 삼고, 박스 mAP50-95(metrics.box.map)와의 격차를 진단에 활용합니다.

실습형

폴리곤 라벨 행#

도시 횡단보도의 Ultralytics YOLO 인스턴스 세그멘테이션. 각 차량과 버스, 모든 보행자에 개별 마스크가 적용되어 있습니다

세그멘테이션 라벨 파일은 감지 라벨처럼 객체마다 한 행을 사용하지만, 박스 대신 정규화된 꼭짓점 쌍으로 객체의 윤곽을 나열합니다. COCO8-seg의 실제 행을 간략히 표시하면 다음과 같습니다.

45 0.782016 0.986521 0.937078 0.874167 0.957297 0.782021 0.950562 0.739333 0.825844 0.561792 ...
위치감지 행세그멘테이션 행
1클래스 인덱스(45는 bowl입니다)클래스 인덱스
2–5x_center y_center width height처음 두 꼭짓점, x1 y1 x2 y2
6+—윤곽에 필요한 만큼의 추가 꼭짓점
단위0–1 정규화0–1 정규화(픽셀 x ÷ 이미지 너비, 픽셀 y ÷ 이미지 높이)

세 가지 규칙이 핵심입니다. 객체마다 꼭짓점이 최소 세 개여야 하고, 좌표는 이미지 크기로 나누어야 하며, 같은 클래스의 객체 두 개가 맞닿아 있어도 인스턴스마다 행을 하나씩 작성해야 합니다. 전체 사양은 세그멘테이션 데이터셋 가이드를 참조하세요.

폴리곤 레이블은 박스와 클래스 맵 학습에도 사용 가능

폴리곤은 박스 레이블을 포함하는 상위 집합입니다. 같은 레이블 폴더로 yolo26n.pt(Ultralytics가 각 폴리곤에서 박스를 추출함)과 yolo26n-sem.pt(폴리곤을 래스터화해 클래스 맵을 생성함)를 학습할 수 있습니다. 두 모델 모두 coco8-seg.yaml으로 학습했습니다. 레이블 지정 후 계획이 바뀌어도 예산을 낭비한 것은 아닙니다.

학습 전에 라벨 확인하기#

값의 개수가 홀수인 행, 픽셀 단위로 작성된 좌표 또는 잘못된 객체를 따라 그린 윤곽이 있어도 학습은 중단되지 않습니다. 이 검사기는 Ultralytics 자체 로더를 사용해 데이터셋을 읽고, 형식이 잘못된 행을 거부하며, 각 폴리곤의 면적과 Ultralytics가 해당 폴리곤에서 계산할 박스를 함께 출력합니다.

from pathlib import Path

import numpy as np

from ultralytics.data.utils import check_det_dataset

data = check_det_dataset("coco8-seg.yaml")  # resolves paths, downloads if needed
for label in sorted(Path(data["path"], "labels", "train").glob("*.txt"))[:2]:
    for n, row in enumerate(label.read_text().splitlines(), 1):
        cls, *coords = row.split()
        if len(coords) % 2 or len(coords) < 6:
            print(f"{label.name}:{n} BAD: needs an even number of values and at least 3 vertices")
            continue
        xy = np.array(coords, dtype=float).reshape(-1, 2)
        if xy.min() < 0 or xy.max() > 1:
            print(f"{label.name}:{n} BAD: coordinates must be normalized to 0-1")
            continue
        x, y = xy[:, 0], xy[:, 1]
        area = 0.5 * abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))  # shoelace formula
        w, h = xy.max(0) - xy.min(0)  # the box Ultralytics derives from the polygon
        name = data["names"][int(cls)]
        print(f"{label.stem} {name:9s} {len(xy):3d} vertices  polygon {area:.3f}  box {w * h:.3f}  fill {area / (w * h):.2f}")
000000000009 bowl       12 vertices  polygon 0.391  box 0.569  fill 0.69
000000000009 bowl       14 vertices  polygon 0.145  box 0.238  fill 0.61
000000000009 broccoli   50 vertices  polygon 0.161  box 0.252  fill 0.64
000000000009 bowl       55 vertices  polygon 0.079  box 0.531  fill 0.15
000000000009 orange     13 vertices  polygon 0.007  box 0.011  fill 0.64
000000000009 orange     13 vertices  polygon 0.005  box 0.009  fill 0.61
000000000009 orange     18 vertices  polygon 0.012  box 0.019  fill 0.61
000000000009 orange     12 vertices  polygon 0.010  box 0.022  fill 0.44
000000000025 giraffe    82 vertices  polygon 0.072  box 0.234  fill 0.31
000000000025 giraffe    19 vertices  polygon 0.010  box 0.027  fill 0.38

면적은 이미지에서 차지하는 비율이므로, 첫 번째 그릇은 사진의 39%를 차지합니다. 채움 비율은 폴리곤 면적을 박스 면적으로 나눈 값이며, 이 열을 집중적으로 확인해야 합니다.

  • 그릇, 오렌지, 브로콜리처럼 형태가 조밀한 대부분의 객체는 약 0.6~0.7입니다.
  • 기린의 0.31은 실제 값입니다. 긴 다리와 목 때문에 박스 대부분이 비어 있습니다. 바로 이런 객체에 마스크가 필요한 이유입니다.
  • 세 번째 그릇의 0.15는 검토자가 열어 봐야 할 행입니다. 음식 주변으로 그릇의 얇은 가장자리만 보여 큰 박스 안에 가느다란 띠 모양의 윤곽이 생깁니다. 이 경우에는 올바른 결과지만, 데이터에서는 채움 비율이 이만큼 낮을 때 잘못된 객체의 윤곽을 그린 경우도 흔합니다.
선택 기준

학습 전에 BAD 행을 모두 수정하세요. 그런 다음 채움 비율을 기준으로 정렬하고, 어노테이션 도구에서 클래스별로 채움 비율이 가장 낮은 몇 퍼센트의 행을 여세요. 채움 비율이 낮은 경우는 얇거나 가려진 객체(유지) 또는 잘못된 윤곽선(수정)입니다.

모든 가장자리를 직접 그리지 않고 폴리곤 얻기#

윤곽을 직접 그리는 작업은 데이터 라벨링에서 비용이 가장 많이 드는 부분입니다. 작업을 시작하기 전에 사용할 수 있는 간편한 방법이 있는지 확인하세요.

이미 보유한 항목도구
세그멘테이션이 포함된 COCO JSONconvert_coco(labels_dir, use_segments=True) — ultralytics.data.converter에서
PNG 마스크(픽셀 값 = 클래스 인덱스 + 1, 0 = 배경)convert_segment_masks_to_yolo_seg(masks_dir, output_dir, classes) — 같은 클래스의 객체가 맞닿아 있으면 하나의 폴리곤으로 생성되므로 검토 과정에서 분리해야 합니다.
박스 라벨만 보유yolo_bbox2segment(im_dir) — 각 박스를 사용해 SAM에 프롬프트를 전달하여 폴리곤을 생성합니다.
아직 아무것도 없음스마트 어노테이션 및 일괄 라벨링 — 한 번 클릭한 뒤 폴리곤을 검토합니다.

Ultralytics Platform에서 스마트 모드는 SAM을 실행합니다. 객체를 긍정 클릭하고 제외할 부분을 부정 클릭하면 마스크가 생성되며, 이후 이를 검토합니다.

SAM 2.1 Tiny를 선택한 Ultralytics Platform 스마트 모드. 창고 로봇을 긍정 클릭하고 부정 클릭하여 마스크를 생성합니다

머신 생성 폴리곤을 모두 검토하기

배경까지 번진 마스크를 학습하면 모델도 똑같이 번지게 됩니다. 박스와 마스크 비교의 품질 기준에 따라 머신 생성 폴리곤을 검토한 다음 위의 레이블 검사를 다시 실행하세요.

COCO8-seg에서 학습하기#

COCO8-seg 학습 모자이크: 얼룩말, 기린, 브로콜리와 오렌지가 담긴 도시락, 꽃병의 꽃. 각 객체에는 개별 색상 마스크와 클래스 인덱스가 적용되어 있습니다

학습은 -seg 모델과 세그멘테이션 데이터셋을 사용하는 감지 워크플로입니다. 노트북 CPU에서 imgsz=320로 한 에폭을 실행하면 몇 초 만에 완료되어 파이프라인 전체가 정상 작동하는지 확인할 수 있습니다.

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
model.train(data="coco8-seg.yaml", epochs=1, imgsz=320, device="cpu")
metrics = model.val()
print(f"box  mAP50-95: {metrics.box.map:.3f}")
print(f"mask mAP50-95: {metrics.seg.map:.3f}")
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95)     Mask(P          R      mAP50  mAP50-95)
                   all          4         17      0.909      0.449      0.661      0.482      0.685      0.409      0.557      0.377
                 horse          1          2          1      0.993      0.995      0.564      0.619      0.857      0.663      0.199
box  mAP50-95: 0.482
mask mAP50-95: 0.377

검증 이미지가 네 장뿐이므로 이 수치는 변동성이 큽니다. 중요한 점은 실행이 완료되고 runs/segment/ 아래에 weights/best.pt이 기록되는 것입니다. 실제 데이터에서는 세그먼트 작업 가이드를 따라 GPU에서 전체 스케줄로 학습하세요.

박스와 마스크 메트릭을 나란히 살펴보기#

검증 표는 두 블록으로 구성됩니다. 박스는 예측된 각 박스를 라벨에서 유추한 박스와 비교해 평가하고, 마스크는 예측된 픽셀을 라벨 폴리곤과 비교해 평가합니다. 두 평가 모두 동일한 mAP 계산 방식을 사용하며, IoU만 다릅니다.

마스크 mAP50-95는 거의 항상 박스 메트릭보다 낮습니다. 픽셀 IoU가 더 엄격하기 때문입니다. 몇 픽셀 어긋난 박스도 여전히 잘 겹칠 수 있지만, 팔을 놓치거나 도로까지 번진 마스크는 0.75~0.95 임계값에서 IoU가 빠르게 낮아집니다. horse 행이 가장 극단적인 예입니다. 박스 mAP50-95는 0.564, 마스크는 0.199입니다. 모델은 두 말 모두 찾아 박스로 잘 표시했지만, 다리와 꼬리의 윤곽은 제대로 그리지 못했습니다. 격차를 바탕으로 수정할 항목을 판단하세요.

  • 둘 다 낮음: 윤곽 그리기가 아니라 객체 탐지가 문제입니다. 먼저 놓친 조건에 해당하는 데이터를 추가하세요.
  • 박스는 양호하지만 마스크는 훨씬 낮음: 모델은 객체를 찾지만 윤곽을 제대로 그리지 못합니다. 라벨을 검사하고, 더 큰 모델을 사용하거나 더 높은 imgsz에서 학습 및 검증을 수행하세요.
  • 박스는 정체되어도 마스크는 개선 중: 괜찮습니다. 세그멘테이션 제품에서는 마스크 mAP50-95가 출시 기준입니다.
마스크 해상도를 변경하지 않는 두 가지 설정

overlap_mask=True은 학습 중 인스턴스 마스크를 하나의 이미지로 합치고, mask_ratio=4은 학습 마스크의 해상도를 낮춥니다. 어느 설정도 예측 마스크의 해상도를 변경하지 않습니다. 예측 마스크 해상도는 다음 강의에서 다루는 retina_masks입니다. 변경해야 할 이유를 측정으로 확인하지 않았다면 두 설정 모두 기본값으로 두세요.

사용해 보기

COCO8-seg 전체(train 및 val)에서 check_seg_labels.py을 실행하고 채움 비율이 가장 낮은 행 세 개를 나열하세요. 그런 다음 coco8-seg.yaml에서 yolo26n-seg.pt을 imgsz=320으로 한 에폭 학습하고, 박스 mAP50-95, 마스크 mAP50-95, 격차가 가장 큰 클래스를 기록하세요. 검증 폴더에서 val_batch0_pred.jpg를 열고 박스보다 마스크가 눈에 띄게 나쁜 객체를 찾으세요.

완료 조건
다음 조건을 모두 충족하면 레슨을 완료한 것입니다.
  • 라벨 검사에서 BAD 행이 0개로 보고되었으며, 채움 비율이 가장 낮은 행 세 개와 각 행의 유지 또는 수정 판정을 기록했습니다.

  • 학습이 완료되었고 runs/segment/ 아래에 best.pt이 있으며, 두 mAP50-95 값을 모두 기록했습니다.

  • 박스와 마스크 간 격차가 가장 큰 클래스와 의사결정 차트에서 제시하는 다음 단계를 기록했습니다.

정답 보기

모든 분할을 확인하려면 반복문에서 폴더를 Path(data["path"], "labels").rglob("*.txt")으로 바꾸세요. 그런 다음 학습합니다.

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
model.train(data="coco8-seg.yaml", epochs=1, imgsz=320, device="cpu")
metrics = model.val()
print(f"box  mAP50-95: {metrics.box.map:.3f}")   # 0.482 in our run
print(f"mask mAP50-95: {metrics.seg.map:.3f}")   # 0.377 in our run

0.1의 격차는 예상된 결과입니다. 마스크 IoU는 경계의 모든 픽셀에 페널티를 적용하지만, 박스 IoU는 네 변에만 적용합니다. 실행 결과 격차가 가장 큰 클래스는 horse입니다(박스 0.564, 마스크 0.199). 박스 mAP는 양호하지만 마스크는 훨씬 낮으므로, 차트는 윤곽 품질을 확인하도록 안내합니다. 먼저 해당 폴리곤을 확인한 다음 더 큰 모델이나 더 높은 imgsz을 사용해 보세요.

다음 단계

다음으로 예측 마스크를 수치로 변환합니다. 면적, 윤곽, 겹침, 투명한 분리 이미지 등을 살펴봅니다.