电竞比分网-中国电竞赛事及体育赛事平台

分享

【干貨】手把手教你Python實現(xiàn)自動貝葉斯調(diào)整超參數(shù)

 LibraryPKU 2018-07-07

【導(dǎo)讀】機器學(xué)習(xí)中,調(diào)參是一項繁瑣但至關(guān)重要的任務(wù),因為它很大程度上影響了算法的性能。手動調(diào)參十分耗時,網(wǎng)格和隨機搜索不需要人力,但需要很長的運行時間。因此,誕生了許多自動調(diào)整超參數(shù)的方法。貝葉斯優(yōu)化是一種用模型找到函數(shù)最小值方法,已經(jīng)應(yīng)用于機器學(xué)習(xí)問題中的超參數(shù)搜索,這種方法性能好,同時比隨機搜索省時。此外,現(xiàn)在有許多Python庫可以實現(xiàn)貝葉斯超參數(shù)調(diào)整。本文將使用Hyperopt庫演示梯度提升機(Gradient Boosting Machine,GBM) 的貝葉斯超參數(shù)調(diào)整的完整示例。文章由貝葉斯優(yōu)化方法、優(yōu)化問題的四個部分、目標(biāo)函數(shù)、域空間、優(yōu)化過程、及結(jié)果展示幾個部分組成。


作者|William Koehrsen

編譯|專知

整理|YIngying,李大囧


貝葉斯優(yōu)化方法

貝葉斯優(yōu)化通過基于目標(biāo)函數(shù)的過去評估結(jié)果建立替代函數(shù)(概率模型),來找到最小化目標(biāo)函數(shù)的值。貝葉斯方法與隨機或網(wǎng)格搜索的不同之處在于,它在嘗試下一組超參數(shù)時,會參考之前的評估結(jié)果,因此可以省去很多無用功。


超參數(shù)的評估代價很大,因為它要求使用待評估的超參數(shù)訓(xùn)練一遍模型,而許多深度學(xué)習(xí)模型動則幾個小時幾天才能完成訓(xùn)練,并評估模型,因此耗費巨大。貝葉斯調(diào)參發(fā)使用不斷更新的概率模型,通過推斷過去的結(jié)果來“集中”有希望的超參數(shù)。


Python中的選擇

Python中有幾個貝葉斯優(yōu)化庫,它們目標(biāo)函數(shù)的替代函數(shù)不一樣。在本文中,我們將使用Hyperopt,它使用Tree Parzen Estimator(TPE)。其他Python庫包括Spearmint(高斯過程代理)和SMAC(隨機森林回歸)。


優(yōu)化問題的四個部分

貝葉斯優(yōu)化問題有四個部分:

  1. 目標(biāo)函數(shù):我們想要最小化的內(nèi)容,在這里,目標(biāo)函數(shù)是機器學(xué)習(xí)模型使用該組超參數(shù)在驗證集上的損失。

  2. 域空間:要搜索的超參數(shù)的取值范圍

  3. 優(yōu)化算法:構(gòu)造替代函數(shù)并選擇下一個超參數(shù)值進行評估的方法。

  4. 結(jié)果歷史記錄:來自目標(biāo)函數(shù)評估的存儲結(jié)果,包括超參數(shù)和驗證集上的損失。


數(shù)據(jù)集

在本例中,我們將使用Caravan Insurance數(shù)據(jù)集,其目標(biāo)是預(yù)測客戶是否購買保險單。 這是一個有監(jiān)督分類問題,訓(xùn)練集和測試集的大小分別為5800和4000。評估性能的指標(biāo)是AUC(曲線下面積)評估準(zhǔn)則和ROC(receiver operating characteristic,以真陽率和假陽率為坐標(biāo)軸的曲線圖)曲線,ROC AUC越高表示模型越好。 數(shù)據(jù)集如下所示:



因為Hyperopt最小化目標(biāo)函數(shù),我們的目標(biāo)函數(shù)返回1-ROC AUC,從而提高ROC AUC。


梯度提升模型

梯度提升機(GBM)是一種基于使用弱學(xué)習(xí)器(如決策樹)組合成強學(xué)習(xí)器的模型。 GBM中有許多超參數(shù)控制整個集合和單個決策樹,如決策樹數(shù)量,決策樹深度等。簡單了解了GBM,接下來我們介紹這個問題對應(yīng)的優(yōu)化模型的四個部分。


目標(biāo)函數(shù)

目標(biāo)函數(shù)是需要我們最小化的。 它的輸入為一組超參數(shù),輸出需要最小化的值(交叉驗證損失)。Hyperopt將目標(biāo)函數(shù)視為黑盒,只考慮它的輸入和輸出。 在這里,目標(biāo)函數(shù)定義為:


def objective(hyperparameters):
   '''Returns validation score from hyperparameters'''

   model = Classifier(hyperparameters)
   validation_loss = cross_validation(model, training_data)
   return validation_loss


我們評估的是超參數(shù)在驗證集上的表現(xiàn),但我們不將數(shù)據(jù)集劃分成固定的驗證集和訓(xùn)練集,而是使用K折交叉驗證。使用10倍交叉驗證和提前停止的梯度提升機的完整目標(biāo)函數(shù)如下所示。

import lightgbm as lgb
from hyperopt import STATUS_OK

N_FOLDS = 10

# Create the dataset
train_set = lgb.Dataset(train_features, train_labels)


def objective(params, n_folds=N_FOLDS):
   '''Objective function for Gradient Boosting Machine Hyperparameter Tuning'''

   # Perform n_fold cross validation with hyperparameters
   # Use early stopping and evalute based on ROC AUC
   cv_results = lgb.cv(params, train_set, nfold=n_folds, num_boost_round=10000,
                       early_stopping_rounds=100, metrics='auc', seed=50)

   # Extract the best score
   best_score = max(cv_results['auc-mean'])

   # Loss must be minimized
   loss = 1 - best_score

   # Dictionary with information for evaluation
   return {'loss': loss, 'params': params, 'status': STATUS_OK}


關(guān)鍵點是cvresults = lgb.cv(...)。為了實現(xiàn)提前停止的交叉驗證,我們使用LightGBM函數(shù)cv,它輸入為超參數(shù),訓(xùn)練集,用于交叉驗證的折數(shù)等。我們將迭代次數(shù)(numboostround)設(shè)置為10000,但實際上不會達到這個數(shù)字,因為我們使用earlystopping_rounds來停止訓(xùn)練,當(dāng)連續(xù)100輪迭代效果都沒有提升時,則提前停止,并選擇模型。因此,迭代次數(shù)并不是我們需要設(shè)置的超參數(shù)。


一旦交叉驗證完成,我們就會得到最好的分數(shù)(ROC AUC),然后,因為我們最小化目標(biāo)函數(shù),所以計算1- ROC AUC,然后返回這個值。


域空間

域空間表示我們要為每個超參數(shù)計算的值的范圍。在搜索的每次迭代中,貝葉斯優(yōu)化算法將從域空間為每個超參數(shù)選擇一個值。當(dāng)我們進行隨機或網(wǎng)格搜索時,域空間是一個網(wǎng)格。在貝葉斯優(yōu)化中,想法是一樣的,但是不是按照順序(網(wǎng)格)或者隨機選擇一個超參數(shù),而是按照每個超參數(shù)的概率分布選擇。


而確定域空間是最困難的。如果我們有機器學(xué)習(xí)方法的經(jīng)驗,我們可以通過在我們認為最佳值的位置放置更大的概率來使用它來告知我們對超參數(shù)分布的選擇。然而,不同數(shù)據(jù)集之間最佳模型不一樣,并且具有高維度問題(許多超參數(shù)),超參數(shù)之間也會互相影響。在我們不確定最佳值的情況下,我們可以將范圍設(shè)定的大一點,讓貝葉斯算法為我們做推理。


首先,我們看看GBM中的所有超參數(shù):

import lgb
# Default gradient boosting machine classifier
model = lgb.LGBMClassifier()
model
LGBMClassifier(boosting_type='gbdt', n_estimators=100,
              class_weight=None, colsample_bytree=1.0,
              learning_rate=0.1, max_depth=-1,                      
              min_child_samples=20,
              min_child_weight=0.001, min_split_gain=0.0,
              n_jobs=-1, num_leaves=31, objective=None,
              random_state=None, reg_alpha=0.0, reg_lambda=0.0,
              silent=True, subsample=1.0,
              subsample_for_bin=200000, subsample_freq=1)


其中一些我們不需要調(diào)整(例如objective和randomstate),我們將使用提前停止來找到最好的n_estimators。 但是,我們還有10個超參數(shù)要優(yōu)化! 首次調(diào)整模型時,我通常會創(chuàng)建一個以默認值為中心的寬域空間,然后在后續(xù)搜索中對其進行細化。

例如,讓我們在Hyperopt中定義一個簡單的域,這是GBM中每棵樹中葉子數(shù)量的離散均勻分布:

from hyperopt import hp
# Discrete uniform distribution
num_leaves = {'num_leaves': hp.quniform('num_leaves', 30, 150, 1)}


這里選擇離散的均勻分布,因為葉子的數(shù)量必須是整數(shù)(離散),并且域中的每個值都可能(均勻)。


另一種分布選擇是對數(shù)均勻,它在對數(shù)標(biāo)度上均勻分布值。 我們將使用對數(shù)統(tǒng)一(從0.005到0.2)來獲得學(xué)習(xí)率,因為它在幾個數(shù)量級上變化:

# Learning rate log uniform distribution
learning_rate = {'learning_rate': hp.loguniform('learning_rate',
                                                np.log(0.005),
                                                np.log(0.2)}


下面分別繪制了均勻分布和對數(shù)均勻分布的圖。 這些是核密度估計圖,因此y軸是密度而不是計數(shù)!



現(xiàn)在,讓我們定義整個域:

# Define the search space
space = {
   'class_weight': hp.choice('class_weight', [None, 'balanced']),
   'boosting_type': hp.choice('boosting_type',
                              [{'boosting_type': 'gbdt',
                                   'subsample': hp.uniform('gdbt_subsample', 0.5, 1)},
                                {'boosting_type': 'dart',
                                    'subsample': hp.uniform('dart_subsample', 0.5, 1)},
                                {'boosting_type': 'goss'}]),
   'num_leaves': hp.quniform('num_leaves', 30, 150, 1),
   'learning_rate': hp.loguniform('learning_rate', np.log(0.01), np.log(0.2)),
   'subsample_for_bin': hp.quniform('subsample_for_bin', 20000, 300000, 20000),
   'min_child_samples': hp.quniform('min_child_samples', 20, 500, 5),
   'reg_alpha': hp.uniform('reg_alpha', 0.0, 1.0),
   'reg_lambda': hp.uniform('reg_lambda', 0.0, 1.0),
   'colsample_bytree': hp.uniform('colsample_by_tree', 0.6, 1.0)
}


這里我們使用了許多不同的域分發(fā)類型:

  • choice:類別變量

  • quniform:離散均勻(整數(shù)間隔均勻)

  • uniform:連續(xù)均勻(間隔為一個浮點數(shù))

  • loguniform:連續(xù)對數(shù)均勻(對數(shù)下均勻分布)

# boosting type domain
boosting_type = {'boosting_type': hp.choice('boosting_type',
                                           [{'boosting_type': 'gbdt',
                                                 'subsample': hp.uniform('subsample', 0.5, 1)},
                                            {'boosting_type': 'dart',
                                                 'subsample': hp.uniform('subsample', 0.5, 1)},
                                            {'boosting_type': 'goss',
                                                 'subsample': 1.0}])}


這里我們使用條件域,這意味著一個超參數(shù)的值取決于另一個超參數(shù)的值。 對于提升類型“goss”,gbm不能使用子采樣(僅選擇訓(xùn)練觀察的子樣本部分以在每次迭代時使用)。 因此,如果提升類型是“goss”,則子采樣率設(shè)置為1.0(無子采樣),否則為0.5-1.0。 這是使用嵌套域?qū)崿F(xiàn)的。


定義域空間之后,我們可以從中采樣查看樣本。

# Sample from the full space
example = sample(space)

# Dictionary get method with default
subsample = example['boosting_type'].get('subsample', 1.0)

# Assign top-level keys
example['boosting_type'] = example['boosting_type']['boosting_type']
example['subsample'] = subsample


example
{'boosting_type': 'gbdt',
'class_weight': 'balanced',
'colsample_bytree': 0.8111305579351727,
'learning_rate': 0.16186471096789776,
'min_child_samples': 470.0,
'num_leaves': 88.0,
'reg_alpha': 0.6338327001528129,
'reg_lambda': 0.8554826167886239,
'subsample_for_bin': 280000.0,
'subsample': 0.6318665053932255}


優(yōu)化算法

雖然這是貝葉斯優(yōu)化中概念上最難的部分,但在Hyperopt中創(chuàng)建優(yōu)化算法只需一行。 要使用Tree Parzen Estimator,代碼為:

from hyperopt import tpe
# Algorithm
tpe_algorithm = tpe.suggest


在優(yōu)化時,TPE算法根據(jù)過去的結(jié)果構(gòu)建概率模型,并通過最大化預(yù)期的改進來決定下一組超參數(shù)以在目標(biāo)函數(shù)中進行評估。


結(jié)果歷史

跟蹤結(jié)果并不是絕對必要的,因為Hyperopt將在內(nèi)部為算法執(zhí)行此操作。 但是,如果我們想知道幕后發(fā)生了什么,我們可以使用Trials對象來存儲基本的訓(xùn)練信息,還可以使用從目標(biāo)函數(shù)返回的字典(包括損失和范圍)。 制創(chuàng)建Trials對象也只要一行代碼:

from hyperopt import Trials
# Trials object to track progress
bayes_trials = Trials()


為了監(jiān)控訓(xùn)練運行進度,可以將結(jié)果歷史寫入csv文件,防止程序意外中斷導(dǎo)致評估結(jié)果消失。

import csv

# File to save first results
out_file = 'gbm_trials.csv'
of_connection = open(out_file, 'w')
writer = csv.writer(of_connection)

# Write the headers to the file
writer.writerow(['loss', 'params', 'iteration', 'estimators', 'train_time'])
of_connection.close()


然后在目標(biāo)函數(shù)中我們可以在每次迭代時添加行寫入csv:

# Write to the csv file ('a' means append)
of_connection = open(out_file, 'a')
writer = csv.writer(of_connection)
writer.writerow([loss, params, iteration, n_estimators, run_time])
of_connection.close()


優(yōu)化


一旦我們定義好了上述部分,就可以用fmin運行優(yōu)化:

from hyperopt import fmin

MAX_EVALS = 500

# Optimize
best = fmin(fn = objective, space = space, algo = tpe.suggest,
           max_evals = MAX_EVALS, trials = bayes_trials)


在每次迭代時,算法從代理函數(shù)中選擇新的超參數(shù)值,該代理函數(shù)基于先前的結(jié)果構(gòu)建并在目標(biāo)函數(shù)中評估這些值。 這繼續(xù)用于目標(biāo)函數(shù)的MAX_EVALS評估,其中代理函數(shù)隨每個新結(jié)果不斷更新。


結(jié)果


從fmin返回的最佳對象包含在目標(biāo)函數(shù)上產(chǎn)生最低損失的超參數(shù):

{'boosting_type': 'gbdt',
  'class_weight': 'balanced',
  'colsample_bytree': 0.7125187075392453,
  'learning_rate': 0.022592570862044956,
  'min_child_samples': 250,
  'num_leaves': 49,
  'reg_alpha': 0.2035211643104735,
  'reg_lambda': 0.6455131715928091,
  'subsample': 0.983566228071919,
  'subsample_for_bin': 200000}


一旦我們有了這些超參數(shù),我們就可以使用它們來訓(xùn)練完整訓(xùn)練數(shù)據(jù)的模型,然后評估測試數(shù)據(jù)。 最終結(jié)果如下:

The best model scores 0.72506 AUC ROC on the test set.
The best cross validation score was 0.77101 AUC ROC.
This was achieved after 413 search iterations.


作為參考,500次隨機搜索迭代返回了一個模型,該模型在測試集上評分為0.7232 ROC AUC,在交叉驗證中評分為0.76850。沒有優(yōu)化的默認模型在測試集上評分為0.7143 ROC AUC。


在查看結(jié)果時,請記住一些重要的注意事項:

  1. 最佳超參數(shù)是那些在交叉驗證方面表現(xiàn)最佳的參數(shù),而不一定是那些在測試數(shù)據(jù)上做得最好的參數(shù)。當(dāng)我們使用交叉驗證時,我們希望這些結(jié)果可以推廣到測試數(shù)據(jù)。

  2. 即使使用10倍交叉驗證,超參數(shù)調(diào)整也會過度擬合訓(xùn)練數(shù)據(jù)。交叉驗證的最佳分數(shù)顯著高于測試數(shù)據(jù)。

  3. 隨機搜索可以通過純粹的運氣返回更好的超參數(shù)(重新運行筆記本可以改變結(jié)果)。貝葉斯優(yōu)化不能保證找到更好的超參數(shù),并且可能陷入目標(biāo)函數(shù)的局部最小值。


另一個重點是超參數(shù)優(yōu)化的效果將隨數(shù)據(jù)集的不同而不同。相對較小的數(shù)據(jù)集(訓(xùn)練集大小為6000),調(diào)整超參數(shù),最終得到的模型的提升并不大,但數(shù)據(jù)集更大時,效果會很明顯。


因此,通過貝葉斯概率來優(yōu)化超參數(shù),我們可以:

  • 在測試集上得到更好的性能

  • 調(diào)整超參數(shù)的迭代次數(shù)減少


可視化結(jié)果


繪制結(jié)果圖表是一種直觀的方式,可以了解超參數(shù)搜索過程中發(fā)生的情況。此外,通過將貝葉斯優(yōu)化與隨機搜索進行比較,可以看出方法的不同之處。


首先,我們可以制作隨機搜索和貝葉斯優(yōu)化中采樣的learning_rate的核密度估計圖。作為參考,我們還可以顯示采樣分布。垂直虛線表示學(xué)習(xí)率的最佳值(根據(jù)交叉驗證)。



我們將學(xué)習(xí)率定義為0.005到0.2之間的對數(shù)正態(tài),貝葉斯優(yōu)化結(jié)果看起來與采樣分布類似。 這告訴我們,我們定義的分布看起來適合于任務(wù),盡管最佳值比我們放置最大概率的值略高。 這可用于告訴域進一步搜索。


另一個超參數(shù)是增強類型,在隨機搜索和貝葉斯優(yōu)化期間評估每種類型的條形圖。 由于隨機搜索不關(guān)注過去的結(jié)果,我們預(yù)計每種增強類型的使用次數(shù)大致相同。



根據(jù)貝葉斯算法,gdbt提升模型比dart或goss更有前途。 同樣,這可以幫助進一步搜索,貝葉斯方法或網(wǎng)格搜索。 如果我們想要進行更明智的網(wǎng)格搜索,我們可以使用這些結(jié)果來定義圍繞超參數(shù)最有希望的值的較小網(wǎng)格。


由于我們有它們,讓我們看看參考分布,隨機搜索和貝葉斯優(yōu)化中的所有數(shù)字超參數(shù)。 垂直線再次表示每次搜索的超參數(shù)的最佳值:








在大多數(shù)情況下(subsampleforbin除外),貝葉斯優(yōu)化搜索傾向于在超參數(shù)值附近集中(放置更多概率),從而產(chǎn)生交叉驗證中的最低損失。這顯示了使用貝葉斯方法進行超參數(shù)調(diào)整的基本思想:花費更多時間來評估有希望的超參數(shù)值。


此處還有一些有趣的結(jié)果可能會幫助我們在將來定義要搜索的域空間時。僅舉一個例子,看起來regalpha和reglambda應(yīng)該相互補充:如果一個是高(接近1.0),另一個應(yīng)該更低。不能保證這會解決問題,但通過研究結(jié)果,我們可以獲得可能適用于未來機器學(xué)習(xí)問題的見解!


搜索的演變


隨著優(yōu)化的進展,我們期望貝葉斯方法關(guān)注超參數(shù)的更有希望的值:那些在交叉驗證中產(chǎn)生最低誤差的值。我們可以繪制超參數(shù)與迭代的值,以查看是否存在明顯的趨勢。



黑星表示最佳值。 colsamplebytree和learningrate會隨著時間的推移而減少,這可能會指導(dǎo)我們未來的搜索。



最后,如果貝葉斯優(yōu)化工作正常,我們預(yù)計平均驗證分數(shù)會隨著時間的推移而增加(相反,損失減少):



來自貝葉斯超參數(shù)優(yōu)化的驗證集上的分數(shù)隨著時間的推移而增加,表明該方法正在嘗試“更好”的超參數(shù)值(應(yīng)該注意,僅根據(jù)驗證集上的表現(xiàn)更好)。隨機搜索沒有顯示迭代的改進。


繼續(xù)搜索


如果我們對模型的性能不滿意,我們可以繼續(xù)使用Hyperopt進行搜索。我們只需要傳入相同的試驗對象,算法將繼續(xù)搜索。


隨著算法的進展,它會進行更多的挖掘 - 挑選過去表現(xiàn)良好的價值 , 而不是探索 - 挑選新價值。因此,開始完全不同的搜索可能是一個好主意,而不是從搜索停止的地方繼續(xù)開始。如果來自第一次搜索的最佳超參數(shù)確實是“最優(yōu)的”,我們希望后續(xù)搜索專注于相同的值。


經(jīng)過另外500次訓(xùn)練后,最終模型在測試集上得分為0.72736 ROC AUC。 (我們實際上不應(yīng)該評估測試集上的第一個模型,而只依賴于驗證分數(shù)。理想情況下,測試集應(yīng)該只使用一次,以便在部署到新數(shù)據(jù)時獲得算法性能的度量)。同樣,由于數(shù)據(jù)集的小尺寸,這個問題可能導(dǎo)致進一步超參數(shù)優(yōu)化的收益遞減,并且最終會出現(xiàn)驗證錯誤的平臺(由于隱藏變量導(dǎo)致數(shù)據(jù)集上任何模型的性能存在固有限制未測量和噪聲數(shù)據(jù),稱為貝葉斯誤差。


結(jié)論


可以使用貝葉斯優(yōu)化來完成機器學(xué)習(xí)模型的超參數(shù)自動調(diào)整。與隨機或網(wǎng)格搜索相比,貝葉斯優(yōu)化對目標(biāo)函數(shù)的評估較少,測試集上的更好的泛化性能。


在本文中,我們使用Hyperopt逐步完成了Python中的貝葉斯超參數(shù)優(yōu)化。除了網(wǎng)格和隨機搜索之外,我們還能夠提高梯度增強機的測試集性能,盡管我們需要謹慎對待訓(xùn)練數(shù)據(jù)的過度擬合。此外,我們通過可視化結(jié)果表看到隨機搜索與貝葉斯優(yōu)化的不同之處,這些圖表顯示貝葉斯方法對超參數(shù)值的概率更大,導(dǎo)致交叉驗證損失更低。


使用優(yōu)化問題的四個部分,我們可以使用Hyperopt來解決各種各樣的問題。貝葉斯優(yōu)化的基本部分也適用于Python中實現(xiàn)不同算法的許多庫。從手動切換到隨機或網(wǎng)格搜索只是一小步,但要將機器學(xué)習(xí)提升到新的水平,需要一些自動形式的超參數(shù)調(diào)整。貝葉斯優(yōu)化是一種易于在Python中使用的方法,并且可以比隨機搜索返回更好的結(jié)果。


原文鏈接:

https:///automated-machine-learning-hyperparameter-tuning-in-python-dfda59b72f8a

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多