まずは蝋の翼から。

学んだことを書きながら確認・整理するためのメモブログ。こういうことなのかな?といったことをふわっと書いたりしていますが、理解が浅いゆえに的はずれなことも多々あると思うのでツッコミ歓迎

SHAP valueを使ってみる

SHAP valueを実際に試してみる。コードは以下の記事のものを拝借。

Shapを用いた機械学習モデルの解釈説明 - Qiita

なお、データはkaggleのHouse Pricingコンペデータをテキトーに加工して作っている。SalePrice(対数化済)を目的変数として、簡易化のため変数重要度がTOP5を試す。

また、モデルはRandomForestを使う。

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

# for visualization
import matplotlib.pyplot as plt
import seaborn as sns

# import data
train = pd.read_csv(‘../input/train.csv’)
test = pd.read_csv(‘../input/test.csv’)

# string label to categorical values
# 全objectタイプをlabel encode
from sklearn.preprocessing import LabelEncoder

for i in range(train.shape[1]):
    if train.iloc[:,i].dtypes == object:
        lbl = LabelEncoder()
        lbl.fit(list(train.iloc[:,i].values) + list(test.iloc[:,i].values)) #ここにある項目で変換する
        train.iloc[:,i] = lbl.transform(list(train.iloc[:,i].values))
        test.iloc[:,i] = lbl.transform(list(test.iloc[:,i].values))

# keep ID for submission
train_ID = train[‘Id’]
test_ID = test[‘Id’]

# split data for training
y_train = train[‘SalePrice’]
X_train = train.drop([‘Id’,‘SalePrice’], axis=1)
X_test = test.drop(‘Id’, axis=1)


# dealing with missing data
#missing多い場合はdrop、少ない場合はmean
X_train = X_train.drop([‘LotFrontage’,‘MasVnrArea’,‘GarageYrBlt’], axis=1)
X_train = X_train.fillna(X_train.median()) 


X_test = X_test.drop([‘LotFrontage’,‘MasVnrArea’,‘GarageYrBlt’], axis=1)
X_test = X_test.fillna(X_train.median())

# add SF
X_train[‘TotalSF’] = X_train[‘TotalBsmtSF’]+X_train[‘1stFlrSF’]+X_train[‘2ndFlrSF’]
X_test[‘TotalSF’] = X_test[‘TotalBsmtSF’]+X_test[‘1stFlrSF’]+X_test[‘2ndFlrSF’]

y_train = np.log(y_train)

scikit-learnのRandomForest.feature_importances_

まずはじめに、一般的によくみる、giniベースのアルゴリズムであるscikit-learnのfeature_importances_関数を用いて変数重要度を見る。

# feature importance using random forest
from sklearn.ensemble import RandomForestRegressor
rf = RandomForestRegressor(n_estimators=80, max_features=‘auto’)
rf.fit(X_train, y_train)
print(‘Training done using Random Forest’)

ranking = np.argsort(-rf.feature_importances_) #降順で配列のindexを返す

# use the top 5 features only
X_train = X_train.iloc[:,ranking[:5]]
X_test = X_test.iloc[:,ranking[:5]]

f:id:chito_ng:20190507205831p:plain

SHAP

SHAPで各特徴量が予測にどう効いたかを見てみる。

まずは、各特徴量の値に対しての、予測への影響度であるSHAP値を算出。

import shap
shap.initjs() # jupyterの表示用

explainer = shap.TreeExplainer(rf)
shap_values = explainer.shap_values(X_train)

SHAP 要約(平均)

summary_plotで各特徴量の寄与度絶対値平均を図示。

これはginiベースのものと概ね同じになった。 ただし、「なにを重要とするか」という指標が各アルゴリズムで異なるため、必ずしも同じような値になるわけではない。

shap.summary_plot(shap_values, X_train, plot_type="bar")

f:id:chito_ng:20190507210033p:plain

SHAP 要約(個々)

summary_plotで各特徴量の、個々の予測値(点1つ)に対する寄与度を図示。色は特徴量の値の大きさ。

shap.summary_plot(shap_values, X_train)

f:id:chito_ng:20190507210547p:plain

インスタンス(各レコード)が点となっていて、値の大小で色づけされている。OverrallQualをみてみると貢献度(SHAP value)が上がるほど色が大きく(赤)なっていることからキレイに相関が出ているし、他でも概ねその傾向がある。

ある予測値に対しての貢献度の内訳

force_plotで、あるインスタンス(以下のコードでは1番目のレコード)に対する各特徴量の予測値に対する寄与の内訳を表している。赤が予測値を増加させる特徴量、青が減少させる特徴量となる。base valueで示されている数値は予測値の期待値。黒太字の数値は予測値。棒の長さはどの程度の寄与をしたかの絶対値、となる。

shap.force_plot(explainer.expected_value, shap_values[0,:], X_train.iloc[0,:])

f:id:chito_ng:20190507210927p:plain

実際の値で例示すると、赤の合計は大体0.23程度で青の合計が0.04程度の長さとなっているので、12.21(output value) ≒ 12.02(base value) + 0.23(赤) - 0.04(青)になっていることを考えればわかりやすい。

個々で見ていくと、OverrallQualの長さは0.17程度だが、これはOverrallQualが7になることでSalePrice(対数化済)が0.17程度増加した、と考えることができる。

全予測値に対してのある特徴量と出力値の関係

先程はforce_plotである1つのインスタンスについての内訳を見たが今回は全インスタンスを一気に見る。 以下の図ではYearBuiltとoutput value(予測値)について出力している。図にカーソルを合わせると個々の内訳が見れる。また、x,y軸は任意のものに変えることができる。

f:id:chito_ng:20190507211814p:plain

shap.force_plot(base_value=explainer.expected_value, shap_values=shap_values, features=X_train)

色の赤/青は、YearBuiltがある値のときにYearBuiltが正に寄与したか負に寄与したかを表している。同じYearBuilt値なのに赤/青が混在している部分では正に寄与したり負に寄与したりとなっているのは、他の特徴量の影響によるもの。

全予測値に対してのある特徴量の貢献度の内訳

ある特徴量の値とSHAP valueの関係。

shap.dependence_plot(ind="YearBuilt",interaction_index='YearBuilt', shap_values=shap_values, features=X_train)

f:id:chito_ng:20190507213055p:plain

上図をみると、値が大きくなるほどSHAP valueも増加していることがわかる。

また、特徴量同士の関係もみることができる。

shap.dependence_plot(ind='YearBuilt',interaction_index='TotalSF', shap_values=shap_values, features=X_train)

f:id:chito_ng:20190507213451p:plain

上手では縦軸にYearBuiltのSHAP value、横軸にYearBuilt、色としてTotalSFの値が表示されている。YearBuiltの値が変わってもTotalSFの大小の傾向はあまり変わらない。ただ、なんとなく1950頃を境に、1950以前はTotalSFが高いとYearBuiltのSHAP valueは高くなっているが、以降ではotalSFが低いとYearBuiltのSHAP valueは高くなっているようにも見える。

参考

https://orizuru.io/blog/machine-learning/shap/

続・機械学習モデルを解釈する方法 SHAP value - 子供の落書き帳 Renaissance

機械学習モデルを解釈する指標SHAPについて – 戦略コンサルで働くデータサイエンティストのブログ