Lightgbm with Hyperopt
如何使用hyperopt對Lightgbm進行自動調參
之前的教程以及介紹過如何使用hyperopt對xgboost進行調參,并且已經說明了,該代碼模板可以十分輕松的轉移到lightgbm,或者catboost上。而本篇教程就是對原模板的一次遷移,前半部分為教程-如何使用hyperopt對xgboost進行自動調參的遷移,后半部分是對在Hyperopt框架下使用XGboost與交叉驗證的遷移。
關于Hyperopt
Hyperopt:是python中的一個用于"分布式異步算法組態/超參數優化"的類庫。使用它我們可以拜托繁雜的超參數優化過程,自動獲取最佳的超參數。廣泛意義上,可以將帶有超參數的模型看作是一個必然的非凸函數,因此hyperopt幾乎可以穩定的獲取比手工更加合理的調參結果。尤其對于調參比較復雜的模型而言,其更是能以遠快于人工調參的速度同樣獲得遠遠超過人工調參的最終性能。
目前中文文檔的地址由本人FontTian在2017年翻譯,但是hyperopt文檔本身確實寫的不怎么樣。所以才有了這份教程。源代碼請前往Github教程地址下載下載。
- 中文文檔地址
- FontTian的博客
- Hyperopt官方文檔地址
遷移一:自動調參
獲取數據
這里我們使用UCI的紅酒質量數據集,除此之外我還額外增加了兩個特征。
import numpy as np import pandas as pddef GetNewDataByPandas():wine = pd.read_csv("/home/fonttian/Data/UCI/wine/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自帶的讀取格式。
from sklearn.model_selection import train_test_split # 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)import lightgbm as lgbtrain_data = lgb.Dataset(data=x_train,label=y_train) test_data = lgb.Dataset(data=x_test,label=y_test)定義參數空間
使用hyperopt自帶的函數定義參數空間,但是因為其randint()方法產生的數組范圍是從0開始的,所以我額外定義了一個數據轉換方法,對原始參數空間進行一次轉換。
關于hyperopt中定義參數區間需要使用的函數請參考:
- 中文地址,請點擊這里
- 英文地址,請點擊這里
創建模型工廠與分數獲取器
lightgbm模型工廠用于生產我們需要的model,而分數獲取器則是為了解耦。這樣在實際的測試工作中更加套用代碼和修改。
from sklearn.metrics import mean_squared_errordef lightgbm_factory(argsDict):argsDict = argsDict_tranform(argsDict)params = {'nthread': -1, # 進程數'max_depth': argsDict['max_depth'], # 最大深度'num_trees': argsDict['num_trees'], # 樹的數量'eta': argsDict['learning_rate'], # 學習率'bagging_fraction': argsDict['bagging_fraction'], # 采樣數'num_leaves': argsDict['num_leaves'], # 終點節點最小樣本占比的和'objective': 'regression','feature_fraction': 0.7, # 樣本列采樣'lambda_l1': 0, # L1 正則化'lambda_l2': 0, # L2 正則化'bagging_seed': 100, # 隨機種子,light中默認為100}params['metric'] = ['rmse']model_lgb = lgb.train(params, train_data, num_boost_round=300, valid_sets=[test_data],early_stopping_rounds=100)return get_tranformer_score(model_lgb)def get_tranformer_score(tranformer):model = tranformerprediction = model.predict(x_predict, num_iteration=model.best_iteration)return mean_squared_error(y_predict, prediction)調用Hyperopt開始調參
之后我們調用hyperopt進行自動調參即可,同時通過返回值獲取最佳模型的結果。
# 開始使用hyperopt進行自動調參 algo = partial(tpe.suggest, n_startup_jobs=1) best = fmin(lightgbm_factory, space, algo=algo, max_evals=20, pass_expr_memo_ctrl=None) /home/fonttian/anaconda3/lib/python3.6/site-packages/lightgbm/engine.py:116: UserWarning: Found `num_trees` in params. Will use it instead of argumentwarnings.warn("Found `{}` in params. Will use it instead of argument".format(alias))[1] valid_0's rmse: 0.793788 Training until validation scores don't improve for 100 rounds. [2] valid_0's rmse: 0.776669 [3] valid_0's rmse: 0.762522 [4] valid_0's rmse: 0.749776 ......... [299] valid_0's rmse: 0.562895 [300] valid_0's rmse: 0.56334 Did not meet early stopping. Best iteration is: [226] valid_0's rmse: 0.560096展示結果
展示我們獲取的最佳參數,以及該模型在訓練集上的最終表現,如果想要使用交叉驗證請參考其他教程。
RMSE = lightgbm_factory(best) print('best :', best) print('best param after transform :') argsDict_tranform(best,isPrint=True) print('rmse of the best lightgbm:', np.sqrt(RMSE)) [1] valid_0's rmse: 0.793948 Training until validation scores don't improve for 100 rounds. [2] valid_0's rmse: 0.776625 ... [239] valid_0's rmse: 0.569307 Early stopping, best iteration is: [139] valid_0's rmse: 0.567378 best : {'bagging_fraction': 0.555, 'learning_rate': 0.0510231388682371, 'max_depth': 29, 'num_leaves': 265, 'num_trees': 629} best param after transform : {'bagging_fraction': 0.5555, 'learning_rate': 0.051020462777364745, 'max_depth': 34, 'num_leaves': 805, 'num_trees': 779} rmse of the best lightgbm: 0.5964674105744058遷移二-交叉驗證
說明
其實本部分與之前的一樣重點依舊在于Hyperopt與Lightgbm參數傳入上的一個沖突,不過解決方案很簡單,先構建帶有Hyperopt參數空間的模型,然后再從構建的模型中獲取參數。其實這點xgboost,hyperopt,catboost三個模型的解決方案都一樣。catboost自帶的教程中也有這種解決方案。只不過catboost自帶的教程不和lightgbm與xgboost一樣在自己的原項目里,而是在原賬號下又額外開了個Github項目,導致不太容易發現。實際上我也是最近在寫這個的時候,才發現catboost原來是自帶教程的。也正因為如此,本系列教程就不再往catboost上遷移代碼了。請自行參考catboost自帶的教程
使用CV方法進行交叉驗證
sklearn部分過于簡單,本處將不再做遷移。如有需要請自行遷移。
import hyperopttrain_all_data = lgb.Dataset(data=x_train_all,label=y_train_all)def hyperopt_objective(params):model = lgb.LGBMRegressor(num_leaves=31,max_depth=int(params['max_depth']) + 5,learning_rate=params['learning_rate'],objective='regression',eval_metric='rmse',nthread=-1,)num_round = 10res = lgb.cv(model.get_params(),train_all_data, num_round, nfold=5, metrics='rmse',early_stopping_rounds=10)return min(res['rmse-mean']) # as hyperopt minimises # 這里的warnings實在太多了,我們加入代碼不再讓其顯示 import warnings warnings.filterwarnings("ignore")from numpy.random import RandomStateparams_space = {'max_depth': hyperopt.hp.randint('max_depth', 6),'learning_rate': hyperopt.hp.uniform('learning_rate', 1e-3, 5e-1), }trials = hyperopt.Trials()best = hyperopt.fmin(hyperopt_objective,space=params_space,algo=hyperopt.tpe.suggest,max_evals=50,trials=trials,rstate=RandomState(123) )print("\n展示hyperopt獲取的最佳結果,但是要注意的是我們對hyperopt最初的取值范圍做過一次轉換") print(best) 展示hyperopt獲取的最佳結果,但是要注意的是我們對hyperopt最初的取值范圍做過一次轉換 {'learning_rate': 0.059356676015000595, 'max_depth': 5}額外的:CSDN與知乎
以后文章會做內容分級:主要內容將發布在知乎,首發仍然在CSDN,知乎只保留部分內容,而CSDN比較全面。CSDN會有的而知乎沒有的:一些比較簡單的內容,或者草稿。知乎主要發重點內容,以質量為主。
- CSDN博客地址
- 知乎主頁
總結
以上是生活随笔為你收集整理的Lightgbm with Hyperopt的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在Python中使用lightgbm
- 下一篇: Hive 快速上手