Ultralytics YOLO27:
Get Started

K-fache Kreuzvalidierung mit Ultralytics#

Einführung#

Diese umfassende Anleitung zeigt, wie du die k-fache Kreuzvalidierung für Objekterkennungs-Datensätze im Ultralytics-Ökosystem implementierst. Wir verwenden das YOLO-Erkennungsformat und wichtige Python-Bibliotheken wie sklearn, pandas und PyYAML und führen dich durch die erforderliche Einrichtung, die Erstellung von Merkmalsvektoren und die Aufteilung eines Datensatzes in k Folds.

K-fold cross validation data splitting

Ob du mit dem Fruit Detection-Datensatz oder einer eigenen Datenquelle arbeitest: Dieses Tutorial soll dir helfen, die k-fache Kreuzvalidierung zu verstehen und anzuwenden, um die Zuverlässigkeit und Robustheit deiner Machine-Learning-Modelle zu erhöhen. Für dieses Tutorial verwenden wir k=5 Folds. Beachte jedoch, dass die optimale Anzahl an Folds je nach Datensatz und Projektanforderungen variieren kann. Die k-fache Kreuzvalidierung bietet den größten Nutzen, wenn dein Datensatz klein, verrauscht oder sehr variabel ist. Bei großen, vielfältigen Datensätzen reicht eine gut zusammengestellte Aufteilung in Trainings-, Validierungs- und Testdaten in der Regel aus.

Lass uns anfangen.

Einrichtung#

  • Deine Annotationen sollten im YOLO-Erkennungsformat vorliegen.

  • Diese Anleitung setzt voraus, dass die Annotationsdateien lokal verfügbar sind.

  • Für unsere Demonstration verwenden wir den Datensatz Fruit Detection.

    • Dieser Datensatz umfasst insgesamt 8479 Bilder.
    • Er enthält 6 Klassenbezeichnungen. Die Gesamtzahl der Instanzen pro Klasse ist unten aufgeführt.
KlassenbezeichnungAnzahl der Instanzen
Apfel7049
Trauben7202
Ananas1613
Orange15549
Banane3536
Wassermelone1976
  • Zu den benötigten Python-Paketen gehören:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • In diesem Tutorial verwenden wir k=5 Folds. Du solltest jedoch die für deinen konkreten Datensatz am besten geeignete Anzahl an Folds ermitteln.

  1. Erstelle für dein Projekt eine neue virtuelle Python-Umgebung (venv) und aktiviere sie. Installiere die folgenden Pakete mit pip (oder deinem bevorzugten Paketmanager):

    • Die Ultralytics-Bibliothek: pip install -U ultralytics. Alternativ kannst du das offizielle Repository klonen.
    • Scikit-learn, pandas und PyYAML: pip install -U scikit-learn pandas pyyaml.
  2. Vergewissere dich, dass deine Annotationen im YOLO-Erkennungsformat vorliegen.

    • Für dieses Tutorial befinden sich alle Annotationsdateien im Verzeichnis Fruit-Detection/labels.

Merkmalsvektoren für einen Objekterkennungsdatensatz erstellen#

  1. Erstelle zunächst eine neue Python-Datei namens example.py, um die folgenden Schritte auszuführen.

  2. Lade die konfigurierten Trainings- und Validierungsbilder über den Datensatz-Owner des Pakets. Lass dabei die Testaufteilung unangetastet.

    from pathlib import Path
    
    from ultralytics.data.dataset import YOLODataset
    from ultralytics.data.utils import check_det_dataset
    
    yaml_file = "path/to/data.yaml"
    data = check_det_dataset(yaml_file)
    dataset_path = Path(data["path"])
    sources = []
    for split in ("train", "val"):
        source = data.get(split)
        if source:
            sources.extend(source if isinstance(source, list) else [source])
    dataset = YOLODataset(sources, data=data, augment=False)
    images = [Path(p) for p in dataset.im_files]
  3. Lies nun den Inhalt der YAML-Datei des Datensatzes aus und extrahiere die Indizes der Klassenbezeichnungen.

    classes = data["names"]
    cls_idx = sorted(classes.keys())
  4. Initialisiere einen leeren pandas-DataFrame.

    import pandas as pd
    
    labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images)
  5. Zähle, wie oft jede Klassenbezeichnung in den Annotationsdateien vorkommt.

    from collections import Counter
    
    for image, label in zip(images, dataset.labels):
        lbl_counter = Counter(label["cls"].flatten().astype(int))
        labels_df.loc[image, list(lbl_counter)] = list(lbl_counter.values())
  6. Die folgende Ansicht zeigt beispielhaft den ausgefüllten DataFrame:

                                                           0    1    2    3    4    5
    '0000a16e4b057580_jpg.rf.00ab48988370f64f5ca8ea4...'  0.0  0.0  0.0  0.0  0.0  7.0
    '0000a16e4b057580_jpg.rf.7e6dce029fb67f01eb19aa7...'  0.0  0.0  0.0  0.0  0.0  7.0
    '0000a16e4b057580_jpg.rf.bc4d31cdcbe229dd022957a...'  0.0  0.0  0.0  0.0  0.0  7.0
    '00020ebf74c4881c_jpg.rf.508192a0a97aa6c4a3b6882...'  0.0  0.0  0.0  1.0  0.0  0.0
    '00020ebf74c4881c_jpg.rf.5af192a2254c8ecc4188a25...'  0.0  0.0  0.0  1.0  0.0  0.0
     ...                                                  ...  ...  ...  ...  ...  ...
    'ff4cd45896de38be_jpg.rf.c4b5e967ca10c7ced3b9e97...'  0.0  0.0  0.0  0.0  0.0  2.0
    'ff4cd45896de38be_jpg.rf.ea4c1d37d2884b3e3cbce08...'  0.0  0.0  0.0  0.0  0.0  2.0
    'ff5fd9c3c624b7dc_jpg.rf.bb519feaa36fc4bf630a033...'  1.0  0.0  0.0  0.0  0.0  0.0
    'ff5fd9c3c624b7dc_jpg.rf.f0751c9c3aa4519ea3c9d6a...'  1.0  0.0  0.0  0.0  0.0  0.0
    'fffe28b31f2a70d4_jpg.rf.7ea16bd637ba0711c53b540...'  0.0  6.0  0.0  0.0  0.0  0.0

Die Zeilen enthalten absolute Bildpfade, die Spalten entsprechen den Indizes der Klassenbezeichnungen. Fehlende Bezeichnungen ergeben Hintergrundzeilen mit ausschließlich Nullen. Diese Datenstruktur ermöglicht die Anwendung der k-fachen Kreuzvalidierung auf einen Objekterkennungsdatensatz.

Aufteilung des Datensatzes in k Folds#

  1. Nun verwenden wir die Klasse KFold aus sklearn.model_selection, um den Datensatz in k Folds aufzuteilen.

    • Wichtig:
      • Mit shuffle=True stellst du sicher, dass die Klassen zufällig auf deine Folds verteilt werden.
      • Wenn du random_state=M auf einen ausgewählten ganzzahligen Wert für M setzt, erhältst du wiederholbare Ergebnisse.
    from sklearn.model_selection import KFold
    
    ksplit = 5
    kf = KFold(n_splits=ksplit, shuffle=True, random_state=20)  # random_state für wiederholbare Ergebnisse festlegen
    
    kfolds = list(kf.split(labels_df))
  2. Der Datensatz ist nun in k Folds aufgeteilt. Jeder Fold enthält eine Liste mit Indizes für train und val. Wir erstellen einen DataFrame, um diese Ergebnisse übersichtlicher darzustellen.

    folds = [f"split_{n}" for n in range(1, ksplit + 1)]
    folds_df = pd.DataFrame(index=labels_df.index, columns=folds)
    
    for i, (train, val) in enumerate(kfolds, start=1):
        folds_df.loc[labels_df.index[train], f"split_{i}"] = "train"
        folds_df.loc[labels_df.index[val], f"split_{i}"] = "val"
  3. Nun berechnen wir für jeden Fold die Verteilung der Klassenbezeichnungen als Verhältnis der in val vorhandenen Klassen zu den in train vorhandenen Klassen.

    fold_lbl_distrb = pd.DataFrame(index=folds, columns=cls_idx)
    
    for n, (train_indices, val_indices) in enumerate(kfolds, start=1):
        train_totals = labels_df.iloc[train_indices].sum()
        val_totals = labels_df.iloc[val_indices].sum()
    
        # To avoid division by zero, we add a small value (1E-7) to the denominator
        ratio = val_totals / (train_totals + 1e-7)
        fold_lbl_distrb.loc[f"split_{n}"] = ratio

    Im Idealfall sind die Klassenverhältnisse in jeder Aufteilung und über alle Klassen hinweg möglichst ähnlich. Was sich erreichen lässt, hängt jedoch von den Besonderheiten deines Datensatzes ab.

  4. Schreibe für jede Aufteilung Bildlisten und eine YAML-Datei des Datensatzes. Textlisten vermeiden, dass der Datensatz k-mal kopiert werden muss.

    import yaml
    
    save_path = dataset_path.parent / f"{ksplit}-Fold_Cross-val"
    save_path.mkdir(parents=True, exist_ok=True)
    ds_yamls = []
    
    for split in folds_df.columns:
        for partition in ("train", "val"):
            split_images = folds_df.index[folds_df[split] == partition]
            paths = "\n".join(map(str, split_images))
            (save_path / f"{split}_{partition}.txt").write_text(f"{paths}\n")
    
        dataset_yaml = save_path / f"{split}.yaml"
        ds_yamls.append(dataset_yaml)
        with open(dataset_yaml, "w") as ds_y:
            yaml.safe_dump(
                {
                    "path": save_path.as_posix(),
                    "train": f"{split}_train.txt",
                    "val": f"{split}_val.txt",
                    "names": classes,
                },
                ds_y,
            )

Datensätze speichern (optional)#

Optional kannst du die Datensätze zur Aufteilung in k Folds und zur Verteilung der Bezeichnungen als CSV-Dateien speichern, um später darauf zurückzugreifen.

folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")

YOLO mit k-fachen Datenaufteilungen trainieren#

  1. Lade zunächst das YOLO-Modell.

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"  # für ein kleines Modell yolo26n.pt verwenden
    model = YOLO(weights_path, task="detect")
  2. Durchlaufe anschließend die YAML-Dateien der Datensätze, um das Training auszuführen. Die Ergebnisse werden in einem Verzeichnis gespeichert, das mit den Argumenten project und name angegeben wird. Standardmäßig ist dies das Verzeichnis 'runs/detect/train#', wobei # für einen ganzzahligen Index steht.

    results = {}
    
    # Define your additional arguments here
    batch = 16
    project = "kfold_demo"
    epochs = 100
    
    for k, dataset_yaml in enumerate(ds_yamls):
        model = YOLO(weights_path, task="detect")
        results[k] = model.train(
            data=dataset_yaml, epochs=epochs, batch=batch, project=project, name=f"fold_{k + 1}"
        )  # include any additional train arguments
  3. Du kannst auch die Funktion Ultralytics data.split.autosplit verwenden, um den Datensatz automatisch aufzuteilen:

    from ultralytics.data.split import autosplit
    
    # Datensatz automatisch in Trainings-, Validierungs- und Testdaten aufteilen
    autosplit(path="path/to/images", weights=(0.8, 0.2, 0.0), annotated_only=True)

Fazit#

In dieser Anleitung haben wir untersucht, wie du die k-fache Kreuzvalidierung zum Trainieren eines YOLO-Objekterkennungsmodells einsetzt. Wir haben gelernt, den Pool aus Trainings- und Validierungsdaten in K Partitionen aufzuteilen und anhand der erzeugten Verhältnistabelle die Klassenverteilung nach der zufälligen Aufteilung zu überprüfen.

Außerdem haben wir uns angesehen, wie du DataFrames für Berichte erstellst, um die Datenaufteilungen und die Verteilung der Bezeichnungen über diese Aufteilungen hinweg zu visualisieren. So erhältst du einen klaren Überblick über den Aufbau deiner Trainings- und Validierungsdatensätze.

Optional haben wir unsere Datensätze für spätere Zwecke gespeichert. Das kann besonders bei umfangreichen Projekten oder bei der Fehlersuche im Zusammenhang mit der Modellleistung hilfreich sein.

Zum Schluss haben wir das Modell tatsächlich trainiert, indem wir in einer Schleife jede Aufteilung verwendet und die Trainingsergebnisse für weitere Analysen und Vergleiche gespeichert haben.

Die k-fache Kreuzvalidierung ist eine robuste Methode, um deine verfügbaren Daten bestmöglich zu nutzen. Sie trägt dazu bei, eine zuverlässige und konsistente Modellleistung über verschiedene Teilmengen der Daten hinweg sicherzustellen. Das Ergebnis ist ein besser verallgemeinerbares und zuverlässigeres Modell, das weniger dazu neigt, sich an bestimmte Datenmuster übermäßig anzupassen.

Denk daran: Auch wenn wir in dieser Anleitung YOLO verwendet haben, lassen sich diese Schritte größtenteils auf andere Machine-Learning-Modelle übertragen. Wenn du sie verstehst, kannst du die Kreuzvalidierung auch in deinen eigenen Machine-Learning-Projekten effektiv einsetzen.

Häufig gestellte Fragen#

  • Bei der k-fachen Kreuzvalidierung wird der Datensatz in „k“ Teilmengen (Folds) aufgeteilt, um die Modellleistung zuverlässiger zu bewerten. Jeder Fold dient sowohl als Trainings- als auch als Validierungsdatensatz. Bei der Objekterkennung trägt die k-fache Kreuzvalidierung dazu bei, die Robustheit und Verallgemeinerbarkeit deines Ultralytics-YOLO-Modells über verschiedene Datenaufteilungen hinweg sicherzustellen und so seine Zuverlässigkeit zu erhöhen. Eine ausführliche Anleitung zur Einrichtung der k-fachen Kreuzvalidierung mit Ultralytics YOLO findest du unter K-fache Kreuzvalidierung mit Ultralytics.

  • Gehe wie folgt vor, um die k-fache Kreuzvalidierung mit Ultralytics YOLO zu implementieren:

    1. Vergewissere dich, dass die Annotationen im YOLO-Erkennungsformat vorliegen.
    2. Verwende Python-Bibliotheken wie sklearn, pandas und pyyaml.
    3. Erstelle Merkmalsvektoren aus deinem Datensatz.
    4. Teile deinen Datensatz mit KFold aus sklearn.model_selection auf.
    5. Trainiere das YOLO-Modell auf jeder Aufteilung.

    Eine ausführliche Anleitung findest du im Abschnitt Datensatz in k Folds aufteilen unserer Dokumentation.

  • Der Arbeitsablauf in dieser Anleitung ist auf das YOLO-Erkennungsformat ausgerichtet, lässt sich aber auf alle YOLO-Aufgaben übertragen – die Aufgabe beeinflusst, wie du die Folds zusammenstellst, nicht, ob die Kreuzvalidierung hilfreich ist:

    AufgabeGestaltung der Folds
    detectTeile die Daten auf Bildebene auf und gleiche die Verteilung von Objekten und Klassen über die Folds hinweg aus. Zusammengehörige Bilder (etwa vom selben Patienten, aus derselben Videosequenz, von derselben Kamera oder vom selben Standort) sollten im selben Fold bleiben.
    segmentVerwende dieselbe Strategie auf Bildebene wie bei der Erkennung und achte zusätzlich darauf, dass Masken und Klassen in jedem Fold abgedeckt sind.
    classifyBevorzuge stratifizierte Folds, damit die Klassenhäufigkeiten in Trainings- und Validierungsdaten ausgewogen bleiben.
    poseTeile die Daten nach Person oder Sequenz auf, damit dieselbe Person oder dasselbe Tier nicht auf beiden Seiten eines Folds auftaucht.
    obbTeile die Daten auf Bildebene auf und halte Kacheln oder Ausschnitte derselben Szene zusammen – das ist besonders bei Luftbildern wichtig.

    Halte unabhängig von der Aufgabe nahezu identische und zusammengehörige Beispiele aus gegenüberliegenden Folds heraus: Diese Art von Datenleckage lässt Validierungsmetriken deutlich besser aussehen, als die Leistung des Modells im Produktiveinsatz tatsächlich sein wird.

  • Ultralytics YOLO ermöglicht modernste Objekterkennung in Echtzeit mit hoher Genauigkeit und Effizienz. Das vielseitige Modell unterstützt verschiedene Aufgaben im Bereich Computer Vision, darunter Erkennung, Instanzsegmentierung, semantische Segmentierung und Klassifizierung. Außerdem lässt es sich nahtlos in Tools wie die Ultralytics Platform integrieren, mit denen du Modelle ohne Programmierung trainieren und bereitstellen kannst. Weitere Informationen zu den Vorteilen und Funktionen findest du auf unserer Ultralytics-YOLO-Seite.

  • Deine Annotationen sollten dem YOLO-Erkennungsformat entsprechen. Jede Annotationsdatei muss die Objektklasse sowie die Koordinaten der zugehörigen Bounding Box im Bild enthalten. Das YOLO-Format ermöglicht eine optimierte und standardisierte Datenverarbeitung für das Training von Objekterkennungsmodellen. Weitere Informationen zur korrekten Formatierung von Annotationen findest du in der Anleitung zum YOLO-Erkennungsformat.

  • Ja, du kannst die k-fache Kreuzvalidierung mit jedem benutzerdefinierten Datensatz verwenden, solange die Annotationen im YOLO-Erkennungsformat vorliegen. Ersetze die Datensatzpfade und Klassenbezeichnungen durch die Angaben für deinen eigenen Datensatz. Dank dieser Flexibilität kann jedes Objekterkennungsprojekt von einer robusten Modellbewertung mit k-facher Kreuzvalidierung profitieren. Ein praktisches Beispiel findest du im Abschnitt Merkmalsvektoren erstellen.

Kommentare