NGBoostの解説


Stanford ML Groupは予測の不確かさを扱える新しい勾配ブースティング「NGBoost」を発表された。今回の記事はNGBoostを解説と実験したいです。

関連記事: 決定木分析ランダムフォレストXgboostLightGBM

目次:

1.  NGBoostとは
2.  NGBoostの特徴
3. 実験・コード
__3.1 データ読み込み
__3.2 Xgboost、LightGBM、NGBoost
__3.3 モデル評価
__3.4 可視化
4. まとめ

1.  NGBoostとは

勾配ブースティング(Gradient Boosting)は、いくつかの弱学習器(または基本学習器)が加法アンサンブルで結合されています。NGBoostアルゴリズムは、ブースティングを使用して条件付き確率分布のパラメーターを推定する、確率的予測のための教師あり学習方法です。

NGBoostの論文:  NGBoost: Natural Gradient Boosting for Probabilistic Prediction

https://arxiv.org/abs/1910.03225

https://stanfordmlgroup.github.io/projects/ngboost/

アルゴリズムには3つの構成要素があります。
Base learner (f)
Parametric probability distribution (Pθ)
Proper scoring rule (S)

Base Learner
決定木などのアルゴリズム

Probability Distribution
出力する確率分布、正規分布、ラプラス分布などの分布設定

Scoring rule
MLE, CRPSなどのスコア関数

2.  NGBoostの特徴

NGBoostの特徴は、確率分布のパラメーターを決定する問題として、自然勾配をキャストして勾配ブーストを実行することです。
通常の勾配は、マルチパラメーター確率分布(正規分布など)の学習には非常に不適切な場合があります。 自然な勾配を使用した学習ダイナミクスは、下記の確率回帰の例に見られるように、はるかに安定し、より良い適合をもたらす傾向があります。

3. 実験・コード

データセット:scikit-learnの load_boston
モデル:Xgboost、LightGBM、NGBoost
モデル評価: RMSE(Root Mean Square Error)(二乗平均平方根誤差)
環境:Google Colab (CPU)
環境設定:
ライブラリのインストール

!pip install ngboost

ライブラリのインポート

# ライブラリのインポート
import pandas as pd
import numpy as np
from tqdm import tqdm
from datetime import datetime

# データロード
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split

# モデル
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor
from ngboost.ngboost import NGBoost
from ngboost.learners import default_tree_learner
from ngboost.scores import MLE
from ngboost.distns import Normal, LogNormal

# 検定
from sklearn.metrics import mean_squared_error
from math import sqrt

# 可視化
import matplotlib.pyplot as plt

3.1 データ読み込み

# サンプルデータの読み込み

seed = 20
result = {}

# サンプルデータロード
loaded_data = load_boston()
X_train, X_test, y_train, y_test = train_test_split(loaded_data.data, loaded_data.target, test_size=0.2, random_state=seed)
print('X_train Shape:', X_train.shape)
print('y_train Shape:', y_train.shape)
print('X_test Shape:', X_test.shape)
print('y_test Shape:', y_test.shape)

X_train Shape: (455, 30)
y_train Shape: (455,)
X_test Shape: (114, 30)
y_test Shape: (114,)

3.2 Xgboost、LightGBM、NGBoost

xgboost

# モデル作成
start_time = datetime.now()
xgb_rg = XGBRegressor(objective='reg:squarederror')
xgb_rg.fit(X_train, y_train)

# 推論
y_pred_xgb = pd.DataFrame(xgb_rg.predict(X_test))

# 検証
result[('1_xgboost', 'RMSE')] = round(sqrt(mean_squared_error(y_test,y_pred_xgb)),4)
result[('1_xgboost', 'Runtime')] = datetime.now() - start_time

lightGBM

# モデル作成
start_time = datetime.now()
lgbm_rg = LGBMRegressor(eval_metric='rmse')
lgbm_rg.fit(X_train, y_train)

# 推論
y_pred_lgb = pd.DataFrame(lgbm_rg.predict(X_test))

# 検証
result[('2_lightGBM', 'RMSE')] = round(sqrt(mean_squared_error(y_test,y_pred_lgb)),4)
result[('2_lightGBM', 'Runtime')] = datetime.now() - start_time

NGBoost

# モデル作成
start_time = datetime.now()
ngb_rg = NGBoost(Base=default_tree_learner, Dist=Normal, natural_gradient=True, verbose=False)
ngb_rg.fit(X_train, y_train, X_val=X_test, Y_val=y_test)

# 推論
y_pred_ngb = pd.DataFrame(ngb_rg.predict(X_test))
y_dists = ngb_rg.pred_dist(X_test)

# 検証
result[('3_NGBoost', 'RMSE')] = round(sqrt(mean_squared_error(y_test,y_pred_ngb)),4)
result[('3_NGBoost', 'Runtime')] = datetime.now() - start_time

3.3 モデル評価

モデルのデフォルトのパラメータにしました。なのでどちらが性能がいいかはわかりませんが、NGBoostはよい結果になっています。

# モデル評価
result_df = pd.Series(result).unstack().reindex(columns=['RMSE', 'Runtime'])
result_df

3.4 可視化

テストデータの分布とそれぞれのXgboost、LightGBM、NGBoostの予測値を可視化しました。テストデータが114件あるので、グラフは114個出力されます。モデルの予測結果は正規分布を利用しているので、平均を点推定していることがわかります。

# データ処理

offset = np.ptp(y_pred_ngb)*0.1
y_range = np.linspace(min(y_test)-offset, max(y_test)+offset, 200).reshape((-1, 1))
dist_values = y_dists.pdf(y_range).transpose()
y_pred_xgb_p = y_pred_xgb.values.tolist()
y_pred_lgb_p = y_pred_lgb.values.tolist()
y_pred_ngb_p = y_pred_ngb.values.tolist()

# グラフ作成
plt.figure(figsize=(15, 50))
for idx in tqdm(np.arange(X_test.shape[0])):
    plt.subplot(30, 5, idx+1)
    plt.plot(y_range, dist_values[idx])
    plt.vlines(y_pred_ngb_p[idx], 0, max(dist_values[idx]), "r", label="ngb pred")
    plt.vlines(y_pred_xgb_p[idx], 0, max(dist_values[idx]), "yellow", label="xgb pred")
    plt.vlines(y_pred_lgb_p[idx], 0, max(dist_values[idx]), "purple", label="lgb pred")
    plt.vlines(y_test[idx], 0, max(dist_values[idx]), "pink", label="ground truth")
    plt.legend(loc="best")
    plt.title(f"idx: {idx}")
    plt.xlim(y_range[0], y_range[-1])
plt.tight_layout()

plt.show()

まとめ

今回はNGBoostのアルゴリズムを解説しました。実験はbostonのデータセットとデフォルトのパラメータにして、xgboost、lightGBM、NGBoostを実験しました。モデルのチュニックしなくて、NGBoostは一番低いエラーになります。ただ、NGBoostの実行時間はxgboostとlightGBMより遅いをわかりました。