AutoML（AutoGluon）とSHAP を組合せて使ってみた

* 製造業出身のデータサイエンティストがお送りする記事
* 今回はAutoML ライブラリー（AutoGluon）とSHAP を組合せてを使ってみました。

##はじめに
過去に他のAutoML ライブラリーやツールについては、別の記事に纏めておりますので下記をご参照ください。

- [PyCaret](https://qiita.com/DS27/items/bc56163b2cc48403deb0)
- [TPOT](https://qiita.com/DS27/items/0311b56972da4bef94d5)
- [VARISTA](https://qiita.com/DS27/items/32349b03e99b3f580569)
- [AutoGluon](https://qiita.com/DS27/items/032e89fe421c74b5027b)
- [Auto-Sklearn](https://qiita.com/DS27/items/08f121929d1c6eb0fcf4)
- [AutoKeras](https://qiita.com/DS27/items/8efcfe8ed132da202cdc)

また、過去にSHAP やLIME の記事は、別の記事に纏めておりますので下記をご参照ください。

- [SHAP](https://qiita.com/DS27/items/9059667acc580ca9bbb7)
- [LIME](https://qiita.com/DS27/items/118732aa6c6009a8496b)


##AutoGluon　とSHAP を組合せて使ってみた

ライブラリーのインストールは下記です（Google Colab 使用前提）。

```php
!pip install --upgrade pip
!pip install --upgrade setuptools
!pip install --upgrade "mxnet<2.0.0"
!pip install --pre autogluon
!pip install shap
```

今回もUCI Machine Learning Repositoryで公開されているボストン住宅の価格データを用いて実施します。

```php
# ライブラリーのインポート
from autogluon.tabular import TabularDataset, TabularPredictor
import pandas as pd
import numpy as np

import seaborn as sns
import matplotlib.pyplot as plt

%matplotlib inline

# ボストンの住宅価格データ
from sklearn.datasets import load_boston

# 前処理
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

import sklearn
import shap
import time

shap.initjs()

import warnings
warnings.filterwarnings('ignore')

# データセットの読込み
boston = load_boston()

# 説明変数の格納
df = pd.DataFrame(boston.data, columns=boston.feature_names)
# 目的変数の追加
df["MEDV"] = boston.target

# ランダムシード値
RANDOM_STATE = 10

# 学習データと評価データの割合
TEST_SIZE = 0.2

# 学習データと評価データを作成
x_train, x_test, y_train, y_test = train_test_split(
    df.iloc[:, 0 : df.shape[1] - 1],
    df.iloc[:, df.shape[1] - 1],
    test_size=TEST_SIZE,
    random_state=RANDOM_STATE,
)

# trainのデータセットの2割をモデル学習時のバリデーションデータとして利用する
x_train, x_valid, y_train, y_valid = train_test_split(
    x_train, y_train, test_size=TEST_SIZE, random_state=RANDOM_STATE
)

df_train = pd.concat([x_train, y_train], axis=1)
df_valid = pd.concat([x_valid, y_valid], axis=1)
```

今回は、オプション指定無しで実行してみます。

```php
predictor = TabularPredictor(
    label='MEDV',
    problem_type='regression', 
    eval_metric='root_mean_squared_error',
)

predictor.fit(
            train_data=df_train,
            tuning_data=df_valid,
            time_limit=None,
)

y_pred = predictor.predict(x_test)
```

ここから、SHAP を使っていきます。
まず、AutoGluon のラッパークラスを作成して、SHAP パッケージ内で予測のために呼び出すことができるように設定します。

```php
class AutogluonWrapper:
    def __init__(self, predictor, feature_names):
        self.ag_model = predictor
        self.feature_names = feature_names
    
    def predict(self, X):
        if isinstance(X, pd.Series):
            X = X.values.reshape(1,-1)
        if not isinstance(X, pd.DataFrame):
            X = pd.DataFrame(X, columns=self.feature_names)
        return self.ag_model.predict(X)
```

次に特徴量のベースライン参照値を定義します。

```php
x_train_summary = shap.kmeans(x_train, 10)
print("Baseline feature-values: \n", x_train_summary)
```

特定のAutoGluon 予測を説明するカーネルSHAP 値を返すKernelExplainer を作成します。

```php
ag_wrapper = AutogluonWrapper(predictor, boston.feature_names)
print_accuracy(ag_wrapper.predict)

explainer = shap.KernelExplainer(ag_wrapper.predict, x_train_summary)

NSHAP_SAMPLES = 100  
N_VAL = 30  
```

`NSHAP_SAMPLES` は、各SHAP 値を計算するために使用するサンプルの数であり、値が大きいほど遅くなります。
`N_VAL` は、予測結果を解釈する際に必要となるデータポイントの数であり、値が大きいほど遅くなります。

特定の行に対しての予測結果は下記のように確認します。

```php
ROW_INDEX = 0  # index of an example datapoint
single_datapoint = x_train.iloc[[ROW_INDEX]]
single_prediction = ag_wrapper.predict(single_datapoint)

shap_values_single = explainer.shap_values(single_datapoint, nsamples=NSHAP_SAMPLES)
shap.force_plot(explainer.expected_value, shap_values_single, x_train.iloc[ROW_INDEX,:]) 
```

カーネルSHAP に関する表示は以下のように実施します。

```php
shap_values = explainer.shap_values(x_valid.iloc[0:N_VAL,:], nsamples=NSHAP_SAMPLES)
shap.force_plot(explainer.expected_value, shap_values, x_valid.iloc[0:N_VAL,:])
```

次に、SHAP value とFeature value の関係性を確認します。

```php
shap.summary_plot(shap_values, x_valid.iloc[0:N_VAL,:])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743453/ffddea1a-0eae-e281-20ac-dce70ca87dcf.png)


特徴量の値とShapley Value　の散布図は下記のように描画できます。

```php
shap.dependence_plot("LSTAT", shap_values, x_valid.iloc[0:N_VAL,:])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743453/c889ed59-c019-767f-5c18-1646c7ab1ef3.png)

##さいごに
最後まで読んで頂き、ありがとうございました。
一部、AutoGluon とSHAP の活用で上手く理解できていない部分もありますが、AutoML　とSHAP を組合せることができるのは良いなと思いました。

訂正要望がありましたら、ご連絡頂けますと幸いです。