Categories
データサイエンス

validation_curveでGridSearchCVとRandomForestClassifierのパラメータチューニング

前回はGridSearchCVを使って、ランダムフォレスト(RandomForestClassifier)のパラメータの最適解を求めました。

「GridSearchCVを使えば、いつでも最適解を出せるから楽だよね

と思ってました。

ですが、甘かったです。前回のはわずか30分程度で終わりましたが、実は最初に適当に各パラメータの候補を最大10個くらい設定して(=配列の要素数を10個)にして行ったら、2時間以上かかってしまいました。

たった数百行の学習データでパラメータも数種類しかないのに2時間だったら、私が実務で使っているのは会員データだけで3000万レコード、購入履歴は数億レコードもあるので、GridSearchCVをそのまま使うのは非現実的です。(それを全部使うことは実際はありませんが。。。)

validation_curveでパラメータの範囲を絞る

そこで使うのがvalidation_curve です。validation_curveについての詳しい説明は省略しますが、訓練データとテストデータでの正解率を比較して、ハイパーパラメータの値が小さすぎて学習不足だったり、逆に値が大きすぎて過学習を起こしたりしていないか?を確認するものです。

それでは実際に前回と同様にKaggleのTitanic課題を使って、max_depthを1,3,5,7・・・29までとして検証してみましょう

from sklearn.model_selection import validation_curve
param_range = list(range(1,30,2))
train_scores, test_scores = validation_curve(estimator = RandomForestClassifier(), X = X_train, y = y_train, param_name="max_depth", param_range=param_range, cv=5, n_jobs=1)
train_mean = np.mean(train_scores, axis=1)
train_std  = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std  = np.std(test_scores, axis=1)
plt.plot(param_range,train_mean,label="Training score",color="black")
plt.plot(param_range,test_mean,label="Validation score",color="dimgrey")
plt.fill_between(param_range, train_mean + train_std, train_mean - train_std, alpha=0.1, color="orange")
plt.fill_between(param_range, test_mean + test_std, test_mean - test_std, alpha=0.1, color="darkblue")
plt.legend(loc="upper left")
plt.title("Validation Curve")
plt.xlabel("max_depth")
plt.ylabel("Accuracy Score")
plt.tight_layout()
plt.show();
  • Training Score:訓練データのグラフ
  • Validation Score:テストデータのグラフ( 標準偏差の範囲を色付けしています)
  • 横軸:max_depthの値(1、3、5・・・・29)
  • 縦軸(Accuracy Score):正解率

訓練データでは17あたりでほぼピークに達しているのがわかります。一方、テストデータをみると7あたりでピークいなっているのがわかりますので、GridSearchCVでは3~15くらいで検証すれば良いと推測できます。

つまり、前回のGridSearchCVではmax_depthを 3, 5, 10, 15, 20, 25, 30, 50, 100で行いましたが15や20・・・100が無駄で、その分、10以下の数値に割り当てたほうが良いということがわかります。

validation_curveを関数化

validation_curveは各パラメータについて使いますので、関数化します。今回はn_jobs=1、画像の凡例の位置を左上に固定していますが、どちらも任意です。

from sklearn.model_selection import validation_curve
import matplotlib.pyplot as plt
def func_validation_curve(model,X_train,y_train, p_name, p_range, cv ):
    train_scores, test_scores = validation_curve(estimator = model, X = X_train, y = y_train, param_name=p_name, param_range=p_range, cv=cv, n_jobs=1)
    train_mean = np.mean(train_scores, axis=1)
    train_std  = np.std(train_scores, axis=1)
    test_mean = np.mean(test_scores, axis=1)
    test_std  = np.std(test_scores, axis=1)
    plt.plot(param_range,train_mean,label="Training score",color="blue")
    plt.plot(param_range,test_mean,label="Validation score",color="red")
    plt.fill_between(param_range, train_mean + train_std, train_mean - train_std, alpha=0.1, color="cyan")
    plt.fill_between(param_range, test_mean + test_std, test_mean - test_std, alpha=0.1, color="magenta")
    plt.legend(loc="upper left")
    plt.title("Validation Curve - " + p_name)
    plt.xlabel(p_name)
    plt.ylabel("Accuracy")
    plt.tight_layout()
    plt.show();

他のパラメータでもvalidation_curveを実施

それでは他のパラメータについてもvalidation_curveを実施します。前回のGridSearchCVで算出した最適解を使って行います。

clf = RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None, max_depth=7
                       criterion='gini', max_features=1.0,
                       max_leaf_nodes=None, max_samples=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=1, min_samples_split=10,
                       min_weight_fraction_leaf=0.0, n_estimators=1000,
                       n_jobs=1, oob_score=False, random_state=1,
                       warm_start=False)

criterion

param_range = ["gini","entropy"]
func_validation_curve(clf, X_train, y_train, 'criterion', param_range, 5)

criterionはginiとentropyの2種類しかないので、GridSearchCVでは両方試して良いと思いますが、参考までにやってみました。

max_features (ノード分割する際に考慮する特徴量数)

param_range = [0.1,0.3,0.5,0.7,0.9,1.0]
func_validation_curve(clf, X_train, y_train, 'max_features', param_range, 5)

max_featuresは0.5以上が良さそうなことが伺えます。1.0になるとTraining Scoreが上がっているのに対してValidation Scoreが下がっているので、過学習を起こしているかもしれません。

min_samples_split (末端ノード内の最小サンプル数)

次にmin_samples_splitです。こちらも前回のGridSearchCVで使った範囲で行います。

param_range = [3, 5, 10, 15, 20, 25, 30, 50, 100]
func_validation_curve(clf, X_train, y_train, 'min_samples_split', param_range, 5)

っが、15以降はTraining ScoreもValidation Scoreも悪化しているので、2~20までで再度検証してみます。

これをみると2~20までほぼ横ばいなのがわかります。GridSearchCVではこの全範囲を対象にするとします。

n_estimators

前回、 n_estimatorsはやっても意味がない、ということを書きましたが、せっかくなのでn_estimatorsも検証してみましょう。

param_range = [10,20,30,50,100,250,500,750,1000]
func_validation_curve(clf, X_train, y_train, 'n_estimators', param_range, 5)

実際にやってみると100以降はほぼ横ばいです。ということで100まででもう1度検証します。

param_range = [1,5,10,20,25,50,75,100]
func_validation_curve(clf, X_train, y_train, 'n_estimators', param_range, 5)

これをみる限りでは25~150までわずかに上昇して、それ以降は下降しているように見えます。いずれにしても前回は1000で固定していたのですが、そこまで大きくする必要はなさそうです。

GridSearchCVを再実行

今回の結果をもとにGridSearchCVを再度実行してみます。結果もそうですが、時間がどれだけ短縮されるかにも着目してみます。

from sklearn.model_selection import GridSearchCV
# RandomForestClassifierで使用するパラメータ
search_params = {
     'n_estimators'      : [150],
      'criterion':['gini','entropy'],
      'max_features'      :[0.5,0.6,0.7,0,8,0.9],
      'random_state'      : [1],
      'min_samples_split' : [2, 3, 5, 7, 9, 11, 13, 15],
      'max_depth'         : [3, 5, 7, 9, 11, 13, 15],
}
# GridSearchCVのオブジェクトを作成
gs = GridSearchCV(RandomForestClassifier(), search_params, cv=5, verbose=2, n_jobs=-1)
# 学習用データを適用
gs.fit(X_train,y_train)
# 最適モデルを取得
best_clf = gs.best_estimator_
# スコアを表示
gs.best_score_

スコアは0.8330709677419355となりました。前回が 0.8406691356474798でしたので、良くなっているのがわかります。

早速これをKaggleにアップロードしてスコアを見てみましょう。

ですが、結果は前回よりも若干悪化してしまいました。

補足:各画像のタイトルで「Validation Curve」とすべきところを「Validationn Curve」としてしまいました。。。

]]>

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.