GBDT - redultimate/utility GitHub Wiki

Models

概要

  • Gradient Boosting Decision Tree, 勾配ブースティング木.
  • 数値の大小関係が重要. 特徴量は数値.
  • 欠損値があっても良い. ただし, 補完した方が良い場合もある.
  • 決定木の分岐の繰り返しによって変数間の相互作用を反映する.
  • Kaggle Masterが勾配ブースティングを解説する

気持ち

  • 精度が高い.
  • パラメータチューニングをしなくても精度が出やすい.
  • 不要な特徴量を追加しても精度が落ちにくい.
  • 特徴量をスケーリングする必要がない.
  • カテゴリ変数をone-hot encodingしなくていい.
  • 疎行列への対応がサポートされている.

アルゴリズム

  • 決定木を直列に組む. (RFは並列).
  • 目的値と、それまでに作成した決定木による予測値の差に対して学習し、逐次的に決定木を追加していく.
  • 予測対象のデータについて, これまでに作成した決定木の属する葉のウェイトの和を予測値とする.

ライブラリ

  • XGBoost
  • LightGBM
    • XGBoostより高速で, 精度は同程度.
    • 決定木の分岐をヒストグラムベースにすることで, 各数値を見ずにすむ→高速化.
    • XGBoostでは木の深さをまとめて増やしていたが, LightGBMでは葉単位でさらに深くできるようにしている.
    • カテゴリ変数の分割を最適化する工夫.
  • CatBoost
    • 特にカテゴリ変数の扱いを工夫.
    • カテゴリ変数をtarget encodingしている.
    • oblivious decision tree
      • 各深さの分岐の条件式が全て同じ.
      • モチベは?
    • ordered boosting
      • データ数が少ない場合に有効(らしい). なんで?
    • MLHEPにいた人たちが開発していると思われる.
    • catboostの推論の仕組みを理解する

実装例

XGBoost

# ---------------------------------
# データ等の準備
# ----------------------------------
import numpy as np
import pandas as pd

# train_xは学習データ、train_yは目的変数、test_xはテストデータ
# pandasのDataFrame, Seriesで保持します。(numpyのarrayで保持することもあります)

train = pd.read_csv('../input/sample-data/train_preprocessed.csv')
train_x = train.drop(['target'], axis=1)
train_y = train['target']
test_x = pd.read_csv('../input/sample-data/test_preprocessed.csv')

# 学習データを学習データとバリデーションデータに分ける
from sklearn.model_selection import KFold

kf = KFold(n_splits=4, shuffle=True, random_state=71)
tr_idx, va_idx = list(kf.split(train_x))[0]
tr_x, va_x = train_x.iloc[tr_idx], train_x.iloc[va_idx]
tr_y, va_y = train_y.iloc[tr_idx], train_y.iloc[va_idx]

# -----------------------------------
# xgboostの実装
# -----------------------------------
import xgboost as xgb
from sklearn.metrics import log_loss

# 特徴量と目的変数をxgboostのデータ構造に変換する
dtrain = xgb.DMatrix(tr_x, label=tr_y)
dvalid = xgb.DMatrix(va_x, label=va_y)
dtest = xgb.DMatrix(test_x)

# ハイパーパラメータの設定
params = {'objective': 'binary:logistic', 'silent': 1, 'random_state': 71}
num_round = 50

# 学習の実行
# バリデーションデータもモデルに渡し、学習の進行とともにスコアがどう変わるかモニタリングする
# watchlistには学習データおよびバリデーションデータをセットする
watchlist = [(dtrain, 'train'), (dvalid, 'eval')]
model = xgb.train(params, dtrain, num_round, evals=watchlist)

# バリデーションデータでのスコアの確認
va_pred = model.predict(dvalid)
score = log_loss(va_y, va_pred)
print(f'logloss: {score:.4f}')

# 予測(二値の予測値ではなく、1である確率を出力するようにしている)
pred = model.predict(dtest)

# -----------------------------------
# 学習データとバリデーションデータのスコアのモニタリング
# -----------------------------------
# モニタリングをloglossで行い、アーリーストッピングの観察するroundを20とする
params = {'objective': 'binary:logistic', 'silent': 1, 'random_state': 71,
          'eval_metric': 'logloss'}
num_round = 500
watchlist = [(dtrain, 'train'), (dvalid, 'eval')]
model = xgb.train(params, dtrain, num_round, evals=watchlist,
                  early_stopping_rounds=20)

# 最適な決定木の本数で予測を行う
pred = model.predict(dtest, ntree_limit=model.best_ntree_limit)

テクニック

  • booster
    • gbtreeを使うといい(デフォルト).
    • gblinearは線形モデル, dartは正則化にDARTを使ったもの.
    • DARTはdropoutのGBDT版. 過学習を防ぐ.
  • 目的関数
    • 回帰: reg:squarederror
    • 二値分類: binary:logistic
    • マルチクラス分類: multi:softprob
  • アーリーストッピング
    • trainメソッドにearly_stopping_roundsを指定するとできる.
    • 予測時にはntree_limitを設定しないとダメ. (そうしないと, 学習で止まったところまでの本数で計算されない)

ハイパーパラメータ

LightGBM

# ---------------------------------
# データ等の準備
# ----------------------------------
import numpy as np
import pandas as pd

# train_xは学習データ、train_yは目的変数、test_xはテストデータ
# pandasのDataFrame, Seriesで保持します。(numpyのarrayで保持することもあります)

train = pd.read_csv('../input/sample-data/train_preprocessed.csv')
train_x = train.drop(['target'], axis=1)
train_y = train['target']
test_x = pd.read_csv('../input/sample-data/test_preprocessed.csv')

# 学習データを学習データとバリデーションデータに分ける
from sklearn.model_selection import KFold

kf = KFold(n_splits=4, shuffle=True, random_state=71)
tr_idx, va_idx = list(kf.split(train_x))[0]
tr_x, va_x = train_x.iloc[tr_idx], train_x.iloc[va_idx]
tr_y, va_y = train_y.iloc[tr_idx], train_y.iloc[va_idx]

# -----------------------------------
# lightgbmの実装
# -----------------------------------
import lightgbm as lgb
from sklearn.metrics import log_loss

# 特徴量と目的変数をlightgbmのデータ構造に変換する
lgb_train = lgb.Dataset(tr_x, tr_y)
lgb_eval = lgb.Dataset(va_x, va_y)

# ハイパーパラメータの設定
params = {'objective': 'binary', 'seed': 71, 'verbose': 0, 'metrics': 'binary_logloss'}
num_round = 100

# 学習の実行
# カテゴリ変数をパラメータで指定している
# バリデーションデータもモデルに渡し、学習の進行とともにスコアがどう変わるかモニタリングする
categorical_features = ['product', 'medical_info_b2', 'medical_info_b3']
model = lgb.train(params, lgb_train, num_boost_round=num_round,
                  categorical_feature=categorical_features,
                  valid_names=['train', 'valid'], valid_sets=[lgb_train, lgb_eval])

# バリデーションデータでのスコアの確認
va_pred = model.predict(va_x)
score = log_loss(va_y, va_pred)
print(f'logloss: {score:.4f}')

# 予測
pred = model.predict(test_x)

CatBoost