Skip to content

Validation croisée K-Fold avec Ultralytics

Introduction

This comprehensive guide illustrates the implementation of K-Fold Cross Validation for object detection datasets within the Ultralytics ecosystem. We'll leverage the YOLO detection format and key Python libraries such as sklearn, pandas, and PyYaml to guide you through the necessary setup, the process of generating feature vectors, and the execution of a K-Fold dataset split.

Aperçu de la validation croisée K-Fold

Whether your project involves the Fruit Detection dataset or a custom data source, this tutorial aims to help you comprehend and apply K-Fold Cross Validation to bolster the reliability and robustness of your machine learning models. While we're applying 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. Utilise pip (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.
  • 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.

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 Python pour suivre les étapes ci-dessous.

  2. Procède à la récupération de tous les fichiers d'étiquettes de 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'
    
  3. Maintenant, lis le contenu du fichier YAML du jeu de données et extrais les indices des étiquettes de classe.

    yaml_file = "path/to/data.yaml"  # your data YAML with data directories and names dictionary
    with open(yaml_file, "r", encoding="utf8") as y:
        classes = yaml.safe_load(y)["names"]
    cls_idx = sorted(classes.keys())
    
  4. Initialise un fichier vide pandas DataFrame.

    import pandas as pd
    
    indx = [label.stem for label in labels]  # uses base filename as ID (no extension)
    labels_df = pd.DataFrame([], columns=cls_idx, index=indx)
    
  5. Compte les instances de chaque classe-étiquette présente dans les fichiers d'annotation.

    from collections import Counter
    
    for label in labels:
        lbl_counter = Counter()
    
        with open(label, "r") 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(" ")[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 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

  1. Nous allons maintenant utiliser le KFold de la classe sklearn.model_selection pour générer k 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.
    from sklearn.model_selection import KFold
    
    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 des données a maintenant été divisé en k plis, 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=indx, columns=folds)
    
    for idx, (train, val) in enumerate(kfolds, start=1):
        folds_df[f"split_{idx}"].loc[labels_df.iloc[train].index] = "train"
        folds_df[f"split_{idx}"].loc[labels_df.iloc[val].index] = "val"
    
  3. 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 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 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.

  4. Ensuite, nous créons les répertoires et les fichiers YAML des jeux de données pour chaque fractionnement.

    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 (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,
            )
    
  5. 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.
    import shutil
    
    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

  1. Charge d'abord le modèle YOLO .

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"
    model = YOLO(weights_path, task="detect")
    
  2. 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 et name 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 !

FAQ

Qu'est-ce que la validation croisée K-Fold et pourquoi est-elle utile dans la détection d'objets ?

K-Fold Cross Validation is a technique where the dataset is divided into 'k' subsets (folds) to evaluate model performance more reliably. Each fold serves as both training and validation data. In the context of object detection, using K-Fold Cross Validation helps to ensure your Ultralytics YOLO model's performance is robust and generalizable across different data splits, enhancing its reliability. For detailed instructions on setting up K-Fold Cross Validation with Ultralytics YOLO, refer to K-Fold Cross Validation with Ultralytics.

Comment mettre en œuvre la validation croisée K-Fold à l'aide de Ultralytics YOLO ?

Pour mettre en œuvre la validation croisée K-Fold avec Ultralytics YOLO , tu dois suivre les étapes suivantes :

  1. Vérifie que les annotations sont au format de détectionYOLO .
  2. Utilise les bibliothèques Python comme sklearn, pandaset pyyaml.
  3. Crée des vecteurs de caractéristiques à partir de ton ensemble de données.
  4. Divise ton jeu de données en utilisant KFold de sklearn.model_selection.
  5. Entraîne le modèle YOLO sur chaque fractionnement.

Pour un guide complet, consulte la section Fractionnement des ensembles de données K-Fold dans notre documentation.

Pourquoi devrais-je utiliser Ultralytics YOLO pour la détection d'objets ?

Ultralytics YOLO offers state-of-the-art, real-time object detection with high accuracy and efficiency. It's versatile, supporting multiple computer vision tasks such as detection, segmentation, and classification. Additionally, it integrates seamlessly with tools like Ultralytics HUB for no-code model training and deployment. For more details, explore the benefits and features on our Ultralytics YOLO page.

Comment puis-je m'assurer que mes annotations sont dans le bon format pour Ultralytics YOLO ?

Your annotations should follow the YOLO detection format. Each annotation file must list the object class, alongside its bounding box coordinates in the image. The YOLO format ensures streamlined and standardized data processing for training object detection models. For more information on proper annotation formatting, visit the YOLO detection format guide.

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, tu peux utiliser la validation croisée K-Fold avec n'importe quel ensemble de données personnalisé, à condition que les annotations soient au format de détection YOLO . Remplace les chemins d'accès à l'ensemble de données et les étiquettes de classe par ceux qui sont spécifiques à ton 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, consulte la section Génération de vecteurs de caractéristiques.


📅 Created 11 months ago ✏️ Updated 17 days ago

Commentaires