ランダムフォレスト② (決定木の数を変更)

ハイパーパラメータで生成する決定木の数を変更してみます。

決定木の数を変更

n_estimatorsの値を10→3に変更し、ランダムフォレストのモデルを構築します。

[Google Colaboratory]

1
rf_change_param = RandomForestRegressor(n_estimators=3, max_depth=20, random_state=0).fit(X_train,y_train)

[実行結果]

残差プロット (n_estimators=3)

予測値を算出し、残差プロットを表示します。

[Google Colaboratory]

1
2
3
4
5
6
7
y_train_pred = rf_change_param.predict(X_train)
y_test_pred = rf_change_param.predict(X_test)

y_train_pred = np.expand_dims(y_train_pred, 1)
y_test_pred = np.expand_dims(y_test_pred, 1)

residual_plot(y_train_pred, y_train, y_test_pred, y_test)

[実行結果]

決定木の数が10本のときとあまり変化は見られません。

精度評価スコア (n_estimators=3)

精度評価スコアを表示します。

[Google Colaboratory]

1
2
3
4
print("訓練データスコア")
get_eval_score(y_train,y_train_pred)
print("テストデータスコア")
get_eval_score(y_test,y_test_pred)

[実行結果]

決定木の数が10本(前回記事)のときとほぼ変わらない結果となっています。

基本的に決定木の数を増やしていくほど精度は上がっていきますが、その精度向上は徐々に収束していきます。

また、ランダムフォレストモデルでは対象データが膨大になったり、決定木の数や深さを増やすほど、処理時間が延びCPU負荷も増えますので注意が必要です。

ランダムフォレスト① (構築・評価)

ランダムフォレスト

ランダムフォレストとは、決定木を複数生成して、それぞれの結果を総合的に判断して学習を行うアルゴリズムです。

単純な決定木ではテストデータに対して比較的高精度で予測はできていましたが、過学習に陥りやすいという問題がありました。

その問題を解消するためにランダムフォレストでは、それぞれの決定木で過学習が起きてしまうことを前提に異なる決定木モデルを複数生成し、それぞれの結果の平均をとることで、予測精度は保ちつつ過学習を抑制しようという仕組みとなっています。

複数のモデルを組み合わせて、高精度なモデルを作る手法のことをアンサンブル法と言います。

ランダムフォレストモデル構築

ランダムフォレストモデルの構築を行います。

scikit-learnRandomForestRegressorクラスを使用します。

[Google Colaboratory]

1
2
3
from sklearn.ensemble import RandomForestRegressor

rf = RandomForestRegressor(n_estimators=10, max_depth=20, random_state=0).fit(X_train,y_train)

n_estimatorsは、ランダムフォレストのハイパーパラメータの1つで、生成する決定木の数を指定します。(3行目)

[実行結果]

残差プロット

予測値を算出し、結果を残差プロットで表示します。

[Google Colaboratory]

1
2
3
4
5
6
7
y_train_pred = rf.predict(X_train)
y_test_pred = rf.predict(X_test)

y_train_pred = np.expand_dims(y_train_pred, 1)
y_test_pred = np.expand_dims(y_test_pred, 1)

residual_plot(y_train_pred, y_train, y_test_pred, y_test)

[実行結果]

一部大きく外れているものもありますが、かなり良く予測できています。

精度評価スコア

最後に精度評価スコアを表示します。

[Google Colaboratory]

1
2
3
4
print("訓練データスコア")
get_eval_score(y_train,y_train_pred)
print("テストデータスコア")
get_eval_score(y_test,y_test_pred)

[実行結果]

テストデータのR2スコアが0.75となり、単純な決定木データよりは良い結果となりました。

また、単純な決定木でmax_depthを20にした場合は過学習に陥っていましたが、ランダムフォレストでは10本の決定木の平均をとっているため、過学習には陥っていません。

決定木モデル⑤ (最小サンプル数を変更)

今回は、リーフノードのサンプル数を変更してみます。

最小サンプル数変更

リーフノードの最小サンプル数を変更するためには、min_samples_leafを設定します。

min_samples_leafのデフォルト値は1となっており、ノード数が1になるまで分岐をし続けることを意味します。

深さ(max_depth)は前回過学習に陥った20としたままで、最小サンプル数(min_samples_leaf)1から5に変更してみます。(1行目)

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
tree_reg_samples_5 = DecisionTreeRegressor(max_depth=20, min_samples_leaf=5,random_state=0).fit(X_train,y_train)

y_train_pred = tree_reg_samples_5.predict(X_train)
y_test_pred = tree_reg_samples_5.predict(X_test)

y_train_pred = np.expand_dims(y_train_pred, 1)
y_test_pred = np.expand_dims(y_test_pred, 1)

residual_plot(y_train_pred, y_train, y_test_pred, y_test)

[実行結果]

中央下にいくつか外れ値がありますが、誤差の範囲が±10程度に集まっていてなかなかの結果になっていると思います。

また訓練データの誤差も適度にばらついていて、過学習の傾向が減っているようです。

精度評価スコア (max_depth=20, min_samples_leaf=5)

精度評価スコアを算出します。

[Google Colaboratory]

1
2
3
4
print("訓練データスコア")
get_eval_score(y_train,y_train_pred)
print("テストデータスコア")
get_eval_score(y_test,y_test_pred)

[実行結果]

リーフノードの最小サンプル数を5に制限したことで、テストデータのR2スコアが0.71となりこれまでで最も良い結果となりました。

また、訓練データのR2スコアが1.0から0.91となり、過学習傾向が軽減されていることが分かります。

決定木は過学習に陥りやすい傾向はありますが、深さや最小サンプル数などのハイパーパラメータを調整することで過学習をある程度抑えることができ、モデルの精度改善を行うことができます。

決定木モデル④ (深さを変更)

決定木の深さを定義するハイパーパラメータであるmax_depthを変更します。

深さの変更 (max_depth=5)

決定木の深さ(max_depth)を5に変更してみます。

[Google Colaboratory]

1
tree_reg_depth_5 = DecisionTreeRegressor(max_depth=5, random_state=0).fit(X_train,y_train)

次に予測値を出力し、残差プロットで可視化します。

[Google Colaboratory]

1
2
3
4
5
6
7
y_train_pred = tree_reg_depth_5.predict(X_train)
y_test_pred = tree_reg_depth_5.predict(X_test)

y_train_pred = np.expand_dims(y_train_pred, 1)
y_test_pred = np.expand_dims(y_test_pred, 1)

residual_plot(y_train_pred, y_train, y_test_pred, y_test)

[実行結果]

深さを5に変更したことにより、誤差のばらつきが少し小さくなりました。

精度評価スコア (max_depth=5)

精度評価スコアを表示します。

[Google Colaboratory]

1
2
3
4
print("訓練データスコア")
get_eval_score(y_train,y_train_pred)
print("テストデータスコア")
get_eval_score(y_test,y_test_pred)

[実行結果]

テストデータのR2スコアが0.7となり、精度が改善しました。

しかし、訓練データのR2スコアも0.85から0.92と上昇しています。

このように決定木の深さを深くするとモデルの精度が上がる一方、訓練データに過度に適合する過学習のリスクも上がってしまいます。

深さの変更 (max_depth=20)

今度は、決定木の深さ(max_depth)を20に変更してみます。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
tree_reg_depth_20 = DecisionTreeRegressor(max_depth=20, random_state=0).fit(X_train,y_train)

y_train_pred = tree_reg_depth_20.predict(X_train)
y_test_pred = tree_reg_depth_20.predict(X_test)

y_train_pred = np.expand_dims(y_train_pred, 1)
y_test_pred = np.expand_dims(y_test_pred, 1)

residual_plot(y_train_pred, y_train, y_test_pred, y_test)

[実行結果]

精度評価スコア (max_depth=20)

精度評価スコアを表示します。

[Google Colaboratory]

1
2
3
4
print("訓練データスコア")
get_eval_score(y_train,y_train_pred)
print("テストデータスコア")
get_eval_score(y_test,y_test_pred)

[実行結果]

訓練データのR2スコアは1.0となり完全に適合していますが、テストデータのR2スコアは深さ5のときよりも下がっています。

これは過学習に陥っていると判断できます。

決定木モデル③ (評価)

前回構築した決定木モデルの評価を行います。

予測値算出

まずは予測値を算出します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
y_train_pred = tree_reg.predict(X_train)
y_test_pred = tree_reg.predict(X_test)

import numpy as np

y_train_pred = np.expand_dims(y_train_pred, 1)
y_test_pred = np.expand_dims(y_test_pred, 1)

print(len(y_train_pred))
print(y_train_pred[:5])
print(len(y_test_pred))
print(y_test_pred[:5])

[実行結果]

可視化

散布図で予測値を可視化します。

[Google Colaboratory]

1
2
3
4
5
6
7
plt.scatter(y_train_pred, y_train, label="train")
plt.scatter(y_test_pred, y_test, label="test")
plt.xlabel("Pred")
plt.ylabel("True")
plt.title("Scatter Plot")
plt.legend()
plt.show()

[実行結果]

線形回帰とは異なる分布になっています。

グラフからは、何種類かの特定の値が予測値として出力されているようです。

予測値として出力される値のパターンはリーフノードの数に依存します。

前回出力した樹形図のリーフノード数は8個だったので、予測値のパターンも8種類ということになります。

残差プロット

次に実測値と予測値の誤差をプロットしてみます。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
def residual_plot(y_train_pred, y_train, y_test_pred, y_test):
plt.scatter(y_train_pred, y_train_pred - y_train, label="train")
plt.scatter(y_test_pred, y_test_pred - y_test, label="test")
plt.plot([0, 50], [0,0] ,color="red")
plt.xlabel("Pred")
plt.ylabel("Pred - True")
plt.title("Residual Plot")
plt.legend()
plt.show()

residual_plot(y_train_pred, y_train, y_test_pred, y_test)

[実行結果]

右側に外れ値がいくつかありますが、誤差の範囲が±10程度と、まずまずの結果となっていると思います。

精度評価スコア

精度評価スコアを算出します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np

def get_eval_score(y_true,y_pred):
mae = mean_absolute_error(y_true,y_pred)
mse = mean_squared_error(y_true,y_pred)
rmse = np.sqrt(mse)
r2score = r2_score(y_true,y_pred)

print(f" MAE = {mae}")
print(f" MSE = {mse}")
print(f" RMSE = {rmse}")
print(f" R2 = {r2score}")

print("訓練データスコア")
get_eval_score(y_train,y_train_pred)
print("テストデータスコア")
get_eval_score(y_test,y_test_pred)

[実行結果]

R2スコアが0.66であり、十分な精度となっていません。

次回は、ハイパーパラメータの設定で決定木の深さを変更し、スコアの向上を図ります。

決定木モデル② (データ準備・決定木モデル構築)

決定木モデルを構築するための、データを準備します。

データ準備

ボストンの住宅価格データを読み込みます。

[Google Colaboratory]

1
2
3
4
5
6
7
8
from sklearn.datasets import load_boston
boston = load_boston()

import pandas as pd
df = pd.DataFrame(boston.data,columns=boston.feature_names)
df["MEDV"] = boston.target

display(df.head())

[実行結果]

説明変数を変数Xに、目的変数を変数yに代入します。

[Google Colaboratory]

1
2
3
4
5
X = df[boston.feature_names]
y = df[["MEDV"]]

display(X.head())
display(y.head())

[実行結果]

訓練データと検証データを7対3の割合で分割します。

[Google Colaboratory]

1
2
3
4
5
6
7
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=0.3,random_state=0)

print(len(X_train))
display(X_train.head())
print(len(X_test))
display(X_test.head())

[実行結果]

以上で、データの準備は完了です。

決定木モデルの構築

決定木モデルを構築するには、scikit-learnDecisionTreeRegressorクラスを使用します。

max_depthは決定木の層の深さの上限を設定するパラメータで、今回は3階層(max_depth=3)の決定木にしています。

[Google Colaboratory]

1
2
from sklearn.tree import DecisionTreeRegressor
tree_reg = DecisionTreeRegressor(max_depth=3, random_state=0).fit(X_train,y_train)

これで決定木モデルの構築と学習が完了しました。

決定木モデルの描画

学習により生成された決定木を描画します。

[Google Colaboratory]

1
2
3
4
5
6
from sklearn import tree
import matplotlib.pyplot as plt
%matplotlib inline

plt.figure(figsize=(20,8))
tree.plot_tree(tree_reg,fontsize=8)

[実行結果]

各ノードの中身に表示されている値は次のような意味となります。

  • X[n] <= m
    次のノードへの分岐条件
  • mae
    ノードの不純度(valueと実測値の平均二乗誤差)
  • samples
    ノードに含まれるデータ件数
  • value
    ノードに含まれるデータの平均値

末端になるノード(リーフノード)のvalueが予測値の候補となっています。

次回は、構築した決定木モデルの評価を行います。

決定木モデル① (アルゴリズム)

今回からは、決定木を使った回帰の手法を試していきます。

決定木とは回帰や分類に使われる手法で、質問に対する分岐( Yes / No )を行う階層的な木構造で学習を行います。

線形回帰では各説明変数の重みや切片を学習によって定義しましたが、決定木ではそれぞれの分岐条件を学習によって定義します。

ハイパーパラメータで決定木の深さや各分岐先の最小サンプル数を調整することにより、過学習の抑制や精度の向上を図ることもできます。

決定木アルゴリズム

決定木系のアルゴリズムは、非常に直感的で分かりやすく、データの尺度に左右されにくい点から、いろいろな場面で利用されます。

決定木を使ったアルゴリズムには下記のようなものがあります。

  • 決定木
    木構造(樹形図)を用いて予測を行う手法。
  • ランダムフォレスト
    決定木を複数生成し予測を行う手法。
    各決定木を並列に扱い、結果を総合的に判断する。
  • 勾配ブースティング決定木
    決定木を複数生成し予測を行う手法。
    逐次的に決定木を増やしていく。
    生成済みの決定木の結果を加味し新たな決定木を生成する。

次回は、決定木モデルを構築するためのデータを準備します。

線形回帰⑪ (まとめ)

線形系のアルゴリズムについてまとめます。

  • 単回帰
    1つの説明変数から1つの目的変数を予測する。
    もっとも扱いやすく、モデルの解釈もしやすい。
    精度面においては複数の変数で説明を行う重回帰のほうが優る。
  • 重回帰
    複数の説明変数から1つの目的変数を予測する。
    単回帰よりも精度は上がりやすいが、過学習に陥りやすい。
  • LASSO回帰
    重回帰に正則化項(L1ノルム)を付与し、変数の重みを0に近づける。
    特定の変数の重みを0にする。
    重みを小さくすることで過学習のリスクを軽減できる。
    使用する説明変数が減ることでモデルがシンプルになり、解釈性が上がる。
    重要と思われる変数の重みまで0にしてしまう可能性がある。
  • リッジ回帰
    重回帰に正則化項(L2ノルム)を付与し、変数の重みを0に近づけるが、0にはならない。
    重みを小さくすることで過学習のリスクを軽減できる。
    全ての変数を使用するため、モデルの解釈性は改善されない。

LASSO回帰、リッジ回帰は重回帰の上位互換といえるアルゴリズムです。

全ての変数を使用したい場合はリッジ回帰、一部の変数が削除されても問題ない場合はLASSO回帰を使うのが良いでしょう。

線形回帰⑩ (リッジ回帰)

リッジ回帰を使ったモデルを構築します。

リッジ回帰

リッジ回帰は、L2ノルムという正則化項を使用したアルゴリズムです。

前回までのLASSO回帰は各変数の重みを0に近づけつつ、特定の変数の重みを0にするという特性がありました。

リッジ回帰は各変数の重みを0に近づけますが、完全な0にはなりません。

そのため、LASSO回帰のようにモデルの解釈を簡単にするという特徴はありませんが、モデルの設計上、全ての説明変数が重要であるというケースではリッジ回帰の方が適していると言えます。

リッジ回帰モデルの構築

リッジ回帰モデルの構築はscikit-learnRidgeクラスを使います。

[Google Colaboratory]

1
2
3
from sklearn.linear_model import Ridge

ridge = Ridge().fit(X_train_scaled, y_train)

リッジ回帰モデルの構築と学習が完了しました。

続いて、重回帰分析の時にスケーリングしたデータを使って予測を行います。

[Google Colaboratory]

1
2
3
4
5
y_train_pred = ridge.predict(X_train_scaled)
y_test_pred = ridge.predict(X_test_scaled)

print(y_train_pred[:5])
print(y_test_pred[:5])

[実行結果]

残差プロット

残差プロットを表示します。

[Google Colaboratory]

1
residual_plot(y_train_pred, y_train, y_test_pred, y_test)

[実行結果]

精度評価スコア

精度評価スコアを表示します。

[Google Colaboratory]

1
2
3
4
print("訓練データスコア")
get_eval_score(y_train,y_train_pred)
print("テストデータスコア")
get_eval_score(y_test,y_test_pred)

[実行結果]

精度評価スコアはLASSO回帰(alpha=1.0)よりもやや高く、重回帰と同程度となりました。

重み

各説明変数の重みを表示します。

[Google Colaboratory]

1
2
3
for i, (col, coef) in enumerate(zip(boston.feature_names, ridge.coef_[0])):
print(f"w{i}({col}) = {coef}")
print(f"b = {ridge.intercept_[0]}")

[実行結果]

LASSO回帰では多くの説明変数が0となっていましたが、リッジ回帰では重みが完全に0となっている説明変数はありません。

重回帰と比べると、全体的に変数の重みは小さくなっています。

交差検証

リッジ回帰についても、LASSO回帰と同様にscikit-learnに交差検証用のRidgeCVというクラスがあり交差検証が可能です。


次回は、これまで扱ってきた単回帰、重回帰、LASSO回帰、リッジ回帰といういろいろな線形系のアルゴリズムについてまとめます。

線形回帰⑨ (交差検証)

前回は、ハイパーパラメータのalphaを1つだけ指定して、学習・評価を行いましたが、最適なalphaを見つけるためには複数の値を設定し、その結果を確認する必要があります。

LassoCVクラスを使うと、一度に複数のalphaの結果を確認することができます。

交差検証

LassoCVクラスは複数のalphaで交差検証を行い、最も精度が高かった時のalphaを結果として表示することができます。

alphaが[0.1, 0.5, 1, 5, 10]の場合の交差検証を行うコードは以下の通りです。

[Google Colaboratory]

1
2
3
from sklearn.linear_model import LassoCV

lasso_cv = LassoCV(alphas=[0.1, 0.5, 1, 5, 10]).fit(X_train_scaled, y_train)

[実行結果]

交差検証が正常に終了しました。

重み確認

交差検証により、最も精度のよかった時のalphaでモデルが構築されています。

最適なalphaと各説明変数の重みを確認します。

[Google Colaboratory]

1
2
3
4
print(f"alpha = {lasso_cv.alpha_}")
for i, (col, coef) in enumerate(zip(boston.feature_names, lasso_cv.coef_)):
print(f"w{i}({col}) = {coef}")
print(f"b = {lasso_cv.intercept_}")

[実行結果]

0.1のときの精度が最も高いということが確認できました。

またペナルティが弱くなったため、重みが0になる説明変数がなくなりました。

精度評価スコア

精度評価スコアを確認します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
y_train_pred = lasso_cv.predict(X_train_scaled)
y_test_pred = lasso_cv.predict(X_test_scaled)

y_train_pred = np.expand_dims(y_train_pred, 1)
y_test_pred = np.expand_dims(y_test_pred, 1)

print("訓練データスコア")
get_eval_score(y_train,y_train_pred)
print("テストデータスコア")
get_eval_score(y_test,y_test_pred)

[実行結果]

精度評価スコアが以前のモデルよりも向上しています。

交差検証を使うと、効率的にパラメータの調整を行うことができるので、積極的に活用していきましょう。

次回は、リッジ回帰を行います。


Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×