Zum Inhalt springen

K-Fold Cross Validation mit Ultralytics

Einführung

Diese umfassende Anleitung veranschaulicht die Implementierung der K-Fold-Kreuzvalidierung für Objekterkennungs-Datensätze innerhalb des Ultralytics-Ökosystems. Wir nutzen das YOLO-Erkennungsformat und wichtige Python-Bibliotheken wie sklearn, pandas und PyYaml, um Sie durch die notwendige Einrichtung, den Prozess der Generierung von Feature-Vektoren und die Ausführung einer K-Fold-Datensatzaufteilung zu führen.

K-Fold Cross Validation Übersicht

Unabhängig davon, ob Ihr Projekt das Fruit Detection Dataset oder eine benutzerdefinierte Datenquelle umfasst, zielt dieses Tutorial darauf ab, Ihnen zu helfen, die K-Fold-Kreuzvalidierung zu verstehen und anzuwenden, um die Zuverlässigkeit und Robustheit Ihrer maschinelles Lernen Modelle übergeben werden. Während wir k=5 Falten für dieses Tutorial. Beachten Sie, dass die optimale Anzahl der Faltungen je nach Datensatz und den Besonderheiten Ihres Projekts variieren kann.

Ohne weiteres Zögern, legen wir los!

Einrichtung

  • Ihre Annotationen sollten im YOLO-Detektionsformat vorliegen.

  • Dieser Leitfaden setzt voraus, dass Annotationsdateien lokal verfügbar sind.

  • Für unsere Demonstration verwenden wir den Frucht-Erkennungsdatensatz.

    • Dieser Datensatz enthält insgesamt 8479 Bilder.
    • Es enthält 6 Klassenbezeichnungen, jeweils mit den unten aufgeführten Gesamtanzahlen der Instanzen.
Klassenbezeichnung Anzahl der Instanzen
Apple 7049
Weintrauben 7202
Ananas 1613
Orange 15549
Banane 3536
Wassermelone 1976
  • Notwendige python-Pakete umfassen:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • Dieses Tutorial arbeitet mit k=5 Faltungen. Sie sollten jedoch die beste Anzahl von Faltungen für Ihren spezifischen Datensatz ermitteln.

  • Initiieren Sie eine neue virtuelle Python-Umgebung (venv) für Ihr Projekt und aktivieren Sie es. Verwenden Sie pip (oder Ihren bevorzugten Paketmanager) zur Installation:

    • Die Ultralytics Bibliothek: pip install -U ultralytics. Alternativ können Sie das offizielle Repository klonen Repository.
    • Scikit-learn, pandas und PyYAML: pip install -U scikit-learn pandas pyyaml.
  • Stellen Sie sicher, dass Ihre Annotationen im YOLO-Erkennungsformat vorliegen.

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

Feature-Vektoren für Objekterkennungsdatensatz generieren

  1. Beginnen Sie mit dem Erstellen eines neuen example.py Python-Datei für die folgenden Schritte.

  2. Fahren Sie fort, alle Beschriftungsdateien für Ihren Datensatz abzurufen.

    from pathlib import Path
    
    dataset_path = Path("./Fruit-detection")  # replace with 'path/to/dataset' for your custom data
    labels = sorted(dataset_path.rglob("*labels/*.txt"))  # all data in 'labels'
    
  3. Lesen Sie nun den Inhalt der YAML-Datei des Datensatzes und extrahieren Sie die Indizes der Klassenbezeichnungen.

    import yaml
    
    yaml_file = "path/to/data.yaml"  # your data YAML with data directories and names dictionary
    with open(yaml_file, encoding="utf8") as y:
        classes = yaml.safe_load(y)["names"]
    cls_idx = sorted(classes.keys())
    
  4. Initialisieren Sie ein leeres pandas DataFrame.

    import pandas as pd
    
    index = [label.stem for label in labels]  # uses base filename as ID (no extension)
    labels_df = pd.DataFrame([], columns=cls_idx, index=index)
    
  5. Zählen Sie die Instanzen jeder Klassenbezeichnung, die in den Annotationsdateien vorhanden sind.

    from collections import Counter
    
    for label in labels:
        lbl_counter = Counter()
    
        with open(label) as lf:
            lines = lf.readlines()
    
        for line in lines:
            # classes for YOLO label uses integer at first position of each line
            lbl_counter[int(line.split(" ", 1)[0])] += 1
    
        labels_df.loc[label.stem] = lbl_counter
    
    labels_df = labels_df.fillna(0.0)  # replace `nan` values with `0.0`
    
  6. Das Folgende ist eine Beispielansicht des 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 indizieren die Beschriftungsdateien, die jeweils einem Bild in Ihrem Datensatz entsprechen, und die Spalten entsprechen Ihren Klassenbeschriftungsindizes. Jede Zeile stellt einen Pseudo-Feature-Vektor dar, mit der Anzahl jeder Klassenbeschriftung, die in Ihrem Datensatz vorhanden ist. Diese Datenstruktur ermöglicht die Anwendung von K-Fold Cross Validation auf einen Objekterkennungsdatensatz.

K-Fold Datensatzaufteilung

  1. Nun werden wir die KFold Klasse von sklearn.model_selection zum Generieren k Aufteilungen des Datensatzes.

    • Wichtig:
      • Einstellung shuffle=True gewährleistet eine randomisierte Verteilung der Klassen in Ihren Aufteilungen.
      • Durch Setzen von random_state=M wobei M ist eine gewählte ganze Zahl, mit der Sie wiederholbare Ergebnisse erzielen können.
    import random
    
    from sklearn.model_selection import KFold
    
    random.seed(0)  # for reproducibility
    ksplit = 5
    kf = KFold(n_splits=ksplit, shuffle=True, random_state=20)  # setting random_state for repeatable results
    
    kfolds = list(kf.split(labels_df))
    
  2. Der Datensatz wurde nun aufgeteilt in k Faltungen, jede mit einer Liste von train und val Indizes. Wir werden ein DataFrame erstellen, um diese Ergebnisse übersichtlicher darzustellen.

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

    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
    

    Das ideale Szenario ist, wenn alle Klassenverhältnisse für jeden Split und über alle Klassen hinweg einigermaßen ähnlich sind. Dies hängt jedoch von den Besonderheiten Ihres Datensatzes ab.

  4. Als Nächstes erstellen wir die Verzeichnisse und Dataset-YAML-Dateien für jeden Split.

    import datetime
    
    supported_extensions = [".jpg", ".jpeg", ".png"]
    
    # Initialize an empty list to store image file paths
    images = []
    
    # Loop through supported extensions and gather image files
    for ext in supported_extensions:
        images.extend(sorted((dataset_path / "images").rglob(f"*{ext}")))
    
    # Create the necessary directories and dataset YAML files
    save_path = Path(dataset_path / f"{datetime.date.today().isoformat()}_{ksplit}-Fold_Cross-val")
    save_path.mkdir(parents=True, exist_ok=True)
    ds_yamls = []
    
    for split in folds_df.columns:
        # Create directories
        split_dir = save_path / split
        split_dir.mkdir(parents=True, exist_ok=True)
        (split_dir / "train" / "images").mkdir(parents=True, exist_ok=True)
        (split_dir / "train" / "labels").mkdir(parents=True, exist_ok=True)
        (split_dir / "val" / "images").mkdir(parents=True, exist_ok=True)
        (split_dir / "val" / "labels").mkdir(parents=True, exist_ok=True)
    
        # Create dataset YAML files
        dataset_yaml = split_dir / f"{split}_dataset.yaml"
        ds_yamls.append(dataset_yaml)
    
        with open(dataset_yaml, "w") as ds_y:
            yaml.safe_dump(
                {
                    "path": split_dir.as_posix(),
                    "train": "train",
                    "val": "val",
                    "names": classes,
                },
                ds_y,
            )
    
  5. Kopieren Sie abschließend Bilder und Beschriftungen in das jeweilige Verzeichnis ('train' oder 'val') für jeden Split.

    • HINWEIS: Die für diesen Teil des Codes benötigte Zeit variiert je nach Größe Ihres Datensatzes und Ihrer Systemhardware.
    import shutil
    
    from tqdm import tqdm
    
    for image, label in tqdm(zip(images, labels), total=len(images), desc="Copying files"):
        for split, k_split in folds_df.loc[image.stem].items():
            # Destination directory
            img_to_path = save_path / split / k_split / "images"
            lbl_to_path = save_path / split / k_split / "labels"
    
            # Copy image and label files to new directory (SamefileError if file already exists)
            shutil.copy(image, img_to_path / image.name)
            shutil.copy(label, lbl_to_path / label.name)
    

Datensätze speichern (optional)

Optional können Sie die Aufzeichnungen der K-Fold-Aufteilung und der Labelverteilungs-DataFrames als CSV-Dateien für zukünftige Referenzzwecke speichern.

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

YOLO-Training mit K-Fold-Datensplits

  1. Laden Sie zuerst das YOLO-Modell.

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"  # use yolo11n.pt for a small model
    model = YOLO(weights_path, task="detect")
    
  2. Als Nächstes iterieren Sie über die Dataset-YAML-Dateien, um das Training auszuführen. Die Ergebnisse werden in einem Verzeichnis gespeichert, das durch die project und name Argumente. Standardmäßig ist dieses Verzeichnis 'runs/detect/train#', wobei # eine ganzzahlige Indexnummer ist.

    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. Sie können auch die Funktion Ultralytics data.utils.autosplit für die automatische Aufteilung von Datensätzen verwenden:

    from ultralytics.data.utils import autosplit
    
    # Automatically split dataset into train/val/test
    autosplit(path="path/to/images", weights=(0.8, 0.2, 0.0), annotated_only=True)
    

Fazit

In dieser Anleitung haben wir den Prozess der Verwendung von K-Fold-Kreuzvalidierung für das Training des YOLO-Objekterkennungsmodells untersucht. Wir haben gelernt, wie wir unseren Datensatz in K Partitionen aufteilen und dabei eine ausgewogene Klassenverteilung über die verschiedenen Faltungen hinweg sicherstellen.

Wir haben auch das Verfahren zur Erstellung von Report-DataFrames untersucht, um die Datenaufteilungen und Label-Verteilungen über diese Aufteilungen hinweg zu visualisieren, was uns einen klaren Einblick in die Struktur unserer Trainings- und Validierungssätze gibt.

Optional haben wir unsere Aufzeichnungen für zukünftige Referenzzwecke gespeichert, was besonders in großen Projekten oder bei der Fehlerbehebung der Modellleistung nützlich sein könnte.

Schließlich haben wir das eigentliche Modelltraining unter Verwendung jedes Splits in einer Schleife implementiert und unsere Trainingsergebnisse für weitere Analysen und Vergleiche gespeichert.

Diese Technik der K-Fold-Kreuzvalidierung ist eine robuste Methode, um das Beste aus Ihren verfügbaren Daten herauszuholen, und sie trägt dazu bei, sicherzustellen, dass Ihre Modellleistung über verschiedene Datenteilmengen hinweg zuverlässig und konsistent ist. Dies führt zu einem generalisierbareren und zuverlässigeren Modell, bei dem die Wahrscheinlichkeit geringer ist, dass es zu Overfitting bei bestimmten Datenmustern kommt.

Denken Sie daran, dass diese Schritte zwar hauptsächlich auf andere Modelle des maschinellen Lernens übertragbar sind, obwohl wir in diesem Leitfaden YOLO verwendet haben. Das Verständnis dieser Schritte ermöglicht es Ihnen, die Kreuzvalidierung effektiv in Ihren eigenen Projekten des maschinellen Lernens anzuwenden. Viel Spaß beim Programmieren!

FAQ

Was ist K-Fold-Kreuzvalidierung und warum ist sie in der Objekterkennung nützlich?

K-Fold Cross Validation ist eine Technik, bei der der Datensatz in 'k' Teilmengen (Folds) unterteilt wird, um die Modellleistung zuverlässiger zu bewerten. Jeder Fold dient sowohl als Trainings- als auch als Validierungsdatensatz. Im Kontext der Objekterkennung trägt die Verwendung von K-Fold Cross Validation dazu bei, dass die Leistung Ihres Ultralytics YOLO-Modells robust und über verschiedene Datensplits hinweg generalisierbar ist, was seine Zuverlässigkeit erhöht. Detaillierte Anweisungen zur Einrichtung der K-Fold Cross Validation mit Ultralytics YOLO finden Sie unter K-Fold Cross Validation mit Ultralytics.

Wie implementiere ich K-Fold-Kreuzvalidierung mit Ultralytics YOLO?

Um K-Fold Cross Validation mit Ultralytics YOLO zu implementieren, musst du folgende Schritte ausführen:

  1. Überprüfen Sie, ob die Annotationen im YOLO-Erkennungsformat vorliegen.
  2. Verwenden Sie Python-Bibliotheken wie sklearn, pandasund pyyaml.
  3. Erstellen Sie Feature-Vektoren aus Ihrem Datensatz.
  4. Teilen Sie Ihren Datensatz mit KFold von sklearn.model_selection.
  5. Trainieren Sie das YOLO-Modell auf jedem Split.

Eine umfassende Anleitung finden Sie im Abschnitt K-Fold Datensatzaufteilung in unserer Dokumentation.

Warum sollte ich Ultralytics YOLO für die Objekterkennung verwenden?

Ultralytics YOLO bietet modernste Objekterkennung in Echtzeit mit hoher Genauigkeit und Effizienz. Es ist vielseitig und unterstützt mehrere Computer Vision-Aufgaben wie Erkennung, Segmentierung und Klassifizierung. Darüber hinaus lässt es sich nahtlos in Tools wie Ultralytics HUB für No-Code-Modelltraining und -bereitstellung integrieren. Weitere Informationen finden Sie auf unserer Ultralytics YOLO Seite, wo Sie die Vorteile und Funktionen erkunden können.

Wie kann ich sicherstellen, dass meine Annotationen das korrekte Format für Ultralytics YOLO haben?

Ihre Annotationen sollten dem YOLO-Detektionsformat entsprechen. Jede Annotationsdatei muss die Objektklasse zusammen mit ihren Begrenzungsrahmen-Koordinaten im Bild auflisten. Das YOLO-Format gewährleistet eine optimierte und standardisierte Datenverarbeitung für das Training von Objekterkennungsmodellen. Weitere Informationen zur korrekten Formatierung von Annotationen finden Sie im YOLO-Detektionsformat-Leitfaden.

Kann ich K-Fold-Kreuzvalidierung mit anderen benutzerdefinierten Datensätzen als der Fruchterkennung verwenden?

Ja, Sie können K-Fold-Kreuzvalidierung mit jedem benutzerdefinierten Datensatz verwenden, solange sich die Anmerkungen im YOLO-Erkennungsformat befinden. Ersetzen Sie die Datensatzpfade und Klassenbezeichnungen durch die für Ihren benutzerdefinierten Datensatz spezifischen. Diese Flexibilität stellt sicher, dass jedes Objekterkennungsprojekt von einer robusten Modellbewertung mithilfe der K-Fold-Kreuzvalidierung profitieren kann. Ein praktisches Beispiel finden Sie in unserem Abschnitt Generieren von Feature-Vektoren.



📅 Erstellt vor 1 Jahr ✏️ Aktualisiert vor 2 Monaten

Kommentare