LoginSignup
0
2

More than 5 years have passed since last update.

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

Posted at

経緯

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))
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2