0.前言
解析源碼之前,還是介紹說明下XGBoost原理,網上對於XGBoost原理已有各種版本的解讀。而這篇博客,筆者主要想根據自己的理解,梳理看過的XGBoost資料,包括陳天奇的論文以及引用論文內容,本文主要內容基於陳天奇的論文與PPT,希望能夠做到系統地介紹XGBoost,同時加入源碼新特性讓內容上有增量。
XGBoost不僅能在單機上通過OMP實現高度並行化,還能通過MPI接口與近似分位點算法(論文中是weighted quantiles sketch)實現高效的分佈式並行化。其中近似分位點算法(approximate quantiles)會附加一篇博客進行詳細說明,分位點算法在分佈式系統、流式系統中真的是個很天才的想法,很多分佈式算法的基石。最早由M.Greenwald和S. Khanna與2001年提出的GK Summay算法,直到到2007年被Q. Zhang和W. Wang提出的多層level的merge與compress/prune框架進行高度優化,而被稱爲A fast algorithm for approximate quantiles,詳情見下一篇博客。
1.Boosting算法框架
XGBoost算法屬於集成學習中的boosting分支,其算法框架遵循1999年Friedman提出的boosting框架,該分支還有GBDT(Gradient Boosting Decision Tree),boosting集成是後一個模型是對前一個模型產生誤差信息進行矯正。gradient boost更具體,新模型的引入是爲了減少上個模型的殘差(residual),我們可以在殘差減少的梯度(Gradient)方向上建立一個新的模型。Friedman論文中針對迴歸過程提出boost框架如下:
Friedman提出boost算法框架過程描述如下:
1. 設定函數初始值
2. 根據參數
3. 每次迭代計算出函數負梯度,基於訓練數據構建模型來擬合負梯度。原則上可以選擇任何模型:樹模型,線性模型或者神經網絡等等,很少框架支持神經網絡,推測:神經網絡容易過擬合,後續函數負梯度恆爲0就無法繼續迭代優化下去。如果用樹模型進行擬合,就是我們熟悉的CART建樹過程。
4. 優化步長,根據目標函數來最優步長
該框架實際上是泛函梯度下降優化過程,儘管中間局部包含變量優化步驟,對比變量優化迭代不難發現相似之處。準確來說適合變量優化的其他策略同樣適合泛函優化:1)基於梯度下降優化,步長優化可以是精確優化和非精確優化。2)基於牛頓法,根據二階梯度直接計算步長
談到集成學習,不得不說bagging集成,比如隨機森林,1)建樹前對樣本隨機抽樣(行採樣),2)每個特徵分裂隨機採樣生成特徵候選集(列採樣),3)根據增益公式選取最優分裂特徵和對應特徵分裂值建樹。建樹過程完全獨立,不像boosting訓練中下一顆樹需要依賴前一顆樹訓練構建完成,因此能夠完全並行化。Python機器學習包sklearn中隨機森林RF能完全並行訓練,而GBDT算法不行,訓練過程還是單線程,無法利用多核導致速度慢。希望後續優化實現並行,Boosting並行不是同時構造N顆樹,而是單顆樹構建中遍歷最優特徵時的並行,類似XGBoost實現過程。隨機森林中行採樣與列採樣有效抑制模型過擬合,XGBoost也支持這2種特性,此外其還支持Dropout抗過擬合。
2. XGBoost原理推導
1. XGBoost考慮正則化項,目標函數定義如下:
其中
根據Boosting框架,可以優化出樹的建模函數
2. 因此,每次建樹優化以下目標:
其中
3. 假設我們已知樹結構
其中
4. 最終的目標值爲:
下圖爲樹的目標值計算樣例:
5. 回顧步驟3,可以發現前提假設是已知樹結構
3. XGBoost算法
1)XGBoost精確貪婪算法
構建樹流程如下:1.遍歷每個特徵
論文提出的精確貪婪算法流程如下:
2)XGBoost近似算法
精確算法由於需要遍歷特徵的所有取值,計算效率低,適合單機小數據,對於大數據、分佈式場景並不適合。論文基於Weighted Quantile Sketch分位點算法提出相應的近似算法,也證明了該分位點的正確性。通過設置
1. 在建樹之前預先將數據進行全局分桶,需要設置更小的
2. 每次分裂重新局部分桶,可以設置較大的
論文給出Higgs案例下,方案1全局分桶設置
近似算法爲什麼能用於分佈式?主要原因是分桶是基於分位點算法,分位點算法支持merge和prune操作,想了解該過程可以移步《分位點算法詳解》,而且XGBoost場景屬於weighted分位點算法,作者在論文後面也證明weighted分位點算法支持merge和prune操作,因此適合與分佈式場景。近似算法主要對數據分佈進行分桶,同時希望每個桶儘量均勻。考慮數據集:
定義rank函數爲
上述條件1爲均勻條件,條件2爲邊界條件。這樣就能得到
3)XGBoost近似算法
對於數據缺失數據、one-hot編碼等造成的特徵稀疏現象,作者在論文中提出可以處理稀疏特徵的分裂算法,主要是對稀疏特徵值miss的樣本學習出默認節點分裂方向:
1. 默認miss value進右子樹,對non-missing value的樣本在左子樹的統計值
2. 默認miss value進左子樹,對non-missing value的樣本在右子樹的統計值
最後,找出增益最大對於的特徵、特徵對於的值、以及miss value的分裂方向,作者在論文中提出基於稀疏分裂算法:
4. XGBoost工程優化
內部數據存儲格式
從算法上看,每種算法都依賴特徵排序,然後掃描,爲了減少特徵排序,XGBoost引入一種名爲block的數據存儲結構,將數據存儲在內存單元,並對每一種特徵進行排序。block中的數據以CSC格式存儲。實際上源代碼中XGBoost會把文件數據讀入先生成CSR格式,然後轉化爲CSC格式。其中CSR格式如下:
CSR包含非0數據塊values,行偏移offsets,列下標indices。offsets數組大小爲(總行數目+1),CSR是對稠密矩陣的壓縮,實際上直接訪問稠密矩陣元素
1. 根據行
2. 在列下標數據塊中二分查找
從訪問單個元素來說,從
CSC與CSR變量結構上並無差別,只是變量意義不同,其中values仍然爲非0數據塊,offsets爲列偏移,即特徵id對應數組,indices爲行下標,對應樣本id數組,XBGoost使用CSC主要用於對特徵的全局預排序。預先將CSR數據轉化爲無序的CSC數據,遍歷每個特徵,並對每個特徵
Cache-aware Access
CSC存儲優化會導致獲取每個樣本獲取統計值而不連續,造成樣本計算cache不斷切換而導致cache-miss,XGBoost通過選擇適當的block size來緩存數據解決小樣本量帶來的資源浪費以及大樣本量帶來的cache-miss之間的權衡問題,XGBoost選擇的block size爲
Out-of-core Computation
XGBoost中提出Out-of-core Computation優化,解決了在硬盤上讀取數據耗時過長,吞吐量不足:
1)Block Compression基於block,數據分塊,每塊
2)Block Sharding將數據shard到多塊硬盤上,每塊硬盤分配一個預取線程,將數據fetche到in-memory buffer中。訓練線程交替讀取多塊buffer,提升了硬盤總體的吞吐量。
5. XGBoost算法複雜度
針對精確貪婪算法,考慮數據樣本量爲
1. 全局特徵預排序,由於全局排序,後期節點再分裂可以複用全局排序信息,而不需要重新排序,因此排序複雜度爲
2. 構建單樹複雜度:由於XGBoost實現基於level-wise, 每層的時間複雜度是爲
3. 最終時間複雜度爲:
參考資料
- Friedman Boosting框架論文:https://statweb.stanford.edu/~jhf/ftp/trebst.pdf
- 陳天奇XGBoost論文:https://arxiv.org/pdf/1603.02754.pdf
- XGBoost項目:https://github.com/dmlc/xgboost
- GK Summary算法論文:http://infolab.stanford.edu/~datar/courses/cs361a/papers/quantiles.pdf
- A fast algorithm for approximate quantiles論文: https://pdfs.semanticscholar.org/03a0/f978de91f70249dc39de75e8958c49df4583.pdf
- wepon GDBT ppt:http://202.38.196.91/cache/2/03/wepon.me/5aa84bcab4e621a09cc475c348590c35/gbdt.pdf