XGBoost推导
XGBoost 用损失的一阶、二阶导数计算叶子输出,再用分裂增益选择划分。
XGBoost(Extreme Gradient Boosting)是一个梯度提升库,可用于回归、分类等任务。 其中的树模型逐轮增加一棵回归树,用新树修正已有预测,并在目标函数中加入正则化项,限制模型复杂度。
训练每棵新树时,需要确定两件事:
- 树怎样划分样本:选择哪些特征和切分点,让样本分别落入哪些叶子。
- 每个叶子输出多少:落入同一叶子的样本,共用一个预测修正量。
XGBoost 用损失函数的二阶近似求解:先假定树的结构已确定,求出各叶子的最优输出;再用这些最优输出计算分裂增益,选择划分。本文讨论单输出回归和二分类。
1. 每轮在已有模型上增加一棵树
回归树通过特征条件逐步划分样本,每个叶子保存一个实数。预测时,样本沿分支到达某个叶子,取出这个数作为树的输出。
设训练集有 个样本, 是第 个样本的特征, 是它的真实标签。尚未加入任何树时,模型先给出初始预测 ;例如平方损失回归可以从训练标签的均值开始。第 轮加入新树 后:
上一轮预测 包含初始预测和此前所有树的输出。 训练新树时,旧模型保持不变,本轮只学习 的结构和叶子值。这就是树提升使用的加法模型。上式先令学习率为 1,后文再加入学习率。
用 表示样本 落入的叶子编号, 表示本轮树的叶子数, 表示第 个叶子的输出:
对于平方损失回归,累加结果 就是预测值。对于二分类,标签取 ,模型先累加原始分数,再通过 sigmoid 函数转成正类概率:
最后再用概率阈值决定类别。分类任务中的树也输出实数修正量:正值提高当前分数,负值降低当前分数,叶子本身不直接输出最终类别。
2. 目标函数:拟合误差与树复杂度
用 衡量模型预测与真实标签之间的误差,用 衡量第 棵树的复杂度。第 轮的完整目标为:
训练时最小化 。树的复杂度由叶子数量和叶子输出的大小共同衡量;这里对叶子输出只考虑 L2 正则化,不加入 L1 项:
- 惩罚叶子数量。 每增加一个叶子,目标函数就增加 ,分裂带来的改善需要抵消这项成本。
- L2 项惩罚叶子值的幅度。 同样的拟合改善下,较小的叶子值受到的惩罚更低。
两棵树都有两个叶子,叶值分别为 和 。它们的叶子值平方和分别为 和 ,因此 L2 惩罚分别为 和 。
两棵树的叶子数量惩罚相同。在预测损失也相同且 时,第一棵树的总目标值更小。
3. 二阶展开:把损失化为本轮树的函数
记上一轮的预测分数为 ,本轮加入新树后变为 。在 处对损失做二阶泰勒展开:
其中 、 是损失对预测分数的一阶、二阶导数,均在上一轮预测处计算:
决定局部的下降方向: 时,减小分数有利于降低损失; 时,应增大分数。 描述损失曲线的弯曲程度,与正则化系数共同影响修正量的大小。
平方损失回归采用 ,因此:
二分类采用二元交叉熵。令 ,损失及其在当前分数处的导数为:
平方损失本身是二次函数,二阶展开是精确的;对二元交叉熵,这里得到的是局部近似,后续计算的分裂增益也以这个近似目标为依据。
本轮训练时, 和旧树的复杂度都是与 无关的常数,去掉它们后得到:
旧模型的影响仍然保留在 、 和 中。
4. 按叶子汇总,求最优输出
固定当前树的结构 ,每个样本属于哪个叶子就确定了。同一叶子中的样本共享输出 ,因此可以把按样本求和改成按叶子求和。
令 为落入叶子 的样本下标集合。代入 ,并展开正则化项:
第二行按叶子分组;第三行把同一叶子的 提到求和外,并将损失中的二次项与 L2 项合并。每个叶子只需要保留两个统计量:
分别表示该叶子中一阶、二阶导数的总和。于是目标简化为:
当 时,每个叶子对应一个关于 的凸二次函数,不同叶子的输出可以分别求解。对 求导并令导数为零:
是固定树结构下叶子 的最优输出。 固定 时, 越大,输出的绝对值越小。
以平方损失为例, 就是叶子里的样本数, 是残差之和。因此 时,最优叶子值就是该叶子内的平均残差; 时,输出会向 0 收缩。
再把 代回单个叶子的目标项:
对所有叶子求和,加上叶子数量惩罚,就得到当前树结构 的评分:
这个评分是当前树结构下二阶近似目标的最小值,越小越好。
5. 分裂增益:比较分裂前后的目标
将一个叶子分成左右两个叶子,分别汇总得到 、。原叶子的统计量是两者之和。
其余叶子保持不变,比较时可以消去它们的目标项,只看这个叶子分裂前后的变化:
定义分裂增益 = 分裂前目标 − 分裂后目标,相减得到:
括号中比较“左右叶子分别输出”与“原叶子共用一个输出”的效果,其中已包含 L2 正则化。末尾的 对应新增一个叶子的成本。
这里的 Gain 已经扣除 γ;有收益的条件是 Gain > 0。 实际分裂还需满足深度、子节点权重等约束。
一个数值例子
假设候选划分得到 ,,并设置 。
原叶子的梯度总和为 0,因此原输出为 0;分开后,两个叶子的最优输出分别为 和 。代入增益公式:
分裂前,两组相反的梯度相互抵消,只能共用修正量 0;分裂后,两组样本分别得到正、负修正。保持划分和 不变,若把 提高到 6,净增益就变成 ,说明改善不足以抵消新增叶子的惩罚。
怎样找到候选划分
对数值特征,精确贪心方法按特征值排序,移动候选阈值时累计左右两侧的 、,比较各阈值的增益。直方图方法先把值归入有限个桶,再比较桶边界,以减少搜索成本。
训练从简单的树开始,在当前叶子上比较允许的候选划分,逐步增加分支。因此,叶子值是在固定结构下求最优,树结构则通过贪心搜索确定,并没有枚举所有树。
训练后,Booster.get_score 可以按不同口径统计特征重要性:
weight:该特征用于分裂的次数。gain:使用该特征的各次分裂的平均增益。total_gain:这些分裂增益的总和。
这里的 gain、total_gain 汇总的是实现记录的分裂分数。它与上文净增益的缩放不同,且未扣除 ,不能直接用上文的 Gain 数值求平均或求和。
6. 学习率与其他训练机制
学习率与采样
学习率 缩小新树对模型的贡献。前文先求一棵树的更新,再按比例加入:
上一节左叶子的输出为 ,取 后,实际加到已有分数上的量约为 。
下一轮用更新后的模型重新计算 、,再训练下一棵树。较小学习率通常需要更多轮次,二者应结合验证集表现选择。
采样则改变本轮使用的数据和可选特征:
subsample控制每轮使用的训练样本比例。colsample_bytree按树选择特征子集。colsample_bylevel、colsample_bynode分别按层和节点选择特征,比例会累积作用;colsample_bynode不适用于exact。
生长限制与剪枝
max_depth限制树的最大深度。min_child_weight要求每个子节点的二阶导数总和 达到阈值。对本文未加样本权重的平方损失,,它才对应最少样本数;二分类时 ,就不能这样解释。
prune 更新器会删除损失改善不足或深度超限的分裂。
缺失值的默认方向
树模型可以为分裂学习一个缺失值默认方向。对于某个候选阈值,步骤如下:
- 按阈值划分非缺失样本,固定它们所在的左右两侧。
- 将该特征缺失的样本全部计入左侧,重新汇总两侧的 、 并计算增益。
- 再将这些缺失样本全部计入右侧,计算另一方案的增益,选择更好的方向。
预测时,若样本在这个分裂特征上缺失,就沿保存的默认方向继续走。方向属于每个分裂节点,因此不同节点可以选择不同方向。
模型学到的是“缺失样本如何分支”,没有恢复丢失的特征值。缺失比例和线上缺失方式变化带来的影响,仍需验证。
评论
使用 GitHub 登录后参与讨论,评论将公开保存在 GitHub。
站长管理删除会同步到 GitHub,无法撤销。
阅读到这里时加载评论。