基于树的集成方法:极端梯度提升(eXtreme Gradient Boosting, XGBoost)的数学原理、目标函数构建与优化求解过程
1. 题目描述
我们需要深入理解极端梯度提升(XGBoost)算法的核心。XGBoost 是一种高效、灵活且强大的梯度提升树(Gradient Boosting Tree)实现。你需要掌握的关键是:XGBoost 如何将原始的目标函数(通常是损失函数+正则化项)通过二阶泰勒展开进行近似,并推导出如何根据这个近似的目标函数,找到最优的树结构(即最优的分裂点)以及计算叶子节点的最优权重。这个过程是 XGBoost 相比传统 GBDT 在效率和效果上取得优势的核心。请详细阐述其数学原理、目标函数的构建、二阶泰勒展开近似、树结构分裂的增益(Gain)计算公式的推导,以及最终的优化求解步骤。
2. 解题过程
步骤1:理解Boosting框架与目标函数
XGBoost 属于加法模型,它通过前向分步算法集成多个决策树(通常为CART回归树)来构建最终模型。假设我们有数据集 \(\{(x_i, y_i)\}_{i=1}^n\), 其中 \(x_i \in R^d\), \(y_i \in R\)。
模型在第 \(t\) 轮迭代后的预测值为:
\[\hat{y}_i^{(t)} = \hat{y}_i^{(t-1)} + f_t(x_i) \]
其中 \(\hat{y}_i^{(0)} = 0\), \(\hat{y}_i^{(t-1)}\) 是前 \(t-1\) 轮的预测(一个常数),\(f_t\) 是第 \(t\) 轮要学习的基学习器(一棵决策树)。
我们的目标是学习函数 \(f_t\) 以最小化一个带正则化的目标函数 \(\mathcal{L}^{(t)}\):
\[\mathcal{L}^{(t)} = \sum_{i=1}^{n} l\left(y_i, \hat{y}_i^{(t-1)} + f_t(x_i)\right) + \Omega(f_t) \]
这里:
- \(l(y_i, \hat{y}_i)\) 是可微的损失函数(如均方误差、逻辑损失等),衡量预测值与真实值的差异。
- \(\Omega(f)\) 是模型复杂度(正则化)项。在XGBoost中,对单棵树 \(f\),其定义为:
\[\Omega(f) = \gamma T + \frac{1}{2} \lambda \sum_{j=1}^{T} w_j^2 \]
$T$ 是树的叶子节点数,$w_j$ 是第 $j$ 个叶子节点的分数(即预测值)。$\gamma$ 是控制叶子节点数量的惩罚系数,$\lambda$ 是 $L_2$ 正则化系数。此正则化项鼓励模型更简单(叶子节点更少,叶子权重更小),防止过拟合。
步骤2:目标函数的二阶泰勒展开近似
为了优化这个目标函数,XGBoost 的关键创新是使用二阶泰勒展开来近似它。我们对损失函数 \(l(y_i, \hat{y}_i^{(t-1)} + f_t(x_i))\) 在点 \(\hat{y}_i^{(t-1)}\) 处进行展开。
首先,定义一阶导数和二阶导数:
\[g_i = \frac{\partial l(y_i, \hat{y}_i^{(t-1)})}{\partial \hat{y}_i^{(t-1)}}, \quad h_i = \frac{\partial^2 l(y_i, \hat{y}_i^{(t-1)})}{\partial (\hat{y}_i^{(t-1)})^2} \]
注意,\(g_i\) 和 \(h_i\) 是常数,因为它们是关于上一轮预测值 \(\hat{y}_i^{(t-1)}\) 计算的。
然后,对损失函数进行二阶泰勒展开:
\[l(y_i, \hat{y}_i^{(t-1)} + f_t(x_i)) \approx l(y_i, \hat{y}_i^{(t-1)}) + g_i f_t(x_i) + \frac{1}{2} h_i f_t^2(x_i) \]
将这个近似代入目标函数 \(\mathcal{L}^{(t)}\) 中,移除常数项 \(l(y_i, \hat{y}_i^{(t-1)})\)(因为它在第 \(t\) 轮优化中是常数),我们得到第 \(t\) 轮的近似目标函数:
\[\tilde{\mathcal{L}}^{(t)} = \sum_{i=1}^{n} [g_i f_t(x_i) + \frac{1}{2} h_i f_t^2(x_i)] + \Omega(f_t) \]
这个近似目标函数是 \(f_t\) 的函数,并且是二次形式,便于我们求解。
步骤3:重新定义目标函数为叶子节点权重的形式
一棵决策树 \(f_t\) 可以被描述为:首先,它将输入 \(x_i\) 通过一系列规则(树的结构)映射到某个叶子节点 \(j\) 上;然后,这个叶子节点输出一个分数 \(w_j\)。即 \(f_t(x_i) = w_{q(x_i)}\),其中 \(q: R^d \rightarrow \{1, 2, ..., T\}\) 是将样本映射到叶子索引的函数。
利用这个定义,我们可以将 \(\tilde{\mathcal{L}}^{(t)}\) 按叶子节点重新组织。定义叶子节点 \(j\) 的样本集合为 \(I_j = \{i | q(x_i) = j\}\)。
\[\begin{aligned} \tilde{\mathcal{L}}^{(t)} &= \sum_{i=1}^{n} [g_i f_t(x_i) + \frac{1}{2} h_i f_t^2(x_i)] + \gamma T + \frac{1}{2} \lambda \sum_{j=1}^{T} w_j^2 \\ &= \sum_{j=1}^{T} [ (\sum_{i \in I_j} g_i) w_j + \frac{1}{2} (\sum_{i \in I_j} h_i + \lambda) w_j^2 ] + \gamma T \end{aligned} \]
为了简化,我们定义:
\[G_j = \sum_{i \in I_j} g_i, \quad H_j = \sum_{i \in I_j} h_i \]
它们分别是映射到叶子 \(j\) 上所有样本的一阶导数和二阶导数之和。
代入后,目标函数变为一个关于叶子权重 \(w_j\) 的二次函数之和:
\[\tilde{\mathcal{L}}^{(t)} = \sum_{j=1}^{T} [G_j w_j + \frac{1}{2} (H_j + \lambda) w_j^2] + \gamma T \]
步骤4:求解最优叶子权重和最小目标值
对于给定的树结构 \(q\) (确定了 \(I_j\), \(G_j\), \(H_j\)),上面这个目标函数 \(\tilde{\mathcal{L}}^{(t)}\) 关于每个 \(w_j\) 是独立的凸二次函数。通过求导并令其为零,可以轻松求出每个叶子节点的最优权重 \(w_j^*\):
\[\frac{\partial \tilde{\mathcal{L}}^{(t)}}{\partial w_j} = G_j + (H_j + \lambda) w_j = 0 \]
解得:
\[w_j^* = -\frac{G_j}{H_j + \lambda} \]
这就是最优的叶子节点分数。
将 \(w_j^*\) 代回目标函数 \(\tilde{\mathcal{L}}^{(t)}\),得到在给定树结构下,可以达到的最小目标值:
\[\tilde{\mathcal{L}}^{(t)*} = -\frac{1}{2} \sum_{j=1}^{T} \frac{G_j^2}{H_j + \lambda} + \gamma T \]
这个值可以作为评估一个树结构好坏的得分。得分越低,说明这个树结构对目标函数的优化越好。
步骤5:寻找最优树结构(分裂准则)
在构建树时,我们从单个节点(包含所有样本)开始,然后尝试所有可能的分裂点,找到能使整体目标函数下降最多的那个分裂点。
假设我们考虑将一个现有的叶子节点(记作包含样本集合 \(I\))分裂成两个子节点 \(I_L\) 和 \(I_R\)。分裂前的目标函数值为:
\[-\frac{1}{2} \frac{(G_L+G_R)^2}{H_L+H_R+\lambda} + \gamma \]
分裂后的目标函数值为(假设新树的叶子数增加了1):
\[[-\frac{1}{2} \frac{G_L^2}{H_L+\lambda} - \frac{1}{2} \frac{G_R^2}{H_R+\lambda}] + 2\gamma \]
则目标函数的下降量(即增益)为:
\[\text{Gain} = \frac{1}{2} \left[ \frac{G_L^2}{H_L+\lambda} + \frac{G_R^2}{H_R+\lambda} - \frac{(G_L+G_R)^2}{H_L+H_R+\lambda} \right] - \gamma \]
这就是XGBoost决策树寻找最佳分裂点所使用的核心公式。
- 增益计算:方括号内的部分衡量了分裂后左右子节点分数之和的“纯度”提升。提升越多,说明分裂越有效。
- 惩罚项:\(\gamma\) 是复杂度惩罚。只有当 \(\text{Gain} > 0\) 时,分裂才会被执行。\(\gamma\) 越大,对模型复杂度的惩罚越大,分裂越难进行,树会越浅。
- 搜索策略:XGBoost通常采用精确贪婪算法(遍历所有可能的分裂点)或近似算法(基于分位数)来寻找使得 \(\text{Gain}\) 最大的特征和特征值。
步骤6:XGBoost优化求解的整体流程
- 初始化:设置初始预测值 \(\hat{y}_i^{(0)}\),通常是一个常数,如 \(\hat{y}_i^{(0)} = \arg\min_{c} \sum_{i=1}^n l(y_i, c)\)。
- 迭代提升:对于 \(t = 1\) 到 \(M\) (总共 \(M\) 轮迭代/树):
a. 计算梯度:对于每个样本 \(i\),计算损失函数在当前模型预测 \(\hat{y}_i^{(t-1)}\) 下的一阶导数 \(g_i\) 和二阶导数 \(h_i\)。
b. 贪婪地生长一棵树:从根节点开始,递归地对每个节点尝试分裂。
* 对于当前节点的所有样本,枚举所有可能的分裂候选(特征和特征值)。
* 对于每个候选分裂,计算分裂后的左、右子节点集合 \(I_L\), \(I_R\),以及相应的 \(G_L, H_L, G_R, H_R\)。
* 使用 Gain 公式计算分裂带来的增益。
* 选择增益最大的候选分裂点。如果最大增益 \(\le 0\),则停止分裂(成为叶子节点)。
c. 计算叶子权重:当一棵树生长完成后(达到最大深度或无法继续分裂),对于这棵树 \(f_t\) 的每一个叶子节点 \(j\),根据公式 \(w_j^* = -\frac{G_j}{H_j + \lambda}\) 计算其最优输出分数。
d. 更新模型:将新树加入模型:\(\hat{y}_i^{(t)} = \hat{y}_i^{(t-1)} + \eta \cdot f_t(x_i)\)。其中 \(\eta\) 是学习率(Shrinkage参数),用于控制每棵树的贡献,防止过拟合。 - 输出最终模型:经过 \(M\) 轮迭代后,得到最终的集成模型:
\[\hat{y}_i = \sum_{t=1}^{M} \eta \cdot f_t(x_i) \]
这个过程清晰地展示了XGBoost如何将目标函数的优化分解为可解的子问题:先通过二阶泰勒展开得到易于处理的近似目标,然后推导出最优叶子权重和分裂增益公式,最后通过贪婪的树生长算法逐步构建出整个集成模型。