0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

交差検証で学習データにオーバーフィットする事例について

Last updated at Posted at 2021-05-23

#はじめに
機械学習において汎化性能を評価する方法として、交差検証(クロスバリデーション)がよく用いられる。

このクロスバリデーションであるが、学習データにオーバーフィットすることもあるので、実施する前にテストデータを残しておき、クロスバリデーション後にテストデータを使って未知のデータに対する精度を検証することが一般的に推奨されている。

とはいえ、5分割とかに分割して検証しているのに、学習データにオーバーフィットすることなんてあるの? と半信半疑だった。今回、見事にオーバーフィットするケースを見つけたので共有したい。

#前提
今回は、http://moleculenet.ai/datasets-1 で公開されているFreeSolv(回帰)のデータを用いる。MoleculeNetによるとこのデータの最高精度はR^20.92程度となっている。

#やってみよう

データを学習449件、テスト193件に分割し、420個の多めの特徴量を用いて、学習データに対し scikit-learn の SequentialFeatureSelector を使って特徴量を5~50まで5個ずつ刻みながら特徴選択をしてみた。

from sklearn.feature_selection import SequentialFeatureSelector
from sklearn.cross_decomposition import PLSRegression

#select_features = [] 
for feature_num in [5,10,15,20,25,30,35,40,45,50]:
    pls = PLSRegression(n_components=5)
    sfs = SequentialFeatureSelector(pls, n_features_to_select=feature_num)
    sfs.fit(train_X_scaled, train_t)
    select_features.append(sfs.get_support())

続いて、選択された5~50の特徴量セット毎にクロスバリデーションスコア、外部検証スコアを求めてグラフ化してみた。

from sklearn.model_selection import cross_validate
scores = []
stds = []
test_scores = []

# 選択された特徴セット(5~50)毎にクロスバリデーションスコア、外部検証スコアをグラフ化する
for featuers in select_features:
    # クロスバリデーションスコア
    train_X_scaled_tmp = train_X_scaled[:, featuers]
    pls = PLSRegression(n_components=5)    
    cv_results = cross_validate(pls, train_X_scaled_tmp, train_t)
    scores.append(cv_results["test_score"].mean())
    stds.append(cv_results["test_score"].std())
    
    # 外部データに対するスコア
    test_X_scaled_tmp = test_X_scaled[:, featuers]
    pls = PLSRegression(n_components=5)    
    pls.fit(train_X_scaled_tmp, train_t)
    test_scores.append(pls.score(test_X_scaled_tmp, test_t))

# 可視化
figure = plt.figure(figsize=(8,4))
ax = figure.add_subplot(111)
ax.set_title("PLS score plot")
ax.set_xlabel("n_components")
ax.set_ylabel("r2 score")
ax.plot(range(len(scores)), scores)
ax.fill_between(range(len(scores)), np.array(scores)-np.array(stds), np.array(scores)+np.array(stds), scores, alpha=0.5, color="lightgray")
ax.plot(range(len(scores)), test_scores)

すると以下図において、クロスバリデーションスコアが青で、外部データに対するスコアがオレンジで得られる。
クロスバリデーションスコアはなんと、MoleculeNetの最高精度を超えている。しかし外部検証スコアは10をピークに下がり続けている。

image.png

グラフを見ると分かるが、20以上の特徴量セットは、学習データにオーバーフィットしてしまっている。

#なんでこうなったのか?
一言でいうと、クロスバリデーションスコアが良くなるような特徴が選ばれたということであろう。
オーバフィットといういうとハイパーパラメータに目が行きがちであるが、このように特徴選択でも起こりうるということを身をもって知った次第である。理論的根拠を説明できないが、線形手法において起こりやすそう。
特徴選択は奥が深い。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?