树模型系列之XGBoost算法
文章目錄
- 樹模型系列之XGBoost算法
- 概要
- 原理
- 損失函數
- 子樹
- xgboost算法流程總結
- 關鍵問題
- 缺失值處理(稀疏問題的分裂點查找 Sparsity-aware Split Finding)
- 查找分裂點的近似算法 Approximate Algorithm
- 特征分布的分位數的理解
- 帶權的分位方案 Weighted Quantile Sketch
- 旨在并行學習的列塊結構 Column Block for Parallel Learning
- 存儲與計算
- 關注緩存的存取 Cache-aware Access
- 核外塊的計算 Blocks for Out-of-core Computation
- xgboost用于特征選擇
- 縮放和列抽樣 shrinkage and column subsampling
- 小問題
- 對比分析
- XGBoost與GBDT
- 區別
- 聯系
- 參考文獻
樹模型系列之XGBoost算法
概要
主要是基于陳天奇論文和slide以及其他博客文章(見參考文獻)的整理,完整的記錄XGBoost的理論和關鍵問題討論,以備溫習和加深記憶。強烈建議看看前兩個參考文獻
主要從以下幾個方面進行闡述
- XGBoost的理論推導
- XGBoost的幾個關鍵核心問題
- XGBoost的優缺點
原理
推導XGBoost:
損失函數
任何機器學習的問題都可以從目標函數(objective function)出發,目標函數的主要由兩部分組成 損失函數+正則項
損失函數用于描述模型擬合數據的程度。
正則項用于控制模型的復雜度
對于正則項,我們常用的L2正則和L1正則:
在這里,當選擇樹模型為基學習器時,我們需要正則的對象,或者說需要控制復雜度的對象就是這K顆樹,通常樹的參數有樹的深度,葉子節點的個數,葉子節點值的取值(xgboost里稱為權重weight)
所以,我們的目標函數形式如下:
對一個目標函數,我們最理想的方法就選擇一個優化方法算法去一步步的迭代的學習出參數。但是這里的參數是一顆顆的樹,沒有辦法通過這種方式來學習。
既然如此,我們可以利用Boosting的思想來解決這個問題,我們把學習的過程分解成先學第一顆樹,然后基于第一棵樹學習第二顆樹。也就是說:
所以,對于第K次的目標函數為:
上面的式子意義很明顯,只需要尋找一顆合適的樹fk使得目標函數最小。然后不斷的迭代K次就可以完成K個學習器的訓練。
子樹
如何尋找合適的子樹fk
在GBDT里面(當然GBDT沒有正則),我們的樹是通過擬合上一顆樹的負梯度值,建樹的時候采用的啟發式準則;
然而,在xgboost里面,它是通過對損失函數進行泰勒展開。(其思想主要來自于文章:Additive logistic regression a statistical view of boosting也是Friedman大牛的作品)
二階泰勒展開:
對損失函數二階泰勒展開:
(1)式和(2)非常的重要,貫穿了整個樹的構建(分裂,葉子節點值的計算)。以及(2)式是我們利用xgboost做特征選擇時的其中一個評價指標。
所以我們可以得到我們進化后的目標函數:
一顆樹用數學模型來描述是什么樣,很簡單其實就是一個分段函數。比如有下面一顆樹:
也就是說,一棵樹其實可以由一片區域以及若干個葉子節點來表達。而同時,構建一顆樹也是為了找到每個節點的區域以及葉子節點的值。
既然一棵樹可以用葉子節點來表達,那么,我們上面的正則項就了其中一個思路。我們可以對葉子節點值進行懲罰(正則),比如取L2正則,以及我們控制一下葉子節點的個數T,那么正則項有:
其實正則為什么可以控制模型復雜度呢?有很多角度可以看這個問題,最直觀就是,我們為了使得目標函數最小,自然正則項也要小,正則項要小,葉子節點個數T要小(葉子節點個數少,樹就簡單)。
而為什么要對葉子節點的值進行L2正則,這個可以參考一下LR里面進行正則的原因,簡單的說就是LR沒有加正則,整個w的參數空間是無限大的,只有加了正則之后,才會把w的解規范在一個范圍內。(對此困惑的話可以跑一個不帶正則的LR,每次出來的權重w都不一樣,但是loss都是一樣的,加了L2正則后,每次得到的w都是一樣的)
這個時候,我們的目標函數(移除常數項后)就可以改寫成這樣(用葉子節點表達):
其實我們可以進一步化簡,那么最后可以化簡成:
(3)式子展開之后按照葉子節點編號進行合并后可以得到(4)??梢宰约号eT=2的例子推導一下。j 表示葉子節點編號,同一個編號的葉子節點數據合并;i 表示第幾次分裂
那么目標函數可以進一步簡化為:
我們做了這么多,其實一直都是在對二階泰勒展開后的式子化簡,其實剛展開的時候就已經是一個二次函數了,只不過化簡到這里能夠把問題看的更加清楚。所以對于這個目標函數,我們對wj求導,然后帶入極值點,可以得到一個極值(葉子節點取值):
花了這么大的功夫,得到了葉子結點取值的表達式。
如果有看過我們前面GBDT文章的朋友應該沒有忘記當時我們也給出了一系列的損失函數下的葉子節點的取值,在xgboost里,葉子節點取值的表達式很簡潔,推導起來也比GBDT的要簡便許多
到這里,我們一直都是在圍繞目標函數進行分析,這個到底是為什么呢?這個主要是為了后面我們尋找fk(x),也就是建樹的過程。 XGBoost 分裂規則直接與損失函數有關
具體來說,我們回憶一下建樹的時候需要做什么,建樹的時候最關鍵的一步就是選擇一個分裂的準則,也就如何評價分裂的質量。比如在前面文章GBDT的介紹里,我們可以選擇MSE,MAE來評價我們的分裂的質量,但是,我們所選擇的分裂準則似乎不總是和我們的損失函數有關,因為這種選擇是啟發式的。
比如,在分類任務里面,損失函數可以選擇logloss,分裂準確選擇MSE,這樣看來,似乎分裂的好壞和我們的損失并沒有直接掛鉤。
但是,在xgboost里面,我們的分裂準則是直接與損失函數掛鉤的準則,這個也是xgboost和GBDT一個很不一樣的地方
具體來說,xgboost選擇這個準則,計算增益Gain
其實選擇這個作為準則的原因很簡單也很直觀。
既然要分裂的時候,我們當然是選擇分裂成左右子節點后,損失減少的最多(或者看回(9)式,由于前面的負號,所以欲求(9)的最小值,其實就是求(10)的最大值)
那么γ的作用是什么呢?利用γ可以控制樹的復雜度,進一步來說,利用γ來作為閾值,只有大于γ時候才選擇分裂。這個其實起到預剪枝的作用。
最后就是如何得到左右子節點的樣本集合?這個和普通的樹一樣,都是通過遍歷特征所有取值,逐個嘗試。
至此,我們把xgboost的基本原理闡述了一遍。我們總結一下,其實就是做了以下幾件事情。
1.在損失函數的基礎上加入了正則項。
2.對目標函數進行二階泰勒展開。
3.利用推導得到的表達式作為分裂準確,來構建每一顆樹。
xgboost算法流程總結
xgboost核心部分的算法流程圖如下:
(這里的m貌似是d)
關鍵問題
主要總結xgboost訓練時幾個關鍵問題
缺失值處理(稀疏問題的分裂點查找 Sparsity-aware Split Finding)
訓練時候
對于數據缺失數據、one-hot編碼等造成的特征稀疏現象,作者在論文中提出可以處理稀疏特征的分裂算法,主要是對稀疏特征值缺失的樣本學習出默認節點分裂方向:
這樣最后求出增益最大的特征值以及 miss value 的分裂方向,作者在論文中提出基于稀疏分裂算法: (修正:下文 “Input: d feature dimension” 這里 “d” 應該改為 “m”)
具體看下面的算法流程:
使用了該方法,相當于比傳統方法多遍歷了一次,但是它只在非缺失值的樣本上進行迭代,因此其復雜度與非缺失值的樣本成線性關系
預測時候
直接將確實樣本特征歸屬到右子樹
查找分裂點的近似算法 Approximate Algorithm
當數據量十分龐大,以致于不能全部放入內存時,精確的貪婪算法就不可能很有效率,通樣的問題也出現在分布式的數據集中,為了高效的梯度提升算法,在這兩種背景下,近似的算法被提出使用,算法的偽代碼如下圖所示
概括一下:枚舉所有特征,根據特征,比如是第 k 個特征的分布的分位數來決定出 l 個候選切分點 Sk={sk1,sk2,?skl} ,然后根據這些候選切分點把相應的樣本映射到對應的桶中,對每個桶的 G,H 進行累加。最后在候選切分點集合上貪心查找,和Exact Greedy Algorithm類似。
特征分布的分位數的理解
論文給出近似算法的2種變體,主要是根據候選點的來源不同區分:
- 在建樹之前預先將數據進行全局(global)分桶,需要設置更小的分位數間隔,這里用 ? 表示,3分位的分位數間隔就是 1/3,產生更多的桶,特征分裂查找基于候選點多,計算較慢,但只需在全局執行一次,全局分桶多次使用。
- 每次分裂重新局部(local)分桶,可以設置較大的 ? ,產生更少的桶,每次特征分裂查找基于較少的候選點,計算速度快,但是需要每次節點分裂后重新執行,論文中說該方案更適合樹深的場景
局部選擇的近似算法的確比全局選擇的近似算法優秀的多,所得出的結果和貪婪算法幾乎不相上下。當然 局部選擇的近似算法也比全局選擇的近似算法速度慢
帶權的分位方案 Weighted Quantile Sketch
二階導hi作為權重
在近似的分裂點查找算法中,一個步驟就是提出候選分裂點,通常情況下,一個特征的分位數使候選分裂點均勻地分布在數據集上,就像前文舉的關于特征分位數的例子
現在應該明白 Weighted Quantile Sketch 帶權的分位方案的由來,下面舉個例子:
即要切分為3個,總和為1.8,因此第1個在0.6處,第2個在1.2處。此圖來自知乎weapon大神的《 GBDT算法原理與系統設計簡介》
旨在并行學習的列塊結構 Column Block for Parallel Learning
CSR vs CSC
稀疏矩陣的壓縮存儲形式,比較常見的其中兩種:壓縮的稀疏行(Compressed Sparse Row)和 壓縮的稀疏列(Compressed Sparse Row)
CSR結構包含非0數據塊values,行偏移offsets,列下標indices。offsets數組大小為(總行數目+1),CSR 是對稠密矩陣的壓縮,實際上直接訪問稠密矩陣元素 (i,j) 并不高效,畢竟損失部分信息,訪問過程如下:
- 根據行 i 得到偏移區間開始位置 offsets[i]與區間結束位置offsets[i+1]-1,得到 i 行數據塊 values[offsets[i]..(offsets[i+1]-1)], 與非0的列下表indices[offsets[i]..(offsets[i+1]-1)]
- 在列下標數據塊中二分查找 j,找不到則返回0,否則找到下標值 k,返回 values[offsets[i]+k]
從訪問單個元素來說,相比坐標系的存儲結構,那么從 O(1) 時間復雜度升到 O(logN), N 為該行非稀疏數據項個數。但是如果要遍歷訪問整行非0數據,則無需訪問indices數組,時間復雜度反而更低,因為少了大量的稀疏為0的數據訪問。
CSC 與 CSR 變量結構上并無差別,只是變量意義不同
- values仍然為矩陣的非0數據塊
- offsets為列偏移,即特征id對應數組
- indices為行下標,對應樣本id數組
XBGoost使用CSC 主要用于對特征的全局預排序。預先將 CSR 數據轉化為無序的 CSC 數據,遍歷每個特征,并對每個特征 i 進行排序:sort(&values[offsets[i]], &values[offsets[i+1]-1])。全局特征排序后,后期節點分裂可以復用全局排序信息,而不需要重新排序。
矩陣的存儲形式,參考此文稀疏矩陣存儲格式總結+存儲效率對比:COO,CSR,DIA,ELL,HYB
存儲與計算
關注緩存的存取 Cache-aware Access
使用Block結構的一個缺點是取梯度的時候,是通過索引來獲取的,而這些梯度的獲取順序是按照特征的大小順序的。這將導致非連續的內存訪問,可能使得CPU cache緩存命中率低,從而影響算法效率
因此,對于exact greedy算法中, 使用緩存預取。具體來說,對每個線程分配一個連續的buffer,讀取梯度信息并存入Buffer中(這樣就實現了非連續到連續的轉化),然后再統計梯度信息。該方式在訓練樣本數大的時候特別有用
在近似算法中,對塊的大小進行了合理的設置。定義Block的大小為Block中最多的樣本數。設置合適的大小是很重要的,設置過大則容易導致命中率低,過小則容易導致并行化效率不高
核外塊的計算 Blocks for Out-of-core Computation
XGBoost 中提出 Out-of-core Computation優化,解決了在硬盤上讀取數據耗時過長,吞吐量不足
- 多線程對數據分塊壓縮 Block Compression存儲在硬盤上,再將數據傳輸到內存,最后再用獨立的線程解壓縮,核心思想:將磁盤的讀取消耗轉換為解壓縮所消耗的計算資源。
- 分布式數據庫系統的常見設計:Block Sharding將數據分片到多塊硬盤上,每塊硬盤分配一個預取線程,將數據fetche到in-memory buffer中。訓練線程交替讀取多塊緩存的同時,計算任務也在運轉,提升了硬盤總體的吞吐量
xgboost用于特征選擇
xgboost主要提供了3個指標來衡量特征重要性:weight、gain、cover
weight指標
調用xgb庫的get_fscore()得到 指標weight,這個指標代表著某個特征被選作分裂的次數
比如我們得到這兩顆樹:
可以看到特征x1被選作分裂點的次數為6,x2被選做分裂點的次數為2。
get_fscore()就是返回這個指標。
gain指標
gain是指某個特征的平均增益。
比如,特征x1被選了6次作為分裂的特征,每次的增益假如為Gain1,Gain2,…Gain6,那么其平均增益為(Gain1+Gain2+…Gain3)/6
cover指標
cover指的是每個特征在分裂時結點處的平均二階導數。
‘cover’ - the average coverage of the feature when it is used in trees。大概的意義就是特征覆蓋了多少個樣本。
這里,我們不妨假設損失函數是mse,也就是0.5?(yi?ypred)20.5?(yi?ypred)2,我們求其二階導數,很容易得到為常數1。也就是每個樣本對應的二階導數都是1,那么這個cover指標不就是意味著,在某個特征在某個結點進行分裂時所覆蓋的樣本個數嗎?
縮放和列抽樣 shrinkage and column subsampling
隨機森林中的用法和目的一樣,用來防止過擬合,主要參考論文2.3節
- 這個xgboost與現代的gbdt一樣,都有shrinkage參數 (最原始的gbdt沒有這個參數)類似于梯度下降算法中的學習速率,在每一步tree boosting之后增加了一個參數 η(被加入樹的權重),通過這種方式來減小每棵樹的影響力,給后面的樹提供空間去優化模型。
- column subsampling 列(特征)抽樣,這個經常用在隨機森林,不過據XGBoost的使用者反饋,列抽樣防止過擬合的效果比傳統的行抽樣還好(xgboost也提供行抽樣的參數供用戶使用),并且有利于后面提到的并行化處理算法。
小問題
-
剪枝與正則化Pruning and Regularization
-
提升樹算法 Recap: Boosted Tree Algorithm
-
列(特征)抽樣作用Column Subsampling
Column Subsampling類似于隨機森林中的選取部分特征進行建樹。其可分為兩種,一種是按層隨機采樣,在對同一層內每個結點分裂之前,先隨機選擇一部分特征,然后只需要遍歷這部分的特征,來確定最優的分割點。另一種是隨機選擇特征,則建樹前隨機選擇一部分特征然后分裂就只遍歷這些特征。一般情況下前者效果更好。 -
并行處理
兩處并行:同層級節點可并行雖然樹與樹之間是串行關系,但是同層級節點可并行。具體的對于某個節點,節點內選擇最佳分裂點,候選分裂點計算增益用多線程并行。訓練速度快。
對比分析
XGBoost與GBDT
區別
分類器:傳統GBDT以CART作為基分類器,xgboost支持多種基礎分類器。比如,線性分類器,這個時候xgboost相當于帶 L1 和 L2正則化項 的邏輯斯蒂回歸(分類問題)或者線性回歸(回歸問題)。
二階導數:傳統GBDT在優化時只用到一階導數信息,xgboost則對損失函數函數進行了二階泰勒展開,同時用到了一階和二階導數,這樣相對會精確地代表損失函數的值。順便提一下,xgboost工具支持自定義代價函數,只要函數可一階和二階求導,詳細參照官網API。(xgboost在建樹的時候利用的準則來源于目標函數推導,也是為啥要費勁二階泰勒展開)
并行處理,相比GBM有了速度的飛躍
- 借助 OpenMP ,自動利用單機CPU的多核進行并行計算
- 支持GPU加速
- 支持分布式
- 當新增分裂帶來負增益時,GBM會停止分裂(貪心策略,非全局的剪枝)
- XGBoost一直分裂到指定的最大深度(max_depth),然后回過頭來剪枝(事后,進行全局剪枝)
- min_child_weight : 分裂最小子節點的增益要大于這個閾值,min_child_weight < min(HL,HR),否則,放棄這個最大增益,考慮次最大增益。如果次最大增益也不滿足min_child_weight<min(HL,HR),則繼續往下找,如果沒有找到一個滿足的,則不進行分裂
加入正則項:boost在代價函數里加入了顯示的正則項,用于控制模型的復雜度。正則項里包含了樹的葉子節點個數、每個葉子節點上輸出的score的L2模的平方和,防止過擬合,這也是xgboost優于傳統GBDT的一個特性。正則化的兩個部分,都是為了防止過擬合,剪枝是都有的,葉子結點輸出L2平滑是新增的。
內置交叉驗證 Built-in Cross-Validation
- XGBoost允許在每一輪boosting迭代中使用交叉驗證,這樣可以方便地獲得最優boosting迭代次數
- GBM使用網格搜索,只能檢測有限個值
列采樣 傳統的GBDT在每輪迭代時使用全部的數據,XGBoost則采用了與隨機 森林相似的策略,支持對數據進行采樣
缺失值處理 傳統的GBDT沒有設計對缺失值進行處理,XGBoost能夠自動學習出缺 失值的處理策略。
決策樹的學習最耗時的一個步驟就是對特征的值進行排序(因為要確定最佳分割點),xgboost在訓練之前,預先對數據進行了排序,然后保存為block結構,后面的迭代中重復地使用這個結構,大大減小計算量。這個block結構也使得并行成為了可能,在進行節點的分裂時,需要計算每個特征的增益,最終選增益最大的那個特征去做分裂,那么各個特征的增益計算就可以開多線程進行
聯系
參考文獻
xgboost原理分析以及實踐
XGBoost原理和底層實現剖析
陳天奇slide https://homes.cs.washington.edu/~tqchen/pdf/BoostedTree.pdf
陳天奇XGBoost論文 https://arxiv.org/pdf/1603.02754.pdf
其他可參考下列博文:
XGBoost詳解
一文讀懂機器學習大殺器XGBoost原理
xgboost原理?
xgboost專欄
深入淺出學會xgboost 系列
GBDT與XGBoost
XGBoost Documentation
機器學習算法中 GBDT 和 XGBOOST 的區別有哪些?
深入理解XGBoost,優缺點分析,原理推導及工程實現
《統計學習方法》第8章 提升方法之AdaBoost\BoostingTree\GBDT
總結
以上是生活随笔為你收集整理的树模型系列之XGBoost算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2013年1季度中国汽车品牌口碑研究报告
- 下一篇: 无线WiFi通信模组方案,智能家居无线物