K-Fold Cross Validation con Ultralytics
Introduzione
Questa guida completa illustra l'implementazione della K-Fold Cross Validation per i dataset di object detection all'interno dell'ecosistema Ultralytics. Sfrutteremo il formato di detection YOLO e librerie Python chiave come sklearn, pandas e PyYaml per guidarti attraverso la configurazione necessaria, il processo di generazione di vettori di feature e l'esecuzione di una divisione del dataset K-Fold.
Che il tuo progetto coinvolga il dataset Fruit Detection o una fonte di dati personalizzata, questo tutorial mira ad aiutarti a comprendere e applicare la convalida incrociata K-Fold per rafforzare l'affidabilità e la robustezza del tuo apprendimento automatico models. Mentre stiamo applicando k=5
fold per questo tutorial, tieni presente che il numero ottimale di fold può variare a seconda del tuo set di dati e delle specifiche del tuo progetto.
Senza ulteriori indugi, tuffiamoci subito!
Configurazione
-
Le tue annotazioni devono essere nel formato di rilevamento YOLO.
-
Questa guida presuppone che i file di annotazione siano disponibili localmente.
-
Per la nostra dimostrazione, utilizziamo il set di dati Fruit Detection.
- Questo dataset contiene un totale di 8479 immagini.
- Include 6 etichette di classe, ognuna con il suo conteggio totale di istanze elencato di seguito.
Etichetta Classe | Conteggio istanze |
---|---|
Apple | 7049 |
Uva | 7202 |
Ananas | 1613 |
Arancione | 15549 |
Banana | 3536 |
Anguria | 1976 |
-
I pacchetti Python necessari includono:
ultralytics
sklearn
pandas
pyyaml
-
Questo tutorial funziona con
k=5
fold. Tuttavia, dovresti determinare il numero migliore di fold per il tuo set di dati specifico. -
Avvia un nuovo ambiente virtuale python (
venv
) per il tuo progetto e attivalo. Usapip
(o il gestore di pacchetti preferito) per installare:- La libreria Ultralytics:
pip install -U ultralytics
. In alternativa, puoi clonare l'ufficiale repo. - Scikit-learn, pandas e PyYAML:
pip install -U scikit-learn pandas pyyaml
.
- La libreria Ultralytics:
-
Verificare che le annotazioni siano nel formato di rilevamento YOLO.
- Per questo tutorial, tutti i file di annotazione si trovano nella
Fruit-Detection/labels
directory.
- Per questo tutorial, tutti i file di annotazione si trovano nella
Generazione di vettori di caratteristiche per il set di dati di rilevamento oggetti
-
Inizia creando un nuovo
example.py
File Python per i passaggi seguenti. -
Procedi per recuperare tutti i file di etichette per il tuo set di dati.
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 set di dati 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 elemento vuoto
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)
-
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`
-
Di seguito è riportata una visualizzazione 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 etichette, ognuno corrispondente a un'immagine nel tuo dataset, e le colonne corrispondono agli indici delle etichette di classe. Ogni riga rappresenta uno pseudo vettore di caratteristiche, con il conteggio di ogni etichetta di classe presente nel tuo dataset. Questa struttura di dati consente l'applicazione della validazione incrociata K-Fold a un dataset di rilevamento oggetti.
Suddivisione del Dataset K-Fold
-
Ora useremo il
KFold
classe dasklearn.model_selection
per generarek
divisioni del dataset.- Importante:
- Impostazione
shuffle=True
assicura una distribuzione casuale delle classi nelle tue suddivisioni. - Impostando
random_state=M
doveM
è un numero intero scelto, puoi ottenere risultati ripetibili.
- 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
k
fold, ognuno con un elenco ditrain
eval
indici. 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"
-
Ora calcoleremo la distribuzione delle etichette di classe per ogni fold come rapporto delle classi presenti in
val
a quelli 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}"] = ratio
Lo scenario ideale è che tutti i rapporti tra le classi siano ragionevolmente simili per ogni suddivisione e tra le classi. Tuttavia, ciò dipenderà dalle specificità del tuo set di dati.
-
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 le immagini e le etichette nella rispettiva directory ('train' o 'val') per ogni suddivisione.
- NOTA: Il tempo necessario per questa porzione di codice varierà in base alle dimensioni del dataset e all'hardware del 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)
Salva i record (opzionale)
Opzionalmente, puoi salvare i record dello split K-Fold e i DataFrame 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")
Addestra YOLO utilizzando gli split di dati K-Fold
-
Innanzitutto, carica il modello YOLO.
from ultralytics import YOLO weights_path = "path/to/weights.pt" # use yolo11n.pt for a small model model = YOLO(weights_path, task="detect")
-
Successivamente, scorri i file YAML del dataset per eseguire il training. I risultati verranno salvati in una directory specificata dal
project
ename
argomenti. 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
-
È inoltre possibile utilizzare la funzione Ultralytics data.utils.autosplit per la suddivisione automatica del dataset:
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)
Conclusione
In questa guida, abbiamo esplorato il processo di utilizzo della cross-validation K-Fold per l'addestramento del modello di rilevamento oggetti YOLO. Abbiamo imparato come dividere il nostro dataset in K partizioni, garantendo una distribuzione di classe bilanciata tra le diverse fold.
Abbiamo anche esplorato la procedura per creare DataFrames di report per visualizzare le suddivisioni dei dati e le distribuzioni delle etichette tra queste suddivisioni, fornendoci una chiara visione della struttura dei nostri set di training 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 dei problemi relativi alle prestazioni del modello.
Infine, abbiamo implementato l'effettivo training del modello utilizzando ogni suddivisione in un ciclo, salvando i nostri risultati di training per ulteriori analisi e confronti.
Questa tecnica di cross-validation K-Fold è un modo efficace per sfruttare al meglio i dati disponibili e aiuta a garantire che le prestazioni del modello siano affidabili e coerenti tra diversi sottoinsiemi di dati. Ciò si traduce in un modello più generalizzabile e affidabile, meno incline all'overfitting 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 efficacemente la cross-validation nei tuoi progetti di machine learning. Buon coding!
FAQ
Cos'è la convalida incrociata K-Fold e perché è utile nel rilevamento oggetti?
La K-Fold Cross Validation è 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 training che di validazione. Nel contesto del rilevamento di oggetti, l'utilizzo della K-Fold Cross Validation 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 K-Fold Cross Validation con Ultralytics YOLO, fare riferimento a K-Fold Cross Validation con Ultralytics.
Come posso implementare la K-Fold Cross Validation utilizzando Ultralytics YOLO?
Per implementare la cross-validation K-Fold con Ultralytics YOLO, è necessario seguire questi passaggi:
- Verificare che le annotazioni siano nel formato di rilevamento YOLO.
- Utilizza librerie Python come
sklearn
,pandas
, epyyaml
. - Crea vettori di feature dal tuo dataset.
- Dividi il tuo dataset usando
KFold
dasklearn.model_selection
. - Addestra il modello YOLO su ogni split.
Per una guida completa, consulta la sezione Suddivisione del set di dati K-Fold nella nostra documentazione.
Perché dovrei usare Ultralytics YOLO per il rilevamento di oggetti?
Ultralytics YOLO offre un rilevamento degli oggetti in tempo reale all'avanguardia, con elevata precisione ed efficienza. È versatile e supporta diverse attività di computer vision come il rilevamento, la segmentazione e la classificazione. Inoltre, si integra perfettamente con strumenti come Ultralytics HUB per l'addestramento e la distribuzione di modelli senza codice. 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 devono 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 il training dei modelli di rilevamento degli oggetti. Per ulteriori informazioni sulla corretta formattazione delle annotazioni, visita la guida al formato di rilevamento YOLO.
Posso utilizzare la convalida incrociata K-Fold con set di dati personalizzati diversi dal rilevamento di frutta?
Sì, puoi usare la convalida 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à assicura che qualsiasi progetto di rilevamento oggetti possa beneficiare di una valutazione robusta del modello usando la convalida incrociata K-Fold. Per un esempio pratico, rivedi la nostra sezione Generazione di Vettori di Caratteristiche.