Ultralytics YOLO27: Trag dich in die Warteliste ein.
Get Started
Leistungsstarke YOLO-Datensätze erstellen·Beschriftung·Lektion 5/10
LektionAnfänger

Qualitätskontrolle von Datensätzen

Prüfe Labels, Klassenverteilung, Duplikate und Abdeckung der Aufteilungen mit einem Skript, lass ein Modell auf wahrscheinlich fehlende Labels hinweisen und behebe die gefundenen Probleme vor dem Training.

Labels zu korrigieren bringt meist mehr Genauigkeit als Hyperparameter abzustimmen. Teams verbringen oft Wochen mit Einstellungen, um ein oder zwei mAP-Punkte herauszuholen, und stellen dann bei einer Prüfung am Nachmittag fest, dass ein Teil einer Klasse die ganze Zeit falsch beschriftet war. Die Qualitätskontrolle findet, was die Annotationsanleitung übersehen hat, bevor ein Trainingslauf Label-Fehler in eine Zahl verwandelt, der niemand vertrauen sollte.

Lernergebnis

Führe eine automatisierte Labelprüfung und eine modellgestützte Prüfung verdächtiger Fälle durch, behebe die gefundenen Probleme und halte das Ergebnis in kurzen QC-Notizen fest.

Schnellkurs
Wenn du dich bereits auskennst, findest du hier die Kurzfassung.
  1. Zähle die Werte mit einem Skript: Instanzen pro Klasse, Anteil der Hintergrundbilder, ungültige Klassen, Duplikate und Klassen, die im Trainingssatz fehlen.

  2. Lass ein vortrainiertes Modell sicher erkannte Objekte ohne passendes Label markieren und prüfe sie mit bloßem Auge.

  3. Behebe ein Klassenungleichgewicht in der richtigen Reihenfolge: zuerst Daten sammeln, dann überabtasten und zuletzt die Verlustfunktion gewichten.

  4. Halte fest, was du geprüft und korrigiert hast.

Praxisnah

Die sieben Prüfungen#

Führe alle sieben Prüfungen vor dem ersten Trainingslauf und erneut nach jedem größeren Annotationsstapel durch. Die Anleitung zum Vorverarbeiten annotierter Daten behandelt mehrere davon ausführlich.

#PrüfungWas sie aufdecktSo findest du es heraus
1Fehlende LabelsObjekte, die von annotierenden Personen ausgelassen wurdenPrüfung verdächtiger Fälle weiter unten; visuelle Stichprobe
2Falsche oder ungültige KlassenVertauschte Klassen, IDs außerhalb der KlassenlistePrüfskript; Konfusionsmatrix nach einem ersten Trainingslauf
3Zu große BoxenBoxen mit Spielraum oder abgeschnittenen RändernVisuelle Stichprobe; Markierungen für sehr kleine Boxen durch die Prüfung
4DuplikateExakte Kopien und nahezu identische EinzelbilderPrüfskript für exakte Kopien; die Ansicht Clustering der Ultralytics Platform, nach Aufteilung eingefärbt, für nahezu identische Duplikate
5KlassenungleichgewichtKlassen, deren Anzahl deutlich unter den Zielwerten der Spezifikation liegtPrüfskript; Registerkarte Charts der Platform
6Abdeckung der Aufteilungen und DatenleckageKlassen, die in einer Aufteilung fehlen; ein Clip in zwei AufteilungenPrüfskript; gruppierte Aufteilungen (nächste Lektion)
7Anteil der HintergrundbilderZu wenige oder zu viele leere BilderPrüfskript; Zielwert 0–10 %

Zählvorgänge automatisieren#

Bei den Prüfungen 2, 4, 5, 6 und 7 geht es ums Zählen, also solltest du sie mit Skripten automatisieren. Diese Prüfung liest eine YAML-Datei für Erkennungsdaten ein, deren train: und val: auf Bildordner verweisen. Sie zählt Instanzen pro Klasse und Hintergrundbilder in jeder Aufteilung, markiert ungültige Klassen-IDs, sehr kleine Boxen, doppelte Labelzeilen und byteidentische Bilder und listet Klassen auf, die in der Validierung, aber nie im Training vorkommen:

import hashlib
from collections import Counter
from pathlib import Path

from ultralytics.data.utils import IMG_FORMATS, check_det_dataset, img2label_paths

data = check_det_dataset("coco8.yaml")  # downloads COCO8 on first run; swap in your data YAML
names, seen = data["names"], {}

for split in ("train", "val"):
    images = sorted(p for p in Path(data[split]).rglob("*.*") if p.suffix[1:].lower() in IMG_FORMATS)
    instances, hashes, problems, background = Counter(), Counter(), [], 0
    for img, label in zip(images, img2label_paths([str(p) for p in images])):
        hashes[hashlib.md5(img.read_bytes()).hexdigest()] += 1
        rows = [r.split() for r in Path(label).read_text().splitlines() if r.strip()] if Path(label).exists() else []
        background += not rows
        if len(rows) != len({tuple(r) for r in rows}):
            problems.append(f"{img.name}: duplicate label rows")
        for r in rows:
            cls, w, h = int(r[0]), float(r[3]), float(r[4])
            if cls not in names:
                problems.append(f"{img.name}: class {cls} is not in names")
            elif w * h < 1e-4:
                problems.append(f"{img.name}: tiny {names[cls]} box")
            instances[cls] += 1
    seen[split] = set(instances)
    duplicates = sum(n - 1 for n in hashes.values())
    print(f"{split}: {len(images)} images, {background} background, {duplicates} duplicate images")
    for cls, n in instances.most_common():
        print(f"  {names.get(cls, cls):>12}  {n}")
    for problem in problems:
        print("  !", problem)

print("in val but never in train:", sorted(names[c] for c in seen["val"] - seen["train"] if c in names))
train: 4 images, 0 background, 0 duplicate images
        orange  4
          bowl  3
       giraffe  2
...
val: 4 images, 0 background, 0 duplicate images
        person  10
         horse  2
...
in val but never in train: ['dog', 'elephant', 'horse', 'person', 'umbrella']

Die letzte Zeile zeigt das Ergebnis. person hat 10 Instanzen in der Validierung und keine im Training. Die Validierung bewertet also eine Klasse, mit der das Modell nie trainiert wurde. COCO8 ist ein Testsatz mit acht Bildern für einen kurzen Funktionstest; dort ist das zu erwarten. In deinem Datensatz heißt das, dass du vor dem Training die Aufteilung korrigieren oder weitere Daten sammeln musst. Wenn Probleme vorliegen, werden sie unter der jeweiligen Aufteilung als ! 000000000025.jpg: class 99 is not in names oder ! 000000000009.jpg: duplicate label rows ausgegeben.

Ein Modell auf verdächtige Labels hinweisen lassen#

Fehlende Labels sind der schädlichste Fehler und beim Durchblättern am schwersten zu erkennen. Ein vortrainiertes Modell kann auf mögliche Fehler hinweisen: Eine sichere Erkennung ohne Label in der Nähe ist entweder ein fehlendes Label oder ein Modellfehler, und beides sollte geprüft werden. Dieses Beispiel läuft mit COCO128:

from pathlib import Path

import torch

from ultralytics import YOLO
from ultralytics.data.utils import check_det_dataset, img2label_paths
from ultralytics.utils.metrics import box_iou
from ultralytics.utils.ops import xywh2xyxy

data = check_det_dataset("coco128.yaml")  # swap in your data YAML
model = YOLO("yolo26n.pt")  # a pretrained model, or your own first fine-tune
images = sorted(Path(data["train"]).glob("*.jpg"))

suspects = []
for result, label in zip(model(images, conf=0.6, stream=True, verbose=False), img2label_paths(map(str, images))):
    rows = [r.split() for r in Path(label).read_text().splitlines() if r.strip()] if Path(label).exists() else []
    boxes = xywh2xyxy(torch.tensor([[float(v) for v in r[1:5]] for r in rows]).reshape(-1, 4))
    iou = box_iou(result.boxes.xyxyn, boxes)  # model boxes vs labeled boxes
    for j, (cls, conf) in enumerate(zip(result.boxes.cls.int().tolist(), result.boxes.conf.tolist())):
        if not rows or iou[j].max() < 0.3:  # confident detection with no label anywhere near it
            suspects.append((conf, Path(result.path).name, model.names[cls]))

print(f"{len(suspects)} confident detections with no matching label; top 5:")
for conf, name, cls in sorted(suspects, reverse=True)[:5]:
    print(f"  {conf:.2f}  {cls:<12} {name}")
4 confident detections with no matching label; top 5:
  0.77  donut        000000000438.jpg
  0.75  person       000000000531.jpg
  0.71  donut        000000000438.jpg
  0.66  bottle       000000000436.jpg

Von 128 Bildern schränkt das Skript die Prüfung auf drei ein. Beim Öffnen dieser Bilder zeigen sich beide Arten von Befunden:

  • 000000000438.jpg zeigt eine Box mit einem Dutzend Donuts, bei denen sich zwei sichere Erkennungen mit keinem der beschrifteten Donuts überschneiden: mögliche fehlende oder falsch platzierte Labels.
  • 000000000531.jpg markiert eine „Person“, bei der es sich um den Schatten des Fotografen auf einem Tennisplatz handelt. Das Label ist richtig, das Modell liegt falsch. Genau deshalb prüft eine Person jeden Verdachtsfall, statt das Label automatisch hinzuzufügen.

Verringere conf, um mehr Verdachtsfälle zu erfassen, und verwende dein eigenes erstes feinabgestimmtes Modell, sobald du eines hast, da es deine Klassen kennt.

Datensatzdiagramme lesen#

Registerkarte Charts der Ultralytics Platform für den Datensatz mit medizinischen Pillen: Split Distribution zeigt 80 Trainings- und 20 Validierungsbilder, und Top Classes zeigt „Pill“ mit 1.755 Annotationen und „Tablet“ mit 0

Auf der Ultralytics Platform stellt die Registerkarte Charts des Datensatzes die Verteilung der Aufteilungen, Klassenanzahlen und Box-Statistiken dar (Datensatzdokumentation). Dieser Screenshot zeigt bereits einen Befund der Qualitätskontrolle: Die Klassenliste enthält zwei Klassen, aber tablet hat 0 von 1.755 Annotationen. Eine Klasse ohne Labels lässt sich nicht erlernen. Beschrifte sie oder entferne sie vor dem Training aus der Klassenliste.

Klassenungleichgewicht in der richtigen Reihenfolge beheben#

   forklift  ████████████████████████ 8000 instances
   person    ████████████████ 5400
   pallet    ██ 700                              ← red flag

Eine solche Lücke führt zu einem Modell, das Gabelstapler sicher erkennt, aber Paletten übersieht. Drei Lösungen, in dieser Reihenfolge:

  1. Mehr Daten der unterrepräsentierten Klasse sammeln und annotieren. Das ist immer die beste Lösung: Strebe mindestens 1.500 Bilder und mindestens 10.000 Instanzen für jede Klasse an.
  2. Nur im Training überabtasten. Führe Bilder, die die unterrepräsentierte Klasse enthalten, mehr als einmal in der Trainingsdateiliste auf, damit sie in jeder Epoche häufiger vorkommen. Verwende keine Kopien in der Validierung und im Test.
  3. Die Verlustfunktion gewichten. Das Trainingsargument cls_pw (standardmäßig 0,0, bis zu 1,0 für eine vollständige Gewichtung nach inverser Häufigkeit) erhöht das Gewicht des Klassifikationsverlusts für seltene Klassen. Verwende es erst nach den Schritten 1 und 2.
Schwierige Bilder niemals löschen, nur um den Wert zu erhöhen

Wenn du unscharfe, überfüllte oder verwirrende Bilder aus der Validierung entfernst, steigt der mAP und das Modell wird nicht schlechter – nur die Auswertung weniger ehrlich. Ist ein Bild eine echte Aufnahme aus dem Einsatz, bleibt es im Satz; korrigiere stattdessen seine Kennzeichnungen. Entferne nur echte Ausreißer (Fehler 9) und exakte Duplikate.

QC-Notizen verfassen#

Ein paar Zeilen reichen und beantworten Monate später die Frage „Was hast du geprüft?“, wenn ein Kunde oder eine prüfende Person danach fragt:

Dataset: forklift-v1   Audit date: 2026-05-09
Images: 4,820   Labels: 18,442   Background: 380 (7.9%)
Audit script: 3 invalid class IDs fixed, 41 duplicate label rows removed
Suspect review: 62 flagged, 19 missing labels added, 43 model errors ignored
Visual sample: 120 images, 4 mislabeled (3.3%), fixed
Split check: grouped by camera-day, every class in every split
Imbalance: pallet 2.5x below person; collecting more next round
Ready to train: yes
Ausprobieren

Verweise qc_audit.py und suspect_labels.py auf deine eigene Daten-YAML-Datei (oder auf COCO8 und COCO128) und führe die Skripte aus. Behebe jedes gemeldete Problem, öffne jedes verdächtige Bild und entscheide, ob ein Label- oder Modellfehler vorliegt. Führe anschließend beide Skripte erneut aus. Ergebnis: qc-notes.md mit den Anzahlen vor und nach deinen Korrekturen.

Abgeschlossen, wenn
Du hast die Lektion abgeschlossen, wenn alle folgenden Punkte zutreffen.
  • Das Prüfskript meldet keine ungültigen Klassen, doppelten Labelzeilen oder im Trainingssatz fehlenden Klassen.

  • Jeder Verdachtsfall aus der modellgestützten Prüfung wurde geöffnet und als Labelkorrektur oder Modellfehler geklärt.

  • Die QC-Notizen halten fest, was geprüft, gefunden und korrigiert wurde.

Lösung anzeigen

QC-Notizen zu den zwei Demoläufen in dieser Lektion:

Dataset: COCO8 + COCO128 demo   Audit date: 2026-05-09
qc_audit.py (COCO8): 0 invalid classes, 0 duplicate rows, 0 duplicate images
  FINDING: dog, elephant, horse, person, umbrella appear in val only
  ACTION: re-split by group or collect these classes into train before training
suspect_labels.py (COCO128, conf 0.6): 4 suspects in 3 images
  000000000438.jpg: 2 donuts without matching labels -> relabel the donut box
  000000000531.jpg: "person" is the photographer's shadow -> model error, label kept
  000000000436.jpg: bottle -> open and decide
Ready to train: no, fix the val-only classes first

Beachte die letzte Zeile. Ein Datensatz mit einer Klasse, die nur in der Validierung vorkommt, besteht die Qualitätskontrolle nicht, selbst wenn jedes einzelne Label korrekt ist, denn die Kennzahl für diese Klasse misst etwas, das das Modell nie erlernen konnte.

Wie es weitergeht

Mit sauberen Labels teilt die nächste Lektion den Datensatz so auf, dass die Validierungswerte die spätere Anwendung widerspiegeln und nicht das Auswendiglernen.