shap package

# shap

`shap`（SHapley Additive exPlanations）パッケージは、機械学習モデルの予測を解釈し、モデルの特徴の重要性を明らかにするためのツールキットです。`shap`は、Shapley値と呼ばれるゲーム理論の概念をベースにしており、特徴の寄与を個々に評価し、モデルの予測を説明するために使用されます。

https://github.com/shap/shap

2023年12月26日, ChatGPT3.5 & GPT4により記載。


## 説明


以下は、`shap`パッケージの主要な機能と用途についての説明です：

1. **Shapley値の計算**: `shap`は、Shapley値を計算するためのアルゴリズムを提供します。Shapley値は、特徴がモデルの予測に対してどれだけ寄与しているかを評価します。これにより、特徴の重要性を定量化することができます。

2. **モデルの解釈**: `shap`を使用すると、機械学習モデルの予測を説明するためにShapley値を利用できます。これにより、モデルがどの特徴にどのように依存しているかを理解しやすくなります。

3. **特徴の可視化**: `shap`は、Shapley値を視覚化するためのツールも提供します。Shapley値のプロットやサマリーを作成することで、特徴の寄与を視覚的に理解するのに役立ちます。

4. **さまざまなモデルのサポート**: `shap`は、さまざまな種類の機械学習モデル（回帰、分類、XGBoost、LightGBMなど）に対応しています。したがって、異なるタイプのモデルに適用できます。

5. **特徴エンジニアリング**: Shapley値を使用することで、特徴エンジニアリングのガイダンスを提供することも可能です。どの特徴がモデルにとって最も重要かを理解することで、特徴選択や特徴変換の意思決定を補完します。

`shap`パッケージは、機械学習モデルの解釈と特徴の重要性の理解を支援し、モデルのトランスペアレンシーを向上させるのに役立つ重要なツールの一つです。データサイエンティストや機械学習エンジニアがモデルの解釈性を向上させ、意思決定プロセスをサポートするために頻繁に使用します。

## インストール

環境：Ubuntu20.04.6, miniconda 22.9.0, Python 3.9.12

```
$ pip install shap
```

Python packageのversion依存性のために、versionを指定してPython packageを追加インストールする必要があるかもしれません。

## 例

### 分類モデル例

#### ソースコード

```python
import shap
import sklearn
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt

# アイリスデータセットをロード
iris = load_iris()
X, y = iris.data, iris.target

# ランダムフォレストモデルをトレーニング
model = RandomForestClassifier()
model.fit(X, y)

# ランダムフォレストの特徴重要度と特徴名を組み合わせる
feature_importances = model.feature_importances_
features = iris.feature_names
importances = list(zip(features, feature_importances))

# 特徴重要度を大きい順にソート
importances.sort(key=lambda x: x[1], reverse=True)

# ソートされた特徴名と重要度を分ける
sorted_features, sorted_importances = zip(*importances)

# 特徴重要度のプロット
fig, ax = plt.subplots()
plt.barh(sorted_features, sorted_importances)
plt.title('Feature Importances')
plt.xlabel('Importance')
plt.ylabel('Feature')
plt.gca().invert_yaxis() # 特徴を大きい順に表示
fig.savefig("cls_feature_importance.png")
plt.show()

# Shapley値を計算
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X)

# Shapley値のサマリープロット
fig, ax = plt.subplots()

shap.summary_plot(shap_values, X, feature_names=iris.feature_names)
fig.savefig("cls_shapley.png")
```

#### 出力図
前半のrandom forestのfeature importanceを示します。
![](cls_feature_importance.png)

後半のshapley図を示します。クラスごとにShapley値を示しました。

![](cls_shapley.png)![cls_shapley.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713556/d21afbc9-6524-8481-c243-f79ed499dcaa.png)
![cls_feature_importance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713556/d296de4d-0ca1-f9a8-6ea5-2574963bcfeb.png)



### 回帰モデル例１

#### ソースコード

```import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
import shap

# Diabetesデータセットをロード
diabetes = load_diabetes()
X = diabetes.data
y = diabetes.target

# データをトレーニングセットとテストセットに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# ランダムフォレスト回帰モデルを作成
rf_model = RandomForestRegressor(random_state=42)
rf_model.fit(X_train, y_train)

# テストデータで予測
y_pred_rf = rf_model.predict(X_test)

# 性能評価（平均二乗誤差）
mse_rf = mean_squared_error(y_test, y_pred_rf)

# ランダムフォレストの特徴重要度と特徴名を組み合わせ
feature_importances = rf_model.feature_importances_
features = diabetes.feature_names
importances = list(zip(features, feature_importances))

# 特徴重要度を大きい順にソート
importances.sort(key=lambda x: x[1], reverse=True)

# ソートされた特徴名と重要度を分ける
sorted_features, sorted_importances = zip(*importances)

# 特徴重要度のプロット
fig, ax = plt.subplots()

plt.barh(sorted_features, sorted_importances)
plt.xlabel('Feature Importance')
plt.ylabel('Feature')
plt.title('Random Forest Feature Importances')
plt.gca().invert_yaxis() # 特徴を大きい順に表示
fig.savefig("Feature_Importance.png")
plt.show()

# Shapley値を計算
explainer = shap.Explainer(rf_model, X_train)
shap_values = explainer(X_train, check_additivity=False )

# Shapley値のサマリープロット
fig, ax = plt.subplots()
shap.summary_plot(shap_values, X_train, feature_names=diabetes.feature_names)
fig.savefig("summary_plot.png")
```

このコードで訓練セット (`X_train`) を用いてShapley値を計算している理由には、いくつかの考慮事項があります。通常、Shapley値はモデルのトレーニングデータに基づいて計算されますが、テストデータを使用することも一般的です。それぞれのアプローチには特定の目的と利点があります。

##### 訓練データに基づいてShapley値を計算する理由：

1. **モデル理解の強化**:
   訓練データに基づいてShapley値を計算することで、モデルがどのようにそのトレーニングデータを学習したかを理解することができます。これは、モデルがデータの特徴をどのように捉えているかを明らかにするのに役立ちます。

2. **過学習の診断**:
   訓練データでのShapley値の分析を通じて、特定の特徴がモデルに過度に影響を与えていないか、またはモデルが過学習していないかを確認することができます。

3. **特徴選択の改善**:
   トレーニングデータに基づいて特徴の重要性を評価することで、より効果的な特徴選択やデータ前処理の戦略を導き出すことができます。

##### テストデータに基づいてShapley値を計算する理由：

1. **一般化能力の評価**:
   テストデータに基づいてShapley値を計算することで、モデルが未知のデータにどのように一般化しているかを評価することができます。

2. **予測の信頼性の確認**:
   テストデータの個々の予測に対する特徴の寄与を分析することで、モデルの予測がどれほど信頼できるかを評価することができます。

##### 結論

どちらのデータセットを使用するかは、分析の目的や特定の状況に応じて異なります。一般的には、モデルの全体的な挙動を理解したい場合は訓練データを、モデルの一般化能力や個々の予測の解釈を行いたい場合はテストデータを使用することが推奨されます。

#### エラー
コメント：
ChatGPTによるコードでは
```
ExplainerError: Additivity check failed in TreeExplainer! Please ensure the data matrix you passed to the explainer is the same shape that the model was trained on. If your data shape is correct then please report this on GitHub. This check failed because for one of the samples the sum of the SHAP values was 242.836801, while the model output was 248.880000. If this difference is acceptable you can set check_additivity=False to disable this check.
```
というエラーが起きるので`check_additivity=False`を加えています。

#### 出力図

前半のランダムフォレストのfeature importranceを以下に示します。

![](Feature_Importance.png) 

後半のShapleyのbeeswarm図を示します。
 ![](summary_plot.png)

この図の意味は以下になります。
このプロットにおける赤と青の点は以下の情報を表しています：

- **赤い点**: これらは、その特徴の値が高いデータポイントを示しています。例えば、`bmi`（Body Mass Index）の行で右側に赤い点が多い場合、高いBMIがモデルの予測値を増加させる（病気の進行やその他の反応変数の増加を示す）ことを意味します。
- **青い点**: これらは、その特徴の値が低いデータポイントを示しています。上の例で言えば、`bmi`の行で左側に青い点が多い場合、低いBMIがモデルの予測値を減少させる傾向があることを意味します。
各点の位置（横軸上）は、そのデータポイントにおける特徴のShapley値（予測への影響度）を表しており、0から離れるほど影響が大きいことを示します。点が縦軸の中央線から右にある場合は、その特徴が予測を増加させる方向に寄与していることを意味し、左にある場合は予測を減少させる方向に寄与していることを意味します。

さらに、点の密度が高いほど、その特徴が多くのデータポイントにおいて予測に寄与していることを示し、Shapley値のバリエーションの大きさを表します。これはモデルがその特徴にどの程度依存しているか、またその特徴の値が予測に与える影響の一貫性を示します。
このビジュアルは、特徴がモデルの予測にどのように寄与しているかを理解するのに非常に有効です。特に、どの特徴が重要であるか、そしてその特徴が予測値を高めるのか低めるのかを明らかにすることができます。


### 回帰モデル例２

```python
import shap
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
import matplotlib.pyplot as plt

# Diabetesデータセットをロード
diabetes = load_diabetes()
X = diabetes.data
y = diabetes.target

# データをトレーニングセットとテストセットに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# ランダムフォレスト回帰モデルを作成
rf_model = RandomForestRegressor(random_state=42)
rf_model.fit(X_train, y_train)

# Shapley値を計算
explainer = shap.Explainer(rf_model, X_train)
shap_values = explainer.shap_values(X_train, check_additivity=False)

shap.plots.force(explainer.expected_value,shap_values)
```

#### 出力図

![](force_plot.png)![force_plot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713556/e5c435a7-a33a-fc0a-871b-6a9c20cc980c.png)
![Feature_Importance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713556/042585c7-71df-c3bf-7c56-bd5534a4b399.png)
![summary_plot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713556/18f11573-1dd4-cf64-abab-f2231ead1890.png)


コメント：jupyter labで表示するとmouseを図上で動かすと'Feature X'のように寄与が大きな説明変数が表示されます。全てのデータインスタンス（横軸）に対して、各説明変数の寄与がどの程度かを表示しています。赤と青の意味は上と同じです。

## 参考

### Shapley値の計算方法

Shapley値は、もともと協力ゲーム理論でプレイヤーの公平な利益分配を決定するために考案されました。機械学習においては、各特徴量をプレイヤーと見なし、予測モデルの出力を利益として扱います。

Shapley値の計算式は以下の通りです：

$$ \phi_i(v) = \sum_{S \subseteq N \setminus \{i\}} \frac{|S|! (|N|-|S|-1)!}{|N|!} [v(S \cup \{i\}) - v(S)] $$

ここで、
- $ \phi_i(v) $ は特徴量 $ i $ のShapley値です。
- $ N $ は全特徴量の集合です。
- $ S $ は $ N $ から特徴量 $ i $ を除いた任意の部分集合です。
- $ |S| $ は集合 $ S $ の要素数です。
- $ v(S) $ は特徴量の集合 $ S $ が存在するときの予測モデルの出力です。

この式は、特徴量 $ i $ を追加することによる予測モデルの出力の変化の平均を取ることに相当します。これにより、特徴量 $ i $ が予測にどれだけ貢献しているかを算出できます。

#### モデル全探索について

理論上、Shapley値を計算するためには、すべての可能な特徴量の組み合わせを考慮する必要があります。つまり、特徴量の集合 $ N $ のすべての部分集合について、モデルの出力を計算する必要があります。これは、特徴量の数が多い場合、計算量が非常に大きくなることを意味します（計算量は特徴量の数に対して指数関数的に増加します）。

実際の応用では、この計算量の大きさが問題となるため、近似的な手法がしばしば用いられます。例えば、ツリーベースのモデルではTree SHAPという高速化されたアルゴリズムが使用されます。このアルゴリズムは、特定のタイプのモデルにおいて、計算効率よく正確なShapley値を計算することができます。一方、より一般的なモデルに対しては、ランダムサンプリングや他の近似手法が使われることがあります。

### Pythonの`shap`パッケージと`shapley`パッケージ

Pythonの`shap`パッケージと`shapley`パッケージは、概念的に似ているものの、用途と機能面でいくつかの違いがあります。

1. **shap パッケージ**:
   - `shap`は、機械学習モデルの予測における各特徴の影響を解釈し、可視化するためのツールです。
   - このパッケージは、SHAP（SHapley Additive exPlanations）値を計算するために使用されます。これは、ゲーム理論に基づくアプローチで、特定の予測に対する各特徴の寄与度を測定します。
   - `shap`は、さまざまなタイプの機械学習モデル（線形モデル、ツリーベースのモデル、ディープラーニングモデルなど）に対応しています。
   - 主に、モデルの解釈性と透明性を高めるために使用されます。

2. **shapley パッケージ**:
   - `shapley`は、PythonでShapley値を計算するための別のツールですが、`shap`パッケージほど広範囲には使用されていません。
   - これは、ゲーム理論のコンテキストでShapley値を計算するために特化している可能性があり、主に協力ゲームや経済モデル内でのプレイヤーの寄与を評価するために使用されます。
   - `shapley`パッケージは、`shap`ほど機械学習の解釈可能性に焦点を当てていない可能性があります。

要するに、`shap`は機械学習モデルの特徴の影響を解釈し、説明するための包括的なツールであり、`shapley`はゲーム理論の文脈でShapley値を計算するためのより専門的なツールである可能性が高いです。ただし、これらのパッケージの詳細な機能や使用法については、各パッケージのドキュメントを参照することをお勧めします。