LESSON 9.4 集成算法的参数空间与网格优化
四 集成算法的參數(shù)空間與網(wǎng)格優(yōu)化
如隨機(jī)森林中所展示的,集成算法的超參數(shù)種類繁多、取值豐富,且參數(shù)之間會相互影響、共同作用于算法的最終結(jié)果,因此集成算法的調(diào)參是一個(gè)難度很高的過程。在超參數(shù)優(yōu)化還未盛行的時(shí)候,隨機(jī)森林的調(diào)參是基于方差-偏差理論(variance-bias trade-off)和學(xué)習(xí)曲線完成的,而現(xiàn)在我們可以依賴于網(wǎng)格搜索來完成自動優(yōu)化。在對任意算法進(jìn)行網(wǎng)格搜索時(shí),我們需要明確兩個(gè)基本事實(shí)
1、參數(shù)對算法結(jié)果的影響力大小
2、用于進(jìn)行搜索的參數(shù)空間
對隨機(jī)森林來說,我們可以大致如下排列各個(gè)參數(shù)對算法的影響:
隨機(jī)森林在剪枝方面的空間總是很大的,因?yàn)槟J(rèn)參數(shù)下樹的結(jié)構(gòu)基本沒有被影響(也就是幾乎沒有剪枝),因此當(dāng)隨機(jī)森林過擬合的時(shí)候,我們可以嘗試粗、精、隨機(jī)等各種方式來影響隨機(jī)森林。通常在網(wǎng)格搜索當(dāng)中,我們會考慮所有有巨大影響力的參數(shù)、以及1、2個(gè)影響力不明顯的參數(shù)。
雖然隨機(jī)森林調(diào)參的空間較大,大部分人在調(diào)參過程中依然難以突破,因?yàn)?strong>樹的集成模型的參數(shù)空間非常難以確定。當(dāng)沒有數(shù)據(jù)支撐時(shí),人們很難通過感覺或經(jīng)驗(yàn)來找到正確的參數(shù)范圍。舉例來說,我們也很難直接判斷究竟多少棵樹對于當(dāng)前的模型最有效,同時(shí),我們也很難判斷不剪枝時(shí)一棵決策樹究竟有多深、有多少葉子、或者一片葉子上究竟有多少個(gè)樣本,更不要談憑經(jīng)驗(yàn)判斷樹模型整體的不純度情況了。可以說,當(dāng)森林建好之后,我們簡直是對森林一無所知。對于網(wǎng)格搜索來說,新增一個(gè)潛在的參數(shù)可選值,計(jì)算量就會指數(shù)級增長,因此找到有效的參數(shù)空間非常重要。此時(shí)我們就要引入兩個(gè)工具來幫助我們:
1、學(xué)習(xí)曲線
2、決策樹對象Tree的屬性
- 學(xué)習(xí)曲線
學(xué)習(xí)曲線是以參數(shù)的不同取值為橫坐標(biāo),模型的結(jié)果為縱坐標(biāo)的曲線。當(dāng)模型的參數(shù)較少、且參數(shù)之間的相互作用較小時(shí),我們可以直接使用學(xué)習(xí)曲線進(jìn)行調(diào)參。但對于集成算法來說,學(xué)習(xí)曲線更多是我們探索參數(shù)與模型關(guān)系的關(guān)鍵手段。許多參數(shù)對模型的影響是確定且單調(diào)的,例如`n_estimators`,樹越多模型的學(xué)習(xí)能力越強(qiáng),再比如`ccp_alpha`,該參數(shù)值越大模型抗過擬合能力越強(qiáng),因此我們可能通過學(xué)習(xí)曲線找到這些參數(shù)對模型影響的極限。我們會圍繞這些極限點(diǎn)來構(gòu)筑我們的參數(shù)空間。
先來看看n_estimators的學(xué)習(xí)曲線:
#參數(shù)潛在取值,由于現(xiàn)在我們只調(diào)整一個(gè)參數(shù),因此參數(shù)的范圍可以取大一些、取值也可以更密集 Option = [1,*range(5,101,5)]#生成保存模型結(jié)果的arrays trainRMSE = np.array([]) testRMSE = np.array([]) trainSTD = np.array([]) testSTD = np.array([])#在參數(shù)取值中進(jìn)行循環(huán) for n_estimators in Option:#按照當(dāng)下的參數(shù),實(shí)例化模型reg_f = RFR(n_estimators=n_estimators,random_state=1412)#實(shí)例化交叉驗(yàn)證方式,輸出交叉驗(yàn)證結(jié)果cv = KFold(n_splits=5,shuffle=True,random_state=1412)result_f = cross_validate(reg_f,X,y,cv=cv,scoring="neg_mean_squared_error",return_train_score=True,n_jobs=-1)#根據(jù)輸出的MSE進(jìn)行RMSE計(jì)算train = abs(result_f["train_score"])**0.5test = abs(result_f["test_score"])**0.5#將本次交叉驗(yàn)證中RMSE的均值、標(biāo)準(zhǔn)差添加到arrays中進(jìn)行保存trainRMSE = np.append(trainRMSE,train.mean()) #效果越好testRMSE = np.append(testRMSE,test.mean())trainSTD = np.append(trainSTD,train.std()) #模型越穩(wěn)定testSTD = np.append(testSTD,test.std())def plotCVresult(Option,trainRMSE,testRMSE,trainSTD,testSTD):#一次交叉驗(yàn)證下,RMSE的均值與std的繪圖xaxis = Optionplt.figure(figsize=(8,6),dpi=80)#RMSEplt.plot(xaxis,trainRMSE,color="k",label = "RandomForestTrain")plt.plot(xaxis,testRMSE,color="red",label = "RandomForestTest")#標(biāo)準(zhǔn)差 - 圍繞在RMSE旁形成一個(gè)區(qū)間plt.plot(xaxis,trainRMSE+trainSTD,color="k",linestyle="dotted")plt.plot(xaxis,trainRMSE-trainSTD,color="k",linestyle="dotted")plt.plot(xaxis,testRMSE+testSTD,color="red",linestyle="dotted")plt.plot(xaxis,testRMSE-testSTD,color="red",linestyle="dotted")plt.xticks([*xaxis])plt.legend(loc=1)plt.show()plotCVresult(Option,trainRMSE,testRMSE,trainSTD,testSTD)當(dāng)繪制學(xué)習(xí)曲線時(shí),我們可以很容易找到泛化誤差開始上升、或轉(zhuǎn)變?yōu)槠椒€(wěn)趨勢的轉(zhuǎn)折點(diǎn)。因此我們可以選擇轉(zhuǎn)折點(diǎn)或轉(zhuǎn)折點(diǎn)附近的n_estimators取值,例如20。然而,n_estimators會受到其他參數(shù)的影響,例如:
- 單棵決策樹的結(jié)構(gòu)更簡單時(shí)(依賴剪枝時(shí)),可能需要更多的樹
- 單棵決策樹訓(xùn)練的數(shù)據(jù)更簡單時(shí)(依賴隨機(jī)性時(shí)),可能需要更多的樹
因此n_estimators的參數(shù)空間可以被確定為range(20,100,5),如果你比較保守,甚至可以確認(rèn)為是range(15,25,5)。
- 決策樹對象Tree
在sklearn中,樹模型是單獨(dú)的一類對象,每個(gè)樹模型背后都有一套完整的屬性供我們調(diào)用,包括樹的結(jié)構(gòu)、樹的規(guī)模等眾多細(xì)節(jié)。在之前的課程中,我們曾經(jīng)使用過樹模型的繪圖功能plot_tree,除此之外樹還有許多有用的屬性。隨機(jī)森林是樹組成的算法,因此也可以調(diào)用這些屬性。我們來舉例說明:
reg_f = RFR(n_estimators=10,random_state=1412) reg_f = reg_f.fit(X,y) #訓(xùn)練一個(gè)隨機(jī)森林屬性.estimators_,查看森林中所有的樹
reg_f.estimators_ #一片隨機(jī)森林中所有的樹 # [DecisionTreeRegressor(max_features='auto', random_state=1630984966), # DecisionTreeRegressor(max_features='auto', random_state=472863509), # DecisionTreeRegressor(max_features='auto', random_state=1082704530), # DecisionTreeRegressor(max_features='auto', random_state=1930362544), # DecisionTreeRegressor(max_features='auto', random_state=273973624), # DecisionTreeRegressor(max_features='auto', random_state=21991934), # DecisionTreeRegressor(max_features='auto', random_state=1886585710), # DecisionTreeRegressor(max_features='auto', random_state=63725675), # DecisionTreeRegressor(max_features='auto', random_state=1374343434), # DecisionTreeRegressor(max_features='auto', random_state=1078007175)]#可以用索引單獨(dú)提取一棵樹 reg_f.estimators_[0] #DecisionTreeRegressor(max_features='auto', random_state=1630984966)#調(diào)用這棵樹的底層結(jié)構(gòu) reg_f.estimators_[0].tree_ #<sklearn.tree._tree.Tree at 0x1c070d8e340>屬性.max_depth,查看當(dāng)前樹的實(shí)際深度
reg_f.estimators_[0].tree_.max_depth #max_depth=None #19#對森林中所有樹查看實(shí)際深度 for t in reg_f.estimators_:print(t.tree_.max_depth) #19 #25 #27 #20 #23 #22 #22 #20 #22 #24#如果樹的數(shù)量較多,也可以查看平均或分布 reg_f = RFR(n_estimators=100,random_state=1412) reg_f = reg_f.fit(X,y) #訓(xùn)練一個(gè)隨機(jī)森林 d = pd.Series([],dtype="int64") for idx,t in enumerate(reg_f.estimators_):d[idx] = t.tree_.max_depthd.mean() #22.25d.describe() #count 100.000000 #mean 22.250000 #std 1.955954 #min 19.000000 #25% 21.000000 #50% 22.000000 #75% 23.000000 #max 30.000000 #dtype: float64假設(shè)現(xiàn)在你的隨機(jī)森林過擬合,max_depth的最大深度范圍設(shè)置在[15,25]之間就會比較有效,如果我們希望激烈地剪枝,則可以設(shè)置在[10,15]之間。
相似的,我們也可以調(diào)用其他屬性來輔助我們調(diào)參:
#一棵樹上的總?cè)~子量 reg_f.estimators_[0].tree_.node_count #1807#所有樹上的總?cè)~子量 for t in reg_f.estimators_:print(t.tree_.node_count) #1807 #1777 #1763 #1821 #1777 #1781 #1811 #1771 #1753 #1779?根據(jù)經(jīng)驗(yàn),當(dāng)決策樹不減枝且在訓(xùn)練集上的預(yù)測結(jié)果不錯(cuò)時(shí),一棵樹上的葉子量常常與樣本量相當(dāng)或比樣本量更多,算法結(jié)果越糟糕,葉子量越少,如果RMSE很高或者R2很低,則可以考慮使用樣本量的一半或3/4作為不減枝時(shí)的葉子量的參考。
#每個(gè)節(jié)點(diǎn)上的不純度下降量,為-2則表示該節(jié)點(diǎn)是葉子節(jié)點(diǎn) reg_f.estimators_[0].tree_.threshold.tolist()[:20] # [6.5, # 5.5, # 327.0, # 214.0, # 0.5, # 1.0, # 104.0, # 0.5, # -2.0, # -2.0, # -2.0, # 105.5, # 28.5, # 0.5, # 1.5, # -2.0, # -2.0, # 11.0, # 1212.5, # 2.5]#你怎么知道m(xù)in_impurity_decrease的范圍設(shè)置多少會剪掉多少葉子? pd.Series(reg_f.estimators_[0].tree_.threshold).value_counts().sort_index() #-2.0 904 # 0.5 43 # 1.0 32 # 1.5 56 # 2.0 32 # ... # 1118.5 1 # 1162.5 1 # 1212.5 2 # 1254.5 1 # 1335.5 1 #Length: 413, dtype: int64pd.set_option("display.max_rows",None) np.cumsum(pd.Series(reg_f.estimators_[0].tree_.threshold).value_counts().sort_index()[1:]) #1.0 32 #1.5 88 #2.0 120 #2.5 167 #... ... #1212.5 858 #1254.5 859 #1335.5 860 #dtype: int64從這棵樹反饋的結(jié)果來看,min_impurity_decrease在現(xiàn)在的數(shù)據(jù)集上至少要設(shè)置到[2,10]的范圍才可能對模型有較大的影響。
#min_sample_split的范圍要如何設(shè)置才會剪掉很多葉子? np.bincount(reg_f.estimators_[0].tree_.n_node_samples.tolist())[:10] #array([ 0, 879, 321, 154, 86, 52, 42, 38, 29, 18], dtype=int64)更多屬性可以參考:
from sklearn.tree._tree import Tree type(Tree) #type help(Tree)- 使用網(wǎng)格搜索在隨機(jī)森林上進(jìn)行調(diào)參
現(xiàn)在模型正處于過擬合的狀態(tài),需要抗過擬合,且整體數(shù)據(jù)量不是非常多,隨機(jī)抽樣的比例不宜減小,因此我們挑選以下五個(gè)參數(shù)進(jìn)行搜索:n_estimators,max_depth,max_features,min_impurity_decrease,criterion。
import numpy as np import pandas as pd import sklearn import matplotlib as mlp import matplotlib.pyplot as plt import time #計(jì)時(shí)模塊time from sklearn.ensemble import RandomForestRegressor as RFR from sklearn.model_selection import cross_validate, KFold, GridSearchCVdef RMSE(cvresult,key):return (abs(cvresult[key])**0.5).mean()data = pd.read_csv(r"D:\Pythonwork\2021ML\PART 2 Ensembles\datasets\House Price\train_encode.csv",index_col=0)X = data.iloc[:,:-1] y = data.iloc[:,-1] X.shape #(1460, 80)X.head()Step 1.建立benchmark?
reg = RFR(random_state=1412) cv = KFold(n_splits=5,shuffle=True,random_state=1412)result_pre_adjusted = cross_validate(reg,X,y,cv=cv,scoring="neg_mean_squared_error",return_train_score=True,verbose=True,n_jobs=-1)RMSE(result_pre_adjusted,"train_score") #11177.272008319653RMSE(result_pre_adjusted,"test_score") #30571.26665524217Step 2.創(chuàng)建參數(shù)空間
param_grid_simple = {"criterion": ["squared_error","poisson"], 'n_estimators': [*range(20,100,5)], 'max_depth': [*range(10,25,2)], "max_features": ["log2","sqrt",16,32,64,"auto"], "min_impurity_decrease": [*np.arange(0,5,10)]}Step 3.實(shí)例化用于搜索的評估器、交叉驗(yàn)證評估器與網(wǎng)格搜索評估器
#n_jobs=4/8,verbose=True reg = RFR(random_state=1412,verbose=True,n_jobs=-1) cv = KFold(n_splits=5,shuffle=True,random_state=1412) search = GridSearchCV(estimator=reg,param_grid=param_grid_simple,scoring = "neg_mean_squared_error",verbose = True,cv = cv,n_jobs=-1)Step 4.訓(xùn)練網(wǎng)格搜索評估器
#=====【TIME WARNING: 7mins】=====# start = time.time() search.fit(X,y) print(time.time() - start) #Fitting 5 folds for each of 1536 candidates, totalling 7680 fits #381.6039867401123Step 5.查看結(jié)果
search.best_estimator_ #RandomForestRegressor(max_depth=23, max_features=16, min_impurity_decrease=0, # n_estimators=85, n_jobs=-1, random_state=1412, # verbose=True)abs(search.best_score_)**0.5 #29179.698261599166ad_reg = RFR(n_estimators=85, max_depth=23, max_features=16, random_state=1412) cv = KFold(n_splits=5,shuffle=True,random_state=1412) result_post_adjusted = cross_validate(ad_reg,X,y,cv=cv,scoring="neg_mean_squared_error",return_train_score=True,verbose=True,n_jobs=-1)RMSE(result_post_adjusted,"train_score") #11000.81099038192RMSE(result_post_adjusted,"test_score") #28572.070208366855#默認(rèn)值下隨機(jī)森林的RMSE xaxis = range(1,6) plt.figure(figsize=(8,6),dpi=80) #RMSE plt.plot(xaxis,abs(result_pre_adjusted["train_score"])**0.5,color="green",label = "RF_pre_ad_Train") plt.plot(xaxis,abs(result_pre_adjusted["test_score"])**0.5,color="green",linestyle="--",label = "RF_pre_ad_Test") plt.plot(xaxis,abs(result_post_adjusted["train_score"])**0.5,color="orange",label = "RF_post_ad_Train") plt.plot(xaxis,abs(result_post_adjusted["test_score"])**0.5,color="orange",linestyle="--",label = "RF_post_ad_Test") plt.xticks([1,2,3,4,5]) plt.xlabel("CVcounts",fontsize=16) plt.ylabel("RMSE",fontsize=16) plt.legend() plt.show()不難發(fā)現(xiàn),網(wǎng)格搜索之后的模型過擬合程度減輕,且在訓(xùn)練集與測試集上的結(jié)果都有提高,可以說從根本上提升了模型的基礎(chǔ)能力。我們還可以根據(jù)網(wǎng)格的結(jié)果繼續(xù)嘗試進(jìn)行其他調(diào)整,來進(jìn)一步降低模型在測試集上的RMSE。?
總結(jié)
以上是生活随笔為你收集整理的LESSON 9.4 集成算法的参数空间与网格优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lesson 8.3Lesson 8.4
- 下一篇: LESSON 9.5 随机森林在巨量数据