最初の機械学習モデルのトレーニングが完了しました。トレーニング データに対して優れたパフォーマンスを発揮します。精度は 98% です。新しいデータでテストすると、精度は 61% とばらばらになりました。何が間違っていたのでしょうか?
ほぼ確実に、モデルには過剰適合があります。これは機械学習で最も一般的な 2 つの故障モードのうちの 1 つであり、これを理解することは、その反対の 過小学習 と並んで、現実世界で実際に機能するモデルを構築するために不可欠です。
例に入る前に、これらの概念の背後にある理論的枠組み、バイアスと分散のトレードオフを理解するのに役立ちます。
どのモデルも予測エラーを起こします。これらのエラーは、次の 3 つの部分に分解できます。
Total Error = Bias² + Variance + Irreducible Noise
バイアスは、モデル内の誤った仮定による誤差です。高バイアス モデルは単純すぎるため、データ内の真のパターンを体系的に見逃しています。
分散 は、トレーニング データの小さな変動に対する感度から生じる誤差です。高分散モデルは複雑すぎるため、基礎となるパターンを学習するのではなく、ノイズを含むトレーニング データを記憶します。
還元不可能なノイズ は、どのモデルも除去できないデータ内の自然なランダム性です。
トレードオフ: バイアスを減らすと分散が増加する傾向があり、その逆も同様です。機械学習実践者としてのあなたの仕事は、スイートスポットを見つけることです。
アンダーフィッティングは、モデルが単純すぎてデータ内の真のパターンを捉えることができない場合に発生します。トレーニング データと新しいデータの「両方」でパフォーマンスが低下します。
サイズに基づいて住宅価格を示すデータがあると想像してください。実際の関係はほぼ緩やかな曲線です。価格はサイズに応じて上昇しますが、最高値ではある程度の頭打ちになります。
このデータに直線の水平線を当てはめると、次のようになります。
# Underfitting: overly simple model
from sklearn.linear_model import LinearRegression
import numpy as np
# True relationship is quadratic, but we're fitting a simple mean
model = DummyRegressor(strategy='mean')
model.fit(X_train, y_train)
# Training accuracy: 55%
# Test accuracy: 54%
# Both are bad — classic underfitting
このモデルは、住宅のサイズと価格の実際の関係を無視しています。トレーニング データを表示するか新しいデータを表示するかは関係ありません。どちらにしても間違っています。
過学習は、モデルがノイズやランダムな変動を含むトレーニング データを 学習しすぎて、新しい例に一般化できない場合に発生します。
15 次の多項式を使用して同じ住宅価格データを近似します。
# Overfitting: overly complex model
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
# Degree-15 polynomial — wildly complex for this problem
model = make_pipeline(PolynomialFeatures(15), LinearRegression())
model.fit(X_train, y_train)
train_score = model.score(X_train, y_train) # 0.99 — looks amazing!
test_score = model.score(X_test, y_test) # 0.43 — terrible on new data
ログイン ディスカッションに参加
多項式は、ノイズの多い外れ値を含むすべてのトレーニング ポイントを通過するために、それ自体をねじって結び目になっています。基礎となるパターンを学習するのではなく、トレーニングセットを記憶してしまいました。目に見えないデータでは役に立ちません。
過学習を検出するための基本的なツールは、データを 3 つのセットに分割することです。
from sklearn.model_selection import train_test_split
# First split: hold out 20% as the final test set
X_train_val, X_test, y_train_val, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Second split: hold out 20% of remaining as validation set
X_train, X_val, y_train, y_val = train_test_split(
X_train_val, y_train_val, test_size=0.25, random_state=42
)
# Result: 60% train / 20% validation / 20% test
検証セットは早期警告システムです。トレーニングの精度は向上し続けているが、検証の精度が頭打ちまたは低下している場合は、過剰適合しています。
正則化により、モデルが過度に複雑なパターンを学習するのを妨げるペナルティが損失関数に追加されます。
L2 正則化 (リッジ) は、大きな重みにペナルティを与えます。
from sklearn.linear_model import Ridge
model = Ridge(alpha=1.0) # alpha controls regularisation strength
model.fit(X_train, y_train)
L1 正則化 (Lasso) は、一部の重みをゼロまで駆動して、特徴選択を実行できます。
from sklearn.linear_model import Lasso
model = Lasso(alpha=0.1)
model.fit(X_train, y_train)
トレーニング中に、一部のニューロンをランダムに「ドロップアウト」(ゼロに設定)します。
import torch.nn as nn
model = nn.Sequential(
nn.Linear(128, 64),
nn.ReLU(),
nn.Dropout(p=0.5), # 50% of neurons randomly deactivated during training
nn.Linear(64, 1)
)
これにより、ニューロンの同時適応が妨げられ、ネットワークはより堅牢な分散表現を学習することになります。
トレーニング中に検証損失を監視し、増加し始めたら停止します。
from tensorflow.keras.callbacks import EarlyStopping
early_stop = EarlyStopping(
monitor='val_loss',
patience=5, # stop after 5 epochs without improvement
restore_best_weights=True
)
model.fit(X_train, y_train,
validation_data=(X_val, y_val),
callbacks=[early_stop],
epochs=1000)
データが増えると、モデルがノイズを記憶することが難しくなります。正確に当てはめるには多すぎるのです。データ収集にコストがかかる場合は、データ拡張 (既存の例の変更されたコピーの作成) が役立ちます。
場合によっては、問題に対して単純に複雑でないモデルを選択することが正しい解決策となる場合があります。
データセットが小さい場合、単一の train/val 分割はランダム性により誤解を招く可能性があります。 K 分割相互検証 により、より信頼性の高い推定値が得られます。
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=100)
# 5-fold cross-validation
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
print(f"Mean accuracy: {scores.mean():.3f} ± {scores.std():.3f}")
# Mean accuracy: 0.847 ± 0.023
データは 5 つに分割されます。モデルは 4 でトレーニングし、1 で検証し、毎回ローテーションします。最終的なスコアは 5 つすべての平均であり、単一の分割よりもはるかに信頼性が高くなります。
モデルはトレーニング データでは 99% の精度を達成しますが、テスト データでは 62% にすぎません。これは何を示しているのでしょうか?