クロスバリデーション — データの一部を「未来のフリ」にする技

7.10 Cross-Validation

ある研究チームが、ガン患者と健常者を見分ける分類器を作ったとします。 手元のデータでの正解率は驚異の 97%。論文を投稿、晴れて採択。 ところが、別の病院で集めた新しい患者データで試したら——正解率は 50%、つまりコイン投げと同じ。 何が起きたのでしょう?

これは作り話ではありません。バイオ系の論文ではこの手の「再現できない結果」が頻発し、 その多くが クロスバリデーション(CV)の誤用 に原因があります。

CV の発想自体は驚くほど単純です。データの一部を一旦隠し、残りで学習して、隠した部分でテストする。 これを少しずつズラしながら何度も繰り返す――それだけ。 けれど、この単純さの裏には、いくつもの罠と、深い理論的疑問が潜んでいます。

訓練データだけでテスト誤差を測れるか?

機械学習モデルを作ったら、まず知りたいのは「これは新しいデータでもちゃんと当たるのか」——新しいデータでの平均誤差、いわゆる 汎化誤差(generalization error)です。

$$\mathrm{Err} = \mathbb{E}\bigl[L(Y, \hat{f}(X))\bigr]$$

ここで $L(\cdot, \cdot)$ は損失関数(回帰なら二乗誤差、分類なら誤分類率)、$(X, Y)$ は訓練データから完全に独立な新しいサンプルです。$\mathbb{E}[\cdot]$ は「平均をとる」操作を表します。 CVの目標は、この $\mathrm{Err}$ を、訓練データだけから推定することです。

理想なら、訓練データとは別に「テストデータ」を山ほど用意して、そこで測ればいい。 でも実世界では、データは貴重で有限。1000サンプルしかないなら、まず全部を学習に使いたい、と思うのが人情です。

ここで、訓練データ自身で誤差を測ってはどうでしょう?――残念ながらこれはダメです。 モデルは訓練データを「覚えて」いるので、訓練データ上の誤差は本当の誤差より必ず低く出てしまう(これを 訓練誤差の楽観性 と呼びます)。 試験前にカンニングペーパーを覚えた生徒に、そのカンペで試験させるようなものです。

データ全体から一部をテスト用として切り出すアニメーション
データの一部を「未来のデータのフリ」をさせる

クロスバリデーションのアイデアは、手元のデータの一部を「未来のデータのフリ」をさせる ことです。 学習には使わず、テスト専用にとっておく。 これなら、訓練データを覚えているという問題は起こりません。 アニメーションが示すように、データ全体から右側の1/5を切り出して「テスト代役」として使います。 青い部分(訓練)とシアンの部分(テスト代役)が分離されていることに注目してください。

K-Fold Cross-Validation — 「順番にテスト役を交代する」

データの一部をテスト用に取っておくのは賢明ですが、ここで欲が出ます。 「テスト用に5分の1を取っちゃったら、学習に使えるデータが減るじゃないか」――その通りです。

そこで K-Fold CV はこう考えます。「テスト役」を順番に交代させればいい

K-Fold CVのテスト役交代アニメーション
5つのfoldが順番にテスト役(黄)を経験する

具体的には、データを K 個の fold(ほぼ同じ大きさの塊)に分けます。 例えば K=5 なら、1番から5番までの5つの塊ができる。 アニメーションで見たように、各foldが一度ずつ「黄色(テスト役)」になります。 すべてのfoldがテスト役を経験したら終了——全データが一度ずつテスト役を経験する という巧妙さがポイントです。

数式で書けば:

$$\mathrm{CV}(\hat{f}) = \frac{1}{N}\sum_{i=1}^{N} L\bigl(y_i, \hat{f}^{-\kappa(i)}(x_i)\bigr)$$

ここで記号の意味を丁寧に解説します:

外側の和は全 $N$ サンプルに渡っており、 各サンプル $i$ は「自分が含まれない fold で学習したモデル」で1回ずつ評価されます。 だから訓練データへの過適合は混入しません。

K の典型値は 5 か 10。 K=N なら Leave-One-Out CV(LOOCV) と呼ばれ、 毎回1つだけテストし、残り N−1 個で学習します。

K をいくつにする? — バイアスとバリアンスのトレードオフ

K-Fold CV で必ず聞かれる質問が「K はいくつがいいの?」です。 実はここに、機械学習を貫く重要な対立——バイアス(偏り)とバリアンス(ブレ)のトレードオフ が顔を出します。

K=N(Leave-One-Out)の場合: 各 fold で N−1 個のサンプルを使うので、ほぼ全データで学習するのと変わらない。 だから真の誤差にとても近い(バイアスは小さい)。 一方で、N 個のモデルは互いにとてもよく似ている(1サンプルしか違わない)ので、結果がブレやすい(バリアンスが高い) 傾向があります。

K=5 の場合: 各 fold で 4N/5 個のサンプルしか使えない。 学習データが減るぶん、モデルの性能が劣化する可能性がある(バイアスが上向き = 真の誤差を過大評価しがち)。 でも、5つのモデルは互いにかなり違うので、平均がブレにくい(バリアンスは小さい)。

このバイアスの正体は、「学習曲線の傾き」 で説明できます。 横軸を学習データの個数、縦軸をモデルの性能にとったグラフを見てみましょう。

学習曲線とK=5およびK=Nの学習サンプル数比較アニメーション
赤=学習曲線、緑=全データN、黄=5-foldが使うサイズ。矢印の幅がバイアスを示す

アニメーションで示したように、データが豊富なとき(N=200)は5-foldのバイアスはほぼゼロです。 しかし、データが少ないとき(N=50)は学習曲線の傾きが急で、5-foldと全データの性能差(矢印の幅)が大きくなります。 この差が「バイアス」です——5-fold CVは真の誤差を過大評価してしまいます。

$$K = N \;\Rightarrow\; \text{Leave-One-Out CV} \quad (\text{バイアス小、バリアンス大})$$
$$K = 5 \text{ or } 10 \;\Rightarrow\; \text{実用的な推奨} \quad (\text{バランス良好})$$

経験則として、5-fold か 10-fold が良いバランス とされています。 LOOCV は理論的にはバイアスが小さいですが、計算コストとバリアンスの面で実用上不利になることが多い。 「実用上は 5-fold か 10-fold」——これさえ覚えておけば、ほとんどの場面で困りません。

間違ったCVと正しいCV — 多くの研究者がハマる落とし穴

ここからが、本セクションで最も重要かつ実用的な部分です。 「正しいCV」と「間違ったCV」――この違いを知らないと、論文の結果が真っ赤な嘘になってしまうことがあります。 実際、トップジャーナルですら、この間違いを犯した論文が多数掲載されてきました。

まず次のアニメーションを見てください。 同じデータ、同じ手順なのに、片方はCV誤差 3%、もう片方はCV誤差 50% という、桁違いの結果が出ます。 一方は嘘、もう一方は本物。どちらが正しいでしょうか?

間違ったCVと正しいCVのフロー比較アニメーション
左(赤 ✗): 間違ったCV — 全データで変数選択してからCV。右(緑 ✓): 正しいCV — CVの内側で変数選択

種明かしです。シミュレーションの設定はこう:

普通のアナリストはこう考えます:

  1. まず、クラスラベルと相関が強い 100個の予測変数をスクリーニングして選ぶ
  2. その 100変数で 1-NN 分類器を作る
  3. CV で誤差を推定

非常にもっともらしい手順です。でも、シミュレーションすると CV 誤差は約 3%。 真の誤差 50% と比べて、信じられないほど低い数字――つまり完全な嘘です。

罠は ステップ 1 にあります。 100個の予測変数を選ぶときに、全データのラベル情報 を使ってしまっている。 だから、その後 CV でデータを分割しても、選ばれた変数たちは「すべてのサンプル(テスト fold も含む)を一度見てしまっている」のです。 テストサンプルが 「すでにカンニングされた」状態 で、公平なテストになっていません。

正しいやり方 は、こうです:

  1. データを K 個の fold に分ける
  2. 各 fold $k$ について:
    • (a) fold $k$ を除いたデータで 変数スクリーニング
    • (b) その変数で fold $k$ を除いたデータで 分類器を学習
    • (c) fold $k$ 予測して誤差を記録
  3. K 個の誤差を平均する

要するに、fold $k$ の中のサンプルは、その fold をテストするまで一切触らない。 変数選択も含めて、すべての学習プロセスを fold の外で完結させます。 こうすると、CV 誤差はちゃんと 50% 付近に戻ります。

覚えておきたいルール: ラベル情報を使う処理はすべて CV の 内側 に閉じ込めること。

$$\text{Wrong:} \quad \underbrace{\text{Select features}}_{\text{ALL data 使用}} \to \underbrace{\text{Build classifier}}_{\text{CVの内側}} \to \mathrm{CV}_{\text{wrong}} \approx 3\%$$
$$\text{Right:} \quad \underbrace{\text{Select features} \to \text{Build classifier}}_{\text{両方とも CV の内側}} \to \mathrm{CV}_{\text{right}} \approx 50\%$$

ただし注意点として、教師なしのスクリーニング(クラスラベルを使わない処理、たとえば「分散の大きい上位1000変数を選ぶ」など)は、 CV の外でやっても OK です。 ラベル情報を漏らしていないので、テストサンプルに不公平な優位を与えないからです。

高次元でCVは本当に動くのか? — 直感を裏切る実験

ここで、もう一つ深い疑問を提示しましょう。次のような「もっともらしい議論」を聞いたことがあるかもしれません:

「フル訓練データに最良の予測変数を見つけたら、その変数は 4/5 のデータでもおおむね最良のはずだ。 したがって 5-fold CV の誤差は小さくなり、真の誤差を過小評価する」

特に 高次元データ(特徴量の数 $p$ がサンプル数 $N$ よりずっと大きい場合)では、これがもっともらしく聞こえます。 実際、N=20 サンプル、p=500 特徴量で、全特徴量がラベルと独立(真の誤差 50%)という極端なシナリオを考えると、 上の議論が正しいなら CV は破綻するはずです。

ところが実際にシミュレーションしてみると、CV の平均誤差はちゃんと 50% 付近 に収まります。 一体なぜでしょう?

高次元データでstumpが各foldで再推定される様子のアニメーション
全20点(左)では完璧に分離。16点で再学習(中)すると、スプリット位置がズレて隠れた4点の2つを誤分類(赤フラッシュ)

種明かしはこうです。上の議論は 「変数は固定で、データを分割するだけ」 という暗黙の仮定を置いていました。 でも CV では、各 fold で完全にモデルを学習し直します。 分割の境界、係数の値、すべてが fold ごとに再推定されるのです。

アニメーションが示すように、全20点に対してフィットした垂直なスプリット線(白)は完璧に2クラスを分離します。 しかし、16点(4/5)だけで再学習すると、スプリット位置が黄色の線にズレ、 残りの4点を評価すると2点(赤くフラッシュ)が誤分類されます——これがCV誤差 ≈ 50% の正体です。

$$\mathrm{Err}_{\mathrm{CV}} \approx 50\% = \mathrm{Err}_{\text{true}}$$

直感の議論(CV誤差は小さくなるはず)が間違っていたのは、「モデルは各 fold で再学習される」 という事実を見落としていたから。 CV は、

$$\hat{f}^{-\kappa(i)} \;\;\text{は fold } \kappa(i) \text{ を除外して学習し直す}$$

ということを忠実に守ることで、はじめて意味のある誤差推定を提供するのです。 これが、Section 4 で見た「ラベル参照の処理はすべて CV の中に閉じ込めよ」というルールの本質的な意味です—— モデルを再学習するからこそ、高次元でも CV は機能します。

線形モデル限定の高速近似 — GCV

このセクションは技術的な内容です。初学者は「線形回帰みたいなシンプルなモデルには、LOOCVを一瞬で計算できる近似式がある」とだけ覚えて、必要になったら戻ってきても OK です。

最後に、LOOCV を超高速で近似する裏ワザ を紹介します。 LOOCV は通常「N回モデルを学習し直す」ので、大きなデータでは計算コストが大変です。 ところが――線形フィット + 二乗誤差 という特別な場合に限れば、 N回の再学習を一切やらずに LOOCV と同等の結果を計算できる魔法のような近似があります。 それが GCV(Generalized Cross-Validation、一般化交差検証) です。

「線形フィット」とは、予測値が学習ラベルに対して線形な関係:

$$\hat{\mathbf{y}} = \mathbf{S} \mathbf{y}$$

で表される手法のことです。$\mathbf{y}$ は学習データのラベル(縦ベクトル)、$\hat{\mathbf{y}}$ は予測値、$\mathbf{S}$スムーザー行列 と呼ばれる N×N の行列です。 線形回帰、リッジ回帰、スムージングスプラインなど、たくさんの手法がこの形をしています。

スムーザー行列Sの対角成分がGCV計算に効くことを視覚化するアニメーション
N×N行列の対角成分(黄)から矢印が伸びてN個の項に対応。最後に平均値(シアン)に集約される

アニメーションが示すように、N×N 行列の 対角成分が順に黄色く光り、 それぞれが右側の N 個の項に対応します。 最後に、対角成分の値が「平均値」として1つに集約されるのが GCV の本質です。

このとき、驚くべき等式が成り立ちます:

$$\frac{1}{N}\sum_{i=1}^{N}\bigl[y_i - \hat{f}^{-i}(x_i)\bigr]^2 = \frac{1}{N}\sum_{i=1}^{N}\left[\frac{y_i - \hat{f}(x_i)}{1 - S_{ii}}\right]^2$$

左辺は本物の LOOCV(N回再学習)、右辺は 「全データで1回だけ学習した結果」と「行列の対角成分 $S_{ii}$」だけで計算できる量。 実に N回の再学習が、たった1回の学習で済んでしまう のです。

GCV はこれをさらに簡略化し、$S_{ii}$ をすべて $\mathrm{trace}(\mathbf{S})/N$(対角成分の平均値)で置き換えます:

$$\mathrm{GCV}(\hat{f}) = \frac{1}{N}\sum_{i=1}^{N}\left[\frac{y_i - \hat{f}(x_i)}{1 - \mathrm{trace}(\mathbf{S})/N}\right]^2$$

ここで $\mathrm{trace}(\mathbf{S})$(行列の対角成分の和)は 有効パラメータ数—— モデルの実質的な複雑度を表す量です。 GCV は前のセクションで見た AIC とよく似た形をしています。

LOOCV を実際に N 回計算する代わりに、全データで1回学習 したあと、 対角成分(あるいはその平均)で割るだけで近似できる——これが GCV の魔法です。

まとめ

クロスバリデーションは、「データの一部を未来のフリにする」 という単純なアイデアから出発し、

  1. K-Fold CV で、テスト役を順番に交代させてデータを最大限活用する
  2. K の選び方 はバイアスとバリアンスのトレードオフ。実用上は 5-fold か 10-fold が推奨
  3. 「正しい CV と間違った CV」 ――ラベルを参照する処理はすべて CV の内側に閉じ込めよ(最重要
  4. 高次元でも CV はちゃんと動く ――ただし、各 fold で完全にモデルを再学習することが前提
  5. GCV は線形フィット+二乗誤差の特別な場合に LOOCV を高速近似

CV の表面的な単純さは、しばしばその深さを覆い隠します。 けれど、ここで見た原則を守れば、CV はあなたの最強の味方になります。 次のセクションでは、CV の親戚であり、もう一つの強力な誤差推定手法である ブートストラップ を見ていきましょう。