筋肉で解決しないために。

日々出会うモノに対する考察をしたり、主に以下のテーマに関して書いています。 データサイエンス/人工知能/AI/機械学習/DeepLearning/Python//数学/統計学/統計処理

決定木とランダムフォレスト分類/回帰の実装と可視化

こんにちは、ワタルです。

はじめに

このエントリでは、サンプルデータを作成し、そのデータを持って決定木とランダムフォレストの実装と可視化をしてみます。

決定木分類をとてもかんたんに説明すると、 ある条件に対してYes or No のように分けて決めていくことでそのサンプルを分類する方法です。

また、ランダムフォレスト(Random forests)は、アンサンブル学習法と呼ばれる学習法の一つです。 アンサンブル学習法とは、いくつかの分類器を集めて構成する学習法のことを指します。 決定木を集めたアンサンブル学習法なので、ランダムフォレストと呼ばれるわけです。

より詳しい説明はこちらを参考にして見てください。 画像もこちらから引用しています。

Enchanted Random Forest – Towards Data Science

f:id:watarumon:20180717171310p:plain

また、コードはこちらを参考にさせていただいております。

Lec86_決定木とランダムフォレスト

それでは実際にコードを書いていきます。

目的とタスク設定

なにごとも、目的とタスクの整理は重要です。

サンプルデータの作成をタスク1 とします。

次に、決定木のみの実装とその可視化をタスク2とします。

次に、ランダムフォレストによる多クラス分類の実装とその可視化をタスク3とします。

最後に、ランダムフォレストによる回帰の実装とその可視化をタスク4とします。

さて、タスク1から始めます。

おまじない

%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn

タスク1

まずはサンプルデータを生成します。

#make_blobsをimportします
from sklearn.datasets import make_blobs

#500の要素
#4つの中心、つまり4つのグループを作る
#再現性のためにrandom_stateを指定
#cluster_stdはばらつき具合
X, y = make_blobs(n_samples=500, centers=4,
                  random_state=7, cluster_std=2.4)

#生成したデータのプロット
plt.figure(figsize=(10,10))
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='jet')
#引数cとcmapは色のため、sはプロット点の大きさ指定です

>>>

f:id:watarumon:20180717172518p:plain

このサンプルデータに対して、分類を実装していきます。

タスク2

次に決定木分類を実装し、可視化します

#決定木分類のインポート
from sklearn.tree import DecisionTreeClassifier

#決定木の可視化
def visualize_tree(classifier, X, y, boundaries=True,xlim=None, ylim=None):

    # fitを使ったモデルの構築
    classifier.fit(X, y)
    
    #軸を自動調整
    if xlim is None:
        xlim = (X[:, 0].min() - 0.1, X[:, 0].max() + 0.1)
    if ylim is None:
        ylim = (X[:, 1].min() - 0.1, X[:, 1].max() + 0.1)

    x_min, x_max = xlim
    y_min, y_max = ylim
    
    
    #meshgridをつくります。
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
                         np.linspace(y_min, y_max, 100))
    
    #分類器の予測をZとして保存
    Z = classifier.predict(np.c_[xx.ravel(), yy.ravel()])
    #np.ravel関数は配列の一次元化、np.c_は配列の結合を行っています

    #meshgridを使って、整形します。
    Z = Z.reshape(xx.shape)
    
    #分類ごとに色を付けます。
    plt.figure(figsize=(10,10))
    plt.pcolormesh(xx, yy, Z, alpha=0.2, cmap='jet')
    #xx,yyの座標について、Zで色分けをしています。alphaは色の薄さです
    
    #訓練データのオーバープロット
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='jet')
    
    plt.xlim(x_min, x_max)
    plt.ylim(y_min, y_max)        
    
    #境界線をプロットする関数をつくります
    def plot_boundaries(i, xlim, ylim):
        if i < 0:
            return

        tree = classifier.tree_
        
        #境界を描画
        if tree.feature[i] == 0:
            plt.plot([tree.threshold[i], tree.threshold[i]], ylim, '-k')
            plot_boundaries(tree.children_left[i],
                            [xlim[0], tree.threshold[i]], ylim)
            plot_boundaries(tree.children_right[i],
                            [tree.threshold[i], xlim[1]], ylim)
        
        elif tree.feature[i] == 1:
            plt.plot(xlim, [tree.threshold[i], tree.threshold[i]], '-k')
            plot_boundaries(tree.children_left[i], xlim,
                            [ylim[0], tree.threshold[i]])
            plot_boundaries(tree.children_right[i], xlim,
                            [tree.threshold[i], ylim[1]])
    
    if boundaries:
        plot_boundaries(0, plt.xlim(), plt.ylim())
#インスタンスを作り、上記のvisualize_tree関数内のモデルに代入します
clf = DecisionTreeClassifier(max_depth=2,random_state=0)
visualize_tree(clf,X,y)

>>>

f:id:watarumon:20180717171435p:plain

垂直が①番目、水平が②番目の順に、決定木分類がされていることがわかります。

深さ2の分類では水色のプロットが全く上手く分類できていないことも見て取れます。

もっと深くして試してみます。

#深さ4にしてみます
clf = DecisionTreeClassifier(max_depth=4,random_state=0)

visualize_tree(clf,X,y)

f:id:watarumon:20180717171448p:plain

#深さ10にしてみます。
clf = DecisionTreeClassifier(max_depth=10,random_state=0)

visualize_tree(clf,X,y)

f:id:watarumon:20180717171501p:plain

だいぶごちゃごちゃしてしまいました。 感覚的に見ても、上手く分類できていないプロットはいくつかあり、過学習を起こしてそうな個所も見受けられます。

そこで、アンサンブル学習法である、ランダムフォレストを試してみます。

ランダムフォレストは、学習データの一部をランダムに選んで、決定木を作るという行動を繰り返す手法です。

タスク3

#ランダムフォレスト分類をインポートします
from sklearn.ensemble import RandomForestClassifier

clf = RandomForestClassifier(n_estimators=100,random_state=0)
#n_estimatorsは、作る木の数です。

# 境界線を書かないようにします。
visualize_tree(clf,X,y,boundaries=False)

f:id:watarumon:20180717171517p:plain

水色の左側のプロットでは、過学習が起きている様な分類結果が見て取れますが、 決定木分類よりは高い精度であることが見て取れます。

最後に、ランダムフォレスト回帰を実装してみます。

タスク4

線形回帰では、回帰曲線を描くのが困難なサンプルデータを作成し、ランダムフォレスト回帰を実装してみます。

まずは2種類のsin波とランダム関数から生成したノイズからなるダミーデータを生成します。

#ランダムフォレスト回帰をインポートします
from sklearn.ensemble import RandomForestRegressor

x = 10 * np.random.rand(100)

def sin_model(x, sigma=0.2):   
    noise = sigma * np.random.randn(len(x))
    return np.sin(5 * x) + np.sin(0.5 * x) + noise

y = sin_model(x)

# Plotします。
plt.figure(figsize=(16,8))
plt.errorbar(x, y, 0.1, fmt='o')
#Xを用意します。
xfit = np.linspace(0, 10, 1000)

#回帰モデルを用意します。
rfr = RandomForestRegressor(100)

#モデルを学習させます。
rfr.fit(x[:, None], y)

#予測値を計算します。
yfit = rfr.predict(xfit[:, None])

#回帰の目指している実際の曲線を描くための値をつくります
ytrue = sin_model(xfit, 0)

#Plotします
plt.figure(figsize=(16,8))

plt.errorbar(x, y, 0.1, fmt='o')

plt.plot(xfit, yfit, '-r');
plt.plot(xfit, ytrue, '-b', alpha=0.5);

>>>

f:id:watarumon:20180717171536p:plain

青が実際の値で、赤が予測値になります。

線形回帰では、ただ、山なりな曲線しか描けなそうなデータに対して、ランダムフォレスト回帰は有効な方法であることを確かめることができました。

さいごに

今回は、サンプルデータを作成し、そのデータに対して決定木分類の実装と可視化、ランダムフォレスト分類と回帰の実装と可視化を行いました。

また、ランダムフォレスト回帰では、線形回帰では、回帰曲線を描くのが困難そうデータに対して、有効な方法であることを確認出来ました。

お疲れ様でした!

それじゃー、また。