Passer au contenu

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.

Aperçu de la validation croisée 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. Utilisez pip (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.
  • 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.

Générer des vecteurs de caractéristiques pour l'ensemble de données de détection d'objets

  1. Commencez par créer un nouveau example.py Fichier Python pour les étapes ci-dessous.

  2. 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'
    
  3. 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())
    
  4. 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)
    
  5. 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`
    
  6. 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

  1. Nous allons maintenant utiliser le KFold classe depuis sklearn.model_selection pour générer k 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=MM est un entier choisi, vous pouvez obtenir des résultats reproductibles.
    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))
    
  2. L'ensemble de données a maintenant été divisé en k folds, chacun ayant une liste de train et val 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"
    
  3. 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 dans train.

    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.

  4. 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,
            )
    
  5. 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

  1. 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")
    
  2. 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 et name 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
    
  3. 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 :

  1. Vérifiez que les annotations sont au format de détection YOLO.
  2. Utiliser des bibliothèques python comme sklearn, pandas, et pyyaml.
  3. Créer des vecteurs de caractéristiques à partir de votre ensemble de données.
  4. Divisez votre ensemble de données en utilisant KFold de sklearn.model_selection.
  5. 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.



📅 Créé il y a 1 an ✏️ Mis à jour il y a 2 mois

Commentaires