Help us understand the problem. What is going on with this article?

クロスバリデーションとグリッドサーチ(備忘録)

このブログについて

  • 備忘録のためにクロスバリデーション、及びグリットサーチについてまとめます
  • Pythonのscikit-learnライブラリを使用します

クロスバリデーション

トレーニングデータとテストデータへの分割

機械学習では過学習を避けるため、及び未知のデータへの予測精度を検証するため、与えられたデータセットを訓練用データと検証用データに分割します。

download.jpg

scikit-learnのデータセットを使ってデータの分割を行います

from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split

# datasetのインスタンスを宣言
wine = load_wine()
# 説明変数と目的変数を生成
X = wine.data
y = wine.target
# データを分割
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
'''
[out]
データセットのレコード数:  178 
トレーニングデータのレコード数:  133 
テストデータのレコード数:  45
'''
print('データセットのレコード数: ', len(X), '\n',
      'トレーニングデータのレコード数: ', len(X_train), '\n',
      'テストデータのレコード数: ', len(X_test))

このデータを使ってモデル(今回はランダムフォレスト)の作成を行います

import numpy as np
from sklearn.ensemble import RandomForestClassifier

# パラメータは一先ず仮置き
depth = 3
leaf = 5
forest = RandomForestClassifier(n_estimators=10, max_depth=depth, min_samples_leaf=leaf).fit(X_train, y_train)

# テストデータを使ったモデルの検証
score = forest.score(X_test, y_test)

# out[score depth3 leaf5:  1.000]
print('score depth{} leaf{}:  {:0.3f}'.format(depth, leaf, score))

クロスバリデーションの実装

上の例ではscikit-learnのtrain_test_splitを使ってデータの分割を行いました。しかし、上記の結果だとトレーニングデータとテストデータの分割に、偏りが出た場合、精度検証に思ってもみないバイアスが生まれる可能性もあります。そこでクロスバリデーションという方法を使います。

download-1.jpg

上図のように与えられたデータセットを$n$個に分割して(上記の場合は5つ)、そのうち一つをテストデータとして検証します。その処理を$n$回行うことで、モデルの精度を検証します。

from sklearn.model_selection import cross_val_score
# 桁数を指定
np.set_printoptions(precision=3)

# cv=5で分割数を指定
scores = cross_val_score(forest, X, y, cv=5)

# out[0.757  0.972  0.944  1.    0.971]
print(scores)

この場合、トレーニングとテストの分割を変えることで、精度が大きく変わってくることが確認できます

グリッドサーチ

モデルの精度を向上させるために用いられる手法で、全てのパラメータの組み合わせを試してみる方法のことです

グリッドサーチの実装

# パラメータのリストを生成
depth = [2, 3, 4, 5, 6, 7]
leaf = [1, 3, 5, 7, 12]
scores = {}

for i in depth:
    for j in leaf:
        forest =  RandomForestClassifier(n_estimators=10, max_depth=i, min_samples_leaf=j, random_state=0).fit(X_train, y_train)
        score = forest.score(X_test, y_test)
        scores['depth{}, leaf{}'.format(i, j)] = round(score, 3)

'''
[out]
{'depth2, leaf1': 0.956, 'depth2, leaf3': 0.933, 'depth2, leaf5': 0.956, 'depth2, leaf7': 0.933, 'depth2, leaf12': 0.889,
'depth3, leaf1': 0.978, 'depth3, leaf3': 0.956, 'depth3, leaf5': 0.956, 'depth3, leaf7': 0.933, 'depth3, leaf12': 0.956,
'depth4, leaf1': 0.956, 'depth4, leaf3': 1.0, 'depth4, leaf5': 0.956, 'depth4, leaf7': 0.956, 'depth4, leaf12': 0.956,
'depth5, leaf1': 0.956, 'depth5, leaf3': 1.0, 'depth5, leaf5': 0.956, 'depth5, leaf7': 0.956, 'depth5, leaf12': 0.956,
'depth6, leaf1': 0.956, 'depth6, leaf3': 1.0, 'depth6, leaf5': 0.956, 'depth6, leaf7': 0.956, 'depth6, leaf12': 0.956,
'depth7, leaf1': 0.956, 'depth7, leaf3': 1.0, 'depth7, leaf5': 0.956, 'depth7, leaf7': 0.956, 'depth7, leaf12': 0.956}
'''
print(scores)
  • 上記方法を使うと、テストデータ(X_test)に対して最も当てははりの良いパラメータ、及びその時のスコアが見つかります
  • しかし、実際は検証用として使うべきテストデータをチューニングに使ってモデルを構築してしまっています

学校のテストで例えると…

  • 本来であれば、トレーニングデータ(学習用の参考書)を使って勉強し、本番のテスト(テストデータ)で学習成果を測るといった流れが無視されています
  • 本番用のテストをカンペして、学習しているような状況になり、どれほど汎化性能の良いモデルができたのか、いまいち分からない状況になっています

この問題を解決するため、トレーニングデータを更に学習データと、パラメータ調整用のデータに分割します。イメージは以下の通りです
download-2.jpg

X_train_val, X_test, y_train_val, y_test = train_test_split(X, y, random_state=0)

X_train, X_val, y_train, y_val = train_test_split(X_train_val, y_train_val, random_state=0)

'''
[out]
X_test shape:  (45, 13) 
X_val shape:  (34, 13) 
X_train shape:  (99, 13)
'''
print('X_test shape: ', X_test.shape, '\n'
      'X_val shape: ', X_val.shape, '\n'
      'X_train shape: ', X_train.shape)

depth = [2, 3, 4, 5, 6, 7]
leaf = [1, 3, 5, 7, 10]
best_val_score = 0   # best_scoreを初期化
best_param = {}   # best_paramを初期化

for i in depth:
    for j in leaf:
        forest = RandomForestClassifier(n_estimators=10, max_depth=i, min_samples_leaf=j, random_state=0).fit(X_train, y_train)
        score = forest.score(X_val, y_val)
        if score > best_val_score:
            best_val_score = score
            best_param = {'max_depth': i, 'min_samples_leaf': j}

'''
[out]
best_val_score:  0.941
best_param:  {'max_depth': 5, 'min_samples_leaf': 1}
'''
print('best_val_score: ', round(best_val_score, 3))
print('best_param: ', best_param)

# テストデータで精度を検証
# 引数として辞書型の前に**を持ってくることで、展開が可能
forest = RandomForestClassifier(n_estimators=10, random_state=0, **best_param).fit(X_train_val, y_train_val)
best_score = forest.score(X_test, y_test)
# [out]  best_score: 0.956
print('best_score:', round(best_score, 3))

クロスバリデーションとグリッドサーチの併用

  • 計算量は大きくなってしまいますが、クロスバリデーションとグリッドサーチを併用することで最良のパラメーターを求める手法もあります
  • これを交差検証法と言います
depth = [2, 3, 4, 5, 6, 7]
leaf = [1, 3, 5, 7, 10]
best_val_score = 0
best_param = {}

for i in depth:
    for j in leaf:
        forest = RandomForestClassifier(n_estimators=10, max_depth=i, min_samples_leaf=j, random_state=0)
        scores = cross_val_score(forest, X_train_val, y_train_val, cv=5)
        mean_score = np.mean(scores)
        if mean_score > best_val_score:
            best_val_score = mean_score
            best_param = {'max_depth': i, 'min_samples_leaf': j}

# out[best_val_score: 0.955]
# out[best_param:  {'max_depth': 2, 'min_samples_leaf': 1}]
print('best_val_score: {:0.3f}'.format(best_val_score))
print('best_param: ', best_param)

この手法はscikit-learnのクラスを使うことで、より少ないコードで実装できます

from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier

param_grid = {'max_depth': [2, 3, 4, 5, 6, 7],
              'min_samples_leaf': [1, 3, 5, 7, 10]}

forest = RandomForestClassifier(n_estimators=10, random_state=0)
grid_search = GridSearchCV(forest, param_grid, iid=True, cv=5, return_train_score=True)

# GridSearchCVは最良パラメータの探索だけでなく、それを使った学習メソッドも持っています
grid_search.fit(X_train_val, y_train_val)


'''
[out]
best score: 0.956
best params: {'max_depth': 2, 'min_samples_leaf': 1}
best val score:  0.955
'''
print('best score: {:0.3f}'.format(grid_search.score(X_test, y_test)))
print('best params: {}'.format(grid_search.best_params_))
print('best val score:  {:0.3f}'.format(grid_search.best_score_))

best val scoreは一つ上のコードと同じ結果になっていることが分かります

GridSearchCV結果の可視化

  • GridSearchCVの結果はcv_resultというオプションを参照すれば出てきます
  • 今回はpandasのデータフレーム 、及びヒートマップを使って結果の可視化を行います
import pandas as  pd
import seaborn as sns
%matplotlib inline

cv_result = pd.DataFrame(grid_search.cv_results_)
cv_result = cv_result[['param_max_depth', 'param_min_samples_leaf', 'mean_test_score']]
cv_result_pivot = cv_result.pivot_table('mean_test_score', 'param_max_depth', 'param_min_samples_leaf')

heat_map = sns.heatmap(cv_result_pivot, cmap='Greys', annot=True);

download.png

Takayoshi_Makabe
ストリートダンスとプロ野球が趣味です。
https://spider-man-tm.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away