菜菜sklearn——XGBoost(3)
4 XGBoost應用中的其他問題
4.1 過擬合:剪枝參數與回歸模型調參
class xgboost.XGBRegressor (max_depth=3, learning_rate=0.1, n_estimators=100, silent=True,objective=‘reg:linear’, booster=‘gbtree’, n_jobs=1, nthread=None, gamma=0, min_child_weight=1,max_delta_step=0, subsample=1, colsample_bytree=1, colsample_bylevel=1, reg_alpha=0, reg_lambda=1,scale_pos_weight=1, base_score=0.5, random_state=0, seed=None, missing=None, importance_type=‘gain’, kwargs)
作為天生過擬合的模型,XGBoost應用的核心之一就是減輕過擬合帶來的影響。作為樹模型,減輕過擬合的方式主要是靠對決策樹剪枝來降低模型的復雜度,以求降低方差。在之前的講解中,我們已經學習了好幾個可以用來防止過擬合的參數,包括上一節提到的復雜度控制 γ\gammaγ,正則化的兩個參數λ\lambdaλ和α\alphaα,控制迭代速度的參數η\etaη以及管理每次迭代前進行的隨機有放回抽樣的參數subsample。所有的這些參數都可以用來減輕過擬合。但除此之外,我們還有幾個影響重大的,專用于剪枝的參數:
這些參數中,樹的最大深度是決策樹中的剪枝法寶,算是最常用的剪枝參數,不過在XGBoost中,最大深度的功能與參數γ\gammaγ相似,因此如果先調節了γ\gammaγ ,則最大深度可能無法展示出巨大的效果。當然,如果先調整了最大深度,則γ\gammaγ也有可能無法顯示明顯的效果。通常來說,這兩個參數中我們只使用一個,不過兩個都試試也沒有壞處。
三個隨機抽樣特征的參數中,前兩個比較常用。在建立樹時對特征進行抽樣其實是決策樹和隨機森林中比較常見的一種方法,但是在XGBoost之前,這種方法并沒有被使用到boosting算法當中過。Boosting算法一直以抽取樣本(橫向抽樣)來調整模型過擬合的程度,而實踐證明其實縱向抽樣(抽取特征)更能夠防止過擬合。
參數min_child_weight不太常用,它是一篇葉子上的二階導數hih_{i}hi?之和,當樣本所對應的二階導數很小時,比如說為0.01,min_child_weight若設定為1,則說明一片葉子上至少需要100個樣本。本質上來說,這個參數其實是在控制葉子上所需的最小樣本量,因此對于樣本量很大的數據會比較有效。如果樣本量很小(比如我們現在使用的波士頓房價數據集,則這個參數效用不大)。就剪枝的效果來說,這個參數的功能也被γ\gammaγ替代了一部分,通常來說我們會試試看這個參數,但這個參數不是我的優先選擇。
通常當我們獲得了一個數據集后,我們先使用網格搜索找出比較合適的n_estimators和eta組合,然后使用gamma或者max_depth觀察模型處于什么樣的狀態(過擬合還是欠擬合,處于方差-偏差圖像的左邊還是右邊?),最后再決定是否要進行剪枝。通常來說,對于XGB模型,大多數時候都是需要剪枝的。接下來我們就來看看使用xgb.cv這個類來進行剪枝調參,以調整出一組泛化能力很強的參數。
讓我們先從最原始的,設定默認參數開始,先觀察一下默認參數下,我們的交叉驗證曲線長什么樣:
dfull = xgb.DMatrix(X,y)param1 = {'silent':True,'obj':'reg:linear',"subsample":1,"max_depth":6,"eta":0.3,"gamma":0,"lambda":1,"alpha":0,"colsample_bytree":1,"colsample_bylevel":1,"colsample_bynode":1,"nfold":5} num_round = 200time0 = time() cvresult1 = xgb.cv(param1, dfull, num_round) print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))fig,ax = plt.subplots(1,figsize=(15,8)) ax.set_ylim(top=5) ax.grid() ax.plot(range(1,201),cvresult1.iloc[:,0],c="red",label="train,original") ax.plot(range(1,201),cvresult1.iloc[:,2],c="orange",label="test,original") ax.legend(fontsize="xx-large") plt.show() #00:00:513584
從曲線上可以看出,模型現在處于過擬合的狀態。我們決定要進行剪枝。我們的目標是:訓練集和測試集的結果盡量接近,如果測試集上的結果不能上升,那訓練集上的結果降下來也是不錯的選擇(讓模型不那么具體到訓練數據,增加泛化能力)。在這里,我們要使用三組曲線。一組用于展示原始數據上的結果,一組用于展示上一個參數調節完畢后的結果,最后一組用于展示現在我們在調節的參數的結果。具體怎樣使用,我們來看:
在這里,為大家提供我調出來的結果,供大家參考:
param1 = {'silent':True,'obj':'reg:linear',"subsample":1,"max_depth":6,"eta":0.3,"gamma":0,"lambda":1,"alpha":0,"colsample_bytree":1,"colsample_bylevel":1,"colsample_bynode":1,"nfold":5} num_round = 200time0 = time() cvresult1 = xgb.cv(param1, dfull, num_round) print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))fig,ax = plt.subplots(1,figsize=(15,8)) ax.set_ylim(top=5) ax.grid() ax.plot(range(1,201),cvresult1.iloc[:,0],c="red",label="train,original") ax.plot(range(1,201),cvresult1.iloc[:,2],c="orange",label="test,original")param2 = {'silent':True,'obj':'reg:linear',"max_depth":2,"eta":0.05,"gamma":0,"lambda":1,"alpha":0,"colsample_bytree":1,"colsample_bylevel":0.4,"colsample_bynode":1,"nfold":5}param3 = {'silent':True,'obj':'reg:linear',"subsample":1,"eta":0.05,"gamma":20,"lambda":3.5,"alpha":0.2,"max_depth":4,"colsample_bytree":0.4,"colsample_bylevel":0.6,"colsample_bynode":1,"nfold":5}time0 = time() cvresult2 = xgb.cv(param2, dfull, num_round) print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))time0 = time() cvresult3 = xgb.cv(param3, dfull, num_round) print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))ax.plot(range(1,201),cvresult2.iloc[:,0],c="green",label="train,last") ax.plot(range(1,201),cvresult2.iloc[:,2],c="blue",label="test,last") ax.plot(range(1,201),cvresult3.iloc[:,0],c="gray",label="train,this") ax.plot(range(1,201),cvresult3.iloc[:,2],c="pink",label="test,this") ax.legend(fontsize="xx-large") plt.show() #00:00:532621 #00:00:223373 #00:00:259346
調參順序:
(1)eta+num_round
(2)gamma/max_depth
(3)抽樣進行調整
(4)正則化參數
在這個調整過程中,大家可能會有幾個問題:
當然可以!只要電腦有足夠的計算資源,并且你信任網格搜索,那任何時候我們都可以使用網格搜索。只是使用的時候要注意,首先XGB的參數非常多,參數可取的范圍也很廣,究竟是使用np.linspace或者np.arange作為參數的備選值也會影響結果,而且網格搜索的運行速度往往不容樂觀,因此建議至少先使用xgboost.cv來確認參數的范圍,否則很可能花很長的時間做了無用功。
并且,在使用網格搜索的時候,最好不要一次性將所有的參數都放入進行搜索,最多一次兩三個。有一些互相影響的參數需要放在一起使用,比如學習率eta和樹的數量n_estimators。
另外,如果網格搜索的結果與你的理解相違背,與你手動調參的結果相違背,選擇模型效果較好的一個。如果兩者效果差不多,那選擇相信手動調參的結果。網格畢竟是枚舉出結果,很多時候得出的結果可能會是具體到數據的巧合,我們無法去一一解釋網格搜索得出的結論為何是這樣。如果你感覺都無法解釋,那就不要去在意,直接選擇結果較好的一個。
會影響,因此在現實中,我們會優先調整那些對模型影響巨大的參數。在這里,我建議的剪枝上的調參順序是:n_estimators與eta共同調節,gamma或者max_depth,采樣和抽樣參數(縱向抽樣影響更大),最后才是正則化的兩個參數。當然,可以根據自己的需求來進行調整。
如果調參之后,交叉驗證曲線確實顯示測試集和訓練集上的模型評估效果是更加接近的,推薦使用調參之后的效果。我們希望增強模型的泛化能力,然而泛化能力的增強并不代表著在新數據集上模型的結果一定優秀,因為未知數據集并非一定符合全數據的分布,在一組未知數據上表現十分優秀,也不一定就能夠在其他的未知數據集上表現優秀。因此不必過于糾結在現有的測試集上是否表現優秀。當然了,在現有數據上如果能夠實現訓練集和測試集都非常優秀,
那模型的泛化能力自然也會是很強的。自己找一個數據集,剪枝試試看吧。
4.2 XGBoost模型的保存和調用
在使用Python進行編程時,我們可能會需要編寫較為復雜的程序或者建立復雜的模型。比如XGBoost模型,這個模型的參數復雜繁多,并且調參過程不是太容易,一旦訓練完畢,我們往往希望將訓練完畢后的模型保存下來,以便日后用于新的數據集。在Python中,保存模型的方法有許多種。我們以XGBoost為例,來講解兩種主要的模型保存和
調用方法。
4.2.1 使用Pickle保存和調用模型
pickle是python編程中比較標準的一個保存和調用模型的庫,我們可以使用pickle和open函數的連用,來將我們的模型保存到本地。以剛才我們已經調整好的參數和訓練好的模型為例,我們可以這樣來使用pickle:
import pickle dtrain = xgb.DMatrix(Xtrain,Ytrain)#設定參數,對模型進行訓練 param = {'silent':True,'obj':'reg:linear',"subsample":1,"eta":0.05,"gamma":20,"lambda":3.5,"alpha":0.2,"max_depth":4,"colsample_bytree":0.4,"colsample_bylevel":0.6,"colsample_bynode":1} num_round = 180bst = xgb.train(param, dtrain, num_round) #保存模型 pickle.dump(bst, open("xgboostonboston.dat","wb")) #注意,open中我們往往使用w或者r作為讀取的模式,但其實w與r只能用于文本文件 - txt #當我們希望導入的不是文本文件,而是模型本身的時候,我們使用"wb"和"rb"作為讀取的模式 #其中wb表示以二進制寫入,rb表示以二進制讀入,使用open進行保存的這個文件中是一個可以進行讀取或者調用的模型#看看模型被保存到了哪里? import sys sys.path #['C:\\Pythonwork\\micro-class\\11 xgboost', # 'C:\\Python\\python37.zip', # 'C:\\Python\\DLLs', # 'C:\\Python\\lib', # 'C:\\Python', # '', # 'C:\\Python\\lib\\site-packages', # 'C:\\Python\\lib\\site-packages\\win32', # 'C:\\Python\\lib\\site-packages\\win32\\lib', # 'C:\\Python\\lib\\site-packages\\Pythonwin', # 'C:\\Python\\lib\\site-packages\\IPython\\extensions', # 'C:\\Users\\Shuyu\\.ipython']#重新打開jupyter labfrom sklearn.datasets import load_boston from sklearn.model_selection import train_test_split as TTS from sklearn.metrics import mean_squared_error as MSE import pickle import xgboost as xgbdata = load_boston()X = data.data y = data.targetXtrain,Xtest,Ytrain,Ytest = TTS(X,y,test_size=0.3,random_state=420) #注意,如果我們保存的模型是xgboost庫中建立的模型,則導入的數據類型也必須是xgboost庫中的數據類型 dtest = xgb.DMatrix(Xtest,Ytest) #導入模型 loaded_model = pickle.load(open("xgboostonboston.dat", "rb")) print("Loaded model from: xgboostonboston.dat") #Loaded model from: xgboostonboston.dat #做預測,直接調用接口predict ypreds = loaded_model.predict(dtest) ypreds #array([ 9.244746, 22.536953, 28.47614 , 13.126131, 9.944413, 21.356094, # 15.187935, 15.559099, 15.629611, 15.555439, 21.427156, 35.502792, # 20.827318, 29.397932, 21.669186, 11.906522, 21.464252, 26.143337, # 。。。 # 8.979549, 28.35794 , 29.80491 , 21.987814, 19.893597, 19.730898, # 10.501988, 17.405378, 40.51527 , 17.420282, 24.272373, 19.771631, # 32.620422, 19.19032 , 12.364113, 38.63305 , 24.189354, 23.38174 , # 16.924698, 22.633028], dtype=float32) from sklearn.metrics import mean_squared_error as MSE, r2_score MSE(Ytest,ypreds) #9.107608696116197 r2_score(Ytest,ypreds) #0.90212543310739384.2.2 使用Joblib保存和調用模型
Joblib是SciPy生態系統中的一部分,它為Python提供保存和調用管道和對象的功能,處理NumPy結構的數據尤其高效,對于很大的數據集和巨大的模型非常有用。Joblib與pickle API非常相似,來看看代碼:
bst = xgb.train(param, dtrain, num_round) import joblib#同樣可以看看模型被保存到了哪里 joblib.dump(bst,"xgboost-boston.dat") #['xgboost-boston.dat'] loaded_model = joblib.load("xgboost-boston.dat") dtest = xgb.DMatrix(Xtest,Ytest) ypreds = loaded_model.predict(dtest) ypreds #array([ 9.244746, 22.536953, 28.47614 , 13.126131, 9.944413, 21.356094, # 15.187935, 15.559099, 15.629611, 15.555439, 21.427156, 35.502792, # 20.827318, 29.397932, 21.669186, 11.906522, 21.464252, 26.143337, # 26.300356, 23.474188, 18.186035, 15.851086, 22.928507, 22.919674, # 。。。 # 8.979549, 28.35794 , 29.80491 , 21.987814, 19.893597, 19.730898, # 10.501988, 17.405378, 40.51527 , 17.420282, 24.272373, 19.771631, # 32.620422, 19.19032 , 12.364113, 38.63305 , 24.189354, 23.38174 , # 16.924698, 22.633028], dtype=float32) MSE(Ytest, ypreds) #9.107608696116197 r2_score(Ytest,ypreds) #0.9021254331073938在這兩種保存方法下,我們都可以找到保存下來的dat文件,將這些文件移動到任意計算機上的python下的環境變量路徑中(使用sys.path進行查看),則可以使用import來對模型進行調用。注意,模型的保存調用與自寫函數的保存調用是兩回事,大家要注意區分。
4.3 分類案例:XGB中的樣本不均衡問題
在之前的學習中,我們一直以回歸作為演示的例子,這是由于回歸是XGB的常用領域的緣故。然而作為機器學習中的大頭,分類算法也是不可忽視的,XGB作為分類的例子自然也是非常多。存在分類,就會存在樣本不平衡問題帶來的影響,XGB中存在著調節樣本不平衡的參數scale_pos_weight,這個參數非常類似于之前隨機森林和支持向量機中我們都使用到過的class_weight參數,通常我們在參數中輸入的是負樣本量與正樣本量之比sum?(negative?instances?)sum(positive?instances)?\frac{\text { sum }(\text { negative instances })}{\text { sum(positive instances) }}?sum(positive?instances)??sum?(?negative?instances?)?
來看看如何使用這個參數吧。
可以看出,在xgboost庫和sklearnAPI中,參數scale_pos_weight都非常有效。本質上來說,scale_pos_weight參數是通過調節預測的概率值來調節,大家可以通過查看bst.predict(Xtest)返回的結果來觀察概率受到了怎樣的影響。因此,當我們只關心預測出的結果是否準確,AUC面積或者召回率是否足夠好,我們就可以使用scale_pos_weight參數來幫助我們。然而xgboost除了可以做分類和回歸,還有其他的多種功能,在一些需要使用精確概率的領域(比如排序ranking),我們希望能夠保持概率原有的模樣,而提升模型的效果。這種時候,我們就無法使用scale_pos_weight來幫助我們。來看看xgboost官網是怎么說明這個問題的:
官網上說,如果我們只在意模型的整表現,則使用AUC作為模型評估指標,使用scale_pos_weight來處理樣本不平衡問題,如果我們在意預測出正確的概率,那我們就無法通過調節scale_pos_weight來減輕樣本不平衡問題帶來的影響。
這種時候,我們需要考慮另一個參數:max_delta_step。
這個參數非常難以理解,它被稱之為是“樹的權重估計中允許的單次最大增量”,既可以考慮成是影響的估計的參數。xgboost官網上認為,如果我們在處理樣本不均衡問題,并且十分在意得到正確的預測概率,則可以設置max_delta_step參數為一個有限的數(比如1)來幫助收斂。max_delta_step參數通常不進行使用,二分類下的樣本不均衡問題時這個參數唯一的用途。
4.4 XGBoost類中的其他參數和功能
到目前為止,我們已經講解了XGBoost類中的大部分參數和功能。這些參數和功能主要覆蓋了XGBoost中的梯度提升樹的原理以及XGBoost自身所帶的一些特性。還有一些其他的參數和用法,是算法實際應用時需要考慮的問題。接下來,我們就來看看這些參數。
-
更多計算資源:n_jobs
nthread和n_jobs都是算法運行所使用的線程,與sklearn中規則一樣,輸入整數表示使用的線程,輸入-1表示使用計算機全部的計算資源。如果我們的數據量很大,則我們可能需要這個參數來為我們調用更多線程。 -
降低學習難度:base_score
base_score是一個比較容易被混淆的參數,它被叫做全局偏差,在分類問題中,它是我們希望關注的分類的先驗概率。比如說,如果我們有1000個樣本,其中300個正樣本,700個負樣本,則base_score就是0.3。對于回歸來說,這個分數默認0.5,但其實這個分數在這種情況下并不有效。許多使用XGBoost的人已經提出,當使用回歸的時候base_score的默認應該是標簽的均值,不過現在xgboost庫尚未對此做出改進。使用這個參數,我們便是在告訴模型一些我們了解但模型不一定能夠從數據中學習到的信息。通常我們不會使用這個參數,但對于嚴重的樣本不均衡問題,設置一個正確的base_score取值是很有必要的。 -
生成樹的隨機模式:random_state
在xgb庫和sklearn中,都存在空值生成樹的隨機模式的參數random_state。在之前的剪枝中,我們提到可以通過隨機抽樣樣本,隨機抽樣特征來減輕過擬合的影響,我們可以通過其他參數來影響隨機抽樣的比例,卻無法對隨機抽樣干涉更多,因此,真正的隨機性還是由模型自己生成的。如果希望控制這種隨機性,可以在random_state參數中輸入固定整數。需要注意的是,xgb庫和sklearn庫中,在random_state參數中輸入同一個整數未必表示同一個隨機模式,不一定會得到相同的結果,因此導致模型的feature_importances也會不一致。 -
自動處理缺失值:missing
XGBoost被設計成是能夠自動處理缺失值的模型,這個設計的初衷其實是為了讓XGBoost能夠處理稀疏矩陣。我們可以在參數missing中輸入一個對象,比如np.nan,或數據的任意取值,表示將所有含有這個對象的數據作為空值處理。XGBoost會將所有的空值當作稀疏矩陣中的0來進行處理,因此在使用XGBoost的時候,我們也可以不處理缺失值。當然,通常來說,如果我們了解業務并且了解缺失值的來源,我們還是希望手動填補缺失值。
XGBoost結語
作為工程能力強,效果優秀的算法,XGBoost應用廣泛并且原理復雜。不過在我們為大家解讀完畢XGBoost之后,相信大家已經意識到,XGBoost的難點在于它是一個集大成的模型。它所涉及到的知識點和模型流程,多半在其他常用的機器學習模型中出現過:比如樹的迭代過程,其實可以和邏輯回歸中的梯度下降過程進行類比;比如剪枝的過程,許多都可以與隨機森林和決策樹中的知識進行對比。當然,XGBoost還有很多可以進行探索,能夠使用XGB算法的庫和模塊也不止sklearn和xgboost,許多庫對xgboost的底層原理進行了更多優化,讓它變得更快更優秀(比如lightGBM,比如使用分布式進行計算等等)。本周我們學習了許多內容,大家已經對XGBoost算法有了基本的認識,了解了如何使用它來建立模型,如何使用它進行基本的調整。本周的課程只不過是梯度提升算法和XGB上的一個向導,希望大家繼續探索XGBoost這個可愛的算法,再接再厲。
總結
以上是生活随笔為你收集整理的菜菜sklearn——XGBoost(3)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 菜菜sklearn——XGBoost(2
- 下一篇: XGBoost的基本使用应用Kaggle