ひとことで言うと
データ分割は、同じデータを使い回して得たスコアを「未知データへの性能」と取り違えないための境界設計です。訓練データはパラメータを学習するため、検証データはモデルやハイパーパラメータを選ぶため、テストデータは最後の性能報告のために使います。
訓練は練習問題、検証は模試、テストは一度だけ開く本番問題です。模試の点を見て勉強法を変え続ければ、模試への慣れも点数に混ざります。本番問題を開いてから勉強法を変えたら、それはもう本番の測定ではありません。
なぜ必要か
訓練誤差は、モデルが見た例への適合度です。これだけでモデルを選ぶと、訓練例の偶然の癖まで覚えた過学習を見抜けません。そこで、訓練に使わない検証データで候補の比較やハイパーパラメータの選択を行い、選択を終えたモデルを未使用のテストデータで一度評価します。
検証データで選択を何度も行えば、選択処理自体が検証データの偶然に適応します。したがって検証誤差も、選択前の未知データに対する誤差より楽観的になります。テストデータを見て設定を変えた時点で、そのデータはテストではなく新しい検証データです。評価値を守る仕組みは、分割を増やすことではなく、用途ごとのアクセスを分けることにあります。
| 段階 | 判断すること | 次に進めるデータ |
|---|---|---|
| 訓練 | パラメータを合わせる | 検証で比較 |
| 検証 | 設定や候補を選ぶ | 固定して最終評価 |
| テスト | 未知データの性能を測る | 報告して終了 |
仕組み
データ全体を 、訓練・検証・テストをそれぞれ とすると、基本条件は次のように表せます。
ここで は共通要素がない集合、各 はサンプルの集合です。ただし行を別集合へ配るだけでは不十分です。特徴量の標準化や欠損値補完の統計量を全体から計算すると、テストの情報が訓練へ流れます。分割後に訓練データだけで前処理を fit し、検証・テストには同じ変換を適用します。
| 分割 | 使う場面 | 触れてよいタイミング |
|---|---|---|
| 訓練 | パラメータの更新 | 学習中に繰り返し |
| 検証 | ハイパーパラメータ・候補の選択 | 選択中 |
| テスト | 最終性能の報告 | 選択を終えた後に原則1回 |
ランダム分割は、サンプルが独立同分布で、順序や所属が予測時の構造に関係しない場合の選択肢です。クラス比率を保ちたい分類では層化を使えます。一方、同じ被験者の測定が複数行あるなら、行をランダムに分けると同一人物の特徴が訓練とテストに現れます。被験者IDなどのグループ単位で分け、同じグループを跨がせません。時系列も同様に、未来を訓練に混ぜない順序を守ります。時間順のニュースをシャッフルすると、近い時点の似た記事が訓練と評価に分かれ、評価が膨らみ得ます。
試験でどう問われるか
| 問われ方 | 正解に寄る条件 | 引っかけ |
|---|---|---|
| 3分割の役割 | 訓練・選択・最終報告を分ける | テストでハイパーパラメータを決める |
| 評価値が楽観的になる理由 | 検証値を見て選択を繰り返す | 検証データを使っただけで必ず漏れるとする |
| 分割方法の選択 | 時系列は順序、被験者データはグループを守る | 常に一様なランダム分割 |
実装で確かめる
IDを無視して行番号で分けると、同じ被験者が両方に入ります。先にグループを分けてから行を集めれば、この漏れを検査できます。
import numpy as np
groups = np.array([0, 0, 1, 1, 2, 2, 3, 3])
rng = np.random.default_rng(7)
unique = rng.permutation(np.unique(groups))
train_groups, test_groups = unique[:2], unique[2:]
train = np.isin(groups, train_groups)
test = np.isin(groups, test_groups)
print(np.intersect1d(groups[train], groups[test]).size)
出力は 0 です。行単位のランダム分割に置き換えると、この値は0とは限りません。少数データで分割の揺れを抑えたい場合は、訓練側だけで交差検証を使って選択し、テストを最後まで隔離します。
前処理、特徴量選択、欠損値補完の fit を分割前に実行しないでください。テストを見て閾値やモデルを調整した場合も、最終テストという名前だけを残してはいけません。
取り違えやすいもの
| 用語 | データ分割との切り分け |
|---|---|
| 訓練誤差 | 見たデータへの適合度で、未知データの評価ではない |
| 検証誤差 | 選択に使う。選択を重ねるほど楽観性に注意する |
| テスト誤差 | 選択後の最終報告用。触れて調整したら役割が変わる |
| 層化 | クラス比率を保つ分け方であり、時系列や被験者の漏れ対策ではない |
想起チェック
テストデータを見て設定を変えた後、そのデータは何になるか
選択に使った検証データです。最終性能を報告するには、別の未使用データが必要です。
同じ被験者の行をランダムに分けてはいけない理由は何か
被験者固有の特徴が訓練とテストの両方に現れ、未知の被験者への性能より高い評価になり得るためです。
時系列でシャッフルを避ける理由は何か
未来に近いサンプルを訓練に混ぜると、実運用では得られない情報で評価することになるためです。