新しい勾配ブースティングアルゴリズム「NGBoost」を使ってみた

こんにちは。今回は、昨年発表された新しい機械学習アルゴリズムである「NGBoost」を使ってみたいと思います。使ってみたいと思いつつ気がつけば半年ほど経ってしまっていました・・思ったらすぐに行動しなきゃですね、、

NGBoostとは?

何か新しい理論・手法を勉強するときは、「原典に当たるべし」ですね!

原文の論文はこちらから読むことができます。読んでみて分かったことを、自分の備忘も兼ねて簡単にまとめておこうと思います。

  • 発表は2019年10月8日(その後、何度か改訂されているようで、直近の更新は2020年6月9日)
  • NGBoostとは、Natural Gradient Boostingの略
  • 「予測の不確かさ」を扱うことができる。一般的な回帰モデルの場合は、1つの予測値を出力するが、NGBoostのようなProbablistic Regression Modelでは、その値である確率も予測することができる。
    (確率を予測することは、分類問題では既に規範となっている)
  • そのため、ヘルスケアや天気予報などの分野に適用が見込まれている。
  • 一般的な回帰モデルでは、予測値のスカラーは、出力のガウス分布の平均等をとって出力される。
  • 回帰問題において予測値の確率を予測する手法として、Bysian Modelなどもあるが、計算コストが高く、単純なモデルにしか適用できていないのが現状。NGBoostはこれらの問題を解決する。(効率的に計算可能)
  • 基本的な考え方としては、各入力における出力の確率分布をパラメータセットの形で表現し、勾配ブースティングの手法により、このパラメータを算出する。
  • この手法では、既存のモデルと比較して精度が劣った結果となった。これを改善するために導入した考え方が、「Natural Gradient Boosting」。これを組み合わせることで、既存モデルと同等、または良い精度を出すことができるようになった。
  • NGBoostは、回帰問題だけでなく、分類問題などにも利用することができる。

論文の中には、手法の数学についても触れられていますが、ここでは説明を割愛します。

全体感が分かったところで、物はためし、まずは実際に使ってみたいと思います。

2020年7月現在のNGBoostのTrendは?

Google検索のトレンドによると、過去5年間と、とっている期間は広いですが、NGBoostは、公開後、やや検索が増えたようですが、まだ既存のブースティング手法と比べると知名度は低いようです。

今後、人気爆発するのでしょうか。楽しみです。

以下は、NGBoostのGithubのReadmeに記載の手順で進めてみました。

NGBoostのインストールとサンプルコードの実装

NGboostは、Anacondaを経由してインストール可能です。

conda install -c conda-forge ngboost

以下が実際に動くサンプルコードです。

from ngboost import NGBRegressor
from ngboost.ngboost import NGBoost
from ngboost.learners import default_tree_learner
from ngboost.scores import MLE
from ngboost.distns import Normal, LogNormal

## データの読み込み
X, Y = load_boston(True)
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2)

## モデルの構築
ngb = NGBRegressor().fit(X_train, Y_train)
Y_preds = ngb.predict(X_test)
Y_dists = ngb.pred_dist(X_test)

## 学習結果の確認
# test Mean Squared Error
test_MSE = mean_squared_error(Y_preds, Y_test)
print('Test MSE', test_MSE)

# test Negative Log Likelihood
test_NLL = -Y_dists.logpdf(Y_test).mean()
print('Test NLL', test_NLL)

## Feature importance for loc trees
feature_importance_loc = ngb.feature_importances_[0]

## Feature importance for scale trees
feature_importance_scale = ngb.feature_importances_[1]

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df_loc = pd.DataFrame({'feature':load_boston()['feature_names'], 
                       'importance':feature_importance_loc})\
    .sort_values('importance',ascending=False)
df_scale = pd.DataFrame({'feature':load_boston()['feature_names'], 
                       'importance':feature_importance_scale})\
    .sort_values('importance',ascending=False)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,6))
fig.suptitle("Feature importance plot for distribution parameters", fontsize=17)
sns.barplot(x='importance',y='feature',ax=ax1,data=df_loc, color="skyblue").set_title('loc param')
sns.barplot(x='importance',y='feature',ax=ax2,data=df_scale, color="skyblue").set_title('scale param')

上記のコードで、predictとpred_distそれぞれで予測をしていますが、中身は以下のような形式です。

predictで予測された値、またはpred_distの’loc’に含まれる値が、予測値の平均で、pred_disctのscaleが、Standard Deviation(標準偏差)を表しています。

モデルのパラメータ

公式ドキュメントによると、チューニング可能なモデルのパラメータには以下があるようです。

パラメータオプション
Distribution (Regression)Normal/LogNormal/Exponential
Distribution (Classification)k_categorical(K)/Bernoulli
ScoreCRPS Score/Log Score
Base LearnersSKLearnで利用可能な全てのモデルを指定可能
learning ratenumeric
n_estimatorsnumeric
minibatch_frac0~1
col_sample0~1
verboseFalse/Numeric

Distribution

予測する確率分布のパターンを指定できるようです。

  • Normal:正規分布
  • Log-Normal:対数正規分布
  • Exponential:指数分布

以下がコードサンプルです。

%%time
X, Y = load_boston(True)
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2)

ngb = NGBRegressor(Dist=LogNormal).fit(X_train, Y_train)
Y_preds = ngb.predict(X_test)
Y_dists = ngb.pred_dist(X_test)

# test Mean Squared Error
test_MSE = mean_squared_error(Y_preds, Y_test)
print('Test MSE', test_MSE)

# test Negative Log Likelihood
test_NLL = -Y_dists.logpdf(Y_test).mean()
print('Test NLL', test_NLL)

Score

モデルの最適化指標を選分ことができる。

  • CRPScore:CRPSとは、Continuous Ranked Probability Scoreの略。詳しいことはこれから勉強しようと思うが、確率予測における検証指標の1つらしい。完全に的中している場合に0をとる。
  • LogScore:Negative Log-Likelifood(尤度の負の対数)

以下がサンプルコード。

from ngboost import NGBRegressor
from ngboost.ngboost import NGBoost
from ngboost.learners import default_tree_learner
from ngboost.scores import MLE, CRPScore
from ngboost.distns import Normal, LogNormal, Exponential

from math import sqrt

%%time
X, Y = load_boston(True)
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2)

ngb = NGBRegressor(Dist=Exponential, Score=CRPScore).fit(X_train, Y_train)
Y_preds = ngb.predict(X_test)
Y_dists = ngb.pred_dist(X_test)

# test Mean Squared Error
test_MSE = mean_squared_error(Y_preds, Y_test)
print('Test MSE', test_MSE)

# test Negative Log Likelihood
test_NLL = -Y_dists.logpdf(Y_test).mean()
print('Test NLL', test_NLL)

Base Learner

Base Learnerには、sklearnで定義されているどのモデルでも指定できるらしい。例えば、以下のような感じで。

from sklearn.tree import DecisionTreeRegressor

learner = DecisionTreeRegressor(criterion='friedman_mse', max_depth=5)

NGBSurvival(Dist=Exponential, Score=CRPScore, Base=learner, verbose=False).fit(X_surv_train, T_surv_train, E_surv_train)

Other Parameters

他のn_estimaorsなどの指定方法のサンプルは以下。

ngb = NGBRegressor(n_estimators=100, learning_rate=0.01, 
             minibatch_frac=0.5, col_sample=0.5)
ngb.fit(X_reg_train, Y_reg_train)

モデルのチューニング

NGBoostのチューニングは、おなじみGrid Search CVが利用可能なようです。

from sklearn.model_selection import GridSearchCV
from sklearn.tree import DecisionTreeRegressor

b1 = DecisionTreeRegressor(criterion='friedman_mse', max_depth=2)
b2 = DecisionTreeRegressor(criterion='friedman_mse', max_depth=4)

param_grid = {
    'minibatch_frac': [1.0, 0.5],
    'Base': [b1, b2]
}

ngb = NGBRegressor(Dist=Normal, verbose=False)

grid_search = GridSearchCV(ngb, param_grid=param_grid, cv=5)
grid_search.fit(X_reg_train, Y_reg_train)
print(grid_search.best_params_)

以上、NGBoostの概要と、サンプルコードの実装をご紹介いたしました。

今度は、他のモデルとの精度比較の検証を行ってみたいと思います!

おしまい

この記事を気に入っていただけたらシェアをお願いします!

コメントを残す

メールアドレスが公開されることはありません。

ABOUT US

Yuu113
初めまして。Yuu113と申します。 兵庫県出身、東京でシステムエンジニアをしております。現在は主にデータ分析、機械学習を活用してビジネスモデリングに取り組んでいます。日々学んだことや経験したことを整理していきたいと思い、ブログを始めました。旅行、カメラ、IT技術、江戸文化が大好きですので、これらについても記事にしていきたいと思っています。