日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

LESSON 12.7 梯度提升树的参数空间与TPE优化

發布時間:2025/4/5 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LESSON 12.7 梯度提升树的参数空间与TPE优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

三 GBDT的參數空間與超參數優化

1 確定GBDT優化的參數空間

豐富的超參數為集成算法提供了無限的可能,以降低偏差為目的的Boosting算法們在調參之后的表現更是所向披靡,因此GBDT的超參數自動優化也是一個重要的課題。對任意集成算法進行超參數優化之前,我們需要明確兩個基本事實:

1、不同參數對算法結果的影響力大小

2、確定用于搜索的參數空間

對GBDT來說,我們可以大致如下排列各個參數對算法的影響:

如果你熟悉隨機森林的超參數,你會發現GBDT中大部分超參數的影響力等級都非常容易理解。樹的集成模型們大多擁有相似的超參數,例如抗過擬合、用來剪枝的參數群(max_depth、min_samples_split等),又比如對樣本/特征進行抽樣的參數們(subsample,max_features等),這些超參數在不同的集成模型中以相似的方式影響模型,因此原則上來說,對隨機森林影響較大的參數對GBDT也會有較大的影響。

然而你可能注意到,在隨機森林中非常關鍵的max_depth在GBDT中沒有什么地位,取而代之的是Boosting中特有的迭代參數學習率learning_rate。在隨機森林中,我們總是在意模型復雜度(max_depth)與模型整體學習能力(n_estimators)的平衡,單一弱評估器的復雜度越大,單一弱評估器對模型的整體貢獻就越大,因此需要的樹數量就越少。在Boosting算法當中,單一弱評估器對整體算法的貢獻由學習率參數learning_rate控制,代替了弱評估器復雜度的地位,因此Boosting算法中我們尋找的是learning_rate與n_estimators的平衡。同時,Boosting算法天生就假設單一弱評估器的能力很弱,參數max_depth的默認值也往往較小(在GBDT中max_depth的默認值是3),因此我們無法靠降低max_depth的值來大規模降低模型復雜度,更難以靠max_depth來控制過擬合,自然max_depth的影響力就變小了。

可見,雖然樹的集成算法們大多共享相同的超參數,都由于不同算法構建時的原理假設不同,相同參數在不同算法中的默認值可能被設置得不同,因此相同參數在不同算法中的重要性和調參思路也不同

在講解隨機森林時我們提到過,精剪枝工具的效用有限,剪枝一般還是大刀闊斧的粗剪枝更有效。在GBDT中,由于max_depth這一粗剪枝工具的默認值為3,因此在Boosting算法中通過削減模型復雜度來控制過擬合的思路就無法走通。特別地,參數init對GBDT的影響很大,如果在參數init中填入具體的算法,過擬合可能會變得更加嚴重,因此我們需要在抑制過擬合、控制復雜度這一點上令下功夫。

如果無法對弱評估器進行剪枝,最好的控制過擬合的方法就是增加隨機性/多樣性,因此max_features和subsample就成為Boosting算法中控制過擬合的核心武器,這也是GBDT中會加入Bagging思想的關鍵原因之一。依賴于隨機性、而非弱評估器結構來對抗過擬合的特點,讓Boosting算法獲得了一個意外的優勢:比起Bagging,Boosting更加擅長處理小樣本高維度的數據,因為Bagging數據很容易在小樣本數據集上過擬合。

需要注意的是,雖然max_depth在控制過擬合上的貢獻不大,但是我們在調參時依然需要保留這個參數。當我們使用參數max_features與subsample構建隨機性、并加大每一棵樹之間的差異后,模型的學習能力可能受到影響,因此我們可能需要提升單一弱評估器的復雜度。因此在GBDT當中,max_depth的調參方向是放大/加深,以探究模型是否需要更高的單一評估器復雜度。相對的在隨機森林當中,max_depth的調參方向是縮小/剪枝,用以緩解過擬合。

那在調參的時候,我們應該選擇哪些參數呢?與隨機森林一樣,我們首先會考慮所有影響力巨大的參數(5星參數),當算力足夠/優化算法運行較快的時候,我們可以考慮將大部分時候具有影響力的參數(4星)也都加入參數空間,如果樣本量較小,我們可能不選擇subsample。除此之外,我們還需要部分影響弱評估器復雜度的參數,例如max_depth。如果算力充足,我們還可以加入criterion這樣或許會有效的參數。在這樣的基本思想下,考慮到硬件與運行時間因素,我將選擇如下參數進行調整,并使用基于TPE貝葉斯優化(HyperOpt)對GBDT進行優化——

在此基礎上,我們需要進一步確認參數空間。幸運的是,GBDT的參數空間幾乎不依賴于樹的真實結構進行調整,且大部分參數都有固定的范圍,因此我們只需要對無界的參數稍稍探索即可。

其中,我們在《二 4 袋外數據》部分已經對n_estimators進行過探索(如果你沒有經過這個探索,則需要繪制繪制學習曲線進行探索),在當前默認參數下,我們大約只需要50次迭代就能夠讓損失函數收斂,因此我們可以就50這個數字向兩側展開來設置n_estimators的搜索范圍。

另外,原則上來說需要用Tree對象來探索min_impurity_decrease的值,但由于我們這個參數只能放大、不能縮小,且我們知道精剪枝對于GBDT的影響較小,因此就不必大費周章地給與該參數一個精細的范圍了,按照(0,C)結構走即可,其中C為任意常數。

具體每個參數的初始范圍確定如下:
一般在初次搜索時,我們會設置范圍較大、較為稀疏的參數空間,然后在多次搜索中逐漸縮小范圍、降低參數空間的維度。需要注意的是,init參數中需要輸入的評估器對象無法被HyperOpt庫識別,因此參數init我們只能手動調參。在之前的課程《二 1.1 初始預測結果H0的設置》中,我們已經對init嘗試過三種參數,分別是精調后的模型rf,字符串"zero"與"None",并且我們已經看到rf給出的結果是最好的,因此我們就按照init= rf來設置參數,不再對該參數進行調整。

2 基于TPE對GBDT進行優化

#日常使用庫與算法 import pandas as pd import numpy as np import sklearn import matplotlib as mlp import matplotlib.pyplot as plt import time from sklearn.ensemble import RandomForestRegressor as RFR from sklearn.ensemble import GradientBoostingRegressor as GBR from sklearn.model_selection import cross_validate, KFold#導入優化算法 import hyperopt from hyperopt import hp, fmin, tpe, Trials, partial from hyperopt.early_stop import no_progress_lossdata = pd.read_csv("/Users/zhucan/Desktop/train_encode.csv",index_col=0) X = data.iloc[:,:-1] y = data.iloc[:,-1] X.shape #(1460, 80)

Step 1.建立benchmark

Step 2.定義參數init需要的算法

rf = RFR(n_estimators=89, max_depth=22, max_features=14,min_impurity_decrease=0,random_state=1412, verbose=False, n_jobs=-1)

Step 3.定義目標函數、參數空間、優化函數、驗證函數

目標函數

def hyperopt_objective(params):reg = GBR(n_estimators = int(params["n_estimators"]),learning_rate = params["lr"],criterion = params["criterion"],loss = params["loss"],max_depth = int(params["max_depth"]),max_features = params["max_features"],subsample = params["subsample"],min_impurity_decrease = params["min_impurity_decrease"],init = rf,random_state=1412,verbose=False)cv = KFold(n_splits=5,shuffle=True,random_state=1412)validation_loss = cross_validate(reg,X,y,scoring="neg_root_mean_squared_error",cv=cv,verbose=False,n_jobs=-1,error_score='raise')return np.mean(abs(validation_loss["test_score"]))

參數空間

param_grid_simple = {'n_estimators': hp.quniform("n_estimators",25,200,25),"lr": hp.quniform("learning_rate",0.05,2.05,0.05),"criterion": hp.choice("criterion",["friedman_mse", "squared_error", "mse", "mae"]),"loss":hp.choice("loss",["squared_error","absolute_error", "huber", "quantile"]),"max_depth": hp.quniform("max_depth",2,30,2),"subsample": hp.quniform("subsample",0.1,0.8,0.1),"max_features": hp.choice("max_features",["log2","sqrt",16,32,64,"auto"]),"min_impurity_decrease":hp.quniform("min_impurity_decrease",0,5,1)}

計算參數空間大小:

len(range(25,200,25)) * len(np.arange(0.05,2.05,0.05)) * 4 * 4 * len(range(2,30,2)) * len(np.arange(0.1,0.8,0.1)) * 6 * len(range(0,5,1)) #13171200

雖然參數分布較為稀疏,但由于參數維度眾多、范圍較大,因此這是一個超大的參數空間。

優化函數

def param_hyperopt(max_evals=100):#保存迭代過程trials = Trials()#設置提前停止early_stop_fn = no_progress_loss(100)#定義代理模型params_best = fmin(hyperopt_objective, space = param_grid_simple, algo = tpe.suggest, max_evals = max_evals, verbose=True, trials = trials, early_stop_fn = early_stop_fn)#打印最優參數,fmin會自動打印最佳分數print("\n","\n","best params: ", params_best,"\n")return params_best, trials

驗證函數(可選)

def hyperopt_validation(params): reg = GBR(n_estimators = int(params["n_estimators"]),learning_rate = params["learning_rate"],criterion = params["criterion"],loss = params["loss"],max_depth = int(params["max_depth"]),max_features = params["max_features"],subsample = params["subsample"],min_impurity_decrease = params["min_impurity_decrease"],init = rf,random_state=1412 #GBR中的random_state只能夠控制特征抽樣,不能控制樣本抽樣,verbose=False)cv = KFold(n_splits=5,shuffle=True,random_state=1412)validation_loss = cross_validate(reg,X,y,scoring="neg_root_mean_squared_error",cv=cv,verbose=False,n_jobs=-1)return np.mean(abs(validation_loss["test_score"]))#然而對gbdt而言random_state只控制特征隨機性不控制樣本隨機性

Step 4.訓練貝葉斯優化器

params_best, trials = param_hyperopt(30) #使用小于0.1%的空間進行訓練 #100%|████████████████████████████████████████████████| 30/30 [00:45<00:00, 1.51s/trial, best loss: 26888.311566837725]# best params: {'criterion': 2, 'learning_rate': 0.2, 'loss': 0, 'max_depth': 24.0, 'max_features': 0, 'min_impurity_decrease': 5.0, 'n_estimators': 175.0, 'subsample': 0.7000000000000001} params_best #注意hp.choice返回的結果是索引,而不是具體的數字 #{'criterion': 2, # 'learning_rate': 0.2, # 'loss': 0, # 'max_depth': 24.0, # 'max_features': 0, # 'min_impurity_decrease': 5.0, # 'n_estimators': 175.0, # 'subsample': 0.7000000000000001}

hyperopt_validation({'criterion': "mse",'learning_rate': 0.2,'loss': "squared_error",'max_depth': 24.0,'max_features': "log2",'min_impurity_decrease': 5.0,'n_estimators': 175.0,'subsample': 0.7}) #26888.311566837725

不難發現,我們已經得到了歷史最好分數,但GBDT的潛力遠不止如此。現在我們可以根據第一次訓練出的結果縮小參數空間,繼續進行搜索。在多次搜索中,我發現loss參數的最優選項基本都是平方誤差"squared_error",因此我們可以將該參數排除出搜索隊伍。同樣,對于其他參數,我們則根據搜索結果修改空間范圍、增加空間密度,一般以被選中的值為中心向兩邊拓展,并減小步長,同時范圍可以向我們認為會被選中的一邊傾斜。例如最大深度max_depth被選為24,我們則將原本的范圍(2,30,2)修改為(10,35,1)。同樣subsample被選為0.7,我們則將新范圍調整為(0.5,1.0,0.05),依次類推。

param_grid_simple = {'n_estimators': hp.quniform("n_estimators",150,200,5),"lr": hp.quniform("learning_rate",0.05,3,0.05),"criterion": hp.choice("criterion",["friedman_mse", "squared_error", "mse","mae"]),"max_depth": hp.quniform("max_depth",10,35,1),"subsample": hp.quniform("subsample",0.5,1,0.05),"max_features": hp.quniform("max_features",10,30,1),"min_impurity_decrease":hp.quniform("min_impurity_decrease",0,5,0.5)}

由于需要修改參數空間,因此目標函數也必須跟著修改:

def hyperopt_objective(params):reg = GBR(n_estimators = int(params["n_estimators"]),learning_rate = params["lr"],criterion = params["criterion"],max_depth = int(params["max_depth"]),max_features = int(params["max_features"]),subsample = params["subsample"],min_impurity_decrease = params["min_impurity_decrease"],loss = "squared_error",init = rf,random_state=1412,verbose=False)cv = KFold(n_splits=5,shuffle=True,random_state=1412)validation_loss = cross_validate(reg,X,y,scoring="neg_root_mean_squared_error",cv=cv,verbose=False,n_jobs=-1,error_score='raise')return np.mean(abs(validation_loss["test_score"]))params_best, trials = param_hyperopt(30) #使用小于0.1%的空間進行訓練 #100%|████████████████████████████████████████████████| 30/30 [00:21<00:00, 1.42trial/s, best loss: 26705.080632614565]# best params: {'criterion': 2, 'learning_rate': 0.28, 'max_depth': 16.0, 'max_features': 16.0, 'min_impurity_decrease': 3.5, 'n_estimators': 185.0, 'subsample': 0.8500000000000001} params_best, trials = param_hyperopt(100) #嘗試增加搜索次數 #100%|███████████████████████████████████████████████| 100/100 [05:09<00:00, 3.09s/trial, best loss: 26519.12578332204]# best params: {'criterion': 2, 'learning_rate': 0.1, 'max_depth': 12.0, 'max_features': 19.0, 'min_impurity_decrease': 0.0, 'n_estimators': 195.0, 'subsample': 0.55}

基于該結果,我們又可以確定進一步確定部分參數的值(比如criterion),再次縮小參數范圍、增加參數空間的密集程度:

def hyperopt_objective(params):reg = GBR(n_estimators = int(params["n_estimators"]),learning_rate = params["lr"],max_depth = int(params["max_depth"]),max_features = int(params["max_features"]),subsample = params["subsample"],min_impurity_decrease = params["min_impurity_decrease"],criterion = "mse",loss = "squared_error",init = rf,random_state=1412,verbose=False)cv = KFold(n_splits=5,shuffle=True,random_state=1412)validation_loss = cross_validate(reg,X,y,scoring="neg_root_mean_squared_error",cv=cv,verbose=False,n_jobs=-1,error_score='raise')return np.mean(abs(validation_loss["test_score"]))param_grid_simple = {'n_estimators': hp.quniform("n_estimators",175,200,1),"lr": hp.quniform("learning_rate",0.1,0.3,0.005),"max_depth": hp.quniform("max_depth",8,22,1),"subsample": hp.quniform("subsample",0.5,1,0.0025),"max_features": hp.quniform("max_features",10,20,1),"min_impurity_decrease":hp.quniform("min_impurity_decrease",1,4,0.25)}params_best, trials = param_hyperopt(300) #縮小參數空間的同時增加迭代次數 # 57%|██████████████████████████▉ | 172/300 [01:46<01:19, 1.62trial/s, best loss: 26501.91471740881]# best params: {'learning_rate': 0.115, 'max_depth': 8.0, 'max_features': 12.0, 'min_impurity_decrease': 2.5, 'n_estimators': 183.0, 'subsample': 1.0} def param_hyperopt(max_evals=100):#保存迭代過程trials = Trials()#設置提前停止#early_stop_fn = no_progress_loss(100)#定義代理模型params_best = fmin(hyperopt_objective, space = param_grid_simple, algo = tpe.suggest, max_evals = max_evals, verbose=True, trials = trials#, early_stop_fn = early_stop_fn)#打印最優參數,fmin會自動打印最佳分數print("\n","\n","best params: ", params_best,"\n")return params_best, trialsparams_best, trials = param_hyperopt(300) #取消提前停止,繼續迭代 #100%|██████████████████████████████████████████████| 300/300 [03:02<00:00, 1.64trial/s, best loss: 26448.278731971972]# best params: {'learning_rate': 0.25, 'max_depth': 9.0, 'max_features': 15.0, 'min_impurity_decrease': 3.25, 'n_estimators': 176.0, 'subsample': 1.0} params_best, trials = param_hyperopt(1000) #100%|████████████████████████████████████████████| 1000/1000 [12:07<00:00, .38trial/s, best loss: 26415.835015071534]# best params: {'learning_rate': 0.21, 'max_depth': 8.0, 'max_features': 13.0, 'min_impurity_decrease': 3.5, 'n_estimators': 187.0, 'subsample': 0.8675} start = time.time() hyperopt_validation({'criterion': "mse",'learning_rate': 0.21,'loss': "squared_error",'max_depth': 8.0,'max_features': 13,'min_impurity_decrease': 3.5,'n_estimators': 187.0,'subsample': 0.8675}) #26415.835015071538 end = (time.time() - start) print(end) #1.5381402969360352


GBDT獲得了目前為止的最高分,雖然這一組參數最終指向了187棵樹,導致GBDT運行所需的時間遠遠高于其他算法,GBDT上得到的分數是比精細調參后的隨機森林還低2000左右,這證明了GBDT在學習能力上的優越性。由于TPE是帶有強隨機性的過程,因此如果我們多次運行,我們將得到不同的結果,但GBDT的預測分數可以穩定在26500上下。如果算力支持使用更多的迭代次數、或使用更大更密集的參數空間,我們或許可以得到更好的分數。同時,如果我們能夠找到一組大學習率、小迭代次數的參數,那GBDT的訓練速度也會隨之上升。你可以試著在最初的范圍中尋找另一個突破口,嘗試將GBDT的速度降低。

總結

以上是生活随笔為你收集整理的LESSON 12.7 梯度提升树的参数空间与TPE优化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。