スタッキングライブラリvecstackの使い方

この記事ではスタッキングについてvecstackの使い方をrisを例として説明します。

スタッキングとは

スタッキングは異なる予測器を1層2層…と重ねていき予測の精度を高めるアンサンブル手法です。下の層の予測を上の層の特徴量として入力します。

1層目以降に設定された予測器が予測を出力し、その結果を上の層に入力します。下の層からの入力を受け取ったら正解データと照らし合わせてどの分類機が信用できるかを上の層が判断して結果をまた上の層に入力します。これを繰り返して一番上にある予測器が最終的な予測を出力します。

vecstack作者によるスタッキングの解説(Stacking understanding. Python package for stacking)が分かりやすそうです。

vecstackを使ったスタッキング

vecstackはスタッキングを実現するためのライブラリです。

veckstackの特徴は

  • 実装が楽
  • 多層のスタックが簡単に構築できる
  • scikit-learn APIにも対応している
  • サンプルが分かりやすい

です。

Function API

以下のサンプルではirisデータセットの分類問題を例にvecstackを使います。

1層目にKNeighborsとLightGBMのモデルをセットして最終層にはXGBoostを設定しています。

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

from lightgbm import LGBMClassifier
from sklearn.neighbors import KNeighborsClassifier
from xgboost import XGBClassifier

from vecstack import stacking

iris = load_iris()
df = pd.DataFrame(data= np.c_[iris['data'], iris['target']],
        columns= iris['feature_names'] + ['target'])

X = df.loc[:,["sepal width (cm)","petal width (cm)","petal length (cm)"]]
y = df.loc[:,"target"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, shuffle=0.3, random_state=0)

models = [
        KNeighborsClassifier(),
        LGBMClassifier()
        ]

S_train, S_test = stacking(models,
        X_train, y_train, X_test,
        regression=False, # 分類問題(classifier)なのでFalse
        mode='oof_pred_bag',
        needs_proba=False,
        save_dir=None,
        metric=accuracy_score,
        n_folds=4,
        stratified=True,
        shuffle=True,
        random_state=0,
        verbose=2)

model = XGBClassifier(random_state=0, n_jobs=-1, learning_rate=0.1, n_estimators=100, max_depth=3)

model = model.fit(S_train, y_train)
y_pred = model.predict(S_test)
print('accuracy: ', accuracy_score(y_test, y_pred))

これを実行すると以下のような出力が得られます。

$ python stacking.py
task:         [classification]
n_classes:    [3]
metric:       [accuracy_score]
mode:         [oof_pred_bag]
n_models:     [2]

model  0:     [KNeighborsClassifier]
    fold  0:  [0.96296296]
    fold  1:  [0.88461538]
    fold  2:  [1.00000000]
    fold  3:  [1.00000000]
    ----
    MEAN:     [0.96189459] + [0.04710961]
    FULL:     [0.96190476]

model  1:     [LGBMClassifier]
    fold  0:  [0.96296296]
    fold  1:  [0.84615385]
    fold  2:  [0.96153846]
    fold  3:  [1.00000000]
    ----
    MEAN:     [0.94266382] + [0.05781418]
    FULL:     [0.94285714]

accuracy:  0.9777777777777777

scikit-learn API

veckstackはscikit-learn APIも提供しています。以下に実装例を示します。

StackingTransformersklearnのPipelineに入れる形式に分類器を変換するものです。

また、StackingTransformer中のvariableは、GitHubのREADMEによると’A’にすると結果を維持したまま時間が短くなるそうです。

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

from lightgbm import LGBMClassifier
from sklearn.neighbors import KNeighborsClassifier
from xgboost import XGBClassifier

from sklearn.pipeline import Pipeline
from vecstack import StackingTransformer

iris = load_iris()
df = pd.DataFrame(data= np.c_[iris['data'], iris['target']],
        columns= iris['feature_names'] + ['target'])

X = df.loc[:,["sepal width (cm)","petal width (cm)","petal length (cm)"]]
y = df.loc[:,"target"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, shuffle=0.3, random_state=0)

estimators_L1 = [
        ('kn', KNeighborsClassifier()),
        ('lgbm', LGBMClassifier()),
        ]

stack = StackingTransformer(estimators=estimators_L1,
                            regression=False,
                            variant='A', # https://github.com/vecxoz/vecstack#24-which-stacking-variant-should-i-use-a-oof_pred_bag-or-b-oof_pred
                            metric=accuracy_score,
                            n_folds=4,
                            shuffle=True,
                            random_state=0,
                            verbose=2)

final_estimator = XGBClassifier(random_state=0, n_jobs=-1, learning_rate=0.1)
steps = [('stack', stack),
         ('final_estimator', final_estimator)]

pipe = Pipeline(steps)
pipe = pipe.fit(X_train, y_train)

y_pred_pipe = pipe.predict(X_test)
print('accuracy: ', accuracy_score(y_test, y_pred_pipe))

実行すると以下のように出力されます。

$ python stacking.py
task:         [classification]
n_classes:    [3]
metric:       [accuracy_score]
variant:      [A]
n_estimators: [2]

estimator  0: [kn: KNeighborsClassifier]
    fold  0:  [0.96296296]
    fold  1:  [1.00000000]
    fold  2:  [0.96153846]
    fold  3:  [0.92307692]
    ----
    MEAN:     [0.96189459] + [0.02720341]

(中略)

estimator  1: [lgbm: LGBMClassifier]
    model from fold  0: done
    model from fold  1: done
    model from fold  2: done
    model from fold  3: done
    ----
    DONE

accuracy:  0.9777777777777777

scikit-learn APIは公式サンプルを参考にしました。

まとめ

vecstackを使うと多層のスタッキングを楽に構築することができることが分かりました。実際に使うときはまずvecstackのGitHubのREADMEを読むといいと思います。Stacking FAQの欄にスタッキングに関する1問1答が30項目載っていてスタッキングをするにあたって非常に良い資料となっています。

参考