高精度に挑戦!Boston Housingの機械学習をランダムフォレストで実装してみた。


本記事の概要


初めまして!
プログラミング見習い中のれんです。

本記事では、機械学習の手法である
🌲「ランダムフォレスト」
のPythonでのやり方を解説していきます。

Python初学者向けになるべく簡単な言葉で説明していきたいと思いますので
学習はじめたての方はぜひ参考にしてみてください!

※実行環境は「GoogleColaboratory」になります。


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


🌴まずは「決定木」について知ろう🌴

「ランダムフォレスト」の説明の前に、まずは「決定木」について説明します。

「決定木」とは…
下図のYes/No占いのように、条件分岐の繰り返しによって結果を分析する機械学習の手法です。

こんな占いありましたよね


子供の頃、「あなたは何タイプ?」のような条件分岐の占いをやったことはありませんか🔮
この占いも、Yes / Noの分岐によって「自分がどのタイプに分類されるか」を学習しているといえるでしょう。

このように我々人間がYes/Noの自問自答を繰り返す事で「何に分類されるか」を学習していくのと同じように、「決定木」もYes/Noの繰り返しで学習をするモデルになっています。


🌲本題:「ランダムフォレスト」について知ろう🌲

「ランダムフォレスト」とは…
複数の「決定木」によって結果を予測し、複数の算出結果の中での多数派を最終的な結果とするモデルです。
※🌴決定が集まって、🌲フォレスト(森)になるということですね。

人間も一人だけで考えるよりも、複数人の考えを踏まえて導き出した答えの方が精度が高くなると思います。
それと同じで、複数の🌲決定木たちの答えから最適解を導き出そうとする手法です。

民主主義的な思想と言えるでしょう

もう少し具体的に処理方法を説明すると以下のようになります!
🌲ランダムフォレストの処理のイメージ🌲
1.大元のデータからランダムにデータを抽出し、複数のデータ群を作る
2.それぞれのデータ群に対して「決定木」の手法で学習して結果を算出する
3.各「決定木」の結果から最終的な結果を導き出す。
 分類問題(データを分類する問題)……多数決で結果を算出 
 回帰問題(データから何かを予測する問題)……平均値を結果とする

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

「1つの決定木だけで学習した結果」には偏りが生じやすいのですが、ランダムフォレストであれば結果が偏りにくくなります
※学習したデータの傾向を受けて結果が偏ることを「過学習」と言います。

👨‍🏫豆知識👨‍🏫
ランダムフォレストでは、大元のデータからデータをランダム抽出する際に復元抽出というやり方で抽出します。

復元抽出とは、
【データをランダム抽出しデータ群を作成した後、「抽出したデータ」を「大元のデータ群」に戻し全データから再度抽出を行う】サンプリング方法です。
抽出したデータを戻して再抽出するため、同じデータが何回も選ばれる可能性があります。

もし同じデータが何回も抽出されてしまった場合は、そのデータの影響を受けるため当然データには偏りが生じやすくなります。
こういった偏りを避けるためにも複数の決定木さんたちの意見を聞く(平均を取る/多数決をする)ことが大事なのです。

なお、ランダムフォレストでは、データだけではなく説明変数もランダムに抽出します。
つまり、1000人分の性別・年齢・身長・体重をまとめたデータがあった時に
ある決定木では性別と年齢の200人分のデータを抽出し、別の決定機では身長と体重の 200人分のデータを抽出するといった形です。
完全にランダムに抽出するからこそ、極端に外れた値のデータが存在しても影響を受けにくいのです。

ランダムフォレストの抽出データ内容のイメージ


また、ランダムフォレストには、下記のような特徴があります。

メリット

  • データが多くても早い処理が出来る(複数の決定木で並列処理を行うため)

  • 過学習しづらい(データの一部を使って学習するため、学習データに依存した結果になりづらい)

  • データの前処理段階での標準化(単位が違うデータを同じ見方に揃える処理)が不要

  • 予測値が導き出されたロジックが理解しやすい(目的変数に寄与した要素を特定したり、予測値が導き出された理由を人に説明しやすい)

デメリット

  • 説明変数が少ないとデータが過学習しやすい(決定木作成のためのランダム抽出時に同じデータを抽出する可能性が高まるため)


ランダムフォレストを実装してみる


それでは、いよいよPythonを使ってランダムフォレストでの機械学習をしていきましょう!
※PythonのScikit-learnライブラリを用います


STEP1:分析するデータと作成するモデルを理解する

住宅や地域環境のデータセットから住宅価格の相場を予測するモデルを作ります!
以下データの詳細になります。

■使用するデータ
Boston Housing
…1970年代後半のボストンの住宅情報と地域環境をまとめたデータセット。

下記のようなデータで構成されています。

〜説明変数(データを予測するための材料となる要素)〜
1.CRIM…(地域人工毎の)犯罪発生率
2.ZN…25,000平方フィート以上の住宅区画の割合
3.INDUS…(地域人口毎の)非小売業の土地面積の割合
4.CHAS…チャールズ川沿いに立地しているかどうか(該当の場合は1、そうでない場合は0)
5.NOX…窒素酸化物の濃度(単位:pphm)
6.RM…平均部屋数/一戸
7.AGE…1940年よりも古い家の割合
8.DIS…5つのボストン雇用センターまでの重み付き距離
9.RAD…主要な高速道路へのアクセス指数
10.TAX…10,000ドルあたりの所得税率
11.PTRATIO…(地域人工毎の)学校教師1人あたりの生徒数
12.B…(地域人工毎の)アフリカ系アメリカ人居住者の割合
13.LSTAT…低所得者の割合

※参考
【Python】ランダムフォレスト回帰のモデル作成と評価方法|scikit-learn・機械学習による回帰分析入門

〜目的変数(求めたい事)〜
MEDV…住宅価格の中央値

今回のデータには説明変数が一定量あり過学習がされにくいと思われる点、また数字だけでなくその背景にある説明変数間の関係性が重要な問題だと思われる点から
ランダムフォレストのモデルを使用します。


STEP2:基本的なライブラリのインポート

データの内容を確認した所で、プログラミングに取り掛かっていきましょう。

まずは必要なライブラリをインポートしていきます!

初めにデータ計算・可視化に関する基本的なライブラリを一式インポートします。

# 数値計算に関する便利ライブラリ「numpy」をインポート
import numpy as np
# データの前処理に関する便利ライブラリ「pandas」をインポート
import pandas as pd

# データのグラフ化に関するライブラリ「matplotlib」をインポート」
import matplotlib.pyplot as plt
# データグラフ化に関するライブラリ「seaborn」をインポート
import seaborn as sns
# グラフの形式を定義する
plt.style.use("ggplot")

# グラフの日本語表記対応
from matplotlib import rcParams
rcParams["font.family"]     = "sans-serif"
rcParams["font.sans-serif"] = "Hiragino Maru Gothic Pro"

次に、今回使用するデータセットもインポートします。

# データセット読込
from sklearn.datasets import load_boston
boston = load_boston()

STEP3:データの前処理

実際に機械学習を実装する前に、データを扱いやすくするための前処理を行います。
データの値が欠損していないかを確認して整えたり、データを使える形に整えたりといった作業です。

■データをデータフレーム形式に変換
まずは、データを扱いやすいようにデータフレームの形式に直します。
pandasのDataFrame関数を使用して、下記のようにカラム名・目的変数を定義します。

# DataFrame作成
df = pd.DataFrame(boston.data)
df.columns = boston.feature_names # デフォルトではカラム名が定義されていないため定義する
df["MEDV"] = boston.target #"MEDV"のカラム(住宅価格の中央値)を目的変数として定義する

一度データフレーム形式に直す理由としては、
この後の工程で行や列を一括で指定するのが作業上ほぼ必須になるからです!
ランダムフォレストなどの機械学習をするための前段階としてはお作法のようなものだと思っておきましょう!

■欠損の確認・処理
次に欠損値の確認・処理を行います。
欠損値があるとデータに偏りが生じたり学習がうまくできなかったりなどして正しく学習が進まない可能性があります。

まずは「そもそも欠損値が存在しているか?」を確認していきます。
下図のようにlen関数でデータ件数を、describe関数でデータの個数を求めます。

# 説明変数・目的変数それぞれのデータ数を確認確認
print("説明変数")
print(f"{len(boston.data)}件")

print("目的変数")
print(f"{len(boston.target)}件")

# 説明変数のカラム数とカラム名を確認確認
print("変数名")
print(f"{len(boston.feature_names)}件")
print(boston.feature_names)

# descirbe関数でデータの要約統計量を確認
df.describe()

すると、

  • 説明変数・目的変数ともにデータは506件である

  • 説明変数のカラムは13個である

ことがわかります。

len関数の結果

また、describe関数のcount欄の結果から
「各説明変数のデータ件数が506件である=len関数で確認したデータ件数と一致している」ことが分かります。
つまりは欠損値がないことが確認できました。
今回は欠損値に対する処理は不要となります。

describe関数の出力結果

👨‍🏫豆知識👨‍🏫
もしここで欠損値があった場合は、下記のような方法で対処することができます。

  • 欠損がランダムに発生している場合…欠損値を含む行を削除する

  • 「欠損が発生する説明変数以外における一定の法則性」で欠損地が発生している場合…欠損値を別の値に置き換える(欠損していないデータの平均値や「NA」「0」など)

上記の判断と対処は一例ですが、状況に応じて処理方法が変わるので適切な判断が必要です。


STEP4:機械学習モデルの実装

それでは、前準備が終わったところでいよいよ機械学習を始めていきます!

■ライブラリのインポート
まずは今回のメインである機械学習のライブラリscikit-learnをインポートします。
※本当はSTEP2のところでまとめてやった方が効率がいいですが、今回は説明の順番的に本STEPで行います。

scikit-learnは世界的にも使用されているライブラリであり、非常に使いやすいので使用していきます。

# ランダムフォレスト用のライブラリ
from sklearn.ensemble import RandomForestRegressor
# Numpy配列を適切に分割するためのライブラリ
from sklearn.model_selection import train_test_split


■変数の定義
次に変数を定義します。
この後コードを書く際に値を参照しやすいように
説明変数をX、目的変数をYに代入します。

# 変数を定義
X = df.iloc[:, :-1].values # 説明変数(目的変数以外)
y = df['MEDV'].values      # 目的変数(住宅価格の中央値)

■データを分割する
ホールドアウト法
の理論に基づき、データを訓練用・テスト用に分割していきます。
テスト用データの割合は40%(test_size=0.4で指定)、実行ごとに値がバラつかないように値を固定(random_state=1)します。

# ホールドアウト法によりデータを訓練用・テスト用に分割する
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=1)

👨‍🏫豆知識👨‍🏫
ホールドアウト法のざっくりとした概要について補足します。
ホールドアウト法とは…
データを訓練用・テスト用に分割し、訓練用データで学習&作成したモデルの精度をテスト用データで検証するという手法です。

手元にあるデータを全て使って機械学習をしてしまうと、作成したモデルの精度が正しいかを判断する術がなくなってしまいます。
ぶっつけ本番のような状態ですね。

そこで、

  1. 手元にあるデータを訓練用とテスト用に分ける

  2. 訓練用データでモデルを作成する

  3. 作成したモデルを使って、テスト用データの中身を予測する

  4. 予測結果とテスト用データの中身を比べて、モデルの精度を評価する

という方法を用いて、手元にあるデータだけでモデル精度評価まで完了させる方法を取ります。

■ランダムフォレストを使って機械学習する
いよいよランダムフォレストを使った機械学習を実装していきます。
初めてに機械学習の詳細設定を行います。
下記のように記述します。

# ランダムフォレストの詳細を設定
forest = RandomForestRegressor(n_estimators=100,
                               criterion='mse', 
                               max_depth=None, 
                               min_samples_split=2, 
                               min_samples_leaf=1, 
                               min_weight_fraction_leaf=0.0, 
                               max_features='auto', 
                               max_leaf_nodes=None, 
                               min_impurity_decrease=0.0, 
                               bootstrap=True, 
                               oob_score=False, 
                               n_jobs=None, 
                               random_state=None, 
                               verbose=0, 
                               warm_start=False, 
                               ccp_alpha=0.0, 
                               max_samples=None
                              )

上記の記述は、
変数:forest=ランダムフォレストのクラス:RandomForestRegressor(ハイパーパラメータ
という構成になっています。

👨‍🏫豆知識👨‍🏫
ハイパーパラメータ
とは…
機械学習実行前に設定する学習の詳細設定のようなもので、
どのような方法で、どれくらいの範囲・回数で機械学習をするかを定める情報になります。
この学習の詳細設定の調整次第で、機械学習の精度は良くも悪くもなります。

ハイパーパラメータの中身が夥しいので嫌になる方もいるかと思いますが、実際はここまで細かい設定を行わなくても大丈夫です。
今回は解説用としてパラメータの値を順番に説明していきます。

  1. n_estimators…並行して学習させる「決定木」の数 / 数値を設定

  2. criterion…不純度を測定する基準(平均二乗誤差、平均絶対誤差など)/MSE,squared_errorなどを設定

  3. max_depth…決定木の深さの制限値。深さとは「Yes/No条件分岐を何回繰り返すか」だと思ってください。深くなりすぎると過学習の状態になるため、このパラメータで深くなりすぎないように制御できる / 数値やNoneを入力

  4. min_samples_split…決定木の深さを深めるために必要なサンプルの最小値。サンプルが最小値を下回った場合、葉(Leaves)となる/数字やNoneを設定

  5. min_samples_leaf…決定木において深さの階層を一段階増やすために必要となるデータ数の最小値。指定した値以上のデータ数を受け取らないと次の階層は作られない。/数字やNoneを設定

  6. min_weight_fraction_leaf…サンプルの重みを考慮した上でのmin_samples_leafに該当/数字を設定

  7. max_features…ランダムに指定する説明変数の数。(全ての説明変数がモデル学習に活用されるわけではなく、ランダムに割り振られます)/log2(入力項目数)、sqrt(平方根数)、Noneなどを設定

  8. max_leaf_nodes…作成される決定木の葉の数を、指定した値以下に制御する/数字やNoneを設定

  9. min_impurity_decrease…決定木の成長の早期停止するための閾値。不純度が指定の値より減少した場合、ノードを分岐し、不純度が指定の値より減少しなければ分岐を抑制。/数字やNoneを設定

  10. bootstrap…決定木作成時に復元抽出を実行するかどうか。/TrueやFalseを設定

  11. oob_score(out-of-bag)…ランダムな復元抽出にて抽出されなかったサンプルを活用するかどうか/TrueやFalseを設定

  12. n_jobs…モデル学習・予測に用いるスレッド数/数字やNoneを設定

  13. random_state…値を指定すればコード実行時に出力される値を固定できる/数字を設定

  14. verbose…モデル作成過程でメッセージを表示するかどうか/0か1を設定

  15. warm_start…Trueの場合はフィット(演算)し終えたモデルに追加の学習が可能/TrueやFalseを設定

  16. ccp_alpha…この値が大きいほどプルーニングされるノードの数が増加。プルーニングとは、精度低下をできるだけ抑えながら過剰な重みを排除するプロセスを指す/数値を設定

  17. max_samples…bootstrapがTrueの場合は、サンプリングする最大値を指定/数字やNoneを設定

※参考
【Python】ランダムフォレスト回帰のモデル作成と評価方法|scikit-learn・機械学習による回帰分析入門

次に、設定した値で学習を行います。
下記の記述で実装されます。

# ランダムフォレストを使った学習を実行
forest.fit(X_train, y_train)

STEP5:モデルの精度の評価

ここまで頑張って作成してきたモデルの予測の精度を評価していきます。

一般的な評価方法である、

  • RMSE(平均平方二乗誤差)

  • 決定係数R2

で評価します。

■ライブラリをインポート
まずはそれぞれの評価方法に適したライブラリをインポートします。

from sklearn.metrics import r2_score            # 決定係数のライブラリ
from sklearn.metrics import mean_squared_error  # RMSEのライブラリ

■作成したモデルを使って予測する
predict関数を用いて、訓練用データ(y_train_pred)とテスト用データ(y_test_pred)について予測値を算出します。

# 予測値(Train)
y_train_pred = forest.predict(X_train)

# 予測値(Test)
y_test_pred = forest.predict(X_test)

■精度を評価する
下記の記述でRMSE、R^2それぞれ評価します。

# 平均平方二乗誤差(RMSE)で評価
print('RMSE: %.2f, テスト: %.2f' % (
        mean_squared_error(y_train, y_train_pred, squared=False), # 学習
        mean_squared_error(y_test, y_test_pred, squared=False)    # テスト
      ))

# 決定係数(R^2)で評価
print('R^2: %.2f, テスト: %.2f' % (
        r2_score(y_train, y_train_pred), # 学習
        r2_score(y_test, y_test_pred)    # テスト
      ))

すると結果が下記のように出ました。

Train=訓練用、Test=テスト用の精度

RMSEは値が小さいほど精度が高く、R^2は値が1に近いほど精度が高くなります。

特にR^2の精度が1に近く精度が高い学習ができていることが伺えます。

👨‍🏫豆知識👨‍🏫
訓練データとテストデータでは、往々にしてテストデータの方が精度が悪く出ることが多いです。
訓練データでは、データの偏りが生じている可能性があるからですね。
仮にそんな結果が出ても焦らず、そういうものだと理解しましょう。


STEP6:モデルの精度を視覚的に確認する

STEP5では、数値からモデルの精度を評価しましたが
視覚的にもモデルの精度を確認しましょう。

残差(「予測値」と「観測値:実際の値」の差)を散布図にして確認してみましょう。
以下のコードで記述します。

# 予測値と訓練データの残差をプロット
plt.scatter(y_train_pred,             # グラフのx値(予測値)を定義  
            y_train_pred - y_train,   # グラフのy値(予測値と訓練データの差)を定義
            c='green',                 # プロットの色を指定
            marker='o',               # マーカーの種類
            s=40,                     # マーカーのサイズ
            alpha=0.7,                # グラフの透過度
            label='Train_Data')         # ラベル名


# 予測値とテストデータの残差をプロット
plt.scatter(y_test_pred,            
            y_test_pred - y_test, 
            c='orange',
            marker='o', 
            s=40,
            alpha=0.7,
            label='Test_Data')

# グラフの詳細設定を行う
plt.xlabel('predict')
plt.ylabel('Residual error')
plt.legend(loc='upper left')
plt.hlines(y=0, xmin=-20, xmax=60, lw=2, color='black')
plt.xlim([-20, 60])
plt.ylim([-50, 40])
plt.tight_layout()
plt.show()

すると以下のような散布図が表示されます。

X軸に予測値(predict)、Y軸に残差(Residual error)
黄色のプロットがテストデータの残差、緑のプロットが訓練データ

残差を散布図で見る際は、真ん中の太線0に近いほど精度が高いという評価になります。

グラフの内容から、

  • 訓練データ・テストデータ共に0に近く、モデルの精度が高い

  • 訓練データ・テストデータのとる値が近く、訓練データを使っても実際のデータを使っても予測内容にそこまで差がない(予測性能誤差が小さい)

ことがわかります。

以上のSTEP5,6の結果から本モデルは性能が高いことが言えます。


STEP7:さらに精度を高める

比較的高い精度のモデルができたものの、さらに高い精度を目指すためにはハイパーパラメータの値を調整するなど様々な方法が考えられます。

本記事では学習に使う説明変数に着目して調整していきたいと思います。

今回は、データ内のすべての説明変数を使ってモデルを作成しましたが
「目的変数と相関のある説明変数」だけに絞ってモデルを作成した方が精度が上がる可能性もあります。

どういう事かというと…
今回のBoston Housingのデータセットには13項目の説明変数がありましたが、
値が変動すればなるほど目的変数(住宅価格の中央値)も変動する説明変数もあれば、
値が変動しても目的変数は変動しない説明変数もあるのです。
※ある値が変動が他の値の変動に影響している状態を「相関がある」といいます。

そこで、今回の13項目の説明変数の中で、目的変数と相関の高い説明変数を特定し
その説明変数だけを使用したモデルを作成することで精度が高まる可能性がある、というわけです。

果たして本当なのか?
まずは相関が高い説明変数を見つける作業を行なっていきます。

■ヒートマップを使う
相関がある説明変数を見つける方法として、主成分分析ヒートマップ分析などがありますが、
今回はヒートマップ分析を行います。
※ここでいうヒートマップとは、「項目ごとの相関の一覧表を作る」とでもお考えください。

下記コードを記述します。

plt.figure(figsize=(10,10))
sns.heatmap(df.corr(), annot=True)
plt.tight_layout()
plt.show()

すると、以下のようなヒートマップが表示されます。

説明変数13項目+目的変数の相関が一覧に

今回は目的変数(MEDV)と相関のある説明変数を特定したいため、一番右の列を参照します。

相関を表す相関係数は-1〜1の範囲で出力され、下記のようなことが読み取れます。

  • 1に近ければ近いほど「正の相関」がある(一方の値が高まれば高まるほど、もう一方の値も高まる)

  •  -1に近ければ近いほど「負の相関」がある(一方の値が高まれば高まるほど、もう一方の値は小さくなる)

  • 0に近ければ近いほど「相関がない」(一方の値が変化しても、もう一方の値は変化しない)

今回は下記4つの説明変数が、目的変数と相関があるといえます。

  • 「RM」(平均部屋数/一戸)…相関係数が0.7

  • 「LSTAT」(低所得者の割合)…相関係数が-0.74

  • 「PTRATIO」(地域人口毎の学校教師1人あたりの生徒数)…相関係数が-0.51

  • 「NOX」(窒素酸化物の濃度(単位:pphm))…相関係数が-0.48

数字上だけでなく、自分の感覚としても納得できる相関になっているか考えてみます。

「RM」部屋数が多いほど住宅価格が上がる、というのは物件の条件がいいので納得できます。
「LSTAT」低所得者が少ないほど住宅価格が上がる、というのは地域の治安がいいことを示すので納得です。
「PTRATIO」先生1人に対する生徒数が少ないほど住宅価格が上がる、というのは1人1人の生徒がしっかり見てもらえるということで負の相関があるのかなと推測します。
「NOX」窒素酸化物の濃度が低いほど住宅価格が上がる、というのは地域環境がいいことを示すので納得です。

このようにデータ上の相関を人間の頭で考えても妥当性があるものになっているかを考えるプロセスは重要かなと思います。

例えば「窒素酸化物の濃度が低いほど住宅価格が上がる」など普通に考えると妥当性のない相関はデータが間違っている(ラベル名が間違っている、データに偏りがあるなど)の可能性を疑った方がいいかもしれません。

それでもデータに誤りがないことが確認できたならば、その相関には大発見があるかもしれません。

■相関がある説明変数だけでモデルを作ってみる
さて、前項で特定した相関がある説明変数だけでモデルを作ってみます。

下記のようなコードで記述していきます。
※ライブラリのインポートは省略します

# 変数定義
# 相関がある説明変数リストを作成
highlist=['LSTAT', 'RM', 'PTRATIO', 'INDUS'] 
X = df[highlist].values # 説明変数(目的変数以外)
y = df['MEDV'].values      # 目的変数(住宅価格の中央値)

# ホールドアウト法によりデータを訓練用・テスト用に分割する
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=1)

# ランダムフォレスト回帰を実行!
forest = RandomForestRegressor(n_estimators=100,
                               criterion='mse', 
                               max_depth=None, 
                               min_samples_split=2, 
                               min_samples_leaf=1, 
                               min_weight_fraction_leaf=0.0, 
                               max_features='auto', 
                               max_leaf_nodes=None, 
                               min_impurity_decrease=0.0, 
                               bootstrap=True, 
                               oob_score=False, 
                               n_jobs=None, 
                               random_state=None, 
                               verbose=0, 
                               warm_start=False, 
                               ccp_alpha=0.0, 
                               max_samples=None
                              )
# ランダムフォレストを使った学習を実行
forest.fit(X_train, y_train)

from sklearn.metrics import r2_score            # 決定係数
from sklearn.metrics import mean_squared_error  # RMSE

# 予測値(Train)
y_train_pred = forest.predict(X_train)

# 予測値(Test)
y_test_pred = forest.predict(X_test)

# 平均平方二乗誤差(RMSE)
print('RMSE Train: %.2f, Test: %.2f' % (
        mean_squared_error(y_train, y_train_pred, squared=False), # 学習
        mean_squared_error(y_test, y_test_pred, squared=False)    # テスト
      ))

# 決定係数(R^2)
print('R^2 Train: %.2f, Test: %.2f' % (
        r2_score(y_train, y_train_pred), # 学習
        r2_score(y_test, y_test_pred)    # テスト
      ))


# 予測値と訓練データの残差をプロット
plt.scatter(y_train_pred,             # グラフのx値(予測値)を定義  
            y_train_pred - y_train,   # グラフのy値(予測値と訓練データの差)を定義
            c='green',                 # プロットの色を指定
            marker='o',               # マーカーの種類
            s=40,                     # マーカーのサイズ
            alpha=0.7,                # グラフの透過度
            label='Train_Data')         # ラベル名


# 予測値とテストデータの残差をプロット
plt.scatter(y_test_pred,            
            y_test_pred - y_test, 
            c='orange',
            marker='o', 
            s=40,
            alpha=0.7,
            label='Test_Data')

# グラフの詳細設定を行う
plt.xlabel('predict')
plt.ylabel('Residual error')
plt.legend(loc='upper left')
plt.hlines(y=0, xmin=-20, xmax=60, lw=2, color='black')
plt.xlim([-20, 60])
plt.ylim([-50, 40])
plt.tight_layout()
plt.show()

すると、下記のような結果となりました。

全説明変数で実行した時よりも精度が落ちた…


すべての説明変数でモデル作成した際は、R^2が0.87だったので精度が落ちました。。
一部の説明変数だけに絞った事で、ランダムフォレストの特徴である「データが少ないと過学習しやすい」という現象が起きてしまいました。

■「相関がある」と定義する範囲を広げる
先ほどは範囲を絞りすぎて精度が落ちたので、今度は「相関がある」と定義する範囲を広げてみます。
相関係数が高い説明変数上位7個まで、上位10個までのモデルをそれぞれ作成します。

【最初のモデル】説明変数13個  ※すべての説明変数
【相関がある説明変数限定モデル】説明変数4個 ※相関係数上位4個まで
NEW→【調整版モデルその1】説明変数7個 ※相関係数上位7個まで
NEW→【調整版モデルその2】説明変数10個 ※相関係数上位10個まで
NEW→【調整版モデルその3】説明変数12個 ※相関係数上位12個まで

コードは、変数の定義の部分だけ変更しています。

  • 上位7個までの記述

# 変数定義
# 相関がある説明変数リスト
highlistBEST7=['LSTAT', 'RM', 'PTRATIO', 'INDUS','TAX','NOX','CRIM']
X = df[highlistBEST7].values # 説明変数(目的変数以外)
y = df['MEDV'].values      # 目的変数(住宅価格の中央値)
  • 上位10個までの記述

# 変数定義
# 相関がある説明変数リスト
highlistBEST10=['LSTAT', 'RM', 'PTRATIO', 'INDUS','TAX','NOX','CRIM','ZN','AGE','RAD']
X = df[highlistBEST10].values # 説明変数(目的変数以外)
y = df['MEDV'].values      # 目的変数(住宅価格の中央値)
  •  上位12個までの記述

# 変数定義
# 相関がある説明変数リスト
highlistBEST12=['LSTAT', 'RM', 'PTRATIO', 'INDUS','TAX','NOX','CRIM','ZN','AGE','RAD','B','DIS']
X = df[highlistBEST12].values # 説明変数(目的変数以外)
y = df['MEDV'].values      # 目的変数(住宅価格の中央値)


結果は以下のようになりました。

相関係数上位7個までの説明変数だけで作成したモデル
相関係数上位10個までの説明変数だけで作成したモデル
相関係数上位12個までの説明変数だけで作成したモデル

当初のR^2が0.87だったので、上位12個に絞った場合に精度が0.01上がりました


結果に対する考察


説明変数の数に着目して精度向上のための調整を行ってきましたが
主に下記のような事がわかったかと思います。

  • 今回のデータでは、ハイパーパラメータなどの設定をそのままにするのであれば
    説明変数を極端に数を絞りすぎても精度が下がる

  • 使用する説明変数の微調整により精度が若干上げられる

なお、最も相関係数が低い説明変数は「CHAS」川沿いに立地しているかどうか)であり、最後の「相関係数上位12個のみ使った学習モデル」ではこの説明変数を除外する事で精度が上がりました。

本データから学べる教訓としては、「ランダムフォレストにおいては目的変数をいかに絞るかという考え方ではなく、精度向上に悪影響を及ぼしているデータを除外する」という考えで取り掛かった方がいい、ということかなと思います。


まとめ


本記事では欠損値の検知、実装方法、精度の高め方など基本的な流れに沿ってきましたが、
学習をして精度を評価しまた精度を高める調整する…という一連の流れが体感いただけたかと思います。

本記事では、一部の手法しか紹介していませんが
主成分分析、ハイパーパラメータの調整、またランダムフォレスト以外のモデルを実装するなど様々な方法でより精度の高いモデルを目指していけるかと思います。

ぜひその他の手法も学習してみることをオススメします。


筆者について


最後に、筆者の自己紹介をさせていただきます。

筆者は、普段会社員としてWEBマーケティングの仕事をしております。
WEBマーケティング業務をする中で、自身が文系出身な事もあり、データ分析に関するリテラシーのなさを痛感する場面がよくありました。。

そこで、自身のスキルアップのためプログラミングスクール「Aidemy」さんを受講することに決めました。
Aidemyさんでは、Pythonをメインとしたデータ分析・機械学習の基本的なやり方や概念を学ぶ事ができ、とても充実した学習期間を過ごすことができました。

データ分析の手法を学ぶ事ができ・データ分析の楽しさを知ることができたので
今後は学んだ知識を活かしてKaggleなどのデータを実際に分析していき、スキルを高めていきたいと思います。

本記事は以上となります。
最後までお読みいただきありがとうございました!


参考文献


【Python】ランダムフォレスト回帰のモデル作成と評価方法|scikit-learn・機械学習による回帰分析入門

この記事が気に入ったらサポートをしてみませんか?