從提升樹到 XGBoost, 原理簡介
提升樹 (Boosting Trees)
提升樹是以分類樹或迴歸樹為基本分類器的提升方法, 模型表示為決策樹的加法模型:
\[ F_M(x) = \sum_{m=0}^M f(x;\Theta_m), \]
其中 \(M\) 為樹的個數, \(f(x;\Theta_m)\) 表示決策樹, \(\Theta_m\) 為其引數.
1. 提升樹演算法
提升樹演算法採用向前分步 (forward stagewise) 演算法 (本質上是一種貪心演算法). 對於訓練資料集 \(D = \{(x_i, y_i) \}_{i=1}^N\), 首先確定初始提升樹 \(F_0(x)=0\), 然後第 \(m\) 步的模型是
\[ F_m(x) = F_{m-1}(x) + f(x;\Theta_m), \]
其中 \(F_{m-1}\) 為當前模型, 通過經驗風險最小化確定下一顆決策樹的引數,
\[ \hat\Theta_m = \operatorname*{argmin}_{\Theta_m}\sum_{i=1}^N l(y_i, F_{m-1}(x_i) + f(x_i;\Theta_m)), \]
其中 \(l\) 為損失函式.
迴歸問題的提升樹可以表示為簡單函式
\[ f(x;\Theta) = \sum_{j=1}^T c_j 1_{R_j}(x), \]
其中 \(R_j\) 互不相交, \(1_{R_j}(x)\) 為示性函式, \(c_j\) 為常數, 引數 \(\Theta=\{(R_j,c_j)\}_{j=1}^T\), \(T\) 為葉節點個數.
綜上,
迴歸問題的提升樹演算法
- 初始化 \(F_0(x) = 0\).
- 對 \(m = 1,\dots,M\): (a) 通過經驗風險最小化學習一個迴歸樹 \(f(x;\Theta_m)\). (b) 更新 \(F_m(x) = F_{m-1}(x) + f(x;\Theta_m)\).
- 輸出迴歸問題提升樹 \(F_M(x) = \sum_{m=1}^M f(x;\Theta_m)\).
2. 梯度提升樹 (Gradient Boosting Decision Trees)
對於一般的損失函式, 每一步經驗風險最小化都不容易. GBDT 是為了便於計算而提出的方法, 它的主要想法來自於梯度下降法.
記損失函式的梯度在當前模型的值為
\[ g_{im} = \left(\frac{\partial l(y_i,F(x_i))}{\partial F(x_i)}\right)_{F = F_{m-1}}. \]
則由梯度下降法,
\[ F_m(x_i) = F_{m-1}(x_i) -\rho_m g_{im}, \]
其中步長 \(\rho_m\) 可以通過線搜尋獲得, 即
\[ \rho_m = \operatorname*{argmin}_{\rho} \sum_{i=1}^Nl(y_i, F_{m-1}(x_i)-\rho g_{im}). \]
梯度下降法是一個很貪心的演算法, 即在當前點取函式下降最快的方向. 但如上這樣做的話我們只獲得了在訓練資料點上的預測, 為了得到可以預測新資料的決策樹, 一種可行的做法是, 用 \(f\) 逼近負梯度方向, ESL [2] p. 321 使用了平方誤差來度量 \(f\) 與負梯度的距離, 即
\[ \tilde\Theta_m = \operatorname*{argmin}_{\Theta}\sum_{i=1}^N (-g_{im} - f(x_i;\Theta))^2. \]
注意到對於 \(l(x,y) = \frac12(x-y)^2\) 的情形, \(\tilde\Theta_m\) 與 \(\hat\Theta_m\) 相等.
其餘操作同提升樹演算法, 從略.
3. XGBoost
3.1. 總體框架
XGBoost 的主要想法是, 除了原有的損失函式, 在目標函式中加入正則項, 利用二階 Taylor 近似代替目標函式再求極值 (回憶之前的梯度提升樹只用了一階導數), 其餘操作同提升樹演算法.
記 \(F_m(x_i) = \hat y_i^{(m)}\),
\(\hat y_i^{(0)}=0\),
\(\hat y_i^{(m)} = \hat y_i^{(m-1)} + f_m(x_i)\), \(m=1,\dots,M\).
\(f_m\) 表示第 \(m\) 輪時所得的樹, 是由最小化目標函式而得; \(\hat y_i^{(m)}\) 表示第 \(m\) 輪時 \(y_i\) 的預測值.
第 \(m\) 輪目標函式為
\[ \mathrm{Obj}^{(m)} = \sum_{i=1}^N l\left(y_i, \hat y_i^{(m-1)} + f_m(x_i)\right) + \Omega(f_m), \]
其中 \(l\) 為損失函式; \(\Omega\) 為正則項, 是人為定義的複雜度, 可以降低模型複雜度, 減小過擬合的風險, 在原論文 [3] 中定義為
\[ \Omega(f) = \gamma T + \frac12 \lambda\sum_{j=1}^T w_j^2, \]
其中 \(\gamma\), \(\lambda\) 為引數, \(T\) 為 \(f\) 表示的數的葉節點數, \(w_j\) 為第 \(j\) 個葉節點的預測值 (權重).
除了加入正則項外, 還可以通過 shrinkage 來降低過擬合風險, 即 \(F_m = F_{m-1} + \nu f_m\), 其中 \(0<\nu<1\), 可以看為學習率, 原來的做法相當於取 \(\nu=1\). 這麼做主要的理由是減少每顆樹對總模型的影響, 防止前幾顆樹擬合地太好 (過擬合) 以至於後面的樹沒有了學習空間.
3.2. 尋找分裂點
對目標函式做二階 Taylor 展開, 略去更高階的無窮小量. 記
\[ g_i = \frac{\partial l(y_i, \hat y_i^{(m-1)})}{\partial{\hat y^{(m-1)}}},\quad h_i = \frac{\partial^2 l(y_i, \hat y_i^{(m-1)})}{\partial{\left(\hat y^{(m-1)}\right)^2}}. \]
例如對於平方損失函式 \((y_i - \hat y_i^{(m-1)})^2\) 來說, \(g_i = 2(\hat y_i^{(m-1)} - y_i)\), \(h_i = 2\).
經過簡單的推導可得最優權重為
\[ w_j^* = -\frac{\sum_{i\in I_j}g_i}{\sum_{i\in I_j} h_i + \lambda}, \]
其中 \(I_j\) 表示被歸到第 \(j\) 個葉節點的全體例項 (的腳標) 集合.
一般而言, 窮舉所有可能的樹結構是不可能的, 作為代替, 我們考慮貪心演算法. 從一個葉節點開始二分, 假設 \(I_L\) 和 \(I_R\) 分別表示分裂後歸為左節點和右節點的例項集合, 記 \(I = I_L \cup I_R\), 則易得分裂後的目標函式減少值 (loss reduction) 為
\[ \frac12\left[\frac{(\sum_{i\in I_L}g_i)^2}{\sum_{i\in I_L} h_i + \lambda} + \frac{(\sum_{i\in I_R}g_i)^2}{\sum_{i\in I_R} h_i + \lambda} - \frac{(\sum_{i\in I}g_i)^2}{\sum_{i\in I} h_i + \lambda}\right] - \gamma \]
把上式第一項視為 gain, 則 \(\gamma\) 相當於設定了分裂所需的最小的 gain, 起到了剪枝的作用.
尋找分裂點的精確貪心演算法 (原文 [3] 中本段有一些 typo)
輸入: \(I\), 當前節點的例項集; \(d\), 特徵維數 (個數).
初始化 \(\mathrm{gain} \leftarrow 0\)
\(G \leftarrow \sum_{i\in I}g_i\), \(H \leftarrow \sum_{i\in I}h_i\)
for \(k=1\) to \(d\) do
\(G_L \leftarrow 0\), \(H_L \leftarrow 0\)
for \(j\) in sorted (\(I\), by \(x_{jk}\)) do
\(G_L\leftarrow G_L + g_j\), \(H_L\leftarrow H_L + h_j\)
\(G_R \leftarrow G-G_L\), \(H_R \leftarrow H- H_L\)
\(\mathrm{gain} \leftarrow \max(\mathrm{gain}, \frac{G_L^2}{H_L + \lambda} + \frac{G_R^2}{H_R + \lambda} - \frac{G^2}{H + \lambda})\)
輸出: 最大 gain 的分裂
精確貪心法是在所有特徵上, 對所有可能的分裂點都進行遍歷, 在資料量大的時候是不現實的. 一個簡單的近似方法是, 排序後 (for \(j\) in sorted (\(I\), by \(x_{jk}\)) 那步), 取適當的分位數作為分裂候選點進行貪心演算法.
3.3. 稀疏資料的分裂點尋找 (sparsity-aware)
主要分為兩種情況:
- 若資料有缺失值, 則把缺失值都歸到同一個節點. 這是處理缺失值的常用方法之一 (另一種常用方法是用適當的方法填補缺失值), 這使得 XGB 可以直接訓練和預測帶有缺失值的資料.
- 若資料稀疏 (比如 one-hot 編碼, 使得資料包含大量的 0), 則把 0 當做缺失值處理. 這個做法的關鍵點在於遍歷時只對非缺失 (非零) 資料遍歷, 在稀疏資料的情況下會大大提高訓練速率.
注: 按照原論文 [3] 的說法, 作者似乎把缺失和稀疏時的 0 都統稱為 "稀疏/缺失", 用同樣的方式處理, [5] 也證實了這一點.
圖中 \(m\) 應該為 \(d\), score 應該為 gain.
3.4. XGB 的優點
XGB 把決策樹的許多啟發式的想法通過最小化目標函式統一起來處理. 除了使用近似演算法, 在系統設計上, 通過並行處理, 優化快取等工程層面上的優化大幅提高了執行速度, 減小了記憶體使用.
References
[1] 李航. (2012). 統計學習方法 (pp. 55-74, 137-153). 北京: 清華大學出版社.
[2] Friedman, J., Hastie, T., & Tibshirani, R. (2001). The elements of statistical learning (pp. 299-344). New York: Springer series in statistics.
[3] Chen, T., & Guestrin, C. (2016). Xgboost: A scalable tree boosting system. Proceedings of the 22nd acm sigkdd international conference on knowledge discovery and data mining (pp. 785-794). ACM.
[4] Introduction to Boosted Trees. (n.d.). Retrieved from https://xgboost.readthedocs.io/en/latest/tutorials/model.html#
[5] Kodi Arfer. XGBoost, missing values, and sparsity. (2018). Retrieved from http://arfer.net/w/xgboost-spars