← 機械学習テキスト 一覧

🎓 レベル:基礎 | 重要度:A(必須)

📎 前提:汎化と過学習・バイアスバリアンス分解

要点(BLUF)

1. なぜ分けるのか

学習の目的は訓練データへの当てはめではなく、未知データでの誤差(汎化誤差)を下げることでした(学習問題の定式化(仮説・損失・経験リスク)汎化と過学習・バイアスバリアンス分解)。ところが、学習に使ったデータでそのまま性能を測ると、モデルは「答えを知っている問題」を解くだけなので、誤差は楽観的に出ます。これは試験勉強で使った問題集をそのまま試験に出すようなもので、本当の実力は測れません。

そこで、データの一部を学習に使わずに取り分け、そこで性能を測ります。役割は3つに分かれます。

データ役割使うタイミング
訓練(train)モデルのパラメータを学習する学習中、何度でも
検証(validation)ハイパーパラメータを選ぶ・モデルを比較する開発中、何度でも
テスト(test)最終的な汎化性能を偏りなく報告する最後に一度だけ
flowchart LR
  ALL["全データ"] --> TR["訓練(train)"]
  ALL --> VA["検証(validation)"]
  ALL --> TE["テスト(test)"]
  TR --> FIT["パラメータ学習"]
  VA --> SEL["ハイパラ選択・モデル比較"]
  TE --> REP["最終性能を1回だけ報告"]

2. パラメータとハイパーパラメータ — 検証が必要な理由

ハイパーパラメータを「複数試して一番良いものを選ぶ」とき、その選択の良し悪しは何かのデータで測る必要があります。これに訓練データを使うと過学習側に選んでしまい、テストデータを使うと「テストに合わせて選んだ」ことになりテストが汚れます。だから検証データという第3の取り分けが要るのです。

要するに:訓練=モデルの中身を決める、検証=モデルの選び方を決める、テスト=決めたものの実力を測る。3つの役割は混ぜてはいけません。

テストは「一度だけ」が鉄則

テストデータを見て「もう少し調整しよう」と戻ると、人間を経由してテスト情報がモデル選択に漏れます。テストを何度も見るほど、テスト誤差は楽観的になり汎化性能の推定として機能しなくなります。テストは封をして最後に開けるものです。

3. データリーク — 最も多い落とし穴

データリーク(data leakage) とは、検証/テストの情報が訓練側に漏れ込むことです。これがあるとモデルは開発中だけ優秀に見え、本番で崩れます。

典型例1:前処理を分割前にやる

標準化(平均0・分散1にする)や欠損値補完、エンコーディングを分割前に全データで行うと、平均や分散といった統計量にテストデータの情報が混じり、訓練が「テストを少し覗いた」状態になります。

正しい順序は 分割 → 訓練データだけで前処理の基準(平均・分散など)を決める → その基準を検証/テストにも適用 です。

flowchart TB
  subgraph BAD["やりがちな誤り"]
    B1["全データで標準化"] --> B2["分割"] --> B3["学習・評価"]
  end
  subgraph GOOD["正しい順序"]
    G1["分割"] --> G2["訓練データだけで平均・分散を決定"]
    G2 --> G3["同じ基準で検証/テストを変換"]
    G3 --> G4["学習・評価"]
  end

典型例2:ターゲットリーク

予測時には手に入らないはずの情報が特徴量に紛れ込むケース。たとえば「退院済みフラグ」で「入院中かどうか」を予測すると、答えを言い換えた特徴で当てているだけです。本番では使えません。

典型例3:時間リーク(未来の混入)

時系列データをシャッフルして分割すると、未来のデータで過去を予測する形になり、現実にはありえない好成績が出ます(詳細は第5節)。

要するに:「本番のその瞬間に、本当にその情報を持っているか?」を問えば、多くのリークは防げます。

4. ホールドアウト vs k分割交差検証

ホールドアウト

データを1回だけ訓練/検証(/テスト)に切る方法。シンプルで速いですが、たまたまの分かれ方で評価が大きくブレます。データが少ないほどこのブレは深刻です。

k分割交差検証(k-fold cross-validation)

データを k 個のかたまり(fold) に分け、1つを検証、残り k−1 を訓練にして学習・評価。これを検証する fold を入れ替えて k 回 繰り返し、k 個のスコアを平均します。全データが「ちょうど一度ずつ」検証に回るので、ホールドアウトより安定した推定が得られます。

flowchart LR
  D["全データを K 個に分割"] -->|"K回繰り返す"| L["毎回1つを検証・残りで訓練"]
  L --> AVG["K個のスコアを平均"]

k 回分の図(k=5 の例。■=検証、□=訓練):

graph TB
  R1["1回目: ■ □ □ □ □"]
  R2["2回目: □ ■ □ □ □"]
  R3["3回目: □ □ ■ □ □"]
  R4["4回目: □ □ □ ■ □"]
  R5["5回目: □ □ □ □ ■"]

k の選び方

k を大きくすると訓練に使えるデータが増えて推定のバイアスは小さくなりますが、各回の訓練セットが互いに似てくるため分散が増え、計算コストも k 倍になります。経験的に k=5 または k=10 が、バイアスと分散・計算量のバランスが良いとされ広く使われます。

注意:交差検証はハイパラ選択やモデル比較のための道具です。最終報告用のテストデータは交差検証とは別に取り分けておくのが安全です(→ 第6節のネスト交差検証)。

5. 特殊な分割:LOO・層化・時系列

LOO(leave-one-out)

k=データ数 n とした極端な交差検証。毎回1サンプルだけを検証に使い、n 回繰り返します。訓練データをほぼ全部使えるのでバイアスは最小ですが、訓練セットがほとんど同じ(1点違うだけ)なので推定の分散が大きく、しかも n 回学習する計算コストが重い。少数データで使われることがあります。

層化 k分割(stratified k-fold)

分類で、各 fold のクラス比率を全体と同じに保つ分割。クラス不均衡のとき、普通のランダム分割だと「ある fold に陽性が1件も入らない」事故が起き、評価が壊れます。層化はこれを防ぎ、各 fold を代表的な構成に保ちます。分類のデフォルトとして推奨されます。

時系列分割(未来を使わない)

時系列データは順序と自己相関に意味があるので、シャッフルしてはいけません。未来で過去を予測する形になり(先読みバイアス)、リークします。代わりに時間順を保ち、過去で訓練 → 未来で検証を前進させます(前進検証 / walk-forward)。

graph TB
  W1["1回目: 訓練[1..3] → 検証[4]"]
  W2["2回目: 訓練[1..4] → 検証[5]"]
  W3["3回目: 訓練[1..5] → 検証[6]"]

要するに:分割の仕方は「本番でどう使うか」を真似る。順序や偏りに意味があるデータでは、ランダム分割が嘘の好成績を生みます。

6. ネストした交差検証

「交差検証でハイパラを選び、同じ交差検証のスコアをそのまま性能として報告する」のは楽観的すぎます。選択に使ったデータで評価しているからです(一種のリーク)。

これを避けるのが ネスト交差検証

flowchart TB
  OUT["外側CV: 性能評価"] --> INNER["各外側foldの中で内側CV: ハイパラ選択"]
  INNER --> EVAL["選んだ設定を外側の検証foldで評価"]
  EVAL --> MEAN["外側スコアを平均=偏りの少ない汎化性能"]

要するに:ハイパラ選びと性能評価を別々のデータでやる。これでチューニングによる楽観バイアスを取り除けます。

⚠️ よくある誤解

対応するシミュレーション

simulations/learning_curve.py:訓練データ量を増やしながら「訓練スコア」と「検証スコア(5分割交差検証)」を描く学習曲線を、高バイアスのモデル(ロジスティック回帰)と高バリアンスのモデル(k=1k=1 の k近傍法)で比較します。高バイアスは両者が低い所で接近、高バリアンスは訓練が高く検証と大きな差、という形の違いから「データを増やすべきかモデルを変えるべきか」を診断できることを示します。

学習曲線で高バイアス/高バリアンスを診断

関連ノート