Ultralytics YOLO27:
Get Started

Ultralyticsで行うK分割交差検証#

はじめに#

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

K-fold cross validation data splitting

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

始めましょう。

セットアップ#

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

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

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

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

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

  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. まず、以下の手順を記述する新しいPythonファイルexample.pyを作成します。

  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分割交差検証を適用できます。

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

  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)  # 再現可能な結果を得るためにrandom_stateを設定
    
    kfolds = list(kf.split(labels_df))
  2. データセットはk個のfoldに分割され、各foldにはtrainとvalのインデックスのリストが含まれます。結果をわかりやすく表示するために、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分割の分割結果とラベル分布のDataFrameをCSVファイルとして保存し、後から参照できます。

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

K分割データ分割を使用してYOLOを学習する#

  1. まず、YOLOモデルを読み込みます。

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"  # 小型モデルにはyolo26n.ptを使用
    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
    
    # データセットをtrain/val/testに自動分割
    autosplit(path="path/to/images", weights=(0.8, 0.2, 0.0), annotated_only=True)

結論#

このガイドでは、YOLO物体検出モデルの学習にK分割交差検証を使用する手順を説明しました。学習用と検証用のデータをK個の分割に分ける方法と、生成された比率テーブルを使ってランダム分割後のクラスバランスを確認する方法を紹介しました。

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

任意で、後から参照できるようにレコードを保存しました。これは大規模プロジェクトやモデルのパフォーマンスのトラブルシューティングで特に役立つ場合があります。

最後に、各分割をループ処理して実際にモデルを学習し、さらなる分析や比較に使えるよう学習結果を保存しました。

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

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

よくある質問#

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

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

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

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

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

    タスクfoldの設計
    detect画像単位で分割し、fold間でオブジェクトとクラスの分布を均衡させます。関連する画像(同じ患者、動画シーケンス、カメラ、または場所の画像)は、同じfold内にまとめます。
    segment検出と同じ画像単位の方法を使い、さらに各foldでマスクとクラスの網羅性を維持します。
    classify学習データと検証データ間でクラスの頻度が均衡するよう、層化foldを優先します。
    pose被験者またはシーケンス単位で分割し、同じ人物や動物がfoldの両側に現れないようにします。
    obb画像単位で分割し、特に航空画像では、同じシーンのタイルやクロップを同じfoldにまとめます。

    タスクにかかわらず、類似画像や関連サンプルが反対側のfoldに入らないようにします。この種のデータリークは検証指標を押し上げ、実運用でモデルが達成する値を大きく上回る結果につながります。

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

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

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

コメント