Validation croisée K-Fold avec Ultralytics
Introduction
Ce guide complet illustre la mise en œuvre de la validation croisée K-Fold pour les jeux 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 comme sklearn, pandas et PyYAML pour t'accompagner dans la configuration nécessaire, le processus de génération de vecteurs de caractéristiques et l'exécution d'une division de jeu de données K-Fold.
Que ton projet implique le jeu de données Fruit Detection ou une source de données personnalisée, ce tutoriel a pour but de t'aider à comprendre et à appliquer la validation croisée K-Fold pour renforcer la fiabilité et la robustesse de tes modèles de machine learning. Bien que nous utilisions k=5 plis pour ce tutoriel, garde à l'esprit que le nombre optimal de plis peut varier en fonction de ton jeu de données et des spécificités de ton projet.
Commençons.
Configuration
-
Tes annotations doivent être au format de détection YOLO.
-
Ce guide suppose que les fichiers d'annotation sont disponibles localement.
-
Pour notre démonstration, nous utilisons le jeu de données Fruit Detection.
- Ce jeu de données contient un total de 8479 images.
- Il inclut 6 étiquettes de classe, chacune avec son nombre total d'instances listé ci-dessous.
| Étiquette de classe | Nombre d'instances |
|---|---|
| Apple | 7049 |
| Grapes | 7202 |
| Pineapple | 1613 |
| Orange | 15549 |
| Banana | 3536 |
| Watermelon | 1976 |
-
Les paquets Python nécessaires incluent :
ultralyticssklearnpandaspyyaml
-
Ce tutoriel fonctionne avec
k=5plis. Cependant, tu devrais déterminer le meilleur nombre de plis pour ton jeu de données spécifique.
-
Initie un nouvel environnement virtuel Python (
venv) pour ton projet et active-le. Utilisepip(ou ton gestionnaire de paquets préféré) pour installer :- La bibliothèque Ultralytics :
pip install -U ultralytics. Alternativement, tu peux cloner le dépôt officiel. - Scikit-learn, pandas et PyYAML :
pip install -U scikit-learn pandas pyyaml.
- La bibliothèque Ultralytics :
-
Vérifie que tes annotations sont au format de détection YOLO.
- Pour ce tutoriel, tous les fichiers d'annotation se trouvent dans le répertoire
Fruit-Detection/labels.
- Pour ce tutoriel, tous les fichiers d'annotation se trouvent dans le répertoire
Génération de vecteurs de caractéristiques pour un jeu de données de détection d'objets
-
Commence par créer un nouveau fichier Python
example.pypour les étapes ci-dessous. -
Procède à la récupération de tous les fichiers d'étiquettes pour ton jeu 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, lis le contenu du fichier YAML du jeu de données et extrais 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()) -
Initialise un
pandasDataFrame vide.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) -
Compte les instances de chaque étiquette de classe présente 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 aperçu 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 ton jeu de données, et les colonnes correspondent aux indices de tes étiquettes de classe. Chaque ligne représente un pseudo vecteur de caractéristiques, avec le compte de chaque étiquette de classe présente dans ton jeu de données. Cette structure de données permet l'application de la validation croisée K-Fold à un jeu de données de détection d'objets.
Division du jeu de données K-Fold
-
Nous allons maintenant utiliser la classe
KFolddesklearn.model_selectionpour générerkdivisions du jeu de données.- Important :
- Définir
shuffle=Truegarantit une distribution aléatoire des classes dans tes divisions. - En définissant
random_state=MoùMest un entier choisi, tu peux obtenir des résultats reproductibles.
- Définir
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 :
-
Le jeu de données a maintenant été divisé en
kplis, chacun ayant une liste d'indicestrainetval. 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" -
Maintenant, nous allons calculer la distribution des étiquettes de classe pour chaque pli sous forme de ratio des classes présentes dans
valpar rapport à celles présentes 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}"] = ratioLe scénario idéal est que tous les ratios de classes soient raisonnablement similaires pour chaque division et à travers les classes. Cela sera cependant soumis aux spécificités de ton jeu de données.
-
Ensuite, nous créons les répertoires et les fichiers YAML de 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, copie les images et les étiquettes dans le répertoire respectif ('train' ou 'val') pour chaque division.
- NOTE : Le temps requis pour cette partie du code variera en fonction de la taille de ton jeu de données et du matériel de ton 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 (Optionnel)
Optionnellement, tu peux enregistrer les enregistrements des DataFrames de division K-Fold et de distribution des étiquettes sous forme de 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
-
D'abord, charge le modèle YOLO.
from ultralytics import YOLO weights_path = "path/to/weights.pt" # use yolo26n.pt for a small model model = YOLO(weights_path, task="detect") -
Ensuite, itère sur les fichiers YAML du jeu de données pour lancer l'entraînement. Les résultats seront enregistrés dans un répertoire spécifié par les arguments
projectetname. 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 -
Tu peux aussi utiliser la fonction Ultralytics data.utils.autosplit pour une division automatique du jeu de données :
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)
Conclusion
Dans ce guide, nous avons exploré le processus d'utilisation de la validation croisée K-Fold pour entraîner le modèle de détection d'objets YOLO. Nous avons appris comment diviser notre jeu de données en K partitions, en assurant une distribution équilibrée des classes à travers les différents plis.
Nous avons aussi exploré la procédure pour créer des DataFrames de rapport afin de visualiser les divisions de données et les distributions d'étiquettes à travers ces divisions, nous fournissant une vision claire de la structure de nos ensembles d'entraînement et de validation.
Optionnellement, 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 réel du modèle en utilisant chaque division dans une boucle, en enregistrant nos résultats d'entraînement pour analyse et comparaison ultérieures.
Cette technique de validation croisée K-Fold est un moyen robuste de tirer le meilleur parti de tes données disponibles, et elle aide à garantir que la performance de ton modèle est fiable et cohérente à travers différents sous-ensembles de données. Cela résulte en un modèle plus généralisable et fiable qui est moins susceptible de faire de l'overfitting sur des modèles de données spécifiques.
Rappelle-toi que bien que nous ayons utilisé YOLO dans ce guide, ces étapes sont pour la plupart transférables à d'autres modèles de machine learning. Comprendre ces étapes te permet d'appliquer la validation croisée efficacement dans tes propres projets de machine learning.
FAQ
Qu'est-ce que la validation croisée K-Fold et pourquoi est-elle utile en détection d'objets ?
La validation croisée K-Fold est une technique où le jeu de données est divisé en 'k' sous-ensembles (plis) pour évaluer la performance du modèle de manière plus fiable. Chaque pli sert à la fois de données d'entraînement et de données de validation. Dans le contexte de la détection d'objets, utiliser la validation croisée K-Fold aide à garantir que la performance de ton modèle Ultralytics YOLO est robuste et généralisable à travers différentes divisions de données, améliorant sa fiabilité. Pour des instructions détaillées sur la configuration de la validation croisée K-Fold avec Ultralytics YOLO, réfère-toi à Validation croisée K-Fold avec Ultralytics.
Comment implémenter la validation croisée K-Fold en utilisant Ultralytics YOLO ?
Pour implémenter la validation croisée K-Fold avec Ultralytics YOLO, tu dois suivre ces étapes :
- Vérifie que les annotations sont au format de détection YOLO.
- Utilise des bibliothèques Python comme
sklearn,pandasetpyyaml. - Crée des vecteurs de caractéristiques à partir de ton jeu de données.
- Divise ton jeu de données en utilisant
KFolddesklearn.model_selection. - Entraîne le modèle YOLO sur chaque division.
Pour un guide complet, vois la section Division du jeu 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 de pointe avec une précision et une efficacité élevées. C'est polyvalent, prenant en charge de multiples tâches de vision par ordinateur comme la détection, la segmentation et la classification. De plus, il s'intègre de manière transparente avec des outils comme Ultralytics Platform pour l'entraînement et le déploiement de modèles sans code. Pour plus de détails, explore les avantages et fonctionnalités sur notre page Ultralytics YOLO.
Comment puis-je m'assurer que mes annotations sont au bon format pour Ultralytics YOLO ?
Tes annotations doivent suivre le format de détection YOLO. Chaque fichier d'annotation doit lister la classe de l'objet, ainsi que ses coordonnées de boîte englobante dans l'image. Le format YOLO assure un traitement des données rationalisé et standardisé pour l'entraînement de modèles de détection d'objets. Pour plus d'informations sur le formatage approprié des annotations, visite le guide du format de détection YOLO.
Puis-je utiliser la validation croisée K-Fold avec des jeux de données personnalisés autres que Fruit Detection ?
Oui, tu peux utiliser la validation croisée K-Fold avec n'importe quel jeu de données personnalisé tant que les annotations sont au format de détection YOLO. Remplace les chemins de jeu de données et les étiquettes de classe par ceux spécifiques à ton jeu de données personnalisé. Cette flexibilité garantit que tout projet de détection d'objets peut bénéficier d'une évaluation de modèle robuste en utilisant la validation croisée K-Fold. Pour un exemple pratique, examine notre section Génération de vecteurs de caractéristiques.