ひとことで言うと
訓練データはモデルの学習、検証データは設定や構成の比較、テストデータは最後の性能報告に使います。役割を混ぜないことが、未知データへの性能を測る条件です。
訓練は練習問題、検証は模試、テストは本番です。模試の点を見て何度も勉強法を変えたなら、その点はすでに選択に使われています。本番の点だけを、最後に一度だけ記録します。
なぜ必要か
学習に使ったデータで評価すると、モデルが正解を覚えた場合でも高得点になります。そこで一部を学習から外します。ただし、検証データも設定を選ぶたびに参照されるため、試行を重ねるほど選択が検証データに適応します。検証性能を最終性能とみなせない理由はここです。
テストデータは、設計と選択が終わるまで封印します。テストの値を見て設定を変更した時点で、それは検証データと同じ役割になり、独立した最終評価ではなくなります。
検証値を見て設定を変える流れは許容されますが、テスト値を見て同じ流れを始めてはいけません。評価値を入力にして設計を更新する回数が増えるほど、その評価値は「未知データの予測」ではなく「選択の材料」になります。
仕組み
データを 、訓練・検証・テストをそれぞれ 、、 と書くと、基本の流れは次の通りです。ここで は訓練で得るモデルパラメータ、 は検証時の評価値です。
検証で選んだ構成を確定したら、必要に応じて訓練と検証を合わせて再学習し、テストを一度だけ評価します。
| データ | 使う場面 | 使ってはいけない場面 |
|---|---|---|
| 訓練 | パラメータの学習 | 最終性能の報告だけで判断すること |
| 検証 | 構成の比較と採用判断 | 何度も見た値を未知性能と呼ぶこと |
| テスト | 確定後の最終評価 | 結果を見て再設計すること |
データが少なく固定の検証部分が不安定なら、テストを残したまま訓練側で 分割交差検証を行います。各回で 個を訓練、残りを検証に回し、得られた評価を平均します。全てのデータを検証に使える一方、学習を 回行うので計算量は増えます。
試験でどう問われるか
| 問われ方 | 正解に寄る条件 | 引っかけ |
|---|---|---|
| 3分割の役割 | 訓練=学習、検証=選択、テスト=確定後の一度の評価 | テストで設定を選ぶ |
| 少数データの評価 | テストを保持し、訓練側で交差検証する | 全データで交差検証してテストも兼ねる |
| 分割方法の選択 | 時系列は時間順、被験者などのまとまりはグループ単位 | 無作為シャッフルで未来や同一個体を混ぜる |
実装で確かめる
train_test_split は既定でシャッフルします。再現性が必要なら random_state を固定し、クラス比を保ちたい分類では stratify=y を指定します。時系列でこの既定動作を使うと、未来の行が訓練側へ入り得るため、時刻で並べたインデックスを明示的に切ります。
import numpy as np
rng = np.random.default_rng(7)
X = np.arange(20).reshape(10, 2)
y = np.arange(10) % 2
order = rng.permutation(len(X))
cut = 7
train, test = order[:cut], order[cut:]
print(X[train].shape, X[test].shape, y[train].tolist())
前処理も分割の一部です。標準化の平均・分散、欠損値の補完値、特徴選択の基準を全データから先に計算すると、検証やテストの情報が訓練処理へ入ります。各分割の訓練部分だけで基準を学び、残りにはその基準を適用します。
取り違えやすいもの
| 用語 | 分割との違い |
|---|---|
| 交差検証 | 検証部分を複数回入れ替える評価手順。テストの代わりではありません |
| 層化分割 | クラス比を保つ分割。検証をテストへ格上げする手順ではありません |
| 時系列分割 | 過去で学習し未来で評価する分割。通常の無作為分割とは前提が違います |
| グループ分割 | 同じ被験者・装置などを訓練と評価にまたがらせない分割です |
想起チェック
テストデータを最後に一度だけ使う理由は
テスト結果を見て設計を変えると、テストが選択に使われ、独立した最終評価でなくなるためです。
検証データで起きる典型的なリークは
標準化の平均や特徴選択の基準を、訓練と検証を分ける前に全データで計算することです。
時系列を無作為に分けると何が起きるか
未来の観測が訓練側に入り、実運用では得られない情報で過去のモデルを評価することになります。