機器學習之Xgboost詳解

專注於計算機視覺的AndyJiang發表於2020-07-27

本部落格參考以下連結,如有侵權,聯絡刪除
xgboost推導
xgboost面試題上
xgboost面試題下

1. 簡單介紹一下XGBoost

首先需要說一說GBDT,它是一種基於boosting增強策略的加法模型,訓練的時候採用前向分佈演算法進行貪婪的學習,每次迭代都學習一棵CART樹來擬合之前 t-1 棵樹的預測結果與訓練樣本真實值的殘差
XGBoost對GBDT進行了一系列優化,比如損失函式進行了二階泰勒展開、目標函式加入正則項、支援並行和預設缺失值處理等,在可擴充套件性和訓練速度上有了巨大的提升,但其核心思想沒有大的變化。

2. XGBoost與GBDT有什麼不同

  • 基分類器:XGBoost的基分類器不僅支援CART決策樹,還支援線性分類器,此時XGBoost相當於帶L1和L2正則化項的Logistic迴歸(分類問題)或者線性迴歸(迴歸問題)。
  • 導數資訊XGBoost對損失函式做了二階泰勒展開GBDT只用了一階導數資訊,並且XGBoost還支援自定義損失函式,只要損失函式一階、二階可導。
  • 正則項:XGBoost的目標函式加了正則項, 相當於預剪枝,使得學習出來的模型更加不容易過擬合
  • 列抽樣:XGBoost支援列取樣,與隨機森林類似,用於防止過擬合
  • 缺失值處理:對樹中的每個非葉子結點,XGBoost可以自動學習出它的預設分裂方向。如果某個樣本該特徵值缺失,會將其劃入預設分支
  • 並行化:注意不是tree維度的並行,而是特徵維度的並行。XGBoost 預先將每個特徵按特徵值排好序,儲存為塊結構,分裂結點時可以採用多執行緒並行查詢每個特徵的最佳分割點,極大提升訓練速度。

3. XGBoost為什麼使用泰勒二階展開

  • 精準性:相對於GBDT的一階泰勒展開,XGBoost採用二階泰勒展開,可以更為精準的逼近真實的損失函式
  • 可擴充套件性:損失函式支援自定義,只需要新的損失函式二階可導

4. XGBoost為什麼可以並行訓練

  • XGBoost的並行,並不是說每棵樹可以並行訓練,XGB 本質上仍然採用boosting思想,每棵樹訓練前需要等前面的樹訓練完成才能開始訓練。

  • XGBoost的並行,指的是特徵維度的並行:在訓練之前,每個特徵按特徵值對樣本進行預排序,並儲存為Block結構,在後面查詢特徵分割點時可以重複使用,而且特徵已經被儲存為一個個block結構,那麼在尋找每個特徵的最佳分割點時,可以利用多執行緒對每個block平行計算

5. XGBoost為什麼快

  • 分塊並行:訓練前每個特徵按特徵值進行排序並儲存為Block結構,後面查詢特徵分割點時重複使用,並且支援並行查詢每個特徵的分割點

  • 候選分位點:每個特徵採用常數個分位點作為候選分割點

  • CPU cache 命中優化: 使用快取預取的方法,對每個執行緒分配一個連續的buffer,讀取每個block中樣本的梯度資訊並存入連續的Buffer中。

  • Block 處理優化:Block預先放入記憶體;Block按列進行解壓縮;將Block劃分到不同硬碟來提高吞吐

6. XGBoost防止過擬合的方法

XGBoost在設計時,為了防止過擬合做了很多優化,具體如下:

  • 目標函式新增正則項葉子節點個數+葉子節點權重的L2正則化
  • 列抽樣:訓練的時候只用一部分特徵(不考慮剩餘的block塊即可)
  • 子取樣:每輪計算可以不使用全部樣本,使演算法更加保守
  • shrinkage: 可以叫學習率或步長,為了給後面的訓練留出更多的學習空間

7. XGBoost如何處理缺失值

XGBoost模型的一個優點就是允許特徵存在缺失值。對缺失值的處理方式如下:

  • 在特徵k上尋找最佳 split point 時,不會對該列特徵 missing 的樣本進行遍歷,而只對該列特徵值為 non-missing 的樣本上對應的特徵值進行遍歷,通過這個技巧來減少了為稀疏離散特徵尋找 split point 的時間開銷。
  • 在邏輯實現上,為了保證完備性,會將該特徵值missing的樣本分別分配到左葉子結點和右葉子結點,兩種情形都計算一遍後,選擇分裂後增益最大的那個方向(左分支或是右分支),作為預測時特徵值缺失樣本的預設分支方向。
  • 如果在訓練中沒有缺失值而在預測中出現缺失,那麼會自動將缺失值的劃分方向放到右子結點

8. XGBoost中葉子結點的權重如何計算出來

XGBoost目標函式最終推導形式如下:
在這裡插入圖片描述
利用一元二次函式求最值的知識,當目標函式達到最小值Obj時,每個葉子結點的權重為wj
具體公式如下:
在這裡插入圖片描述

9. XGBoost中的一棵樹的停止生長條件

  • 當新引入的一次分裂所帶來的增益Gain<0時,放棄當前的分裂。這是訓練損失和模型結構複雜度的博弈過程。
  • 當樹達到最大深度時,停止建樹,因為樹的深度太深容易出現過擬合,這裡需要設定一個超引數max_depth。
  • 當引入一次分裂後,重新計算新生成的左、右兩個葉子結點的樣本權重和。如果任一個葉子結點的樣本權重低於某一個閾值,也會放棄此次分裂。這涉及到一個超引數:最小樣本權重和,是指如果一個葉子節點包含的樣本數量太少也會放棄分裂,防止樹分的太細

10. RF和GBDT的區別

相同點

  • 都是由多棵樹組成,最終的結果都是由多棵樹一起決定。

不同點

  • 整合學習:RF屬於bagging思想,而GBDT是boosting思想
  • 偏差-方差權衡:RF不斷的降低模型的方差,而GBDT不斷的降低模型的偏差
  • 訓練樣本:RF每次迭代的樣本是從全部訓練集中有放回抽樣形成的,而GBDT每次使用全部樣本
  • 並行性:RF的樹可以並行生成,而GBDT只能順序生成(需要等上一棵樹完全生成)
  • 最終結果:RF最終是多棵樹進行多數表決(迴歸問題是取平均),而GBDT是加權融合
  • 資料敏感性:RF對異常值不敏感,而GBDT對異常值比較敏感
  • 泛化能力:RF不易過擬合,而GBDT容易過擬合

11. XGBoost如何處理不平衡資料

對於不平衡的資料集,例如使用者的購買行為,肯定是極其不平衡的,這對XGBoost的訓練有很大的影響,XGBoost有兩種自帶的方法來解決:

第一種,如果你在意AUC,採用AUC來評估模型的效能,那你可以通過設定scale_pos_weight來平衡正樣本和負樣本的權重。例如,當正負樣本比例為1:10時,scale_pos_weight可以取10;

第二種,如果你在意概率(預測得分的合理性),你不能重新平衡資料集(會破壞資料的真實分佈),應該設定max_delta_step為一個有限數字來幫助收斂(基模型為LR時有效)。

除此之外,還可以通過上取樣、下采樣、SMOTE演算法或者自定義代價函式的方式解決正負樣本不平衡的問題

12. 比較LR和GBDT,說說什麼情景下GBDT不如LR

先說說LR和GBDT的區別:

  • LR是線性模型,可解釋性強,很容易並行化,但學習能力有限,需要大量的人工特徵工程
  • GBDT是非線性模型,具有天然的特徵組合優勢,特徵表達能力強,但是樹與樹之間無法並行訓練,而且樹模型很容易過擬合;

當在高維稀疏特徵的場景下,LR的效果一般會比GBDT好。原因如下:
先看一個例子:
假設一個**二分類問題**,label為0和1,特徵有100維,如果有1w個樣本,但其中只要10個正樣本1,而這些樣本的特徵 f1的值為全為1,而其餘9990條樣本的f1特徵都為0(在**高維稀疏的情況下**這種情況很常見)。 我們都知道在這種情況下,樹模型很容易優化出一個使用f1特徵作為重要分裂節點的樹,因為這個結點直接能夠將訓練資料劃分的很好,但是當測試的時候,卻會發現效果很差,因為這個特徵f1只是剛好偶然間跟y擬合到了這個規律,這也是我們常說的**過擬合**。
那麼這種情況下,如果採用LR的話,應該也會出現類似過擬合的情況呀:y = W1f1 + Wifi+….,其中 W1特別大以擬合這10個樣本。為什麼此時樹模型就過擬合的更嚴重呢?

仔細想想發現,因為現在的模型普遍都會帶著正則項,而 LR 等線性模型的正則項是對權重的懲罰,也就是 W1一旦過大,懲罰就會很大,進一步壓縮 W1的值,使他不至於過大。但是,樹模型則不一樣,樹模型的懲罰項通常為葉子節點數和深度等,而我們都知道,對於上面這種 case,樹只需要一個節點就可以完美分割9990和10個樣本,一個結點,最終產生的懲罰項極其之小

這也就是為什麼在高維稀疏特徵的時候,線性模型會比非線性模型好的原因了:帶正則化的線性模型比較不容易對稀疏特徵過擬合。

13. XGBoost中如何對樹進行剪枝(同停止生長條件)

  • 在目標函式中增加了正則項:使用葉子結點的數目和葉子結點權重的L2模的平方,控制樹的複雜度。
  • 在結點分裂時,定義了一個閾值,如果分裂後目標函式的增益小於該閾值,則不分裂
  • 當引入一次分裂後,重新計算新生成的左、右兩個葉子結點的樣本權重和。如果任一個葉子結點的樣本權重低於某一個閾值(最小樣本權重和),也會放棄此次分裂。
  • XGBoost 先從頂到底建立樹直到最大深度,再從底到頂反向檢查是否有不滿足分裂條件的結點,進行剪枝。

14. XGBoost如何選擇最佳分裂點?

XGBoost在訓練前預先將特徵按照特徵值進行了排序,並儲存為block結構,以後在結點分裂時可以重複使用該結構。

因此,可以採用特徵並行的方法利用多個執行緒分別計算每個特徵的最佳分割點,根據每次分裂後產生的增益,最終選擇增益最大的那個特徵的特徵值作為最佳分裂點

如果在計算每個特徵的最佳分割點時,對每個樣本都進行遍歷,計算複雜度會很大,這種全域性掃描的方法並不適用大資料的場景。XGBoost還提供了一種直方圖近似演算法,對特徵排序後僅選擇常數個候選分裂位置作為候選分裂點,極大提升了結點分裂時的計算效率。

15. XGBoost的Scalable性如何體現

  • 基分類器的scalability:弱分類器可以支援CART決策樹,也可以支援LR和Linear
  • 目標函式的scalability:支援自定義loss function,只需要其一階、二階可導。有這個特性是因為泰勒二階展開,得到通用的目標函式形式。
  • 學習方法的scalability:Block結構支援並行化,支援 Out-of-core計算。

16. XGBoost如何評價特徵的重要性

我們採用三種方法來評判XGBoost模型中特徵的重要程度:

  • weight :該特徵在所有樹中被用作分割樣本的特徵的總次數
  • gain :該特徵在其出現過的所有樹中產生的平均增益
  • cover :該特徵在其出現過的所有樹中的平均覆蓋範圍

注意:覆蓋範圍這裡**指的是一個特徵用作分割點後,其影響的樣本數量,即有多少樣本經過該特徵分割到兩個子節點。**

17. XGBooost引數調優的一般步驟

首先需要初始化一些基本變數,例如:
max_depth = 5
min_child_weight = 1
gamma = 0
subsample, colsample_bytree = 0.8
scale_pos_weight = 1

(1) 確定learning rate和estimator的數量
learning rate可以先用0.1,用cv來尋找最優的estimators(樹的數量)
(2) max_depth和 min_child_weight
我們調整這兩個引數是因為,這兩個引數對輸出結果的影響很大。我們首先將這兩個引數設定為較大的數,然後通過迭代的方式不斷修正,縮小範圍。
max_depth,每棵子樹的最大深度,check from range(3,10,2)。
min_child_weight,子節點的權重閾值,check from range(1,6,2)。
如果一個結點分裂後,它的所有子節點的權重之和都大於該閾值,該葉子節點才可以劃分。
(3) gamma
也稱作最小劃分損失min_split_loss,check from 0.1 to 0.5,指的是,對於一個葉子節點,當對它採取劃分之後,損失函式的降低值的閾值
如果大於該閾值,則該葉子節點值得繼續劃分
如果小於該閾值,則該葉子節點不值得繼續劃分
(4) subsample, colsample_bytree
subsample是對訓練的取樣比例
colsample_bytree是對特徵的取樣比例
both check from 0.6 to 0.9
(5) 正則化引數
alpha 是L1正則化係數,try 1e-5, 1e-2, 0.1, 1, 100
lambda 是L2正則化係數
(6) 降低學習率
降低學習率的同時增加樹的數量,通常最後設定學習率為0.01~0.1

18. XGBoost模型如果過擬合了怎麼解決

當出現過擬合時,有兩類引數可以緩解:
第一類引數:用於直接控制模型的複雜度。包括max_depth,min_child_weight,gamma 等引數
第二類引數:用於增加隨機性,從而使得模型在訓練時對於噪音不敏感。包括subsample,colsample_bytree
還有就是直接減小learning rate,但需要同時增加estimator 引數

19.為什麼XGBoost相比某些模型對缺失值不敏感

對存在缺失值的特徵,一般的解決方法是:

  • 離散型變數:用出現次數最多的特徵值填充;
  • 連續型變數:用中位數或均值填充;

一些模型如SVM和KNN,其模型原理中涉及到了對樣本距離的度量,如果缺失值處理不當,最終會導致模型預測效果很差。

而樹模型對缺失值的敏感度低,大部分時候可以在資料缺失時時使用。原因就是,一棵樹中每個結點在分裂時,尋找的是某個特徵的最佳分裂點(特徵值),完全可以不考慮存在特徵值缺失的樣本,也就是說,如果某些樣本缺失的特徵值缺失,對尋找最佳分割點的影響不是很大
XGBoost對缺失資料有特定的處理方法,詳情參考第7題。
因此,對於有缺失值的資料在經過缺失處理後:
當資料量很小時,優先用樸素貝葉斯
資料量適中或者較大,用樹模型,優先XGBoost
資料量較大,也可以用神經網路
避免使用距離度量相關的模型,如KNN和SVM

20. XGBoost和LightGBM的區別

在這裡插入圖片描述
(1)樹生長策略:XGB採用level-wise的分裂策略,LGB採用leaf-wise的分裂策略。XGB對每一層所有節點做無差別分裂,但是可能有些節點增益非常小,對結果影響不大,帶來不必要的開銷。Leaf-wise是在所有葉子節點中選取分裂收益最大的節點進行的,但是很容易出現過擬合問題,所以需要對最大深度做限制 。
(2)分割點查詢演算法:XGB使用特徵預排序演算法,LGB使用基於直方圖的切分點演算法,其優勢如下:

  • 減少記憶體佔用,比如離散為256個bin時,只需要用8位整形就可以儲存一個樣本被對映為哪個bin(這個bin可以說就是轉換後的特徵),對比預排序的exact greedy演算法來說(用int_32來儲存索引+ 用float_32儲存特徵值),可以節省7/8的空間。

  • 計算效率提高,預排序的Exact greedy對每個特徵都需要遍歷一遍資料,並計算增益,複雜度為?(#???????×#????)。而直方圖演算法在建立完直方圖後,只需要對每個特徵遍歷直方圖即可,複雜度為?(#???????×#????)。

  • LGB還可以使用直方圖做差加速,一個節點的直方圖可以通過父節點的直方圖減去兄弟節點的直方圖得到,從而加速計算
    但實際上xgboost的近似直方圖演算法也類似於lightgbm這裡的直方圖演算法,為什麼xgboost的近似演算法比lightgbm還是慢很多呢? xgboost在每一層都動態構建直方圖, 因為xgboost的直方圖演算法不是針對某個特定的feature,而是所有feature共享一個直方圖(每個樣本的權重是二階導),所以每一層都要重新構建直方圖,而lightgbm中對每個特徵都有一個直方圖,所以構建一次直方圖就夠了。
    (3)支援離散變數:無法直接輸入類別型變數,因此需要事先對類別型變數進行編碼(例如獨熱編碼),而LightGBM可以直接處理類別型變數。
    (4)快取命中率:XGB使用Block結構的一個缺點是取梯度的時候,是通過索引來獲取的,而這些梯度的獲取順序是按照特徵的大小順序的,這將導致非連續的記憶體訪問,可能使得CPU cache快取命中率低,從而影響演算法效率。而LGB是基於直方圖分裂特徵的,梯度資訊都儲存在一個個bin中,所以訪問梯度是連續的,快取命中率高。
    (5)LightGBM 與 XGboost 的並行策略不同:

  • 特徵並行 :LGB特徵並行的前提是每個worker留有一份完整的資料集,但是每個worker僅在特徵子集上進行最佳切分點的尋找;worker之間需要相互通訊,通過比對損失來確定最佳切分點;然後將這個最佳切分點的位置進行全域性廣播,每個worker進行切分即可。XGB的特徵並行與LGB的最大不同在於XGB每個worker節點中僅有部分的列資料,也就是垂直切分,每個worker尋找區域性最佳切分點,worker之間相互通訊,然後在具有最佳切分點的worker上進行節點分裂,再由這個節點廣播一下被切分到左右節點的樣本索引號,其他worker才能開始分裂。二者的區別就導致了LGB中worker間通訊成本明顯降低,只需通訊一個特徵分裂點即可,而XGB中要廣播樣本索引。

  • 資料並行 :當資料量很大,特徵相對較少時,可採用資料並行策略。LGB中先對資料水平切分,每個worker上的資料先建立起區域性的直方圖,然後合併成全域性的直方圖,採用直方圖相減的方式,先計算樣本量少的節點的樣本索引,然後直接相減得到另一子節點的樣本索引,這個直方圖演算法使得worker間的通訊成本降低一倍,因為只用通訊以此樣本量少的節點。XGB中的資料並行也是水平切分,然後單個worker建立區域性直方圖,再合併為全域性,不同在於根據全域性直方圖進行各個worker上的節點分裂時會單獨計運算元節點的樣本索引,因此效率賊慢,每個worker間的通訊量也就變得很大。

  • 投票並行(LGB):當資料量和維度都很大時,選用投票並行,該方法是資料並行的一個改進。資料並行中的合併直方圖的代價相對較大,尤其是當特徵維度很大時。大致思想是:每個worker首先會找到本地的一些優秀的特徵,然後進行全域性投票,根據投票結果,選擇top的特徵進行直方圖的合併,再尋求全域性的最優分割點。

相關文章