コンテンツへスキップ

Kフォールド・クロス・バリデーションUltralytics

はじめに

この包括的なガイドでは、Ultralytics エコシステム内のオブジェクト検出データセットに対する K-Fold Cross Validation の実装について説明します。YOLO 検出フォーマットと、sklearn、pandas、PyYaml などの主要なPython ライブラリを活用して、必要なセットアップ、特徴ベクトルの生成プロセス、K-Fold データセット分割の実行をガイドします。

Kフォールド交差検証の概要

このチュートリアルでは、Fruit Detection データセットまたはカスタム・データ・ソースのいずれを使用するプロジェクトであっても、K-Fold Cross Validation を理解し適用することで、機械学習モデルの信頼性と頑健性を高めることを目的としています。このチュートリアルでは k=5 このチュートリアルでは、最適な回数はデータセットやプロジェクトの仕様によって異なることを念頭に置いています。

では、さっそく見ていこう!

セットアップ

  • 注釈は、YOLO 検出フォーマットでなければなりません。

  • このガイドでは、注釈ファイルがローカルにあることを前提としています。

  • このデモでは、Fruit Detectionデータセットを使用する。

    • このデータセットには合計8479枚の画像が含まれている。
    • これは6つのクラス・ラベルを含み、それぞれのインスタンス総数は以下の通りである。
クラスラベル インスタンス数
アップル 7049
ブドウ 7202
パイナップル 1613
オレンジ 15549
バナナ 3536
スイカ 1976
  • 必要なPython パッケージは以下の通り:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • このチュートリアルでは k=5 回である。しかし、特定のデータセットに最適なフォールド数を決定する必要がある。

  • 新しいPython 仮想環境 (venv)をプロジェクトに追加し、アクティベートする。使用方法 pip (またはお好みのパッケージ・マネージャー)をインストールする:

    • Ultralytics ライブラリー: pip install -U ultralytics.または、公式の レポ.
    • Scikit-learn、pandas、PyYAML: pip install -U scikit-learn pandas pyyaml.
  • 注釈がYOLO 検出フォーマットであることを確認してください。

    • このチュートリアルでは、すべての注釈ファイルは Fruit-Detection/labels ディレクトリにある。

物体検出データセットの特徴ベクトルの生成

  1. まず、新しい example.py Python 以下の手順のファイル。

  2. データセットのすべてのラベルファイルを取得する。

    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. さて、データセットのYAMLファイルの中身を読んで、クラスラベルのインデックスを抽出しましょう。

    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. 空の pandas データフレーム。

    import pandas as pd
    
    indx = [l.stem for l in labels]  # uses base filename as ID (no extension)
    labels_df = pd.DataFrame([], columns=cls_idx, index=indx)
    
  5. 注釈ファイルに存在する各クラスラベルのインスタンスを数える。

    from collections import Counter
    
    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`
    
  6. 以下は、入力されたDataFrameのサンプルビューです:

                                                           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
    

行はラベルファイルのインデックスを表し、それぞれがデータセット中の画像に対応し、列はクラスラベルのインデックスに対応する。各行は擬似的な特徴ベクトルを表し、データセットに存在する各クラスラベルのカウントを表す。このデータ構造により、物体検出データセットにK-Foldクロスバリデーションを適用することができる。

Kフォールド・データセット分割

  1. では KFold クラス sklearn.model_selection を生成する。 k データセットの分割。

    • 重要だ:
      • セッティング shuffle=True は、分割におけるクラスのランダムな分布を保証します。
      • セッティング random_state=M どこ M を整数にすれば、再現性のある結果を得ることができる。
    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. データセットは現在、以下のように分割されている。 k のリストを持つ。 train そして val インデックスを作成します。これらの結果をより明確に表示するために、DataFrameを作成する。

    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. では、各フォールドのクラス・ラベルの分布を、次のように計算する。 val に出席した。 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
    

    理想的なシナリオは、すべてのクラスの比率が、各スプリットで、またクラス間で、適度に似ていることです。しかし、これはデータセットの仕様に依存します。

  4. 次に、各スプリットのディレクトリとデータセットのYAMLファイルを作成します。

    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. 最後に、画像とラベルを各スプリットのディレクトリ('train'または'val')にコピーする。

    • 注:コードのこの部分に要する時間は、データセットのサイズとシステムのハードウェアによって異なります。
    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)
    

レコードの保存(オプション)

オプションで、K-Fold分割とラベル配布のDataFrameのレコードをCSVファイルとして保存し、後で参照することができます。

folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")

K-Fold データ分割を使用したトレーニングYOLO

  1. まず、YOLO のモデルをロードする。

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"
    model = YOLO(weights_path, task="detect")
    
  2. 次に、データセットのYAMLファイルを反復処理してトレーニングを実行します。結果は project そして name 引数で指定する。デフォルトでは、このディレクトリは'exp/runs#'であり、#は整数インデックスである。

    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
    

結論

このガイドでは、YOLO オブジェクト検出モデルのトレーニングに K-Fold 交差検証を使用するプロセスを探った。データセットをK個のパーティションに分割し、異なるフォールド間でバランスのとれたクラス分布を確保する方法を学びました。

また、レポートDataFramesを作成し、データの分割と分割されたラベル分布を可視化する手順も検討した。

これは、大規模なプロジェクトや、モデルの性能をトラブルシューティングするときに特に役立つ。

最後に、各スプリットを使用した実際のモデル学習をループで実行し、さらなる分析と比較のために学習結果を保存した。

このK-Foldクロスバリデーションのテクニックは、利用可能なデータを最大限に活用するロバストな方法であり、モデルのパフォーマンスが信頼でき、異なるデータ・サブセット間で一貫していることを保証するのに役立ちます。その結果、特定のデータ・パターンに過剰適合する可能性が低く、より一般化可能で信頼性の高いモデルが出来上がります。

このガイドではYOLO を使用したが、これらのステップは他の機械学習モデルにもほとんど適用できることを忘れないでほしい。これらのステップを理解することで、あなた自身の機械学習プロジェクトでクロスバリデーションを効果的に適用することができます。ハッピー・コーディング!

よくあるご質問

K-Foldクロスバリデーションとは何か、なぜ物体検出に有用なのか?

Kフォールド交差検証は、モデルの性能をより確実に評価するために、データセットを「k」個のサブセット(フォールド)に分割する手法である。それぞれのフォールドはトレーニングデータと検証データの両方の役割を果たします。物体検出の文脈では、K-Fold Cross Validation を使用することで、Ultralytics YOLO モデルの性能がロバストで、異なるデータ分割にわたって一般化可能であることを確認し、その信頼性を高めることができます。K-Fold Cross Validation withUltralytics YOLO のセットアップの詳細な手順については、K-Fold Cross Validation withUltralytics を参照してください。

Ultralytics YOLO を使って K-Fold 交差検証を実装するには?

K-Fold Cross Validation をUltralytics YOLO で実施するには、以下のステップに従う必要がある:

  1. 注釈がYOLO 検出フォーマットであることを確認する。
  2. のようなPython ライブラリを使用する。 sklearn, pandasそして pyyaml.
  3. データセットから特徴ベクトルを作成します。
  4. を使用してデータセットを分割します。 KFold より sklearn.model_selection.
  5. YOLO モデルを各スプリットでトレーニングする。

包括的なガイドについては、ドキュメントのK-Fold Dataset Splitセクションを参照してください。

なぜ物体検出にUltralytics YOLO を使う必要があるのか?

Ultralytics YOLO は、高い精度と効率性を備えた最先端のリアルタイム物体検出を提供します。検出、セグメンテーション、分類など、複数のコンピュータビジョンタスクをサポートする汎用性があります。さらに、Ultralytics HUB のようなツールとシームレスに統合し、コードなしでモデルのトレーニングやデプロイを行うことができます。詳細については、Ultralytics YOLO の ページで利点と機能をご覧ください。

注釈がUltralytics YOLO の正しいフォーマットであることを確認するにはどうすればよいですか?

注釈は、YOLO の検出形式に従ってください。各アノテーションファイルには、画像内のバウンディングボックス座標とともにオブジェクトクラスを記載する必要があります。YOLO フォーマットは、オブジェクト検出モデルをトレーニングするための合理的で標準化されたデータ処理を保証します。適切なアノテーションフォーマットの詳細については、YOLO 検出フォーマットガイドをご覧ください。

フルーツ検出以外のカスタムデータセットでK-Foldクロスバリデーションを使用できますか?

注釈がYOLO 検出形式であれば、どのようなカスタムデータセットでも K-Fold Cross Validation を使用できます。データセットのパスとクラスラベルは、カスタムデータセット固有のものに置き換えてください。この柔軟性により、どのような物体検出プロジェクトでも、K-Fold Cross Validationを使用したロバストなモデル評価の恩恵を受けることができます。実用的な例として、特徴ベクトルの生成のセクションをご覧ください。



作成日:2023-11-12 更新日:2024-07-05
作成者:glenn-jocher(10),IvorZhu331(1),Burhan-Q(1)

コメント