【Hyperopt】XGBoost で DART を試してみる【Python】

| 0件のコメント

環境は以下です。

  • macOS siera
  • Python 2.7.13
  • xgboost==0.6a2
  • hyperopt==0.1

DART

DART (Dropouts meet Multiple Additive Regression Trees) はMART (多重加法回帰木) におけるアンサンブルのサイズの増加時に現れる特定のインスタンスでの一種の over-fit を起こす問題 (over-specialization) に対して, DNN の Dropouts のアイデアを導入し改善した正則化手法。

over-specialization の説明をみてみると後半の反復で追加されたツリーは, 少しのインスタンスの予測にだけ影響を与える傾向があり, 残りの多くのインスタンスには僅かにしか貢献しないとあり, Boosting ではよく知られた問題のようだ。

XGBoost で DART

XGBoost での DART の使い方は tutorials にあるように簡単で, パラメータの booster に dart を指定し, rate_drop と skip_drop を [0.0, 1.0] の範囲で指定する。
また, dart は gbtree を継承しているので, eta, gamma, max_depth を持っている。

param = {'booster': 'dart',
         'max_depth': 5,
         'learning_rate': 0.1,
         'objective': 'binary:logistic',
         'silent': True,
         'sample_type': 'uniform',
         'normalize_type': 'tree',
         'rate_drop': 0.1,
         'skip_drop': 0.5}

Dropouts はランダム性がある為, early stop を小さくすると安定性を損なう場合があるらしい。

Hyperopt でパラメータチューニング

hyperopt は 分散非同期で動くハイパーパラメータ最適化のライブラリで, Random Search と Tree of Parzen Estimators (TPE) が実装されている。

space の各keyの値には hp.quniform(‘パラメータ名’, 最小値, 最大値, 間隔) のように記述する。spaceの範囲で fn に指定した目的関数を最小化する。

def optimize(trials):
    space = {
         'learning_rate': hp.quniform('learning_rate', 0.1, 1, 0.05),
         'objective': 'reg:linear',
         'booster': 'dart',
         'sample_type': 'uniform',
         'normalize_type': 'tree',
         'rate_drop': hp.quniform('rate_drop', 0.1, 0.8, 0.1),
         'skip_drop': hp.quniform('skip_drop', 0.1, 0.8, 0.1),
         'max_depth' : hp.quniform('max_depth', 6, 13, 1),
         'silent': True,
    }

    # minimize the objective over the space
    best_params = fmin(
        fn=objective,
        space=space,
        algo=tpe.suggest,
        trials=trials,
        max_evals=10
    )

DART と Hyperopt を試してみる

使うデータセットは Boston house-prices で米国ボストン市郊外における地域別の住宅価格のデータ。
築年数, 高速道路へのアクセスしやすさ, 不動産税率 などからオーナーが所有する住宅価格の中央値を予測する回帰問題。

DARTモデルのハイパーパラメータチューニングを Hyperopt で行ってみた。

最終的なコードは以下。得られたパラメータでの MSE は 23.31 となった。

# -*- coding: utf-8 -*-

import numpy as np
import xgboost as xgb
from hyperopt import fmin, tpe, hp, STATUS_OK, Trials
from sklearn import datasets, metrics, cross_validation

def objective(params):
    # https://github.com/dmlc/xgboost/issues/1034
    params['max_depth'] = int(params['max_depth'])

    skf = cross_validation.StratifiedKFold(
        train_y, # Samples to split in K folds
        n_folds=5, # Number of folds. Must be at least 2.
        shuffle=True, # Whether to shuffle each stratification of the data before splitting into batches.
        random_state=30 # pseudo-random number generator state used for shuffling
    )

    boost_rounds = []
    score = []

    for train, test in skf:
        _train_x, _test_x, _train_y, _test_y = \
            train_x[train], train_x[test], train_y[train], train_y[test]

        train_xd = xgb.DMatrix(_train_x, label=_train_y)
        test_xd = xgb.DMatrix(_test_x, label=_test_y)
        watchlist = [(train_xd, 'train'),(test_xd, 'eval')]

        model = xgb.train(
            params,
            train_xd,
            num_boost_round=100,
            evals=watchlist,
            early_stopping_rounds=30
        )

        boost_rounds.append(model.best_iteration)
        score.append(model.best_score)

    print('average of best iteration:', np.average(boost_rounds))
    return {'loss': np.average(score), 'status': STATUS_OK}

def optimize(trials):
    space = {
         'learning_rate': hp.quniform('learning_rate', 0.1, 1, 0.05),
         'objective': 'reg:linear',
         'booster': 'dart',
         'sample_type': 'uniform',
         'normalize_type': 'tree',
         'rate_drop': hp.quniform('rate_drop', 0.1, 0.8, 0.1),
         'skip_drop': hp.quniform('skip_drop', 0.1, 0.8, 0.1),
         'max_depth' : hp.quniform('max_depth', 6, 13, 1),
         'silent': True,
    }

    # minimize the objective over the space
    best_params = fmin(
        fn=objective,
        space=space,
        algo=tpe.suggest,
        trials=trials,
        max_evals=10
    )

    return best_params

if __name__ == '__main__':
    np.random.seed(131)

    boston = datasets.load_boston()
    train_x = boston.data[0:400,:]
    train_y = boston.target[0:400]
    test_x = boston.data[401:501,:]
    test_y = boston.target[401:501]

    trials = Trials()
    best_params = optimize(trials)
    print(best_params)
    print(objective(best_params))

    train_xd = xgb.DMatrix(np.array(train_x), label=train_y)
    bst = xgb.train(best_params, train_xd, num_boost_round=100)

    pred_y = bst.predict(xgb.DMatrix(test_x))
    mse = metrics.mean_squared_error(test_y, pred_y)
    print(mse)

    bst.save_model('./model/housing-hyperopt.model')

[1] complete-guide-parameter-tuning-xgboost-with-codes-python
[2] PythonでXGBoostをちゃんと理解する(3) hyperoptでパラメーターチューニング
[3] scikit-learn に付属しているデータセット
[4] bergstra_hyperopt
[5] sklearn.metrics.mean_squared_error
[6] XGBoostにDart boosterを追加しました
[7] Feature Importance and Feature Selection With XGBoost in Python
[8] ランダムフォレスト系ツールで特徴量の重要度を測る

コメントを残す

必須欄は * がついています