python數據處理——機器學習樹模型介紹

轉載自從 XGB 到 LGB:美團外賣樹模型的迭代之路 

 

 

XGB、LGB 都是 GBDT 的方法。GB,選擇梯度下降方向,學習弱分類器。GBDT 的弱學習器爲迴歸樹,舉一個例子。這個地方需要預測一個人對電腦、遊戲感興趣的程度,我們需要學習 N 棵樹,每棵樹學習過程當中都有分裂節點,這個時候我們需要選擇 Splite Feature 和對應的 Split Value,比如這個人對應的年齡,以及你是大於 20 還是大於 30,根據這個進行往左走還是往右走,我們會把多棵樹葉子節點上面的 Predict Score 進行組合,得到最終的 Predict Score,這是樹模型簡單的描述。XGB、LGB 只是樹模型的不同實現方式。

XGB 的優點:

 

 

一是針對 Loss 做了二階泰勒展開,收斂速度更加快。

二是引入正側項,進一步防止過擬合。

三是按葉子加和。

四是極小值解析解。

除此以外,這是其他一些特點,它跟傳統的 GBDT 一樣,採用 Layer-wise 的方式生長。接下來看一下落地過程中遇到的一些問題。

 首先是精度方面,採用 leaf-wise 分裂。我們看整個樹的生長方式,整個 XGB 是 layer-wise 生長方式,這是正常的生長方式。這裏面會有什麼小問題呢?它不能保證我當前最優的分裂是被優先進行選擇的。比如說,A 節點它可以進行分裂,B 節點也可以進行分裂,B 節點的分裂收益更加大但高度更加深的話,有可能由於高度限制,按層進行生長方式下它可能不會被生長出來。針對這樣的問題,LGB 就把這種生長方式改成 Leaf-wise,這樣可以做到優先選擇最優的分裂點,Best First。

 

第二個,核心點是計算加速,我們需要進行計算加速的時候要看性能受限在哪塊,樹模型在進行計算的時候,基本上受兩方面因素影響。 

有 N 個 feature,我需要挑選出來一個,每個 feature 內部還有 M 個對應的 Split Value,我需要過一遍 data,這個工作量是非常大的,一個簡單的優化方式是,我可以把我對應的 Split Value 進行分桶,在分裂的時候不需要過每個 date,我們提前做好預先計算就可以了。 

 

 我們再來看一下,在樣本量非常大的時候怎樣進行優化的,這個方法叫 GOSS,假設我們有 N 個 sample,每個 sample 都有梯度值,對我們迭代影響比較大的都梯度值較大的 sample。所以它根據閾值,會分兩部分,LGB 會對梯度值較小的部分進行採樣,這個就是加速發生的地方。這樣的一個優化,特別是針對於樣本非常不均衡的場景下,可能帶來的提升會比較大。例如,聯盟情況下點擊率在千分之幾,這種情況帶來的加速比較大。

 

針對 feature 比較多的,它會把很多 feature 綁在一起,這個背後思考是什麼?因爲我們在進行分裂的時候,我會變 N 個 feature,如果有些 feature 它們共現程度不是很高,我就可以把這幾個 feature 綁在一起。比如 A 和 B 兩個共現程度不高,在分裂的時候可以把這兩個 feature 綁定在一起考慮。整個算法分爲四步,首先構建一個無向圖,根據任意兩個 feature 之間的衝突或貢獻的次數,在這個無向圖裏我們會根據度進行降序排列。如果加入一個新 feature 的時候,它對應的衝突個數大於一個閾值,我們就新開一個 bundle。

接下來,看一下網絡通信部分。

我們在 XGB 裏面需要進行網絡通信的有哪幾塊?第一個是直方圖求和部分。第二個是每個 worker 找到了分裂點,我們怎麼樣尋找全局的最優的分裂點,這兩部分都需要進行網絡通信。XGB 對應的方法是有容錯的 AllReduce。比如說,我們需要進行求和的時候,這是最經典的方案,假設我們有七個節點,每個節點會收集它的左孩子和右孩子傳過來的結果,同時會跟自己的本身的結果進行求和,這樣的話就得到整個左子樹的和,他會把這樣的結果再去往上發,最終到根節點,他就可以知道全局節點的和是多少,這是 bottom up 的過程。

 第二步,把這個結果告訴其他的 worker,這是一個 bottom down 的過程。每個節點接受父親節點的結果,同時往下進行分發。AllReduce 的方式是大規模分佈式的 Batch Learning 中最常用的通信方式。接下來我們看看 LGB 針對這塊做了哪些優化。

 

並行化,分爲五步:

第一步,使用本地數據計算特徵分桶,並將特徵值壓縮爲 int 桶號;

第二步,本地計算所有特徵的直方圖,通過 reducescatter 得到每個 work 分配到的那兩個特徵的全局直方圖;

第三步,每個 work 求出本地最優分裂(最優分裂節點,分裂的特徵,以及特徵的分裂閾值);通過全局歸約得到全局最優分裂;

第四步,每個 work 根據全局最優分裂對本地模型進行分裂。計算葉子節點的權重;

第五步,重新計算分裂出的葉子節點的直方圖,重複 2,3,4 步驟直到收斂;

計算特徵分桶是求直方圖的先決條件,給定每個特徵要分成的桶數,計算每桶的特徵值上下界。在分佈式計算中,每個 work 計算自己負責的特徵的 bin_mapper,之後再通過 allreduce 得到所有特徵的 bin_mapper。最後可以根據 bin_mapper,將 float 的特徵值映射到 bin 中,就可以用 int 桶編號來表示。

本地建立所有特徵的直方圖,每個特徵直方圖可以通過遍歷已經編碼爲 bin_id 的數據快速實現,最終得到 6 個直方圖,包含樣本數,以及樣本的一階二階導數之和。

合併全局直方圖,傳統的 xgboost 在這裏使用一個 allreduce 操作使每個 work 都同步了 6 個全局直方圖。但實際上每個 worker 其實只需要關心自己分得的特徵的直方圖,這裏 lightGBM 對 AllReduce 操作進行優化,使用 ReduceScatter 算法,最終每個 worker 只得到自己分配到的特徵的直方圖,有效的降低了數據通信量,提高了訓練效率。

 

舉例:4 個 worker,R0-R3,總共 8 個特徵,每個 work 已經計算完本地直方圖,下面要進行合併直方圖。

通過遞歸調用,一半一半的進行歸約操作,優點是大大減少了通信量,不需要向 AllReduce 每次都需要傳遞所有的信息,這個通信量是巨大的,缺點是隻能用於 2^k worker,後面我們會講到針對這一問題的解決方案。

 

每個 worker 得到分配到的兩個的特徵的全局直方圖之後,即開始在本地遍歷葉子節點和特徵,計算本地的最大分裂增益及分列方式。

之後 xgboost 通過 allreduce 方式計算全局最優分裂,而 lgb 採用 allgather+ 本地 reduce 操作得到全局最優分裂

 

接下來我們看下 AllGather:

  1. 整個 AllReduce 有兩個過程 bottom up 和 bottom down, 完成本地計算之後,我們需要把多個本地計算結果挑出全局最優特徵,這個在 XGB 裏面也是基於 all reduce 實現的。在 LGB 裏面使用 all Gather。假設我們有四個節點,第一步 R1 給 R0,第二步 R2 同步給 R0,這個時候 R0 的 V0 是自己的,V2、V3 從 R2 拿的,五個節點的內容 R0 知道了四個。最終通過補全的方式,比如 R4 把剩下的幾個結果一一補到對應的位置上去,通過這樣的過程,每個節點都知道整個集羣裏面我對應的結果是怎麼樣。然後我在本地做一次 reduce 操作,這樣每個節點都知道全局的結果。

  2. 在存儲和計算上面,AllGather 和 AllReduce 相比有什麼不一樣呢:存儲上,左節點接收一個,右節點接收一個,累計的結果往上傳,看過 AllReduce 代碼後我們會發現這個只需要一份內存 +Buff 的空間,比較節約空間,AllGather 呢?只要有幾個節點,我就會存儲幾個結果;計算上,和 AllReduce 對比,少一次 BottomDown 廣播;綜上 AllGather 會更加吃內存、速度會更加快,而樹模型本身佔的空間並不大,這個也是 AllGather 在樹模型上適用的原因。

 

 

我們再看 LGB 易用性。傳統的類別特徵(見 PPT),可指定忽略。這樣的特點在實際業務應用過程會帶來什麼樣的好處呢?例如我們要調研多個特徵的時候,我們就可以建一條數據流,通過制定忽略的方式,得到不同特徵的調研結果,這樣可以減少數據流建設的成本。

 在工業界場景下,我們對數據存儲和資源調度也做了一些定製,使用 HDFS 作爲分佈式存儲,使用 Yarn 做資源的調度;

 此外,我們還做了一些其他優化。ReduceScatter 比較強的依賴於我的節點必須是 2 的 N 次方,很多情況下不一定是 2 的 N 次方,針對這種情況我們可以看下速度變化情況,64 個節點情況下直方圖求和的時間,XGB 是 13 毫秒,LGB 是 2 毫秒,如果變成 65,LGB 從 2 毫秒變成 28 毫秒,針對這個可以做一些優化,使用 VirtualRank+ReduceScatter 的方法,使得時間從 28 毫秒降到 3 毫秒。

 

LGB 在分佈式的 valid 是每臺機器加載一份,當數據規模比較大的時候會出現什麼問題?會 OOM,所以一個簡單的方法是我們把對應的 data 進行分佈式拆分。我們再看分佈式 Evaluation 怎麼做,指標分兩種類型:第一種是 LogLoss/RMSE 等,計算完了,求個平均或者累加就可以了;第二種是 AUC,可以簡單求平均,但不夠準確,針對這樣的指標,我們會做特殊的優化,使得分佈式評估會更加準。

 

 

最後我們看一些其他的優化點。

第一個,這裏有兩個工作。RGF,是張潼老師的工作,針對於剛纔 best first 樹的生長方式它又走了一步,我們剛纔說的樹的生長方式只是限制在一棵樹內部,他把這個思路放得更加開闊,比如有已經成型的樹,去進行篩選的時候,不單單考慮我這棵樹自己本身,可以新開一棵樹,這樣可能帶來更好的精度提升。

第二個,每次學一顆樹,學完後,Boosting 的過程中參數是確定的,這個可能不是一個最優的選擇,針對這個問題可以做一個優化,當每次學到一棵樹的時候,把當前的參數以及對應歷史上到現在爲止的參數全部再優化一遍,這是裏面兩個重要的點。但這會帶來一個問題,model 會更加容易 overfit,所以要做結構化,這兩個之間怎麼平衡。第二個工作,南大周志華老師提出了 deep forest,其實也是一種網絡結構的優化。

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