複数の分類器に分類を行わせ、その結果を平均した結果を得ればより正しい結果が得られるだろう・・・ということらしい。
sklearn.ensemble.VotingClassifier — scikit-learn 0.20.1 documentation
先に結論を書いておくと、何種類かの分類器を入れてsklearnのdigitsデータで素直に試したところ、パラメタを適当に設定したSVMに勝てなかった。理由としては、
- SVMで素直に分類すれば0.97とかのf1値が得られるデータだった
- こうなるとかえって悪い奴が足を引っ張る
これに尽きる。かといって効果なしというのも悲しいので、無理矢理効果が見られるようなシチュエーションを作ることにした。
基本的には汎化性能に効いてくるはずなので、汎化性能が必要なようにデータを改造する。
digits = load_digits()
noised_data = digits.data + np.random.random(digits.data.shape)*15
データ全体にノイズをまぶし、
X_train, X_test, y_train, y_test = train_test_split(
noised_data, digits.target, test_size=0.8)
全データの8割をテストに使う。2割しか学習に回されないので、少ないデータから良さげな分離平面を引くチカラが要求されるという訳。
そしていろいろな分類器を突っ込んで実験する。
# coding: UTF-8 import numpy as np from sklearn.svm import SVC from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier as KNN from sklearn.naive_bayes import GaussianNB as GNB from sklearn.ensemble import RandomForestClassifier as RFC from sklearn.ensemble import BaggingClassifier from sklearn.neural_network import MLPClassifier from sklearn.ensemble import VotingClassifier from sklearn.datasets import load_digits from sklearn.model_selection import train_test_split from sklearn.metrics import precision_recall_fscore_support def main(): digits = load_digits() noised_data = digits.data + np.random.random(digits.data.shape)*15 X_train, X_test, y_train, y_test = train_test_split( noised_data, digits.target, test_size=0.8) svm =SVC(C=5, gamma=0.001, probability=True) lr = LogisticRegression() knn = KNN(n_jobs=-1) nb = GNB() rfc = RFC(n_estimators=500, n_jobs=-1) bgg = BaggingClassifier(n_estimators=300, n_jobs=-1) mlp = MLPClassifier(hidden_layer_sizes=(40, 20), max_iter=1000) estimators = list(zip(["svm","lr","knn","nb","rfc","bgg","mlp"], [svm,lr,knn,nb,rfc,bgg,mlp])) for name, clf in estimators: clf.fit(X_train, y_train) preds = clf.predict(X_test) print(name) print("p:{0:.4f} r:{1:.4f} f1:{2:.4f}".format( *precision_recall_fscore_support(y_test, preds, average="macro"))) for v in ["hard", "soft"]: vc_hard = VotingClassifier(estimators, voting=v) vc_hard.fit(X_train, y_train) preds = vc_hard.predict(X_test) print(v, "voting") print("p:{0:.4f} r:{1:.4f} f1:{2:.4f}".format( *precision_recall_fscore_support(y_test, preds, average="macro"))) if __name__ == "__main__": main()
突っ込んだ分類器は、
- svm
soft voting(各分類器の推定確率の平均を取る)を使いたいときはprobability=Trueを設定すること
- ロジスティック回帰
- k近傍
- ナイーブベイズ
- ランダムフォレスト
- バギング
- 多層パーセプトロン
の7種類。デフォルトのパラメタだと悲惨な結果になる奴もいるので、そういう奴には適当なパラメタを与えた。
そして結果は、
svm p:0.8709 r:0.8479 f1:0.8348 lr p:0.7532 r:0.7580 f1:0.7542 knn p:0.8440 r:0.8459 f1:0.8399 nb p:0.8493 r:0.8460 f1:0.8428 rfc p:0.8443 r:0.8354 f1:0.8235 bgg p:0.7971 r:0.7943 f1:0.7879 mlp p:0.6769 r:0.6712 f1:0.6668 hard voting p:0.8649 r:0.8588 f1:0.8505 soft voting p:0.8704 r:0.8718 f1:0.8675
うまい具合に効いた、というかこの結果を得るまでに苦労があった。
まぶすノイズが少ないとSVMやRandomForestが有利だが、まぶすノイズの量を増やすとナイーブベイズが相対的に際立ってくる*1とか新しい知見も得られたが、そのせいでノイズをまぶしすぎるとvoting classifierのスコアがナイーブベイズに負けるとか、色々あった。
ノイズ量と学習に回すデータ数を調整して、なんとかvoting classifierが勝つように調整したというのが実情。実データに対する実用性はあるのか? けっこう微妙かもしれない・・・
*1:正規分布を仮定した確率モデルなのでそうなる理由はなんとなくわかるような気はする