Cross-Validation K-Fold con Ultralytics
Introduzione
Questa guida completa illustra l'implementazione della Cross-Validation K-Fold per dataset di object detection all'interno dell'ecosistema Ultralytics. Sfrutteremo il formato di rilevamento YOLO e le librerie Python chiave come sklearn, pandas e PyYAML per guidarti attraverso la configurazione necessaria, il processo di generazione dei vettori delle caratteristiche e l'esecuzione di una suddivisione del dataset K-Fold.
Che il tuo progetto coinvolga il dataset Fruit Detection o una sorgente dati personalizzata, questo tutorial mira ad aiutarti a comprendere e applicare la Cross-Validation K-Fold per rafforzare l'affidabilità e la robustezza dei tuoi modelli di machine learning. Sebbene per 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.
Configurazione
-
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 il conteggio totale delle istanze elencato di seguito.
| Etichetta della Classe | Conteggio Istanze |
|---|---|
| Apple | 7049 |
| Grapes | 7202 |
| Pineapple | 1613 |
| Orange | 15549 |
| Banana | 3536 |
| Watermelon | 1976 |
-
I pacchetti Python necessari includono:
ultralyticssklearnpandaspyyaml
-
Questo tutorial opera con
k=5fold. Tuttavia, dovresti determinare il numero migliore di fold per il tuo dataset specifico.
-
Avvia un nuovo ambiente virtuale Python (
venv) per il tuo progetto e attivalo. Usapip(o il tuo gestore di pacchetti preferito) per installare:- La libreria Ultralytics:
pip install -U ultralytics. In alternativa, puoi clonare il repo ufficiale. - Scikit-learn, pandas e PyYAML:
pip install -U scikit-learn pandas pyyaml.
- La libreria Ultralytics:
-
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.
- Per questo tutorial, tutti i file di annotazione si trovano nella directory
Generazione dei vettori delle caratteristiche per il dataset di object detection
-
Inizia creando un nuovo file Python
example.pyper i passaggi seguenti. -
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' -
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()) -
Inizializza un DataFrame
pandasvuoto.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) -
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` -
Quella che segue è una vista di 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 indicizzano i file di etichetta, ognuno corrispondente a un'immagine nel tuo dataset, e le colonne corrispondono ai tuoi indici di etichetta di classe. Ogni riga rappresenta un pseudo vettore delle caratteristiche, con il conteggio di ogni etichetta di classe presente nel tuo dataset. Questa struttura dati consente l'applicazione della Cross-Validation K-Fold a un dataset di object detection.
Suddivisione del dataset K-Fold
-
Ora useremo la classe
KFolddasklearn.model_selectionper generareksuddivisioni del dataset.- Importante:
- L'impostazione
shuffle=Truegarantisce una distribuzione casuale delle classi nelle tue suddivisioni. - Impostando
random_state=MdoveMè un intero scelto, puoi ottenere risultati ripetibili.
- L'impostazione
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)) - Importante:
-
Il dataset è stato ora suddiviso in
kfold, ognuna con un elenco di indicitraineval. Costruiremo un DataFrame per visualizzare questi risultati più chiaramente.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" -
Ora calcoleremo la distribuzione delle etichette di classe per ogni fold come rapporto tra le classi presenti in
vale quelle presenti intrain.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}"] = ratioLo scenario ideale è che tutti i rapporti di classe siano ragionevolmente simili per ogni suddivisione e tra le classi. Questo, tuttavia, sarà soggetto alle specifiche del tuo dataset.
-
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, ) -
Infine, copia immagini ed etichette nella directory rispettiva ('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)
Salvataggio dei record (Opzionale)
Opzionalmente, puoi salvare i record della suddivisione K-Fold e i DataFrame della 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")Addestra YOLO utilizzando le suddivisioni dei dati K-Fold
-
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") -
Successivamente, scorri i file YAML del dataset per eseguire l'addestramento. I risultati saranno salvati in una directory specificata dagli argomenti
projectename. 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 -
Puoi anche utilizzare la funzione Ultralytics data.utils.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)
Conclusione
In questa guida, abbiamo esplorato il processo di utilizzo della cross-validation K-Fold per l'addestramento del modello di object detection YOLO. Abbiamo imparato come suddividere il nostro dataset in K partizioni, garantendo una distribuzione bilanciata delle classi tra le diverse 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.
Opzionalmente, 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 effettivo del modello utilizzando ogni suddivisione in un ciclo, salvando i risultati dell'addestramento per ulteriori analisi e confronti.
Questa tecnica di cross-validation 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 che ha meno probabilità di overfit su specifici pattern di dati.
Ricorda che, sebbene in questa guida abbiamo utilizzato YOLO, questi passaggi sono per lo più trasferibili ad altri modelli di machine learning. Comprendere questi passaggi ti consente di applicare la cross-validation in modo efficace nei tuoi progetti di machine learning.
FAQ
Cos'è la Cross-Validation K-Fold e perché è utile nell'object detection?
La Cross-Validation 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 addestramento che da dati di validazione. Nel contesto dell'object detection, utilizzare la Cross-Validation 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 Cross-Validation K-Fold con Ultralytics YOLO, fai riferimento a Cross-Validation K-Fold con Ultralytics.
Come implemento la Cross-Validation K-Fold utilizzando Ultralytics YOLO?
Per implementare la Cross-Validation K-Fold con Ultralytics YOLO, devi seguire questi passaggi:
- Verifica che le annotazioni siano nel formato di rilevamento YOLO.
- Usa librerie Python come
sklearn,pandasepyyaml. - Crea vettori delle caratteristiche dal tuo dataset.
- Suddividi il tuo dataset utilizzando
KFolddasklearn.model_selection. - Addestra il modello YOLO su ogni suddivisione.
Per una guida completa, consulta la sezione Suddivisione del dataset K-Fold nella nostra documentazione.
Perché dovrei usare Ultralytics YOLO per l'object detection?
Ultralytics YOLO offre un'object detection all'avanguardia in tempo reale con elevata accuratezza ed efficienza. È versatile, supportando molteplici attività di computer vision come rilevamento, segmentazione e classificazione. Inoltre, si integra perfettamente con strumenti come Ultralytics Platform per l'addestramento e il deployment di modelli no-code. Per maggiori dettagli, esplora i vantaggi e le funzionalità sulla nostra pagina Ultralytics YOLO.
Come 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 bounding box nell'immagine. Il formato YOLO garantisce un'elaborazione dei dati semplificata e standardizzata per l'addestramento di modelli di object detection. Per ulteriori informazioni sulla corretta formattazione delle annotazioni, visita la guida al formato di rilevamento YOLO.
Posso utilizzare la Cross-Validation K-Fold con dataset personalizzati diversi da Fruit Detection?
Sì, puoi utilizzare la Cross-Validation 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 object detection possa beneficiare di una solida valutazione del modello utilizzando la Cross-Validation K-Fold. Per un esempio pratico, rivedi la nostra sezione Generazione dei vettori delle caratteristiche.