Validation croisée K-Fold avec Ultralytics
Introduction
Ce guide complet illustre l'implémentation de la validation croisée K-Fold pour les ensembles de données de détection d'objets au sein de l'écosystème Ultralytics. Nous utiliserons le format de détection YOLO et des bibliothèques python clés telles que sklearn, pandas et PyYaml pour vous guider à travers la configuration nécessaire, le processus de génération de vecteurs de caractéristiques et l'exécution d'une division d'ensemble de données K-Fold.
Que votre projet implique l'ensemble de données de détection de fruits ou une source de données personnalisée, ce didacticiel vise à vous aider à comprendre et à appliquer la validation croisée K-Fold pour renforcer la fiabilité et la robustesse de votre apprentissage automatique modèles. Pendant que nous appliquons k=5
folds pour ce tutoriel, gardez à l'esprit que le nombre optimal de folds peut varier en fonction de votre ensemble de données et des spécificités de votre projet.
Sans plus attendre, plongeons-nous dedans !
Configuration
-
Vos annotations doivent être au format de détection YOLO.
-
Ce guide suppose que les fichiers d'annotations sont disponibles localement.
-
Pour notre démonstration, nous utilisons l'ensemble de données Détection de fruits.
- Cet ensemble de données contient un total de 8479 images.
- Il comprend 6 étiquettes de classe, chacune avec son nombre total d'instances listé ci-dessous.
Étiquette de classe | Nombre d'instances |
---|---|
Apple | 7049 |
Raisins | 7202 |
Ananas | 1613 |
Orange | 15549 |
Banane | 3536 |
Pastèque | 1976 |
-
Les paquets Python nécessaires incluent :
ultralytics
sklearn
pandas
pyyaml
-
Ce tutoriel fonctionne avec
k=5
folds. Cependant, vous devez déterminer le meilleur nombre de folds pour votre ensemble de données spécifique. -
Créer un nouvel environnement virtuel Python (
venv
) pour votre projet et activez-le. Utilisezpip
(ou votre gestionnaire de paquets préféré) pour installer :- La bibliothèque Ultralytics :
pip install -U ultralytics
. Vous pouvez également cloner le repo. - Scikit-learn, pandas et PyYAML :
pip install -U scikit-learn pandas pyyaml
.
- La bibliothèque Ultralytics :
-
Vérifiez que vos annotations sont au format de détection YOLO.
- Pour ce tutoriel, tous les fichiers d'annotation se trouvent dans le
Fruit-Detection/labels
répertoire.
- Pour ce tutoriel, tous les fichiers d'annotation se trouvent dans le
Générer des vecteurs de caractéristiques pour l'ensemble de données de détection d'objets
-
Commencez par créer un nouveau
example.py
Fichier Python pour les étapes ci-dessous. -
Passez à la récupération de tous les fichiers d'étiquettes pour votre ensemble de données.
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'
-
Maintenant, lisez le contenu du fichier YAML de l'ensemble de données et extrayez les indices des étiquettes de 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())
-
Initialiser un vide
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)
-
Comptez les instances de chaque étiquette de classe présentes dans les fichiers d'annotation.
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`
-
Voici un exemple de vue du DataFrame rempli :
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
Les lignes indexent les fichiers d'étiquettes, chacun correspondant à une image de votre ensemble de données, et les colonnes correspondent à vos indices d'étiquettes de classe. Chaque ligne représente un pseudo vecteur de caractéristiques, avec le nombre de chaque étiquette de classe présente dans votre ensemble de données. Cette structure de données permet l'application de la validation croisée K-Fold à un ensemble de données de détection d'objets.
Division de l'ensemble de données K-Fold
-
Nous allons maintenant utiliser le
KFold
classe depuissklearn.model_selection
pour générerk
fractions de l'ensemble de données.- Important :
- Paramètre
shuffle=True
assure une distribution aléatoire des classes dans vos divisions. - En définissant
random_state=M
oùM
est un entier choisi, vous pouvez obtenir des résultats reproductibles.
- Paramètre
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))
- Important :
-
L'ensemble de données a maintenant été divisé en
k
folds, chacun ayant une liste detrain
etval
indices. Nous allons construire un DataFrame pour afficher ces résultats plus clairement.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"
-
Nous allons maintenant calculer la distribution des étiquettes de classe pour chaque pli en tant que ratio des classes présentes dans
val
à ceux présents danstrain
.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
Le scénario idéal est que tous les ratios de classes soient raisonnablement similaires pour chaque division et entre les classes. Ceci, cependant, sera soumis aux spécificités de votre ensemble de données.
-
Ensuite, nous créons les répertoires et les fichiers YAML du jeu de données pour chaque division.
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, )
-
Enfin, copiez les images et les étiquettes dans le répertoire respectif ('train' ou 'val') pour chaque division.
- REMARQUE : Le temps requis pour cette partie du code variera en fonction de la taille de votre ensemble de données et du matériel de votre système.
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)
Enregistrer les enregistrements (facultatif)
Vous pouvez également enregistrer les enregistrements du fractionnement K-Fold et les DataFrames de distribution des étiquettes en tant que fichiers CSV pour référence future.
folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")
Entraîner YOLO en utilisant les divisions de données K-Fold
-
Tout d'abord, chargez le modèle YOLO.
from ultralytics import YOLO weights_path = "path/to/weights.pt" # use yolo11n.pt for a small model model = YOLO(weights_path, task="detect")
-
Ensuite, itérez sur les fichiers YAML du jeu de données pour exécuter l'entraînement. Les résultats seront enregistrés dans un répertoire spécifié par le
project
etname
arguments. Par défaut, ce répertoire est 'runs/detect/train#' où # est un index entier.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
-
Vous pouvez également utiliser la fonction Ultralytics data.utils.autosplit pour le fractionnement automatique de l'ensemble de données :
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)
Conclusion
Dans ce guide, nous avons exploré le processus d'utilisation de la validation croisée K-Fold pour l'entraînement du modèle de détection d'objets YOLO. Nous avons appris à diviser notre ensemble de données en K partitions, garantissant une distribution équilibrée des classes dans les différents plis.
Nous avons également exploré la procédure de création de DataFrames de rapport pour visualiser les divisions de données et les distributions d'étiquettes à travers ces divisions, ce qui nous donne un aperçu clair de la structure de nos ensembles d'entraînement et de validation.
Éventuellement, nous avons enregistré nos enregistrements pour référence future, ce qui pourrait être particulièrement utile dans les projets à grande échelle ou lors du dépannage des performances du modèle.
Enfin, nous avons implémenté l'entraînement du modèle proprement dit en utilisant chaque division dans une boucle, en sauvegardant nos résultats d'entraînement pour une analyse et une comparaison plus approfondies.
Cette technique de validation croisée K-Fold est un moyen robuste de tirer le meilleur parti de vos données disponibles, et elle contribue à garantir que les performances de votre modèle sont fiables et cohérentes sur différents sous-ensembles de données. Il en résulte un modèle plus généralisable et fiable, moins susceptible de sur-adapter à des modèles de données spécifiques.
N'oubliez pas que, bien que nous ayons utilisé YOLO dans ce guide, ces étapes sont pour la plupart transférables à d'autres modèles d'apprentissage automatique. La compréhension de ces étapes vous permet d'appliquer efficacement la validation croisée dans vos propres projets d'apprentissage automatique. Bon codage !
FAQ
Qu'est-ce que la validation croisée K-Fold et pourquoi est-elle utile dans la détection d'objets ?
La validation croisée K-Fold est une technique où l'ensemble de données est divisé en 'k' sous-ensembles (folds) pour évaluer la performance du modèle de manière plus fiable. Chaque fold sert à la fois de données d'entraînement et de validation. Dans le contexte de la détection d'objets, l'utilisation de la validation croisée K-Fold permet de garantir que la performance de votre modèle Ultralytics YOLO est robuste et généralisable à travers différentes divisions de données, améliorant ainsi sa fiabilité. Pour des instructions détaillées sur la configuration de la validation croisée K-Fold avec Ultralytics YOLO, consultez Validation croisée K-Fold avec Ultralytics.
Comment implémenter la validation croisée K-Fold à l’aide d’Ultralytics YOLO ?
Pour implémenter la validation croisée K-Fold avec Ultralytics YOLO, vous devez suivre ces étapes :
- Vérifiez que les annotations sont au format de détection YOLO.
- Utiliser des bibliothèques python comme
sklearn
,pandas
, etpyyaml
. - Créer des vecteurs de caractéristiques à partir de votre ensemble de données.
- Divisez votre ensemble de données en utilisant
KFold
desklearn.model_selection
. - Entraîner le modèle YOLO sur chaque division.
Pour un guide complet, consultez la section Fractionnement de l'ensemble de données K-Fold dans notre documentation.
Pourquoi devrais-je utiliser Ultralytics YOLO pour la détection d'objets ?
Ultralytics YOLO offre une détection d'objets en temps réel à la pointe de la technologie avec une grande précision et efficacité. Il est polyvalent et prend en charge plusieurs tâches de vision par ordinateur telles que la détection, la segmentation et la classification. De plus, il s'intègre de manière transparente avec des outils tels que Ultralytics HUB pour l'entraînement et le déploiement de modèles sans code. Pour plus de détails, explorez les avantages et les fonctionnalités sur notre page Ultralytics YOLO.
Comment puis-je m'assurer que mes annotations sont dans le format correct pour Ultralytics YOLO ?
Vos annotations doivent respecter le format de détection YOLO. Chaque fichier d'annotation doit répertorier la classe d'objet, ainsi que les coordonnées de sa boîte englobante dans l'image. Le format YOLO garantit un traitement des données rationalisé et standardisé pour l'entraînement des modèles de détection d'objets. Pour plus d'informations sur le formatage correct des annotations, consultez le guide du format de détection YOLO.
Puis-je utiliser la validation croisée K-Fold avec des ensembles de données personnalisés autres que la détection de fruits ?
Oui, vous pouvez utiliser la validation croisée K-Fold avec n'importe quel ensemble de données personnalisé tant que les annotations sont au format de détection YOLO. Remplacez les chemins d'accès aux ensembles de données et les étiquettes de classe par ceux spécifiques à votre ensemble de données personnalisé. Cette flexibilité garantit que tout projet de détection d'objets peut bénéficier d'une évaluation robuste du modèle à l'aide de la validation croisée K-Fold. Pour un exemple pratique, consultez notre section sur la génération de vecteurs de caractéristiques.