YOLO Vision 2026:

Ultralyticsを使用したK-Fold交差検証#

はじめに#

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

K-fold cross validation data splitting

プロジェクトがFruit Detectionデータセットを使う場合でもカスタムデータソースを使う場合でも、このチュートリアルはmachine learningモデルの信頼性と堅牢性を高めるために、K-Fold交差検証を理解して適用するのに役立つことを目的としています。このチュートリアルではk=5分割を適用しますが、最適な分割数はデータセットやプロジェクトの詳細によって異なる場合があることに留意してください。K-Fold交差検証は、データセットが小さい、ノイズが多い、または変動が大きい場合に最大の価値を発揮します。大規模で多様なデータセットの場合は、通常、適切に構築されたtrain/val/test分割で十分です。

それでは始めましょう。

セットアップ#

  • アノテーションはYOLO検出フォーマットである必要があります。

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

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

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

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • このチュートリアルではk=5分割を使用します。ただし、特定のデータセットに最適な分割数を決定する必要があります。

  1. プロジェクト用に新しいPython仮想環境(venv)を開始し、有効化します。pip(または好みのパッケージマネージャー)を使用して以下をインストールします:

    • Ultralyticsライブラリ:pip install -U ultralytics。または、公式リポジトリをクローンすることもできます。
    • 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 Cross Validationをオブジェクト検出データセットに適用できるようになります。

K-Foldデータセット分割#

  1. 次に、sklearn.model_selection から KFold クラスを使用して、データセットの 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分割に分割されており、それぞれが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. 次に、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分割交差検証のテクニックは、利用可能なデータを最大限に活用するための堅牢な方法であり、さまざまなデータサブセット間でモデルのパフォーマンスが信頼性高く一貫していることを保証するのに役立ちます。これにより、特定のデータパターンに過学習しにくい、より汎用性が高く信頼性の高いモデルが実現します。

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

よくある質問 (FAQ)#

  • K分割交差検証は、モデルのパフォーマンスをより信頼性高く評価するために、データセットを「k」個のサブセット(分割)に分割するテクニックです。各分割は、トレーニングデータと検証データの両方として機能します。物体検出の文脈において、K分割交差検証を使用することは、異なるデータ分割にわたってUltralytics YOLOモデルのパフォーマンスが頑健で汎用性があることを保証し、その信頼性を高めるのに役立ちます。Ultralytics YOLOでのK分割交差検証のセットアップに関する詳細な手順については、K分割交差検証とUltralyticsを参照してください。

  • Ultralytics YOLOでK-Fold交差検証を実装するには、以下の手順に従う必要があります:

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

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

  • このガイドのワークフローはYOLO検出形式を対象としていますが、同じアプローチがすべてのYOLOタスクに適応します。タスクによって分割の構成方法が変わるのであって、交差検証が役立つかどうか自体が変わるわけではありません:

    タスク分割の設計
    detect画像レベルで分割し、分割間でオブジェクトとクラスの分布をバランスさせます。関連する画像(同じ患者、ビデオシーケンス、カメラ、またはサイト)を1つの分割内に保持します。
    segment検出と同じ画像レベルの戦略を使用し、さらにすべての分割でマスクとクラスのカバレッジを維持します。
    classifyトレーニングとバリデーションの間でクラスの頻度がバランスされた状態を保つために、層化分割を優先します。
    pose同じ人物や動物が分割の両方に現れないように、被験者またはシーケンスごとに分割します。
    obb画像レベルで分割し、同じシーンからのタイルやクロップをまとめます。これは特に空中画像において重要です。

    タスクが何であれ、ほぼ重複したサンプルや関連するサンプルを対立する分割に入れないでください。そのようなデータの漏洩は、モデルが本番環境で達成する性能をはるかに超えてバリデーション指標を誇張させます。

  • Ultralytics YOLO は、高い accuracy と効率性を備えた最先端のリアルタイム物体検出を提供します。汎用性が高く、detectioninstance segmentationsemantic segmentationclassification などの複数の computer vision タスクをサポートしています。さらに、コード不要のモデルトレーニングとデプロイメントのために Ultralytics Platform などのツールとシームレスに統合されます。詳細については、Ultralytics YOLO page で利点と機能をご確認ください。

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

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

コメント