Baggingとその偽物(?)の効果を調べてみた

  • 0
    いいね
  • 0
    コメント

    経緯

    cross validationをしたときに、作ったモデルがもったいないので、「作ったモデルの結果の平均を最終結果にする」という似非Baggingを時々やっていたものの、ちゃんとBootstrapサンプリングした場合に比べて性能が悪かったりするんだろうか、と不安に思っていたので、ちゃんと調べてみることに。

    お題

    手ごろな大きさと難易度ということで、MNIST。オーバーフィッティングしやすいように訓練データを1万件にして残りを検証データに。

    弱学習器はLRとRFとMLP。

    CVとBaggingで、弱学習器の数は5, 10, 15。(Baggingとしては少な目?)

    結果

    結構ばらついたので、seedを変えつつ3回やってみました。

    val_acc(1回目) val_acc(2回目) val_acc(3回目) val_acc(平均)
    lr-base 85.2 85.4 85.2 85.3
    lr-cv5 87.5 86.8 87.3 87.2
    lr-cv10 87.1 86.9 87.2 87.1
    lr-cv15 86.8 86.6 86.9 86.8
    lr-bag5 87.7 87.2 87.5 87.5
    lr-bag10 87.9 87.6 88.3 87.9
    lr-bag15 88.0 88.1 88.5 88.2
    val_acc(1回目) val_acc(2回目) val_acc(3回目) val_acc(平均)
    rf-base 95.0 94.9 94.9 94.9
    rf-cv5 95.1 94.9 94.9 95.0
    rf-cv10 95.3 95.1 95.1 95.2
    rf-cv15 95.4 95.2 95.2 95.2
    rf-bag5 94.7 94.6 94.6 94.6
    rf-bag10 94.8 94.6 94.7 94.7
    rf-bag15 94.9 94.7 94.7 94.7
    val_acc(1回目) val_acc(2回目) val_acc(3回目) val_acc(平均)
    nn-base 93.9 93.2 93.6 93.6
    nn-cv5 95.3 95.5 95.2 95.3
    nn-cv10 95.8 95.9 95.7 95.8
    nn-cv15 96.0 96.1 96.0 96.0
    nn-bag5 95.1 95.0 95.1 95.1
    nn-bag10 95.3 95.5 95.3 95.4
    nn-bag15 95.4 95.6 95.4 95.5

    結論

    • LRはBaggingの方が良さそう?
    • RFを更に(少数で)Baggingするのは逆効果っぽい?
    • MLPはCVの方が良さそう?

    だいぶ僅差なので細かいところはよく分からないけど、とりあえず「CVの結果の平均」はそう悪くなさそう。

    ソースコード

    """
    アンサンブルのお試しコード
    """
    
    import numpy as np
    import sklearn.base
    import sklearn.ensemble
    import sklearn.metrics
    import sklearn.model_selection
    import sklearn.linear_model
    import sklearn.neural_network
    import sklearn.datasets
    
    
    class CVAggregatingClassifier:
    
        def __init__(self, base_estimator, n_fold):
            self.base_estimator = base_estimator
            self.n_fold = n_fold
            self.estimators_ = []
            self.cv_score_ = 0
    
        def fit(self, X, y):
            self.estimators_ = []
            score_list = []
            count_list = []
            skf = sklearn.model_selection.StratifiedKFold(n_splits=self.n_fold, shuffle=True)
            for train, test in skf.split(X, y):
                estimator = sklearn.base.clone(self.base_estimator)
                estimator.fit(X[train], y[train])
                pred = estimator.predict(X[test])
                score = sklearn.metrics.accuracy_score(y[test], pred)
                score_list.append(score)
                count_list.append(len(y[test]))
                self.estimators_.append(estimator)
            self.cv_score_ = np.average(score_list, weights=count_list)
    
        def predict(self, X):
            return np.argmax(self.predict_proba(X), axis=-1)
    
        def predict_proba(self, X):
            return np.mean([e.predict_proba(X) for e in self.estimators_], axis=0)
    
    
    if __name__ == '__main__':
        np.random.seed(3456)
    
        mnist = sklearn.datasets.fetch_mldata('MNIST original')
        X, y = mnist.data, mnist.target
        X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size=60000.0 / len(y))
        print('train size={} test size={}'.format(len(X_train), len(X_test)))
    
        factories = [
            ('lr', lambda: sklearn.linear_model.LogisticRegression(n_jobs=-1)),
            ('rf', lambda: sklearn.ensemble.RandomForestClassifier(n_estimators=100, n_jobs=-1)),
            ('nn', lambda: sklearn.neural_network.MLPClassifier(hidden_layer_sizes=(1000, 1000))),
        ]
        for base_name, factory in factories:
            estimators = [
                ('base', factory()),
                ('cv5', CVAggregatingClassifier(factory(), n_fold=5)),
                ('cv10', CVAggregatingClassifier(factory(), n_fold=10)),
                ('cv15', CVAggregatingClassifier(factory(), n_fold=15)),
                ('bag5', sklearn.ensemble.BaggingClassifier(factory(), n_estimators=5, oob_score=True)),
                ('bag10', sklearn.ensemble.BaggingClassifier(factory(), n_estimators=10, oob_score=True)),
                ('bag15', sklearn.ensemble.BaggingClassifier(factory(), n_estimators=15, oob_score=True)),
            ]
            for name, estimator in estimators:
                estimator.fit(X_train, y_train)
                y_pred = estimator.predict(X_test)
                acc = sklearn.metrics.accuracy_score(y_test, y_pred)
    
                if hasattr(estimator, 'oob_score_'):
                    print('{}-{:5s}: val_acc={:.2f} oob_score={:.2f}'.format(base_name, name, acc * 100, estimator.oob_score_ * 100))
                elif hasattr(estimator, 'cv_score_'):
                    print('{}-{:5s}: val_acc={:.2f} cv_score={:.2f}'.format(base_name, name, acc * 100, estimator.cv_score_ * 100))
                else:
                    print('{}-{:5s}: val_acc={:.2f}'.format(base_name, name, acc * 100))