shap-model-explainability について
shap-model-explainabilityは、SHAPで機械学習の予測を説明するためのスキルです。モデルに応じた説明器の選択から、局所・全体の分析や特徴量の関係分析まで扱います。
主な機能
- 01モデル種別に応じた説明器の選択
- 02特徴量の寄与度の計算
- 03個別予測の説明
- 04全体的な特徴量重要度の集計
- 05特徴量関係の可視化
- 06グループ間の説明結果の比較
活用シーン
- 01予測に影響した特徴量の特定
- 02モデルの挙動やデータ漏洩の調査
- 03人口統計グループ間での説明比較
- 04関係者へのモデル説明の提示
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill shap-model-explainabilityインストール後、Claude Code で/shap-model-explainabilityを入力して読み込まれたか確認します。
同梱ファイルreferences/theory.md
詳細
SHAP によるモデルの説明可能性
概要
SHAP(SHapley Additive exPlanations)は、協力ゲーム理論のシャープレイ値を使用して機械学習モデルの予測を説明するための統一フレームワークです。各特徴量が個々の予測にどの程度寄与しているかを定量化し、一貫性と加法性に関する理論的保証を備えた、ローカル(インスタンス単位)およびグローバル(データセット単位)の両方の説明を提供します。
使用する場面
- モデルの予測を左右する特徴量を説明する(全体的な重要度)
- モデルが特定の予測を行った理由を理解する(ローカルな説明)
- モデルの挙動をデバッグし、データリークを特定する
- デモグラフィックグループ間でモデルの公平性を分析する
- 複数のモデル間で特徴量の重要度を比較する
- ステークホルダー向けに解釈可能なモデルの説明を生成する
- 木ベースのモデルを解釈する場合は、置換重要度やジニ重要度よりも SHAP を優先してください(より正確で、インスタンス単位の説明が可能です)
- 画像の深層学習モデルを解釈する場合は GradCAM を検討し、表形式/構造化データには SHAP を使用してください
前提条件
pip install shap matplotlib
# Optional: xgboost lightgbm tensorflow torch (depending on model)
クイックスタート
import shap
import xgboost as xgb
from sklearn.model_selection import train_test_split
# Load example data
X, y = shap.datasets.adult()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Train model
model = xgb.XGBClassifier(n_estimators=100).fit(X_train, y_train)
# Explain: select explainer → compute → visualize
explainer = shap.TreeExplainer(model)
shap_values = explainer(X_test)
shap.plots.beeswarm(shap_values) # Global importance
shap.plots.waterfall(shap_values[0]) # Single prediction
print(f"Base value: {shap_values.base_values[0]:.3f}")
print(f"SHAP values shape: {shap_values.values.shape}") # (n_samples, n_features)
ワークフロー
ステップ 1:適切な説明器を選択する
モデルの種類に応じて選択します。
| モデルの種類 | 説明器 | 速度 | 正確性 |
|---|---|---|---|
| 木ベース(XGBoost、LightGBM、RF、CatBoost) | TreeExplainer | 高速 | 正確 |
| 線形(LogReg、GLM、Ridge) | LinearExplainer | 即時 | 正確 |
| 深層学習(TensorFlow、PyTorch) | DeepExplainer | 高速 | 近似 |
| 深層学習(勾配ベース) | GradientExplainer | 高速 | 近似 |
| 任意のモデル(ブラックボックス) | KernelExplainer | 低速 | 近似 |
| 任意のモデル(置換ベース) | PermutationExplainer | 非常に低速 | 正確 |
| 不明な場合 | shap.Explainer | 自動 | 自動 |
# Tree-based models (most common)
explainer = shap.TreeExplainer(model)
# Linear models
explainer = shap.LinearExplainer(model, X_train)
# Deep learning
explainer = shap.DeepExplainer(model, X_train[:100])
# Any model (model-agnostic, slower)
explainer = shap.KernelExplainer(model.predict, shap.kmeans(X_train, 50))
# Auto-select
explainer = shap.Explainer(model, X_train)
ステップ 2:SHAP 値を計算する
shap_values = explainer(X_test)
# shap_values object contains:
# .values — SHAP values array (n_samples, n_features)
# .base_values — Expected model output (baseline)
# .data — Original feature values
# Verify additivity: prediction = base_value + sum(SHAP values)
print(f" {shap_values.base_values[0]:.3f} + {shap_values.values[0].sum():.3f} = "
f"{shap_values.base_values[0] + shap_values.values[0].sum():.3f}")
ステップ 3:グローバルな説明
# Beeswarm: feature importance + value distributions (most informative)
shap.plots.beeswarm(shap_values, max_display=15)
# Bar: clean mean |SHAP| importance
shap.plots.bar(shap_values)
ステップ 4:ローカルな説明(個々の予測)
# Waterfall: detailed breakdown of one prediction
shap.plots.waterfall(shap_values[0])
# Force: additive force visualization
shap.plots.force(shap_values[0])
ステップ 5:特徴量の関係
# Scatter: how a feature affects predictions
shap.plots.scatter(shap_values[:, "Age"])
# Colored by interaction feature
shap.plots.scatter(shap_values[:, "Age"], color=shap_values[:, "Education-Num"])
ステップ 6:高度な可視化
# Heatmap: multi-sample SHAP grid
shap.plots.heatmap(shap_values[:100])
# Decision plot: cumulative SHAP paths
shap.plots.decision(shap_values.base_values[0], shap_values.values[:10],
feature_names=X_test.columns.tolist())
# Cohort comparison
import numpy as np
mask_a = X_test["Age"] < 40
shap.plots.bar({
"Under 40": shap_values[mask_a],
"40+": shap_values[~mask_a]
})
主要なパラメーター
| パラメーター | 説明器/関数 | デフォルト | 効果 |
|---|---|---|---|
feature_perturbation | TreeExplainer | "tree_path_dependent" | 因果的な解釈には "interventional" を指定します(背景データが必要です) |
model_output | TreeExplainer | "raw" | 対数オッズではなく確率を説明するには "probability" を指定します |
data(背景データ) | KernelExplainer、DeepExplainer | 必須 | 代表的なサンプルを 100~1000 件指定します。効率化には shap.kmeans(X, 50) を使用してください |
nsamples | KernelExplainer | "auto" | 値を大きくすると精度は上がりますが、処理が遅くなります。最小値は特徴量数の 2 倍です |
max_display | すべてのプロット関数 | 10 | プロットに表示する特徴量の数 |
alpha | scatter/beeswarm | 1.0 | データセットが密集している場合の点の透明度 |
show | すべてのプロット関数 | True | 保存用の matplotlib 図を取得するには False に設定します |
clustering | beeswarm | None | 相関する特徴量をクラスタリングするには shap.utils.hclust(...) を使用します |
主要な概念
SHAP 値の特性
SHAP 値には、説明手法の中でも独自の 3 つの理論的保証があります。
- 加法性:
prediction = base_value + sum(SHAP values)— 正確に分解されます - 一貫性:モデル内で特徴量の重要度が高まると、その SHAP 値も増加します
- 欠損性:存在しない特徴量にはゼロの帰属値が割り当てられます
解釈:正の SHAP 値は予測を高め、負の値は予測を下げます。絶対値は影響の強さを示します。
モデル出力の種類
モデルがどのような値を出力するかを把握してください。SHAP は出力空間を説明します。
- 回帰:SHAP 値は目的変数の単位(例:ドル、温度)で表されます
- 分類(対数オッズ):木ベースの分類器ではデフォルトです。確率による説明には
model_output="probability"を使用します - 分類(確率):SHAP 値の合計は、ベースラインからの確率の偏差になります
SHAP とその他の手法
| 手法 | ローカル | グローバル | 一貫性 | モデル非依存 |
|---|---|---|---|---|
| SHAP | はい | はい | はい | はい |
| 順列重要度 | いいえ | はい | いいえ | はい |
| ジニ重要度/分割重要度 | いいえ | はい | いいえ | 決定木のみ |
| LIME | はい | いいえ | いいえ | はい |
| 統合勾配 | はい | いいえ | 一部 | NN のみ |
相互作用値(TreeExplainer のみ)
shap_interaction = explainer.shap_interaction_values(X_test)
# Shape: (n_samples, n_features, n_features)
# Diagonal = main effects; off-diagonal = pairwise interactions
背景データの選択
背景データはベースライン(モデル出力の期待値)を設定します。選択によって SHAP の値の大きさは変わりますが、相対的な重要度は変わりません。
- 学習データからランダムにサンプリング:100〜500 サンプル
- 効率的に要約するには
shap.kmeans(X_train, 50)を使用します tree_path_dependentを使う TreeExplainer の場合:背景データは不要です(木構造を使用します)- DeepExplainer/KernelExplainer の場合:精度と速度のバランスを取るには 100〜1000 サンプルを使用します
よくあるレシピ
レシピ:モデルのデバッグ
import numpy as np
# Find misclassified samples
predictions = model.predict(X_test)
errors = predictions != y_test
error_indices = np.where(errors)[0]
# Explain errors
for idx in error_indices[:3]:
print(f"Sample {idx}: predicted={predictions[idx]}, actual={y_test.iloc[idx]}")
shap.plots.waterfall(shap_values[idx])
# Check for data leakage: unexpected high-importance features
mean_abs_shap = np.abs(shap_values.values).mean(0)
top_features = X_test.columns[mean_abs_shap.argsort()[-5:]]
print(f"Top features (check for leakage): {list(top_features)}")
レシピ:公平性分析
# Compare SHAP distributions across groups
group_a = shap_values[X_test["Sex"] == 0]
group_b = shap_values[X_test["Sex"] == 1]
shap.plots.bar({"Female": group_a, "Male": group_b})
# Check protected attribute importance
sex_importance = np.abs(shap_values[:, "Sex"].values).mean()
total_importance = np.abs(shap_values.values).mean()
print(f"Sex contribution: {sex_importance/total_importance:.1%} of total importance")
レシピ:本番環境でのキャッシュ
import joblib
# Save explainer for reuse
joblib.dump(explainer, 'explainer.pkl')
explainer = joblib.load('explainer.pkl')
# Batch computation for API responses
def explain_batch(X_batch, explainer, top_n=5):
sv = explainer(X_batch)
results = []
for i in range(len(X_batch)):
top_idx = np.abs(sv.values[i]).argsort()[-top_n:]
results.append({
'prediction': sv.base_values[i] + sv.values[i].sum(),
'top_features': {X_batch.columns[j]: sv.values[i][j] for j in top_idx}
})
return results
レシピ:MLflow との統合
import mlflow
import matplotlib.pyplot as plt
with mlflow.start_run():
model = xgb.XGBClassifier().fit(X_train, y_train)
explainer = shap.TreeExplainer(model)
shap_values = explainer(X_test)
shap.plots.beeswarm(shap_values, show=False)
mlflow.log_figure(plt.gcf(), "shap_beeswarm.png")
plt.close()
for feat, imp in zip(X_test.columns, np.abs(shap_values.values).mean(0)):
mlflow.log_metric(f"shap_{feat}", imp)
想定される出力
| 出力 | 型 | 説明 |
|---|---|---|
shap_values | shap.Explanation | .values (n_samples, n_features)、.base_values(ベースライン)、.data(入力特徴量)を含むオブジェクト |
| ウォーターフォールプロット | matplotlib figure | ベース値から予測値までの特徴量の寄与を示す、単一インスタンスの説明 |
| ビーズウォームプロット | matplotlib figure | グローバルな要約:すべてのサンプルにおける特徴量重要度とその方向 |
| 棒グラフ | matplotlib figure | 特徴量ごとの SHAP 値の絶対値の平均(グローバルな重要度の順位付け) |
| フォースプロット | HTML/matplotlib | 単一の予測をインタラクティブまたは静的に可視化 |
mean_abs_shap | pd.Series | 順位付けと報告に使う、特徴量ごとの SHAP 値の絶対値の平均 |
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
| 計算が非常に遅い | 木モデルに KernelExplainer を使用している | 木ベースのモデルには TreeExplainer を使用します |
| 大規模なデータセットで遅い | すべてのサンプルを一度に計算している | サブセットをサンプリングします:explainer(X_test[:1000]) またはバッチ処理 |
| SHAP 値の合計が予測値と一致しない | モデル出力の型が間違っている | model_output パラメーターを確認し、加法性を検証します |
| 対数オッズと確率が混同される | TreeExplainer の分類器は既定で対数オッズを使用する | TreeExplainer(model, model_output="probability") を使用します |
| プロットがごちゃつきすぎる | 表示される特徴量が多すぎる | max_display=10 を設定するか、特徴量クラスタリングを使用します |
| DeepExplainer のエラー | 背景データが少なすぎる | 背景サンプルを 100〜1000 件使用します |
| メモリエラー | 大規模なデータセットと多数の特徴量 | shap.kmeans(X, 50) を使って背景データを減らします |
| フォースプロットが表示されない | ノートブックに JS がない | ノートブックの冒頭で shap.initjs() を実行します |
| 実行ごとに重要度が一貫しない | KernelExplainer のサンプリング分散 | nsamples を増やすか、決定論的な Explainer を使用します |
| 関連する特徴量の重要度が負になる | 特徴量の相互作用または相関 | feature_perturbation="interventional" または散布図を使用します |
同梱リソース
references/theory.md— 数学的基礎:Shapley値の公式、主要な特性(加法性、対称性、ダミー、単調性)、計算アルゴリズム(Tree SHAP、Kernel SHAP、Deep SHAP、Linear SHAP)、条件付き期待値(介入的 vs 観測的)、LIME/DeepLIFT/LRP/Integrated Gradientsとの比較、相互作用値、理論上の限界
元の内容から移行していないもの:references/explainers.md(340行) — 各explainerクラスのコンストラクター引数、メソッド、性能ベンチマークの詳細。explainerの選び方ガイドと一般的な使い方は、ワークフローのステップ1と主要なパラメーターで説明しています。
元の内容から移行していないもの:references/plots.md(508行) — 9種類すべてのプロットタイプについて、高度なカスタマイズ(バイオリン、決定、特徴量クラスタリング)を含む包括的なパラメーターリファレンス。主なプロットタイプは、ワークフローのステップ3〜6で説明しています。
元の内容から移行していないもの:references/workflows.md(606行) — 特徴量エンジニアリング、モデル比較、ディープラーニングの説明、本番環境へのデプロイ、時系列分析についての詳細な手順別ワークフロー。基本的なパターンは「よく使うレシピ」で説明しています。より詳しいワークフローについては、元の資料を参照してください。
ベストプラクティス
- まずは特化型explainerを選択する —
TreeExplainer>LinearExplainer>DeepExplainer>KernelExplainer。特化型のものがない場合に限り、モデル非依存のexplainerを使用してください - 全体から始めて、次に局所を見る — まずbeeswarm/barで全体的な重要度を確認し、次にwaterfall/scatterで個々の予測や特徴量の関係を調べます
- 複数の可視化を使う — プロットによって異なる知見が得られます。全体(beeswarm)+局所(waterfall)+関係性(scatter)を組み合わせてください
- 適切な背景データを選択する — 学習データから代表的なサンプルを100〜500件選びます。効率化には
shap.kmeans()を使用してください - ドメイン知識で検証する — 特徴量の重要度が予想外に高い場合、真の予測力ではなくデータリークを示している可能性があります
- SHAPが示すのは関連性であり、因果関係ではないことを念頭に置く — 特徴量のSHAP重要度が高いということは、モデルがその特徴量を使用していることを意味し、結果に因果的な影響を与えていることを意味するわけではありません
- 特徴量の相関を考慮する — 相関する特徴量はSHAP重要度を共有します。因果的な解釈には
feature_perturbation="interventional"を使用するか、特徴量クラスタリングでグループ単位の重要度を確認してください
参考資料
- Lundberg & Lee(2017)、「モデル予測を解釈するための統一的なアプローチ」(NeurIPS)
- Lundberg et al.(2020)、「説明可能なAIを用いた局所的な説明から大域的な理解へ:木モデルの場合」(Nature Machine Intelligence)
- 公式ドキュメント:https://shap.readthedocs.io/
- GitHub:https://github.com/shap/shap
関連スキル
- scikit-learn-machine-learning — SHAP分析用のモデル学習
- matplotlib-scientific-plotting — SHAPプロットのカスタムスタイル設定とエクスポート
- statistical-analysis — SHAPの解釈を補完する統計的検定