PythonでXGBoostをちゃんと理解する(3) hyperoptでパラメーターチューニング
xgboostのハイパーパラメーターを調整するのに、何が良さ気かって調べると、結局「hyperopt」に落ち着きそう。
対抗馬はSpearmintになりそうだけど、遅いだとか、他のXGBoost以外のモデルで上手く調整できなかった例があるとかって情報もあって、時間の無い今はイマイチ踏み込む勇気はない。
Hyperparameter Optimization using Hyperopt - Otto Group Product Classification Challenge | Kaggle
Optimizing hyperparams with hyperopt - FastML
前回辺りにアルゴリズム振り返って、チューニングには特別気をつけなきゃいけないことも無さそうなので、ガリガリとコード書いて動かしてみます。
hyperoptはつまるところ最適化問題のソルバーで、目的関数を一定の条件下で最大or最小化するというもの。
もともとはベイズ的最適化のためにデザインされてたけど、今はランダムサーチとTree of Parzen Estimators (TPE)だけがインプリされてる状況。
hyperopt本体の挙動を視覚的に理解するには、下記の記事がオススメ。districtdatalabs.silvrback.com
で、id:puyokwさんも記事の中で挙げてらっしゃったけど、KaggleのOttoのコンペにXGBoostにhyperoptを使ったコードが公開されてました。
xgboost package のR とpython の違い - puyokwの日記
optimizing hyperparameters of an xgboost model on otto dataset · bamine/Kaggle-stuff@17f7828 · GitHub
で、もうやりたかったことこれだよね、ってpuyokwさんと丸かぶりなアイデアだったので参考と言うなのほぼ写経。ありがとうございます。
元のコードに交差検定加えたいなと思ったけど、xgb.CVは使い勝手悪いし、sklearnでやってます。
import numpy as np import xgboost as xgb import pandas as pd from sklearn import datasets, cross_validation from sklearn.cross_validation import train_test_split from sklearn.metrics import confusion_matrix, log_loss from sklearn.cross_validation import KFold from sklearn import preprocessing from hyperopt import fmin, tpe, hp, STATUS_OK, Trials iris=datasets.load_iris() X = iris.data y = iris.target #Split data set to train and test data X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.5, random_state=25) np.random.seed(25) def score(params): print "Training with params : " print params N_boost_round=[] Score=[] skf = cross_validation.StratifiedKFold(y_train, n_folds=10, shuffle=True,random_state=25) for train, test in skf: X_Train, X_Test, y_Train, y_Test = X_train[train], X_train[test], y_train[train], y_train[test] dtrain = xgb.DMatrix(X_Train, label=y_Train) dvalid = xgb.DMatrix(X_Test, label=y_Test) watchlist = [(dtrain, 'train'),(dvalid, 'eval')] model = xgb.train(params, dtrain, num_boost_round=150,evals=watchlist,early_stopping_rounds=10) predictions = model.predict(dvalid) N = model.best_iteration N_boost_round.append(N) score = model.best_score Score.append(score) Average_best_num_boost_round = np.average(N_boost_round) Average_best_score = np.average(Score) print "\tAverage of best iteration {0}\n".format(Average_best_num_boost_round) print "\tScore {0}\n\n".format(Average_best_score) return {'loss': Average_best_score, 'status': STATUS_OK} def optimize(trials): space = { "objective": "multi:softprob", "eval_metric": "mlogloss", #Control complexity of model "eta" : hp.quniform("eta", 0.2, 0.6, 0.05), "max_depth" : hp.quniform("max_depth", 1, 10, 1), "min_child_weight" : hp.quniform('min_child_weight', 1, 10, 1), 'gamma' : hp.quniform('gamma', 0, 1, 0.05), #Improve noise robustness "subsample" : hp.quniform('subsample', 0.5, 1, 0.05), "colsample_bytree" : hp.quniform('colsample_bytree', 0.5, 1, 0.05), 'num_class' : 3, 'silent' : 1} best = fmin(score, space, algo=tpe.suggest, trials=trials, max_evals=250) print "best parameters",best trials = Trials() optimize(trials) #出力 #best parameters {'colsample_bytree': 0.9500000000000001, 'min_child_weight': 1.0, #'subsample':0.9500000000000001, 'eta': 0.6000000000000001, 'max_depth': 3.0, 'gamma': 0.0} #Adapt best params params={'objective': 'multi:softprob', 'eval_metric': 'mlogloss', 'colsample_bytree': 0.9500000000000001, 'min_child_weight': 1.0, 'subsample': 0.9500000000000001, 'eta': 0.6000000000000001, 'max_depth': 3.0, 'gamma': 0.0, 'num_class': 3 } score(params) #出力 #Training with params : #{'subsample': 0.9500000000000001, 'eta': 0.6000000000000001, 'colsample_bytree': 0.9500000000000001, 'gamma': #0.0, 'eval_metric': 'mlogloss', 'objective': 'multi:softprob', 'num_class': 3, 'max_depth': 3.0, #'min_child_weight': 1.0} # Average of best iteration 37.7 # Score 0.1092628 X_train = pd.DataFrame(X_train) X_test = pd.DataFrame(X_test) dtrain = xgb.DMatrix(X_train.as_matrix(),label=y_train.tolist()) dtest=xgb.DMatrix(X_test.as_matrix()) bst=xgb.train(params,dtrain,num_boost_round=38) pred=bst.predict(dtest) pred=pd.DataFrame(pred) print confusion_matrix(y_test, pred.idxmax(axis=1)) #出力:Confusion_matrix #[[26 0 0] # [ 0 24 2] # [ 0 2 21]]
ちなみに、パラメーターを全部デフォルトにした素うどんXGBoostでやってみても、結果は一緒でした。
あやめのデータだとこんなもんですかね。
params_Default={'objective': 'multi:softprob', 'eval_metric': 'mlogloss', 'eta': 0.3, 'max_depth': 6, 'min_child_weight': 1, 'subsample': 1, 'colsample_bytree': 1, 'num_class': 3, } cv=xgb.cv(params_Default,dtrain,num_boost_round=200,nfold=10) print(cv) bst=xgb.train(params_Default,dtrain,num_boost_round=13) pred=bst.predict(dtest) pred=pd.DataFrame(pred) print confusion_matrix(y_test, pred.idxmax(axis=1)) #出力:Confusion_matrix #[[26 0 0] # [ 0 24 2] # [ 0 2 21]]
最近、やりたいことはすでにほかの人がやっちゃってるパターン多くて恥ずかしい限りですね。
本当はこの後、xgboostで得た情報をどう特徴選択などなどでどう使うかまでやろうかと思ったけど、そのネタはアンサンブルとして扱った方が良いのかなと思いxgboostはとりあえず一旦終了。
次は、Spermintとhyperoptの比較あたりやってみようかな。
ChainerとかLasagneとかのニューラルネット周りもやりたいけど。