SHAP分析の統合
「予測できるだけでは不十分だ。なぜその顧客が離反しそうなのか、説明できなければビジネスには使えない。」
田中VPoEが強調する。
「SHAPは予測の根拠をフェアに分解できる手法だ。各特徴量がどれだけ予測に寄与しているかを数値化し、そこから施策に変換する。これがAIエージェントの真価だ。」
SHAPとは
SHAP(SHapley Additive exPlanations)は、ゲーム理論のShapley値に基づく機械学習モデルの説明手法である。
SHAPの特徴:
1. 局所説明: 個別の予測に対する各特徴量の寄与度を計算
2. 大域説明: 全データに対する特徴量の全体的な重要度を計算
3. 一貫性: 特徴量の寄与の合計 = 予測値 - ベースライン
4. 公平性: ゲーム理論に基づく数学的に公正な配分
SHAP値の直感的な理解
# SHAP値の意味
# f(x) = base_value + SHAP(x1) + SHAP(x2) + ... + SHAP(xn)
# ベースライン: 全データの平均予測値
# SHAP(xi): 特徴量xiが予測値をベースラインからどれだけ変化させるか
import shap
import numpy as np
# TreeExplainerの作成(決定木ベースモデル用)
explainer = shap.TreeExplainer(model)
# 単一サンプルのSHAP値を計算
sample = X_test.iloc[0:1]
shap_values = explainer.shap_values(sample)
# クラス1(離反)のSHAP値
if isinstance(shap_values, list):
sv = shap_values[1][0] # [クラス][サンプル]
else:
sv = shap_values[0]
print(f"ベースライン(平均離反確率): {explainer.expected_value[1]:.4f}")
print(f"予測値: {model.predict_proba(sample)[0][1]:.4f}")
print(f"SHAP値の合計: {sv.sum():.4f}")
局所説明(個別の予測)
# 個別予測のSHAP分析
def individual_shap_analysis(model, explainer, sample, feature_names):
"""1件の顧客に対するSHAP分析"""
shap_values = explainer.shap_values(sample)
if isinstance(shap_values, list):
sv = shap_values[1][0]
else:
sv = shap_values[0]
# 要因の整理
factors = sorted(
zip(feature_names, sv, sample.values[0]),
key=lambda x: abs(x[1]),
reverse=True
)
print("=== 離反要因分析 ===")
for name, shap_val, feat_val in factors[:5]:
direction = "↑離反促進" if shap_val > 0 else "↓離反抑制"
print(f" {name}: SHAP={shap_val:+.4f} (値={feat_val:.2f}) {direction}")
return factors
factors = individual_shap_analysis(model, explainer, sample, X_test.columns.tolist())
Force Plot(力グラフ)
# 個別予測のForce Plot
shap.force_plot(
explainer.expected_value[1],
shap_values[1][0],
sample.iloc[0],
feature_names=X_test.columns.tolist(),
matplotlib=True
)
plt.savefig('shap_force_plot.png', dpi=150, bbox_inches='tight')
Waterfall Plot(滝グラフ)
# Waterfall Plot
shap_explanation = shap.Explanation(
values=shap_values[1][0],
base_values=explainer.expected_value[1],
data=sample.values[0],
feature_names=X_test.columns.tolist()
)
shap.waterfall_plot(shap_explanation)
plt.savefig('shap_waterfall.png', dpi=150, bbox_inches='tight')
大域説明(全体的な重要度)
# 全テストデータのSHAP値を計算
shap_values_all = explainer.shap_values(X_test)
# Summary Plot(蜂群図)
shap.summary_plot(
shap_values_all[1] if isinstance(shap_values_all, list) else shap_values_all,
X_test,
plot_type='dot',
show=False
)
plt.savefig('shap_summary.png', dpi=150, bbox_inches='tight')
# Bar Plot(重要度棒グラフ)
shap.summary_plot(
shap_values_all[1] if isinstance(shap_values_all, list) else shap_values_all,
X_test,
plot_type='bar',
show=False
)
plt.savefig('shap_importance.png', dpi=150, bbox_inches='tight')
SHAP結果からビジネス施策への変換
def shap_to_business_actions(factors, customer_data):
"""SHAP要因をビジネス施策に変換する"""
actions = []
# 施策マッピングルール
rules = {
"Contract": {
"condition": lambda sv, fv: sv > 0,
"action": "年間契約への移行を提案(初月20%割引 + ポイント2倍)",
"rationale": "短期契約が離反を促進しています",
},
"tenure": {
"condition": lambda sv, fv: sv > 0,
"action": "オンボーディングプログラムへの招待",
"rationale": "利用期間が短く、まだサービスの価値を実感できていない可能性があります",
},
"MonthlyCharges": {
"condition": lambda sv, fv: sv > 0,
"action": "プラン最適化の提案(不要サービスの見直し)",
"rationale": "月額料金の高さが負担になっている可能性があります",
},
"InternetService": {
"condition": lambda sv, fv: sv > 0,
"action": "回線品質チェックの実施と改善保証",
"rationale": "インターネットサービスへの不満が離反要因です",
},
"OnlineSecurity": {
"condition": lambda sv, fv: sv > 0,
"action": "オンラインセキュリティ3ヶ月無料トライアルの提供",
"rationale": "セキュリティサービス未加入が離反リスクを高めています",
},
"TechSupport": {
"condition": lambda sv, fv: sv > 0,
"action": "テックサポート優先対応チケットの提供",
"rationale": "テックサポート未加入が離反リスクを高めています",
},
"PaymentMethod": {
"condition": lambda sv, fv: sv > 0,
"action": "自動引き落とし設定で毎月¥500割引",
"rationale": "手動支払いが離反の一因となっています",
},
}
for name, shap_val, feat_val in factors[:5]:
if shap_val <= 0:
continue
for key, rule in rules.items():
if key in name and rule["condition"](shap_val, feat_val):
actions.append({
"action": rule["action"],
"rationale": rule["rationale"],
"shap_impact": round(float(shap_val), 4),
})
break
return actions
エージェントへの統合
# エージェントのexplainノードを強化
def explain_enhanced(state: ChurnAnalysisState) -> dict:
"""SHAP分析 + 施策変換の強化版"""
features_array = np.array(state["features"]).reshape(1, -1)
explainer = shap.TreeExplainer(MODEL)
shap_values = explainer.shap_values(features_array)
if isinstance(shap_values, list):
sv = shap_values[1][0]
else:
sv = shap_values[0]
# 要因の整理
factors = sorted(
zip(state["feature_names"], sv, features_array[0]),
key=lambda x: abs(x[1]),
reverse=True
)
# SHAP結果
explanations = [{
"feature": name,
"shap_value": round(float(shap_val), 4),
"direction": "離反促進" if shap_val > 0 else "離反抑制",
} for name, shap_val, _ in factors[:5]]
# 施策変換
actions = shap_to_business_actions(factors, state["raw_data"])
return {
"shap_explanations": explanations,
"retention_actions": actions,
}
まとめ
| 項目 | ポイント |
|---|---|
| SHAP | ゲーム理論に基づく公正な特徴量寄与度の計算 |
| 局所説明 | 個別顧客の離反要因をTop5で表示 |
| 大域説明 | 全体の特徴量重要度をSummary Plotで可視化 |
| 施策変換 | SHAP要因 → ルールベースで具体的アクションに変換 |
| 可視化 | Force Plot, Waterfall, Summary Plotの3種 |
チェックリスト
- SHAP値の意味を説明できる(ベースラインからの変化量)
- TreeExplainerで局所説明を実行できる
- Force PlotとSummary Plotを作成できる
- SHAP結果からビジネス施策を導出できる
- エージェントにSHAP分析を統合できる
次のステップへ
SHAP分析の統合が完了した。次は演習で、離反分析AIエージェントを一気通貫で実装してみよう。
推定読了時間: 30分