LightGBM 相关知识理解
文章目錄
- lightGBM 簡介
- 直方圖算法(Histogram algorithm)
- 基本思想
- 直方圖做差
- 帶深度限制的 Leaf-wise 算法
- 單邊梯度采樣算法(GOSS)
- 互斥特征捆綁算法(EFB)
- 1,解決哪些特征應該綁在一起
- 2,解決怎么把特征綁為一捆
- LightGBM的工程優化
- 1,原生支持類別特征
- 2,高效并行
- 特征并行
- 數據并行
- 投票并行
- 3,Cache命中率優化
- LightGBM的優缺點
- 缺點
- 參考文章
lightGBM 簡介
GBDT是個經典的模型,主要是利用弱分類器(決策樹)迭代訓練以得到最優模型,該模型具有訓練效果好、不易過擬合等優點,常被用于多分類、點擊率預測、搜索排序等任務。
在LightGBM提出之前,還有個GBDT的高效實現:XGBoost。XGBoost是屬于boosting家族,也是GBDT算法的一個工程實現。 在模型的訓練過程中是聚焦殘差,在目標函數中使用了二階泰勒展開并加入了正則,在決策樹的生成過程中采用近似分割的方式(可以理解為分桶的思路),選出一些候選的分裂點,然后再遍歷這些較少的分裂點,計算按照這些候選分裂點位分裂后的全部樣本的目標函數增益,找到最大的那個增益對應的特征和候選分裂點位,從而進行分裂。這樣一層一層的完成建樹過程, XGBoost訓練的時候,是通過加法的方式進行訓練,也就是每一次通過聚焦殘差訓練一棵樹出來, 最后的預測結果是所有樹的加和表示。
對于上面的過程,不難發現時間復雜度和空間復雜度比較高:
總的來說,XGBoost尋找最優分裂點的復雜度由下面三個因素構成:
特征數量×分裂點的數量×樣本的數量特征數量×分裂點的數量×樣本的數量 特征數量×分裂點的數量×樣本的數量
LightGBM(Light Gradient Boosting Machine)也是一個實現GBDT算法的框架,支持高效率的并行訓練,并且具有更快的訓練速度、更低的內存消耗、更好的準確率、支持分布式可以快速處理海量數據等優點。它主要對上面的三個因素分別優化,下面提到的1,直方圖算法就是為了減少分裂點的數量, 2,單邊梯度抽樣算法就是為了減少樣本的數量,3, 互斥特征捆綁算法就是為了減少特征的數量。 并且后面兩個是Lightgbm的亮點所在。
直方圖算法(Histogram algorithm)
基本思想
直方圖算法的基本思想是:先把連續的浮點特征值離散化成 k 個整數,同時構造一個寬度為 k 的直方圖。在遍歷數據的時候,根據離散化后的值作為索引在直方圖中累積計數,當遍歷一次數據后,直方圖累積了需要的統計量,然后根據直方圖的離散值,遍歷尋找最優的分割點。如下圖所示:
XGBoost 在進行預排序時只考慮非零值進行加速,而 LightGBM 也采用類似策略:只用非零特征構建直方圖。這種離散化分桶思路其實有很多優點的, 首先最明顯就是內存消耗的降低,XGBoost 需要用32位的浮點數去存儲特征值, 并用32位的整型去存儲索引:
而Lightgbm的直方圖算法不僅不需要額外存儲預排序的結果,而且可以只保存特征離散化后的值,而這個值一般用8位整型存儲就足夠了,即內存消耗從32+32變為了8,降低為原來的1/8。
同時,計數復雜度也大幅度降低,XGBoost的預排序算法每遍歷一個特征值就需要計算一次分裂的增益,而Lightgbm直方圖算法只需要計算k次(k可以認為是常數):
時間復雜度從:
O((特征取值個數?1)×特征個數)O((特征取值個數?1)× 特征個數) \\ O((特征取值個數?1)×特征個數)
變為:
O((分桶數?1)×特征個數)O((分桶數?1)× 特征個數) O((分桶數?1)×特征個數)
其中,特征取值個遠大于分桶樹。
直方圖做差
實際上,直方圖算法還可以進一步加速。一般情況下,構造直方圖需要遍歷該葉子上的所有數據才行。但是對于二叉樹而言,一個葉子節點的直方圖可以直接由父節點的直方圖和兄弟節點的直方圖做差得到:
通過該方法,只需要遍歷直方圖的k個捅,速度提升了一倍。
舉個例子,假設有15個訓練樣本,2個特征x1,x2x_1,x_2x1?,x2?, 然后我先對這兩個特征進行分桶:
把x1x_1x1?根據取值分成4個桶,每個桶里面的樣本個數分別是5, 4, 4, 3。
把x2x_2x2?根據取值也分成4個桶,每個桶里面的樣本個數分別是4, 4, 5, 3。
然后我遍歷特征,每個特征我遍歷候選分割點(分桶后),發現在x1x_1x1?的第一個候選點那收益比較大,則第一個候選點進行分裂成兩個節點。如下圖所示 :
可以看到,直方圖算法可以起到的作用就是可以減小分割點的數量, 加快計算。直方圖算法并不是完美的。由于特征被離散化后,找到的并不是很精確的分割點,所以會對結果產生影響。但在實際的數據集上表明,離散化的分裂點對最終的精度影響并不大,甚至會好一些。原因在于決策樹本身就是一個弱學習器,分割點是不是精確并不是太重要,采用直方圖算法會起到正則化的效果,有效地防止模型的過擬合(分桶的數量決定了正則化的程度,分桶數越少懲罰越嚴重,欠擬合風險越高)。
XGBoost 使用的近似分割算法其實也有類似直方圖算法的思想,但是他的分桶策略基于Weight Quantile Sketch算法,也就是基于二階導的值 hi 來選擇劃分點。可以理解為所有樣本都共享二階導 hi 構成的直方圖,一旦數據被分割,數據的分布就變了,導致 hi 的計算結果也發生變化,因此每次分裂都得重新構建直方圖。
而對 lightgbm 而言,每個特征都有一個直方圖,分裂后還能通過直方圖做差來進行加速,因此速度要快于 XGBoost。
帶深度限制的 Leaf-wise 算法
直方圖算法,不僅有趣也很有效率。這還沒有結束,在直方圖算法之上,LightGBM進行進一步的優化。首先它拋棄了大多數GBDT工具使用的按層生長 (level-wise) 的決策樹生長策略,而使用了帶有深度限制的按葉子生長 (leaf-wise) 算法。
XGBoost 采用 Level-wise 的增長策略,該策略遍歷一次數據可以同時分裂同一層的所有葉節點,容易進行多線程優化,也好控制模型復雜度,不容易過擬合。過程如下圖所示:
但實際上Level-wise是一種低效的算法,因為它不加區分的對待同一層的葉子,實際上很多葉子的分裂增益較低,沒必要進行搜索和分裂,因此帶來了很多沒必要的計算開銷。
LightGBM采用Leaf-wise的增長策略,該策略每次從當前所有葉子中,找到分裂增益最大的一個葉子,然后分裂,如此循環。
因此同Level-wise相比,Leaf-wise的優點是:在分裂次數相同的情況下,Leaf-wise可以降低更多的誤差,得到更好的精度;Leaf-wise的缺點是:可能會長出比較深的決策樹,產生過擬合。因此LightGBM會在Leaf-wise之上增加了一個最大深度的限制,在保證高效率的同時防止過擬合。
單邊梯度采樣算法(GOSS)
我們觀察到GBDT中每個數據都有不同的梯度值,即梯度小的樣本,訓練誤差也比較小,說明數據已經被模型學習得很好了,直接想法就是丟掉這部分梯度小的數據。然而這樣做會改變數據的分布,將會影響訓練模型的精確度,為了避免此問題,提出了單邊梯度抽樣算法。
單邊梯度抽樣算法(Gradient-based One-Side Sampling)是從減少樣本的角度出發, 排除大部分權重小的樣本,僅用梯度大的樣本和少數梯度小的樣本計算信息增益,它是一種在減少數據和保證精度上平衡的算法。
具體來說,GOSS在進行數據采樣的時候只保留了梯度較大的樣本,對于梯度較小的樣本,如果全部丟棄會影響數據的總體分布,因此需要對梯度小的樣本進行采樣。GOSS首先將要進行分裂的特征的所有取值按照絕對值大小降序排序,僅僅保存絕對值最大的 a?100%a?100 \%a?100% 個數據。然后在剩下的較小梯度數據中隨機選擇 b?100%b?100 \%b?100%個數據。既然小梯度的樣本數量比較少,那么可以給與樣本更大的權重1?ab\frac{1-a}{b}b1?a?,這樣算法一方面會更關注訓練不足的樣本,另一方面不至于過多的改變原始數據集的分布。
算法流程如下:
舉個例子,假設 a=14,b=14a=\frac{1}{4},b=\frac{1}{4}a=41?,b=41?,對于下面的數據:
對一階導降序排序,得到:
然后選擇 8*1/4=2 個梯度大的樣本,再從剩下的 8 - 2=6 個小梯度樣本中隨機采樣出 8*1/4=2 個,例如選出了2號和4號樣本:
可以發現上面選擇到的數據分布在不同的桶內:
對于采樣得到的2號和4號樣本,還需要乘上一個權重系數1?ab=3\frac{1-a}{b} = 3b1?a?=3,減少分布的變化,對于6號和7號樣本則無需此操作。具體而言,這個系數是乘在樣本個數、一階導數和二階導上的:
梯度小的樣本乘上相應的權重之后,可以發現樣本個數 NiN_iNi? 的總個數依然是8個, 雖然6個梯度小的樣本中去掉了4個,留下了兩個。 但是這2個樣本在梯度上和個數上都進行了3倍的放大,所以可以防止采樣對原數數據分布造成太大的影響, 這也就是論文里面說的將更多的注意力放在訓練不足的樣本上的原因。
通過這樣的方式,在不過分降低精度的同時,減少了用于訓練的樣本數量,使得訓練速度得到了加快。
互斥特征捆綁算法(EFB)
高維度的數據往往是稀疏的,這種稀疏性啟發我們設計一種無損的方法來減少特征的維度。通常被捆綁的特征都是互斥的(即特征不會同時為非零值,像one-hot),這樣兩個特征捆綁起來才不會丟失信息。如果兩個特征并不是完全互斥(部分情況下兩個特征都是非零值),可以用一個指標對特征不互斥程度進行衡量,稱之為沖突比率,當這個值較小時,我們可以選擇把不完全互斥的兩個特征捆綁,而不影響最后的精度。
舉個例子說明特征捆綁在做什么:
上圖中原始數據的形式很類似于one-hot編碼,可以將上面的4個特征捆綁到一起,達到減少特征維度的作用。上面的例子比較特殊,被捆綁的特征都是互斥的(即特征不會同時為非零值,像one-hot),這樣兩個特征捆綁起來才不會丟失信息。實際情況下,很多特征之間存在沖突(存在某個樣本的兩個特征同時不為零),可以用一個指標對特征不互斥程度進行衡量,稱之為沖突比率,當這個值較小時,我們可以選擇把不完全互斥的兩個特征捆綁,而不影響最后的精度。這樣,構建直方圖時的時間復雜度就從:
O(數據數量×特征數量)O(數據數量 \times 特征數量) O(數據數量×特征數量)
變為:
O(數據數量×捆綁特征數量)O(數據數量\times捆綁特征數量) O(數據數量×捆綁特征數量)
顯然捆綁特征數量遠少于特征數量,可以達到加速的效果。
說到這里,會遇到兩個問題:
1,解決哪些特征應該綁在一起
LightGBM的EFB算法將這個問題轉化為圖著色的問題來求解,將所有的特征視為圖的各個頂點,將不是相互獨立的特征用一條邊連接起來,邊的權重就是兩個相連接的特征的總沖突值,這樣需要綁定的特征就是在圖著色問題中要涂上同一種顏色的那些點(特征)。舉個例子:
此外,我們注意到通常有很多特征,盡管不是100%相互排斥,但也很少同時取非零值。上面這個過程的時間復雜度其實是O(特征數2)O(特征數^2 )O(特征數2)的,因為要遍歷特征,每個特征還要遍歷所有的簇, 在特征不多的情況下還行,但是如果特征維度很大,就不好使了。 所以為了改善效率,可以不建立圖,而是將特征按照非零值個數進行排序,因為更多的非零值的特征會導致更多的沖突,所以跳過了上面的第一步,直接排序然后第三步分簇。
EFB 算法流程如下:
算法允許兩兩特征并不完全互斥來增加特征捆綁的數量,通過設置最大沖突比率 γ\gammaγ 來平衡算法的精度和效率。
這樣哪些特征捆綁的問題就解決了。
2,解決怎么把特征綁為一捆
特征合并算法,其關鍵在于原始特征能從合并的特征中分離出來。綁定幾個特征在同一個bundle里需要保證綁定前的原始特征的值可以在bundle中識別,考慮到直方圖算法將連續的值保存為離散的bins,我們可以使得不同特征的值分到bundle中的不同桶中,這可以通過在特征值中加一個偏置常量來解決。比如,我們在bundle中綁定了兩個特征A和B,A特征的原始取值為區間 [0,10),B特征的原始取值為區間[0,20),我們可以在B特征的取值上加一個偏置常量101010,將其取值范圍變為[10,30),綁定后的特征取值范圍為 [0,30),這樣就可以放心的融合特征A和B了。
例如對于特征A和B在1和2的部分有重疊,則可以通過“右移”特征B來避免重疊:
通過EFB,許多有少量沖突的特征就被捆綁成了更少的密集特征,這個大大減少的特征的數量,對訓練速度又帶來很大的提高。利用這種思路,可以通過對某些特征的取值重新編碼,將多個這樣互斥的特征捆綁成為一個新的特征。有趣的是,對于類別特征,如果轉換成onehot編碼,則這些onehot編碼后的多個特征相互之間是互斥的,從而可以被捆綁成為一個特征。因此,對于指定為類別型的特征,LightGBM可以直接將每個類別取值和一個bin關聯,從而自動地處理它們,而無需預處理成onehot編碼。
具體的特征合并算法如下所示:
LightGBM的工程優化
1,原生支持類別特征
LightGBM 是原生支持類別變量的模型,其他的大多數模型例如LR,SVM等都需要先將類別特征利用獨熱編碼轉化為多維0/1特征再輸入到模型。對于基于決策樹的算法而言,不推薦使用獨熱編碼,會存在下面的問題:
LightGBM 原生支持類別特征,采用 many-vs-many(每次將若干個類作為正類,若干個其他類作為負類) 的切分方式將類別特征分為兩個子集,實現類別特征的最優切分。
例如當X=A∣∣X=CX=A || X=CX=A∣∣X=C時,放到左孩子,不符合條件的放到右孩子。數據會被切分到兩個比較大的空間,進一步的學習也會更好。
具體而言,在枚舉類別變量的分割點之前,先把直方圖按照每個類別對應的所有樣本的label計算均值(sum(y)count(y)\frac{sum(y)}{count(y)}count(y)sum(y)?),然后進行排序:
最后按照排序的結果依次枚舉最優分割點。這個方法很容易過擬合,所以LightGBM里面還增加了很多對于這個方法的約束和正則化。 實驗結果證明,這個方法可以使訓練速度加速8倍。
2,高效并行
LightGBM支持三個角度的并行:特征并行,數據并行和投票并行。 下面我們一一來看看:
特征并行
特征并行的主要思想是:不同機器在不同的特征子集上分別尋找最優的分割點,然后在機器間同步最優的分割點。XGBoost使用的就是這種特征并行方法。這種特征并行方法有個很大的缺點:就是對數據進行垂直劃分,每臺機器所含數據不同,然后使用不同機器找到不同特征的最優分裂點,劃分結果需要通過通信告知每臺機器,增加了額外的通信和同步上的復雜度。
LightGBM 則不進行數據垂直劃分,而是在每臺機器上保存全部訓練數據,在得到最佳劃分方案后可在本地執行劃分而減少了不必要的通信。具體過程如下圖所示。
數據并行
傳統的數據并行策略主要為水平劃分數據,讓不同的機器先在本地構造直方圖,然后進行全局的合并,最后在合并的直方圖上面尋找最優分割點。這種數據劃分有一個很大的缺點:通訊開銷過大。如果使用點對點通信,一臺機器的通訊開銷大約為:
O(機器數量?特征數量?分桶數量)O(機器數量?特征數量?分桶數量) O(機器數量?特征數量?分桶數量)
如果使用集成的通信,則通訊開銷為:
O(2?特征數量?分桶數量)O(2?特征數量?分桶數量) O(2?特征數量?分桶數量)
LightGBM在數據并行中使用分散規約 (Reduce scatter) 把直方圖合并的任務分攤到不同的機器,降低通信和計算,并利用直方圖做差,進一步減少了一半的通信量。具體過程如下圖所示:
投票并行
基于投票的數據并行進一步優化數據并行中的通信代價,使得通信代價為常數級別。具體而言,該方法只合并部分特征的直方圖從而達到降低通信量的目的,可以得到非常好的加速效果。具體過程如下圖所示。大致步驟為兩步:
本地找出 Top K 特征,并基于投票篩選出可能是最優分割點的特征;
合并時只合并每個機器選出來的特征。
3,Cache命中率優化
XGBoost對cache優化不友好,如下圖所示。在預排序后,特征對梯度的訪問是一種隨機訪問,并且不同的特征訪問的順序不一樣,無法對cache進行優化。同時,在每一層長樹的時候,需要隨機訪問一個行索引到葉子索引的數組,并且不同特征訪問的順序也不一樣,也會造成較大的cache miss。
LightGBM 所使用直方圖算法對 Cache 天生友好:
首先,所有的特征都采用相同的方式獲得梯度(區別于XGBoost的不同特征通過不同的索引獲得梯度),只需要對梯度進行排序并可實現連續訪問,大大提高了緩存命中率;
其次,因為不需要存儲行索引到葉子索引的數組,降低了存儲消耗,而且也不存在 Cache Miss的問題。
LightGBM的優缺點
優點
這部分主要總結下 LightGBM 相對于 XGBoost 的優點,從內存和速度兩方面進行介紹。
(1) 速度更快:
- LightGBM 采用了直方圖算法將遍歷樣本轉變為遍歷直方圖,極大的降低了時間復雜度;
- LightGBM 在訓練過程中采用單邊梯度算法過濾掉梯度小的樣本,減少了大量的計算;
- LightGBM 采用了基于 Leaf-wise 算法的增長策略構建樹,減少了很多不必要的計算量;
- LightGBM 采用優化后的特征并行、數據并行方法加速計算,當數據量非常大的時候還可以采用投票并行的策略;
- LightGBM 對緩存也進行了優化,增加了緩存命中率;
(2) 內存更小:
- LightGBM 采用了直方圖算法將存儲特征值轉變為存儲 bin 值,且不需要特征值到樣本的索引,降低了內存消耗;
- LightGBM 在訓練過程中采用互斥特征捆綁算法減少了特征數量,降低了內存消耗。
缺點
- 可能會長出比較深的決策樹,產生過擬合。因此LightGBM在Leaf-wise之上增加了一個最大深度限制,在保證高效率的同時防止過擬合;
- Boosting族是迭代算法,每一次迭代都根據上一次迭代的預測結果對樣本進行權重調整,所以隨著迭代不斷進行,誤差會越來越小,模型的偏差(bias)會不斷降低,所以會對噪點較為敏感;
- 在尋找最優解時,依據的是最優切分變量,沒有將最優解是全部特征的綜合這一理念考慮進去;
參考文章
深入理解LightGBM
白話機器學習算法理論+實戰番外篇之LightGBM
總結
以上是生活随笔為你收集整理的LightGBM 相关知识理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PHP usleep() 函数
- 下一篇: LightGBM 重要参数、方法、函数理