[機械学習のはじめ方] Part22: K-fold交差検証とStratified K-fold

はじめに:なぜモデル評価が大切なの?

機械学習モデルを作ったら、そのモデルがどれだけ「良い」のかを評価する必要があります。でも、作ったモデルを訓練に使ったデータだけで評価してしまうと、そのデータにだけすごく詳しい「過学習」モデルになってしまうかもしれません😭。

本当に知りたいのは、モデルがまだ見たことのない新しいデータに対してどれだけうまく予測できるか、つまり「汎化性能」です。

単純にデータを「訓練用」と「テスト用」の2つに分ける方法もありますが、データの分け方によって評価結果が偶然良かったり悪かったりすることがあります。もっと信頼できる評価方法はないのでしょうか? 🤔

そこで登場するのが交差検証 (Cross Validation) です! 🎉 交差検証は、データを無駄なく使い、より安定した汎化性能の評価を与えてくれるテクニックです。今回は、その中でも特によく使われる K-fold交差検証Stratified K-fold交差検証 について学んでいきましょう!

K-fold交差検証 (K-Fold Cross Validation)

🤔 K-foldってどうやるの?

K-fold交差検証は、データを K 個のほぼ同じサイズのグループ(これを「fold」と呼びます)に分割することから始まります。そして、以下の手順を K 回繰り返します。

  1. K 個の fold のうち、1つをテストデータとして選びます。
  2. 残りの K-1 個の fold を訓練データとして使って、モデルを学習させます。
  3. ステップ1で選んだテストデータを使って、モデルの性能を評価します。

これを K 回繰り返すと、K 個の評価スコアが得られます。最終的なモデルの評価値は、これらのスコアの平均値などを計算して求めます。一般的に K は 5 や 10 がよく使われます (5-fold CV, 10-fold CV)。

✨ K-foldのメリット

  • データを有効活用できる: 全てのデータが訓練とテストの両方に使われるため、データを無駄にしません。
  • 評価の信頼性向上: 複数回の評価の平均を取るため、データの分割方法による偶然の影響を受けにくく、より安定した汎化性能の指標が得られます。

⚠️ K-foldの注意点

  • 計算コスト: モデルの学習を K 回行うため、単純な訓練/テスト分割よりも計算時間がかかります。
  • データの偏り (特に分類問題): ランダムに分割するため、各 fold の中のクラスの割合(例えば、陽性/陰性の割合)が元のデータセットの割合と大きく異なってしまう可能性があります。これは、特にデータが不均衡な場合に問題となります。

💻 Python (scikit-learn) でやってみよう

Python の機械学習ライブラリ scikit-learn を使うと、K-fold交差検証を簡単に実装できます。

KFold クラスを使って分割のインデックスを生成する方法:


from sklearn.model_selection import KFold
import numpy as np

# サンプルデータ (特徴量Xとターゲットy)
X = np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]])
y = np.array([0, 0, 1, 1, 0]) # ターゲット変数 (例)

# 5-fold CV を準備 (データをシャッフルしてから分割)
kf = KFold(n_splits=5, shuffle=True, random_state=42)

print("KFold の分割例:")
for i, (train_index, test_index) in enumerate(kf.split(X)):
    print(f"Fold {i+1}:")
    print(f"  Train index: {train_index}")
    print(f"  Test index:  {test_index}")
    # ここで実際にモデルを訓練・評価する
    # model.fit(X[train_index], y[train_index])
    # score = model.score(X[test_index], y[test_index])
      

もっと簡単に、cross_val_score 関数を使うと、モデルの訓練から評価までを一括で行ってくれます。


from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression # 例としてロジスティック回帰

# モデルを準備
model = LogisticRegression()

# 5-fold CV で評価スコアを計算 (scoring='accuracy' は正解率)
# cv に KFold オブジェクトまたは整数を指定できる
scores = cross_val_score(model, X, y, cv=KFold(n_splits=5, shuffle=True, random_state=42), scoring='accuracy')

print(f"\n各FoldのAccuracy: {scores}")
print(f"平均Accuracy: {scores.mean():.3f} (+/- {scores.std() * 2:.3f})")
      

Stratified K-fold交差検証 (Stratified K-Fold Cross Validation)

🤔 なぜ Stratified が必要?

K-fold交差検証の注意点で触れたように、特に分類問題でデータセット内のクラスの比率が偏っている場合(不均衡データ)、単純な K-fold だと問題が起きることがあります。

例えば、病気の陽性患者が全体の10%しかいないデータセットを考えます。通常の K-fold でランダムに 5分割 した場合、ある fold には陽性患者が全く含まれなかったり、逆に陽性患者ばかりが集まってしまう可能性があります 😱。これでは、その fold をテストデータとして使ったときの評価が、データ全体の状況を正しく反映しているとは言えません。

📊 Stratified K-fold の仕組み

Stratified K-fold は、この問題を解決するために、データを分割する際に各クラスの比率を元のデータセットと同じになるように維持します。

つまり、どの fold を取っても、陽性患者と陰性患者の割合が、元のデータ全体の割合とほぼ同じになるように分割してくれるのです。これにより、各 fold での評価がより信頼できるものになります ✨。

👍 Stratified K-fold のメリット

  • 不均衡データに強い: クラスの比率を保って分割するため、不均衡なデータセットに対するモデル評価の信頼性が大幅に向上します。
  • 分類問題の標準: 特に理由がなければ、分類問題の交差検証では Stratified K-fold を使うのが一般的です。

💻 Python (scikit-learn) でやってみよう

scikit-learn では、StratifiedKFold クラスが用意されています。使い方は KFold と似ていますが、split メソッドには特徴量 X に加えてターゲット変数 y も渡す必要があります(クラス比率を考慮するため)。


from sklearn.model_selection import StratifiedKFold

# サンプルデータ (クラス比率が少し不均衡な例)
X = np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13, 14], [15, 16]])
y = np.array([0, 0, 0, 0, 1, 1, 0, 0]) # クラス1が少ない (2/8 = 25%)

# 4-fold Stratified CV を準備 (データをシャッフルしてから分割)
skf = StratifiedKFold(n_splits=4, shuffle=True, random_state=42)

print("StratifiedKFold の分割例:")
for i, (train_index, test_index) in enumerate(skf.split(X, y)): # y を渡す点がKFoldと違う
    print(f"Fold {i+1}:")
    print(f"  Train index: {train_index}")
    print(f"  Test index:  {test_index}")
    print(f"  Test data y: {y[test_index]} (クラス1の割合: {np.mean(y[test_index]):.2f})") # 各foldでクラス1が1つ(25%)になるはず
      

cross_val_score 関数を使う場合、分類タスク (ターゲット変数 y が離散値) であれば、cv に整数を指定した場合や、cvKFoldStratifiedKFold を明示的に指定しない場合、多くの場合デフォルトで Stratified K-fold が使われます。ただし、明示的に指定する方が確実です。


from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression

model = LogisticRegression()

# Stratified 4-fold CV で評価スコアを計算
# 分類器と分類タスクyを渡すと、デフォルトで Stratified K-Fold が使われることが多い
# 明示的に StratifiedKFold を指定する例:
skf_cv = StratifiedKFold(n_splits=4, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=skf_cv, scoring='accuracy')

print(f"\n各FoldのAccuracy (Stratified): {scores}")
print(f"平均Accuracy: {scores.mean():.3f} (+/- {scores.std() * 2:.3f})")
      

どっちを使えばいいの? 🤔

基本的な使い分けは以下のようになります。

状況 推奨される交差検証 理由
分類問題 (特に不均衡データ) Stratified K-fold 各 fold でクラス比率を維持し、信頼性の高い評価ができるため。
分類問題 (クラス比率が均等に近い) Stratified K-fold (推奨) または K-fold Stratified K-fold を使っておけば間違いがない。K-fold でも大きな問題はないことが多い。
回帰問題 K-fold クラス比率という概念がないため、通常の K-fold を使用する。

迷ったら、特に分類問題では Stratified K-fold を使うことを検討しましょう! 👍

まとめ

今回は、モデルの汎化性能をより正確に評価するための強力なテクニック、K-fold交差検証Stratified K-fold交差検証 について学びました。

  • ✅ 交差検証は、データを分割して複数回の学習・評価を行い、評価の信頼性を高める手法です。
  • K-fold はデータを K 個に分割し、順番にテストデータとして使います。
  • Stratified K-fold は、K-fold を改良し、特に分類問題において各 fold のクラス比率を維持します。不均衡データに有効です。
  • scikit-learn を使えば、これらの交差検証を簡単に実装できます。

適切な交差検証の手法を選ぶことで、モデルの性能をより客観的に評価し、改善に繋げることができます。ぜひ活用してみてください! 💪