SHAP valueを使ってみる
SHAP valueを実際に試してみる。コードは以下の記事のものを拝借。
なお、データは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]]
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")
SHAP 要約(個々)
summary_plot
で各特徴量の、個々の予測値(点1つ)に対する寄与度を図示。色は特徴量の値の大きさ。
shap.summary_plot(shap_values, X_train)
インスタンス(各レコード)が点となっていて、値の大小で色づけされている。OverrallQualをみてみると貢献度(SHAP value)が上がるほど色が大きく(赤)なっていることからキレイに相関が出ているし、他でも概ねその傾向がある。
ある予測値に対しての貢献度の内訳
force_plot
で、あるインスタンス(以下のコードでは1番目のレコード)に対する各特徴量の予測値に対する寄与の内訳を表している。赤が予測値を増加させる特徴量、青が減少させる特徴量となる。base valueで示されている数値は予測値の期待値。黒太字の数値は予測値。棒の長さはどの程度の寄与をしたかの絶対値、となる。
shap.force_plot(explainer.expected_value, shap_values[0,:], X_train.iloc[0,:])
実際の値で例示すると、赤の合計は大体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軸は任意のものに変えることができる。
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)
上図をみると、値が大きくなるほどSHAP valueも増加していることがわかる。
また、特徴量同士の関係もみることができる。
shap.dependence_plot(ind='YearBuilt',interaction_index='TotalSF', shap_values=shap_values, features=X_train)
上手では縦軸にYearBuiltのSHAP value、横軸にYearBuilt、色としてTotalSFの値が表示されている。YearBuiltの値が変わってもTotalSFの大小の傾向はあまり変わらない。ただ、なんとなく1950頃を境に、1950以前はTotalSFが高いとYearBuiltのSHAP valueは高くなっているが、以降ではotalSFが低いとYearBuiltのSHAP valueは高くなっているようにも見える。
参考
https://orizuru.io/blog/machine-learning/shap/