はじめに
sklearnのAdaBoostを使う機会がありましたが、デフォルトパラメータのまま使ってみたら性能が悪すぎて驚きました。
対策を書きます。
症状
とりあえずデフォルトパラメータで動かしてみて、様子を見るというシチュエーションはたくさんあると思います。しかし、AdaBoostはいまいちそういう用途には向いていません。
何も考えずに使うとこんな感じです。
from sklearn.datasets import load_digits from sklearn.ensemble import AdaBoostClassifier from sklearn.metrics import classification_report from sklearn.model_selection import train_test_split def main(): digits = load_digits() ada = AdaBoostClassifier() X_train, X_test, y_train, y_test = train_test_split( digits.data, digits.target, stratify=digits.target) ada.fit(X_train, y_train) pred = ada.predict(X_test) print(classification_report(y_test, pred)) if __name__ == "__main__": main() """ => UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. 'precision', 'predicted', average, warn_for) precision recall f1-score support 0 0.92 0.98 0.95 45 1 0.13 1.00 0.23 46 2 0.00 0.00 0.00 44 3 0.00 0.00 0.00 46 4 0.00 0.00 0.00 45 5 0.00 0.00 0.00 46 6 0.59 0.44 0.51 45 7 0.00 0.00 0.00 45 8 0.00 0.00 0.00 43 9 0.50 0.24 0.33 45 micro avg 0.27 0.27 0.27 450 macro avg 0.21 0.27 0.20 450 weighted avg 0.21 0.27 0.20 450 """
さすがにひどい。どうしてなんでしょうね。
原因と対処
ドキュメントをしっかり読んでみます。するとここが気になります。
base_estimator : object, optional (default=None)
The base estimator from which the boosted ensemble is built. Support for sample weighting is required, as well as proper classes_ and n_classes_ attributes. If None, then the base estimator is DecisionTreeClassifier(max_depth=1)sklearn.ensemble.AdaBoostClassifier — scikit-learn 0.21.2 documentation
深さ1の決定木はさすがに駄目なことが予想されます。特徴量を1つ取って真ん中で分けるだけか……
ベース分類器をもっと優秀なものにしてあげます。とりあえず、max_depth=Noneにした(伸び放題の)決定木。
from sklearn.datasets import load_digits from sklearn.ensemble import AdaBoostClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.metrics import classification_report from sklearn.model_selection import train_test_split def main(): digits = load_digits() ada = AdaBoostClassifier(DecisionTreeClassifier(max_depth=None)) X_train, X_test, y_train, y_test = train_test_split( digits.data, digits.target, stratify=digits.target) ada.fit(X_train, y_train) pred = ada.predict(X_test) print(classification_report(y_test, pred)) if __name__ == "__main__": main() """ => precision recall f1-score support 0 1.00 0.96 0.98 45 1 0.73 0.89 0.80 46 2 0.92 0.82 0.87 44 3 0.89 0.85 0.87 46 4 0.85 0.91 0.88 45 5 1.00 0.85 0.92 46 6 0.98 0.93 0.95 45 7 0.87 0.89 0.88 45 8 0.72 0.79 0.76 43 9 0.82 0.82 0.82 45 micro avg 0.87 0.87 0.87 450 macro avg 0.88 0.87 0.87 450 weighted avg 0.88 0.87 0.87 450 """
大幅に改善。要するに、性能を上げたければベースを良くする必要があるということですね。
わかったら、あとはパラメータチューニングして追い込みます。
from sklearn.datasets import load_digits from sklearn.ensemble import AdaBoostClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.metrics import classification_report from sklearn.model_selection import GridSearchCV, train_test_split def main(): digits = load_digits() ada = AdaBoostClassifier(n_estimators=100) # ついでに増やす params = {"base_estimator" : [DecisionTreeClassifier(max_depth=x) for x in range(5, 10)], "learning_rate" : [0.5, 1.0, 1.5] } cv = GridSearchCV(ada, params, cv=3, n_jobs=-1, verbose=2) X_train, X_test, y_train, y_test = train_test_split( digits.data, digits.target, stratify=digits.target) cv.fit(X_train, y_train) print(cv.best_params_) pred = cv.predict(X_test) print(classification_report(y_test, pred)) if __name__ == "__main__": main() """ => {'learning_rate': 1.0, 'base_estimator': DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=8, max_features=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, presort=False, random_state=None, splitter='best')} precision recall f1-score support 0 1.00 1.00 1.00 45 1 0.98 1.00 0.99 46 2 1.00 0.98 0.99 44 3 1.00 0.96 0.98 46 4 1.00 1.00 1.00 45 5 0.98 0.91 0.94 46 6 1.00 0.98 0.99 45 7 0.98 0.98 0.98 45 8 0.91 1.00 0.96 43 9 0.91 0.96 0.93 45 micro avg 0.98 0.98 0.98 450 macro avg 0.98 0.98 0.98 450 weighted avg 0.98 0.98 0.98 450 """
最大深さ8の決定木で、データの限界に近いであろう数字が出ました。これで満足しておきます。AdaBoostやればできる子ですね。
まとめ
base_estimatorをいじって多少パラメータチューニングすれば性能が出るので、デフォルトのまま使って使えない子だと判断しないように。アダブーストは十分強いです。