Ultralyticsを使用したK-分割交差検証
はじめに
この包括的なガイドでは、Ultralyticsエコシステム内での物体検出データセットに対するK-分割交差検証の実装について説明します。必要なセットアップ、特徴ベクトルの生成プロセス、およびK-分割データセットの実行について、YOLO検出形式とsklearn、pandas、PyYamlなどの主要なPythonライブラリを活用して説明します。
プロジェクトに Fruit Detection データセットまたはカスタムデータソースが含まれているかどうかにかかわらず、このチュートリアルは、K-Fold Cross Validation を理解し、適用して、信頼性と堅牢性を高めるのに役立つことを目的としています。 機械学習 モデルを適用する際に、 k=5
このチュートリアルのフォールドについては、データセットとプロジェクトの具体的な内容によって最適なフォールド数が異なる可能性があることに注意してください。
それでは早速、詳しく見ていきましょう!
セットアップ
-
アノテーションは、YOLO検出形式である必要があります。
-
このガイドでは、アノテーションファイルがローカルで利用可能であることを前提としています。
-
今回のデモンストレーションでは、Fruit Detectionデータセットを使用します。
- このデータセットには、合計8479枚の画像が含まれています。
- これには6つのクラスラベルが含まれており、それぞれの総インスタンス数が以下にリストされています。
クラスラベル | インスタンス数 |
---|---|
Apple | 7049 |
Grapes | 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
.
- Ultralyticsライブラリ:
-
アノテーションがYOLO検出形式であることを確認してください。
- このチュートリアルでは、すべてのアノテーションファイルが以下にあります。
Fruit-Detection/labels
ディレクトリ。
- このチュートリアルでは、すべてのアノテーションファイルが以下にあります。
物体検出データセットのフィーチャーベクトルの生成
-
まず、新しい
example.py
以下の手順のためのpythonファイル。 -
データセットのすべてのラベルファイルを取得してください。
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'
-
次に、データセットYAMLファイルの内容を読み取り、クラスラベルのインデックスを抽出します。
import yaml yaml_file = "path/to/data.yaml" # your data YAML with data directories and names dictionary with open(yaml_file, encoding="utf8") as y: classes = yaml.safe_load(y)["names"] cls_idx = sorted(classes.keys())
-
空の初期化
pandas
DataFrame。import pandas as pd index = [label.stem for label in labels] # uses base filename as ID (no extension) labels_df = pd.DataFrame([], columns=cls_idx, index=index)
-
アノテーションファイルに存在する各クラスラベルのインスタンス数をカウントします。
from collections import Counter for label in labels: lbl_counter = Counter() with open(label) 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(" ", 1)[0])] += 1 labels_df.loc[label.stem] = lbl_counter labels_df = labels_df.fillna(0.0) # replace `nan` values with `0.0`
-
以下は、データが入力された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-分割データセット分割
-
次に、以下を使用します
KFold
classをsklearn.model_selection
を生成するにはk
データセットの分割。- 重要:
- 設定
shuffle=True
分割におけるクラスのランダムな分布を保証します。 - 設定により
random_state=M
場所:M
は選択された整数であるため、再現可能な結果が得られます。
- 設定
import random from sklearn.model_selection import KFold random.seed(0) # for reproducibility ksplit = 5 kf = KFold(n_splits=ksplit, shuffle=True, random_state=20) # setting random_state for repeatable results kfolds = list(kf.split(labels_df))
- 重要:
-
データセットは現在、以下に分割されています。
k
フォールド。それぞれがリストを持っていますtrain
およびval
インデックス。これらの結果をより明確に表示するために、DataFrameを構築します。folds = [f"split_{n}" for n in range(1, ksplit + 1)] folds_df = pd.DataFrame(index=index, columns=folds) for i, (train, val) in enumerate(kfolds, start=1): folds_df[f"split_{i}"].loc[labels_df.iloc[train].index] = "train" folds_df[f"split_{i}"].loc[labels_df.iloc[val].index] = "val"
-
次に、各フォールドのクラスラベルの分布を、存在するクラスの比率として計算します。
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
理想的なシナリオは、すべてのクラス比が、分割ごと、およびクラス全体で、合理的に類似していることです。ただし、これはデータセットの特性に左右されます。
-
次に、分割ごとにディレクトリとデータセット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 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, )
-
最後に、分割ごとに画像とラベルをそれぞれのディレクトリ('train'または'val')にコピーします。
- 注意: コードのこの部分に必要な時間は、データセットのサイズとシステムハードウェアによって異なります。
import shutil from tqdm import tqdm for image, label in tqdm(zip(images, labels), total=len(images), desc="Copying files"): 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を学習する
-
まず、YOLOモデルをロードします。
from ultralytics import YOLO weights_path = "path/to/weights.pt" # use yolo11n.pt for a small model model = YOLO(weights_path, task="detect")
-
次に、データセット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
-
自動的なデータセット分割には、Ultralytics data.utils.autosplit関数も使用できます。
from ultralytics.data.utils import autosplit # Automatically split dataset into train/val/test autosplit(path="path/to/images", weights=(0.8, 0.2, 0.0), annotated_only=True)
結論
このガイドでは、YOLOオブジェクト検出モデルのトレーニングにK-Fold交差検証を使用するプロセスを解説しました。データセットをK個のパーティションに分割し、異なるフォールド間でバランスの取れたクラス分布を確保する方法を学びました。
また、レポートDataFrameを作成して、データ分割とこれらの分割全体のラベル分布を視覚化する手順についても調査しました。これにより、トレーニングセットと検証セットの構造を明確に把握できました。
オプションで、今後の参照用に記録を保存しました。これは、大規模プロジェクトやモデルのパフォーマンスのトラブルシューティングを行う場合に特に役立ちます。
最後に、ループ内の各分割を使用して実際のモデルトレーニングを実装し、トレーニング結果を保存してさらに分析および比較しました。
このK-Fold交差検証の手法は、利用可能なデータを最大限に活用するための堅牢な方法であり、モデルのパフォーマンスがさまざまなデータサブセット間で信頼性が高く一貫性があることを保証するのに役立ちます。これにより、特定のデータパターンに過学習しにくい、より汎化可能で信頼性の高いモデルが得られます。
このガイドではYOLOを使用していますが、これらの手順は他の機械学習モデルにもほぼ適用できることに注意してください。これらの手順を理解することで、独自の機械学習プロジェクトで効果的にクロスバリデーションを適用できます。ハッピーコーディング!
よくある質問
K分割交差検証とは何ですか?また、オブジェクト検出においてなぜ有用なのですか?
K-分割交差検証は、モデルのパフォーマンスをより確実に評価するために、データセットを「k」個のサブセット(フォールド)に分割する手法です。各フォールドは、トレーニングデータと検証データの両方として機能します。オブジェクト検出のコンテキストでは、K-分割交差検証を使用すると、Ultralytics YOLOモデルのパフォーマンスが、さまざまなデータ分割にわたって堅牢で汎用性があることが保証され、信頼性が向上します。Ultralytics YOLOを使用したK-分割交差検証の設定に関する詳細な手順については、Ultralyticsを使用したK-分割交差検証を参照してください。
Ultralytics YOLOを使用してK-Fold交差検証を実装するにはどうすればよいですか?
Ultralytics YOLOでK-分割交差検証を実装するには、以下の手順に従ってください。
- アノテーションがYOLO検出形式であることを確認してください。
- 次のようなpythonライブラリを使用します。
sklearn
,pandas
、およびpyyaml
. - データセットから特徴ベクトルを作成します。
- データセットを分割するには
KFold
fromsklearn.model_selection
. - 各分割でYOLOモデルを学習します。
包括的なガイドについては、ドキュメントのK-分割データセットのセクションを参照してください。
オブジェクト検出にUltralytics YOLOを使用する理由は何ですか?
Ultralytics YOLOは、最先端のリアルタイム物体検出を提供し、高い精度と効率を実現します。検出、セグメンテーション、分類など、複数のコンピュータビジョンタスクをサポートする汎用性も備えています。さらに、Ultralytics HUBなどのツールとシームレスに統合することで、コード不要のモデルトレーニングとデプロイメントが可能です。詳細については、Ultralytics YOLOページで利点と機能をご確認ください。
Ultralytics YOLOでアノテーションが正しい形式であることを確認するにはどうすればよいですか?
アノテーションは、YOLO検出形式に従う必要があります。各アノテーションファイルには、オブジェクトクラスと、画像内のそのバウンディングボックスの座標をリストする必要があります。YOLO形式は、オブジェクト検出モデルのトレーニングのための合理化された標準化されたデータ処理を保証します。適切なアノテーション形式の詳細については、YOLO検出形式ガイドをご覧ください。
Fruit Detection以外のカスタムデータセットでK-Fold Cross Validationを使用できますか?
はい、アノテーションがYOLO検出形式である限り、任意のカスタムデータセットでK-Fold Cross Validationを使用できます。データセットのパスとクラスラベルを、カスタムデータセットに固有のものに置き換えてください。この柔軟性により、あらゆるオブジェクト検出プロジェクトが、K-Fold Cross Validationを使用した堅牢なモデル評価の恩恵を受けることができます。具体的な例については、フィーチャーベクトルの生成のセクションをご覧ください。