← 機械学習テキスト 一覧

🎓 レベル:標準 | 重要度:B(標準)

📎 前提:汎化と過学習・バイアスバリアンス分解多項式回帰と基底関数

要点(BLUF)

1. なぜ実験するのか

汎化と過学習・バイアスバリアンス分解の分解式は、固定した点 xx における期待二乗誤差を次のように分けたものでした。

E[(yf^(x))2]=σ2ノイズ+(fˉ(x)g(x))2バイアス2+ED[(f^(x)fˉ(x))2]バリアンス\mathbb{E}\big[(y - \hat f(x))^2\big] = \underbrace{\sigma^2}_{\text{ノイズ}} + \underbrace{\big(\bar f(x) - g(x)\big)^2}_{\text{バイアス}^2} + \underbrace{\mathbb{E}_D\big[(\hat f(x) - \bar f(x))^2\big]}_{\text{バリアンス}}

要するに:消せないノイズ σ2\sigma^2 に、「平均的にどれだけズレるか(バイアス²)」と「データ次第でどれだけ暴れるか(バリアンス)」を足したものが誤差です。

ここで fˉ(x)=ED[f^(x)]\bar f(x) = \mathbb{E}_D[\hat f(x)] は「訓練データ DD を取り替えたときの予測の平均」です。式の中に期待値 ED[]\mathbb{E}_D[\cdot] が入っているのがポイントで、これは1本の訓練データだけでは計算できません。訓練データを何度も取り替えて初めて意味を持つ量です。

そこで発想を逆にします。期待値を解析的に計算する代わりに、訓練データを実際に何百本も作って平均で近似する(モンテカルロ近似)。こうすればバイアス²もバリアンスも電卓のように数えられます。これが本ノートの実験です。

2. 実験のセットアップ

題材は多項式回帰と基底関数で扱った多項式回帰です。設定をひとつ決めておきます。

真の関数 ggσ2\sigma^2こちらで決めてデータを作るのが実験の肝です。現実のデータでは ggσ2\sigma^2 も未知でバイアスとバリアンスは直接測れませんが、シミュレーションなら正解を知っているので分解が数値で検証できます

3. 手順(モンテカルロ分解)

各次数 dd について、次を実行します。

  1. 訓練セットを BB 本生成するb=1,,Bb = 1, \dots, B(例:B=300B = 300)について、毎回新しいノイズを引いて y=g(x)+εy = g(x) + \varepsilon から訓練データ DbD_b を作る。
  2. DbD_b で次数 dd の多項式を学習し、予測器 f^b\hat f_b を得る
  3. テスト点ごとに BB 本の予測を集める。テスト点 xjx_j に対し、f^1(xj),,f^B(xj)\hat f_1(x_j), \dots, \hat f_B(x_j) という BB 個の予測値が並ぶ。
  4. 平均予測を出すfˉ(xj)=1Bb=1Bf^b(xj)\bar f(x_j) = \dfrac{1}{B}\sum_{b=1}^{B} \hat f_b(x_j)
  5. 点ごとにバイアス²とバリアンスを計算する
    • バイアス² =(fˉ(xj)g(xj))2= \big(\bar f(x_j) - g(x_j)\big)^2(平均予測が真の値からどれだけズレているか)
    • バリアンス =1Bb=1B(f^b(xj)fˉ(xj))2= \dfrac{1}{B}\sum_{b=1}^{B}\big(\hat f_b(x_j) - \bar f(x_j)\big)^2(予測が平均のまわりでどれだけ散らばるか)
  6. テスト点全体で平均する:手順5の2つの量を全テスト点で平均し、その次数 dd の代表値とする。
  7. 総誤差を組み立てるσ2+\sigma^2 + バイアス² ++ バリアンス。これが分解式の右辺で、実測の期待二乗誤差とほぼ一致するはずです。
flowchart TD
  G["真の関数 g(x) と ノイズ分散 σ²"] --> GEN["訓練セットを B 本生成<br/>y = g(x) + ε"]
  GEN --> FIT["各セットで次数 d を学習<br/>予測器 f_b を得る"]
  FIT --> COL["テスト点ごとに B 本の予測を集める"]
  COL --> MEAN["平均予測 f̄(x) を計算"]
  MEAN --> BIAS["バイアス² = ( f̄ − g )²"]
  MEAN --> VAR["バリアンス = 予測の散らばり"]
  BIAS --> SUM["総誤差 = σ² + バイアス² + バリアンス"]
  VAR --> SUM
  SUM --> REP["次数 d を変えて繰り返す"]

4. 結果として何が見えるか

次数 dd を横軸に、3つの量を並べると次の挙動が出ます。

xychart-beta
    title "次数を上げたときの誤差分解"
    x-axis ["1次", "3次", "5次", "9次", "15次"]
    y-axis "誤差" 0 --> 100
    line [78, 30, 14, 8, 5]
    line [4, 10, 20, 45, 82]
    line [92, 50, 44, 63, 97]

上から:バイアス²(右下がり)/バリアンス(右上がり)/総誤差(U字)。総誤差の谷が、この問題での「ちょうどよい次数」です。

谷の位置は真の関数の複雑さとノイズの大きさで決まります。ノイズ σ2\sigma^2 を大きくするとバリアンスの立ち上がりが早まり、谷は低い次数側に動きます。逆にデータ点を増やすとバリアンスが全体的に下がり、谷は浅く・右に動きます(バイアスはデータ量では下がらない点に注意。詳しくは汎化と過学習・バイアスバリアンス分解)。

5. 検証データとの対応

実務では真の関数 gg を知らないので、上の分解は直接は測れません。その代わりに使うのが検証データでの誤差です(訓練・検証・テストと交差検証)。検証誤差は「ノイズ + バイアス² + バリアンス」をまとめて推定した量なので、検証誤差が最小になる次数を選べば、結果的にU字の谷を選んでいることになります。

この実験は「検証誤差の谷の正体が、バイアス²とバリアンスのせめぎ合いである」ことを、正解を知っている人工データで裏側から確認する作業だと言えます。複雑さの抑え込み(正則化)でバリアンスを下げる手段は正則化(Ridge・Lasso・Elastic Net)を参照してください。

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

simulations/bias_variance.py:上の手順をそのまま実装します。真の関数 sin(2πx)\sin(2\pi x) にノイズを足して訓練セットを多数生成し、次数を変えて多項式回帰を学習。テスト点ごとにバイアス²・バリアンスを数値分解し、次数対誤差のグラフ(対数軸)でU字を再現します。高次でも数値的に安定させるため、当てはめは入力を [1,1][-1,1] に正規化する numpy.polynomial.Polynomial.fit を使っています。

複雑さを上げるとバイアス↓・バリアンス↑で総誤差がU字

⚠️ よくある誤解

関連ノート