機械学習において、構築したモデルが「未知のデータ」に対してどれくらい正確に予測できるか(汎化性能)を正しく測ることは、極めて重要です。この記事では、Pythonの代表的な機械学習ライブラリであるScikit-learn(サイキットラーン)のcross_val_scoreを用いた、「交差検証(クロスバリデーション)」の具体的な実装方法を解説します。
この記事を読むことで、データの偏りに左右されない正確なモデル評価の手法と、データリークを防ぐ安全な実装の型を身につけることができます。実務で使える堅牢なAI・機械学習モデルを作るための第一歩を踏み出しましょう。本記事で解説している内容の公式ドキュメントはこちらです。
※本記事で紹介するコードや概念は、Scikit-learn 1.0以降の幅広いバージョンで共通して利用できる標準的な仕様に基づいています。
なぜモデル評価にcross_val_scoreが必要なのか?
結論:データを無駄なく使い、評価の「偏り」や「偶然の正解」を排除して、モデルの真の実力を正確に測るためです。
機械学習モデルの評価において最もシンプルな方法は、データを学習用とテスト用に1回だけ分割する「ホールドアウト法」です。しかし、ホールドアウト法には大きな弱点があります。それは「たまたまテストデータに簡単な問題ばかりが集まってしまう」というデータ分割の運によって、評価スコアが大きくブレてしまう点です。
これを解決するのが**交差検証(クロスバリデーション)**です。 交差検証では、データを複数のグループ(例えば5つ)に分割します。そして「4つを学習用、1つをテスト用」とするパターンを、テスト用のグループを入れ替えながら5回繰り返します。
これにより、すべてのデータが必ず1回はテストデータとして評価されるため、データの偏りによる評価のブレを最小限に抑え、より信頼性の高い「汎化性能」を測定できるのです。Scikit-learnのcross_val_scoreは、この複雑な手順をたった数行のコードで自動化してくれる非常に強力な関数です。
cross_val_scoreの基本的な使い方
結論:モデル、特徴量(X)、目的変数(y)を渡すだけで、指定した分割数に応じた評価スコアのリストを取得できます。
まずは最も基本的な使い方を見ていきましょう。cross_val_scoreを使用するには、評価したい機械学習モデルのインスタンスと、データセットを用意するだけです。関数を実行すると、各分割におけるテストスコアが配列(NumPy配列)として返ってきます。
以下のコードでは、乳がんのデータセットを用いて、ランダムフォレストモデルの交差検証を行っています。コードはそのままコピー&ペーストして実行可能です。
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
# 1. データセットの準備
# 乳がんデータセット(分類問題)を読み込みます
data = load_breast_cancer()
X = data.data # 特徴量(細胞の半径、面積などの数値データ)
y = data.target # 目的変数(悪性か良性かのラベル)
# 2. 評価したいモデルの定義
# ここではランダムフォレストを使用します
model = RandomForestClassifier(random_state=42)
# 3. cross_val_scoreによる交差検証の実行
# estimator: モデル, X: 特徴量, y: 目的変数, cv: 分割数(デフォルトは5)
scores = cross_val_score(estimator=model, X=X, y=y, cv=5)
# 4. 結果の確認
# 5回のテスト結果が配列で返ってきます
print(f"各分割のスコア: {scores}")
# 通常は、スコアの平均値と標準偏差(ばらつき)を確認してモデルを評価します
print(f"平均正解率: {scores.mean():.3f}")
print(f"標準偏差: {scores.std():.3f}")Code language: Python (python)
出力された平均正解率(mean)がモデルの総合的な評価額となり、標準偏差(std)が小さいほど、データの分割に依存しない安定したモデルであると判断できます。
引数 cv の詳細設定(K-FoldとStratified K-Fold)
結論:cv引数には数値を直接指定するだけでなく、交差検証の挙動を細かく制御するためのクラス(KFoldなど)を渡すことができます。
cross_val_scoreの引数cvに整数(例:cv=5)を指定した場合、Scikit-learnは賢く振る舞います。分類問題であれば、各分割における正解ラベルの比率が均等になるように分割する「Stratified K-Fold(層化K分割)」が自動的に選択されます。回帰問題であれば、通常の「KFold」が選択されます。
しかし、データをシャッフルしてから分割したい場合や、時系列データを扱う場合は、専用の交差検証クラスをインスタンス化してcvに渡す必要があります。
from sklearn.model_selection import StratifiedKFold
# shuffle=Trueにしてデータをシャッフルしてから分割する設定
# random_stateを固定することで、実行のたびに結果が変わるのを防ぎます
cv_strategy = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# cv引数に作成した戦略を渡します
scores = cross_val_score(model, X, y, cv=cv_strategy)
print(f"シャッフルありの平均正解率: {scores.mean():.3f}")Code language: PHP (php)
スマホで確認する際も、このように明示的に分割方法を記述しておくことで、後からコードを見返したときの意図が明確になります。
評価指標 scoring のカスタマイズ
結論:分類のF1スコアや、回帰の二乗誤差など、プロジェクトの目的に応じて評価指標を自由に変更できます。
デフォルトでは、分類問題なら正解率(Accuracy)、回帰問題なら決定係数(R2)が計算されます。しかし、クラスの偏りがあるデータでは、正解率だけを見ているとモデルの性能を見誤る可能性があります。
引数scoringに文字列を指定することで、様々な評価指標を使用できます。
- 分類問題:
'accuracy'(正解率),'f1'(F1スコア),'roc_auc'(AUC),'precision'(適合率),'recall'(再現率) - 回帰問題:
'neg_mean_squared_error'(負の平均二乗誤差),'neg_mean_absolute_error'(負の平均絶対誤差)
# F1スコアでモデルを評価する場合
f1_scores = cross_val_score(model, X, y, cv=5, scoring='f1')
print(f"平均F1スコア: {f1_scores.mean():.3f}")Code language: PHP (php)
【重要】回帰問題における注意点 Scikit-learnの交差検証では、「スコアは大きいほど良い」という統一ルールの下で動作します。そのため、本来は値が小さいほど良いとされる誤差(MSEなど)は、マイナス記号がつけられたneg_mean_squared_errorが使われます。実際の誤差を確認する際は、結果にマイナスを掛けて符号を反転させる点に注意してください。
【実践】cross_val_scoreを用いたモデル比較のワークフロー
結論:前処理(標準化など)とモデルをPipeline(パイプライン)で結合し、それをcross_val_scoreに渡すのが、最も安全で確実な実務のベストプラクティスです。
初心者が最も陥りやすい罠が「データリーク(情報の漏洩)」です。交差検証を行う前に、データ全体に対してStandardScalerなどで標準化を行ってしまうと、テストデータに使うべき情報(全体データの平均値や分散)が学習データに混入してしまい、スコアが不当に高く出てしまいます。
これを防ぐためには、データを分割した後に、学習データのみを使って標準化を行い、その基準でテストデータを変換する必要があります。Scikit-learnのPipelineを使えば、この複雑な手順を安全に自動化できます。
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
# 1. パイプラインの構築
# データの前処理(標準化)と、機械学習モデル(SVM)を連結します
pipeline = Pipeline([
('scaler', StandardScaler()), # 第一段階:データのスケールを揃える
('svm', SVC(kernel='rbf', random_state=42)) # 第二段階:SVMで分類
])
# 2. パイプラインごと交差検証にかける
# estimator引数にpipelineを渡すだけで、分割ごとに正しい前処理が行われます
# n_jobs=-1 を指定すると、PCの全コアを使って並列処理を行い高速化できます
pipeline_scores = cross_val_score(estimator=pipeline, X=X, y=y, cv=5, n_jobs=-1)
print(f"パイプラインを用いた各分割のスコア: {pipeline_scores}")
print(f"平均スコア: {pipeline_scores.mean():.3f}")Code language: PHP (php)
このようにPipelineとcross_val_scoreを組み合わせることで、前処理を含めた一連のプロセス全体の汎化性能を、厳密かつ公平に評価できるようになります。
cross_val_scoreを使用する際の注意点
結論:計算時間が分割数倍に跳ね上がる点と、平均値だけでなく「標準偏差」も必ず確認することに注意してください。
非常に便利なcross_val_scoreですが、利用にあたっていくつか留意すべきポイントがあります。
- 計算コストの増加 交差検証は、指定した分割数(K回)だけモデルの学習と予測を繰り返します。つまり、単純計算でホールドアウト法のK倍の時間がかかります。巨大なデータセットや、学習に時間のかかるディープラーニングモデルに適用する場合は、引数
cvの数を減らすか、n_jobs=-1を指定して並列処理を行うなどの工夫が必要です。 - 結果のばらつき(標準偏差)の確認 平均スコアが高くても、各分割でのスコアのばらつき(標準偏差)が大きい場合は注意が必要です。それは「データの与えられ方によって性能が大きく変動する不安定なモデル」であることを意味します。必ず
scores.std()を出力し、安定して高い性能を出せているかをチェックする習慣をつけましょう。
まとめ:cross_val_scoreで信頼性の高い機械学習モデルを構築しよう
結論:cross_val_scoreを使いこなし、データの偏りに強い、実社会で本当に役立つ機械学習モデルを作り上げましょう。
本記事では、Scikit-learnのcross_val_scoreを用いた交差検証の実装方法について解説しました。ポイントを振り返ります。
- ホールドアウト法より確実: データを無駄なく使い、評価のブレを防ぐことができる。
- 引数の設定が鍵:
cvで分割方法を、scoringで評価指標を柔軟に変更可能。 - Pipelineとの併用が必須: データリークを防ぎ、正しい汎化性能を測定するために、前処理は必ずPipelineに組み込む。
機械学習の学習段階では、どうしても「より精度の高いモデルを作ること」に目が向きがちです。しかし、実務で本当に求められるのは「未知のデータに対しても安定して予測を外さない堅牢なモデル」です。今回学んだcross_val_scoreを今後のプロジェクトに積極的に取り入れ、信頼性の高いAI開発を目指してください。

コメント