【機械学習】PDPBoxでPartial Dependency Plotを描いて機械学習モデルを解釈する

こんにちは。今日は、統計・機械学習モデルの解釈の手助けとなる、Partial Dependency Plot (PDP)の実装方法をまとめておこうと思います。

Contents

Partial Dependency Plotとは?

線形モデルでは、目的指標と説明変数の関係は、係数を見れば明確です。しかし、決定木系のアルゴリズムだったりすると、通常「変数重要度」という形で目的指標の判別・予測に有効な説明変数を知ることができますが、それぞれの説明変数が変化したときに目的変数がどのように変化するのか、は直感的には分かりません。

これを解決する方法が今回紹介するPartial Dependency Plotになります。

Partial Dependency Plot (PDP)とは
  • ある特徴量と目的指標の関係を、可視化する方法
  • (実装の特徴として)構築済のモデルを使って計算する(ため、計算コストが低い)

Partial Dependencyの数学

Partial Dependenceは以下の考え方で計算されます。

fは機械学習モデル、xsは、目的指標との関係を確認した変数群(通常1つ、もしくは2つ)、xcは、それ以外の(今回興味の対象ではない)変数群を指しています。

モデルを、興味の対象である変数とそれ以外の変数群の関数として表現した時、変数Sと目的指標の関係は、各Sの値毎に、モデルにより得られる予測結果をデータセットの数だけある変数Cに渡って足しあげ(て、平均)ることで得られます。

このように計算することで、変数Cの影響を取り除いて(周辺化して)、目的指標をxsのみの関数として表現できるようになります。

Partial Dependence Function

上のマーカーの部分を、数式で表現すると、以下のようになります。nはデータの数で、周辺化のためにあるXsの値に対して、目的指標値を算出したいときは、Xsを固定してデータセットの数だけあるXcの組み合わせを変化させて予測値を出し、それをデータセットの数で平均をとっています。

Partial Dependency Plotで大事なのは、「Xsの値が変化するにつれて目的指標がどのように変化するか」ということなので、上記の計算で算出された平均値をXsの値の変化に渡ってプロットした時の「変化の仕方」が重要です。

Partial Dependency PlotのPython実装方法

Partial Dependency Plotは、sklearnにもデフォルトで関数が組み込まれていますが、なぜかGradient Boostingモデルにしか対応していないようで、Random Forestなどのベーシックなモデルには直接適用できないようでした・・

https://scikit-learn.org/stable/modules/partial_dependence.html

そこで、Random Forestなどでも利用できるライブラリなどがあったりしないか探してみると・・ありました!

Kaggleのこちらのページで紹介されている、pdpboxというライブラリで実現できるようです。こちら、githubの更新は最近されていなくてStar/Forkもそこまで多いわけではないようです。あんまりみんな使ってないのかな・・?

https://www.kaggle.com/dansbecker/partial-plots

というわけで、pdpboxの導入と可視化までの手順を書いておきます。

conda install -c conda-forge pdpbox

簡易に、Bostonの住宅価格データセットを使って、Random Forestモデルを構築します。

from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import RandomForestRegressor
from sklearn.inspection import permutation_importance
from sklearn.model_selection import KFold, StratifiedKFold, GridSearchCV, train_test_split,cross_val_score
from sklearn.metrics import mean_squared_error, roc_auc_score, r2_score

from sklearn.ensemble.partial_dependence import plot_partial_dependence
from sklearn.ensemble.partial_dependence import partial_dependence

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

## データセットの読み込み
boston = load_boston()

data_X = pd.DataFrame(boston.data, columns=boston.feature_names)
data_y = boston.target

data_X.head()
## モデル構築
from sklearn.ensemble import RandomForestRegressor

X_train, X_test, y_train, y_test = train_test_split(data_X, data_y, test_size=0.2, random_state=440)

clf = RandomForestRegressor(n_estimators=30,max_depth=9)
#print("Cross Val Score (MSE):"+str(cross_val_score(clf, data_X, data_y, cv=5, scoring='r').mean()))

clf = clf.fit(X_train, y_train)
pred = clf.predict(X_test)

print(mean_squared_error(y_test, pred))

df_importance = pd.DataFrame(zip(X_train.columns, clf.feature_importances_),columns=["Features","Importance"])
df_importance = df_importance.sort_values("Importance",ascending=False)

plt.figure(figsize=(10,5))
sns.barplot(x="Importance", y="Features",data=df_importance,ci=None)
plt.title("Gini Importance")
plt.tight_layout()

さて、次が構築したモデルを使ってPDP Plotを出力するコードです。

PDP Boxでは複数メソッドのが用意されているので、それぞれからどういうことが分かるか試してみます。

PDP Isolate Plot

これは、ある特定の変数と目的指標の変化の関係をプロットするメソッドです。下の例だと、LSTAT(低所得者割合)の上昇は、住宅価格が低下する方向に寄与していることが分かります。(かつ、20%以上になると、住宅価格への寄与度は横ばいになる)

from pdpbox import pdp, get_dataset, info_plots

# Create the data that we will plot
fig = plt.figure(figsize=(14,5))

pdp_goals = pdp.pdp_isolate(model=clf, dataset=X_train, model_features=X_test.columns, feature='LSTAT')
# plot it
pdp.pdp_plot(pdp_goals,'LSTAT')
plt.show()

PDP Interact Plot

これは、2つの変数の値の組み合わせが目的指標をどのように変化させたかを知るために使います。

fig = plt.figure(figsize=(14,5))
pdp_score = pdp.pdp_interact(model=clf, dataset=X_train, model_features=X_test.columns, features=['LSTAT','RM'])
pdp.pdp_interact_plot(pdp_score,['LSTAT',"RM"],x_quantile=True,ncols=2,plot_pdp=False)
plt.show()

これ、実は実行すると引数エラーが出ます。調べてみると、どうやらmatplotlibとpdpboxのバージョン差異によるもののようです。それぞれのバージョンを互換性のある組み合わせに変更すると動くようです・・詳しくはこちら。

https://forums.fast.ai/t/pbpbox-error-on-pdp-interact-plot/28468

一方で、上記の等高線表示をGrid表示にするとエラーなく表示可能です。

fig = plt.figure(figsize=(14,5))
pdp_score = pdp.pdp_interact(model=clf, dataset=X_train, model_features=X_test.columns, features=['LSTAT','RM'])
pdp.pdp_interact_plot(pdp_score,['LSTAT',"RM"],plot_type='grid',x_quantile=True,ncols=2,plot_pdp=True)
plt.show()

以上、PDPBoxライブラリを利用した、Partial Dependency Plotの可視化方法のまとめでした!

この記事が少しでもお役に立ちましたら、下のいいねボタンをポチっていただけると励みになります!

おしまい

この記事を気に入っていただけたらシェアをお願いします!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT US
Yuu113
初めまして。ゆうたろうと申します。 兵庫県出身、東京でシステムエンジニアをしております。現在は主にデータ分析、機械学習を活用してビジネスモデリングに取り組んでいます。 日々学んだことや経験したことを整理していきたいと思い、ブログを始めました。旅行、カメラ、IT技術、江戸文化が大好きですので、これらについても記事にしていきたいと思っています。