説明可能なAI⑨(dependence_plotで可視化)

dependence_plot

dependence_plotを使うと、各カテゴリごとに2つのスコア(説明変数のSHAP値とその説明変数)を可視化することができます。

今回は、worst concave points(輪郭の凹部の数の最悪値)について可視化を行います。(3行目)

[Google Colaboratory]

1
2
3
4
5
6
for i in range(2):
print("Class ", i)
shap.dependence_plot(ind="worst concave points",
interaction_index=None,
shap_values=shap_values[i],
features=X_test)

乳がんの診断データセットは二値分類なので、ループさせて各カテゴリごとのSHAP値でdependence_plotを表示しています。

[実行結果]

同じデータに対して陽性か陰性かという2つのカテゴリに対するスコアが表示されています。

上図が陽性で、下図が陰性に関するグラフになります。

縦軸がSHAP値を表し、横軸がworst concave points(輪郭の凹部の数の最悪値)を表しています。

worst concave pointsが高ければカテゴリ0(陽性)に分類するSHAP値が高くなり、カテゴリ1(陰性)はその逆になっていることが確認できます。

説明可能なAI⑧(summary_plotで可視化)

summary_plot

分類系モデルにおける各説明変数の貢献度をsummary_plotで確認します。

[Google Colaboratory]

1
2
3
4
shap.summary_plot(shap_values=shap_values,
features=X_train,
plot_type="bar",
max_display=5)

[実行結果]

worst perimeter(周囲の最悪値)やworst concave points(輪郭の凹部の数の最悪値)などの貢献度が高いことが分かります。

また、分類系モデルのsummary_plotでは、色でどのカテゴリに対する貢献度が高いのかを確認することができます。

今回は二値分類なので2色で表示されており、どの説明変数もほぼ均等に貢献していることが確認できます。

説明可能なAI⑦(分類系モデルのSHAP値)

回帰モデルと同じように分類モデルでもSHAP値を確認することができます。

分類モデルを元に、SHAPモデルを作成して、SHAP値を確認します。

分類系モデル

乳がんの診断データセットを用いた分類モデルのランダムフォレストを利用します。

今回は説明変数をすべて利用します。(8行目)

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
load_data = load_breast_cancer()
tg_df = pd.DataFrame(load_data.data, columns = load_data.feature_names)
tg_df["y"] = load_data.target
X = tg_df[tg_df.columns[tg_df.columns != "y"]] # 全ての説明変数を使用
y = tg_df["y"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
print(len(X_train))
display(X_train.head(1))
print(len(X_test))
display(X_test.head(1))
print(len(tg_df))
print(tg_df["y"].unique())

rf_cls = RandomForestClassifier(max_depth=3,random_state=0).fit(X_train, y_train)

[実行結果(一部略)]

分類系のランダムフォレストモデルが作成できました。

SHAPモデル

分類系モデルを引数にして、SHAPモデルを作成します。(1行目)

決定木モデルのランダムフォレストに対してはshap.TreeExplainerを利用します。

[Google Colaboratory]

1
2
explainer = shap.TreeExplainer(rf_cls)
explainer

[実行結果]

これでSHAPモデルが作成できました。

SHAP値

SHAP値を確認します。

[Google Colaboratory]

1
2
3
shap_values = explainer.shap_values(X_test)
print(len(shap_values))
print(shap_values)

[実行結果]

分類系モデルをベースにした場合、SHAP値は各カテゴリごとの配列になり、各カテゴリに対するSHAP値を出力します。

乳がんの診断データセットは二値分類なので2つのSHAP値が出力されます。

次回は回帰系と同じように、グラフを描画してSHAP値の解釈をしていきます。

説明可能なAI⑥(具体的な貢献度を可視化)

waterfall_plot

waterfall_plotは、force_plotと表現方法が違うだけで同じように具体的な貢献度を可視化することができます。

[Google Colaboratory]

1
2
3
4
5
row_index = X_test.index.get_loc(253)
shap.plots._waterfall.waterfall_legacy(
expected_value=explainer.expected_value[0],
shap_values=shap_values[row_index,:],
features=X_train.iloc[row_index,:])

[実行結果]

force_plotでのbase_valueは、上図でE[f(X)]=22.745と表示されています。

E[f(X)]を基点として下から上へSHAP値を足し引きして、最終的にf(x)=28.55になることが確認できます。

force_plotwaterfall_plotは、表現方法が異なるだけなので好きな方を利用するようにしましょう。

説明可能なAI⑤(具体的な貢献度を可視化)

各サンプルごとの説明変数について、具体的な貢献度を可視化してみます。

force_plot

force_plot関数は、SHAP値と特徴量の貢献度を視覚化することができます。

[Google Colaboratory]

1
2
3
4
5
shap.initjs()
row_index = X_test.index.get_loc(253)
shap.force_plot(base_value=explainer.expected_value,
shap_values=shap_values[row_index,:],
features=X_test.iloc[row_index,:])

まず、force_plotを使うための前処理メソッド(initjs)を実行します。(1行目)

次に確認したいデータの位置(253行目)を指定します。(2行目)

[実行結果]

上図のbase value(横軸)は、与えられたX_test(テストデータの説明変数)における予測値の平均で、全データ共通の指標です。

それに対し、各説明変数のSHAP値を足し引きした結果、最終的に予測結果がf(x)になるという見方となります。

赤色が価格の上昇に貢献、青色が価格の減少に貢献した説明変数になります。

今回はbase valueが22.75で、各説明変数のSHAP値を足し引きした結果、28.55に予測したことが確認できます。

253行目のデータについては、RMが高くLSTATが低いので、その点では価格の上昇に貢献していますが、PTRATIOが価格の減少に貢献した結果、最終的な予測値が低くなったことが分かります。

説明可能なAI④(SHAP値の散布図)

SHAP値の散布図

dependence_plotは、特定の説明変数とSHAP値の散布図で、相関関係を確認する場合に便利です。

前回記事で上位に表示された、LSTATをdependence_plotで表示してみます。

[Google Colaboratory]

1
2
3
4
shap.dependence_plot(ind="LSTAT",
interaction_index=None,
shap_values=shap_values,
features=X_test)

indオプションで確認したい説明変数(LSTAT)を指定しています。(1行目)

[実行結果]

横軸に説明変数、縦軸に同じ説明変数のSHAP値をプロットしています。

説明変数のSHAP値と、相関関係がみられるほど、予測への影響も高くなります。

上のグラフからはLSTATが低いほどSHAP値が高く、予測の結果に大きく影響を与えることが確認できます。


またinteraction_indexを指定することで、色に対して別の説明変数を指定することができます。

interaction_indexRMを指定して実行してみます。(2行目)

[Google Colaboratory]

1
2
3
4
shap.dependence_plot(ind="LSTAT",
interaction_index="RM",
shap_values=shap_values,
features=X_test)

[実行結果]

LSTATが高くなると、RMが低くなる傾向が確認できます。

各説明変数の関係性や、どのように予測に影響しているかを確認する場合にdependence_plotはとても有効です。

説明可能なAI③(SHAPの可視化)

summary_plot(bar)

summary_plotを使うと、どの説明変数が大きく影響していたかを可視化することができます。

大局的に結果を確認する場合に便利です。

[Google Colaboratory]

1
2
3
4
shap.summary_plot(shap_values=shap_values,
features=X_test,
plot_type="bar",
max_display=5)

plot_type“bar”を指定することで、各説明変数を貢献度順に確認することができます。(3行目)

max_displayは上位項目の表示数で、今回は上位5項目まで表示しています。(4行目)

[実行結果]

横軸は平均SHAP値、縦軸は説明変数の項目になります。

縦軸の上位項目ほどモデルへの貢献度が高いことを表しており、今回のモデルではRM、LSTATの貢献度が高いことが確認できます。

summary_plot(dot)

次に、plot_type“dot”を指定して実行してみます。(3行目)

[Google Colaboratory]

1
2
3
4
shap.summary_plot(shap_values=shap_values,
features=X_test,
plot_type="dot",
max_display=5)

[実行結果]

各点がデータで、横軸にSHAP値、縦軸に説明変数の項目、色は説明変数の大小を表しています。

RMが高くなるほど予測値も高くなり、RMが低くなるほど予測値も低くなる傾向があります。

一方、LSTATが高ければ予測値は低くなり、LSTATが低ければ予測値は高くなるという傾向を視覚的に確認することができます。

説明可能なAI②(SHAP)

SHAPのインストール

SHAPライブラリのインストールを行います。

[Google Colaboratory]

1
!pip install shap

[実行結果]

正常にインストールができました。

SHAPモデルの作成

回帰モデルを引数にして、SHAPモデルを作成します。(2行目)

[Google Colaboratory]

1
2
3
import shap
explainer = shap.TreeExplainer(tree_reg)
explainer

前回記事で用意したのは決定木モデルなので、決定木モデル用のshap.TreeExplainerクラスを使っています。

このクラスは決定木以外にも、ランダムフォレストXgBoostなどで利用できます。

[実行結果]

SHAPモデルが作成できました。

SHAP値の確認

SHAP値を確認します。

[Google Colaboratory]

1
2
shap_values = explainer.shap_values(X_test)
shap_values

[実行結果]

SHAP値は、入力したデータセットと同じ次元の要素数になり、値が大きいほど予測への影響が大きくなります。

行方向に見れば特定の予測に各説明変数がどれくらい貢献したかを確認できます。

列方向に見れば予測全体でその説明変数がどれくらい貢献したかを確認できます。


SHAP値には、グラフを描画する仕組みが用意されています。

次回は各グラフを表示してみます。

説明可能なAI①(SHAP)

SHAP

SHAPは、学習済みモデルにおいて各説明変数が予測値にどのような影響を与えたかを貢献度として定義して算出するモデルです。

各データごとに結果を出力し、可視化することができます。

前準備

前準備として、ボストンデータセットを用いた回帰モデル(決定木)を作成し、予測結果を確認します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor

boston = load_boston()
df = pd.DataFrame(boston.data,columns=boston.feature_names)
df["MEDV"] = boston.target
X= df[boston.feature_names]
y = df[["MEDV"]]
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=0.3,random_state=0)

print(len(X_train))
display(X_train.head(1))
print(len(X_test))
display(X_test.head(1))

tree_reg = DecisionTreeRegressor(max_depth=3, random_state=0).fit(X_train,y_train)

[実行結果]

以上で、回帰系の決定木モデルが作成できました。

重要度

作成したモデルの説明変数ごとに重要度を表示します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt
import numpy as np

features = X_train.columns
importances = tree_reg.feature_importances_
indices = np.argsort(importances)

plt.figure(figsize=(6,6))
plt.barh(range(len(indices)), importances[indices], color="b", align="center")
plt.yticks(range(len(indices)), features[indices])
plt.show()

feature_importances_を参照し、説明変数ごとの重要度を取得しています。(5行目)

[実行結果]

このモデルでは、RMLSTATなどの重要度が高く、予測に強く影響していることが確認できます。

予測値の算出

予測値を算出します。

[Google Colaboratory]

1
2
3
X_test_pred = X_test.copy()
X_test_pred["pred"] = np.round(tree_reg.predict(X_test), 2)
X_test_pred.describe()[["RM","LSTAT","CRIM","DIS","PTRATIO","pred"]]

テストデータで予測値を算出し、結果を説明変数とマージしています。(2行目)

重要度の高い説明変数の上位5項目と、予測値(pred)を表示しています。(3行目)

[実行結果]

RMは8.7付近が最大値となっており、predの最大は50になっています。

予測値の表示

最も重要度の高かった説明変数であるRMでソートして、結果を確認してみます。

[Google Colaboratory]

1
X_test_pred.sort_values("RM")

[実行結果]

predの最大値が50だったので、RMが高いほどpredも高く、RMが低いほどpredも低く出ている傾向が見られます。

2番目に重要度が高かったLSTATはその逆で、LSTATが高いとpredは低く、LSTATが低ければpredが高くなっているようです。

このようにfeature_importances_は、モデル作成時にどのような説明変数が重要であるかを知るために大局的な指標となります。

一方SHAPは、作成したモデルの各説変数がどのように予測に寄与してしるかを知るための局所的な指標となります。

次回はSHAPを実装して予測結果を確認していきます。

分類モデルの評価⑦(まとめ)

今回は、これまで実行してきたいろいろな分類モデルをまとめて評価します。

PR曲線の可視処理を関数化

まず、前回実行したPR曲線の可視化処理を関数化します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def plot_pr_curve(y_true,proba):
precision, recall, thresholds = precision_recall_curve(y_true, proba[:,0], pos_label=0)
auc_score = auc(recall, precision)

plt.figure(figsize=(12, 4))
plt.subplot(1,2,1)

plt.plot(recall, precision,label=f"PR Curve (AUC = {round(auc_score,3)})")
plt.plot([0,1], [1,1], linestyle="--", color="red", label="Ideal Line")

tg_thres = [0.3,0.5,0.8]
for thres in tg_thres:
tg_index = np.argmin(np.abs(thresholds - thres))
plt.plot(recall[tg_index], precision[tg_index], marker = "o",markersize=10, label=f"Threshold = {thres}")

plt.legend()
plt.title("PR curve")
plt.xlabel("Recall")
plt.ylabel("Precision")
plt.grid()

plt.subplot(1,2,2)

plt.plot(np.append(thresholds, 1), recall, label = "Recall")
plt.plot(np.append(thresholds, 1), precision, label = "Precision")
plt.xlabel("Thresholds")
plt.ylabel("Score")
plt.grid()
plt.legend()

plt.show()

スケーリング

決定木以外のアルゴリズムのために、データのスケーリングを行います。

[Google Colaboratory]

1
2
3
4
5
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

各モデルの定義

辞書型で各モデルの定義を行います。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier

models = {"Logistic Regression":LogisticRegression(),
"Linear SVM":SVC(kernel="linear",probability=True,random_state=0),
"Kernel SVM":SVC(kernel="rbf",probability=True,random_state=0),
"K Neighbors":KNeighborsClassifier(),
"Decision Tree":DecisionTreeClassifier(max_depth=3,random_state=0),
"Random Forest":RandomForestClassifier(max_depth=3,random_state=0)}

SVCクラスでpredict_probaを使用するために、probabilityをTrueにします。

[Google Colaboratory]

1
data_set = {"Train":[X_train_scaled,y_train],"Test":[X_test_scaled,y_test]}

事前準備は以上で終了です。

各モデルの評価

各モデルごとに以下の処理を行います。

  1. モデルの構築・学習(4行目)
  2. 予測(11行目)
  3. データセットごとに分類レポートを出力(13~16行目)
    output_dictをTrueにすることで辞書型で出力し、それをデータフレームにしています。
  4. テストデータに関してのPR曲線の可視化(20行目)

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for model_name in models.keys():
print()
print(f"{model_name} Score Report")
model = models[model_name].fit(X_train_scaled, y_train)

for data_set_name in data_set.keys():

X_data = data_set[data_set_name][0]
y_true = data_set[data_set_name][1]

y_pred = model.predict(X_data)

score_df = pd.DataFrame(classification_report(y_true, y_pred, output_dict=True))
score_df["model"] = model_name
score_df["type"] = data_set_name
display(score_df)

if data_set_name == "Test":
proba = model.predict_proba(X_data)
plot_pr_curve(y_true, proba)

各モデルの結果は次のようになりました。

[ロジスティック回帰の評価結果]

[線形SVMの評価結果]

[カーネルSVMの評価結果]

[K近傍法の評価結果]

[決定木の評価結果]

[ランダムフォレストの評価結果]

スコアを見ると、各アルゴリズムの評価に大きな差はないようです。

決定木やロジスティック回帰のF1値が高く出ているので、今回のデーセットに対しては、シンプルなアルゴリズムでもうまく分類できていると言えます。

分類レポートのスコアは全て閾値が50%として出力されているので、PR曲線などを見て閾値を調整すると評価結果が変わります。

いろいろな閾値を試してみると良いかもしれません。