Validation croisée k-fold avec Ultralytics#
Introduction#
Ce guide complet présente la mise en œuvre de la validation croisée k-fold pour les jeux de données de détection d'objets dans 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 te guider dans la configuration nécessaire, la génération de vecteurs de caractéristiques et la création de partitions k-fold du jeu de données.
Que ton projet porte sur le jeu de données Fruit Detection ou sur une source de données personnalisée, ce tutoriel vise à 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. Nous utilisons k=5 plis dans ce tutoriel, mais garde à l'esprit que le nombre optimal de plis peut varier selon ton jeu de données et les spécificités de ton projet. La validation croisée k-fold est particulièrement utile lorsque ton jeu de données est petit, bruité ou très variable ; pour les grands jeux de données diversifiés, une partition train/val/test bien conçue suffit généralement.
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 au total 8479 images.
- Il comprend 6 étiquettes de classe, dont le nombre total d'instances est indiqué ci-dessous.
| Étiquette de classe | Nombre d'instances |
|---|---|
| Pomme | 7049 |
| Raisins | 7202 |
| Ananas | 1613 |
| Orange | 15549 |
| Banane | 3536 |
| Pastèque | 1976 |
-
Les packages Python nécessaires comprennent :
ultralyticssklearnpandaspyyaml
-
Ce tutoriel utilise
k=5plis. Tu dois toutefois déterminer le nombre de plis le mieux adapté à ton jeu de données.
-
Crée et active un nouvel environnement virtuel Python (
venv) pour ton projet. Utilisepip(ou le gestionnaire de paquets de ton choix) pour installer :- La bibliothèque Ultralytics :
pip install -U ultralytics. Tu peux aussi 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 suivre les étapes ci-dessous. -
Charge les images d'entraînement et de validation configurées à l'aide du propriétaire du jeu de données du package, sans toucher à la partition de 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] -
Lis ensuite le contenu du fichier YAML du jeu de données et extrais les indices des étiquettes de classe.
classes = data["names"] cls_idx = sorted(classes.keys()) -
Initialise un DataFrame
pandasvide.import pandas as pd labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images) -
Compte les instances de chaque classe présente dans les fichiers d'annotation.
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()) -
Voici un exemple du DataFrame renseigné :
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 utilisent des chemins d'accès absolus aux images, et les colonnes correspondent aux indices des classes. Les étiquettes manquantes restent des lignes d'arrière-plan entièrement composées de zéros. Cette structure de données permet d'appliquer la validation croisée k-fold à un jeu de données de détection d'objets.
Partitionnement du jeu de données en k-folds#
-
Nous allons maintenant utiliser la classe
KFolddesklearn.model_selectionpour générerkpartitions du jeu de données.- Important :
- Définir
shuffle=Truegarantit une répartition aléatoire des classes dans tes partitions. - En définissant
random_state=MavecMégal à un entier choisi, tu peux obtenir des résultats reproductibles.
- Définir
from sklearn.model_selection import KFold ksplit = 5 kf = KFold(n_splits=ksplit, shuffle=True, random_state=20) # définir random_state pour obtenir des résultats reproductibles kfolds = list(kf.split(labels_df)) - Important :
-
Le jeu de données est maintenant divisé en
kplis, chacun contenant une liste d'indicestrainetval. Nous allons créer un DataFrame pour présenter ces résultats plus clairement.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" -
Nous allons maintenant calculer la répartition des étiquettes de classe pour chaque pli, sous forme de ratio entre les classes présentes dans
valet 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}"] = ratioL'idéal est que les ratios de classes soient assez similaires dans chaque partition et d'une classe à l'autre. Cela dépend toutefois des spécificités de ton jeu de données.
-
Écris les listes d'images et un fichier YAML de jeu de données pour chaque partition. Les listes de fichiers texte évitent de copier le jeu de données
kfois.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, )
Enregistrer les résultats (facultatif)#
Tu peux éventuellement enregistrer les DataFrames de partitionnement k-fold et de répartition des étiquettes dans des fichiers CSV pour t'y référer ultérieurement.
folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")Entraîner YOLO avec les partitions de données k-fold#
-
Commence par charger le modèle YOLO.
from ultralytics import YOLO weights_path = "path/to/weights.pt" # utiliser yolo26n.pt pour un petit modèle model = YOLO(weights_path, task="detect") -
Parcours ensuite 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 indice 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.split.autosplit pour diviser automatiquement le jeu de données :
from ultralytics.data.split import autosplit # Diviser automatiquement le jeu de données en ensembles 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é l'utilisation de la validation croisée k-fold pour entraîner le modèle YOLO de détection d'objets. Nous avons appris à diviser l'ensemble d'entraînement et de validation en K partitions et à utiliser le tableau de ratios généré pour examiner l'équilibre des classes après une répartition aléatoire.
Nous avons également vu comment créer des DataFrames de rapport pour visualiser les partitions de données et la répartition des étiquettes dans celles-ci, ce qui nous donne une vue claire de la structure de nos ensembles d'entraînement et de validation.
Nous avons aussi enregistré nos résultats pour pouvoir les consulter ultérieurement, ce qui peut s'avérer particulièrement utile dans les projets à grande échelle ou lors du diagnostic des performances du modèle.
Enfin, nous avons mis en œuvre l'entraînement proprement dit en entraînant le modèle sur chaque partition dans une boucle, puis en enregistrant les résultats pour les analyser et les comparer.
La validation croisée k-fold est une méthode robuste pour tirer le meilleur parti des données disponibles et contribue à garantir que les performances de ton modèle sont fiables et cohérentes sur différents sous-ensembles de données. Elle permet d'obtenir un modèle plus généralisable et plus fiable, moins susceptible de surapprendre les motifs propres à certaines données.
Même si nous avons utilisé YOLO dans ce guide, n'oublie pas que ces étapes sont largement transposables à d'autres modèles de machine learning. En les comprenant, tu pourras appliquer efficacement la validation croisée à tes propres projets de machine learning.
FAQ#
La validation croisée k-fold est une technique qui consiste à diviser le jeu de données en « k » sous-ensembles (plis) afin d'évaluer les performances du modèle de façon plus fiable. Chaque pli sert à la fois de données de validation et de données d'entraînement. Pour la détection d'objets, la validation croisée k-fold contribue à garantir que les performances de ton modèle Ultralytics YOLO sont robustes et généralisables d'une partition de données à l'autre, ce qui améliore sa fiabilité. Pour des instructions détaillées sur la configuration de la validation croisée k-fold avec Ultralytics YOLO, consulte Validation croisée k-fold avec Ultralytics.
Pour mettre en œuvre la validation croisée k-fold avec Ultralytics YOLO, suis 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 avec
KFolddesklearn.model_selection. - Entraîne le modèle YOLO sur chaque partition.
Pour un guide complet, consulte la section Partitionnement du jeu de données en k-folds de notre documentation.
Le workflow de ce guide cible le format de détection YOLO, mais la même approche s'adapte à toutes les tâches YOLO : la tâche détermine la façon de composer les plis, pas l'utilité de la validation croisée :
Tâche Conception des plis detectRépartis les données au niveau de l'image, en équilibrant la distribution des objets et des classes entre les plis. Regroupe les images associées (même patient, séquence vidéo, caméra ou site) dans un seul pli. segmentUtilise la même stratégie au niveau de l'image que pour la détection, en veillant également à ce que les masques et les classes soient représentés dans chaque pli. classifyPrivilégie les plis stratifiés afin d'équilibrer la fréquence des classes entre l'entraînement et la validation. poseEffectue la répartition par sujet ou par séquence, afin qu'une même personne ou un même animal n'apparaisse jamais des deux côtés d'un pli. obbRépartis les données au niveau de l'image et conserve ensemble les tuiles ou les recadrages issus d'une même scène — un point particulièrement important pour les images aériennes. Quelle que soit la tâche, évite de répartir les échantillons quasi identiques ou apparentés dans des plis opposés : ce type de fuite de données gonfle les métriques de validation bien au-delà des performances que le modèle atteindra en production.
Ultralytics YOLO offre une détection d'objets en temps réel à la pointe de la technologie, avec une précision et une efficacité élevées. Polyvalent, il prend en charge plusieurs tâches de vision par ordinateur, comme la détection, la segmentation d'instances, la segmentation sémantique et la classification. Il s'intègre également parfaitement à des outils comme Ultralytics Platform pour entraîner et déployer des modèles sans code. Pour en savoir plus, découvre les avantages et les fonctionnalités sur notre page Ultralytics YOLO.
Tes annotations doivent respecter le format de détection YOLO. Chaque fichier d'annotation doit indiquer la classe de l'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 en savoir plus sur le formatage correct des annotations, consulte le guide du format de détection YOLO.
Oui, tu peux utiliser la validation croisée k-fold avec n'importe quel jeu de données personnalisé, à condition que les annotations soient au format de détection YOLO. Remplace les chemins d'accès au jeu de données et les étiquettes de classe par ceux qui correspondent à ton jeu de données personnalisé. Cette flexibilité permet à tout projet de détection d'objets de bénéficier d'une évaluation robuste du modèle par validation croisée k-fold. Pour un exemple pratique, consulte notre section Génération de vecteurs de caractéristiques.