• home
  • about
  • 全ての投稿
  • ソフトウェア・ハードウェアの設定のまとめ
  • 分析関連のまとめ
  • ヘルスケア関連のまとめ
  • 生涯学習関連のまとめ

shap

date: 2021-01-02 excerpt: shapの使い方

tag: 解釈lightgbm


shapの使い方

概要

  • 特徴量の重要度を判別可能
  • その方法は特徴量のpermutationを行うことで可能にしている(独立性の確保)

インストール

$ pip install shap

具体例: binary分類

import lightgbm as lgb
import shap
import logging
import warnings
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import pandas as pd
import matplotlib.pyplot as plt

# 警告を抑制
warnings.filterwarnings('ignore', message='No further splits with positive gain, best gain: -inf')
logging.getLogger('lightgbm').setLevel(logging.WARNING)

# データの読み込みとデータフレームの作成
data = load_breast_cancer()
df = pd.DataFrame(data.data, columns=data.feature_names)
df['target'] = data.target

# データの分割
X_train, X_valid, y_train, y_valid = train_test_split(df.drop(columns=['target']), df['target'], test_size=0.2, random_state=42)

# LightGBMのデータセットに変換
train_data = lgb.Dataset(X_train, label=y_train)
valid_data = lgb.Dataset(X_valid, label=y_valid, reference=train_data)

# パラメータ設定
params = {
    'boosting_type': 'gbdt',
    'objective': 'binary',
    'metric': ['binary_logloss', 'auc'],
    'learning_rate': 0.1,
    'num_leaves': 31,
    'verbose': -1  # ここで警告を抑制
}

# モデルのトレーニング
gbm = lgb.train(params,
                train_data,
                num_boost_round=100,
                valid_sets=[train_data, valid_data],
                valid_names=['train', 'valid'],
                callbacks=[lgb.log_evaluation(period=10),
                           lgb.early_stopping(stopping_rounds=10, first_metric_only=True)])

# SHAP値の計算
explainer = shap.TreeExplainer(gbm)
shap_values = explainer.shap_values(X_valid)

# 特徴重要度のバープロット
shap.summary_plot(shap_values, X_valid, plot_type="bar")

# 特徴の詳細な影響の可視化
shap.summary_plot(shap_values, X_valid)

# 特定のレコードの判断に効いた要素を確認
record_index = 0  # 確認したいレコードのインデックス
shap.force_plot(explainer.expected_value, shap_values[record_index], X_valid.iloc[record_index], matplotlib=True, text_rotation=45)

具体例; JavaScriptで特徴量の可視化を行う

  • 各モデルの平均的なshapの値を計算し、平均を計算する
  • shap.initjs()でJSを初期化
  • プロットを行う

平均値の計算

import shap
shap_vals = []
for model in ret["models"]:
    explainer = shap.TreeExplainer(model)
    shap_val = np.array(explainer.shap_values(X))
    shap_vals.append(shap_val)
shap_val_np: np.array = np.mean(shap_vals, axis=0)

サマリーの計算

shap.initjs()
shap.summary_plot(shap_val_np[1, :], X)

具体例; CVで学習し、回帰で使用する例

  • shapは一度に1つのモデルしか評価できないので、複数のモデルを利用する際は、すべてのモデルをshapで調査し、結果の平均を計算する

shap実行部分

import shap

shap_vals = []
for model in ret["models"]:
    explainer = shap.TreeExplainer(model)
    shap_val = np.array(explainer.shap_values(X_train))
    shap_vals.append(shap_val)
shap_val = np.mean(shap_vals, axis=0)
# shap_val = shap_val[1, :] # 回帰の場合、必要ない
shap_val= pd.DataFrame(shap_val)
shap_val.columns = [f"{c}_sv" for c in X_train.columns]
shap = pd.concat([X_train, shap_val], axis=1)
display(shap)

特徴量がポジティブ、ネガティブに働くかを計算

data = []
for fname in ["feat_0", "feat_1", ...]:
    x = shap.copy()
    x[f"{fname}_sv"] = (x[f"{fname}_sv"] > 0).astype(int)
    data.append( (fname, x[f"{fname}_sv"].mean() ) )

x = pd.DataFrame(data)
x.columns = ["特徴量名", "ポジティブに働く確率"] 
x.sort_values(by=["ポジティブに働く確率"], ascending=False, inplace=True)
display(x)


解釈lightgbm Share Tweet