初カキコ…ども…(RのICEboxパッケージを利用した機械学習モデルの要因分析のススメ)

  • 2
    Like
  • 0
    Comment

はじめに ー初カキコ…ども…ー

初カキコ…ども…

俺みたいな人事でデータ分析してる腐れ野郎、他に、いますかっていねーか、はは(ry

そんなわけで、今回はアンサンブルな機械学習モデルにおける要因分析を行うためのパッケージとして、RのICEboxパッケージを紹介したいと思います。

実務上、高精度なアンサンブルな機械学習モデルを使いつつも要因を把握したい時がある ーあなた、卑怯者です!!ー

自分は人事でデータ分析をやっているのですが、他の人事の方に分析結果を説明する際によく議論になるのが、アンサンブルな機械学習モデルの結果のブラックボックス性です。

最近実際に出会ったのは、予測モデルの結果、ある人が退職する確率が高いのは分かったけれど、その人のどの要素が原因になっているかがよくわからないから、アクションにつながらないと言ったようなことです。

こういったときにまず取りうるのがロジスティック回帰のような回帰モデルを使うというアプローチだと思います。しかし高精度であることも同時に求められる時もあり、なかなかロジスティック回帰を使うことに切り替えるのも難しかったりします。

これらのことに悩むことも多く、最近機械学習モデルの要因分析はどのようにできうるかを調べていたところ、R上での解決方法として3つのパッケージを見つけました。

これらは基本的に部分従属プロット(partial dependency plot)をもとに、要因を把握するというものになります。

その中でも個人的にはICEboxが特にオススメなのですが、まずは一つ一つ概要を見ていきたいと思います。

edarfは基本的にRandom Forestを前提としているので他のモデルが使いたい時辛い ーこういう時どんな顔すればいいのか分からないのー

edarfパッケージに関しては既に、わかりやすくまとめてくれた方の記事があるのでそちらを紹介したいと思います。

edarf パッケージ:ランダムフォレストで探索的データ分析

ここでポイントとなるのは、

ランダムフォレスト系のパッケージ(randomForest, randomForestSRC, ranger, party)に対して,統一的なインターフェイスで

この部分です。仮に勾配ブースティングを使っているけれど、要因分析をしたいと行った時などはこちらのパッケージが利用できず、他の方法を探す必要があります。

またRadom Forestであっても機械学習フレームワークのcaretパッケージを通してrandomForestパッケージやrangerパッケージを使ったモデルをもとに使うことができないのは個人的にはネックだと感じる点です。

#データ準備・ライブラリ読み込み
library(randomForest)
library(caret)
library(doParallel)
library(kernlab)
data(spam)


#モデリング
cl <- makeCluster(4) #並列化設定
registerDoParallel(cl)

fitControl <- trainControl(method = "cv", number = 5) #CV設定
tune_grid  <-  expand.grid(mtry = (10:20)) #グリッドチェーン設定
rf_fit_caret <- train(type ~ .,data = spam, method = "rf", 
                   trControl = fitControl, 
                   tuneGrid = tune_grid) #学習

stopCluster(cl)#並列化終了

#部分従属プロット
pd <- partial_dependence(rf_fit_caret, data = spam, vars = "hp")

## UseMethod("partial_dependence", fit) でエラー: 
##   'partial_dependence' をクラス "c('train', 'train.formula')" のオブジェクトに適用できるようなメソッドがありません 

考えられる原因としてはcaretパッケージからrandomForestを使う際でモデル式が内部的(?)に変わっている点が考えられます。

#randomForestパッケージ直使い
library(randomForest)
library(kernlab)
data(spam)

rf_fit <- randomForest(type ~ ., spam)
rf_fit


##Call:
## randomForest(formula = type ~ ., data = spam) #ここが異なる
##               Type of random forest: classification
##                     Number of trees: 500


rf_fit_caret$finalModel #caretパッケージからrandomForest呼び出したモデル

##Call:
## randomForest(x = x, y = y, mtry = param$mtry) #ここが異なる
##               Type of random forest: classification
##                     Number of trees: 500

とはいえ仮にrandomForestパッケージ等を直接呼び出しモデリングしているような場合は、edarfパッケージを使うことは一つの選択としておすすめできます。

mlrは日本語の変数名が使えないのがネック ーこっちの事情も知らずに、エラーはいけませんよー

mlrパッケージはRにおける機械学習フレームワークのようなもので、caretパッケージに似ています。

使い方は以下のリンクが非常に解りやすくまとまっています。
mlrパッケージによる予測モデルの構築・評価

mlrは厳密には機械学習の要因分析を行うパッケージではないため、もちろんモデリングから精度の評価まで様々なことが可能です。部分従属プロットに関してはgeneratePartialDependenceDataplotPartialDependence等の関数を使うことで部分従属プロットを描くことが可能です。

詳細は公式サイトの以下のページを参照して下さい。
Exploring Learner Predictions

ただ、mlrパッケージはデータフレームに日本語の変数名が含まれていると、モデリングの準備段階で以下のエラーを吐いてしまいます。

library(mlr)
library(kernlab)#spamデータの読み込みのため
data("spam")#spamデータ読み込む

names(spam)[1] <- "メイク" #あえて日本語名に変える

## モデリングのためのtask定義
task = makeClassifTask(id = "tutorial", data = spam, target = "type")

## makeClassifTask(id = "tutorial", data = spam, target = "type") でエラー: 
##  Assertion on 'data' failed: Columns must be named according to R's variable naming rules.

こちらはデータフレームの変数名から、日本語名を半角のアルファベットに書き換えてあげれば動きます。しかしそういった処理が面倒な場合は別の方法を検討する必要が発生します。

以上の問題があるにせよ、データフレーム内の変数名に日本語が含まれない、あるいは仮に含まれたとしても書き換えが面倒では無い方には、mlrは非常にたくさんの事ができるのでおすすめです。

ICEboxは機械学習モデルさえ作ればどんなパッケージを応用して作ったモデルでもおそらく可能です ーデータ分析は爆発だ!ー

ここまで見てきたedarfmlrの両パッケージではできないことをまとめると以下の通りになります。

  • edarfでできないこと
    • caretパッケージのような機械学習フレームワークを利用したモデルの部分従属プロット
    • Random Forest以外の手法によるモデルの部分従属プロット
  • mlrでできないこと
    • 日本語の変数名が含まれるデータの分析

これらの問題を解決できるのがICEboxパッケージです。

ICEboxパッケージはImplements Individual Conditional Expectation (ICE) plotsを行うことのできるパッケージで、部分従属プロット(partial dependence plot)を改良したものになるようです。

こちらの理論的な部分は以下の論文にまとまっています。
Peeking Inside the Black Box: Visualizing Statistical Learning with Plots of Individual Conditional Expectation

ICEboxパッケージで要因分析する際にはice関数というものを使うのですが、この関数内で行っているのは事前に用意したモデルをつかってpredict関数で予測させてその結果を使ってプロット用のデータを準備するという形になっているため、predictに当てはめることが可能なモデル(オブジェクト)さえ準備できれば良いということになります。

では次にRで実際にどのように行うかというのを紹介したいと思います。

ICEboxを使った要因分析の実際 ーこの実例を見よー

それではこれから、実際にICEboxを使った要因分析を行ってみたいと思います。

まずはデータの準備とライブラリの読み込みです。

library(randomForest)
library(xgboost)
library(caret)
library(doParallel)#並列化に利用
library(kernlab)#スパムデータのため
data(spam) #スパムデータを利用

次にcaretパッケージを利用してモデリングを行います。

ここではRandom Forestと勾配ブースティングのモデルをそれぞれ作り、要因分析を行ってみます。今回チューニングは適当です。

##random forest
cl <- makeCluster(4) #並列化設定
registerDoParallel(cl)

###学習設定
fitControl <- trainControl(method = "cv", number = 5, classProbs = TRUE)
tune_grid  <-  expand.grid(mtry = (10:20))
###学習
rf_fit_caret <- train(type ~ .,data = spam, method = "rf", #randomForestパッケージを利用
                   trControl = fitControl, 
                   tuneGrid = tune_grid)

stopCluster(cl)

##xgboost
cl <- makeCluster(4) #並列化設定
registerDoParallel(cl)

###学習設定
fitControl <- trainControl(method = "cv", number = 5, classProbs = TRUE)
tune_grid <- expand.grid(nrounds = 500,
                        eta = c(0.01, 0.1),
                        max_depth = c(2, 6, 10),
                        gamma = 0, 
                        colsample_bytree = 1, 
                        min_child_weight = 1)
###学習
xg_fit_caret <- train(type ~ .,data = spam, method = "xgbTree", #xgboostパッケージを利用
                      trControl = fitControl, 
                      tuneGrid = tune_grid)

stopCluster(cl)

これでモデリングが終わりました。

ここで作ったモデルをICEboxパッケージの関数を利用して要因分析を行いましょう。

library(ICEbox)
X <- spam #説明変数だけ取り出す
X$type <- NULL
y <- spam$type #目的変数だけ取り出す

par(mfrow = c(1,2))#プロット時のレイアウト設定

#random forestの要因分析
rf_ice = ice(object = rf_fit_caret, #random forestのモデルを指定
             X = X, #説明変数のデータフレーム
             predictor = "hp", #検討したい要因
             logodds = TRUE, #プロット時の縦軸をlog-odds取るかどうか
             frac_to_build = 0.5, #プロットに使うデータの全体に対する割合
             predictfcn = function(object, newdata){ 
               predict(object, newdata, type = "prob")[, 2] #予測モデルの当てはめ方を指定
            })

plot(rf_ice, #iceオブジェクトを指定
     x_quantile = FALSE, #x軸を割合表示するかどうか
     frac_to_plot = 0.3 #描くカーブの数
     )

#xgboost
xg_ice = ice(object = xg_fit_caret, 
             X = X, 
             predictor = "hp", 
             logodds = TRUE,
             frac_to_build = 0.5,
             predictfcn = function(object, newdata){ 
               predict(object, newdata, type = "prob")[, 2]
            })

plot(xg_ice, 
     x_quantile = FALSE, 
     frac_to_plot = 0.3)

そうするとこんな図が出てきます(左がRandom Forest、右が勾配ブースティング)。

Rplot.png

これを見ると、どちらのモデルによってもhpの値が大きくなるほど、目的変数の(スパムメールである)可能性が低くなることが分かります。また薄いグレーの線を確認することで、どの程度ばらつきうるかも分かるようです。

今回は一つの変数のみしか見ませんが、同じ要領で各変数ごとにプロットをすることで、各説明変数と目的変数の関係性を可視化でき、要因を把握することが可能になります。

ICEboxのちょっとがっかりなところ ー見なかったことにしようー

汎用性があってとても良さそうなICEboxですが、もちろん欠点もあります。

データサイズが大きくなると計算に時間がかかる

ice関数がiceオブジェクトを返すまでの時間が非常に長くなります。

計算時間を求めてみたところデータをフルに使ってice関数を実行すると156秒かかりました。

一方edarfパッケージ内のpartial_dependence関数はデータをフルで使っても12秒くらいです。

もちろんやっていることが全く一緒なわけではないので、一概に比較できないのですがなかなか時間がかかります。

そのためにice関数の中にはfrac_to_buildの引数があると思うのですが、情報量が少なくなるのはネックだと思います。

プロットにオシャレ感がない

edarfパッケージでプロットさせるとggplot2を使ったいい感じに出力してくれるのですが、ICEboxはデフォルトの出力でちょっと味気ないですね。あと平均値が黄色のハイライトなのも好みが分かれそうです。

以下参考までにedarfパッケージの出力です。
Rplot01.png

まとめ ーそれでもICEboxにイエスと言うー

ICEboxはアンサンブルな機械学習モデルに対して汎用的に利用できるパッケージなので、これまで実務で使ってきているモデルを簡単に利用して要因分析を行うことが可能です。

欠点などもありながらも、非常に価値のあることができるようになるパッケージだと思います。

皆様是非使ってみて下さい!