Overslaan naar inhoud

K-voudige kruisvalidatie met Ultralytics

Inleiding

Deze uitgebreide handleiding illustreert de implementatie van K-Fold Cross Validation voor objectdetectie datasets binnen het Ultralytics ecosysteem. We maken gebruik van het YOLO detectieformaat en de belangrijkste Python bibliotheken zoals sklearn, pandas en PyYaml om je door de noodzakelijke instellingen, het proces van het genereren van kenmerkvectoren en de uitvoering van een K-Fold dataset splitsing te leiden.

Overzicht K-voudige kruisvalidatie

Of je project nu de Fruit Detection dataset of een aangepaste databron bevat, deze tutorial helpt je bij het begrijpen en toepassen van K-Fold Cross Validation om de betrouwbaarheid en robuustheid van je machine learning modellen te versterken. Terwijl we k=5 Houd er rekening mee dat het optimale aantal vouwen voor deze tutorial kan variƫren, afhankelijk van je dataset en de specifieke kenmerken van je project.

Zonder verder oponthoud, laten we erin duiken!

Setup

  • Je annotaties moeten het formaatYOLO detectie hebben.

  • Deze handleiding gaat ervan uit dat annotatiebestanden lokaal beschikbaar zijn.

  • Voor onze demonstratie gebruiken we de Fruit Detection dataset.

    • Deze dataset bevat in totaal 8479 afbeeldingen.
    • Het bevat 6 klassenlabels, elk met zijn totale aantal instanties hieronder vermeld.
Klasse-etiket Aantal instanties
Appel 7049
Druiven 7202
Ananas 1613
Oranje 15549
Banaan 3536
Watermeloen 1976
  • Noodzakelijke Python pakketten zijn onder andere:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • Deze tutorial werkt met k=5 vouwen. Je moet echter het beste aantal vouwen bepalen voor je specifieke dataset.

  • Start een nieuwe Python virtuele omgeving (venv) voor je project en activeer het. Gebruik pip (of de pakketbeheerder van je voorkeur) om te installeren:

    • De Ultralytics bibliotheek: pip install -U ultralytics. Je kunt ook de officiĆ«le repo.
    • Scikit-learn, pandas en PyYAML: pip install -U scikit-learn pandas pyyaml.
  • Controleer of je annotaties het formaatYOLO detectie hebben.

    • Voor deze tutorial zijn alle annotatiebestanden te vinden in de Fruit-Detection/labels map.

Genereren van objectvectoren voor objectdetectiedataset

  1. Begin met het maken van een nieuwe example.py Python bestand voor de onderstaande stappen.

  2. Ga verder met het ophalen van alle labelbestanden voor je dataset.

    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. Lees nu de inhoud van het YAML-bestand van de dataset en extraheer de indices van de klassenlabels.

    yaml_file = "path/to/data.yaml"  # your data YAML with data directories and names dictionary
    with open(yaml_file, "r", encoding="utf8") as y:
        classes = yaml.safe_load(y)["names"]
    cls_idx = sorted(classes.keys())
    
  4. Een lege pandas Dataframe.

    import pandas as pd
    
    indx = [l.stem for l in labels]  # uses base filename as ID (no extension)
    labels_df = pd.DataFrame([], columns=cls_idx, index=indx)
    
  5. Tel de instanties van elke klasse-label in de annotatiebestanden.

    from collections import Counter
    
    for label in labels:
        lbl_counter = Counter()
    
        with open(label, "r") as lf:
            lines = lf.readlines()
    
        for l in lines:
            # classes for YOLO label uses integer at first position of each line
            lbl_counter[int(l.split(" ")[0])] += 1
    
        labels_df.loc[label.stem] = lbl_counter
    
    labels_df = labels_df.fillna(0.0)  # replace `nan` values with `0.0`
    
  6. Het volgende is een voorbeeldweergave van het bevolkte 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
    

De rijen indexeren de labelbestanden, die elk corresponderen met een afbeelding in je dataset, en de kolommen corresponderen met je klasse-label indices. Elke rij vertegenwoordigt een pseudo-kenmerkvector, met de telling van elk klasse-label aanwezig in je dataset. Deze gegevensstructuur maakt het mogelijk om K-Fold kruisvalidatie toe te passen op een dataset voor objectdetectie.

K-voudige dataset opsplitsing

  1. Nu zullen we de KFold klasse van sklearn.model_selection om k splitsingen van de dataset.

    • Belangrijk:
      • Instellen shuffle=True zorgt voor een willekeurige verdeling van klassen in je splitsingen.
      • Door random_state=M waarbij M een gekozen geheel getal is, kun je herhaalbare resultaten krijgen.
    from sklearn.model_selection import KFold
    
    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. De dataset is nu opgesplitst in k vouwen, elk met een lijst van train en val indexen. We zullen een DataFrame maken om deze resultaten duidelijker weer te geven.

    folds = [f"split_{n}" for n in range(1, ksplit + 1)]
    folds_df = pd.DataFrame(index=indx, columns=folds)
    
    for idx, (train, val) in enumerate(kfolds, start=1):
        folds_df[f"split_{idx}"].loc[labels_df.iloc[train].index] = "train"
        folds_df[f"split_{idx}"].loc[labels_df.iloc[val].index] = "val"
    
  3. Nu berekenen we de verdeling van klassenlabels voor elke vouw als een verhouding van de klassen die aanwezig zijn in val aan de aanwezigen 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
    

    Het ideale scenario is dat alle klassenratio's redelijk gelijk zijn voor elke splitsing en tussen klassen. Dit is echter afhankelijk van de specifieke kenmerken van je dataset.

  4. Vervolgens maken we de mappen en dataset YAML-bestanden aan voor elke splitsing.

    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 (unchanged)
    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. Kopieer tenslotte de afbeeldingen en labels naar de respectievelijke map ('train' of 'val') voor elke splitsing.

    • OPMERKING: De tijd die nodig is voor dit deel van de code hangt af van de grootte van je dataset en je systeemhardware.
    import shutil
    
    for image, label in zip(images, labels):
        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)
    

Records opslaan (optioneel)

Optioneel kun je de records van de K-Fold split en label distributie DataFrames opslaan als CSV-bestanden voor toekomstig gebruik.

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

Train YOLO met behulp van K-voudige gegevenssplitsingen

  1. Laad eerst het model YOLO .

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"
    model = YOLO(weights_path, task="detect")
    
  2. Doorloop vervolgens de YAML-bestanden van de dataset om de training uit te voeren. De resultaten worden opgeslagen in een map gespecificeerd door de project en name argumenten. Standaard is deze map 'exp/runs#' waarbij # een gehele index is.

    results = {}
    
    # Define your additional arguments here
    batch = 16
    project = "kfold_demo"
    epochs = 100
    
    for k in range(ksplit):
        dataset_yaml = ds_yamls[k]
        model.train(data=dataset_yaml, epochs=epochs, batch=batch, project=project)  # include any train arguments
        results[k] = model.metrics  # save output metrics for further analysis
    

Conclusie

In deze handleiding hebben we het proces onderzocht van het gebruik van K-voudige kruisvalidatie voor het trainen van het YOLO objectdetectiemodel. We hebben geleerd hoe we onze dataset in K partities kunnen verdelen, waarbij we zorgen voor een evenwichtige klassenverdeling over de verschillende vouwen.

We onderzochten ook de procedure voor het maken van rapport DataFrames om de datasplitsingen en labelverdelingen over deze splitsingen te visualiseren, waardoor we een duidelijk inzicht kregen in de structuur van onze trainings- en validatiesets.

Optioneel sloegen we onze records op voor toekomstig gebruik, wat vooral handig kan zijn bij grootschalige projecten of bij het oplossen van problemen met modelprestaties.

Tot slot implementeerden we de eigenlijke modeltraining met elke splitsing in een lus, waarbij we onze trainingsresultaten opsloegen voor verdere analyse en vergelijking.

Deze techniek van K-voudige kruisvalidatie is een robuuste manier om het beste uit je beschikbare gegevens te halen en het helpt om ervoor te zorgen dat de prestaties van je model betrouwbaar en consistent zijn over verschillende subsets van gegevens. Dit resulteert in een meer generaliseerbaar en betrouwbaar model dat minder snel te veel past bij specifieke gegevenspatronen.

Onthoud dat we in deze gids weliswaar YOLO hebben gebruikt, maar dat deze stappen meestal ook toepasbaar zijn op andere modellen voor machinaal leren. Als je deze stappen begrijpt, kun je cross-validation effectief toepassen in je eigen machine learning projecten. Veel plezier met coderen!

FAQ

Wat is K-Fold Cross Validation en waarom is het nuttig bij objectdetectie?

K-voudige kruisvalidatie is een techniek waarbij de dataset wordt verdeeld in 'k' subsets (vouwen) om de prestaties van het model betrouwbaarder te evalueren. Elke vouw dient als trainings- en validatiegegevens. In de context van objectdetectie helpt het gebruik van K-voudige kruisvalidatie om ervoor te zorgen dat de prestaties van je Ultralytics YOLO model robuust zijn en generaliseerbaar over verschillende gegevenssplitsingen, waardoor de betrouwbaarheid wordt verbeterd. Voor gedetailleerde instructies over het opzetten van K-Fold Cross Validation met Ultralytics YOLO , zie K-Fold Cross Validation met Ultralytics.

Hoe implementeer ik K-Fold Cross Validation met Ultralytics YOLO ?

Om K-Fold Cross Validation te implementeren met Ultralytics YOLO , moet je de volgende stappen volgen:

  1. Controleer of annotaties het formaatYOLO hebben.
  2. Gebruik Python bibliotheken zoals sklearn, pandasen pyyaml.
  3. Maak kenmerkvectoren van je dataset.
  4. Splits je dataset op met KFold van sklearn.model_selection.
  5. Train het YOLO model op elke splitsing.

Zie voor een uitgebreide handleiding de sectie K-Fold Dataset Splitsen in onze documentatie.

Waarom zou ik Ultralytics YOLO gebruiken voor objectdetectie?

Ultralytics YOLO biedt geavanceerde realtime objectdetectie met hoge nauwkeurigheid en efficiƫntie. Het is veelzijdig en ondersteunt meerdere computer vision taken zoals detectie, segmentatie en classificatie. Bovendien integreert het naadloos met tools zoals Ultralytics HUB voor het zonder code trainen en implementeren van modellen. Bekijk voor meer details de voordelen en functies op onze Ultralytics YOLO pagina.

Hoe kan ik ervoor zorgen dat mijn annotaties het juiste formaat hebben voor Ultralytics YOLO ?

Je annotaties moeten het YOLO detectieformaat volgen. Elk annotatiebestand moet de objectklasse vermelden, samen met de coƶrdinaten van de begrenzende box in de afbeelding. Het YOLO formaat zorgt voor gestroomlijnde en gestandaardiseerde gegevensverwerking voor het trainen van objectdetectiemodellen. Ga voor meer informatie over de juiste opmaak van annotaties naar de gids voor hetYOLO detectieformaat.

Kan ik K-Fold Cross Validation gebruiken met andere aangepaste datasets dan Fruit Detection?

Ja, je kunt K-Fold kruisvalidatie gebruiken met elke aangepaste dataset zolang de annotaties in het YOLO detectieformaat zijn. Vervang de paden en klassenlabels van de dataset door de paden en klassenlabels die specifiek zijn voor je eigen dataset. Deze flexibiliteit zorgt ervoor dat elk objectdetectieproject kan profiteren van robuuste modelevaluatie met K-Fold Cross Validation. Bekijk voor een praktisch voorbeeld onze sectie Kenmerkvectoren genereren.



Aangemaakt 2023-11-12, Bijgewerkt 2024-07-05
Auteurs: glenn-jocher (10), IvorZhu331 (1), Burhan-Q (1)

Reacties