MachineLearning

ランダムフォレストとは?Pythonを使ったハンズオンで解説【機械学習】

ランダムフォレスト Python
機械学習を勉強中の人
機械学習を勉強中の人
ランダムフォレストってなんだろう…。

どんな機械学習なのかだけではなく、Pythonを使った実装も合わせて知りたいな…。

この記事では、上記のような悩みを解決していきます。

 

この記事の想定読者

想定している読者は、次のとおりです。

  • 機械学習を勉強している人
  • ランダムフォレストが分からない人
  • ランダムフォレストの概要だけではなく、実装も知りたい人

 

この記事では「ランダムフォレストの理論と実装」について紹介していきます。

ランダムフォレストって聞いたことがあるけど、イマイチどんな学習手法なのか分からないですよね。

 

でも本記事を読み終えれば、ランダムフォレストについて分かるだけでなく、Pythonを使った実装も紹介しています。

この記事を書いている僕は、大学時代にディープラーニングを学んで、現在データサイエンティストとして働いています。

参考になる情報を提供できているはずなので、ぜひ最後まで読んでいただけたらと思います(`・ω・´)!

 

ランダムフォレストとは?

ランダムフォレスト Python

ランダムフォレストは、アンサンブル学習の1つで、複数の決定木を組み合わせた機械学習モデルです。

ランダムフォレストは、分類問題にも回帰問題にも使える機械学習手法になっています。

 

ランダムフォレストのイメージ

ランダムフォレストのイメージ図が、以下のとおりです。

ランダムフォレスト

決定木をたくさん用意して、分類問題や回帰問題を解いていきます。

ランダムフォレストの出力は、分類問題であれば多数決、回帰問題であれば平均値を取ることが多いですね。

 

ランダムフォレストの疑問① : なぜ決定木を組み合わせるのか?

 

決定木を複数組み合わせる理由は、その方が1つの決定木よりも、安定して性能の良いモデルを作成できるからです。

決定木モデルは単体だと、精度があまり良くありません。

でも、ランダムフォレストのように、精度の低い学習器(=弱学習器)を複数用意することで、安定した精度が出せるようになっています。

 

直感的にも、1人で問題解決するより複数人で問題解決した方が、正しい答えを導けそうですよね。

実際に、総理大臣を選ぶときも、日本人の誰か1人が選ぶのではなくて、選挙権を与えられた日本人で選んでいますよね。

ランダムフォレストでやりたいことも、日本の選挙と同じことです。複数の決定木を組み合わせることで、より正確な精度を出そうとしています。

 

なお、ランダムフォレストのように、弱学習器を複数並べて機械学習する方法を、アンサンブル学習と言います。

ランダムフォレストの場合は、とくに「バギング」というアンサンブル学習になります。

 

アンサンブル学習にも色々な種類があるので、詳しくは以下の記事をご覧ください。

※準備中です。

 

ランダムフォレストの疑問② : 何がランダムなのか?

 

ここまでを聞くと、ただ決定木を並べただけで、何がランダムなのか疑問になりますよね。

 

ランダムフォレストでランダムになっているのは、以下の2つです。

  • 学習するデータ
  • 条件分岐で使う特徴量

 

たとえば、機械学習の入門で有名なアヤメのデータセットは、以下のようになっています。

sepal lengthsepal widthpetal lengthpetal widthSpecies
05.13.51.40.20
14.93.01.40.20
24.73.21.30.20
34.63.11.50.20

ランダムフォレストでは、1つの決定木で学習するデータ(=行)を繰り返しOKで選んで、さらに決定木で使われる特徴量(=列)をランダムで選択します。

 

具体的なイメージにすると、以下の画像のとおりです。

ランダムフォレスト

この場合、決定木Aで、1つ目と2つ目のデータを使って学習することにして、最初の分岐でsepal width > 3.1などの条件分岐をおこなっていく感じになります。

 

特徴量をランダムに選択する理由

さらに、込み入った話になりますが、なぜ特徴量をランダムに選択するのかというと、決定木同士の相関を低くするためです。

というのも、元のデータ100個から繰り返しOKでデータを選ぶと、どうしたって選ばれたデータ同士は同じ分布になります。

元のデータが同じなので、当たり前ですね。

 

そして、同じような分布を持ったデータで学習させているため、通常どおりにバギングを使って学習するだけだと、決定木同士の相関が高くなってしまいます。

こういったバギング上の課題をクリアするためにも、ランダムフォレストは有効なモデルだと言えます。

 

決定木同士が色々な角度から学習をおこなってくれるので、決定木1本よりも性能強化できているんですね。

 

ランダムフォレストの疑問③ : メリット・デメリット

 

ランダムフォレストを使うメリットは、以下のとおりです。

  • ツリー構造が分かる
  • 良い精度が出やすい
  • 学習時の速度が速い

良い精度が出やすかったり学習時の速度が速いのは、もちろん重要ですが、1番のメリットは、ツリー構造が分かることです。

 

というのも、ディープラーニングのような機械学習手法は、中身が分からずブラックボックスになりがちなんですよね。

でも、ランダムフォレストだと、どういった条件で予測が行われているのか分かりやすいです。

予測過程が分かりやすいと、実務の上でも根拠として提示できるので、単純に精度的な意味以外でもメリットの多いモデルですね。

 

ランダムフォレストのデメリット

もちろん良いことばかりではなく、ランダムフォレストにはデメリットもあります。

  • データ数がないと機能しない
  • こちらで選択するパラメータが多い

以上のとおりで、ランダムフォレストは、データ数がないとパフォーマンスを発揮できません。

データ選択するときに、繰り返し選んでOKなので、データ数が少ないと同じものばかり選ばれてしまうためですね。

 

さらに、あとで実装をおこなっていきますが、ランダムフォレストでは設定しなければいけないパラメータが多いです。

もちろん、デフォルトで設定されているパラメータがあるので、とりあえず実装する分には問題ありません。

でも、ある程度の精度を求めるのであれば、パラメータ選択が必須になってきます。

 

【具体例】ランダムフォレストを、Pythonを使ったハンズオンで解説

 

ランダムフォレストについて理解したところで、Pythonを使った実装をおこなっていきましょう。

 

この記事で使うコード

import numpy as np
import pandas as pd

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

import matplotlib.pyplot as plt
import seaborn as sns


def get_iris(target_name=False):
    iris = load_iris()
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['Species'] = iris.target
    if target_name:
        df.Species = df.Species.apply(lambda x:'setosa' if x == 1 else 'versicolor' if x == 2 else 'virginica')

    return df

def plot_pairplot(df):
    g = sns.pairplot(df, hue='Species', plot_kws={'alpha': 0.5}, palette='rainbow_r')

    plt.show() 

def get_train_test_split(df):
    X = df.drop('Species', axis=1)
    y = df.Species
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

    return X_train, X_test, y_train, y_test

def get_accuracy(X_train, X_test, y_train, y_test):
    clf = RandomForestClassifier()
    clf.fit(X_train, y_train)
    pred = clf.predict(X_test)
    
    print('予測値:', pred)
    print('実測値:', np.array(y_test))
    print('精度:', accuracy_score(pred, y_test))


if __name__ == "__main__":
    # ペアプロットを出力
    df = get_iris(target_name=True)
    plot_pairplot(df)

    # ランダムフォレストの実装
    df = get_iris()
    X_train, X_test, y_train, y_test = get_train_test_split(df)
    get_accuracy(X_train, X_test, y_train, y_test)

※①Pythonファイルで書いています。notebookが良い方は、このファイルをimportして使ってください。

※②転記OKです。その場合、この記事のURLを「参考URL」として付けてください。

 

STEP① : 予測するデータ(irisデータセット)について確認

 

今回使用するのはirisデータセットです。

irisデータセットは、以下のような3種類のアヤメが入っているデータになります。

 

irisデータセットは、Pythonライブラリのscikit-learnに搭載されていて、実際の中身には、以下のような数値が入っています。

  • がく片の長さ(sepal length)
  • がく片の幅(sepal width)
  • 花びらの長さ(petal length)
  • 花びらの幅(petal width)

以上のデータに応じて、花の品種が3種類入っている感じです。

 

イメージ的には、こんな感じですね。

sepal lengthsepal widthpetal lengthpetal widthSpecies
05.13.51.40.20
14.93.01.40.20
24.73.21.30.20
34.63.11.50.20

 

Speciesが花の品種で、デフォルトだと数値になっています。

  • 0 : virginica
  • 1 : setosa
  • 2 : versicolor

とりあえず、花の特徴と品種が入っていることをおさえておきましょう。

 

STEP② : irisデータセットの読み込み

 

irisデータセットの読み込みには、以下の関数を使ってあげましょう。

def get_iris(target_name=False):
    iris = load_iris()
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['Species'] = iris.target
    if target_name:
        df.Species = df.Species.apply(lambda x:'setosa' if x == 1 else 'versicolor' if x == 2 else 'virginica')

    return df

これを使ってあげると、データフレームの扱いやすい形でirisデータセットを返してくれます。

 

target_name=Trueにした場合

なお、target_name=Trueにした場合には、目的変数(=今回予測する花の種類)を名前で返してくれます。

get_iris(target_name=True)を使って入手したデータを、以下の関数に渡してあげるとペアプロットを表示できるようになります。

def plot_pairplot(df):
    g = sns.pairplot(df, hue='Species', plot_kws={'alpha': 0.5}, palette='rainbow_r')

    plt.show()
ランダムフォレスト iris pairplot

上の画像のように、各パラメータごとの散布図を一枚絵に出来ます。

 

STEP③ : 学習データとテストデータに分割

 

読み込んだirisデータセットを学習データとテストデータに分割するには、以下の関数を使っていきます。

def get_train_test_split(df):
    X = df.drop('Species', axis=1)
    y = df.Species
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

    return X_train, X_test, y_train, y_test

取得したデータフレームを渡してあげれば、あとは学習データとテストデータを返してくれる関数です。

 

STEP④ : 学習および予測をおこなう

 

最後に、get_accuracy()を使って、ランダムフォレストを使った学習と予測をおこなっていきます。

def get_accuracy(X_train, X_test, y_train, y_test):
    clf = RandomForestClassifier()
    clf.fit(X_train, y_train)
    pred = clf.predict(X_test)

    print('予測値:', pred)
    print('実測値:', np.array(y_test))
    print('精度:', accuracy_score(pred, y_test))

 

そうすると、以下のような結果が出力されるようになっています。

予測値: 2 1 0 2 1 1 2 2 0 2 0 0 1 0 0 2 2 0 1 1 1 1 1 1 1 1 1 2 2 0

実測値: 2 1 0 2 1 1 2 2 0 2 0 0 1 0 0 2 1 0 1 1 1 1 1 1 1 2 1 2 2 0

精度: 0.9333333333333333

これを見ると、93.3%の精度で予測できていることが分かりますね。

今回はデフォルトの設定から変更していませんが、十分に高い精度が出ているかと思います。

 

まとめ : ランダムフォレスト以外にも、データサイエンスを学習しよう

 

というわけで、ランダムフォレストの概要と実装について紹介してきました。

最近だと、ツリー系のアルゴリズムは、XGBoostやLightGBMが強いですが、ランダムフォレストはアルゴリズムが分かりやすいこともあり、簡易分析に使われたりします。

なので、しっかりとおさえておいて損はないかと(`・ω・´)!

 

今回は紹介しませんでしたが、数式を使った理論を学びたい場合には、以下の書籍がおすすめです。

 

他にも、以下の記事でデータサイエンスを学べるおすすめの書籍を紹介しています。

データサイエンス 本 おすすめ
【2020年最新】データサイエンスでおすすめの本10冊【現役が紹介】 【2020年最新】データサイエンスでおすすめの本10冊【現役が紹介】 2020年最新版にて、データサイエン...

 

データサイエンスや機械学習は楽しいですからね。どんどん学習していきましょう(`・ω・´)!

おしまいです。

 

おすすめの記事