YOLOv8 チュートリアル シリーズ: 3. K 分割交差検証 - ラベル付きデータのすべての部分を最大限に活用します (yolov8 ターゲット検出 + k 分割交差検証メソッド)

YOLOv8 チュートリアル シリーズ: 3. K 分割交差検証 - ラベル付きデータのすべての部分を最大限に活用します (yolov8 ターゲット検出 + k 分割交差検証メソッド)

0 まえがき

K 分割相互検証 (K-Fold
Cross-Validation) は、モデルのパフォーマンスと汎化能力を推定するために機械学習で一般的に使用されるモデル評価手法です。その主な役割は、限定されたデータセットでモデルを評価し、新しいデータでモデルがどのように実行されるかをより正確に把握することです。

K 分割相互検証の基本的な考え方は、元のデータセットを K 個のサブセット (分割) に分割し、各サブセットを検証セットとして順番に使用し、他の K-1 サブセットをトレーニング セットとして使用することです。 K 回のトレーニングと検証。各検証の後、精度率、適合率、再現率など、検証セット上のモデルのパフォーマンス指標を計算します。最後に、K 個の検証のパフォーマンス メトリクスが、データセット全体におけるモデルのパフォーマンス推定値として平均化されます。

K 分割交差検証の機能には以下が含まれます。

  1. モデルのパフォーマンス評価: K 分割交差検証により、データセット上のモデルのパフォーマンスをより正確に評価でき、不均一なデータ分布によって引き起こされる不正確な評価結果の問題を回避できます。
  2. 一般化推定: 異なるトレーニング セットと検証セットで複数回評価することにより、モデルの一般化能力、つまりモデルが新しいデータに対してどの程度うまく機能するかをより適切に推定することができます。
  3. 過学習を減らす: K 分割相互検証は、モデルが過学習しているかどうかを検出するのに役立ちます。モデルがトレーニング セットでは良好に機能するが、検証セットでは不十分な場合は、過剰適合が発生している可能性があります。
  4. パラメーターの調整: 相互検証の各ラウンドで、モデルをさまざまなパラメーター設定でトレーニングし、検証セットで最高のパフォーマンスを発揮するパラメーターの組み合わせを見つけることができます。
  5. データ利用: K 分割相互検証では、各サンプルが異なる分割でのトレーニングと検証に使用されるため、データセット内のすべてのサンプルが最大限に活用されます。

要約すると、K 分割相互検証はモデルのパフォーマンスの評価と改善に役立つ重要な手法であり、特にデータが限られている場合、新しいデータでのモデルのパフォーマンスをより正確に推定できます。
ここに画像の説明を挿入

1. データの準備

相互検証を使用する前に、yolo 形式でデータを準備する必要があります。データの準備方法がわからない友人は、この記事を読むことができます: YOLOv8 チュートリアル シリーズ: 1. カスタム データ セットを使用して YOLOv8 モデルをトレーニングする (詳細バージョン)環境構築/データ準備
/モデル学習/予測/検証/エクスポート等を含むチュートリアルの1章→パラメータ調整戦略を読むだけで済みます

アノテーション
│ │ ├─ ./data/Annotations /fall_0.xml
│ │ §─ ./data/Annotations/fall_1000.xml
│ │ §─ ./data/Annotations/fall_1001.xml
│ │ ├─ ./ data/Annotations/fall_1002.xml
│ │ ├─ ./data/Annotations/fall_1003.xml
│ │ §─ ./data/Annotations/fall_1004.xml
│ │ ├─ …
│ ├─ ./data/画像
│ │ ├─ ./data/images/ fall_0.jpg
│ │ §─ ./data/images/fall_1000.jpg
│ │ §─ ./data/images/fall_1001.jpg
│ │ ├─ ./ data/images/fall_1002.jpg
│ │ §── ./data/images/fall_1003.jpg
│ │ ├─ ./data/images/fall_1004.jpg
│ │ §─ …
│ ├─ ./data/ImageSets
│ │ │ ./data/labels
│ │ §─ ./data/images/fall_0 .txt
│ │ ├─ ./data/images/fall_1000.txt
│ │ §─ ./data/images/fall_1001.txt
│ │ §─ ./data/images/fall_1002.txt
│ │ ├─ . /data/images/fall_1003.txt
│ │ §── ./data/images/fall_1004.txt
│ §── ./data/classes.yaml
この中で注意すべき点は、新しいclasses.yamlファイルを作成する必要があることです。次に、次のように独自のタグを順番に入力します。

names:
  0: your_label_1
  1: your_label_2

2. コードの準備

次のコードは、私のデータ形式に従って、このコードが data の上位ディレクトリに配置されていれば、何も変更せずに直接実行できます。

import datetime
import shutil
from pathlib import Path
from collections import Counter
import os

import yaml
import numpy as np
import pandas as pd
from ultralytics import YOLO
from sklearn.model_selection import KFold

# 定义数据集路径
dataset_path = Path('./data')  # 替换成你的数据集路径

# 获取所有标签文件的列表
labels = sorted(dataset_path.rglob("*labels/*.txt"))  # 所有标签文件在'labels'目录中

# 获取当前文件的绝对路径
current_file_path = os.path.abspath(__file__)

# 获取当前文件所在的文件夹路径(即当前文件的根目录)
root_directory = os.path.dirname(current_file_path)

print("当前文件运行根目录:", root_directory)

# 从YAML文件加载类名
yaml_file = 'data/classes.yaml'
with open(yaml_file, 'r', encoding="utf8") as y:
    classes = yaml.safe_load(y)['names']
cls_idx = sorted(classes.keys())

# 创建DataFrame来存储每张图像的标签计数
indx = [l.stem for l in labels]  # 使用基本文件名作为ID(无扩展名)
labels_df = pd.DataFrame([], columns=cls_idx, index=indx)

# 计算每张图像的标签计数
for label in labels:
    lbl_counter = Counter()

    with open(label, 'r') as lf:
        lines = lf.readlines()

    for l in lines:
        # YOLO标签使用每行的第一个位置的整数作为类别
        lbl_counter[int(l.split(' ')[0])] += 1

    labels_df.loc[label.stem] = lbl_counter

# 用0.0替换NaN值
labels_df = labels_df.fillna(0.0)

# 使用K-Fold交叉验证拆分数据集
ksplit = 5
kf = KFold(n_splits=ksplit, shuffle=True, random_state=20)  # 设置random_state以获得可重复的结果
kfolds = list(kf.split(labels_df))
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'

# 计算每个折叠的标签分布比例
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()

    # 为避免分母为零,向分母添加一个小值(1E-7)
    ratio = val_totals / (train_totals + 1E-7)
    fold_lbl_distrb.loc[f'split_{
      
      n}'] = ratio

# 创建目录以保存分割后的数据集
save_path = Path(dataset_path / f'{
      
      datetime.date.today().isoformat()}_{
      
      ksplit}-Fold_Cross-val')
save_path.mkdir(parents=True, exist_ok=True)

# 获取图像文件列表
images = sorted((dataset_path / 'images').rglob("*.jpg"))  # 更改文件扩展名以匹配你的数据
ds_yamls = []

# 循环遍历每个折叠并复制图像和标签
for split in folds_df.columns:
    # 为每个折叠创建目录
    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)



    # 创建数据集的YAML文件
    dataset_yaml = split_dir / f'{
      
      split}_dataset.yaml'
    ds_yamls.append(dataset_yaml.as_posix())
    split_dir = os.path.join(root_directory, split_dir.as_posix())

    with open(dataset_yaml, 'w') as ds_y:
        yaml.safe_dump({
    
    
            'path': split_dir,
            'train': 'train',
            'val': 'val',
            'names': classes
        }, ds_y)
print(ds_yamls)

# 将文件路径保存到一个txt文件中
with open('data/file_paths.txt', 'w') as f:
    for path in ds_yamls:
        f.write(path + '\n')

# 为每个折叠复制图像和标签到相应的目录
for image, label in zip(images, labels):
    for split, k_split in folds_df.loc[image.stem].items():
        # 目标目录
        img_to_path = save_path / split / k_split / 'images'
        lbl_to_path = save_path / split / k_split / 'labels'

        # 将图像和标签文件复制到新目录中
        # 如果文件已存在,可能会抛出SamefileError
        shutil.copy(image, img_to_path / image.name)
        shutil.copy(label, lbl_to_path / label.name)

コードを実行すると、データ ディレクトリの下にフォルダーが生成され、このフォルダーには 5 つの異なる分割データセットが含まれます。

3. トレーニングを開始する

以下のコードは上記のコードと同じディレクトリに配置されており、状況に応じてトレーニングパラメータを調整できます

from ultralytics import YOLO

weights_path = 'checkpoints/yolov8s.pt'
model = YOLO(weights_path, task='train')
ksplit = 5
# 从文本文件中加载内容并存储到一个列表中
ds_yamls = []
with open('data/file_paths.txt', 'r') as f:
    for line in f:
        # 去除每行末尾的换行符
        line = line.strip()
        ds_yamls.append(line)

# 打印加载的文件路径列表
print(ds_yamls)


results = {
    
    }
for k in range(ksplit):
    dataset_yaml = ds_yamls[k]
    model.train(data=dataset_yaml, batch=6, epochs=2, imgsz=1280, device=0, workers=8, single_cls=False, ) 

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_45921929/article/details/132429632