【三部曲02】從GBDT到XGBoost

參考文獻引用來源:

1.XGBoost論文翻譯和理解
2.CART,迴歸樹,GBDT,XGBoost,LightGBM一路理解過來
3.Chen T , Guestrin C . XGBoost: A Scalable Tree Boosting System[C]// Proceedings of the 22nd ACM SIGKDD International Conference on Knowledge Discovery and Data Mining. ACM, 2016.
4.Newton Boosting Tree 算法原理:詳解 XGBoost,強烈推薦!!!!
5.xgb博客


Question1. 從論文本身表述看,xgboost有哪些工作點?

【1】設計和構建高度可擴展的端到端提升樹系統。
【2】提出了一個理論上合理的加權分位數略圖。 這個東西就是推薦分割點的時候用,能不用遍歷所有的點,只用部分點就行,近似地表示,省時間。
【3】引入了一種新穎的稀疏感知算法用於並行樹學習。 令缺失值有默認方向。
【4】提出了一個有效的用於核外樹形學習的緩存感知塊結構。 用緩存加速尋找排序後被打亂的索引的列數據的過程。
除了這些主要的貢獻之外,還提出了一個改進正則化學習的方法


Question2. 相較於GBDT,xgboost在算法層面的改進點有哪些?

【1】改進殘差函數,不用Gini作爲殘差,用二階泰勒展開+樹的複雜度(正則項)
帶來如下好處:
1.可以控制樹的複雜度
2.帶有關於梯度的更多信息,獲得了二階導數
3.可以用線性分類器

【2】採用預排序
因爲每一次迭代中,都要生成一個決策樹,而這個決策樹是殘差的決策樹,所以傳統的不能並行;
但是陳天奇注意到,每次建立決策樹,在分裂節點的時候,比如選中A特徵,就要對A進行排序,再計算殘差,這個花很多時間。於是陳天奇想到: 每一次殘差計算好之後,全部維度預先排序,並且此排序是可以並行的;並行排序好後,對每一個維度,計算一次最佳分裂點,求出對應的殘差增益,於是只要不斷選擇最好的殘差作爲分裂點就可以。

也就是說:雖然森林的建立是串行的沒有變,但是每一顆樹的建立變成並行的了

這樣帶來的好處:
1.分裂點的計算可並行了,不需要等到一個特徵的算完再下一個了;
2.每層可以並行:當分裂點的計算可以並行,對每一層,比如分裂了左兒子和右兒子,那麼在這兩個兒子上分裂哪個特徵及其增益也計算好了;

【3】Shrinkage(縮減)
相當於學習速率(XGBoost中的eta)。XGBoost在進行完一次迭代時,會將葉子節點的權值乘上該係數,主要是爲了削弱每棵樹的影響,讓後面有更大的學習空間。(GBDT也有學習速率)

【4】列抽樣
XGBoost借鑑了隨機森林的做法,支持列抽樣,不僅防止過 擬合,還能減少計算。


Question3. xgboost具體內容

首先我們知道:
在GBDT中,針對的是損失函數進行的梯度計算;
而在xgboost中,是對誤差函數進行了泰勒二階展開,並且添加了正則化項。

在這裏插入圖片描述
上式中,T表示第k次迭代中生成的樹模型fk的葉子節點數;w表示fk各個葉子結點的輸出值;
γ和λ爲正則化係數,可以看出這兩個值能對模型的複雜度和輸出值起到很強的控制;

需要強調的是:
1.等式右側第二項的正則項,作用只是用於迭代時防止模型fk出現過擬合,其並不出現在模型集成中!

2.此外,XGBoost要求損失函數L 至少是二階連續可導的凸函數。因爲後面的計算要用到它的一階和二階導數

我們將上面加了正則項的目標函數中的損失函數部分進行二階泰勒展開 ,則有:
在這裏插入圖片描述
其中gi爲L函數對Fm-1(xi)的一階導數;hi爲L函數對Fm-1(xi)的二階導數;(注意一下,這是對具體樣本點xi的導數!!)

這樣展開後,由於第一項對於第t次迭代來說是常數,因此在優化過程中是可以去掉的,這樣目標函數就變爲:
在這裏插入圖片描述
最後展開正則項,我們得到了:
在這裏插入圖片描述
公式(4)的變換相當於從樣本視角轉入迴歸樹視角:
上面的式子是基於n個數據樣本的表示,下面的則是基於T個葉子結點的表示;

可以這麼理解:
就是說一共n個樣本,我們當然可以對每一個樣本計算設定的損失函數值,這樣n個樣本的損失函數值加起來自然就是目標損失函數值;但是從另一個角度,n個樣本最終是要分到T個葉子結點的,既然我們可以從樣本的角度進行損失函數值的統計,那麼我們當然可以從葉子結點的角度進行損失函數的統計,這種統計就是(4)中下面的式子的表示形式:即分別對於每一個葉子結點,統計劃分到該節點的樣本所產生的損失函數值;

附:關於上面公式中的參數說明:
(1)數據集是n*m的,就是有n個數據,每個數據有m維特徵;
(2)F就是組合成的迴歸樹模型,共k棵樹組成了F;
(3)每棵樹fk有T片葉子,γ是這一項的係數,λ是所有葉子節點的輸出值的L2正則之和的係數。當正則項係數爲0時(即γ和λ爲0時),整體的目標就退化爲了GBDT;
(4)gi爲函數Fm-1關於樣本xi的一階導數,h爲函數Fm-1關於樣本xi的二階導數;
(5)ωj爲第j個葉子結點的輸出值;


注:關於這個二階泰勒展開形成的目標公式,陳天奇的解釋:

他說:這個目標函數有一個很明顯的特點,那就是隻依賴於每個數據點的在目標函數上的一階和二階導數,有人可能會問,這個形式似乎比我們之前學過的決策樹學習難懂。爲什麼要花這麼多力氣來做推導呢?因爲這樣做使得我們可以很清楚地理解整個目標是什麼,並且一步一步推導出如何進行樹的學習。
這一個抽象的形式對於實現機器學習工具也是非常有幫助的。傳統的GBDT可能大家可以理解如優化平方殘差,但是我們這裏的這樣一個形式包含所有可以求導的目標函數。也就是說有了這個形式,我們寫出來的代碼可以用來求解包括迴歸,分類和排序的各種問題,正式的推導可以使得機器學習的工具更加一般。


在這裏插入圖片描述
在推出上面公式(4)後,其中葉子結點j的權重參數ωj的計算,可基於上式(5);這是因爲此時的損失函數實際上是可以看做ωj的函數:
在這裏插入圖片描述
其中:
在這裏插入圖片描述
因此直接對式 3.6 求ωj的導數,並令其等於零,就可以得到第 j 個葉子節點的最優輸出值ωj*,即如式(5)所示;

我們將(5)式求出的ωj的值帶入(4)式即可得到目標損失函數最小值,如(6)式所示;其中 q 表示第 m 輪迭代中生成的樹的結構,(6)式的值代表了第 m 次迭代中生成的模型帶來的損失減小值;很顯然這個值是小於0的,而且其越小,則第 m 個模型加入後得到的新集成模型Fm相對於加入前的集成模型Fm-1減小的損失函數值就越大。

公式(5)和公式(6)反映出:使得新集成模型Fm損失最小的第m棵樹,其葉子結點的最優輸出值只由損失函數在Fm-1的一階導數和二階導數決定,因此在第 m 輪迭代時,只要我們預先計算出 g 和 h,在訓練生成一顆樹模型後就可以直接算出每個葉子節點的最優輸出值。

那麼現在的問題是根據什麼條件來生成一顆合適的樹呢?

我們知道,樹結構對應的具體就是各特徵的分割點的確定。很顯然不可能去計算所有的樹結構。直觀上,一種簡單的貪婪方法就是從一個單葉子開始,迭代計算並添加分支到樹中。
具體操作方面,我們令IL爲分支左邊裏的樣本集合,IR爲分支右邊裏的樣本集合。前面介紹的(6)式可以視爲一種不純度得分函數,用於衡量樹結構的合理性。這樣的話,我們可以借用其表示形式,將分裂損失函數表示爲下式(7)所示,這種形式經常用於候選分割點的計算,是XGBoost分裂點選取的核心 (這個指標的作用類似於ID3中的信息增益,C4.5中的信息增益比等指標) ,而且區別於傳統的 CART 迴歸樹的分裂條件是均方差(或絕對偏差等等)減小最大的點作爲分裂點:
在這裏插入圖片描述


處理過擬合

上面損失函數算是改進完了。基於上述算法,經過T次迭代,我們就可以得到T+1個弱學習器,集成的方法就是將這T+1個弱學習器直接相加:
在這裏插入圖片描述
我們知道在學習過程中存在過擬合問題,相應的有兩種抑制過擬合的技術:

1.就是GBDT裏講到的Shrinkage技術。簡單來說就是對於本輪新加入的樹模型,調低權值η能減少個體的影響,給後續的模型更多學習空間。

2.就是列的重採樣技術(column subsampling)。根據一些使用者反饋,列的subsampling比行的subsampling效果好,列的subsampling也加速了並行化的特徵篩選。這裏應該就跟RF差不多吧,不過論文沒說具體怎麼column subsampling,API裏有個參數能控制subsampe的比例。

調參經驗:關於η和迭代次數 T 的取值,可以通過交叉驗證得到合適的值,通常針對不同問題,其具體值是不同的。一般來說,當條條件允許時(如對模型訓練時間沒有要求等)可以設置一個較大的迭代次數 T,然後針對該 T 值利用交叉驗證來確定一個合適的η值。但η的取值也不能太小,否則模型達不到較好的效果;


Question4.XGBoost的優化

1.xgboost核心的分割點選取算法

分割點選取算法正是xgboost算法速度較快的原因之一!

我們繼續上一部分的思路,上面公式(5)-(7)介紹了分割點的衡量標準,但這只是分割點的衡量指標,我們還不知道怎麼選取分割點合適;對於具體分割點的選取,直觀上我們當然可以採用 窮舉的方法來暴力搜索最優分割點,如下算法1所示:
在這裏插入圖片描述
算法說明:
該算法會在所有特徵 (features) 上,枚舉所有可能的劃分(splits)。爲了更高效,該算法必須首先根據特徵值對數據進行排序,以有序的方式訪問數據來枚舉Gain公式中的結構得分 (structure score) 的梯度統計 (gradient statistics)。


但是很顯然這種方法是很耗時的,而且當數據量過大時,內存方面會承受巨大壓力或者完全運行不了,因此爲了進行有效可行的分割點選取,需要提出一種近似算法,如下算法2所示:
在這裏插入圖片描述
原文中的算法說明:
該算法會首先根據特徵分佈的百分位數 (percentiles of feature distribution),提出候選劃分點 (candidate splitting points)。接着,該算法將連續型特徵映射到由這些候選點劃分的分桶(buckets) 中,聚合統計信息,基於該聚合統計找到在 proposal 間的最優解。

step1:這個算法根據特徵分佈的百分位數 (percentiles of feature distribution),做個proposal(獲取某個特徵K的候選切割點的方式叫proposal),提出候選分割點 (candidate splitting points)。
step2:然後該算法將連續型特徵映射到由這些候選分割點劃分的分桶(buckets) 中,聚合統計信息,則要選取的分割點就從這幾個proposed candidate points裏選,能大大提高效率。
(將每個特徵的取值映射到由這些該特徵對應的候選點集劃分的分桶(buckets)區間中,對每個桶(區間)內的樣本統計值 G,H 進行累加統計,最後在這些累計的統計量上尋找最佳分裂點。這樣做的主要目的是獲取每個特徵的候選分割點的 G,H 量)

這裏有兩種proposal的方式,一種是global的,一種是local的:
【1】global的是在建樹之前就做proposal,然後之後每次分割都要更新一下proposal;(學習每棵樹前,提出候選切分點;)
【2】local的方法是在每次split之後更新proposal。(每次分裂前,重新提出候選切分點;)

通常發現local的方法需要更少的candidate,而global的方法在有足夠的candidate的時候效果跟local差不多。我們的系統能充分支持exact greedy跑在單臺機器或多臺機器上,也支持這個proposal的近似算法,並且都能設定global還是local的proposal方式;
(這個算法的參數我沒有在一般的API裏看到,可能做超大型數據的時候纔會用這個吧,因爲前者雖然費時間但是更準確,通常我們跑的小數據用exact greedy就行)

我們總結整理一下,這一算法所做的就是:

1.首先基於特徵分佈的百分比,挑出每個特徵的幾個候選分割點;
2.而後基於這些分割點,將連續的特徵值劃分到區間桶內,而後基於彙總的統計數據來確定最優的分割點;
(感覺和採樣的思路很像??就是縮小候選集範圍)


加權分位數略圖
這裏算法在研究特徵分佈然後做候選分裂點集合提取的時候,用到了加權分位數略圖(weighted quantile sketch),原文說不加權的分位數略圖有不少了,但是支持加權的以前沒人做。


插播:關於分位點、分位數略圖和加權分位數略圖

1.關於分位點
在這裏插入圖片描述
2.關於分位數略圖
在這裏插入圖片描述
3.關於加權分位數略圖
在這裏插入圖片描述
XGBoost 算法在計算特徵 k 的分位數點時並沒有按照均勻分爲的方式來將樣本等分到各個區間,而是採用了加權求分位數的方式。那這個權重是怎麼來的呢?

在這裏插入圖片描述
樣本權重代表的就是概率,概率越大表示該點出現的次數或是該點附近的值出現的次數就越多。XGBoost 算法依據該權重分步來選取分位數。

算法步驟如下:

在這裏插入圖片描述


Question5. 稀疏感知的劃分查找算法(Sparsity-aware Split Finding)

在實際使用中,考慮到輸入數據的稀疏表示的現象,使算法能夠意識到數據的稀疏模式是很重要的。因此在文中提出在每個樹節點中添加一個默認方向,作爲值缺失時,能夠直接使用的方向。
在這裏插入圖片描述
讓算法意識到數據中的稀疏模式很重要。爲了這麼做,我們提出了在每個樹節點上增加一個缺省的方向(default direction),如上圖所示。當稀疏矩陣x中的值缺失時,樣本實例被歸類到缺省方向上。在每個分枝上,缺省方向有兩種選擇。最優的缺省方向可以從數據中學到。如算法3所示。關鍵的改進點是:只訪問非缺失的條目Ik。上述算法會將未出現值(non-presence)當成是一個missing value,學到最好的方向來處理missing values。當未出現值對應於一個用戶指定值時,應用相同的算法,可以通過將枚舉(enumeration)限定到一致的解上。

一個值是缺失值時,我們就把他分類到默認方向,每個分支有兩個選擇,具體應該選哪個?這裏提出一個算法,枚舉向左和向右的情況,稀疏感知的劃分查找算法計算切分後的評判標準是:哪個Gain大選哪個
在這裏插入圖片描述


Question6. XGBoost在系統層面做了哪些優化?

這裏XGB將所有的列數據都預先排了序:
在這裏插入圖片描述

以壓縮形式分別存到block裏,不同的block可以分佈式存儲,甚至存到硬盤裏。在特徵選擇的時候,可以並行的處理這些列數據,XGB就是在這實現的並行化,用多線程來實現加速。同時這裏陳博士還用cache加了一個底層優化:

在這裏插入圖片描述

當數據排序後,索引值是亂序的,可能指向了不同的內存地址,找的時候數據是不連續的,這裏加了個緩存,讓以後找的時候能找到小批量的連續地址,以實現加速!這裏是在每個線程裏申請了一個internal buffer來實現的!這個優化在小數據下看不出來,數據越多越明顯。

一些其他點:

實驗數據裏提到column subsampling表現不太穩定,有時候sub比不sub要好,有時候sub要好,什麼時候該用subsampling呢?當沒有重要的特徵要選,每個特徵值的重要性都很平均的時候,對列的subsampling效果就比較差了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章