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 ensembles de données de détection d'objets dans l'écosystème Ultralytics . Nous nous appuierons sur le format de détection YOLO et les principales bibliothèques Python telles que sklearn, pandas et PyYaml pour te guider dans la configuration nécessaire, le processus de génération de vecteurs de caractéristiques et l'exécution d'un fractionnement de l'ensemble de données K-Fold.
Que ton projet implique l'ensemble 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 d'apprentissage automatique. Alors que nous appliquons k=5
plis pour ce tutoriel, garde à l'esprit que le nombre optimal de plis peut varier en fonction de ton ensemble de données et des spécificités de ton projet.
Sans plus attendre, plongeons dans le vif du sujet !
Mise en place
-
Tes annotations doivent être au format de détectionYOLO .
-
Ce guide suppose que les fichiers d'annotation sont disponibles localement.
-
Pour notre démonstration, nous utilisons l'ensemble de données de 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 indiqué ci-dessous.
Étiquette de classe | Nombre d'instances |
---|---|
Pomme | 7049 |
Raisins | 7202 |
Ananas | 1613 |
Orange | 15549 |
Banane | 3536 |
Melon d'eau | 1976 |
-
Les paquets nécessaires sur Python comprennent
ultralytics
sklearn
pandas
pyyaml
-
Ce tutoriel fonctionne avec
k=5
plis. Cependant, tu dois déterminer le meilleur nombre de plis pour ton ensemble de données spécifique. -
Lance un nouvel environnement virtuel Python (
venv
) pour ton projet et l'activer. Utilisepip
(ou ton gestionnaire de paquets préféré) pour l'installer :- La bibliothèque Ultralytics :
pip install -U ultralytics
. Tu peux aussi cloner le fichier officiel repo. - 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étectionYOLO .
- Pour ce tutoriel, tous les fichiers d'annotation se trouvent dans le dossier
Fruit-Detection/labels
répertoire.
- Pour ce tutoriel, tous les fichiers d'annotation se trouvent dans le dossier
Générer des vecteurs de caractéristiques pour l'ensemble de données de détection d'objets
-
Commence par créer un nouveau fichier Python et importe les bibliothèques nécessaires.
-
Procède à la récupération de tous les fichiers d'étiquettes de ton jeu de données.
-
Maintenant, lis le contenu du fichier YAML du jeu de données et extrais les indices des étiquettes de classe.
-
Initialise un fichier vide
pandas
DataFrame. -
Compte les instances de chaque classe-étiquette présente dans les fichiers d'annotation.
for label in labels: lbl_counter = Counter() with open(label,'r') as lf: lines = lf.readlines() for l in lines: # classes for YOLO label uses integer at first position of each line lbl_counter[int(l.split(' ')[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 peuplé :
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 à tes 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 ton ensemble de données. Cette structure de données permet d'appliquer la validation croisée K-Fold à un ensemble de données de détection d'objets.
Fractionnement de l'ensemble de données K-Fold
-
Nous allons maintenant utiliser le
KFold
de la classesklearn.model_selection
pour générerk
les divisions de l'ensemble de données.- Important :
- RĂ©glage
shuffle=True
assure une répartition aléatoire des classes dans tes fractionnements. - En réglant
random_state=M
oĂąM
est un nombre entier choisi, tu peux obtenir des résultats reproductibles.
- RĂ©glage
- Important :
-
L'ensemble des données a maintenant été divisé en
k
plis, chacun ayant une liste detrain
etval
indices. Nous allons construire un DataFrame pour afficher ces résultats plus clairement. -
Nous allons maintenant calculer la distribution des étiquettes de classe pour chaque pli sous la forme d'un ratio des classes présentes dans
val
aux personnes 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}'] = ratio
Le scénario idéal est que tous les ratios de classe soient raisonnablement similaires pour chaque fractionnement et pour toutes les classes. Cela dépend toutefois des spécificités de ton jeu de données.
-
Ensuite, nous créons les répertoires et les fichiers YAML des jeux de données pour chaque fractionnement.
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 (unchanged) 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 fractionnement.
- NOTE : Le temps nécessaire pour cette partie du code variera en fonction de la taille de ton ensemble de données et du matériel de ton système.
for image, label in zip(images, labels): 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)
Sauvegarder les enregistrements (optionnel)
En option, tu peux enregistrer les enregistrements des DataFrames de fractionnement K-Fold et de distribution d'étiquettes sous forme de fichiers CSV pour référence ultérieure.
folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")
Entraîne YOLO à l'aide de K-Fold Data Splits
-
Charge d'abord le modèle YOLO .
-
Ensuite, itère sur les fichiers YAML de l'ensemble de données pour exécuter la formation. Les résultats seront enregistrés dans un répertoire spécifié par l'option
project
etname
arguments. Par défaut, ce répertoire est 'exp/runs#' où # est un indice entier.results = {} # Define your additional arguments here batch = 16 project = 'kfold_demo' epochs = 100 for k in range(ksplit): dataset_yaml = ds_yamls[k] model.train(data=dataset_yaml,epochs=epochs, batch=batch, project=project) # include any train arguments results[k] = model.metrics # save output metrics for further analysis
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, en veillant à ce que la répartition des classes soit équilibrée entre 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 sur ces divisions, ce qui nous a permis d'avoir un aperçu clair de la structure de nos ensembles de formation et de validation.
En option, nous avons sauvegardé nos enregistrements pour les consulter ultérieurement, 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 mis en œuvre la formation réelle du modèle en utilisant chaque fractionnement dans une boucle, en sauvegardant nos résultats de formation 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 tes données disponibles, et elle permet de s'assurer que les performances de ton modèle sont fiables et cohérentes dans les différents sous-ensembles de données. Il en résulte un modèle plus généralisable et plus fiable, moins susceptible de s'adapter de façon excessive à des modèles de données spécifiques.
N'oublie pas que même si nous avons utilisé YOLO dans ce guide, ces étapes sont en grande partie transférables à d'autres modèles d'apprentissage automatique. Comprendre ces étapes te permet d'appliquer efficacement la validation croisée dans tes propres projets d'apprentissage automatique. Bon codage !