Kaggle-titanicコンペのデータを用いてRandomForest,SVM,LightGBMを試す
前回のつづきです。
(注)本記事はコンペ目的ではなく,ライブラリの使用感を確かめているところで個人メモの範疇です。
前処理やデータなど
KaggleのTitanicを使っています。 前処理は前記事を参照してください。
RandomForest
例のごとくscikit learnのライブラリを用いていきます。
前回の木構造を複数学習して多数決をとるような手法です。
Boostingなどのキーワードは調べておいても良いでしょう。
コード
デフォルトでは100本の木でアンサンブルしようとするので10本程度に収まるようにさせました。深さ4を上限としています。
import sklearn.tree as tree from sklearn.ensemble import RandomForestClassifier # RandomForest 10個のモデルの重ね合わせ clf = RandomForestClassifier(max_depth=4,random_state=0,n_estimators=10)
ここから交差検証に入ります。5分割くらいでも良かったかも。
# 3分割交差検証を指定し、インスタンス化 from sklearn.model_selection import KFold kf = KFold(n_splits=3, shuffle=True, random_state=0) # スコアとモデルを格納するリスト score_list = [] models = [] # 各分割ごとに評価 for fold_, (train_index, valid_index) in enumerate(kf.split(X_train, y_train)): print(f'fold{fold_ + 1} start') train_x = X_train.iloc[train_index] valid_x = X_train.iloc[valid_index] train_y = y_train.iloc[train_index] valid_y = y_train.iloc[valid_index] ## 分割データで学習・予測・評価 model = clf.fit(train_x, train_y) # データを用いて予測,記録 predicted = model.predict(valid_x) score_list.append(accuracy_score(predicted,valid_y)) models.append(model) print(score_list, '平均score', round(np.mean(score_list), 3))
このように交差検証法で多数決をとることでデータの偏りの影響を低減できます。(ランダムフォレストの多数決とは別の多数決,ややこしいですね。)
もちろん図示も可能ですが,複数の木の多数決構造をとっているので可視化をする時はそのうちの一つに絞ります。
model.estimators_
にリストで入ってるので適当な番号のを覗いてみましょう。
図は前回を参照にしてください。
重要度は全部の木について計算してくれているのかmodelから直接とることができます。
# bar plot fig, ax = plt.subplots() plt.grid() ax.bar(train_x.columns,model.feature_importances_) fig.autofmt_xdate() # make space for and rotate the x-axis tick labels plt.show()
何本アンサンブルすればいいの?
木を増やせば汎化性能は上がりそうですが,計算量の問題もあります。 モデルの複雑性が増すと過学習しないかなという懸念もあります。
結構議論されているようですが,いくつか見て以下の意見を参考にしました。
How to determine the number of trees to be generated in Random Forest algorithm?
10本 RF 交差検証:0.808 本番:0.77990 30本 RF 交差検証:0.805 本番:0.76555 100本 RF 交差検証:0.805 本番:0.78468
確かに,若干は上がるようです。この辺は試行錯誤する必要があるので後で説明するgrid searchをすると良いでしょう。
SVM:サポートベクターマシン
簡単に言うとデータ群を最適に分割する超平面を求める手法です。
www.slideshare.net
- カーネルトリック:平面で分割とは言ってもデータの変数をかさ増しすることで非線形な分割も可能になります。例えば元の変数が
x,y
なら,x^2, xy ,y^2
も変数に加えた空間で分割すれば元の空間では直線ではなくなります。ポイントは内積計算が楽になるようにこのかさ増しを工夫することです。
sklearnではデフォルトでRBFカーネルというものが使われています。
参考:
[python 機械学習初心者向け] scikit-learnでSVMを簡単に実装する - Qiita
カーネル近似(クラス分類)【Pythonとscikit-learnで機械学習:第2回】
コード
from sklearn.svm import SVC clf = SVC(kernel='rbf', C=1, gamma=0.1) # 3分割交差検証を指定し、インスタンス化 from sklearn.model_selection import KFold kf = KFold(n_splits=3, shuffle=True, random_state=0) # スコアとモデルを格納するリスト score_list = [] models = [] # 各分割ごとに評価 for fold_, (train_index, valid_index) in enumerate(kf.split(X_train, y_train)): print(f'fold{fold_ + 1} start') train_x = X_train.iloc[train_index] valid_x = X_train.iloc[valid_index] train_y = y_train.iloc[train_index] valid_y = y_train.iloc[valid_index] ## 分割データで学習・予測・評価 model = clf.fit(train_x, train_y) # データを用いて予測,記録 predicted = model.predict(valid_x) score_list.append(accuracy_score(predicted,valid_y)) models.append(model) print(score_list, '平均score', round(np.mean(score_list), 3))
結果は[0.7104377104377104, 0.6868686868686869, 0.6666666666666666] 平均score 0.688
と少し低めに出てしまいました。
ハイパーパラメータ調整の指針
以下のサイトから引用します。
- γ:「一つの訓練データが与える影響の範囲」を意味します。小さいほど「遠く」、大きいほど「近く」まで影響します。
- C:分類ミスに対するペナルティと分離平面からの距離のトレードオフです。大きいほど誤りに厳しく,データすれすれに線をひきます。したがって小さいほど大雑把でシンプルな分類になりやすいようです。
グリッドサーチ grid search
実際のシチュエーションでは複数のハイパーパラメータを試して最も良さそうな組み合わせをチョイスする必要があります。
for文でも書けますが,sklearnにはgrid_search
という関数があります。
バージョンによって関数名が変わるのですが,Ver1.0では以下のように書きます。
from sklearn.model_selection import GridSearchCV # グリッドサーチを用いたオートチューニング parameters = [{'kernel':['rbf'], 'C':np.logspace(-4, 4, 9), 'gamma':np.logspace(-4, 4, 9)}] # RBF kernel でグリッドサーチ # 交差検定5回でSVCを用いてやる clf = GridSearchCV(cv=5,estimator=SVC(), param_grid=parameters, n_jobs = -1) out = clf.fit(X_train, y_train) print(out.best_estimator_)
このbest estimatorが最も成績の良かった学習器です。
SVC(C=10000.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0, decision_function_shape='ovr', degree=3, gamma=0.0001, kernel='rbf', max_iter=-1, probability=False, random_state=None, shrinking=True, tol=0.001, verbose=False)
このときの精度は0.8417508417508418
と前回より随分高くなりました。
LightGBM
LightGBMは決定木のアルゴリズムを用いた機械学習のフレームワークです。以下のサイトが勉強になります。
コード
使い方はsklearnのものと非常によく似ています。
import lightgbm as lgb # LightGBMの分類器をインスタンス化 gbm = lgb.LGBMClassifier(objective='binary') # trainとvalidを指定し学習 gbm.fit(train_x, train_y, eval_set = [(valid_x, valid_y)], early_stopping_rounds=20, # 20回連続でlossが下がらなかったら終了 verbose=10 # 10round毎に、lossを表示 ) ;
一応推論をしてみると,score 81.48 %
と出ます。
# valid_xについて推論 oof = gbm.predict(valid_x, num_iteration=gbm.best_iteration_) # oofはout of fold print('score', round(accuracy_score(valid_y, oof)*100,2), '%') # 正解率の表示
図示
こちらはデフォルトでそこそこ見やすい図が出ます。
lgb.create_tree_digraph(gbm)
重要度は以下のように簡単にplotできます。
lgb.plot_importance(gbm, figsize=(12, 6))
提出に用いた交差検証 LightGBM
こちらも交差検証を用いて複数のモデルの多数決をとります。
# 5分割交差検証を指定し、インスタンス化 from sklearn.model_selection import KFold kf5 = KFold(n_splits=5, shuffle=True, random_state=0) # スコアとモデルを格納するリスト score_list = [] models = [] test_predicts = [] # 各分割ごとに評価 for fold_, (train_index, valid_index) in enumerate(kf5.split(X_train, y_train)): print(f'fold{fold_ + 1} start') train_x = X_train.iloc[train_index] valid_x = X_train.iloc[valid_index] train_y = y_train.iloc[train_index] valid_y = y_train.iloc[valid_index] ## 分割データで学習・予測・評価 gbm.fit(train_x, train_y, eval_set = [(valid_x, valid_y)], early_stopping_rounds=20, # 20回連続でlossが下がらなかったら終了 verbose=10 # 10round毎に、lossを表示 ) ; # データを用いて予測,記録 test_predicts.append(gbm.predict(test, num_iteration=gbm.best_iteration_) ) predicted = gbm.predict(valid_x, num_iteration=gbm.best_iteration_) score_list.append(accuracy_score(predicted,valid_y)) print(score_list, '平均score', round(np.mean(score_list), 3))
[0.8491620111731844, 0.8089887640449438, 0.8314606741573034, 0.8033707865168539, 0.8089887640449438] 平均score 0.82
コンペ結果
気休めですが,LightBGMが僅差でランダムフォレストを上回っていました。
他にも手法があるので,概略と動かし方をわかっていれば今後の解析の時に役立ちそうですね。
[https://scikit-learn.org/stable/images/sphx_glr_plot_classifier_comparison_001.png:image=https://scikit-learn.org/stable/images/sphx_glr_plot_classifier_comparison_001.png]