スタッキングで分類・回帰 (scikit-learn)

複数の機械学習モデルを組み合わせる方法の一つとしてスタッキングがありますが、Python の scikit-learnのStackingClassifierとStackingRegressorを使ってみました。

# StackingClassifier

## スタッキングによる分類

分類モデルの性能を確認するため、乳がんデータを使ってみます。

```Python
from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)
```

```Python
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y)
```

ろくなパラメータチューニングもしてないので改良の余地はありますが、とりあえず

```Python
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.ensemble import StackingClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.linear_model import LogisticRegression

estimators = [
        ('svc', make_pipeline(StandardScaler(), SVC())),
        ('rf', RandomForestClassifier()),
        ('mlp', MLPClassifier(max_iter=10000))
        ]
clf = StackingClassifier(
    estimators=estimators,
    final_estimator=LogisticRegression(max_iter=10000)
)
clf.fit(X_train, y_train)
clf.score(X_test, y_test)
```

```
0.972027972027972
```

## 単独の分類モデルの性能

比較として、単独の分類モデルの正解率を計算してみます。

```Python
make_pipeline(StandardScaler(), SVC()).fit(X_train, y_train).score(X_test, y_test)
```

```
0.965034965034965
```

```Python
RandomForestClassifier().fit(X_train, y_train).score(X_test, y_test)
```

```
0.951048951048951
```

```Python
MLPClassifier(max_iter=10000).fit(X_train, y_train).score(X_test, y_test)
```

```
0.9090909090909091
```

```Python
LogisticRegression(max_iter=10000).fit(X_train, y_train).score(X_test, y_test)
```

```
0.958041958041958
```

単独で使うよりも、組み合わせた方が良いという結果になりました。

ですが、train_test_split から計算し直すと、分割のされ方によっては単独の分類モデルのほうが性能が良くなったりします。

性能比較には、ランダムシードを固定せずに、何度も計算を繰り返して、その性能がどのくらい安定なのかを確認した方がいいと思っています。

# StackingRegressor

## スタッキングによる回帰

回帰モデルの性能を確認するため、糖尿病データを使ってみます。

```Python
from sklearn.datasets import load_diabetes
X, y = load_diabetes(return_X_y=True)
```

```Python
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y)
```

こちらも、ろくなパラメータチューニングしてないので改良の余地はありますが、

```Python
from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVR
from sklearn.ensemble import StackingRegressor
from sklearn.neural_network import MLPRegressor
from sklearn.cross_decomposition import PLSRegression

estimators = [
        ('svr', make_pipeline(StandardScaler(), SVR())),
        ('rf', RandomForestRegressor()),
        ('mlp', MLPRegressor(max_iter=10000))
        ]
clf = StackingRegressor(
    estimators=estimators,
    final_estimator=PLSRegression(),
)
clf.fit(X_train, y_train)
clf.score(X_test, y_test)
```

```
0.4940607294168183
```

## 単独の回帰モデルの性能


比較として、単独の回帰モデルののR2値を計算してみます。

```Python
make_pipeline(StandardScaler(), SVR()).fit(X_train, y_train).score(X_test, y_test)
```

```
0.17571936903725216
```

```Python
RandomForestRegressor().fit(X_train, y_train).score(X_test, y_test)
```

```
0.46261715392586217
```

```Python
MLPRegressor(max_iter=10000).fit(X_train, y_train).score(X_test, y_test)
```

```
0.4936782755875562
```

```Python
PLSRegression().fit(X_train, y_train).score(X_test, y_test)
```

```
0.4927059150604132
```

こちらも、単独で使うよりも、組み合わせた方が良いという結果になりました。

ですが、train_test_split から計算し直すと、分割のされ方によっては単独の回帰モデルのほうが性能が良くなったりします。

性能比較には、ランダムシードを固定せずに、何度も計算を繰り返して、その性能がどのくらい安定なのかを確認した方がいいと思っています。