梯度提升树
按顺序添加树,拟合当前损失的负梯度方向,逐步修正已有模型。
学习前需要掌握
背景与问题
一个常数预测器可能太简单,而直接拟合一棵很深的树又容易过拟合。梯度提升从简单初值开始,依次加小树,让每轮新树修正当前预测的损失方向;每棵树的任务由已经建成的模型决定。
概念定义
梯度提升树是以树为增量的加法模型。每轮在训练样本上计算当前预测处的损失负梯度,再拟合一棵受限复杂度的树,并乘学习率加入旧模型。只有平方误差等特定损失下,负梯度才恰好是普通残差。
直观理解
先画一张粗略地图,再一层层添加修正,而不是让许多完整地图平权投票。每层修正通常较小,避免一次把偶然误差全部记住。
核心原理
平方损失的一半形式对应负梯度 y−F(x),分类的对数损失则通常在 margin 或 logit 空间计算梯度。分类树增量不是直接加到概率后随意裁剪,链接函数必须与目标一致。
learning_rate 小会让每步修正温和,但往往需要更多树;单树深度、叶子数、最小叶样本与子采样共同控制容量。降低学习率却大幅增加迭代数,仍可能过拟合,不能只看一个参数。
早停基于验证目标选迭代数,验证数据不能再被当作最终独立测试。直方图实现通过分箱提高分裂搜索效率,可能还用二阶信息优化叶值;不能把所有实现细节简化成完全相同的残差树。
数学表达
加法模型的本轮修正
h 拟合当前损失的负梯度方向,η 为学习率;具体叶值计算取决于损失与实现。
变量含义
变量定义与本页概念定义一致,代码中的同名变量保持相同语义。
计算步骤
- 1选择损失与链接函数,用训练数据计算合适常数初值。
- 2在当前预测处计算负梯度或实现所需的一二阶量。
- 3用受限复杂度树拟合本轮修正目标。
- 4乘学习率累加到当前模型,记录训练与验证损失。
代码对应位置
示例 1平方损失下的一次小步树增量:固定标签、旧预测和弱树输出,核对残差与学习率累加。
手算与场景核对
平方误差回归有两条标签 1、3,初始常数均为 2,残差为 −1、1。假设本轮弱树只拟合出 −0.5、0.5,学习率 0.2,新预测变成 1.9、2.1。
均方误差从 1 降到 0.81。树只修正一部分残差;这说明给定本轮方向的损失变化,不能作为任意学习率都安全或验证误差必降的保证。
计算与实现步骤
- 选择损失与链接函数,用训练数据计算合适常数初值。
- 在当前预测处计算负梯度或实现所需的一二阶量。
- 用受限复杂度树拟合本轮修正目标。
- 乘学习率累加到当前模型,记录训练与验证损失。
- 按固定早停或预算规则选择迭代数,再作独立测试。
代码实现
示例 1
平方损失下的一次小步树增量
example_01.py用途:固定标签、旧预测和弱树输出,核对残差与学习率累加。
targets, predictions = [1.0, 3.0], [2.0, 2.0]
weak_tree_output, rate = [-0.5, 0.5], 0.2
residual = [y-p for y, p in zip(targets, predictions)]
updated = [p + rate*h for p, h in zip(predictions, weak_tree_output)]
mse = lambda values: sum((y-p)**2 for y, p in zip(targets, values))/len(targets)
print(f"residual={residual}")
print("updated=" + ",".join(f"{p:.3f}" for p in updated))
print(f"MSE_before={mse(predictions):.3f}, MSE_after={mse(updated):.3f}")代码解析
解析始终位于完整代码下方,并按实际代码段逐项对应。
输入数据与任务
固定标签、旧预测和弱树输出,核对残差与学习率累加。
y- 目标标签或连续目标值。
Step 1 · 1–8 行
输出中间参数、形状或最终指标,用于核对代码是否符合预期。
targets, predictions = [1.0, 3.0], [2.0, 2.0]
weak_tree_output, rate = [-0.5, 0.5], 0.2
residual = [y-p for y, p in zip(targets, predictions)]
updated = [p + rate*h for p, h in zip(predictions, weak_tree_output)]
mse = lambda values: sum((y-p)**2 for y, p in zip(targets, values))/len(targets)
print(f"residual={residual}")
print("updated=" + ",".join(f"{p:.3f}" for p in updated))
print(f"MSE_before={mse(predictions):.3f}, MSE_after={mse(updated):.3f}")预期输出或运行结果
residual=[-1.0, 1.0] updated=1.900,2.100 MSE_before=1.000, MSE_after=0.810
常见错误 · 3 条
- 把任何分类目标的负梯度都叫 y−预测概率残差。
- 只减学习率却不控制总迭代容量。
- 用最终测试集选早停轮数,泄露评测信息。
实际应用
- 表格数据分类与回归,逐步拟合非线性和交互。
- 在验证预算固定时比较树数、学习率与叶子复杂度的组合。
常见错误
- 把任何分类目标的负梯度都叫 y−预测概率残差。
- 只减学习率却不控制总迭代容量。
- 用最终测试集选早停轮数,泄露评测信息。
资料与假设
参见 scikit-learn:Gradient Tree Boosting。 本页数值均为构造例;涉及模型分数时仅说明给定数据与协议下的计算。
推荐学习资料
参考库不会生成虚假资源或无效外部链接。