SHAP: 機械学習モデルが出力する個別の予測値の理由を説明する

機械学習モデルを解釈する手法の一つとして、「予測モデルの特定の出力値がどの特徴量によってもたらされたのか」を計算するSHAPがあります。本ブログ記事では、SHAPの計算方法やPythonによる実行例、利点と注意点などについて説明します。
1 はじめに
Deep Neural NetworkやLightGBMなどの高度な機械学習アルゴリズムは高い予測精度をもたらす一方で解釈性が低い傾向にあります。ビジネスでそれらの機械学習手法を利用するためには、モデルの挙動について説明できるほうがよいでしょう。
過去の記事で、「特徴量重要度」を計算するPermutaiton Importance (PI) や「モデルの予測値と特徴量の平均的な関係」を可視化するPartial Dependence Plot (PDP) の概要を説明しました。
PIやPDPはモデルとデータセット全体とのグローバルな関係を捉える手法であるため、モデルが出力した個別の予測値に対して、個別のデータ点ごとの異質性を考慮して解釈することはできません。機械学習モデルが出力した個別の予測値がどの特徴量によってもたらされたのかを計算する手法として、SHapley Additive eXplanations (SHAP) が知られています。
本ブログ記事では、SHAPの計算方法やPythonによる実行例、実際に利用する際の注意点などについて説明します。
2 SHAPの計算方法
SHAPのアイデア
SHAPは、モデルが出力する個別の予測値を各特徴量の貢献度に分解する手法です。
森下 (2021)『機械学習を解釈する技術』を元に作成)
貢献度の計算アルゴリズム
各特徴量の貢献度(SHAP値)の具体的な算出方法については、以下の記事が参考になります。
3 Pythonでの実行例
実行環境
Python 3.11.9
ライブラリのインポート
# 必要なライブラリをインポート
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
from sklearn.datasets import fetch_california_housing
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
import shap
データの準備
ここでは、scikit-learnの fetch_california_housing データセットを使用します。 このデータセットに含まれる様々な特徴量から、カリフォルニア州の住宅価格を予測する回帰モデルを構築します。
# カリフォルニア住宅価格データセットを読み込む
california = fetch_california_housing()
# データをDataFrameに変換
df = pd.DataFrame(california.data, columns=california.feature_names)
# カラム名を日本語に変更
japanese_columns = {
'MedInc': '世帯の中央値収入',
'HouseAge': '住宅の築年数',
'AveRooms': '平均部屋数',
'AveBedrms': '平均寝室数',
'Population': '人口',
'AveOccup': '世帯の平均人数',
'Latitude': '緯度',
'Longitude': '経度',
}
df.rename(columns=japanese_columns, inplace=True)
df['住宅価格'] = california.target
# 特徴量とターゲットに分ける
X = df.drop('住宅価格', axis=1)
y = df['住宅価格']
# データをトレーニングセットとテストセットに分割(8:2)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
学習済みモデルの準備
今回は、scikit-learnの GradientBoostingRegressor を用いて予測モデルを学習します。
SHAPはモデルの学習アルゴリズムに依存しないため、解きたい問題設定に応じて他のアルゴリズムを使用することもできます。
# モデルの構築
gbr = GradientBoostingRegressor()
# モデルのトレーニング
gbr.fit(X_train, y_train)
SHAPの計算と可視化
Pythonでは、簡単にSHAPを実行できるライブラリ shap
が提供されています。
ここでは、テストデータにおける1行目のデータ点を対象に、モデルの予測値に対する特徴量の貢献度を可視化します。
# SHAP値の計算
explainer = shap.Explainer(gbr, X_test)
shap_values = explainer(X_test.iloc[0:1]) # テストデータ1行目のSHAP値を計算
# SHAP値の可視化
shap.initjs() # JavaScriptの初期化
shap.waterfall_plot(shap_values[0]) # 1行目のデータに対するSHAP値を可視化
出力されるグラフは以下のようになります。 個別の予測値 f(X) とテストデータ全体における予測値の期待値 E[f(X)] の差を各特徴量の貢献度に分解していることが確認できます。
SHAPは、個別のデータ点に対するSHAP値を平均したり、分布として可視化することで、モデルをグローバルに解釈することも可能です。
テストデータ全体に対するSHAP値(の絶対値)の平均を求めることで、特徴量重要度を計算できます。
# マクロな解釈(平均)
explainer = shap.Explainer(gbr, X_test)
shap_values = explainer(X_test) # テストデータ全体のSHAP値を計算
# 平均的な重要度を可視化
shap.summary_plot(shap_values, X_test, plot_type='bar')
以下のようなグラフによって、モデル全体における特徴量の重要度が可視化されます。
次に、テストデータ全体におけるSHAP値の分布を可視化します。
# マクロな解釈(分布)
explainer = shap.Explainer(gbr, X_test)
shap_values = explainer(X_test) # テストデータ全体のSHAP値を計算
# SHAP値の分布を可視化
shap.summary_plot(shap_values, X_test)
以下のようなグラフが出力されます。
4 利点と課題
SHAPには、以下のようなメリットがあります。
・ 予測モデルの学習後に適用されるため、モデルの学習アルゴリズムに依存しない
・ 個別のデータに対するモデルの予測値へのミクロな解釈に加えて、適切な粒度で平均化したり集計したりすることでマクロな解釈も可能
一方で、次のような限界もあるため注意して利用することが大切です。
・ 大規模なデータに対して適用する場合は、計算コストが大きい。
・ 計算方法が複雑であるため、他の手法(Permutation Importanceなど)と比べてモデル使用者への説明が難しい。
5 まとめ
本記事では、SHAP(SHapley Additive exPlanations)の計算方法のアイデアやPythonでの実行例、利点と課題について解説しました。SHAPは、機械学習モデルの予測値を各特徴量の貢献度に分解する手法であり、特に個別の予測値に対する解釈が可能であるという強みを持っています。一方で、計算コストが大きい点や非専門家への説明が難しい点など、いくつかの限界も存在します。SHAPの課題を理解したうえで使用することで、誤解を招くモデルの解釈を防ぐことができるでしょう。
6 参考資料
森下(2021)『機械学習を解釈する技術』技術評論社
Christoph Molnar (2024) Interpretable Machine Learning.