静かなる名辞

pythonとプログラミングのこと

2019/03/22:TechAcademyがteratailの質問・回答を盗用していた件
2019/03/26:TechAcademy盗用事件 公式発表と深まる疑念



AdaBoostとRandomForestの比較

はじめに

 個人的にAdaBoostの性質がまだよくわかっていないので、比較を行ってみようと思います。

参考文献

 大元はsklearnのこの記事です。

scikit-learn.org

 また、コードを1から書き上げるほどの情熱が今回沸かなかったので、自分の過去記事からコピペして書いています。

www.haya-programming.com

www.haya-programming.com

 AdaBoostのパラメータの決め方については、こちらの記事を御覧ください。

www.haya-programming.com

実験

 適当な2次元データに対して、分類境界の可視化を試みる。

 AdaBoostはベース分類器に決定木を使い、木の最大深さ5と10で試しています。

 コード

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

from sklearn.datasets import make_moons, make_circles, make_classification
from sklearn.ensemble import AdaBoostClassifier, RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier

def main():
    ada5 = AdaBoostClassifier(
        base_estimator=DecisionTreeClassifier(max_depth=5),
        n_estimators=100)
    ada10 = AdaBoostClassifier(
        base_estimator=DecisionTreeClassifier(max_depth=10),
        n_estimators=100)

    rfc = RandomForestClassifier(n_estimators=100)

    X, y = make_classification(
        n_features=2, n_redundant=0, n_informative=2,
        random_state=1, n_clusters_per_class=1)
    rng = np.random.RandomState(2)
    X += 2 * rng.uniform(size=X.shape)
    linearly_separable = (X, y)

    datasets = [make_moons(noise=0.3, random_state=0),
                make_circles(noise=0.2, factor=0.5, random_state=1),
                linearly_separable
            ]

    fig, axes = plt.subplots(nrows=3, ncols=3, figsize=(10,10))

    cm = plt.cm.RdBu
    cm_bright = ListedColormap(['#FF0000', '#0000FF'])
    for i, (X, y) in enumerate(datasets):
        x_min, x_max = X[:, 0].min()-0.5, X[:, 0].max()+0.5
        y_min, y_max = X[:, 1].min()-0.5, X[:, 1].max()+0.5

        xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),
                             np.arange(y_min, y_max, 0.1))

        for j, (cname, clf) in enumerate([("ADA d=5", ada5),
                                          ("ADA d=10", ada10),
                                          ("RFC", rfc)]):
            clf.fit(X, y)
            if hasattr(clf, "decision_function"):
                Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
            else:
                Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
            Z = Z.reshape(xx.shape)
            axes[j,i].contourf(xx, yy, Z, 20, cmap=cm, alpha=.8)
            axes[j,i].scatter(X[:,0], X[:,1], c=y, s=20,
                              cmap=cm_bright, edgecolors="black")
            
            axes[j,i].set_title(cname)
    plt.savefig("result.png", bbox_inches="tight")

if __name__ == "__main__":
    main()

結果

 こんなものになります。

result.png
result.png

 AdaBoostそのものには過学習を抑制するような仕組みはないので、ベース分類器の汎化性能がなければほぼ決定木になることがわかります。バギング系の手法とは違います。

 最大深さ5では概ねまともですが、これでもよく見るとランダムフォレストより過学習の傾向を示しています。変なところにあるデータに引きずられる訳です。そうすると、決定木をもっと浅くするとどうなる? という疑問もありますが、見るに堪えない結果になるので、ある程度の識別性能のある分類器を使ったほうが良いです(見たい方は各自書き換えて実行してください)。

結論

 気軽に使うというわけにはいかないけど、アルゴリズムの原理的にはパラメータチューニングするとランダムフォレストより良くなる可能性を秘めているようです。

 それって要するに玄人向けなんじゃ・・・かといって絶対性能で最強でもないんですが。

 とはいえ、ベース分類器の過学習さえ抑制しておけばそこそこランダムフォレストと同等に使えそうなので、軽めのアンサンブル手法として便利ではあります。