Ultralytics YOLO27:

UltralyticsによるK-Fold交差検証#

はじめに#

この包括的なガイドでは、Ultralyticsエコシステム内の物体検出データセットにK-Fold交差検証を実装する方法を説明します。YOLO検出フォーマットと、sklearn、pandas、PyYAMLなどの主要なPythonライブラリを使用して、必要なセットアップ、特徴ベクトルの生成プロセス、K-Foldデータセット分割の実行方法を順に解説します。

K-fold cross validation data splitting

プロジェクトでFruit Detectionデータセットを使用する場合でも、カスタムデータソースを使用する場合でも、このチュートリアルでは、K-Fold交差検証を理解して適用し、機械学習モデルの信頼性と堅牢性を高める方法を説明します。このチュートリアルではk=5個のfoldを使用しますが、最適なfold数はデータセットやプロジェクトの詳細によって異なる場合があります。K-Fold交差検証は、データセットが小さい、ノイズが多い、または変動性が高い場合に最も効果を発揮します。大規模で多様なデータセットでは、適切に構成されたtrain/val/test分割で通常は十分です。

それでは始めましょう。

セットアップ#

  • アノテーションはYOLO検出フォーマットで記述してください。

  • このガイドでは、アノテーションファイルがローカルで利用可能であることを前提としています。

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

    • このデータセットには合計8479枚の画像が含まれています。
    • 6つのクラスラベルが含まれており、それぞれのインスタンス総数を以下に示します。
クラスラベルインスタンス数
Apple7049
Grapes7202
Pineapple1613
Orange15549
Banana3536
Watermelon1976
  • 必要なPythonパッケージは次のとおりです。

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • このチュートリアルではk=5個のfoldを使用します。ただし、使用するデータセットに最適なfold数を決定してください。

  1. プロジェクト用に新しいPython仮想環境(venv)を作成して有効化します。pip(または任意のパッケージマネージャー)を使用して、次のパッケージをインストールします。

    • Ultralyticsライブラリ:pip install -U ultralytics。または、公式のリポジトリをcloneできます。
    • Scikit-learn、pandas、PyYAML:pip install -U scikit-learn pandas pyyaml
  2. アノテーションがYOLO検出フォーマットで記述されていることを確認します。

    • このチュートリアルでは、すべてのアノテーションファイルがFruit-Detection/labelsディレクトリにあります。

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

  1. まず、以下の手順用に新しいexample.py Pythonファイルを作成します。

  2. パッケージのデータセットオーナーを介して、設定済みの学習画像と検証画像を読み込み、テスト分割は変更せずに保持します。

    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]
  3. 次に、データセットYAMLファイルの内容を読み込み、クラスラベルのインデックスを抽出します。

    classes = data["names"]
    cls_idx = sorted(classes.keys())
  4. 空のpandas DataFrameを初期化します。

    import pandas as pd
    
    labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images)
  5. アノテーションファイルに存在する各クラスラベルのインスタンス数を数えます。

    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())
  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-Foldデータセット分割#

  1. ここでは、sklearn.model_selectionKFoldクラスを使用して、データセットをk個に分割します。

    • 重要:
      • shuffle=Trueを設定すると、分割後の各データにクラスがランダムに分布します。
      • Mに選択した整数を指定してrandom_state=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個のfoldに分割され、各foldにはtrainvalのインデックスリストが含まれています。これらの結果をより明確に表示するため、DataFrameを作成します。

    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"
  3. 次に、各foldのクラスラベルの分布を、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を作成します。テキストリストを使用すると、データセットをk回コピーせずに済みます。

    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,
            )

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

必要に応じて、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"  # use yolo26n.pt for a small model
    model = YOLO(weights_path, task="detect")
  2. 次に、データセットYAMLファイルを反復処理して学習を実行します。結果は、project引数とname引数で指定したディレクトリに保存されます。デフォルトでは、このディレクトリは「runs/detect/train#」で、#は整数のインデックスです。

    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. Ultralytics data.split.autosplit関数を使用して、データセットを自動的に分割することもできます。

    from ultralytics.data.split import autosplit
    
    # Automatically split dataset into train/val/test
    autosplit(path="path/to/images", weights=(0.8, 0.2, 0.0), annotated_only=True)

まとめ#

このガイドでは、K-Fold交差検証を使用してYOLO物体検出モデルを学習するプロセスを説明しました。学習用と検証用のデータプールをK個のパーティションに分割し、生成された比率テーブルを使用して、ランダム分割後のクラスバランスを確認する方法を学びました。

また、データ分割と各分割におけるラベル分布を可視化するレポート用DataFrameの作成手順についても説明し、学習セットと検証セットの構造を明確に把握できるようにしました。

必要に応じてレコードを後で参照できるよう保存しました。これは、大規模なプロジェクトやモデル性能のトラブルシューティングで特に役立ちます。

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

このK-Fold交差検証の手法は、利用可能なデータを最大限に活用する堅牢な方法であり、異なるデータサブセット間でモデル性能の信頼性と一貫性を確保するのに役立ちます。その結果、特定のデータパターンに過学習しにくく、より汎化性能と信頼性の高いモデルを得られます。

このガイドではYOLOを使用しましたが、これらの手順の大部分は他の機械学習モデルにも適用できます。これらの手順を理解することで、自身の機械学習プロジェクトに交差検証を効果的に適用できます。

FAQ#

  • K-Fold交差検証は、モデル性能をより信頼性高く評価するために、データセットを「k」個のサブセット(fold)に分割する手法です。各foldは学習データと検証データの両方として使用されます。物体検出では、K-Fold交差検証を使用することで、Ultralytics YOLOモデルの性能が異なるデータ分割間で堅牢かつ汎化可能であることを確認し、信頼性を高められます。Ultralytics YOLOでK-Fold交差検証を設定する詳しい手順については、UltralyticsによるK-Fold交差検証を参照してください。

  • Ultralytics YOLOでK-Fold交差検証を実装するには、次の手順に従います。

    1. アノテーションがYOLO検出フォーマットで記述されていることを確認します。
    2. sklearnpandaspyyamlなどのPythonライブラリを使用します。
    3. データセットから特徴ベクトルを作成します。
    4. sklearn.model_selectionKFoldを使用してデータセットを分割します。
    5. 各分割でYOLOモデルを学習します。

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

  • このガイドのワークフローはYOLO検出フォーマットを対象としていますが、同じアプローチをすべてのYOLOタスクに適用できます。タスクによって変わるのはfoldの構成方法であり、交差検証の有効性ではありません。

    タスクFoldの設計
    detect画像単位で分割し、fold間で物体とクラスの分布のバランスを取ります。関連する画像(同じ患者、動画シーケンス、カメラ、またはサイトの画像)は、1つのfold内にまとめます。
    segment検出と同じ画像単位の戦略を使用し、各foldでマスクとクラスのカバレッジも維持します。
    classify学習と検証の間でクラス頻度のバランスが保たれるよう、層化foldを優先します。
    pose同じ人物や動物がfoldの両側に現れないよう、被験者またはシーケンス単位で分割します。
    obb画像単位で分割し、同じシーンのタイルやcropをまとめます。これは特に航空画像で重要です。

    タスクにかかわらず、ほぼ重複するサンプルや関連するサンプルは、対向するfoldに入れないでください。このようなリーケージにより、検証メトリクスが本番環境でモデルが達成する値を大幅に上回ってしまいます。

  • Ultralytics YOLOは、高い精度と効率を備えた、最先端のリアルタイム物体検出を提供します。複数のコンピュータビジョンタスクに対応する汎用性を備えており、検出インスタンスセグメンテーションセマンティックセグメンテーション分類などをサポートします。さらに、Ultralytics Platformなどのツールとシームレスに統合でき、コード不要のモデル学習とデプロイに対応します。詳しくは、Ultralytics YOLOページで利点と機能を確認してください。

  • アノテーションはYOLO検出フォーマットに従う必要があります。各アノテーションファイルには、画像内のバウンディングボックス座標とともに、物体クラスを記述する必要があります。YOLOフォーマットにより、物体検出モデルの学習に向けたデータ処理を効率化し、標準化できます。適切なアノテーション形式の詳細については、YOLO検出フォーマットガイドを参照してください。

  • はい、アノテーションがYOLO検出フォーマットで記述されていれば、任意のカスタムデータセットでK-Fold交差検証を使用できます。データセットのパスとクラスラベルを、カスタムデータセット固有のものに置き換えてください。この柔軟性により、あらゆる物体検出プロジェクトで、K-Fold交差検証による堅牢なモデル評価のメリットを得られます。実践的な例については、特徴ベクトルの生成セクションを参照してください。

コメント