Meet YOLO26: next-gen vision AI.

Link to this sectionConvalida incrociata K-Fold con Ultralytics#

Link to this sectionIntroduzione#

Questa guida completa illustra l'implementazione della Convalida incrociata K-Fold per dataset di rilevamento oggetti all'interno dell'ecosistema Ultralytics. Utilizzeremo il formato di rilevamento YOLO e le principali librerie Python come sklearn, pandas e PyYAML per guidarti attraverso la configurazione necessaria, il processo di generazione dei vettori di caratteristiche e l'esecuzione di una suddivisione del dataset K-Fold.

K-fold cross validation data splitting

Che il tuo progetto riguardi il dataset Fruit Detection o una fonte dati personalizzata, questo tutorial mira ad aiutarti a comprendere e applicare la Convalida incrociata K-Fold per rafforzare l'affidabilità e la robustezza dei tuoi modelli di machine learning. Sebbene in questo tutorial applichiamo k=5 fold, tieni presente che il numero ottimale di fold può variare a seconda del tuo dataset e delle specifiche del tuo progetto.

Iniziamo.

Link to this sectionConfigurazione#

  • Le tue annotazioni dovrebbero essere nel formato di rilevamento YOLO.

  • Questa guida presuppone che i file di annotazione siano disponibili localmente.

  • Per la nostra dimostrazione, utilizziamo il dataset Fruit Detection.

    • Questo dataset contiene un totale di 8479 immagini.
    • Include 6 etichette di classe, ognuna con i relativi conteggi totali di istanze elencati di seguito.
Etichetta di classeConteggio istanze
Mela7049
Uva7202
Ananas1613
Arancia15549
Banana3536
Anguria1976
  • I pacchetti Python necessari includono:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • Questo tutorial opera con k=5 fold. Tuttavia, dovresti determinare il miglior numero di fold per il tuo dataset specifico.

  1. Avvia un nuovo ambiente virtuale Python (venv) per il tuo progetto e attivalo. Usa pip (o il tuo gestore di pacchetti preferito) per installare:

    • La libreria Ultralytics: pip install -U ultralytics. In alternativa, puoi clonare il repository ufficiale.
    • Scikit-learn, pandas e PyYAML: pip install -U scikit-learn pandas pyyaml.
  2. Verifica che le tue annotazioni siano nel formato di rilevamento YOLO.

    • Per questo tutorial, tutti i file di annotazione si trovano nella directory Fruit-Detection/labels.

Link to this sectionGenerazione di vettori di caratteristiche per il dataset di rilevamento oggetti#

  1. Inizia creando un nuovo file Python example.py per i passaggi seguenti.

  2. Procedi a recuperare tutti i file di etichetta per il tuo 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. Ora, leggi il contenuto del file YAML del dataset ed estrai gli indici delle etichette di classe.

    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. Inizializza un DataFrame pandas vuoto.

    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. Conta le istanze di ogni etichetta di classe presente nei file di annotazione.

    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. Di seguito è riportata una vista campione del DataFrame popolato:

                                                           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

Le righe indicizzano i file di etichetta, ciascuno corrispondente a un'immagine nel tuo dataset, e le colonne corrispondono ai tuoi indici di etichetta di classe. Ogni riga rappresenta uno pseudo vettore di caratteristiche, con il conteggio di ciascuna etichetta di classe presente nel tuo dataset. Questa struttura dati consente l'applicazione della Convalida incrociata K-Fold a un dataset di rilevamento oggetti.

Link to this sectionSuddivisione del dataset K-Fold#

  1. Ora useremo la classe KFold di sklearn.model_selection per generare k suddivisioni del dataset.

    • Importante:
      • L'impostazione di shuffle=True garantisce una distribuzione casuale delle classi nelle tue suddivisioni.
      • Impostando random_state=M dove M è un intero scelto, puoi ottenere risultati ripetibili.
    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. Il dataset è stato ora suddiviso in k fold, ognuno dei quali possiede un elenco di indici train e val. Costruiremo un DataFrame per visualizzare questi risultati in modo più chiaro.

    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. Ora calcoleremo la distribuzione delle etichette di classe per ogni fold come rapporto delle classi presenti in val rispetto a quelle presenti 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

    Lo scenario ideale è che tutti i rapporti di classe siano ragionevolmente simili per ogni suddivisione e tra le classi. Questo, tuttavia, sarà soggetto alle specificità del tuo dataset.

  4. Successivamente, creiamo le directory e i file YAML del dataset per ogni suddivisione.

    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. Infine, copia le immagini e le etichette nella rispettiva directory ('train' o 'val') per ogni suddivisione.

    • NOTA: Il tempo richiesto per questa parte del codice varierà in base alla dimensione del tuo dataset e all'hardware del tuo sistema.
    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)

Link to this sectionSalvataggio dei record (Opzionale)#

Facoltativamente, puoi salvare i record dei DataFrame di suddivisione K-Fold e di distribuzione delle etichette come file CSV per riferimento futuro.

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

Link to this sectionAddestra YOLO utilizzando le suddivisioni dei dati K-Fold#

  1. Per prima cosa, carica il modello YOLO.

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"  # use yolo26n.pt for a small model
    model = YOLO(weights_path, task="detect")
  2. Successivamente, itera sui file YAML del dataset per eseguire l'addestramento. I risultati verranno salvati in una directory specificata dagli argomenti project e name. Per impostazione predefinita, questa directory è 'runs/detect/train#' dove # è un indice numerico.

    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. Puoi anche utilizzare la funzione Ultralytics data.split.autosplit per la suddivisione automatica del dataset:

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

Link to this sectionConclusione#

In questa guida, abbiamo esplorato il processo di utilizzo della convalida incrociata K-Fold per l'addestramento del modello di rilevamento oggetti YOLO. Abbiamo imparato come suddividere il nostro dataset in K partizioni, garantendo una distribuzione equilibrata delle classi tra i diversi fold.

Abbiamo anche esplorato la procedura per creare DataFrame di report per visualizzare le suddivisioni dei dati e le distribuzioni delle etichette tra queste suddivisioni, fornendoci una visione chiara della struttura dei nostri set di addestramento e validazione.

Facoltativamente, abbiamo salvato i nostri record per riferimento futuro, il che potrebbe essere particolarmente utile in progetti su larga scala o durante la risoluzione di problemi relativi alle prestazioni del modello.

Infine, abbiamo implementato l'addestramento del modello vero e proprio utilizzando ogni suddivisione in un ciclo, salvando i risultati dell'addestramento per ulteriori analisi e confronti.

Questa tecnica di convalida incrociata K-Fold è un modo robusto per sfruttare al meglio i tuoi dati disponibili e aiuta a garantire che le prestazioni del tuo modello siano affidabili e coerenti tra diversi sottoinsiemi di dati. Ciò si traduce in un modello più generalizzabile e affidabile con minori probabilità di overfitting su specifici pattern di dati.

Ricorda che, sebbene abbiamo utilizzato YOLO in questa guida, questi passaggi sono per lo più trasferibili ad altri modelli di machine learning. Comprendere questi passaggi ti consente di applicare efficacemente la convalida incrociata nei tuoi progetti di machine learning.

Link to this sectionFAQ#

Link to this sectionChe cos'è la Convalida incrociata K-Fold e perché è utile nel rilevamento oggetti?#

La Convalida incrociata K-Fold è una tecnica in cui il dataset viene diviso in 'k' sottoinsiemi (fold) per valutare le prestazioni del modello in modo più affidabile. Ogni fold funge sia da dati di addestramento che da dati di validazione. Nel contesto del rilevamento oggetti, utilizzare la Convalida incrociata K-Fold aiuta a garantire che le prestazioni del tuo modello Ultralytics YOLO siano robuste e generalizzabili tra diverse suddivisioni dei dati, migliorandone l'affidabilità. Per istruzioni dettagliate sulla configurazione della Convalida incrociata K-Fold con Ultralytics, fai riferimento a Convalida incrociata K-Fold con Ultralytics.

Link to this sectionCome implemento la Convalida incrociata K-Fold utilizzando Ultralytics YOLO?#

Per implementare la Convalida incrociata K-Fold con Ultralytics YOLO, devi seguire questi passaggi:

  1. Verifica che le annotazioni siano nel formato di rilevamento YOLO.
  2. Utilizza librerie Python come sklearn, pandas e pyyaml.
  3. Crea vettori di caratteristiche dal tuo dataset.
  4. Suddividi il tuo dataset utilizzando KFold da sklearn.model_selection.
  5. Addestra il modello YOLO su ogni suddivisione.

Per una guida completa, consulta la sezione Suddivisione del dataset K-Fold nella nostra documentazione.

Link to this sectionPerché dovrei usare Ultralytics YOLO per il rilevamento oggetti?#

Ultralytics YOLO offre un rilevamento oggetti in tempo reale all'avanguardia con elevata precisione ed efficienza. È versatile e supporta molteplici attività di computer vision come rilevamento, segmentazione di istanze, segmentazione semantica e classificazione. Inoltre, si integra perfettamente con strumenti come la Piattaforma Ultralytics per l'addestramento e il deployment di modelli senza codice. Per maggiori dettagli, esplora i vantaggi e le funzionalità sulla nostra pagina Ultralytics YOLO.

Link to this sectionCome posso assicurarmi che le mie annotazioni siano nel formato corretto per Ultralytics YOLO?#

Le tue annotazioni dovrebbero seguire il formato di rilevamento YOLO. Ogni file di annotazione deve elencare la classe dell'oggetto, insieme alle coordinate del suo riquadro di delimitazione nell'immagine. Il formato YOLO garantisce un'elaborazione dei dati semplificata e standardizzata per l'addestramento dei modelli di rilevamento oggetti. Per ulteriori informazioni sulla formattazione corretta delle annotazioni, visita la guida al formato di rilevamento YOLO.

Link to this sectionPosso utilizzare la Convalida incrociata K-Fold con dataset personalizzati diversi da Fruit Detection?#

Sì, puoi utilizzare la Convalida incrociata K-Fold con qualsiasi dataset personalizzato, a condizione che le annotazioni siano nel formato di rilevamento YOLO. Sostituisci i percorsi del dataset e le etichette di classe con quelli specifici per il tuo dataset personalizzato. Questa flessibilità garantisce che qualsiasi progetto di rilevamento oggetti possa beneficiare di una valutazione robusta del modello utilizzando la Convalida incrociata K-Fold. Per un esempio pratico, rivedi la nostra sezione Generazione di vettori di caratteristiche.

Commenti