1. 程式人生 > >如何使用hyperopt對xgboost進行自動調參

如何使用hyperopt對xgboost進行自動調參

本教程重點在於傳授如何使用Hyperopt對xgboost進行自動調參。但是這份程式碼也是我一直使用的程式碼模板之一,所以在其他資料集上套用該模板也是十分容易的。

同時因為xgboost,lightgbm,catboost。三個類庫呼叫方法都比較一致,所以在本部分結束之後,我們有理由相信,你將會學會在這三個類庫上使用hyperopt。除此之外要額外說明的是,本文並不涉及交叉驗證的問題,交叉驗證請檢視其他教程。

什麼是Hyperopt?

Hyperopt:是python中的一個用於"分散式非同步演算法組態/超引數優化"的類庫。使用它我們可以拜託繁雜的超引數優化過程,自動獲取最佳的超引數。廣泛意義上,可以將帶有超引數的模型看作是一個必然的非凸函式,因此hyperopt幾乎可以穩定的獲取比手工更加合理的調參結果。尤其對於調參比較複雜的模型而言,其更是能以遠快於人工調參的速度同樣獲得遠遠超過人工調參的最終效能。

文件地址?

目前中文文件的地址由本人FontTian在2017年翻譯,但是hyperopt文件本身確實寫的不怎麼樣。所以才有了這份教程。原始碼請前往Github教程地址下載下載。

教程

獲取資料

這裡我們使用UCI的紅酒質量資料集,除此之外我還額外增加了兩個特徵。

from hyperopt import fmin, tpe, hp, partial
import numpy as np
from sklearn.model_selection import train_test_split,
cross_val_score from sklearn.metrics import mean_squared_error, zero_one_loss import xgboost as xgb import pandas as pd def GetNewDataByPandas(): wine = pd.read_csv("../data/wine.csv") wine['alcohol**2'] = pow(wine["alcohol"], 2) wine['volatileAcidity*alcohol'] = wine["alcohol"] * wine['volatile acidity'
] y = np.array(wine.quality) X = np.array(wine.drop("quality", axis=1)) columns = np.array(wine.columns) return X, y, columns

分割資料並轉換

首先將資料分割為三份,一部分用於預測,訓練資料則同樣分成額外的兩部分用於evallist引數。

同時為了加快速度和減少記憶體,我們將資料轉換為xgboost自帶的讀取格式。

# Read wine quality data from file
X, y, wineNames = GetNewDataByPandas()

# split data to [[0.8,0.2],01]
x_train_all, x_predict, y_train_all, y_predict = train_test_split(X, y, test_size=0.10, random_state=100)

x_train, x_test, y_train, y_test = train_test_split(x_train_all, y_train_all, test_size=0.2, random_state=100)

dtrain = xgb.DMatrix(data=x_train,label=y_train,missing=-999.0)
dtest = xgb.DMatrix(data=x_test,label=y_test,missing=-999.0)

evallist = [(dtest, 'eval'), (dtrain, 'train')]

定義引數空間

使用hyperopt自帶的函式定義引數空間,但是因為其randint()方法產生的陣列範圍是從0開始的,所以我額外定義了一個數據轉換方法,對原始引數空間進行一次轉換。

關於hyperopt中定義引數區間需要使用的函式請參考:

  • 中文地址,請點選這裡
  • 英文地址,請點選這裡
# 自定義hyperopt的引數空間
space = {"max_depth": hp.randint("max_depth", 15),
         "n_estimators": hp.randint("n_estimators", 300),
         'learning_rate': hp.uniform('learning_rate', 1e-3, 5e-1),
         "subsample": hp.randint("subsample", 5),
         "min_child_weight": hp.randint("min_child_weight", 6),
         }

def argsDict_tranform(argsDict, isPrint=False):
    argsDict["max_depth"] = argsDict["max_depth"] + 5
    argsDict['n_estimators'] = argsDict['n_estimators'] + 150
    argsDict["learning_rate"] = argsDict["learning_rate"] * 0.02 + 0.05
    argsDict["subsample"] = argsDict["subsample"] * 0.1 + 0.5
    argsDict["min_child_weight"] = argsDict["min_child_weight"] + 1
    if isPrint:
        print(argsDict)
    else:
        pass

    return argsDict

建立模型工廠與分數獲取器

xgboost模型工廠用於生產我們需要的model,而分數獲取器則是為了解耦。這樣在實際的測試工作中更加套用程式碼和修改。

def xgboost_factory(argsDict):
    argsDict = argsDict_tranform(argsDict)
    
    params = {'nthread': -1,  # 程序數
              'max_depth': argsDict['max_depth'],  # 最大深度
              'n_estimators': argsDict['n_estimators'],  # 樹的數量
              'eta': argsDict['learning_rate'],  # 學習率
              'subsample': argsDict['subsample'],  # 取樣數
              'min_child_weight': argsDict['min_child_weight'],  # 終點節點最小樣本佔比的和
              'objective': 'reg:linear',
              'silent': 0,  # 是否顯示
              'gamma': 0,  # 是否後剪枝
              'colsample_bytree': 0.7,  # 樣本列取樣
              'alpha': 0,  # L1 正則化
              'lambda': 0,  # L2 正則化
              'scale_pos_weight': 0,  # 取值>0時,在資料不平衡時有助於收斂
              'seed': 100,  # 隨機種子
              'missing': -999,  # 填充缺失值
              }
    params['eval_metric'] = ['rmse']

    xrf = xgb.train(params, dtrain, 300, evallist,early_stopping_rounds=100)

    return get_tranformer_score(xrf)

def get_tranformer_score(tranformer):
    
    xrf = tranformer
    dpredict = xgb.DMatrix(x_predict)
    prediction = xrf.predict(dpredict, ntree_limit=xrf.best_ntree_limit)
  
    return mean_squared_error(y_predict, prediction)

呼叫Hyperopt開始調參

之後我們呼叫hyperopt進行自動調參即可,同時通過返回值獲取最佳模型的結果。

# 開始使用hyperopt進行自動調參
algo = partial(tpe.suggest, n_startup_jobs=1)
best = fmin(xgboost_factory, space, algo=algo, max_evals=20, pass_expr_memo_ctrl=None)
[15:23:32] /workspace/src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 142 extra nodes, 0 pruned nodes, max_depth=10
[0]	eval-rmse:5.03273	train-rmse:4.90203
Multiple eval metrics have been passed: 'train-rmse' will be used for early stopping.

Will train until train-rmse hasn't improved in 100 rounds.
[15:23:32] /workspace/src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 172 extra nodes, 0 pruned nodes, max_depth=10
[1]	eval-rmse:4.77384	train-rmse:4.64767

...

[15:24:04] /workspace/src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 192 extra nodes, 0 pruned nodes, max_depth=15
[299]	eval-rmse:0.570382	train-rmse:0.000749

展示結果

展示我們獲取的最佳引數,以及該模型在訓練集上的最終表現,如果想要使用交叉驗證請參考其他教程。

RMSE = xgboost_factory(best)
print('best :', best)
print('best param after transform :')
argsDict_tranform(best,isPrint=True)
print('rmse of the best xgboost:', np.sqrt(RMSE))
[15:24:52] /workspace/src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 428 extra nodes, 0 pruned nodes, max_depth=14
[0]	eval-rmse:5.02286	train-rmse:4.89385
Multiple eval metrics have been passed: 'train-rmse' will be used for early stopping.

Will train until train-rmse hasn't improved in 100 rounds.
[15:24:52] /workspace/src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 680 extra nodes, 0 pruned nodes, max_depth=14
[1]	eval-rmse:4.75938	train-rmse:4.63251

...

[298]	eval-rmse:0.583923	train-rmse:0.000705
[15:24:54] /workspace/src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 22 extra nodes, 0 pruned nodes, max_depth=7
[299]	eval-rmse:0.583926	train-rmse:0.000704
best : {'learning_rate': 0.05385158551863543, 'max_depth': 14, 'min_child_weight': 2, 'n_estimators': 173, 'subsample': 0.8}
best param after transform :
{'learning_rate': 0.051077031710372714, 'max_depth': 19, 'min_child_weight': 3, 'n_estimators': 323, 'subsample': 0.5800000000000001}
rmse of the best xgboost: 0.5240080946197716