機械学習界隈で、最強アルゴリズムの一角を占めていたランダムフォレスト。ディープラーニングが登場した後急速に存在感をなくすものの、その利便性と強力さから多くのデータサイエンティストが現役利用中。

scikit-learnでのランダムフォレスト、分類モデルと重要度評価の実装方法まとめました。

決定木の問題点

決定木は、上から順に条件分岐を作って分類モデルを作る手法です。ルールが可視化できる、正規化や標準化などのデータ加工が不要など、素晴らしいアルゴリズムです。
しかし、決定木は過学習を起こしやすいという問題点がありました。

前の記事:Pythonの決定木分析できのこ派とたけのこ派を予測する

ランダムフォレストとは

ランダムフォレストは、決定木をたくさん作ってその結果を多数決(平均化)することで、決定木の過学習を平準化するアルゴリズムです(アンサンブル学習と言います)。

ランダムフォレストの学習手順は、
1.ランダムにデータを選択する(サンプル数と特徴量を複数組選択する)
2.複数の決定木を作る
3.複数の決定木の結果を多数決して分類結果を決定する。

という流れになります。
決定木をたくさん作って平均化することで、高精度な学習済みモデルを作成することが出来るのです。

ランダムフォレストの特徴

ランダムフォレストの特徴は、4つあります。

1.多数決によるアンサンブル学習のため過学習が起こりにくい
2.データの標準化や正規化の処理が不要
3.ハイパーパラメータが少ない(サンプリング数と決定木の特徴量数くらい)
4.どの特徴量が重要かを知ることができる

過学習が起こりにくいし、面倒なデータの正規化も不要です。こんなに便利なアルゴリズムは他にありません。ベテランのデータサイエンティストが、「ランダムフォレスト最強」と主張されるのも良く分かります。

ランダムフォレストは分類アルゴリズムとして非常に優秀なのですが、ここで4番を見てください。実は特徴量の重要度も評価できます。もうエクスカリバーみたいなもんですね。

RandomForestClassifierとExtraTreesClassifierの違い

scikit-learnでランダムフォレストの分類モデルには、RandomForestClassifierExtraTreesClassifierがあります。
scikit-learnのチュートリアルを見る限り、使い分けとしては、精度の高い分類モデルを作りたい場合はRandomForestClassifierを利用し、特徴量の重要度を特に評価したい場合はExtraTreesClassifierを利用することが多いようです。今回は、RandomForestClassifierを利用します。

なお、ランダムフォレストで回帰を行う場合は、RandomForestRegressorやExtraTreesRegressorのクラスを活用してください。

RandomForestClassifierの主なパラメータ

ランダムフォレストも、他の機械学習アルゴリズムと同様に、ハイパーパラメータの設定が必要です。ただランダムフォレストの場合、デフォルトでもわりと良い精度が出るのであまりこだわらなくても大丈夫です。こだわる人は、いろいろ設定してグリッドサーチしましょう。

n_estimators →デフォルト値100。決定木の数。
max_features →デフォルト値は特徴量数の平方根。決定木の特徴量の数。大きいと似たような決定木が増える、小さいと決定木がばらけるがデータに適合できにくくなる。
random_state →デフォルト値なし。乱数ジェネレータをどの数値から始めるか。前回と異なるモデルを得たい場合は数値を変更すると良いです。
max_depth →デフォルト値なし。決定木の最大の深さ。
min_samples_split →デフォルト値2。木を分割する際のサンプル数の最小数。

ランダムフォレストの分類モデル実装

scikit-learnのランダムフォレストで、分類モデルを実装します。データはおなじみのIrisデータです。言語はPython3.7です。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report

# Pandas のデータフレームとして表示
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)

#データの定義
x = iris.data
y = iris.target

#学習データとテストデータに分割
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.20, random_state=42)

# 学習
clf = RandomForestClassifier(n_estimators=20, random_state=42)
clf.fit(x_train, y_train)

#予測データ作成
y_predict = clf.predict(x_test)

# 評価レポート
print(classification_report(y_test, y_predict))

→ホールドアウト法で評価していますが、本来はクロスバリデーションで評価した方が良いです。評価方法の詳細は以下の記事にまとめました。

参考記事:交差検定(クロスバリデーション)など機械学習の評価方法まとめ

特徴量の重要度を評価する方法

scikit-learnのランダムフォレストでは、特徴量の重要度を算出できます。重要度の算出方法は、以下の通りです。

1.ランダムにデータを取って決定木を作る。
2.作った決定木のある特徴量について、データの並び順をぐちゃぐちゃにする。
3.ぐちゃぐちゃにする前と後で、決定木の精度が変わるかどうか比較する。
4.精度が大きく変わったら重要な特徴量、変わらなかったら重要でない特徴量とする。
5.いろんな決定木で1~4を繰り返して、多数決する。

重要度は、あくまで特徴量全体から見た相対的な指標です。そのため、すべての特徴量の重要度を足すと1になります。数字自体が意味を持つ相関とは、また違った概念です。

ちなみに重要度評価は、実務でもかなり使えます。重要度が高い特徴量とは、学習済みモデルの予測に重要という意味なので、関係者にモデルの中身を説明することができます。

ランダムフォレストで特徴量の重要度評価を実装

feature_importances_関数で特徴量の重要度を取得できます。今回コードでは、重要な特徴量を上から並べ棒グラフを作りました。

#特徴量の重要度
feature = clf.feature_importances_

#特徴量の名前
label = df.columns[0:]

#特徴量の重要度順(降順)
indices = np.argsort(feature)[::-1]

for i in range(len(feature)):
    print(str(i + 1) + "   " + str(label[indices[i]]) + "   " + str(feature[indices[i]]))

plt.title('Feature Importance')
plt.bar(range(len(feature)),feature[indices], color='lightblue', align='center')
plt.xticks(range(len(feature)), label[indices], rotation=90)
plt.xlim([-1, len(feature)])
plt.tight_layout()
plt.show()

→出力結果
1   petal length (cm)   0.440336982824
2   petal width (cm)   0.415328928276
3   sepal length (cm)   0.11628156486
4   sepal width (cm)   0.0280525240407

petal length (cm) が最も重要な特徴量と評価されました。
100


学習データの次元数が多い場合、全ての特徴量を学習に使うのではなく、まずランダムフォレストで特徴量を評価して、重要でない特徴量を削除すると精度が上がることも多いです。

テーブルデータでは、計算が速く、モデルの可視化性が高いランダムフォレストやロジスティック回帰が初手の定石です。その後、精度を追求するなら、計算量が大きいが高精度が期待できるLightGBMを使いましょう。