ローリングコンバットピッチなう!

AIとか仮想化とかペーパークラフトとか

ランダムフォレスト分類器(scikit-learn)の結果をLIMEとSHAPで解釈する

[technology]Explainable AI(LIME,SHAP)をscikit-learnと組わせて試す

Explainable AI

最近はAIの推論精度の他に、「AIはブラックボックスなので、精度が高くても判断根拠が人間に理解できない。これでは使えない」みたいな話で盛り上がってきて、説明可能なAIというのがバズり始めています。最近仕事でもそれに近い様な話も出てきていて(それよりちゃんとデータ蓄積して精度高いモデル構築するのが先なんだけど..)、以前より気になっていたLIMEとSHAPという手法を試しに動かしてみる事にしました。

いずれもオープンソースで公開されていて、scikit-learn等で組んだモデルと組み合わせて使う事が出来ます。

github.com
github.com

メタボ判定シミュレーション

既にネットを漁れば、LIMEやSHAPを試した例は見つかって、おなじみのirisだったり、タイタニックの生存者推定だったり、赤ワインの良品判定だったり、見つかるのですが、できれば自分で何かデータも作ってみたいなと色々考えました。
ふと目に止まったのがメタボリック症候群の判定基準。これは厚生労働省が定義を決めていて腹囲や中性脂肪、HDL、血糖値、血圧のいずれかがある基準を上回る(HDLだけは基準値を下回る)とメタボリック症候群と判定されます。

男性: 腹囲85cm以上、女性: 腹囲90cm以上
中性脂肪 150mg/DL以上
HDLコレステロール 40mg/DL未満
収縮期(最大)血圧 130mmHg以上
拡張期(最小)血圧 85mmHg以上
血糖値 110mg/DL以上

ポイントは腹囲のスレッショルドのみが男女で異なる事です。
そこで上記のデータを乱数を使ってあるレンジで発生させ、厚労省の基準に従ってメタボ、非メタボのラベルを付けたデータを自動生成しました。
約20000点生成、8割をトレーニングに使用しています。メタボ、非メタボのデータ生成バランスは非メタボが数割多いのですが、まあ許容範囲かなと思ってそのままscikit-learnでランダムフォレスト分類器に学習させました。結果として98%以上の分類精度を達成しています。
学習の結果生成された分類器を使い、LIMEとSHAPにそれぞれ分類器の特性を解析させています。

ソースはgistを貼っただけなので、見にくいですが下記です。
LIMEとSHAPの結果がgist上で上手く表示出来なかったので、画面キャプチャしたものを下に貼りました。



LIMEの試行

LIMEでメタボ判定されたデータと非メタボ判定されたデータを解釈した結果。

メタボ判定されるデータと非メタボ判定されるデータは下記の様に作っています。
メタボ判定されるデータは性別=男性、腹囲が86.2cm,中性脂肪が140.2mg/DL,HDLコレステロール45.8mg/DL,最大血圧118mmHg,最小血圧80mmHg,血糖値90mg/DLです。
非メタボ判定されるデータはこれに対して腹囲のみを84.2cmにしたものです。つまりメタボか非メタボかの差は腹囲のみです。(性別は腹囲の境界値に影響する。女性の場合は86.2cmでも非メタボになる)

x_test2 = np.array([[1.0,86.2,140.2,45.8,118,80,90]])
x_test3 = np.array([[1.0,84.2,140.2,45.8,118,80,90]])

LIMEの解析結果を見ると、メタボ判定自体は正しく推定されています。その理由として腹囲が上がっているのは正しいのですが、例えば同じ中性脂肪値がメタボ判定側では非メタボ要因に非メタボ判定側ではメタボ要因として上げられています。最小血圧も同じで、同じ値が判定結果に応じて異なる解釈になっています。
このあたり、なかなか厳密な境界条件の自動解析は難しい様です。上記の2データは各数値を境界条件のかなり近くの置いているからかもしれません。

SHAPの試行

SHAPでメタボ判定されたデータを解釈した結果。

2つのスコアが可視化されていますが、これは同じデータに対してメタボ、非メタボという2つの分類クラスに対するスコアと、そのスコアに対する各説明変数の寄与度を示しています。
このデータは「女性、腹囲88.8cm、中性脂肪 151mg/DL,HDLコレステロール 48.6mg/DL,血圧113/73mmHg,血糖値83.9mg/DL」で、正しい分類結果は「メタボ」になるので、SHAPで可視化されたスコア(メタボが1.0付近、非メタボが0.0付近)は正しいのですが、メタボ判定の主因として腹囲と中性脂肪が上がっています。
女性なので、正しくは中性脂肪だけがメタボの基準を満たしており、腹囲は非メタボ判定なのですが、男性と女性における腹囲の基準値に違いまではこの手法では判りません。
各説明変数毎の分類結果への寄与度を個別に求めているため、腹囲については85〜90cmあたりに境界条件があることは判るものの、性別でこの境界条件が変わる事までは機械的には抽出出来ないのだと理解しています。

性別パラメータは、腹囲とは独立して評価されて男性の方が女性よりメタボの可能性が高い..という統計論的な解釈が成されます。
あくまでも全体の傾向で見ているので、これはいくらデータ量を増やしても正確に境界条件を抽出することは出来ないという事でしょう。

本質的にこういうものなのですが、Explainable AIというものに過剰な期待をしている人が居ると、説明するのが難儀だなあと思う今日この頃。
説明変数毎の寄与度を機械的に推定してくれるだけでも、十分に価値はあるとエンジニア的には思います。

ところでSHAPについてはscikit-learnの分類器に使う場合、コーディング的にgithubの公式リポジトリに載っているxgboostを使ったチュートリアルと少し異なる部分があります。

xgboostを使った場合、

explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X)

# visualize the first prediction's explanation (use matplotlib=True to avoid Javascript)
shap.force_plot(explainer.expected_value, shap_values[0,:], X.iloc[0,:])

scikit-learnの分類器を使った場合、

explainer = shap.TreeExplainer(rf_clf)
shap_values = explainer.shap_values(x_train[0:5000])
shap.force_plot(explainer.expected_value[1], shap_values[1][6],x_train[6] ,feature_names = feature_names)
shap.force_plot(explainer.expected_value[0], shap_values[0][6],x_train[6],feature_names = feature_names)

どちらもTreeExplainerのインスタンスを分類器のモデルを渡して生成後、そのインスタンスに解析対象データ(=トレーニングデータ)を渡し、shap_valuesを求めるところまでは同じ、scikit-learnを使った場合にチュートリアルと同じコーディングをするとエラーが出ます。
これはscikit-learnのランダムフォレスト分類器に様に、クラス毎にスコアを吐き出す様なモデルでは、クラス毎にshap_valuesも算出され、shap_valuesがクラス毎の配列になっているためです。同様にexplainer_expected_valueもクラス毎に配列になっており、force_plot等を実施する場合にはクラスのインデックスをexplainer_expected_valueとshap_valuesに指定する必要がある様です。

これは公式ドキュメントのclassshap.TreeExplainerのところに下記の様に説明されています。
これを理解してforce_plot()を動かすのに少し苦労しました。

For models with a single output this returns a matrix of SHAP values (# samples x # features). Each row sums to the difference between the model output for that sample and the expected value of the model output (which is stored in the expected_value attribute of the explainer when it is constant). For models with vector outputs this returns a list of such matrices, one for each output.

jupyter notebookは下記githubリポジトリで公開しています。
github.com


【参考文献】
LIMEのドキュメント
Local Interpretable Model-Agnostic Explanations (lime) — lime 0.1 documentation

SHAPのドキュメント
Welcome to the SHAP documentation — SHAP latest documentation

christophm.github.io

LIMEの試行例
kamonohashiperry.com


SHAPの試行例
https://orizuru.io/blog/machine-learning/shap/orizuru.io