22(4).模型融合---Xgboost


一、簡介

全稱:eXtreme Gradient Boosting

作者:陳天奇

基礎:GBDT

所屬:boosting迭代型、樹類算法

適用范圍:回歸,分類,排序

xgboost工具包:sklearn xgboost鏈接 | xgboost工具包(中文)鏈接 | xgboost工具包(英文)鏈接

論文鏈接 | 項目地址 | ppt

優點:

  • 顯示的把樹模型復雜度作為正則項加到優化目標中。
  • 公式推導中用到了二階導數,用了二階泰勒展開。
  • 實現了分裂點尋找近似算法。
  • 利用了特征的稀疏性。
  • 數據事先排序並且以block形式存儲,有利於並行計算。
  • 基於分布式通信框架rabit,可以運行在MPI和yarn上。
  • 實現做了面向體系結構的優化,針對cache和內存做了性能優化。

缺點:(與LightGBM相比)

  • XGBoost采用預排序,在迭代之前,對結點的特征做預排序,遍歷選擇最優分割點,數據量大時,貪心法耗時,LightGBM方法采用histogram算法,占用的內存低,數據分割的復雜度更低;
  • XGBoost采用level-wise生成決策樹,同時分裂同一層的葉子,從而進行多線程優化,不容易過擬合,但很多葉子節點的分裂增益較低,沒必要進行跟進一步的分裂,這就帶來了不必要的開銷;LightGBM采用深度優化,leaf-wise生長策略,每次從當前葉子中選擇增益最大的結點進行分裂,循環迭代,但會生長出更深的決策樹,產生過擬合,因此引入了一個閾值進行限制,防止過擬合;

二、Xgboost

1.損失函數

xgboost 也是使用與提升樹相同的前向分步算法。其區別在於:xgboost 通過結構風險極小化來確定下一個決策樹的參數 :

最初損失函數:

$L_t=\sum\limits_{i=1}^mL(y_i, f_{t-1}(x_i)+ h_t(x_i)) + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2$ 

在GBDT損失函數$L(y, f_{t-1}(x)+ h_t(x))$的基礎上,加入正則項$\Omega(h_t) = \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2$其中,J是葉子節點的個數,$w_{tj}$是第j個葉子節點的最優值,這里的$w_{tj}$和GBDT中的$c_{tj}$是一個意思,Xgboost論文中用的是w表示葉子的值,這里和論文保持一致。

損失函數的二階展開:

$\begin{align} L_t & = \sum\limits_{i=1}^mL(y_i, f_{t-1}(x_i)+ h_t(x_i)) + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \\ & \approx \sum\limits_{i=1}^m( L(y_i, f_{t-1}(x_i)) + \frac{\partial L(y_i, f_{t-1}(x_i) }{\partial f_{t-1}(x_i)}h_t(x_i) + \frac{1}{2}\frac{\partial^2 L(y_i, f_{t-1}(x_i) }{\partial f_{t-1}^2(x_i)} h_t^2(x_i)) + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \end{align}$

為了方便,記第i個樣本在第t個弱學習器的一階和二階導數分別為:

$g_{ti} = \frac{\partial L(y_i, f_{t-1}(x_i) }{\partial f_{t-1}(x_i)}, \; h_{ti} = \frac{\partial^2 L(y_i, f_{t-1}(x_i) }{\partial f_{t-1}^2(x_i)}$

則損失函數可以表達為:

$L_t \approx \sum\limits_{i=1}^m( L(y_i, f_{t-1}(x_i)) + g_{ti}h_t(x_i) + \frac{1}{2} h_{ti} h_t^2(x_i)) + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2$

第一項是常數,對最小化loss無影響,可以去掉,同時由於每個決策樹的第j個葉子節點的取值最終是同一個值$w_{tj}$,因此損失函數簡化為:

$\begin{align} L_t & \approx \sum\limits_{i=1}^m g_{ti}h_t(x_i) + \frac{1}{2} h_{ti} h_t^2(x_i)) + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \\ & = \sum\limits_{j=1}^J (\sum\limits_{x_i \in R_{tj}}g_{ti}w_{tj} + \frac{1}{2} \sum\limits_{x_i \in R_{tj}}h_{ti} w_{tj}^2) + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \\ & = \sum\limits_{j=1}^J [(\sum\limits_{x_i \in R_{tj}}g_{ti})w_{tj} + \frac{1}{2}( \sum\limits_{x_i \in R_{tj}}h_{ti}+ \lambda) w_{tj}^2] + \gamma J \end{align}$

把每個葉子節點區域樣本的一階和二階導數的和單獨表示如下:

$G_{tj} = \sum\limits_{x_i \in R_{tj}}g_{ti},\; H_{tj} = \sum\limits_{x_i \in R_{tj}}h_{ti}$

最終損失函數的形式可以表示為:

$L_t = \sum\limits_{j=1}^J [G_{tj}w_{tj} + \frac{1}{2}(H_{tj}+\lambda)w_{tj}^2] + \gamma J$

 

問題1:xgboost如何使用MAE或MAPE作為目標函數?參考鏈接

xgboost需要目標函數的二階導數信息(或者hess矩陣),在回歸問題中經常將MAE或MAPE作為目標函數,然而,這兩個目標函數二階導數不存在。

$MAE=\frac{1}{n}\sum_1^n|y_i-\hat y_i|$,$MAPE=\frac{1}{n}\sum _i^n \frac{|y_i-\hat y_i|}{y_i}$

其中,$y_i$是真實值,$\hat y_i$是預測值

方法(1):利用可導的函數逼近MAE或MAPE---MSE、Huber loss、Pseudo-Huber loss

  利用MSE逼近是可以的,但是MSE在訓練初誤差較大的時候,loss是其平方,會使得訓練偏離MAE的目標函數,一般難以達到高精度的要求。

  利用Huber loss進行逼近也可以,但是Huber loss是分段函數,不方便計算,其中$\delta$是可調節參數。

  

  實際采用Huber loss的可導逼近形式:Pseudo-Huber loss function

  

  一階導數:

   

  二階導數:

 

方法(2):自定義二階導數的值:$ln(cosh(x))$

 用$ln(cosh(x))$以及$log(exp(-x) + exp(x))$進行逼近

 

$ln(cosh(x))$的一階導數:$tanh(x)$

$ln(cosh(x))$的二階導數:$1-tanh(x)*tanh(x)$

2.結構分

當$w_{tj}$取最優解的時候,原損失函數對應的表達式為:$L_t = -\frac{1}{2}\sum\limits_{j=1}^J\frac{G_{tj}^2}{H_{tj} + \lambda} +\gamma J$
在推導過程中假設$w_{tj}$與$T,G_j,H_j$無關,這其實假設已知樹的結構。事實上$L_t$是與$T$相關的,甚至與樹的結構相關,因此定義$L_t$為結構分。 結構分刻畫了:當已知樹的結構時目標函數的最小值。

3.尋找分裂節點的候選集

Xgboost框架用tree_method[默認為’auto’] 指定了構建樹的算法,可以為下列的值(分布式,以及外存版本的算法只支持 ‘approx’,’hist’,’gpu_hist’ 等近似算法):

‘auto’: 使用啟發式算法來選擇一個更快的tree_method:
      對於小的和中等的訓練集,使用exact greedy 算法分裂節點
      對於非常大的訓練集,使用近似算法分裂節點
      舊版本在單機上總是使用exact greedy 分裂節點
‘exact’: 使用exact greedy 算法分裂節點
‘approx’: 使用近似算法分裂節點
‘hist’: 使用histogram 優化的近似算法分裂節點(比如使用了bin cacheing 優化)
‘gpu_exact’: 基於GPU 的exact greedy 算法分裂節點
‘gpu_hist’: 基於GPU 的histogram 算法分裂節點

3.1 暴力枚舉(exact greedy)

(1)第一種方法是對現有的葉節點加入一個分裂,然后考慮分裂之后目標函數降低多少。

  • 如果目標函數下降,則說明可以分裂;
  • 如果目標函數不下降,則說明該葉節點不宜分裂。

(2)對於一個葉節點,加入給定其分裂點,定義划分到左子樣本節點的集合為:$\mathbb{I_R}$,則有:

(3)定義葉節點的分裂增益為:

其中,

  • $\frac{G^2_L}{H_L+\lambda}$表示:該葉節點的左子樹的結構分
  • $\frac{G^2_R}{H_R+\lambda}$表示:該葉節點的右子樹的結構分
  • $\frac{G^2}{H+\lambda}$表示:如果不分裂,則該葉節點自身的結構分
  • $-\lambda$表示:因為分裂導致葉節點數量增大1,從而導致增益的下降。

每次只有一個葉節點分裂,因此其他葉節點不會發生變化,因此:

  • 若$Gain>0$,則該葉節點應該分裂;
  • 若$Gain<0$,則該葉節點不宜分裂。

(4)現在的問題是:不知道分裂點,對於每個葉節點,存在多個分裂點,且可能很多分裂點都能帶來增益。

解決辦法:對於葉節點中的所有可能的分裂點進行一次掃描。然后計算每個分裂點的增益,選取增益最大的分裂點作為本葉節點的最優分裂點。

(5)最優分裂點貪心算法

輸入:$D={(X_1,y_1),(X_2,y_2), ...(X_m,y_m)}$,屬於當前葉節點的樣本集的下標集合$\mathbb{I}$

輸出:當前葉節點最佳分裂點

算法:

  step1:初始化 $score \leftarrow 0$,$G \leftarrow_{i\in \mathbb{I}}g_i$,$H \leftarrow_{i\in \mathbb{I}}h_i$

  step2:遍歷各維度 $k=1,2,...,m$:

    a)初始化:$G_L \leftarrow 0,H_L \leftarrow 0$

    b)如果第$k$維特征為連續值,則將當前葉節點中的樣本從小到大排序。然后用$j$順序遍歷排序后的樣本下標。

    

     c)如果第$k$維特征為離散值${a_1,a_2,...,a_{n_k}},設當前葉節點中第$k$維取值$a_j$樣本的下標集合為$\mathbb{I_j}$,則遍歷$j=1,2,...,n_k$:

    

  step3:選取最大的$score$對應的維度和拆分點作為最優拆分點。

分裂點貪心算法嘗試所有特征和所有分裂位置,從而求得最優分裂點。當樣本太大且特征為連續值時,這種暴力做法的計算量太大。

3.2 近似算法(approx)

(1)近似算法尋找最優分裂點時不會枚舉所有的特征值,而是對特征值進行聚合統計,然后形成若干個桶。然后僅僅將桶邊界上的特征的值作為分裂點的候選,從而獲取計算性能的提升。

(2)對第k個特征進行分桶,分桶的數量l就是所有樣本在第k個特征上的取值的數量。

  如果第k個特征為連續特征,則執行百分位分桶,得到分桶的區間為:$S_k={s_{k,1},s_{k,2},...,s_{k,l}}$,其中$s_{k,1}<s_{k,2}<...<s_{k,l}$,分桶的數量、分桶的區間都是超參數,需要仔細挑選

  如果第k個特征為離散特征,則執行按離散值分桶,得到的分桶為:$S_k={s_{k,1},s_{k_2},...,s_{k,l}}$,其中,$s_{k,1}<s_{k,2}<...<s_{k,l}$ 為第k個特征的所有可能的離散值。

(3)最優分裂點近似算法

算法流程:

輸入:數據集$D={(X_1,y_1),(X_2,y_2),...,(X_N,y_N)}$,屬於當前葉結點的樣本集的下標集合$\mathbb{I}$

輸出:當前葉節點最佳分裂點

step1:對每個特征進行分桶。假設對第k個特征上的值進行分桶為:$S_k={s_{k,1},s_{k,2},...,s_{k,l}}$,如果第k個特征為連續特征,則要求滿足$s_{k,1}<s_{k,2}<...<s_{k,l}$

step2:初始化:$score \leftarrow 0,G\leftarrow \sum_{i\in \mathbb{I}} g_i ,H\leftarrow \sum_{i\in \mathbb{I}} h_i$

step3:遍歷各維度:$k=1,...,n$

    初始化:$G_L \leftarrow 0,H_L \leftarrow 0$

    遍歷各拆分點,即遍歷$j=1,2,...,l$:

      如果是連續特征,即設葉節點的樣本中,第k個特征取值在區間$(s_{k,j},s_{k,j+1}]$的樣本的下標集合為$\mathbb{I}_j$,則:

           

      如果是離散特征,則設葉結點的樣本中,第k個特征取值等於$s_{k,j}$的樣本的下標集合為$\mathbb{I}$ ,則:

            

    選取最大的score對應的維度和拆分點作為最優拆分點。

(4)分桶有兩種模式:

全局模式:在算法開始時,對每個維度分桶一次,后續的分裂都依賴於該分桶並不再更新;

  優點:只需要計算一次,不需要重復計算;

  缺點在經過多次分裂之后,葉節點的樣本有可能在很多全局桶中是空的。 

局部模式:每次拆分之后再重新分桶;

  優點:每次分桶都能保證各桶中的樣本數量都是均勻的;

  缺點:計算量較大。

全局模式會構造更多的候選拆分點,而局部模式會更適合構造構造更深的樹。

(5)分桶時的桶區間間隔大小是個重要的參數。區間間隔越小,則桶越多,划分的越精細,候選的拆分點就越多。

3.2.1 Quantile

(1)$\phi$-quantile

Quantile就是ranking。如果有$N$個元素,那么$\phi$-quantile就是指rank在$⌊\phi × N⌋$的元素。例如$S=[11,21,24,61,81,39,89,56,12,51]$,首先排序為$[11,12,21,24,39,51,56,61,81,89]$,則$0.1-quantile=11, 0.5-quantile=39$. 上面的是exact quantile尋找方法,如果數據集非常大,難以排序,則需要引入$\epsilon-approximate \phi-quantiles$

該方法為離線算法(所有的數必須要排序,再找分位點),是不適用於數據流的。

(2)$\epsilon$-approximate $\phi$-quantiles

$\phi$-quantile是在區間$[⌊(\phi−\epsilon)×N⌋,⌊(\phi+\epsilon)×N⌋]$

  當$N$增加時,$φ$-quantile的“正確”答案($\epsilon$-近似)的集合增加。因此,您可以從輸入流中刪除一些元素,並仍保留ε近似分位數查詢的正確答案(=詢問$\epsilon$近似分位數的查詢)

  回到XGBoost的建樹過程,在建立第i棵樹的時候已經知道數據集在前面i−1棵樹的誤差,因此采樣的時候是需要考慮誤差,對於誤差大的特征值采樣粒度要加大,誤差小的特征值采樣粒度可以減小,也就是說采樣的樣本是需要權重的。

重新審視目標函數:

$$\begin{equation} \sum_{i=1}^n [g_i f_t(x_i) + \frac{1}{2} h_i f_t^2(x_i)] + \Omega(f_t) \end{equation}$$

通過配方可以得到

$$\begin{equation} \sum_{1}^n \left[ \frac {1}{2} h_i \left( f_t(x_i) - (-g_i/h_i)\right)^2 \right] + \Omega (f_t) + constant \end{equation}$$

因此可以將該目標看做是第$m$棵決策樹,關於真實標簽為$-\frac{g_i}{h_i}$和權重為$h_i$的、損失函數為平方損失的形式。

3.2.2 Weighted Quantile Sketch

(1)二階導數$h$為權重的解釋

如果損失函數是Square loss,即$Loss(y, \widehat y) = (y - \widehat y)^2$,則$h=2$,那么實際上是不帶權;如果損失函數是Log Loss,則$h=pred * (1-pred)$。這是個開口朝下的一元二次函數,所以最大值在0.5。當$pred$在0.5附近,這個值是非常不穩定的,很容易誤判,h作為權重則因此變大,那么直方圖划分,這部分就會被切分的更細。

(2)問題轉換

  假設候選樣本的第k維特征,及候選樣本的損失函數的二階偏導數為:$\begin{equation} D_k = \{(x_{1k}, h_1), (x_{2k}, h_2), \cdots (x_{nk}, h_n)\} \end{equation}$

  定義排序函數:$x_{i,k}$表示樣本$x_i$的第$k$個特征

  

  它刻畫的是:第$k$維特征小於$z$的樣本的$h$之和,占總的$h$之和的比例,其中二階導數$h$可以視為權重,在這個排序函數下,找到一組點$\{ s_{k1}, s_{k2}, ... ,s_{kl} \}$,滿足:$% <![CDATA[ \begin{equation} | r_k (s_{k,j}) - r_k (s_{k, j+1}) | < \varepsilon \end{equation} %]]>$。

  其中,${s_{k1}} = \mathop {\min }\limits_i {x_{ik}},{s_{kl}} = \mathop {\max }\limits_i {x_{ik}}$,$\epsilon$為采樣率,直觀上理解,最后會得到$1/{\epsilon}$個分界點。其中$x_{i,k}$表示樣本$x_i$的第$k$個特征,即:

  最小的拆分點:所有樣本第$k$維的最小值;

  最大的拆分點:所有樣本第$k$維的最大值;

  中間的拆分點:選取拆分點,使得相鄰拆分點的排序函數值小於$\epsilon$(分桶的桶寬)。其意義為:第$k$維大於等於$s_{k,j}$,小於$s_{k,j+1}$的樣本的$h$之和,占總的$h$之和的比例小於$\epsilon$;這種拆分點使得每個桶內的以$h$為權重的樣本數量比較均勻,而不是樣本個數比較均勻。

舉例:

 要切分為3個,總和為1.8,因此第1個在0.6處,第2個在1.2處。

  對於每個樣本都有相同權重的問題,有quantile sketch算法解決該問題,作者提出Weighted Quantile Sketch算法解決這種weighted datasets的情況

(3)Weighted Quantile Sketch算法

問題:To design an algorithm, you must first design an adequate data structure to maintain the information used by the algorithm

a) [$v_i,min_i,max_i$]

 

 

 

該數據結構需要每插入值進行大量操作。 雖然它很有用,但效率不高

b) [$v_i,g_i$]

 

 這個數據結構存在問題:它不包含足夠的信息來刪除不必要的條目 

c) Greenwald & Khanna's 算法:[$v_i,g_i,\triangle_i$]

 定義:

 

$v_0$=目前為止遇到的最小的數

$v_{s-1}$=目前為止遇到的最大的數

三個性質:

  • 性質1:$r_{min}(v_i) = \sum_{j=0}^{i}g_j$

  • 性質2:$r_{max}(v_i) = \sum_{j=0}^{i}g_j+\triangle_i$

  • 性質3:$g_0+g_1+...+g_{s-1}=N$

舉例:

命題1:summary達到的准確度,誤差$e=max_{all i}(g_i+\triangle_i)/2$

推論1:Greenwald和Khanna算法的不變性

GK算法框架:先判斷是否要合並,再插入

 插入算法:

  • Inserting an arriving value must maintain the consistency of the information in the summary

證明

刪除算法:

 how to use the quantile summary?

  • The quantile summary is used to answer quantile queries 
  • Given a ε-approximate quantile summary, how do we use it to answer a quantile queries ?

 

3.3 直方圖算法(hist

直方圖聚合是樹木生長中的主要計算瓶頸。我們引入了一種新的樹生長方法hist,其中只考慮了可能的分裂值的子集。與FastBDT和LightGBM一樣,連續特征被分成不連續的區域。由於較少的索引操作,直方圖累積變得更快

新方法與tree_method = approx有何不同?

  • xgboost中現有的近似分裂方法還將連續特征存儲到離散區間以加速訓練。 approx方法為每次迭代生成一組新的bin,而hist方法在多次迭代中重用bin。

hist方法可以實現approx方法無法實現的額外優化,如下所示:

  • 箱的緩存:用bin ID替換訓練數據中的連續特征值並緩存數據結構
  • 直方圖減法技巧:為了計算一個節點的直方圖,我們簡單地取其父和兄弟的直方圖之間的差異。

除了上述改進之外,還有一些亮點

  • 自然支持稀疏矩陣的有效表示,如xgboost,稀疏矩陣,混合稀疏+密集矩陣的有效加速
  • 可擴展到xgboost中的其他現有功能,例如單調約束,語言綁定。

如何使用?

  • 只需將tree_method設置為hist即可。您可能還需要設置max_bin,它表示存儲連續特征的(最大)離散區間數。默認情況下,max_bin設置為256.增加此數字可以提高分割的最佳性,但代價是計算時間較長。

 

 

4.缺失值

(1)真實場景中,有很多可能導致產生稀疏。如:數據缺失、某個特征上出現很多 0 項、人工進行 one-hot 編碼導致的大量的 0。

  • 理論上,數據缺失和數值0的含義是不同的,數值 0 是有效的。
  • 實際上,數值0的處理方式類似缺失值的處理方式,都視為稀疏特征。
  • 在xgboost 中,數值0的處理方式和缺失值的處理方式是統一的。這只是一個計算上的優化,用於加速對稀疏特征的處理速度。
  • 對於稀疏特征,只需要對有效值進行處理,無效值則采用默認的分裂方向。

  注意:每個結點的默認分裂方向可能不同。

(2)在xgboost 算法的實現中,允許對數值0進行不同的處理。可以將數值0視作缺失值,也可以將其視作有效值。 如果數值0是有真實意義的,則建議將其視作有效值。

(3)缺失值處理算法

輸入:數據集$D={(X_1,y_1),(X_2,y_2),...,(X_N,y_N)}$

   屬於當前葉結點的樣本集的下標集合$\mathbb{I}$

   屬於當前葉節點,且第$k$維特征有效的樣本的下標集合$\mathbb{I}_k = \{{i\in \mathbb{I}| x_{k,i}\neq missing}\}$

輸出:當前葉節點最佳分裂點

step1:初始化:$score \leftarrow 0,G\leftarrow \sum_{i\in \mathbb{I}} g_i ,H\leftarrow \sum_{i\in \mathbb{I}} h_i$

step3:遍歷各維度:$k=1,...,n$

      先從左邊開始遍歷:

      初始化:$G_L \leftarrow 0,H_L \leftarrow 0$

      遍歷各拆分點:沿着第$k$維,將當前有效的葉節點的樣本從小到大排序。這相當於所有無效特征值的樣本放在最右側,因此可以保證無效的特征值都在右子樹。然后用$j$順序遍歷排序后的樣本下標:

      

 

     再從右邊開始遍歷:

      初始化:$G_R \leftarrow 0,H_R \leftarrow 0$

      遍歷各拆分點:沿着第$k$維,將當前有效的葉節點的樣本從大到小排序。這相當於所有無效特征值的樣本放在最左側,因此可以保證無效的特征值都在左子樹。然后用$j$逆序遍歷排序后的樣本下標:

      

    選取最大的score對應的維度和拆分點作為最優拆分點。

缺失值處理算法中,通過兩輪遍歷可以確保稀疏值位於左子樹和右子樹的情形。

5. 其他優化

5.1 正則化

xgboost在學習過程中使用了如下的正則化策略來緩解過擬合:

  • 通過學習率$v$來更新模型:$f_m(x)=f_{m-1}(x)+vh_m(x;\theta_m)$,$0<v<=1$,也叫shrinkage
  • 類似於隨機森林,采取隨機屬性選擇,也叫col_sample

5.2 計算速度提升

xgboost在以下方面提出改進來提升計算速度:

  • 預排序pre-sorted;
  • cache-aware預取;
  • Out-of-Core大數據集

5.2.1 預排序

(1)xgboost提出column block數據結構來降低排序時間。

  • 每一個block代表一個特征的值,樣本在該block中按照它在該特征的值排好序。這些block只需要在程序開始的時候計算一次,后續排序只需要線性掃描這些block即可。
  • Block中的數據以稀疏格式CSC進行存儲。
  • 由於屬性之間是獨立的,因此在每個維度尋找划分點可以並行計算。

時間復雜度減少:

  • 在Exact greedy算法中,將整個數據集存放在一個Block中。這樣,復雜度從原來的$O(Hd||x||_0logn)$降為$O(Hd||x||_0+||x||_0logn)$,其中$||x||_0$為訓練集中非缺失值的個數。這樣,Exact greedy算法就省去了每一步中的排序開銷。 在近似算法中,使用多個Block,每個Block對應原來數據的子集。不同的Block可以在不同的機器上計算。該方法對Local策略尤其有效,因為Local策略每次分支都重新生成候選切分點。

(2)block可以僅存放樣本的索引,而不是樣本本身,這樣節省了大量的存儲空間。

如:block_1代表所有樣本在feature_1上的從小到大排序:sample_no1,sample_no2,...

其中樣本編號出現的位置代表了該樣本的排序。

可以看出,只需在建樹前排序依次,后面節點分裂時可以直接根據索引得到梯度信息。

5.2.2 預取

(1)由於在column block中,樣本的順序會被打亂,這會使得從導數數組中獲取$g_i$時的緩存命中率較低。

因此,xgboost提出了cache-aware預取算法,對每個線程分配一個連續的buffer,讀取梯度信息並存入Buffer中(這樣就實現了非連續到連續的轉化),然后再統計梯度信息。該方式在訓練樣本數大的時候特別有用,用於提升緩存命中率。

(2)xgboost會以minibatch的方式累加數據,然后在后台開啟一個線程來加載需要用到的導數$g_i$。

這里有個折中:minibatch太大,會引起cache miss;太小,則並行程度較低。

5.2.3 Out-of-Core

(1)xgboost利用硬盤來處理超過內存容量的大數據量,其中使用了下列技術:

  • 使用block壓縮技術來緩解內存和硬盤的數據交換IO:數據按列壓縮,並且在硬盤到內存的傳輸過程中被自動解壓縮;
  • 數據隨機分片到多個硬盤,每個硬盤對應一個預取線程,從而加大“內存-硬盤”交換數據的吞吐量。

 

 

 

參考文獻:

【1】XGBoost算法原理小結

【2】xgboost如何使用MAE或MAPE作為目標函數?

【3】XGBoost解讀(2)--近似分割算法

【4】ε-approximate quantiles

【5】『我愛機器學習』集成學習(三)XGBoost - 細語呢喃

【6】gbdt.pdf

【7】Xgboost系統設計:分塊並行、緩存優化和Blocks for Out-of-core Computation - anshuai_aw1的博客 - CSDN博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM