case-kの備忘録

日々の備忘録です。データ分析とか基盤系に興味あります。

モデルのパラメータ探索手法、「グリッドサーチ」ってなんだ

今回はモデルの性能を向上させるための手法、グリッドサーチの記事を書いてみました。

本記事の目的

・グリッドサーチの概要を理解する
・実際にグリッドサーチを活用した分析ができるようになること

グリッドサーチとは

機械学習を行うモデルを構築して実際に予測しても予測精度が低いことが多いと思います。グリッドサーチとは、モデルの精度を向上させるために用いられる手法です。全てのパラメータの組み合わせを試してみる方法のことです。イメージとしてはループさせて、パラメータの組み合わせを全て試し、最も評価精度の良いものを探索する方法です。パラメータを変更することで予測精度は飛躍的に変わります。

単純なグリッドサーチ

単純なグリッドサーチは、2つのパラメータに対するただのforループで実装することができます。
実際に試してみたいと思います。アルゴリズムは分類問題で利用される、SVM(サポートベクターマシン)を用いてみます。
実施していることとしてはfor分でSVMのハイパーパラメータである、「gammaとC」を調整しているだけです。

ハイパーパラメータとは
機械学習で使われるモデルには多かれ少なかれ分析者が設定しなければならないパラメータがあります。機械学習で学習されない、機械学習の上にある(ハイパーな)パラメータなのでハイパーパラメータと呼ばれます。

処理フロー
1.for list[gamma]
2.for list[C]
3.create the model
4.get model score
5.if model score > model score[pre]
6.save model score and params
7.print best score and params

# library
from sklearn.svm import SVC
from sklearn.datasets import make_blobs
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
import mglearn
from matplotlib import pyplot as plt
%matplotlib inline
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, random_state=0)
print("Size of training set:{} size of test set:{}".format(X_train.shape[0], X_test.shape[0]))

best_score = 0

for gamma in [0.001, 0.01, 0.1, 1, 10, 100]:
    for C in [0.001, 0.01, 0.1, 1, 10, 100]:
        #それぞれのパラメータの組み合わせに足してSVCを訓練
        svm = SVC(gamma=gamma, C=C)
        svm.fit(X_train, y_train)
        #SVCをテストセットで評価
        score = svm.score(X_test, y_test)
        #良いスコアであればスコアとパラメータを保持
        if score > best_score:
            best_score = score
            best_parameters = {'C':C, 'gamma':gamma}
print("Best score: {:.2f}".format(best_score))
print("Best parameters: {}".format(best_parameters))

# Out
Size of training set:112 size of test set:38
Best score: 0.97
Best parameters: {'C': 100, 'gamma': 0.001}

評価制度の最も高くなるパラメータは、gamma: 0.001、C: 100であり、精度は97%と非常に高いスコアを得ることができました。

単純なグリッドサーチの問題点

単純なグリッドサーチは高い精度が確認できますが大きな問題点があります。テストデータをパラメータのチューニングで活用していることです。
パラメータのチューニングで活用したテストデータでモデルの精度を評価することができません。これは訓練データとテストデータを分けた理由と同じで、一度活用したデータで予測を行うこと=未知のデータに対する予測にはならないからです。

単純なグリッドサーチの解決策について

この問題を解決する1つの方法は、もう一度分割し、3つのセットにする方法です。各データセットの役割は以下の通りです。

・training set:モデルを構築する訓練セット
・validation set:モデルのパラメータ選択に用いる検証セット
・test set:選択したパラメータの性能を評価するためのテストセット

f:id:casekblog:20180903204857p:plain

処理フロー
1.split and create train , validation, test data
2.loop params
3.create model by train data
4.evaluate model by validation data
5.save the high score params
6.evaluate model made by high params by test data

from sklearn.svm import SVC
# データを訓練 + 検証セットとテストセットに分割する
X_trainval, X_test, y_trainval, y_test = train_test_split(
    iris.data, iris.target, random_state=0)
#訓練+検証セットを訓練セットと検証セットに分割する
X_train, X_valid, y_train, y_valid = train_test_split(X_trainval,y_trainval,random_state=1)
print("size of train set :{}\nsize of validation set:{}\nsize of test set:"
       "{}\n".format(X_train.shape[0], X_valid.shape[0], X_test.shape[0]))

best_score = 0

for gamma in [0.001, 0.01, 0.1, 1, 10, 100]:
    for C in [0.001, 0.01, 0.1, 1, 10, 100]:
        # それぞれのパラメータの組み合わせに対してSVCを訓練する
        svm = SVC(gamma=gamma, C=C)
        svm.fit(X_train, y_train)
        # SVCを検証セットで評価
        score = svm.score(X_valid, y_valid)
        # 良いスコアだったらスコアとパラメータを保持
        if score > best_score:
            best_score = score
            best_parameters = {'C':C, 'gamma':gamma}
# 訓練セットと検証セットを用いてモデルを再構築し、
# テストセットで評価
svm = SVC(**best_parameters)
svm.fit(X_trainval, y_trainval)
test_score = svm.score(X_test, y_test)
print("Best score on validation set:{:.2f}".format(best_score))
print("Best parameters:",best_parameters)
print("Test set score with best parameters:{:.2f}".format(test_score))

# Out
ize of train set :84
size of validation set:28
size of test set:38

Best score on validation set:0.96
Best parameters: {'C': 10, 'gamma': 0.001}
Test set score with best parameters:0.92

検証データに対する性能は0.96と先ほどより低くなっている。
→ おそらく学習するデータ量が減ったため
テストデータに対する性能は0.92とさらに低くなっている。
→ 新しいデータに対して主張できる性能は、単純なグリッドサーチで行った0.97ではなく、0.92である。

交差検証を用いたグリッドサーチ

上記で未知のデータに対して予測できるようになりました。しかし、汎化性を担保するためには分割時点で交差検証を用いる必要があります。データの分割方法によって性能が大きく変わるからです。交差検証に関しては以下を確認してください。

case-k.hatenablog.com

処理フロー
1.split the data set trainval and test
2.for list[gamma]
3.for list[C]
4.create the model
5.get model score by cross validation
6.if model score > model score[pre]
7.save model score and params
8.print best score and params

X_trainval, X_test, y_trainval, y_test = train_test_split(
    iris.data, iris.target, random_state=0)

for gmma in [0.001,0.01, 0.1, 1, 10, 100]:
    for C in [0.001,0.01,0.1,1,10,100]:
        # それぞれのパラメータの組み合わせに対して
        # SVCを訓練する
        svm = SVC(gamma=gamma, C=C)
        # 交差検証を行う
        scores = cross_val_score(svm, X_trainval,y_trainval,cv=5)
        # 交差検証の平均値を計算
        score = np.mean(scores)
        # 良いスコアであれbスコアとパラメータを記録
        if score > best_score:
            best_score = score
            best_parameters = {'C':C,'gamma':gamma}
# 訓練セットと検証セットを合わせてモデルを再構築する
svm = SVC(**best_parameters)
svm.fit(X_trainval, y_trainval)

# Out

SVC(C=10, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma=0.001, kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False)

分割数は5なので、6*6*5=180通りのモデルを構築しています。膨大な時間がかかるのが交差検証の問題点と言えます。交差検証はグリッドサーチのようなパラメータ探索手法と併用されることが多いため、交差検証を用いたグリッドサーチを指して交差検証と呼ぶことが多いです。scikit-learnはグリッドサーチを実装したクラス[GridSearchCV]を用意してくれています。
実際に試してみたいと思います。

処理フロー
1.create params
2.create the GridSearchCV instance
3.split the data train and test
4.cross-validation

# ライブラリ
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC

# パラメータ
param_grid = {'C':[0.001, 0.01, 0.1, 1,10, 100],
              'gamma':[0.001, 0.01, 0.1, 1, 10, 100]}
print("Parameter grid:\n{}".format(param_grid))

# GridSearch インスタンス生成
grid_search = GridSearchCV(SVC(), param_grid, cv=5)

# パラメータの過剰適合を防ぐためにさらに訓練セットとテストセットを分割
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, random_state=0)

# 交差検証を実行
grid_search.fit(X_train, y_train)
print("test set score:{:.2f}".format(grid_search.score(X_test, y_test)))
print("Best parameters:{}".format(grid_search.best_params_))
print("Best cross-validation score :{:.2f}".format(grid_search.best_score_))

# Out
Parameter grid:
{'C': [0.001, 0.01, 0.1, 1, 10, 100], 'gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
test set score:0.97
Best parameters:{'C': 100, 'gamma': 0.01}
Best cross-validation score :0.97

この方法だとパラメータの過剰適合が起こってしまうのでは?と思った方がいるかもしれません。
重要なのはテストセットを使わなかったということです。グリッドサーチで見つけたパラメータはbest_params属性に、交差検証精度はbest_score属性に格納されています。
注意しなければいけないことは、各データが何を意味しているかです。
各関数の意味を正確に理解しておく必要があります。

・grid_search.score(X_test, y_test))
こちらは訓練セット全体を用いて訓練したモデルをテストデータで評価した精度です。

・grid_search.best_score_
こちらは訓練セットに対する交差検証の平均交差懸賞精度です。

なのでGridSearchCVで探索した最適なパラメータは「grid_search.best_params_」に格納されており、モデルの汎化性能に関する評価精度は「grid_search.score(X_test, y_test)」に格納されてます。


今回はグリッドサーチについて学んでみました。毎回思うのですがPython便利です!
とは言え、各関数やデータが何を意味しているか理解していないと大変なことになりますね。
気をつけないと(汗)

参考書
https://www.amazon.co.jp/Python%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92-%E2%80%95scikit-learn%E3%81%A7%E5%AD%A6%E3%81%B6%E7%89%B9%E5%BE%B4%E9%87%8F%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%A8%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AE%E5%9F%BA%E7%A4%8E-Andreas-C-Muller/dp/4873117984/ref=sr_1_1/358-5134796-9422624?ie=UTF8&qid=1535802578&sr=8-1&keywords=python%E3%81%A7%E5%A7%8B%E3%82%81%E3%82%8B%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92