機械学習手法「ランダムフォレスト」でクラス分類にチャレンジしよう

Deep Learningのようなパワフルな機械学習モデルもいいですが、もっと手軽なモデルがたくさんあります

Pythonとscikit-learnで手軽に機械学習を体験しちゃいましょう!

この記事で紹介する機械学習モデルは「ランダムフォレスト」です。

この記事でわかること

  • ランダムフォレストとは
  • ランダムフォレストの使い方
  • ハイパーパラメータのチューニング方法

機械学習アルゴリズムの一つに「決定木」というものがありますが、これを使ったアンサンブル学習の一つとして有名な「ランダムフォレスト」については広く使われる有名なモデルです。

「ランダムフォレスト」を使いこなして一歩進んだデータ解析を行いましょう。

この記事のコードはPython 3.6.7、scikit-learn 0.19.2で動作確認しました。

目次

ランダムフォレストとは

ランダムフォレストは機械学習モデルの一つです。

クラス分類、回帰、クラスタリングなどに利用する事ができます。

複数の決定木を使って各決定木の予測結果の多数決で結果を求める方法です。

このように複数のモデルを使って性能を上げる学習方法をアンサンブル学習といい、ランダムフォレスト以外にも広く使われています。

もっと詳しく知りたい方は、参考書や侍エンジニアのマンツーマンレッスンを試してみてください。

ランダムフォレストの使い方

目的

scikit-learnを使って簡単にランダムフォレストを試しましょう。

ここではscikit-learnのdigitsデータセットを使ってクラス分類を行います。

ライブラリのimport

まずはライブラリを読み込みましょう。

from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier as RFC

from sklearn.model_selection import train_test_split, GridSearchCV

4行目のimportがここで使うランダムフォレストのクラスです。

scikit-learn(以下sklearn)には

  • RandomForestClassifier
    • クラス分類用(今回はこっち)
  • RandomForestRegressor 
    • 回帰分析用

の二種類のランダムフォレストクラスがあるので、目的にあったものを使いましょう。

データの読み込み

データを読み込みます。

mnist = datasets.load_digits()

train_images, test_images, train_labels, test_labels = 
train_test_split(mnist.data, mnist.target, test_size=0.2)

読み込みはdatasets.load_foo()関数で行います。

その次の行は読み込んだデータを

  1. 教師データ
  2. テストデータ
  3. 教師ラベル
  4. テストラベル

に分割するコードです。

ここで、train_test_split関数のtest_sizeに与えた値がテストデータの割合になるので注意してください。

ここで読み込んだデータは8×8のピクセルがある手書き文字でした。

実際に表示してみてみましょう。

plt.figure(figsize=(15,15))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(train_images[i].reshape((8,8)), cmap=plt.cm.binary)
    plt.xlabel(train_labels[i])

ここでは25個のデータを可視化してみました。

この画像を、ラベルの数字をクラスとしてクラス分類を行うのが目的です。

ランダムフォレストモデルの学習

sklearnでのランダムフォレストの使用は非常に簡単です。

  1. sklearnの機械学習モデル(ランダムフォレスト)のインスタンスを作成する
  2. 教師データと教師ラベルを使い、fitメソッドでモデルを学習
clf = RFC(verbose=True,       # 学習中にログを表示します。この指定はなくてもOK
          n_jobs=-1,          # 複数のCPUコアを使って並列に学習します。-1は最大値。
          random_state=2525)  # 乱数のシードです。
clf.fit(train_images, train_labels)

[出力結果]

[Parallel(n_jobs=-1)]: Done  10 out of  10 | elapsed:    0.0s finished
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=None, max_features='auto', max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=-1,
            oob_score=False, random_state=2525, verbose=True,
            warm_start=False)

この設定だと、ランダムフォレストのハイパーパラメータ(インスタンスを作る際の引数)は最低限で、ほぼデフォルトのままで行っています。

fitメソッドをキックした段階で学習が始まるので、普通ならば一瞬で学習が終わります。

学習済みモデルの評価

実際に学習したモデルの評価を行いましょう。

まずはaccuracy(正答率)を見てみましょう。

正答率は

acc = (予測ラベルがテストラベルと同じだった数)/ (テストデータの数)

で計算できます。

sklearnではscoreというメソッドで簡単に計算しましょう。

print(f"acc: {clf.score(test_images, test_labels)}")
# acc: 0.9416666666666667

チューニングを行っていないランダムフォレストでも94%の正答率になることがわかります。

また、予測ラベルだけが欲しい場合はpredictメソッドを使います。

predicted_labels = clf.predict(test_images)

これでpredicted_labelsにはtest_labelsと同じサイズの配列が保存されました。

実際に可視化して結果を詳しく見てみます。

plt.figure(figsize=(15,15))

# 先頭から25枚テストデータを可視化
for i in range(25):

    # 画像を作成
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(test_images[i].reshape((8,8)), cmap=plt.cm.binary)

    # 今プロットを作っている画像データの予測ラベルと正解ラベルをセット
    predicted_label = predicted_labels[i]
    true_label      = test_labels[i]

    # 予測ラベルが正解なら緑、不正解なら赤色を使う
    if predicted_label == true_label:
        color = 'green' # True label color
    else:
        color = 'red'   # False label color
    plt.xlabel("{} True({})".format(predicted_label,
                                  true_label),
                                  color=color)

この図はテストデータと予測ラベル、そしてテストラベルを示しています。

赤色になっているものは誤答です。

これでも十分良い成果だと思いますが、もう少し性能を上げる方法を考えてみましょう。

ハイパーパラメータをチューニング

グリッドサーチとは

先程の方法では、ランダムフォレストのハイパーパラメータを全くチューニングしませんでした

ここではハイパーパラメータのチューニングをグリッドサーチと呼ばれる方法で行います。

ランダムフォレストなどの機械学習モデルは、ハイパーパラメータの設定を変えると性能が大きく変わることがあります。

ですがどのような設定にすればいいのかは、経験則で決めることが多いです。

これを自動で探索してしまう方法がいくつかありますが、その中でも一番愚直な方法がグリッドサーチです。

パラメータの組み合わせをすべて試して、一番汎化性能が高かったものを最も良いパラメータの組み合わせとして教えてくれます。

パラメータを探索

まずは探索するパラメータ空間を決めます。

これには辞書型を用います。

この辞書型のkeyはランダムフォレストクラスの引数の名前で、値にその引数で試したいものをリストにして渡します。

search_params = {
     'n_estimators'      : [5, 10, 20, 30, 50, 100, 300],
      'max_features'      : [3, 5, 10, 15, 20],
      'random_state'      : [2525],
      'n_jobs'            : [1],
      'min_samples_split' : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100],
      'max_depth'         : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100]
}

keyはクラスの引数を見ながら決めてください。

例えばランダムフォレストについての知識があるならば、もう少し賢いパラメータ探索空間があるかもしれませんね。

これをGridSearchCVクラスに渡して探索します。

gs = GridSearchCV(RFC(),           # 対象の機械学習モデル
                  search_params,   # 探索パラメタ辞書
                  cv=3,            # クロスバリデーションの分割数
                  verbose=True,    # ログ表示
                  n_jobs=-1)       # 並列処理
gs.fit(train_images, train_labels)

print(gs.best_estimator_)

[出力結果]

Fitting 3 folds for each of 3500 candidates, totalling 10500 fits
[Parallel(n_jobs=-1)]: Done  68 tasks      | elapsed:    1.8s
[Parallel(n_jobs=-1)]: Done 1225 tasks      | elapsed:   11.4s
[Parallel(n_jobs=-1)]: Done 2225 tasks      | elapsed:   22.8s
[Parallel(n_jobs=-1)]: Done 3325 tasks      | elapsed:   36.5s
[Parallel(n_jobs=-1)]: Done 4225 tasks      | elapsed:   48.4s
[Parallel(n_jobs=-1)]: Done 5325 tasks      | elapsed:  1.0min
[Parallel(n_jobs=-1)]: Done 6625 tasks      | elapsed:  1.3min
[Parallel(n_jobs=-1)]: Done 8125 tasks      | elapsed:  1.6min
[Parallel(n_jobs=-1)]: Done 9825 tasks      | elapsed:  2.0min
[Parallel(n_jobs=-1)]: Done 10469 out of 10500 | elapsed:  2.1min remaining:    0.4s
[Parallel(n_jobs=-1)]: Done 10500 out of 10500 | elapsed:  2.2min finished
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=15, max_features=10, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=3,
            min_weight_fraction_leaf=0.0, n_estimators=100, n_jobs=1,
            oob_score=False, random_state=2525, verbose=0,
            warm_start=False)

gsインスタンスを学習(探索)させるには、他のsklearnのクラスと同様にfitメソッドを使います。

学習済みのgsインスタンスのbest_estimator_属性最も性能が良かったランダムフォレストのインスタンスが入っています

先ほどと同様にして評価すると、accは98.88%でした。

かなり良くなっていますね。

予測ラベルを可視化してみると、先程よりも誤答が減っている(表示している範囲では誤答ゼロ)ことがわかります。

まとめ

この記事では簡単なランダムフォレストの説明と、その使い方を紹介しました。

発展的な内容として紹介したパラメタサーチは非常に有用な方法で、実際のデータ解析では広く使われています。

もっと詳しくランダムフォレストについて知りたい、ランダムフォレストを自分で実装したい、という方はマンツーマンレッスンでお待ちしています

実用的な機械学習モデルの一つであるランダムフォレスト、使いこなして機械学習を身近なものにしちゃいましょう!

この記事を書いた人

第一言語はPythonです。
皆さんRustやりましょう。

目次