Ultralytics YOLO27:
Get Started

Validazione incrociata K-Fold con Ultralytics#

Introduzione#

Questa guida completa illustra l'implementazione della validazione incrociata K-Fold per i dataset di rilevamento degli oggetti nell'ecosistema Ultralytics. Useremo il formato di rilevamento YOLO e librerie Python fondamentali come sklearn, pandas e PyYAML per guidarti nella configurazione necessaria, nella generazione dei vettori di caratteristiche e nell'esecuzione della suddivisione del dataset K-Fold.

K-fold cross validation data splitting

Che il tuo progetto riguardi il dataset Fruit Detection o una fonte di dati personalizzata, questo tutorial vuole aiutarti a comprendere e applicare la validazione incrociata K-Fold per aumentare l'affidabilità e la robustezza dei tuoi modelli di machine learning. In questo tutorial usiamo k=5 fold, ma tieni presente che il numero ottimale di fold può variare in base al dataset e alle specificità del progetto. La validazione incrociata K-Fold è particolarmente utile quando il dataset è piccolo, rumoroso o molto variabile; per dataset grandi e diversificati, di solito è sufficiente una suddivisione train/val/test ben costruita.

Iniziamo.

Configurazione#

  • Le annotazioni devono essere nel formato di rilevamento YOLO.

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

  • Per la dimostrazione, usiamo il dataset Fruit Detection.

    • Questo dataset contiene in totale 8479 immagini.
    • Include 6 etichette di classe, con il numero totale di istanze per ciascuna indicato di seguito.
Etichetta di classeNumero di istanze
Mela7049
Uva7202
Ananas1613
Arancia15549
Banana3536
Anguria1976
  • I pacchetti Python necessari includono:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • Questo tutorial usa k=5 fold. Tuttavia, dovresti determinare il numero di fold più adatto al tuo dataset.

  1. Crea un nuovo ambiente virtuale Python (venv) per il progetto e attivalo. Usa pip (o il gestore di pacchetti che preferisci) 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 annotazioni siano nel formato di rilevamento YOLO.

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

Generazione dei vettori di caratteristiche per un dataset di rilevamento degli oggetti#

  1. Per iniziare, crea un nuovo file Python example.py per eseguire i passaggi seguenti.

  2. Carica le immagini di training e validazione configurate tramite il proprietario del dataset del pacchetto, lasciando invariata la suddivisione di test.

    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. Ora leggi il contenuto del file YAML del dataset ed estrai gli indici delle etichette di classe.

    classes = data["names"]
    cls_idx = sorted(classes.keys())
  4. Inizializza un DataFrame pandas vuoto.

    import pandas as pd
    
    labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images)
  5. Conta le istanze di ciascuna etichetta di classe nei file di annotazione.

    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. Ecco un esempio 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 contengono percorsi assoluti delle immagini e le colonne corrispondono agli indici delle etichette di classe. Le etichette mancanti restano righe di background composte solo da zeri. Questa struttura dati consente di applicare la validazione incrociata K-Fold a un dataset di rilevamento degli oggetti.

Suddivisione del dataset K-Fold#

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

    • Importante:
      • Impostare shuffle=True assicura una distribuzione casuale delle classi nelle suddivisioni.
      • Impostando random_state=M su un valore in cui M è un intero scelto, puoi ottenere risultati ripetibili.
    from sklearn.model_selection import KFold
    
    ksplit = 5
    kf = KFold(n_splits=ksplit, shuffle=True, random_state=20)  # impostazione di random_state per ottenere risultati ripetibili
    
    kfolds = list(kf.split(labels_df))
  2. Il dataset è stato suddiviso in k fold, ognuno dei quali contiene un elenco di indici train e val. Creeremo un DataFrame per mostrare questi risultati più chiaramente.

    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. Ora calcoleremo la distribuzione delle etichette di classe per ciascun fold come rapporto tra le classi presenti in val e 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 prevede che i rapporti di tutte le classi siano ragionevolmente simili per ogni suddivisione e tra le classi. Tuttavia, questo dipende dalle specificità del dataset.

  4. Scrivi gli elenchi di immagini e un YAML del dataset per ogni suddivisione. Gli elenchi di testo evitano di copiare il dataset k volte.

    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,
            )

Salvataggio dei record (facoltativo)#

Facoltativamente, puoi salvare i record della suddivisione K-Fold e i DataFrame della distribuzione delle etichette come file CSV per consultarli in futuro.

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

Addestrare YOLO con le suddivisioni dei dati K-Fold#

  1. Per prima cosa, carica il modello YOLO.

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"  # usa yolo26n.pt per un modello piccolo
    model = YOLO(weights_path, task="detect")
  2. Poi, scorri i file YAML del dataset per avviare 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 intero.

    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 usare la funzione Ultralytics data.split.autosplit per suddividere automaticamente il dataset:

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

Conclusione#

In questa guida abbiamo esaminato il processo di utilizzo della validazione incrociata K-Fold per addestrare il modello YOLO di rilevamento degli oggetti. Abbiamo imparato a suddividere il gruppo di training e validazione in K partizioni e a usare la tabella dei rapporti generata per verificare il bilanciamento delle classi dopo una suddivisione casuale.

Abbiamo inoltre esaminato la procedura per creare DataFrame di riepilogo che visualizzano le suddivisioni dei dati e le distribuzioni delle etichette tra queste suddivisioni, fornendoci una visione chiara della struttura dei set di training e validazione.

Facoltativamente, abbiamo salvato i record per consultarli in futuro, una possibilità particolarmente utile nei progetti su larga scala o durante la risoluzione di problemi relativi alle prestazioni del modello.

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

Questa tecnica di validazione incrociata K-Fold è un metodo efficace per sfruttare al meglio i dati disponibili e contribuisce a garantire prestazioni affidabili e coerenti del modello tra diversi sottoinsiemi di dati. Il risultato è un modello più generalizzabile e affidabile, meno soggetto a overfitting su specifici pattern dei dati.

Ricorda che, anche se in questa guida abbiamo usato YOLO, questi passaggi sono perlopiù applicabili anche ad altri modelli di machine learning. Comprendendo questi passaggi, puoi applicare efficacemente la validazione incrociata ai tuoi progetti di machine learning.

Domande frequenti#

  • La validazione incrociata K-Fold è una tecnica che divide il dataset in 'k' sottoinsiemi (fold) per valutare le prestazioni del modello in modo più affidabile. Ogni fold funge sia da dati di validazione che da dati di training. Nel rilevamento degli oggetti, la validazione incrociata K-Fold contribuisce 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 validazione incrociata K-Fold con Ultralytics YOLO, consulta Validazione incrociata K-Fold con Ultralytics.

  • Per implementare la validazione incrociata K-Fold con Ultralytics YOLO, segui questi passaggi:

    1. Verifica che le annotazioni siano nel formato di rilevamento YOLO.
    2. Usa librerie Python come sklearn, pandas e pyyaml.
    3. Crea vettori di caratteristiche a partire dal dataset.
    4. Suddividi il dataset usando 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.

  • Il flusso di lavoro descritto in questa guida è pensato per il formato di rilevamento YOLO, ma lo stesso approccio si adatta a ogni task YOLO: il task cambia il modo in cui componi i fold, non l'utilità della validazione incrociata:

    AttivitàProgettazione dei fold
    detectSuddividi a livello di immagine, bilanciando la distribuzione di oggetti e classi tra i fold. Mantieni le immagini correlate (stesso paziente, sequenza video, telecamera o sito) all'interno di un unico fold.
    segmentUsa la stessa strategia a livello di immagine del rilevamento, preservando inoltre la copertura delle maschere e delle classi in ogni fold.
    classifyPreferisci fold stratificati per mantenere bilanciate le frequenze delle classi tra training e validazione.
    poseSuddividi per soggetto o sequenza, in modo che la stessa persona o lo stesso animale non compaia mai su entrambi i lati di un fold.
    obbSuddividi a livello di immagine, mantenendo insieme le tessere o i ritagli della stessa scena, un aspetto particolarmente importante per le immagini aeree.

    Qualunque sia il task, evita di inserire campioni quasi duplicati o correlati in fold contrapposti: questo tipo di fuga di dati gonfia le metriche di validazione ben oltre i risultati che il modello otterrà in produzione.

  • Ultralytics YOLO offre un rilevamento degli oggetti in tempo reale all'avanguardia, con elevata accuratezza ed efficienza. È versatile e supporta diversi task di visione artificiale, come rilevamento, segmentazione di istanze, segmentazione semantica e classificazione. Inoltre, si integra perfettamente con strumenti come Ultralytics Platform per addestrare e distribuire modelli senza scrivere codice. Per maggiori dettagli, scopri i vantaggi e le funzionalità nella nostra pagina Ultralytics YOLO.

  • Le annotazioni devono seguire il formato di rilevamento YOLO. Ogni file di annotazione deve indicare la classe dell'oggetto e le coordinate del relativo riquadro di delimitazione nell'immagine. Il formato YOLO garantisce un'elaborazione dei dati semplificata e standardizzata per l'addestramento dei modelli di rilevamento degli oggetti. Per maggiori informazioni sulla formattazione corretta delle annotazioni, consulta la guida al formato di rilevamento YOLO.

  • Sì, puoi usare la validazione incrociata K-Fold con qualsiasi dataset personalizzato, purché le annotazioni siano nel formato di rilevamento YOLO. Sostituisci i percorsi del dataset e le etichette di classe con quelli specifici del tuo dataset personalizzato. Questa flessibilità consente a qualsiasi progetto di rilevamento degli oggetti di trarre vantaggio da una valutazione robusta del modello tramite la validazione incrociata K-Fold. Per un esempio pratico, consulta la sezione Generazione dei vettori di caratteristiche.

Commenti