機器學習筆記之機器學習算法XGBoost


0x00 概述

在上一篇Boosting方法的介紹中,對XGBoost有過簡單的介紹。為了更還的掌握XGBoost這個工具。我們再來對它進行更加深入細致的學習。

 

0x01 什么是XGBoost

  • 全稱:eXtreme Gradient Boosting
  • 作者:陳天奇(華盛頓大學博士)
  • 基礎:GBDT
  • 所屬:boosting迭代型、樹類算法。
  • 適用范圍:分類、回歸
  • 優點:速度快、效果好、能處理大規模數據、支持多種語言、支持自定義損失函數等等。
  • 缺點:算法參數過多,調參負責,對原理不清楚的很難使用好XGBoost。不適合處理超高維特征數據。
  • 項目地址:https://github.com/dmlc/xgboost

 

0x02 XGBoost的原理

XGBoost 所應用的算法就是 gradient boosting decision tree,既可以用於分類也可以用於回歸問題中。那什么是 Gradient Boosting?Gradient boosting 是 boosting 的其中一種方法。所謂 Boosting ,就是將弱分離器 f_i(x) 組合起來形成強分類器 F(x) 的一種方法。所以 Boosting 有三個要素:

  • A loss function to be optimized:例如分類問題中用 cross entropy,回歸問題用 mean squared error。
  • A weak learner to make predictions:例如決策樹
  • An additive model:將多個弱學習器累加起來組成強學習器,進而使目標損失函數達到極小。

Gradient boosting 就是通過加入新的弱學習器,來努力糾正前面所有弱學習器的殘差,最終這樣多個學習器相加在一起用來進行最終預測,准確率就會比單獨的一個要高。之所以稱為 Gradient,是因為在添加新模型時使用了梯度下降算法來最小化的損失。一般來說,gradient boosting 的實現是比較慢的,因為每次都要先構造出一個樹並添加到整個模型序列中。而 XGBoost 的特點就是計算速度快,模型表現好,這兩點也正是這個項目的目標。

 

0x03 XGBoost的優勢

XGBoost算法可以給預測模型帶來能力的提升。當我對它的表現有更多了解的時候,當我對它的高准確率背后的原理有更多了解的時候,你會發現它具有很多優勢:

  • 正則化。XGBoost在代價函數里加入了正則項,用於控制模型的復雜度。正則項里包含了樹的葉子節點個數、每個葉子節點上輸出的score的L2模的平方和。從Bias-variance tradeoff角度來講,正則項降低了模型的variance,使學習出來的模型更加簡單,防止過擬合,這也是xgboost優於傳統GBDT的一個特性。
  • XGBoost工具支持並行。Boosting不是一種串行的結構嗎?怎么並行的?注意XGBoost的並行不是tree粒度的並行,XGBoost也是一次迭代完才能進行下一次迭代的(第t次迭代的代價函數里包含了前面t-1次迭代的預測值)。XGBoost的並行是在特征粒度上的。我們知道,決策樹的學習最耗時的一個步驟就是對特征的值進行排序(因為要確定最佳分割點),XGBoost在訓練之前,預先對數據進行了排序,然后保存為block結構,后面的迭代中重復地使用這個結構,大大減小計算量。這個block結構也使得並行成為了可能,在進行節點的分裂時,需要計算每個特征的增益,最終選增益最大的那個特征去做分裂,那么各個特征的增益計算就可以開多線程進行。
  • 高度的靈活性。XGBoost支持用戶自定義目標函數和評估函數,只要目標函數二階可導就行。
  • 缺失值處理。XGBoost內置處理缺失值的規則。用戶需要提供一個和其它樣本不同的值,然后把它作為一個參數傳進去,以此來作為缺失值的取值。XGBoost在不同節點遇到缺失值時采用不同的處理方法,並且會學習未來遇到缺失值時的處理方法。
  • 剪枝。當分裂時遇到一個負損失時,GBM會停止分裂。因此GBM實際上是一個貪心算法。 XGBoost會一直分裂到指定的最大深度(max_depth),然后回過頭來剪枝。如果某個節點之后不再有正值,它會去除這個分裂。 這種做法的優點,當一個負損失(如-2)后面有個正損失(如+10)的時候,就顯現出來了。GBM會在-2處停下來,因為它遇到了一個負值。但是XGBoost會繼續分裂,然后發現這兩個分裂綜合起來會得到+8,因此會保留這兩個分裂。比起GBM,這樣不容易陷入局部最優解。
  • 內置交叉驗證。XGBoost允許在每一輪boosting迭代中使用交叉驗證。因此,可以方便地獲得最優boosting迭代次數。而GBM使用網格搜索,只能檢測有限個值。
  • 在已有的模型基礎上繼續。XGBoost可以在上一輪的結果上繼續訓練。

 

0x04 基礎知識——GBDT

XGBoost是在GBDT的基礎上對boosting算法進行的改進,內部決策樹使用的是回歸樹,簡單回顧GBDT如下:

回歸樹的分裂結點:

對於平方損失函數,擬合的就是殘差;

對於一般損失函數(梯度下降),擬合的就是殘差的近似值,分裂結點划分時枚舉所有特征的值,選取划分點。

最后預測的結果是每棵樹的預測結果相加。

 

0x05 XGBoost算法原理知識

5.1 定義樹的復雜度

1、把樹拆分成樹結構部分q和葉子權重部分w

 

 2、樹的復雜度函數和樣例

 定義樹的結構和復雜度的原因很簡單,這樣就可以衡量模型的復雜度了啊,從而可以有效控制過擬合。

 

5.2 XGBoost中的boosting tree模型

 和傳統的boosting tree模型一樣,XGBoost的提升模型也是采用的殘差(或梯度負方向),不同的是分裂結點選取的時候不一定是最小平方損失。

 

5.3 對目標函數的改寫——二階泰勒展開(關鍵)

最終的目標函數只依賴於每個數據點的在誤差函數上的一階導數和二階導數。這么寫的原因很明顯,由於之前的目標函數求最優解的過程中只對平方損失函數時候方便求,對於其他的損失函數變得很復雜,通過二階泰勒展開式的變換,這樣求解其他損失函數變得可行了。當定義了分裂候選集合的時候

可以進一步改目標函數。分裂結點的候選集是很關鍵的一步,這是xgboost速度快的保證,怎么選出來這個集合,后面會介紹。

 

5.4 樹結構的打分函數

Obj代表了當指定一個樹的結構的時候,在目標上面最多減少多少?

 對於每一次嘗試去對已有的葉子加入一個分割

這樣就可以在建樹的過程中動態的選擇是否要添加一個結點。

 

 假設要枚舉所有x < a 這樣的條件,對於某個特定的分割a,要計算a左邊和右邊的導數和。對於所有的a,我們只要做一遍從左到右的掃描就可以枚舉出所有分割的梯度和GL、GR。然后用上面的公式計算每個分割方案的分數就可以了。

 

5.5 尋找分裂結點的候選集

1、暴力枚舉

2、近似方法 ,近似方法通過特征的分布,按照百分比確定一組候選分裂點,通過遍歷所有的候選分裂點來找到最佳分裂點。兩種策略:全局策略和局部策略。

  • 在全局策略中,對每一個特征確定一個全局的候選分裂點集合,就不再改變;
  • 在局部策略中,每一次分裂都要重選一次分裂點。

前者需要較大的分裂集合,后者可以小一點。對比補充候選集策略與分裂點數目對模型的影響。全局策略需要更細的分裂點才能和局部策略差不多

3、Weighted Quantile Sketch

陳天奇提出並從概率角度證明了一種帶權重的分布式的Quantile Sketch。

參考鏈接:

 

0x06 如何使用XGBoost

6.1 GBoost的安裝

網上很多教程教的是如何進行編譯安裝,過程比較繁瑣,比較簡單的方式是下載已經編譯好的.whl文件進行安裝。

 

6.2 XGBoost的參數

XGBoost的作者把所有的參數分成了三類:

  • 通用參數:宏觀函數控制。
  • Booster參數:控制每一步的booster(tree/regression)。
  • 學習目標參數:控制訓練目標的表現。

在這里我們會類比GBM來講解,所以作為一種基礎知識。

 

6.3 通用參數

這些參數用來控制XGBoost的宏觀功能。

  • booster[默認gbtree] 選擇每次迭代的模型,有兩種選擇:
    • gbtree:基於樹的模型
    • gbliner:線性模型。使用帶l1,l2 正則化的線性回歸模型作為基學習器。因為boost 算法是一個線性疊加的過程,而線性回歸模型也是一個線性疊加的過程。因此疊加的最終結果就是一個整體的線性模型,xgboost 最后會獲得這個線性模型的系數。
    • dart: 表示采用dart booster
  • silent[默認0] 當這個參數值為1時,靜默模式開啟,不會輸出任何信息。一般這個參數就保持默認的0,因為這樣能幫我們更好地理解模型。
  • nthread[默認值為最大可能的線程數] 這個參數用來進行多線程控制,應當輸入系統的核數。如果你希望使用CPU全部的核,那就不要輸入這個參數,算法會自動檢測它。
  • num_pbuffer 指定了prediction buffer(該buffer 用於保存上一輪boostring step 的預測結果) 的大小。通常設定為訓練樣本的數量。該參數由xgboost 自動設定,無需用戶指定。
  • num_feature 樣本的特征數量。通常設定為特征的最大維數。該參數由xgboost 自動設定,無需用戶指定。

 

0x07 Tree Booster 參數

因為tree booster的表現遠遠勝過linear booster,所以linear booster很少用到。針對tree booster 的參數(適用於booster=gbtree,dart) :

  • eta[默認3] 和GBM中的 learning rate 參數類似。通過減少每一步的權重,可以提高模型的魯棒性。范圍為 [0,1],典型值為0.01-0.2。
  • min_child_weight[默認1] 子節點的權重閾值。它刻畫的是:對於一個葉子節點,當對它采取划分之后,它的所有子節點的權重之和的閾值。所謂的權重,對於線性模型(booster=gblinear),權重就是:葉子節點包含的樣本數量,對於樹模型(booster=gbtree,dart),權重就是:葉子節點包含樣本的所有二階偏導數之和。這個參數用於避免過擬合。當它的值較大時,可以避免模型學習到局部的特殊樣本。但是如果這個值過高,會導致欠擬合。這個參數需要使用CV來調整。該值越大,則算法越保守(盡可能的少划分)。
    • 如果它的所有子節點的權重之和大於該閾值,則該葉子節點值得繼續划分
    • 如果它的所有子節點的權重之和小於該閾值,則該葉子節點不值得繼續划分
  • max_delta_step[默認為0] 每棵樹的權重估計時的最大delta step。取值范圍為[0,],0 表示沒有限制。
  • max_depth[默認6] GBM中的參數相同,這個值為樹的最大深度。這個值也是用來避免過擬合的。max_depth越大,模型會學到更具體更局部的樣本。需要使用CV函數來進行調優。典型值:3-10
  • max_leaf_nodes 樹上最大的節點或葉子的數量。可以替代max_depth的作用。因為如果生成的是二叉樹,一個深度為n的樹最多生成n^2個葉子。如果定義了這個參數,GBM會忽略max_depth參數。
  • gamma[默認0] 也稱作最小划分損失min_split_loss。 它刻畫的是:對於一個葉子節點,當對它采取划分之后,損失函數的降低值的閾值。該值越大,則算法越保守(盡可能的少划分)。默認值為 0
    • 如果大於該閾值,則該葉子節點值得繼續划分
    • 如果小於該閾值,則該葉子節點不值得繼續划分
  • max_delta_step[默認0] 這參數限制每棵樹權重改變的最大步長。如果這個參數的值為0,那就意味着沒有約束。如果它被賦予了某個正值,那么它會讓這個算法更加保守。通常,這個參數不需要設置。但是當各類別的樣本十分不平衡時,它對邏輯回歸是很有幫助的。這個參數一般用不到,但是你可以挖掘出來它更多的用處。
  • subsample[默認1] 對訓練樣本的采樣比例。和GBM中的subsample參數一模一樣。這個參數控制對於每棵樹,隨機采樣的比例。減小這個參數的值,算法會更加保守,避免過擬合。但是,如果這個值設置得過小,它可能會導致欠擬合。 典型值:5-1
  • colsample_bytree[默認1] 構建子樹時,對特征的采樣比例。和GBM里面的max_features參數類似。用來控制每棵隨機采樣的列數的占比(每一列是一個特征)。典型值:5-1
  • colsample_bylevel[默認1] 尋找划分點時,對特征的采樣比例。用來控制樹的每一級的每一次分裂,對列數的采樣的占比。一般不太用這個參數,因為subsample參數和colsample_bytree參數可以起到相同的作用。如果為5, 表示隨機使用一半的特征來尋找最佳划分點。它有助於緩解過擬合。
  • lambda[默認1] 權重的L2正則化項。(和Ridge regression類似)。這個參數是用來控制XGBoost的正則化部分的。該值越大則模型越簡單。
  • alpha[默認0] 權重的L1正則化項。(和Lasso regression類似)。 可以應用在很高維度的情況下,使得算法的速度更快。該值越大則模型越簡單。
  • scale_pos_weight[默認1] 用於調整正負樣本的權重,常用於類別不平衡的分類問題。一個典型的參數值為:負樣本數量/正樣本數量。
  • tree_method[默認為’auto’] 指定了構建樹的算法,可以為下列的值(分布式,以及外存版本的算法只支持 ‘approx’,’hist’,’gpu_hist’ 等近似算法):
    • ‘auto’: 使用啟發式算法來選擇一個更快的tree_method:
      • 對於小的和中等的訓練集,使用exact greedy 算法分裂節點
      • 對於非常大的訓練集,使用近似算法分裂節點
      • 舊版本在單機上總是使用exact greedy 分裂節點
    • ‘exact’: 使用exact greedy 算法分裂節點
    • ‘approx’: 使用近似算法分裂節點
    • ‘hist’: 使用histogram 優化的近似算法分裂節點(比如使用了bin cacheing 優化)
    • ‘gpu_exact’: 基於GPU 的exact greedy 算法分裂節點
    • ‘gpu_hist’: 基於GPU 的histogram 算法分裂節點
  • sketch_eps[默認值為03] 指定了分桶的步長。其取值范圍為 (0,1)。它僅僅用於 tree_medhod=’approx’。它會產生大約1/ sketch_eps個分桶。它並不會顯示的分桶,而是會每隔 sketch_pes 個單位(一個單位表示最大值減去最小值的區間)統計一次。用戶通常不需要調整該參數。
  • updater[默認為 ‘grow_colmaker,prune’] 它是一個逗號分隔的字符串,指定了一組需要運行的tree updaters,用於構建和修正決策樹。該參數通常是自動設定的,無需用戶指定。但是用戶也可以顯式的指定。
  • refresh_leaf[默認為1] 它是一個updater plugin。 如果為 true,則樹節點的統計數據和樹的葉節點數據都被更新;否則只有樹節點的統計數據被更新。
  • process_type 指定要執行的處理過程(如:創建子樹、更新子樹)。該參數通常是自動設定的,無需用戶指定。
  • grow_policy[默認為’depthwise’] 用於指定子樹的生長策略。僅僅支持tree_method=’hist’。 有兩種策略:
    • ‘depthwise’:優先拆分那些靠近根部的子節點。
    • ‘lossguide’:優先拆分導致損失函數降低最快的子節點
  • max_leaves[默認為0] 最多的葉子節點。如果為0,則沒有限制。該參數僅僅和grow_policy=’lossguide’ 關系較大。
  • max_bin[默認值為 256] 指定了最大的分桶數量。該參數僅僅當 tree_method=’hist’,’gpu_hist’ 時有效。
  • Predictor[默認為’cpu_predictor’] 指定預測器的算法,可以為:
    • ‘cpu_predictor’: 使用CPU 來預測
    • ‘gpu_predictor’: 使用GPU 來預測。對於tree_method=’gpu_exact,gpu_hist’, ‘gpu_redictor’ 是默認值。

 

7.1 Dart Booster 參數

XGBoost基本上都是組合大量小學習率的回歸樹。在這種情況,越晚添加的樹比越早添加的樹更重要。Rasmi根據深度神經網絡社區提出一個新的使用dropout的boosted trees,並且證明它在某些情況有更好的結果。以下是新的tree boosterdart的介紹。

DART booster 原理:為了緩解過擬合,采用dropout 技術,隨機丟棄一些樹。

由於引入了隨機性,因此dart 和gbtree 有以下的不同:

  • 因為隨機dropout不使用用於保存預測結果的buffer所以訓練會更慢
  • 因為隨機早停可能不夠穩定

DART算法和MART(GBDT)算法主要有兩個不同點:

dropout

計算下一棵樹要擬合的梯度的時候,僅僅隨機從已經生成的樹中選取一部分。假設經過n次迭代之后當前模型為M,M=ni=1Ti,當中Ti是第i次學習到的樹。DART算法首先選擇一個隨機子集I{1,,n},創建模型M^=iITi。樹T從{(x,Lx(M^(x)))}學習得到,當中Lx()表示求損失函數的梯度作為下一次的標簽,GDBT中使用損失函數的梯度作為下一個樹的輸入標簽。

歸一化

DART和MART第二點不同就是DART添加一棵樹時需要先歸一化。歸一化背后的原理是:樹T是嘗試減少M^和最優預測器之間的差距,dropped trees也是為了減少這個差距。因此引入new tree和dropped trees都是為了達到相同的目標。進一步說,假設通過I建立模型M^時drop掉k棵樹。所以新的樹T大概是dropped trees中每一個獨立的樹的k倍。因此,DART算法將樹T乘以1/k,這使T的大小和每一個單獨的dropped trees相同。然后,新的樹和dropped trees都乘以k/(1+k),再將新的樹加入集成模型中。乘以k/(k+1)是為了保證新的樹的引入和不引入的效果一樣。

XGBoost官方文檔:

 

原始鏈接:Rashmi Korlakai Vinayak, Ran Gilad-Bachrach. “DART: Dropouts meet Multiple Additive Regression Trees.

DART booster繼承了gbtree,所以dart也有eta.gamma,max_depth等參數,額外增加的參數如下:

  • sample_type[默認值’uniform’] 它指定了丟棄時的策略:
    • ‘uniform’: 隨機丟棄子樹
    • ‘weighted’: 根據權重的比例來丟棄子樹
  • normaliz_type它指定了歸一化策略:
    • ‘tree’: 新的tree和每一個dropped trees有同樣的權重,新的子樹將被縮放為:1K+v;被丟棄的子樹被縮放為vK+v。其中v為學習率,K為被丟棄的子樹的數量
    • ‘forest’:新的樹和所有dropped trees的和有相同的權重,新的子樹將被縮放為:11+v;被丟棄的子樹被縮放為v1+v。其中v為學習率
  • rate_drop[默認為0] dropout rate,指定了當前要丟棄的子樹占當前所有子樹的比例。范圍為[0,1]。
  • one_drop[默認為0] 如果該參數為true,則在dropout 期間,至少有一個子樹總是被丟棄。
  • skip_drop[默認為0] 它指定了不執行 dropout 的概率,其范圍是[0,1]。
    • 如果一次dropout被略過,新的樹被添加進model用和gbtree一樣的方式。
    • 非0的skip_drop比rate_drop和one_drop有更高的優先級。

 

7.2 Linear Booster 參數

  • Lambda[默認為0] L2 正則化系數(基於weights 的正則化),該值越大則模型越簡單
  • Alpha[默認為0] L1 正則化系數(基於weights的正則化),該值越大則模型越簡單
  • lambda_bias[默認為0] L2 正則化系數(基於bias 的正則化), 沒有基於bias 的 L1 正則化,因為它不重要。

 

7.3 Tweedie Regression 參數

  • weedie_variance_power[默認為5] 指定了tweedie 分布的方差。取值范圍為 (1,2),越接近1,則越接近泊松分布;越接近2,則越接近 gamma 分布。

 

7.4 學習目標參數

這個參數用來控制理想的優化目標和每一步結果的度量方法。

  • objective[默認reg:linear] 指定任務類型
    • ‘reg:linear’: 線性回歸模型。它的模型輸出是連續值
    • ‘reg:logistic’: 邏輯回歸模型。它的模型輸出是連續值,位於區間[0,1] 。
    • ‘binary:logistic’:二分類的邏輯回歸模型,它的模型輸出是連續值,位於區間[0,1] ,表示取正負類別的概率。它和’reg:logistic’ 幾乎完全相同,除了有一點不同:
      • ‘reg:logistic’ 的默認evaluation metric 是 rmse 。
      • ‘binary:logistic’ 的默認evaluation metric 是 error
    • ‘binary:logitraw’: 二分類的邏輯回歸模型,輸出為分數值(在logistic 轉換之前的值)
    • ‘count:poisson’: 對 count data 的 poisson regression, 輸出為泊松分布的均值。
    • ‘multi:softmax’: 基於softmax 的多分類模型。此時你需要設定num_class 參數來指定類別數量。返回預測的類別(不是概率)
    • ‘multi:softprob’: 基於softmax 的多分類模型,但是它的輸出是一個矩陣:ndata*nclass,給出了每個樣本屬於每個類別的概率。
    • ‘rank:pairwise’: 排序模型(優化目標為最小化pairwise loss)
    • ‘reg:gamma’: gamma regression,輸出為伽馬分布的均值。
    • ‘reg:tweedie’:’tweedie regression’。
  • base_score[默認為5] 所有樣本的初始預測分,它用於設定一個初始的、全局的bias。當迭代的數量足夠大時,該參數沒有什么影響。
  • eval_metric[默認值取決於objective參數的取值] 對於有效數據的度量方法。對於回歸問題,默認值是rmse,對於分類問題,默認值是error。排序問題的默認值是 mean average precision。典型值有:
    • rmse 均方根誤差
    • mae 平均絕對誤差
    • logloss 負對數似然函數值
    • error 二分類錯誤率(閾值為5),它計算的是:預測錯誤的樣本數/所有樣本數
    • error@t 二分類的錯誤率。但是它的閾值不再是5, 而是由字符串t 給出(它是一個數值轉換的字符串)
    • merror 多分類錯誤率,它計算的是:預測錯誤的樣本數/所有樣本數
    • mlogloss多類分類的負對數似然函數
    • auc 損失函數 auc 曲線下面積
    • ndcg Normalized Discounted Cumulative Gain 得分
    • map Mean average precision 得分
    • ndcg@n,map@n n 為一個整數,用於切分驗證集的top 樣本來求值。
    • ndcg-,map-,ndcg@n-,map@n- NDCG and MAP will evaluate the score of a list without any positive samples as 1. By adding “-” in the evaluation metric XGBoost will evaluate these score as 0 to be consistent under some conditions. training repeatedly
    • poisson-nloglik 對於泊松回歸,使用負的對數似然
    • gamma-nloglik 對於伽馬回歸,使用負的對數似然
    • gamma-deviance 對於伽馬回歸,使用殘差的方差
    • tweedie-nloglik: 對於tweedie 回歸,使用負的對數似然
  • Seed[默認0] 隨機數的種子,設置它可以復現隨機數據的結果,也可以用於調整參數。

如果你之前用的是Scikit-learn,你可能不太熟悉這些參數。但是有個好消息,python的XGBoost模塊有一個sklearn包。這個包中的參數是按sklearn風格命名的。會改變的函數名是:

  • eta ->learning_rate
  • lambda->reg_lambda
  • alpha->reg_alpha

你肯定在疑惑為啥咱們沒有介紹和GBM中的’n_estimators’類似的參數。XGBoost中確實有一個類似的參數,是在標准XGBoost實現中調用擬合函數時,把它作為’num_boosting_rounds’參數傳入。

參考鏈接:

 

7.5 外存計算

對於external-memory 和 in-memory 計算,二者幾乎沒有區別。除了在文件名上有所不同。

  • in-memory 的文件名為:filename
  • external-memory 的文件名為:filename#cacheprefix。
    • filename:是你想加載的數據集(當前只支持導入libsvm 格式的文件)的路徑名
    • cacheprefix:指定的cache 文件的路徑名。xgboost 將使用它來做external memory cache。如:dtrain = xgb.DMatrix(‘../data/my_data.txt.train#train_cache.cache’),此時你會發現在txt 所在的位置會由xgboost 創建一個my_cache.cache 文件。
  • 推薦將nthread 設置為真實CPU 的數量。現代的CPU都支持超線程,如4核8線程。此時nthread 設置為4而不是8。
  • 對於分布式計算,外存計算時文件名的設定方法也相同:data = “hdfs:///path-to-data/my_data.txt.train#train_cache.cache”

 

7.6 GPU計算

xgboost 支持使用gpu 計算,前提是安裝時開啟了GPU 支持。要想使用GPU 訓練,需要指定tree_method 參數為下列的值:

  • ‘gpu_exact’: 標准的xgboost 算法。它會對每個分裂點進行精確的搜索。相對於’gpu_hist’,它的訓練速度更慢,占用更多內存
  • ‘gpu_hist’:使用xgboost histogram 近似算法。它的訓練速度更快,占用更少內存

當tree_method 為’gpu_exact’,’gpu_hist’ 時,模型的predict 默認采用GPU 加速。

你可以通過設置predictor 參數來指定predict 時的計算設備:

  • ‘cpu_predictor’: 使用CPU 來執行模型預測
  • ‘gpu_predictor’: 使用GPU 來執行模型預測

多GPU 可以通過grow_gpu_hist 參數和 n_gpus 參數配合使用。如果n_gpus設置為 -1,則所有的GPU 都被使用。它默認為1。多GPU 不一定比單個GPU 更快,因為PCI總線的帶寬限制,數據傳輸速度可能成為瓶頸。可以通過gpu_id 參數來選擇設備,默認為 0 。如果非0,則GPU 的編號規則為  mod(gpu_id + i) % n_visible_devices for i in 0~n_gpus-1

GPU 計算支持的參數:

 

7.7 單調約束

在模型中可能會有一些單調的約束:當xx時:

如果想在xgboost 中添加單調約束,則可以設置monotone_constraints 參數。假設樣本有 2 個特征,則:

  • params[‘monotone_constraints’] = “(1,-1)” :表示第一個特征是單調遞增;第二個特征是單調遞減
  • params[‘monotone_constraints’] = “(1,0)” :表示第一個特征是單調遞增;第二個特征沒有約束
  • params[‘monotone_constraints’] = “(1,1)” :表示第一個特征是單調遞增;第二個特征是單調遞增

右側的 1 表示單調遞增約束;0 表示無約束; -1 表示單調遞減約束。 有多少個特征,就對應多少個數值。

 

0x08 XGBoost的數據接口

8.1 數據格式

xgboost 的數據存儲在DMatrix 對象中,xgboost 支持直接從下列格式的文件中加載數據:

  • libsvm 文本格式的文件。其格式為:
 [label] [index1]:[value1] [index2]:[value2] ...
 [label] [index1]:[value1] [index2]:[value2] ...
 ...
  • xgboost binary buffer 文件
dtrain = xgb.DMatrix('train.svm.txt') #libsvm 格式
dtest = xgb.DMatrix('test.svm.buffer') # xgboost binary buffer 文件
  • 二維的numpy array 中加載數據
data = np.random.rand(5, 10)  
label = np.random.randint(2, size=5) 
dtrain = xgb.DMatrix(data, label=label)#從 numpy array 中加載
  • sparse array 中加載數據
csr = scipy.sparse.csr_matrix((dat, (row, col)))
dtrain = xgb.DMatrix(csr)

 

 

8.2 DMatrix

DMatrix: 由xgboost 內部使用的數據結構,它存儲了數據集,並且針對了內存消耗和訓練速度進行了優化。

xgboost.DMatrix(data, label=None, missing=None, weight=None, silent=False, 
     feature_names=None, feature_types=None, nthread=None)

參數:

  • data:表示數據集。可以為:
    • 一個字符串,表示文件名。數據從該文件中加載
    • 一個二維的 numpy array, 表示數據集。
  • label:一個序列,表示樣本標記。
  • missing: 一個值,它是缺失值的默認值。
  • weight:一個序列,給出了數據集中每個樣本的權重。
  • silent: 一個布爾值。如果為True,則不輸出中間信息。
  • feature_names: 一個字符串序列,給出了每一個特征的名字
  • feature_types: 一個字符串序列,給出了每個特征的數據類型
  • nthread:線程數

屬性:

  • feature_names: 返回每個特征的名字
  • feature_types: 返回每個特征的數據類型

方法:

  • .get_base_margin(): 返回一個浮點數,表示DMatrix 的 base margin。
  • .set_base_margin(margin): 設置DMatrix 的 base margin。參數:margin,t一個序列,給出了每個樣本的prediction margin
  • .get_float_info(field): 返回一個numpy array, 表示DMatrix 的 float property 。
  • .set_float_info(field,data): 設置DMatrix 的 float property。
  • .set_float_info_npy2d(field,data): 設置DMatrix 的 float property。這里的data 是二維的numpy array參數:
    • field: 一個字符串,給出了information 的字段名。注:意義未知。
    • data: 一個numpy array,給出了數據集每一個點的float information
  • .get_uint_info(field): 返回DMatrix 的 unsigned integer property。
  • .set_unit_info(field,data): 設置DMatrix 的 unsigned integer property。返回值:一個numpy array,表示數據集的unsigned integer information
  • .get_label(): 返回一個numpy array,表示DMatrix 的 label 。
  • .set_label(label): 設置樣本標記。
  • .set_label_npy2d(label): 設置樣本標記。這里的label 為二維的numpy array。參數: label: 一個序列,表示樣本標記
  • .get_weight():一個numpy array,返回DMatrix 的樣本權重。
  • .set_weight(weight): 設置樣本權重。
  • .set_weight_npy2d(weight): 設置樣本權重。這里的weight 為二維的numpy array
  • .num_col(): 返回DMatrix 的列數,返回值:一個整數,表示特征的數量
  • .num_row(): 返回DMatrix 的行數,返回值:一個整數,表示樣本的數量
  • save_binary(fname,silent=True): 保存DMatrix 到一個 xgboost buffer 文件中。參數:
    • fname: 一個字符串,表示輸出的文件名
    • silent: 一個布爾值。如果為True,則不輸出中間信息。
  • .set_group(group): 設置DMatrix 每個組的大小(用於排序任務)參數:group: 一個序列,給出了每個組的大小
  • slice(rindex): 切分DMaxtrix ,返回一個新的DMatrix。 該新的DMatrix 僅僅包含rindex
    • 參數:rindex: 一個列表,給出了要保留的index
    • 返回值:一個新的DMatrix 對象

示例:

data/train.svm.txt 的內容:

1 1:1 2:2
1 1:2 2:3
1 1:3 2:4
1 1:4 2:5
0 1:5 2:6
0 1:6 2:7
0 1:7 2:8
0 1:8 2:9

測試代碼:

import xgboost as xgt
import numpy as np


class MatrixTest:
    '''
    測試 DMatrix
    '''

    def __init__(self):
        self._matrix1 = xgt.DMatrix('data/train.svm.txt')
        self._matrix2 = xgt.DMatrix(data=np.arange(0, 12).reshape((4, 3)),
                                    label=[1, 2, 3, 4], weight=[0.5, 0.4, 0.3, 0.2],
                                    silent=False, feature_names=['a', 'b', 'c'],
                                    feature_types=['int', 'int', 'float'], nthread=2)

    def print(self, matrix):
        print('feature_names:%s' % matrix.feature_names)
        print('feature_types:%s' % matrix.feature_types)

    def run_get(self, matrix):
        print('get_base_margin():', matrix.get_base_margin())
        print('get_label():', matrix.get_label())
        print('get_weight():', matrix.get_weight())
        print('num_col():', matrix.num_col())
        print('num_row():', matrix.num_row())

    def test(self):
        print('查看 matrix1 :')
        self.print(self._matrix1)
        # feature_names:['f0', 'f1', 'f2']
        # feature_types:None

        print('\n查看 matrix2 :')
        self.print(self._matrix2)
        # feature_names:['a', 'b', 'c']
        # feature_types:['int', 'int', 'float']

        print('\n查看 matrix1 get:')
        self.run_get(self._matrix1)
        # get_base_margin(): []
        # get_label(): [1. 1. 1. 1. 0. 0. 0. 0.]
        # get_weight(): []
        # num_col(): 3
        # num_row(): 8

        print('\n查看 matrix2 get:')
        self.run_get(self._matrix2)
        # get_base_margin(): []
        # get_label(): [1. 2. 3. 4.]
        # get_weight(): [0.5 0.4 0.3 0.2]
        # num_col(): 3
        # num_row(): 4

        print(self._matrix2.slice([0, 1]).get_label())
        # [1. 2.]

 

0x09 XGBoost模型接口

9.1 Booster

Booster 是xgboost 的模型,它包含了訓練、預測、評估等任務的底層實現。

xbgoost.Booster(params=None,cache=(),model_file=None)

參數:

  • params: 一個字典,給出了模型的參數。該Booster 將調用set_param(params) 方法來設置模型的參數。
  • cache:一個列表,給出了緩存的項。其元素是DMatrix 的對象。模型從這些DMatrix 對象中讀取特征名字和特征類型(要求這些DMatrix 對象具有相同的特征名字和特征類型)
  • model_file: 一個字符串,給出了模型文件的位置。如果給出了model_file,則調用load_model(model_file) 來加載模型。

屬性:通過方法來存取、設置屬性。

方法:

  • .attr(key): 獲取booster 的屬性。如果該屬性不存在,則返回None參數:key: 一個字符串,表示要獲取的屬性的名字
  • .set_attr(**kwargs): 設置booster 的屬性。參數:kwargs: 關鍵字參數。注意:參數的值目前只支持字符串。如果參數的值為None,則表示刪除該參數。
  • .attributes(): 以字典的形式返回booster 的屬性
  • .set_param(params,value=None): 設置booster 的參數。參數:
    • params:一個列表(元素為鍵值對)、一個字典、或者一個字符串。表示待設置的參數;
    • value:如果params 為字符串,那么params 就是鍵,而value就是參數值。
  • .boost(dtrain,grad,hess): 執行一次訓練迭代。參數:
    • dtrain:一個 DMatrix 對象,表示訓練集
    • grad:一個列表,表示一階的梯度
    • hess:一個列表,表示二階的偏導數
  • .update(dtrain,iteration,fobj=None):對一次迭代進行更新。參數:
    • dtrain:一個 DMatrix 對象,表示訓練集
    • iteration:一個整數,表示當前的迭代步數編號
    • fobj: 一個函數,表示自定義的目標函數。由於Booster 沒有.train() 方法,因此需要用下面的策略進行迭代:
for i in range(0,100):
    booster.update(train_matrix,iteration=i)
  • .copy(): 拷貝當前的booster,並返回一個新的Booster 對象
  • .dump_model(fout,fmap=”,with_stats=False): dump 模型到一個文本文件中。參數:
    • fout: 一個字符串,表示輸出文件的文件名;
    • fmap: 一個字符串,表示存儲feature map 的文件的文件名。booster 需要從它里面讀取特征的信息,該文件每一行依次代表一個特征,每一行的格式為:feature name:feature type,其中feature type 為int、float 等表示數據類型的字符串;
    • with_stats:一個布爾值。如果為True,則輸出split 的統計信息。
  • .get_dump(fmap=”,with_stats=False,dump_format=’text’): dump 模型為字符的列表(而不是存到文件中)。參數:dump_format: 一個字符串,給出了輸出的格式。回值:一個字符串的列表。每個字符串描述了一棵子樹。
  • .eval(data,name=’eval’,iteration=0): 對模型進行評估。返回值:一個字符串,表示評估結果參數:
    • data: 一個DMatrix 對象,表示數據集
    • name: 一個字符串,表示數據集的名字
    • iteration: 一個整數,表示當前的迭代編號
  • .eval_set(evals,iteration=0,feval=None): 評估一系列的數據集。返回值:一個字符串,表示評估結果。參數:
    • evals: 一個列表,列表元素為元組(DMatrix,string), 它給出了待評估的數據集
    • iteration: 一個整數,表示當前的迭代編號
    • feval: 一個函數,給出了自定義的評估函數
  • .get_fscore(fmap=”): 返回每個特征的重要性。booster 需要從它里面讀取特征的信息。返回值:一個字典,給出了每個特征的重要性
  • .get_score(fmap=”,importance_type=’weight’): 返回每個特征的重要性。返回值:一個字典,給出了每個特征的重要性。參數:importance_type:一個字符串,給出了特征的衡量指標。可以為:
    • ‘weight’: 此時特征重要性衡量標准為:該特征在所有的樹中,被用於划分數據集的總次數。
    • ‘gain’: 此時特征重要性衡量標准為:該特征在樹的’cover’ 中,獲取的平均增益。
  • .get_split_value_histogram(feature,fmap=”,bins=None,as_pandas=True):獲取一個特征的划分value histogram。返回值:以一個numpy ndarray 或者DataFrame 形式返回的、代表拆分點的histogram 的結果。參數:
    • feature: 一個字符串,給出了划分特征的名字
    • fmap:一個字符串,給出了feature map 文件的文件名。booster 需要從它里面讀取特征的信息。
    • bins: 最大的分桶的數量。如果bins=None 或者 bins>n_unique,則分桶的數量實際上等於n_unique。 其中 n_unique 是划分點的值的unique
    • as_pandas :一個布爾值。如果為True,則返回一個DataFrame; 否則返回一個numpy ndarray。
  • .load_model(fname): 從文件中加載模型。參數:fname: 一個文件或者一個內存buffer, xgboost 從它加載模型
  • .save_model(fname): 保存模型到文件中。參數:fname: 一個字符串,表示文件名
  • save_raw(): 將模型保存成內存buffer。返回值:一個內存buffer,代表該模型
  • .load_rabit_checkpoint(): 從rabit checkpoint 中初始化模型。返回值:一個整數,表示模型的版本號
  • .predict(data,output_margin=False,ntree_limit=0,pred_leaf=False,pred_contribs=False,approx_contribs=False): 執行預測。該方法不是線程安全的。對於每個booster來講,你只能在某個線程中調用它的.predict 方法。如果你在多個線程中調用.predict 方法,則可能會有問題。要想解決該問題,你必須在每個線程中調用copy() 來拷貝該booster 到每個線程中。返回值:一個ndarray,表示預測結果。參數:
    • data: 一個 DMatrix 對象,表示測試集
    • output_margin: 一個布爾值。表示是否輸出原始的、未經過轉換的margin value
    • ntree_limit: 一個整數。表示使用多少棵子樹來預測。默認值為0,表示使用所有的子樹。如果訓練的時候發生了早停,則你可以使用best_ntree_limit。
    • pred_leaf: 一個布爾值。如果為True,則會輸出每個樣本在每個子樹的哪個葉子上。它是一個nsample x ntrees 的矩陣。每個子樹的葉節點都是從1 開始編號的。
    • pred_contribs: 一個布爾值。如果為True, 則輸出每個特征對每個樣本預測結果的貢獻程度。它是一個nsample x ( nfeature+1) 的矩陣。之所以加1,是因為有bias 的因素。它位於最后一列。其中樣本所有的貢獻程度相加,就是該樣本最終的預測的結果。
    • approx_contribs: 一個布爾值。如果為True,則大致估算出每個特征的貢獻程度。

Booster 沒有 train 方法。因此有兩種策略來獲得訓練好的 Booster

  • 從訓練好的模型的文件中.load_model() 來獲取
  • 多次調用.update() 方法

示例:

import xgboost as xgt
import pandas as pd
from sklearn.model_selection import train_test_split

_label_map = {
    # 'Iris-setosa':0, #經過裁剪的,去掉了 iris 中的 setosa 類
    'Iris-versicolor': 0,
    'Iris-virginica': 1
}


class BoosterTest:
    '''
    測試 Booster
    '''

    def __init__(self):
        df = pd.read_csv('./data/iris.csv')
        _feature_names = ['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width']
        x = df[_feature_names]
        y = df['Class'].map(lambda x: _label_map[x])

        train_X, test_X, train_Y, test_Y = train_test_split(x, y,
                                                            test_size=0.3, stratify=y, shuffle=True, random_state=1)
        self._train_matrix = xgt.DMatrix(data=train_X, label=train_Y,
                                         eature_names=_feature_names,
                                         feature_types=['float', 'float', 'float', 'float'])
        self._validate_matrix = xgt.DMatrix(data=test_X, label=test_Y,
                                            feature_names=_feature_names,
                                            feature_types=['float', 'float', 'float', 'float'])
        self._booster = xgt.Booster(params={
            'booster': 'gbtree',
            'silent': 0,  # 打印消息
            'eta': 0.1,  # 學習率
            'max_depth': 5,
            'tree_method': 'exact',
            'objective': 'binary:logistic',
            'eval_metric': 'auc',
            'seed': 321},
            cache=[self._train_matrix, self._validate_matrix])

    def test_attribute(self):
        '''
        測試屬性的設置和獲取
        :return:
        '''
        self._booster.set_attr(key1='1')
        print('attr:key1 -> ', self._booster.attr('key1'))
        print('attr:key2 -> ', self._booster.attr('key2'))
        print('attributes -> ', self._booster.attributes())

    def test_dump_model(self):
        '''
        測試 dump 模型
        :return:
        '''
        _dump_str = self._booster.get_dump(fmap='model/booster.feature',
                                           with_stats=True, dump_format='text')
        print('dump:', _dump_str[0][:20] + '...' if _dump_str else [])
        self._booster.dump_model('model/booster.model',
                                 fmap='model/booster.feature', with_stats=True)

    def test_train(self):
        '''
        訓練
        :return:
        '''
        for i in range(0, 100):
            self._booster.update(self._train_matrix, iteration=i)
            print(self._booster.eval(self._train_matrix, name='train', iteration=i))
            print(self._booster.eval(self._validate_matrix, name='eval', iteration=i))

    def test_importance(self):
        '''
        測試特征重要性
        :return:
        '''
        print('fscore:', self._booster.get_fscore('model/booster.feature'))
        print('score.weight:', self._booster.get_score(importance_type='weight'))
        print('score.gain:', self._booster.get_score(importance_type='gain'))

    def test(self):
        self.test_attribute()
        # attr:key1 ->  1
        # attr:key2 ->  None
        # attributes ->  {'key1': '1'}
        self.test_dump_model()
        # dump: []
        self.test_train()
        # [0]   train-auc:0.980816
        # [0]   eval-auc:0.933333
        # ...
        # [99]  train-auc:0.998367
        # [99]  eval-auc:0.995556
        self.test_dump_model()
        # dump: 0:[f2<4.85] yes=1,no...
        self.test_importance()
        # score: {'f2': 80, 'f3': 72, 'f0': 6, 'f1': 5}
        # score.weight: {'Petal Length': 80, 'Petal Width': 72, 'Sepal Length': 6, 'Sepal Width': 5}
        # score.gain: {'Petal Length': 3.6525380337500004, 'Petal Width': 2.2072901486111114, 'Sepal Length': 0.06247816666666667, 'Sepal Width': 0.09243024}


if __name__ == '__main__':
    BoosterTest().test()

 

9.2 直接學習

xgboost.train(): 使用給定的參數來訓練一個booster

xgboost.train(params, dtrain, num_boost_round=10, evals=(), obj=None, feval=None,
   maximize=False, early_stopping_rounds=None, evals_result=None, verbose_eval=True,
   xgb_model=None, callbacks=None, learning_rates=None)

參數:

  • params: 一個列表(元素為鍵值對)、一個字典,表示訓練的參數
  • dtrain:一個DMatrix 對象,表示訓練集
  • num_boost_round: 一個整數,表示boosting 迭代數量
  • evals: 一個列表,元素為(DMatrix,string)。 它給出了訓練期間的驗證集,以及驗證集的名字(從而區分驗證集的評估結果)。
  • obj:一個函數,它表示自定義的目標函數
  • feval: 一個函數,它表示自定義的evaluation 函數
  • maximize: 一個布爾值。如果為True,則表示是對feval 求最大值;否則為求最小值
  • early_stopping_rounds:一個整數,表示早停參數。如果在early_stopping_rounds 個迭代步內,驗證集的驗證誤差沒有下降,則訓練停止。該參數要求evals 參數至少包含一個驗證集。如果evals 參數包含了多個驗證集,則使用最后的一個。返回的模型是最后一次迭代的模型(而不是最佳的模型)。如果早停發生,則模型擁有三個額外的字段:
    • .best_score: 最佳的分數
    • .best_iteration: 最佳的迭代步數
    • .best_ntree_limit: 最佳的子模型數量
  • evals_result: 一個字典,它給出了對測試集要進行評估的指標。
  • verbose_eval: 一個布爾值或者整數。如果為True,則evalutation metric 將在每個boosting stage 打印出來。如果為一個整數,則evalutation metric 將在每隔verbose_eval個boosting stage 打印出來。另外最后一個boosting stage,以及早停的boosting stage 的 evalutation metric 也會被打印。
  • learning_rates: 一個列表,給出了每個迭代步的學習率。你可以讓學習率進行衰減。
  • xgb_model: 一個Booster實例,或者一個存儲了xgboost 模型的文件的文件名。它給出了待訓練的模型。這種做法允許連續訓練。
  • callbacks: 一個回調函數的列表,它給出了在每個迭代步結束之后需要調用的那些函數。你可以使用xgboost 中預定義的一些回調函數(位於callback 模塊) 。如:xgboost.reset_learning_rate(custom_rates)

返回值:一個Booster 對象,表示訓練好的模型

xgboost.cv(): 使用給定的參數執行交叉驗證 。它常用作參數搜索

xgboost.cv(params, dtrain, num_boost_round=10, nfold=3, stratified=False, folds=None,
     metrics=(), obj=None, feval=None, maximize=False, early_stopping_rounds=None,
     fpreproc=None, as_pandas=True, verbose_eval=None, show_stdv=True, seed=0,
     callbacks=None, shuffle=True)

參數:

  • params: 一個列表(元素為鍵值對)、一個字典,表示訓練的參數
  • dtrain:一個DMatrix 對象,表示訓練集
  • num_boost_round: 一個整數,表示boosting 迭代數量
  • nfold: 一個整數,表示交叉驗證的fold 的數量
  • stratified: 一個布爾值。如果為True,則執行分層采樣
  • folds: 一個scikit-learn 的 KFold 實例或者StratifiedKFold 實例。
  • metrics:一個字符串或者一個字符串的列表,指定了交叉驗證時的evaluation metrics。如果同時在params 里指定了eval_metric,則metrics 參數優先。
  • obj:一個函數,它表示自定義的目標函數
  • feval: 一個函數,它表示自定義的evaluation 函數
  • maximize: 一個布爾值。如果為True,則表示是對feval 求最大值;否則為求最小值
  • early_stopping_rounds:一個整數,表示早停參數。如果在early_stopping_rounds 個迭代步內,驗證集的驗證誤差沒有下降,則訓練停止。返回evaluation history 結果中的最后一項是最佳的迭代步的評估結果
  • fpreproc: 一個函數。它是預處理函數,其參數為(dtrain,dtest,param), 返回值是經過了變換之后的 (dtrain,dtest,param)
  • as_pandas: 一個布爾值。如果為True,則返回一個DataFrame ;否則返回一個numpy.ndarray
  • verbose_eval: 參考train()
  • show_stdv: 一個布爾值。是否verbose 中打印標准差。它對返回結果沒有影響。返回結果始終包含標准差。
  • seed: 一個整數,表示隨機數種子
  • callbacks: 參考train()
  • shuffle: 一個布爾值。如果為True,則創建folds 之前先混洗數據。

返回值:一個字符串的列表,給出了evaluation history 。它給的是早停時刻的history(此時對應着最優模型),早停之后的結果被拋棄。

示例:

import xgboost as xgt
import pandas as pd
from sklearn.model_selection import train_test_split

_label_map = {
    # 'Iris-setosa':0, #經過裁剪的,去掉了 iris 中的 setosa 類
    'Iris-versicolor': 0,
    'Iris-virginica': 1
}


class TrainTest:
    def __init__(self):
        df = pd.read_csv('./data/iris.csv')
        _feature_names = ['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width']
        x = df[_feature_names]
        y = df['Class'].map(lambda x: _label_map[x])
        train_X, test_X, train_Y, test_Y = train_test_split(x, y, test_size=0.3,
                                                            stratify=y, shuffle=True, random_state=1)
        self._train_matrix = xgt.DMatrix(data=train_X, label=train_Y,
                                         feature_names=_feature_names,
                                         feature_types=['float', 'float', 'float', 'float'])
        self._validate_matrix = xgt.DMatrix(data=test_X, label=test_Y,
                                            feature_names=_feature_names,
                                            feature_types=['float', 'float', 'float', 'float'])

    def train_test(self):
        params = {
            'booster': 'gbtree',
            'eta': 0.01,
            'max_depth': 5,
            'tree_method': 'exact',
            'objective': 'binary:logistic',
            'eval_metric': ['logloss', 'error', 'auc']
        }
        eval_rst = {}
        booster = xgt.train(params, self._train_matrix, num_boost_round=20,
                            evals=([(self._train_matrix, 'valid1'), (self._validate_matrix, 'valid2')]),
                            early_stopping_rounds=5, evals_result=eval_rst, verbose_eval=True)
        ## 訓練輸出
        # Multiple eval metrics have been passed: 'valid2-auc' will be used for early stopping.
        # Will train until valid2-auc hasn't improved in 5 rounds.
        # [0]   valid1-logloss:0.685684 valid1-error:0.042857   valid1-auc:0.980816 valid2-logloss:0.685749 valid2-error:0.066667   valid2-auc:0.933333
        # ...
        # Stopping. Best iteration:
        # [1]   valid1-logloss:0.678149 valid1-error:0.042857   valid1-auc:0.99551  valid2-logloss:0.677882 valid2-error:0.066667   valid2-auc:0.966667

        print('booster attributes:', booster.attributes())
        # booster attributes: {'best_iteration': '1', 'best_msg': '[1]\tvalid1-logloss:0.678149\tvalid1-error:0.042857\tvalid1-auc:0.99551\tvalid2-logloss:0.677882\tvalid2-error:0.066667\tvalid2-auc:0.966667', 'best_score': '0.966667'}

        print('fscore:', booster.get_fscore())
        # fscore: {'Petal Length': 8, 'Petal Width': 7}

        print('eval_rst:', eval_rst)
        # eval_rst: {'valid1': {'logloss': [0.685684, 0.678149, 0.671075, 0.663787, 0.656948, 0.649895], 'error': [0.042857, 0.042857, 0.042857, 0.042857, 0.042857, 0.042857], 'auc': [0.980816, 0.99551, 0.99551, 0.99551, 0.99551, 0.99551]}, 'valid2': {'logloss': [0.685749, 0.677882, 0.670747, 0.663147, 0.656263, 0.648916], 'error': [0.066667, 0.066667, 0.066667, 0.066667, 0.066667, 0.066667], 'auc': [0.933333, 0.966667, 0.966667, 0.966667, 0.966667, 0.966667]}}

    def cv_test(self):
        params = {
            'booster': 'gbtree',
            'eta': 0.01,
            'max_depth': 5,
            'tree_method': 'exact',
            'objective': 'binary:logistic',
            'eval_metric': ['logloss', 'error', 'auc']
        }

        eval_history = xgt.cv(params, self._train_matrix, num_boost_round=20,
                              nfold=3, stratified=True, metrics=['error', 'auc'],
                              early_stopping_rounds=5, verbose_eval=True, shuffle=True)
        ## 訓練輸出
        # [0]   train-auc:0.974306+0.00309697   train-error:0.0428743+0.0177703 test-auc:0.887626+0.0695933 test-error:0.112374+0.0695933
        # ....
        print('eval_history:', eval_history)
        # eval_history:    test-auc-mean  test-auc-std  test-error-mean  test-error-std  \
        # 0       0.887626      0.069593         0.112374        0.069593
        # 1       0.925821      0.020752         0.112374        0.069593
        # 2       0.925821      0.020752         0.098485        0.050631

        # train-auc-mean  train-auc-std  train-error-mean  train-error-std
        # 0        0.974306       0.003097          0.042874          0.01777
        # 1        0.987893       0.012337          0.042874          0.01777
        # 2        0.986735       0.011871          0.042874          0.01777

 

 

9.3 Scikit-Learn API

xgboost 給出了針對scikit-learn 接口的API。

xgboost.XGBRegressor: 它實現了scikit-learn 的回歸模型API

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, **kwargs)

參數:

  • max_depth: 一個整數,表示子樹的最大深度
  • learning_rate: 一個浮點數,表示學習率
  • n_estimators:一個整數,也就是弱學習器的最大迭代次數,或者說最大的弱學習器的個數。
  • silent: 一個布爾值。如果為False,則打印中間信息
  • objective: 一個字符串或者可調用對象,指定了目標函數。其函數簽名為:objective(y_true,y_pred) -> gra,hess。 其中:
    • y_true: 一個形狀為[n_sample] 的序列,表示真實的標簽值
    • y_pred: 一個形狀為[n_sample] 的序列,表示預測的標簽值
    • grad: 一個形狀為[n_sample] 的序列,表示每個樣本處的梯度
    • hess: 一個形狀為[n_sample] 的序列,表示每個樣本處的二階偏導數
  • booster: 一個字符串。指定了用哪一種基模型。可以為:’gbtree’,’gblinear’,’dart’
  • n_jobs: 一個整數,指定了並行度,即開啟多少個線程來訓練。如果為-1,則使用所有的CPU
  • gamma: 一個浮點數,也稱作最小划分損失min_split_loss。 它刻畫的是:對於一個葉子節點,當對它采取划分之后,損失函數的降低值的閾值。
  • min_child_weight: 一個整數,子節點的權重閾值。它刻畫的是:對於一個葉子節點,當對它采取划分之后,它的所有子節點的權重之和的閾值。
  • max_delta_step: 一個整數,每棵樹的權重估計時的最大delta step。取值范圍為,0 表示沒有限制,默認值為 0 。
  • subsample:一個浮點數,對訓練樣本的采樣比例。取值范圍為 (0,1],默認值為 1 。如果為5, 表示隨機使用一半的訓練樣本來訓練子樹。它有助於緩解過擬合。
  • colsample_bytree: 一個浮點數,構建子樹時,對特征的采樣比例。取值范圍為 (0,1], 默認值為 1。如果為5, 表示隨機使用一半的特征來訓練子樹。它有助於緩解過擬合。
  • colsample_bylevel: 一個浮點數,尋找划分點時,對特征的采樣比例。取值范圍為 (0,1], 默認值為 1。如果為5, 表示隨機使用一半的特征來尋找最佳划分點。它有助於緩解過擬合。
  • reg_alpha: 一個浮點數,是L1 正則化系數。它是xgb 的alpha 參數
  • reg_lambda: 一個浮點數,是L2 正則化系數。它是xgb 的lambda 參數
  • scale_pos_weight: 一個浮點數,用於調整正負樣本的權重,常用於類別不平衡的分類問題。默認為 1。
  • base_score:一個浮點數, 給所有樣本的一個初始的預測得分。它引入了全局的bias
  • random_state: 一個整數,表示隨機數種子。
  • missing: 一個浮點數,它的值代表發生了數據缺失。默認為nan
  • kwargs: 一個字典,給出了關鍵字參數。它用於設置Booster 對象

xgboost.XGBClassifier :它實現了scikit-learn 的分類模型API

class xgboost.XGBClassifier(max_depth=3, learning_rate=0.1, n_estimators=100, 
     silent=True, objective='binary:logistic', 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, **kwargs)

參數參考xgboost.XGBRegressor

xgboost.XGBClassifier 和 xgboost.XGBRegressor 的方法:

  • fit(X, y, sample_weight=None, eval_set=None, eval_metric=None,early_stopping_rounds=None,verbose=True, xgb_model=None) 訓練模型
    • X: 一個array-like,表示訓練集
    • y: 一個序列,表示標記
    • sample_weight: 一個序列,給出了每個樣本的權重
    • eval_set: 一個列表,元素為(X,y),給出了驗證集及其標簽。它們用於早停。如果有多個驗證集,則使用最后一個
    • eval_metric: 一個字符串或者可調用對象,用於evaluation metric。如果為字符串,則是內置的度量函數的名字;如果為可調用對象,則它的簽名為(y_pred,y_true)==>(str,value)
    • early_stopping_rounds: 指定早停的次數。參考train()
    • verbose: 一個布爾值。如果為True,則打印驗證集的評估結果。
    • xgb_model:一個Booster實例,或者一個存儲了xgboost 模型的文件的文件名。它給出了待訓練的模型。這種做法允許連續訓練。
  • predict(data, output_margin=False, ntree_limit=0) 執行預測
    • data: 一個 DMatrix 對象,表示測試集
    • output_margin: 一個布爾值。表示是否輸出原始的、未經過轉換的margin value
    • ntree_limit: 一個整數。表示使用多少棵子樹來預測。默認值為0,表示使用所有的子樹。
    • 如果訓練的時候發生了早停,則你可以使用best_ntree_limit。
  • .predict_proba(data, output_margin=False, ntree_limit=0) 執行預測,預測的是各類別的概率。它只用於分類問題,返回的是預測各類別的概率。參數:參考.predict()
  • .evals_result(): 返回一個字典,給出了各個驗證集在各個驗證參數上的歷史值。它不同於cv() 函數的返回值。cv() 函數返回evaluation history 是早停時刻的。而這里返回的是所有的歷史值

示例:

import xgboost as xgt
import pandas as pd
from sklearn.model_selection import train_test_split

_label_map = {
    # 'Iris-setosa':0, #經過裁剪的,去掉了 iris 中的 setosa 類
    'Iris-versicolor': 0,
    'Iris-virginica': 1
}


class SKLTest:
    def __init__(self):
        df = pd.read_csv('./data/iris.csv')
        _feature_names = ['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width']
        x = df[_feature_names]
        y = df['Class'].map(lambda x: _label_map[x])
        
        self.train_X, self.test_X, self.train_Y, self.test_Y = \
            train_test_split(x, y, test_size=0.3, stratify=y, shuffle=True, random_state=1)


def train_test(self):
    clf = xgt.XGBClassifier(max_depth=3, learning_rate=0.1, n_estimators=100)
    clf.fit(self.train_X, self.train_Y, eval_metric='auc',
            eval_set=[(self.test_X, self.test_Y), ],
            early_stopping_rounds=3)
    # 訓練輸出:
    # Will train until validation_0-auc hasn't improved in 3 rounds.
    # [0]   validation_0-auc:0.933333
    # ...
    # Stopping. Best iteration:
    # [2]   validation_0-auc:0.997778
    print('evals_result:', clf.evals_result())
    # evals_result: {'validation_0': {'auc': [0.933333, 0.966667, 0.997778, 0.997778, 0.997778]}}
    print('predict:', clf.predict(self.test_X))
# predict: [1 1 0 0 0 1 1 1 0 0 0 1 1 0 1 1 0 1 0 0 0 0 0 1 1 0 0 1 1 0]

 

9.4 繪圖API

xgboost.plot_importance():繪制特征重要性

xgboost.plot_importance(booster, ax=None, height=0.2, xlim=None, ylim=None,
       title='Feature importance', xlabel='F score', ylabel='Features',
       importance_type='weight', max_num_features=None, grid=True, 
       show_values=True, **kwargs)

參數:

  • booster: 一個Booster對象, 一個 XGBModel 對象,或者由get_fscore() 返回的字典
  • ax: 一個matplotlib Axes 對象。特征重要性將繪制在它上面。如果為None,則新建一個Axes
  • grid: 一個布爾值。如果為True,則開啟axes grid
  • importance_type: 一個字符串,指定了特征重要性的類別。參考get_fscore()
  • max_num_features: 一個整數,指定展示的特征的最大數量。如果為None,則展示所有的特征
  • height: 一個浮點數,指定bar 的高度。它傳遞給barh()
  • xlim: 一個元組,傳遞給xlim()
  • ylim: 一個元組,傳遞給ylim()
  • title: 一個字符串,設置Axes 的標題。默認為”Feature importance”。 如果為None,則沒有標題
  • xlabel: 一個字符串,設置Axes 的X 軸標題。默認為”F score”。 如果為None,則X 軸沒有標題
  • ylabel:一個字符串,設置Axes 的Y 軸標題。默認為”Features”。 如果為None,則Y 軸沒有標題
  • show_values: 一個布爾值。如果為True,則在繪圖上展示具體的值。
  • kwargs: 關鍵字參數,用於傳遞給barh()

xgboost.plot_tree(): 繪制指定的子樹

xgboost.plot_tree(booster, fmap='', num_trees=0, rankdir='UT', ax=None, **kwargs)

參數:

  • booster: 一個Booster對象, 一個 XGBModel 對象
  • fmap: 一個字符串,給出了feature map 文件的文件名
  • num_trees: 一個整數,制定了要繪制的子數的編號。默認為 0
  • rankdir: 一個字符串,它傳遞給graphviz的graph_attr
  • ax: 一個matplotlib Axes 對象。特征重要性將繪制在它上面。如果為None,則新建一個Axes
  • kwargs: 關鍵字參數,用於傳遞給graphviz 的graph_attr

xgboost.to_graphviz(): 轉換指定的子樹成一個graphviz 實例

在IPython中,可以自動繪制graphviz 實例;否則你需要手動調用graphviz 對象的.render() 方法來繪制。

xgboost.to_graphviz(booster, fmap='', num_trees=0, rankdir='UT', yes_color='#0000FF',
     no_color='#FF0000', **kwargs)

參數:

  • yes_color: 一個字符串,給出了滿足node condition 的邊的顏色
  • no_color: 一個字符串,給出了不滿足node condition 的邊的顏色
  • 其它參數參考 plot_tree()

示例:

import xgboost as xgt
import pandas as pd
from sklearn.model_selection import train_test_split
from matplotlib.pylab import plot as plt

_label_map = {
    # 'Iris-setosa':0, #經過裁剪的,去掉了 iris 中的 setosa 類
    'Iris-versicolor': 0,
    'Iris-virginica': 1
}


class PlotTest:
    def __init__(self):
        df = pd.read_csv('./data/iris.csv')
        _feature_names = ['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width']
        x = df[_feature_names]
        y = df['Class'].map(lambda x: _label_map[x])

        train_X, test_X, train_Y, test_Y = train_test_split(x, y,
                                                            test_size=0.3, stratify=y, shuffle=True, random_state=1)
        self._train_matrix = xgt.DMatrix(data=train_X, label=train_Y,
                                         feature_names=_feature_names,
                                         feature_types=['float', 'float', 'float', 'float'])
        self._validate_matrix = xgt.DMatrix(data=test_X, label=test_Y,
                                            feature_names=_feature_names,
                                            feature_types=['float', 'float', 'float', 'float'])

    def plot_test(self):
        params = {
            'booster': 'gbtree',
            'eta': 0.01,
            'max_depth': 5,
            'tree_method': 'exact',
            'objective': 'binary:logistic',
            'eval_metric': ['logloss', 'error', 'auc']
        }
        eval_rst = {}
        booster = xgt.train(params, self._train_matrix,
                            num_boost_round=20, evals=([(self._train_matrix, 'valid1'),
                                                        (self._validate_matrix, 'valid2')]),
                            early_stopping_rounds=5, evals_result=eval_rst, verbose_eval=True)
        xgt.plot_importance(booster)
        plt.show()

 

0x0A XGBoost超參數調優實戰

在對XGBoost進行優化調參的時候,使用的是Scikit-learn中sklearn.model_selection.GridSearchCV

常用參數解讀:

  • estimator:所使用的分類器,如果比賽中使用的是XGBoost的話,就是生成的model。比如: model = xgb.XGBRegressor(**other_params)
  • param_grid:值為字典或者列表,即需要最優化的參數的取值。比如:cv_params = {‘n_estimators’: [550, 575, 600, 650, 675]}
  • scoring :准確度評價標准,默認None,這時需要使用score函數;或者如scoring=’roc_auc’,根據所選模型不同,評價准則不同。字符串(函數名),或是可調用對象,需要其函數簽名形如:scorer(estimator, X, y);如果是None,則使用estimator的誤差估計函數。scoring參數選擇如下:

具體參考地址:http://scikit-learn.org/stable/modules/model_evaluation.html

演示時使用的r2這個得分函數,你也可以根據自己的實際需要來選擇。調參剛開始的時候,一般要先初始化一些值:

  • learning_rate: 0.1
  • n_estimators: 500
  • max_depth: 5
  • min_child_weight: 1
  • subsample: 0.8
  • colsample_bytree:0.8
  • gamma: 0
  • reg_alpha: 0
  • reg_lambda: 1

調參的時候一般按照以下順序來進行:

 

1、最佳迭代次數:n_estimators

if __name__ == '__main__':
    ...
    cv_params = {'n_estimators': [400, 500, 600, 700, 800]}
    other_params = {'learning_rate': 0.1, 'n_estimators': 500, 'max_depth': 5, 'min_child_weight': 1, 'seed': 0,
                    'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0, 'reg_alpha': 0, 'reg_lambda': 1}
    model = xgb.XGBRegressor(**other_params)
    optimized_GBM = GridSearchCV(estimator=model, param_grid=cv_params, scoring='r2', cv=5, verbose=1, n_jobs=4)
    optimized_GBM.fit(X_train, y_train)
    evalute_result = optimized_GBM.grid_scores_
    print('每輪迭代運行結果:{0}'.format(evalute_result))
    print('參數的最佳取值:{0}'.format(optimized_GBM.best_params_))
    print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))

運行后的結果為:

[Parallel(n_jobs=4)]: Done  25 out of  25 | elapsed:  1.5min finished
每輪迭代運行結果:[mean: 0.94051, std: 0.01244, params: {'n_estimators': 400}, mean: 0.94057, std: 0.01244, params: {'n_estimators': 500}, mean: 0.94061, std: 0.01230, params: {'n_estimators': 600}, mean: 0.94060, std: 0.01223, params: {'n_estimators': 700}, mean: 0.94058, std: 0.01231, params: {'n_estimators': 800}]
參數的最佳取值:{'n_estimators': 600}
最佳模型得分:0.9406056804545407

由輸出結果可知最佳迭代次數為600次。但是,我們還不能認為這是最終的結果,由於設置的間隔太大,所以,我們又測試了一組參數,這次粒度小一些:

cv_params = {'n_estimators': [550, 575, 600, 650, 675]}
other_params = {'learning_rate': 0.1, 'n_estimators': 600, 'max_depth': 5, 'min_child_weight': 1, 'seed': 0,
                    'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0, 'reg_alpha': 0, 'reg_lambda': 1}

運行后最佳迭代次數變成了550。有人可能會問,那還要不要繼續縮小粒度測試下去呢?這個可以看個人情況,如果你想要更高的精度,當然是粒度越小,結果越准確,大家可以自己慢慢去調試。

 

2、接下來要調試的參數是min_child_weight以及max_depth

注意:每次調完一個參數,要把 other_params對應的參數更新為最優值。

cv_params = {'max_depth': [3, 4, 5, 6, 7, 8, 9, 10], 'min_child_weight': [1, 2, 3, 4, 5, 6]}
other_params = {'learning_rate': 0.1, 'n_estimators': 550, 'max_depth': 5, 'min_child_weight': 1, 'seed': 0,
                    'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0, 'reg_alpha': 0, 'reg_lambda': 1}

運行后可知參數的最佳取值:{‘min_child_weight’: 5, ‘max_depth’: 4}。(代碼輸出結果被我省略了一部分,因為結果太長了,以下也是如此)

 

3、接着我們就開始調試參數:gamma

cv_params = {'gamma': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]}
other_params = {'learning_rate': 0.1, 'n_estimators': 550, 'max_depth': 4, 'min_child_weight': 5, 'seed': 0,
                    'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0, 'reg_alpha': 0, 'reg_lambda': 1}

由輸出結果可知參數的最佳取值:{‘gamma’: 0.1}。

 

4、接着是subsample以及colsample_bytree

cv_params = {'subsample': [0.6, 0.7, 0.8, 0.9], 'colsample_bytree': [0.6, 0.7, 0.8, 0.9]}
other_params = {'learning_rate': 0.1, 'n_estimators': 550, 'max_depth': 4, 'min_child_weight': 5, 'seed': 0,
                    'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0.1, 'reg_alpha': 0, 'reg_lambda': 1}

運行后顯示參數的最佳取值:{‘subsample’: 0.7,’colsample_bytree’: 0.7}

 

5、緊接着就是:reg_alpha以及reg_lambda

cv_params = {'reg_alpha': [0.05, 0.1, 1, 2, 3], 'reg_lambda': [0.05, 0.1, 1, 2, 3]}
other_params = {'learning_rate': 0.1, 'n_estimators': 550, 'max_depth': 4, 'min_child_weight': 5, 'seed': 0,
                    'subsample': 0.7, 'colsample_bytree': 0.7, 'gamma': 0.1, 'reg_alpha': 0, 'reg_lambda': 1}

由輸出結果可知參數的最佳取值:{‘reg_alpha’: 1, ‘reg_lambda’: 1}。

 

6、最后就是learning_rate,一般這時候要調小學習率來測試

cv_params = {'learning_rate': [0.01, 0.05, 0.07, 0.1, 0.2]}
other_params = {'learning_rate': 0.1, 'n_estimators': 550, 'max_depth': 4, 'min_child_weight': 5, 'seed': 0,
                    'subsample': 0.7, 'colsample_bytree': 0.7, 'gamma': 0.1, 'reg_alpha': 1, 'reg_lambda': 1}

由輸出結果可知參數的最佳取值:{‘learning_rate’: 0.1}。

我們可以很清楚地看到,隨着參數的調優,最佳模型得分是不斷提高的,這也從另一方面驗證了調優確實是起到了一定的作用。不過,我們也可以注意到,其實最佳分數並沒有提升太多。提醒一點,這個分數是根據前面設置的得分函數算出來的。

 

0x0B 更多參考

 

0x0C 轉載

https://www.biaodianfu.com/xgboost.html#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8XGBoost%EF%BC%9F

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM