この記事では、ランダムフォレストによるEDA(探索的データ解析)の実例を紹介します。
ランダムフォレストとは
複数の異なる決定木を組み合わせて予測を行うアルゴリズムです。
決定木は分析結果の説明力が高いメリットがある一方、階層を深くすると過学習を起こしやすくなり、予測精度が落ちるという欠点があります。
ランダムフォレストでは、アンサンブル学習という手法によって過学習の問題を解決しようと試みます。
アンサンブル学習とは、複数のモデルの平均や多数決をとって予測値を出す手法です。
1人の判断よりも、多くの人から多数決をとった判断の方が精度が良いだろうというアイデアに基づくアルゴリズムです。
ランダムフォレストでは、複数の異なる決定木を作成し、これらの予測結果の平均または多数決をもとに予測結果を出します。

個々の決定木を作成する際には、学習に用いるデータおよび特徴量はランダムに選択されるため、それぞれ異なる決定木となります。
様々な特徴量の組み合わせを考慮した仕組みであるため、高い予測力が期待できますが、予測値を求める計算はブラックボックスであり、人間には解釈できません。
そのため、説明性が求められるケースではランダムフォレストをモデルとして採用しがたいことがあります。
一方で、各特徴量がもつ予測値への影響度を数値化したり、可視化したりすることが容易であり、EDA(探索的データ解析)に有効利用できるケースが多くあります。
決定木やEDAについては下記で解説しています。こちらもあわせてお読みください。


ランダムフォレストを使ったEDA
ランダムフォレストモデルによるEDAの例を紹介します。
もし、ランダムフォレストモデルが高い予測力を持っていて、特徴量と予測値の関係を可視化できれば、モデル構築の特徴量選択に利用できます。
以下では単純なデータに対して、ランダムフォレストを使用したEDAの実例を紹介します。
ランダムフォレストモデルの作成
RにあらかじめインストールされているMASSパッケージのBostonデータセットを使用してランダムフォレストのモデルを作成します。
データは、モデル作成に使用する学習データ(train)と、モデルの予測性能評価に使うテスト用データ(test)に分割します。
#Bostonデータ xy <- Boston colnames(xy)[ncol(xy)] <- "y" #データを学習用とテスト用に分割 n <- nrow(xy) set.seed(111, sample.kind = "Rounding") test.id <- sample(n, round(n / 4)) test <- xy[test.id, ] train <- xy[-test.id, ]
Rにはランダムフォレストのパッケージが複数存在しますが、今回は最も古典的なrandomForestパッケージを使用します。
library(randomForest) set.seed(111) #ランダムフォレストモデルの学習 boston.rf <- randomForest(y ~ ., data = train, importance = TRUE) #テストデータに対する予測 pred <- predict(boston.rf, newdata = test) #観測値と予測値をプロット plot(test$y, pred, main = boston.rf$call) curve(identity, add = TRUE) #予測誤差(RMSE:二乗平均平方根誤差) rms <- function(act, pred) { sqrt(mean((act - pred) ^ 2)) } cat(" RMSE =", rms(test$y, pred)) #線形回帰モデルの予測誤差と比較 cat(" RMSE = ", rms(test$y, predict(lm(y ~ ., data = train), newdata = test)))

このデータにおける予測誤差は、線形回帰モデルよりもランダムフォレストの方がかなり小さいことが分かります。
特徴量重要度
ここからランダムフォレストを使って特徴量重要度を可視化していきます。
特徴量重要度とは目的変数に対する特徴量の寄与の大きさを示すものです。
特徴量重要度はrandomForestパッケージのimportance関数にモデルオブジェクトを入力するだけで出力できます。
特徴量重要度の高い順で並び替えて棒グラフを出力するコードは次のとおりです。
#特徴量重要度の出力 type = 1 boston.imp <- sort(importance(boston.rf, type = 1, scale = FALSE)[, 1], decreasing = TRUE) barplot(boston.imp, names.arg = rownames(boston.imp))

randomForestパッケージのimportance関数による特徴量重要度の計算方法は複数のtypeが用意されています。
importance(boston.rf, type = 1, scale = FALSE)では、ある特徴量をシャッフルした場合に予測誤差がどれだけ悪化するかを特徴量ごとに計算しています。
特徴量をシャッフルしても予測誤差が悪化しなければ、その特徴量は重要ではないという考え方です。

ランダムフォレストでは、各決定木で異なるサンプルを使って学習しますが、学習データのうち平均で1/3くらいは学習に使われません。
学習に使われなかったデータをOOB(out-ob-bag)といいます。
「何も手付かずのOOBに対する予測誤差」と「特定の特徴量の並び順をシャッフルしたOOBに対する予測誤差」の差分を特徴量重要度とします。
すべての決定木におけるOOB間の差分を平均し、差分の標準偏差で正規化されます。
差分の標準偏差が0に等しい場合は正規化は行われません。
予測誤差に使われる指標は、回帰の場合はMSE、分類の場合は誤り率です。
一方、importance(boston.rf, type = 2, scale = FALSE)とした場合は、決定木ノードの不純度の減少量を特徴量重要度に近似します。
ノードの不純度は、分類の場合はジニ係数、回帰の場合は残差平方和によって計算されます。
以下はtype = 2で特徴量重要度を出力するコードと実行結果です。
#特徴量重要度の出力 type = 2 boston.imp <- sort(importance(boston.rf, type = 2, scale = FALSE)[, 1], decreasing = TRUE) barplot(boston.imp, names.arg = rownames(boston.imp))

重要度の並び順に若干の違いはありますが、上位5位までの並び順は同じです。
type1,2のどちらを使うかで悩むより、type1,2の両方を出力して傾向を掴むのが良さそうです。
コメント