树模型(七):LightGBM
1 XGB vs LGB
2 直方圖算法
2.1 緣起
XGBoost的Exact greedy算法流程
這個算法比較精確,但是缺點明顯:
使用直方圖算法進行劃分點的查找可以很好的克服這些缺點
2.2 算法描述
直方圖算法(Histogram algorithm)的做法是把連續的浮點特征值離散化為k個整數(其實又是分桶的思想,而這些桶稱為bin)比如[0,0.1)→0, [0.1,0.3)→1。
關鍵點(個人理解)
- pre-bin,先分箱后分裂
- LightGBM采用的直方圖方法與XGBoost的”加權分位圖”類似,直方圖的邊界即分裂值
同時,將特征根據其所在的bin進行梯度累加。這樣,遍歷一次數據后,直方圖累積了需要的梯度信息,然后可以直接根據直方圖,尋找最優的切分點。
流程描述(似乎loss計算那塊有些問題,不過不影響理解直方圖)
結合偽代碼一起看
仔細看上面的偽代碼,相信你有幾個問題:
- 如何將特征映射到bin呢?即如何分桶?
- 如何構建直方圖?直方圖算法累加的g是什么?
- 構建完直方圖如何找最優特征,有用到二階信息么?
2.2.1 分桶(bin)策略
對于數值型特征
- 如果特征取值數比max_bin數量少,直接取distinct_values的中點放置
- 特征取值比max_bin來得大,說明幾個特征取值要共用一個bin
- 計算mean size for one bin
- 標記一個特征取值數超過mean,因為這些特征需要單獨一個bin
- 剩下的特征取值中平均每個bin的取值個數
對于類別特征
- 首先對特征取值按出現的次數排序(大到小)
- 取前min(max_bin, distinct_values_int.size())中的每個特征做第3步(這樣可能忽略一些出現次數很少的特征取值)
- 然后用𝑏𝑖𝑛_2_𝑐𝑎𝑡𝑒𝑔𝑜𝑟𝑖𝑐𝑎𝑙_bin_2_categorical_(vector類型)記錄b對應的特征取值,以及用𝑐𝑎𝑡𝑒𝑔𝑜𝑟𝑖𝑐𝑎𝑙_2_𝑏𝑖𝑛_categorical_2_bin_(unordered_map類型) 將特征取值到哪個bin和一一對應起來。這樣,以后就能很方便的進行bin到特征取值和特征取值到bin的轉化。
2.2.2 做差加速
直方圖算法還可以進一步加速:一個葉子節點的直方圖可以由它的父親節點的直方圖與其兄弟的直方圖做差得到
原來構造直方圖,需要遍歷該葉子上的所有數據,但直方圖做差僅需遍歷直方圖的#bin個桶。使用這個方法,構建完一個葉子的直方圖后,可以用非常微小的代價得到它兄弟的直方圖,相當于速度提升了一倍
2.3 小結
可以看出,直方圖算法的有點有:
- 可以減少內存占用,比如離散為256個Bin時,只需要用8位整形就可以保存一個樣本被映射為哪個Bin(這個bin可以說就是轉換后的特征),對比預排序的Exact greedy算法來說(用int_32來存儲索引+ 用float_32保存特征值),可以節省7/8的空間。
- 計算效率也得到提高,預排序的Exact greedy對每個特征都需要遍歷一遍數據,并計算增益,復雜度為𝑂(#𝑓𝑒𝑎𝑡𝑢𝑟𝑒×#𝑑𝑎𝑡𝑎。而直方圖算法在建立完直方圖后,只需要對每個特征遍歷直方圖即可,復雜度為𝑂(#𝑓𝑒𝑎𝑡𝑢𝑟𝑒×#𝑏𝑖𝑛𝑠)。
- 提高緩存命中率,因為它訪問梯度是連續的(直方圖)。
- 此外,在數據并行的時候,直方圖算法可以大幅降低通信代價。(數據并行、特征并行在本文后面講解)
當然也有不夠精確的缺點:
由于特征被離散化后,找到的并不是很精確的分割點,所以會對結果產生影響。但在不同的數據集上的結果表明,離散化的分割點對最終的精度影響并不是很大,甚至有時候會更好一點。原因是決策樹本來就是弱模型,分割點是不是精確并不是太重要;較粗的分割點也有正則化的效果,可以有效地防止過擬合;即使單棵樹的訓練誤差比精確分割的算法稍大,但在梯度提升(Gradient Boosting)的框架下沒有太大的影響。
3 直方圖算法的改進
直方圖算法仍有優化的空間,建立直方圖的復雜度為𝑂(#𝑓𝑒𝑎𝑡𝑢𝑟𝑒×#𝑑𝑎𝑡𝑎)O(#feature×#data),如果能降低特征數或者降低樣本數,訓練的時間會大大減少。以往的降低樣本數的方法中,要么不能直接用在GBDT上,要么會損失精度。而降低特征數的直接想法是去除弱的特征(通常用PCA完成),然而,這些方法往往都假設特征是有冗余的,然而通常特征是精心設計的,去除它們中的任何一個可能會影響訓練精度。因此LightGBM提出了GOSS算法和EFB算法。
3.1 GOSS算法——(行)減少訓練樣本數
使用GOSS進行采樣,使得訓練算法更加的關注沒有充分訓練(under-trained)的樣本,并且只會稍微的改變原有的數據分布。
3.1.1 直觀理解
GOSS全稱為Gradient-based One-Side Sampling,中文譯為”基于梯度的單邊采樣”。具體采樣方式是,對于權值大的樣本全部進行保留,而對于權值小的樣本,可以選擇部分丟棄,是謂“單邊采樣”。但是這個權值為什么選擇”梯度”呢?因為GOSS認為,一個樣本如果其梯度值很小,則表明它的訓練誤差小,也就是說它很好地被訓練了,這時再擬合它的意義已經不大了;并且在分裂點計算時,它對Loss下降的貢獻也不大。因此,可以將梯度小的樣本拋棄。
但是這樣又帶來一個問題,就是改變了原始樣本的分布(準確來說是影響了梯度的分布),容易過擬合。為了彌補這個缺限,Goss會對拋棄的樣本集合的梯度累計值進行一定程度的比例放大。
3.1.2 算法描述
GOSS的做法偽代碼描述如下:
即:
原有的在特征j值為d處分數據帶來的增益可以定義為:
Vj∣O(d)=1nO((∑xi∈O:xij≤dgi)2nl∣Oj(d)+(∑xi∈O:xij>dgi)2nr∣Oj(d))V_{j|O}(d) = \frac{1}{n_O}\left(\frac{(\sum_{x_i\in O:x_{ij} \le d}g_i)^2}{n_{l|O}^j(d)} + \frac{(\sum_{x_i\in O:x_{ij} \gt d}g_i)^2}{n_{r|O}^j(d)} \right)Vj∣O?(d)=nO?1?(nl∣Oj?(d)(∑xi?∈O:xij?≤d?gi?)2?+nr∣Oj?(d)(∑xi?∈O:xij?>d?gi?)2?)
其中:
- O為在決策樹待分裂節點的訓練集
- no=∑I(xi∈O)n_o = \sum I(x_i \in O)no?=∑I(xi?∈O)
- nl∣Oj(d)=∑I[xi∈O:xij≤d]andnr∣Oj(d)=∑I[xi∈O:xij>d]n_{l|O}^j(d) = \sum I[x_i \in O: x_{ij} \le d]\ and\ n_{r|O}^j(d) = \sum I[x_i \in O: x_{ij} \gt d]nl∣Oj?(d)=∑I[xi?∈O:xij?≤d]?and?nr∣Oj?(d)=∑I[xi?∈O:xij?>d]
而使用GOSS后,增益定義為:
Vj∣O(d)=1nO((∑xi∈Algi+1?ab∑xi∈Blgi)2nlj(d)+(∑xi∈Argi+1?ab∑xi∈Blgr)2nrj(d))V_{j|O}(d) = \frac{1}{n_O}\left(\frac{(\sum_{x_i\in A_l} g_i + \frac{1-a} \sum_{x_i\in B_l} g_i)^2 }{n_{l}^j(d)} + \frac{(\sum_{x_i\in A_r} g_i + \frac{1-a} \sum_{x_i\in B_l} g_r)^2 }{n_{r}^j(d)} \right)Vj∣O?(d)=nO?1?(nlj?(d)(∑xi?∈Al??gi?+b1?a?∑xi?∈Bl??gi?)2?+nrj?(d)(∑xi?∈Ar??gi?+b1?a?∑xi?∈Bl??gr?)2?)
其中:
- Al={xi∈A:xij≤d},Ar={xi∈A:xij>d}A_l = \{x_i \in A: x_{ij} \le d\}, A_r = \{x_i \in A: x_{ij} \gt d\}Al?={xi?∈A:xij?≤d},Ar?={xi?∈A:xij?>d}
- Bl={xi∈B:xij≤d},Br={xi∈B:xij>d}B_l = \{x_i \in B: x_{ij} \le d\}, B_r = \{x_i \in B: x_{ij} \gt d\}Bl?={xi?∈B:xij?≤d},Br?={xi?∈B:xij?>d}
3.1.3 示例
定義
NNN表示總樣本數
aaa表示大梯度樣本比例值
bbb表示小梯度樣本的采樣比值(很多文章理解從剩下的小梯度樣本中采樣,這是錯誤的,實際這里的百分比是相對于全部樣本而言的)
舉例
有100個樣本(N=100),其中大梯度樣本20個,小梯度樣本80個。此時a=0.2a=0.2a=0.2,因為20/100 = 0.2。假設從小梯度樣本中采樣30%,則b=0.3b=0.3b=0.3,即30個,因為100*0.3=30。為了保證采樣前后的分布保持一致,最后小樣本數據計算梯度時需要乘以(1?𝑎)/𝑏 = (1-0.2)/0.3 = 8/3。
驗證一下: 20:80 = 20:30(8/3)*
3.2 EFB——(列)減少訓練特征
高維數據通常是非常稀疏的,而且很多特征是互斥的(即兩個或多個特征列不會同時為0),lightGBM對這類數據采用了名為EFB(exclusive feature bundling)的優化策略,將這些互斥特征分組合并為#bundle個維度。通過這種方式,可以將特征的維度降下來,相應的,構建histogram所耗費的時間復雜度也從O(#data ×× #feature)變為O(#data ×× #bundle),其中#feature << #bundle。方法說起來雖然簡單,但是實現起來將面臨兩大難點
針對這兩個問題,paper里面提到了兩種算法:Greedy Bundling和Merge Exclusive feature。
3.2.1 Greedy Bundling
可以簡單理解為"互斥特征綁定"
對于第一個問題,將特征劃分為最少數量的Bundle本質上屬于NP-hard problem。原理與圖著色相同,給定一個圖G,定點為V,表示特征,邊為E,表示特征之間的互斥關系,接著采用貪心算法對圖進行著色,以此來生成bundle。不過論文中指出,對于特征值得互斥在一定程度上是可以容忍的,具體的讀者可以參考下原paper。具體的算法流程如Algorithm 3所示。
采用這種方法對于特征數目不大的數據,還算OK,但是對于超大規模的特征將會出現性能瓶頸。一個優化的方向就是:采用非0值得個數作為排序的值,因為非零值越多通常沖突就越大。
3.2.2 Merge Exclusive feature
可以簡單理解為"互斥特征合并"
對于第二個問題:應該如何如何構建bundle?關鍵在于構建前的特征的值在構建后的bundle中能夠識別。由于基于histogram的方法存儲的是離散的bin而不是連續的數值,因此我們可以將不同特征的bin值設定為不同的區間即可。例如,特征A的bin值為[0,10),特征B的bin值為[0,20),要想將兩個特征bin合并,我們可以將特征B的特征bin的值加上10,其取值區間將變為[0,30)。整個方法描述如下圖所示。
不用擔心特征值還原的問題,因為EBF合并特征只為生成更簡單的直方圖,進而生成樹,以后在預測時,相關特征依然會進行同樣的偏移,只要能得到最終正確的回歸值即可。
通過MEF算法,將許多互斥的稀疏特征轉化為稠密的特征,降低了特征的數量,提高了建直方圖的效率。
4 樹的生成策略
Level-wise過一次數據可以同時分裂同一層的葉子,容易進行多線程優化,也好控制模型復雜度,不容易過擬合。但實際上Level-wise是一種低效的算法,因為它不加區分的對待同一層的葉子,帶來了很多沒必要的開銷,因為實際上很多葉子的分裂增益較低,沒必要進行搜索和分裂。
Leaf-wise則是一種更為高效的策略,每次從當前所有葉子中,找到分裂增益最大的一個葉子,然后分裂,如此循環。因此同Level-wise相比,在分裂次數相同的情況下,Leaf-wise可以降低更多的誤差,得到更好的精度。Leaf-wise的缺點是可能會長出比較深的決策樹,產生過擬合。因此LightGBM在Leaf-wise之上增加了一個最大深度的限制,在保證高效率的同時防止過擬合。
5 支持類別特征
傳統的機器學習工具一般,不能直接輸入類別特征,需要預先做離散化,抓換為很多,多維的0,1特征,這樣的做法無論在時間上還是空間上,效率都不高。
LightGBM通過更改決策樹算法的決策規則,直接原生支持類別特征,不需要額外的離散化。并且通過一些實驗,MRSA研究人員驗證了直接使用離散特征可以比使用0-1離散化后的特征,速度快到8倍以上 。
6 Increase cache hit chance
6.1 Pre-sorted算法
Pre-sorted 的算法當中,有兩個操作頻繁的地方,會造成cache-miss。
-
對梯度的訪問,在計算gain的時候,需要利用梯度,但不同的feature訪問梯度順序都是不一樣的,而且是隨機的。
-
對于索引表的訪問,pre-sorted使用一個行號和葉子節點號的索引表,防止數據切分的時候,對所有的feature進行切分。對訪問梯度一樣,所有的feature都要通過訪問這個索引表,所以,都是隨機的訪問,這個時候,會帶了非常大的系統性能的下降。
6.2 直方圖算法
LightGBM使用直方圖算法則是天然的cache friendly,首先,對梯度的訪問,因為不需要對feature進行排序,同時,所有的feature都采用同樣的方式進行訪問,所以只需要對梯度訪問的順序進行一個重新的排序,所有的feature都能連續地訪問梯度。
此外,直方圖算法不需要數據id到葉子id的一個索引表,沒有這樣一個cache-miss的問題。事實上,在cache-miss這樣一個方面,對速度的影響是很大的,尤其在數據量很大的時候,MRSA研究人員進行過測試,在數據量很多的時候,相比于隨機訪問,順序訪問的速度可以快4倍以上,這其中速度的差異基本上就是由cache-miss而帶來的。
7 并行計算
7.1 特征并行
特征并行主要是并行化決策樹中尋找最優劃分點(“Find Best Split”)的過程,因為這部分最為耗時。
7.1.1 傳統特征并行
傳統算法的缺點是:
7.1.2 LGB特征并行
每個worker保存所有的數據集,這樣找到全局最佳切分點后各個worker都可以自行劃分,就不用進行廣播劃分結果,減小了網絡通信量。過程如下:
但是這樣仍然有缺點:
7.2 數據并行
7.2.1 傳統數據并行
數據并行目標是并行化整個決策學習的過程:
在第3步中,有兩種合并的方式:
- 采用點對點方式(point-to-point communication algorithm)進行通訊,每個worker通訊量為O(#machine*#feature*#bin)
- 采用collective communication algorithm(如“All Reduce”)進行通訊(相當于有一個中心節點,通訊后在返回結果),每個worker的通訊量為O(2*#feature*#bin)
可以看出通信的代價是很高的,這也是數據并行的缺點。
7.2.2 LGB數據并行
通過上述兩點做法,通信開銷降為O(0.5?#feature?#bin)
7.3 Voting Parallel
LightGBM采用一種稱為PV-Tree的算法進行投票并行(Voting Parallel),其實這本質上也是一種數據并行。
PV-Tree和普通的決策樹差不多,只是在尋找最優切分點上有所不同。
其算法偽代碼描述如下:
可以看出,PV-tree將原本需要#feature×#bin 變為了2k×#bin,通信開銷得到降低。此外,可以證明,當每個worker的數據足夠多的時候,top-2k個中包含全局最佳切分點的概率非常高。
總結
以上是生活随笔為你收集整理的树模型(七):LightGBM的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Golang库集合
- 下一篇: 如何使用TestFlight发布和安装测