Ultralytics YOLO27: Trag dich in die Warteliste ein.
Get Started
LektionFortgeschrittene

Instanzsegmentierung beschriften und trainieren

Erstelle Polygonbeschriftungen, prüfe sie im Code, trainiere yolo26n-seg.pt mit COCO8-seg und diagnostiziere ein Modell anhand der Abweichung zwischen seinen Box- und Maskenmetriken.

Bei der Instanzsegmentierung erhält jedes Objekt seinen eigenen Umriss. Das brauchst du, wenn die Fläche, Überlappung oder eine Grenze die Entscheidung bestimmt. Polygone sind die aufwendigsten Beschriftungen, für die die meisten Teams bezahlen, und eine fehlerhafte Zeile lässt das Training nicht abstürzen – sie trainiert unbemerkt mit der falschen Form. In dieser Lektion lernst du das richtige Zeilenformat kennen, prüfst einen Beschriftungsdatensatz im Code und trainierst ein erstes Modell. So siehst du, was die zusätzliche Beschriftungsarbeit bringt: eine zweite Metrik, die Umrisse statt nur Positionen bewertet.

Lernergebnis

Prüfe einen Polygonbeschriftungsdatensatz auf fehlerhafte Zeilen, trainiere yolo26n-seg.pt mit coco8-seg.yaml und entscheide anhand der Abweichung zwischen Box- und Masken-mAP50-95, was zu korrigieren ist.

Schnellkurs
Wenn du dich bereits auskennst, findest du hier die Kurzfassung.
  1. Schreibe eine Zeile pro Objekt: Klassenindex, dann normalisierte x y-Scheitelpunktpaare, mindestens drei Scheitelpunkte.

  2. Prüfe die Beschriftungen auf eine ungerade Anzahl von Werten, Koordinaten außerhalb des gültigen Bereichs und verdächtig geringe Polygonfüllung.

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

  4. Lege den Grenzwert anhand der Masken-mAP50-95 (metrics.seg.map) fest und nutze die Abweichung zur Box-mAP50-95 (metrics.box.map) als Diagnose.

Praxisnah

Die Polygonbeschriftungszeile#

Ultralytics YOLO-Instanzsegmentierung eines städtischen Zebrastreifens mit einer separaten Maske für jedes Auto, den Bus und alle Fußgänger

Eine Segmentierungs-Beschriftungsdatei enthält eine Zeile pro Objekt, wie eine Beschriftungsdatei für die Objekterkennung. Statt einer Box führt sie jedoch den Umriss des Objekts als normalisierte Scheitelpunktpaare auf. Eine gekürzte echte Zeile aus COCO8-seg:

45 0.782016 0.986521 0.937078 0.874167 0.957297 0.782021 0.950562 0.739333 0.825844 0.561792 ...
PositionErkennungszeileSegmentierungszeile
1Klassenindex (45 ist bowl)Klassenindex
2–5x_center y_center width heightdie ersten beiden Scheitelpunkte, x1 y1 x2 y2
6+—weitere Scheitelpunkte, so viele wie für den Umriss nötig
Einheitennormalisiert auf 0–1normalisiert auf 0–1 (Pixel-x ÷ Bildbreite, Pixel-y ÷ Bildhöhe)

Drei Regeln sind besonders wichtig: mindestens drei Scheitelpunkte pro Objekt, Koordinaten geteilt durch die Bildgröße und eine Zeile pro Instanz, selbst wenn sich zwei Objekte derselben Klasse berühren. Die Anleitung zu Segmentierungsdatensätzen enthält die vollständige Spezifikation.

Polygon-Labels trainieren auch Boxen und Klassenkarten

Polygone sind eine Obermenge von Box--Labels. Mit demselben Label-Ordner trainierst du yolo26n.pt (Ultralytics leitet jede Box aus dem jeweiligen Polygon ab) und yolo26n-sem.pt (die Polygone werden in Klassenkarten umgewandelt) – wir haben beide mit coco8-seg.yaml trainiert. Wenn sich der Plan nach dem Beschriften ändert, ist das Budget nicht verloren.

Beschriftungen vor dem Training prüfen#

Eine Zeile mit einer ungeraden Anzahl von Werten, eine in Pixeln angegebene Koordinate oder ein Umriss um das falsche Objekt hält das Training nicht auf. Dieses Prüfskript lädt den Datensatz mit Ultralytics' eigenem Loader, weist fehlerhafte Zeilen zurück und gibt die Fläche jedes Polygons neben der Box aus, die Ultralytics daraus ableitet:

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

Flächen sind als Anteil des Bildes angegeben. Die erste Schüssel bedeckt also 39 % ihres Fotos. Füllgrad ist die Polygonfläche geteilt durch die Boxfläche. Auf diese Spalte solltest du achten:

  • Die meisten kompakten Objekte (Schüsseln, Orangen, Brokkoli) liegen bei etwa 0,6–0,7.
  • Der Wert 0,31 für die Giraffe ist plausibel: Lange Beine und ein langer Hals lassen den größten Teil ihrer Box leer. Genau deshalb braucht dieses Objekt überhaupt eine Maske.
  • Der Wert 0,15 für die dritte Schüssel ist die Zeile, die ein Reviewer öffnen sollte. Von der Schüssel ist um das Essen herum nur ein schmaler Rand sichtbar, ihr Umriss bildet also ein schmales Band innerhalb einer großen Box. Das ist hier korrekt. Bei deinen Daten weist ein derart niedriger Füllgrad jedoch genauso oft darauf hin, dass der Umriss um das falsche Objekt gezogen wurde.
Entscheidungsregel

Korrigiere vor dem Training jede fehlerhafte Zeile. Sortiere anschließend nach Füllgrad und öffne für jede Klasse die untersten paar Prozent der Zeilen in einem Annotationswerkzeug: Ein niedriger Füllgrad bedeutet entweder ein dünnes oder verdecktes Objekt (beibehalten) oder eine fehlerhafte Kontur (korrigieren).

Polygone erhalten, ohne jede Kante nachzuzeichnen#

Das manuelle Nachzeichnen von Umrissen ist der aufwendige Teil der Datenbeschriftung. Prüfe, welche Abkürzung infrage kommt, bevor jemand damit anfängt:

Du hast bereitsWerkzeug
COCO-JSON mit Segmentierungconvert_coco(labels_dir, use_segments=True) aus ultralytics.data.converter
PNG-Masken (Pixelwert = Klassenindex + 1, 0 = Hintergrund)convert_segment_masks_to_yolo_seg(masks_dir, output_dir, classes) – sich berührende Objekte derselben Klasse werden zu einem Polygon zusammengefasst. Trenne sie bei der Überprüfung.
Nur Boxbeschriftungenyolo_bbox2segment(im_dir) – übergibt SAM jede Box als Prompt, um ein Polygon zu erzeugen
Noch nichtsIntelligente Annotation und Stapelbeschriftung – einmal klicken, Polygon überprüfen

Im Ultralytics Platform führt der intelligente Modus SAM aus: Ein positiver Klick auf das Objekt und ein negativer Klick auf das, was ausgeschlossen werden soll, erzeugen eine Maske, die du anschließend überprüfst.

Intelligenter Modus der Ultralytics Platform mit ausgewähltem SAM 2.1 Tiny: Ein positiver und ein negativer Klick auf einen Lagerroboter erzeugen dessen Maske

Jedes maschinell erzeugte Polygon überprüfen

Eine Maske, die in den Hintergrund übergeht, bringt dem Modell genau das Gleiche bei. Prüfe maschinell erzeugte Polygone anhand der Qualitätskriterien unter Boxen oder Masken und führe anschließend die obige Labelprüfung erneut aus.

Mit COCO8-seg trainieren#

Trainingsmosaik von COCO8-seg: Zebras, eine Giraffe, Brokkoli- und Orangenschalen sowie eine Blumenvase, jedes Objekt mit eigener farbiger Maske und eigenem Klassenindex

Das Training folgt dem Arbeitsablauf für die Objekterkennung, verwendet aber ein -seg-Modell und einen Segmentierungsdatensatz. Eine Epoche mit imgsz=320 auf einer Laptop-CPU ist in wenigen Sekunden abgeschlossen – genug, um die gesamte Pipeline zu überprüfen:

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

Bei vier Validierungsbildern sind diese Zahlen nur Rauschen. Wichtig ist, dass der Lauf abgeschlossen wird und weights/best.pt unter runs/segment/ gespeichert wird. Für echte Daten befolgst du die Anleitung zur Segmentierungsaufgabe und trainierst den vollständigen Zeitplan auf einer GPU.

Box- und Maskenmetriken nebeneinander betrachten#

Die Validierungstabelle hat zwei Bereiche. Box bewertet jede vorhergesagte Box im Vergleich zur Box, die sich aus der Beschriftung ergibt; Maske vergleicht die vorhergesagten Pixel mit dem beschrifteten Polygon. Beide verwenden dieselbe mAP-Berechnung. Nur die IoU unterscheidet sich.

Die Masken-mAP50-95 ist fast immer niedriger als die Box-mAP50-95, denn die Pixel-IoU ist strenger: Eine um einige Pixel verschobene Box überlappt immer noch gut, während eine Maske, der ein Arm fehlt oder die auf die Straße übergreift, bei den Schwellenwerten von 0,75–0,95 schnell IoU verliert. Die Zeile horse zeigt den Extremfall: Box-mAP50-95 0,564, Maske 0,199. Das Modell hat beide Pferde gefunden und gut eingerahmt, aber Beine und Schwänze nicht umrissen. Nutze die Abweichung, um zu entscheiden, was korrigiert werden muss:

  • Beide niedrig: Das Problem liegt darin, Objekte zu finden, nicht sie zu umreißen. Ergänze zuerst Daten zu den Bedingungen, unter denen Objekte übersehen werden.
  • Box gut, Maske deutlich schlechter: Das Modell findet Objekte, umreißt sie aber schlecht. Prüfe die Beschriftungen, probiere ein größeres Modell aus oder trainiere und validiere mit einem höheren imgsz.
  • Maske verbessert sich, während die Box stagniert: Das ist in Ordnung – bei einem Segmentierungsprodukt ist die Masken-mAP50-95 das Freigabekriterium.
Zwei Einstellungen, die die Maskenauflösung nicht verändern

overlap_mask=True führt beim Training Instanzmasken in einem Bild zusammen, und mask_ratio=4 verkleinert Trainingsmasken. Beides verändert nicht die Auflösung vorhergesagter Masken – dafür ist retina_masks zuständig, das in der nächsten Lektion behandelt wird. Lass beide Einstellungen unverändert, sofern du keinen gemessenen Grund für eine Änderung hast.

Ausprobieren

Führe check_seg_labels.py mit dem gesamten COCO8-seg-Datensatz aus (Training und Validierung) und liste die drei Zeilen mit dem niedrigsten Füllgrad auf. Trainiere dann yolo26n-seg.pt mit coco8-seg.yaml eine Epoche lang bei imgsz=320, notiere Box-mAP50-95, Masken-mAP50-95 und die Klasse mit der größten Abweichung und öffne val_batch0_pred.jpg im Validierungsordner. Suche dort ein Objekt, dessen Maske sichtbar schlechter ist als seine Box.

Abgeschlossen, wenn
Du hast die Lektion abgeschlossen, wenn alle folgenden Punkte zutreffen.
  • Deine Beschriftungsprüfung weist null fehlerhafte Zeilen aus, und du hast die drei Zeilen mit dem niedrigsten Füllgrad aufgelistet und für jede entschieden, ob sie beibehalten oder korrigiert werden soll.

  • Der Trainingslauf wurde abgeschlossen, best.pt ist unter runs/segment/ vorhanden und du hast beide mAP50-95-Werte notiert.

  • Du hast die Klasse mit der größten Abweichung zwischen Box und Maske sowie den nächsten Schritt aus dem Entscheidungsdiagramm benannt.

Lösung anzeigen

Um alle Aufteilungen zu prüfen, ersetze den Ordner in der Schleife durch Path(data["path"], "labels").rglob("*.txt"). Trainiere dann:

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

Die Abweichung von 0,1 ist zu erwarten: Die Masken-IoU bestraft jedes Pixel an der Grenze, die Box-IoU nur die vier Kanten. In unserem Lauf hat horse die größte Abweichung (Box 0,564, Maske 0,199). Die Box-mAP ist gut und die Maske liegt deutlich darunter. Das Entscheidungsdiagramm weist daher auf die Qualität der Umrisse hin: Prüfe zuerst diese Polygone, dann probiere ein größeres Modell oder ein höheres imgsz.

Wie es weitergeht

Als Nächstes machen wir aus vorhergesagten Masken konkrete Werte: Flächen, Umrisse, Überlappungen und transparente Freisteller.