scikit-learnモデルのVotingとキャッシング
先月末まで、Shelter Animal Outcomes | Kaggleに参加していました。
同僚の力も借りつつ、なんとかTOP2%に入り込む結果になりました。
トップはほぼパーフェクトに近いスコアの方もいたり、leak多すぎて実態がよくわからん、という印象。
データの前処理はフォーラムを見つつテキトーに、
最終的にモデルはXGB、Random Forest、Extreme randomized treesの三つのモデルのVoting ensembleを行いました。
ところで、コンペ終盤では決定木の本数を増やしたり、
重みを調節したりしてるうちに
計算量が多くなりやってられなくなりました。
そこで、自作のキャッシュシステム(https://github.com/fukatani/stacked_generalization)を使い、一度学習と予測を行った各モデルはキャッシュして、
何度アンサンブルに使っても、個別のモデルの学習と予測での再計算が発生しないようにしました。
使用例はこうなります。
from sklearn import datasets from sklearn.cross_validation import StratifiedKFold from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier from stacked_generalization.lib.joblibed import JoblibedClassifier # irisデータのロード iris = datasets.load_iris()
# trainデータとtestデータの分割 train_idx, test_idx = list(StratifiedKFold(iris.target, 3))[0]
xs_train = iris.data[train_idx]
y_train = iris.target[train_idx]
xs_test = iris.data[test_idx]
y_test = iris.target[test_idx]
# キャッシュの対象となる二つのモデルの宣言 rf = JoblibedClassifier(RandomForestClassifier(n_estimators=40), "rf")
et = JoblibedClassifier(ExtraTreesClassifier(n_estimators=40), "et")
# 重みを変えた二つのVoting型アンサンブルの宣言
v_clf1 = VotingClassfier([rf, eta], voting="soft", [1.0, 1.0])
v_clf2 = VotingClassfier([rf, eta], voting="soft", [2.0, 1.0])
# 一回目の計算。fittingとpredictionに時間かかる。
v_clf1.fit(xs_train, y_train, train_idx)
score = v_clf1.score(xs_test, y_test, test_idx)
# 二回目の計算。
# 各モデルのfittingとpredictionは計算がスキップされ、結果のみロード
v_clf2.fit(xs_train, y_train, train_idx)
score = v_clf2.score(xs_test, y_test, test_idx)
Votingの場合、実質的に二回目以降の計算は各モデルの予測結果のロード及び、それらの重みとの線形結合程度の計算時間しかからず、数秒で終わります。
n_estimatorsを1000とかに増やすと、手元環境だと十分程度時間がかかっていたのですが、これにより何十回と重みを変えてもすぐに結果が返ってくるようになり、細かい単位でgridsearch、hyperoptすることができました。
結果、我々のShelter Animal Outcomesの最終提出は
XGB: 6.0
RandomForest: 3.0
Extreme randomized trees: 1.0
となりました。
ちなみに
VotingClaasifierにはソフトとハードがあります。
デフォルトはハードで、voting='soft'とすると、ソフトが使えます。
sklearn.ensemble.VotingClassifier — scikit-learn 0.17.1 documentation
なにが違うのかと言うと、ハードでは各モデルにもっとも有り得そうなクラスを投票させて多数決を行うのに対し、ソフトでは、各モデルの予測確率の平均を取ります。
例えばクラス0とクラス1に分類する分類問題において、あるサンプルに対しモデルAが60%、モデルBが55%、モデルCが0%の確率でクラス0に所属すると判定したとします。
すると、ハード予測の場合はモデルAとモデルBではクラス0と予測しているので、多数派のクラス0が予測結果になります。
一方ソフト予測の場合はクラス0に所属する確率は(60+55+0)/3 = 38.3%、クラス1に所属する確率は71.6%と計算され、クラス1が予測結果になります。
また、ハードではデータに対してもっとも有り得そうなクラスを判定することはできますが、各クラスに所属する確率を算出することができない、という違いがあります。
分類問題のコンペでは予測クラスを提出して正解率を競うコンペと、
所属確率を提出して、ロジスティック損失を競うコンペがあります。
例えば、Shelter Animal Outcomesは後者でした。
ロジスティック損失を競うコンペでは予測確率を提出するので、ソフト予測を行うよう、voiting='soft'とすべきです。
例えば、ハード予測において所属すると判定されたクラスの所属確率を1にして、残りを0にするなどして結果を予測確率に変換することはできますが、いい成績を出すのはツライ…と思います。
ハードにしろソフトにしろ、予測を外さなければどうということはないのですが、後者の場合予測に自信がないときには所属確率をさげることによって損失の拡大を抑えることができるのです。
ちなみにちなみに
ロジスティック損失を競うコンペでは「なんでランキング一位の方がスコアが小さいの?」みたいな質問をForumでよく見かけます。
優秀なモデルは正解率が高く、ロジスティック損失が小さいのです。