1.3 Boosting家族谱系与XGBoost的登场


文档摘要

1.3 Boosting家族谱系与XGBoost的登场 Boosting 的发展是一条"拟合目标"不断进化的历史:AdaBoost 调样本权重,GBDT 拟合负梯度,XGBoost 再引入二阶信息与显式正则。 本节沿时间线梳理三代算法的关键差异,并用 sklearn 自带的 GradientBoosting 与 XGBoost 做一次同台对比,让"每一代解决上一代的什么问题"落到实处。 上一节确认了 Boosting 路线"串行修残差"的骨架。但残差这个说法其实只在平方损失下严格成立——换一个损失函数,"误差"该怎么进入下一轮的学习目标?对这个问题的不同回答,划出了家族三代算法的边界。

1.3 Boosting家族谱系与XGBoost的登场

Boosting 的发展是一条"拟合目标"不断进化的历史:AdaBoost 调样本权重,GBDT 拟合负梯度,XGBoost 再引入二阶信息与显式正则。 本节沿时间线梳理三代算法的关键差异,并用 sklearn 自带的 GradientBoosting 与 XGBoost 做一次同台对比,让"每一代解决上一代的什么问题"落到实处。

上一节确认了 Boosting 路线"串行修残差"的骨架。但残差这个说法其实只在平方损失下严格成立——换一个损失函数,"误差"该怎么进入下一轮的学习目标?对这个问题的不同回答,划出了家族三代算法的边界。

一、第一代:AdaBoost,用样本权重聚焦错例

1997 年的 AdaBoost 思路是:不改拟合目标,改样本权重。每轮训练后,分错的样本权重上调,分对的下调,下一个弱学习器自然被迫关注难例,最后按各自表现加权投票。它的巧妙在于"聚焦错例"可以被证明等价于某种指数损失的梯度下降——但这个解释是后来才补上的,算法诞生时并没有明确的目标函数。没有目标函数,就没有地方安放正则化,模型复杂度只能靠轮数粗放控制。这是第一代留下的悬念。

二、第二代:GBDT,把残差推广为负梯度

2001 年的 GBDT(梯度提升决策树)把事情一般化了:不管什么损失函数,第 t 轮该拟合的目标统一取损失关于当前预测的负梯度。平方损失下负梯度恰好等于残差,所以第 1.1 节的手工实验就是 GBDT 的特例;换成绝对值损失、Huber 损失、对数损失,同一套循环照跑。这个统一框架让"提升"摆脱了平方损失的束缚。

代价是每轮只用了损失的一阶信息——就像下山时只知道坡度,不知道坡度变化,步子迈不精准。而且 GBDT 的工程实现长期停在单机、串行、全量分裂点扫描,数据一大就慢。第二代留下两个问题:优化上缺二阶信息,工程上缺速度。

三、第三代:XGBoost,二阶信息加显式正则

2014 年陈天奇提出的 XGBoost,直接在第 2 章将要推导的目标函数层面动手术:对损失做泰勒二阶展开,一阶二阶导数同时进入分裂增益公式;正则项显式写成"叶子数 × γ + 叶权平方和 × λ",树每长一个节点都要先证明自己带来的增益超过惩罚。工程上,预排序的特征块结构、缓存感知访问、列抽样与并行分裂查找,让它在大数据上也能跑。三代差异汇总如下:

Boosting 三代算法演进时间线

Boosting 三代算法的演进:每一代解决上一代的悬案

Boosting 三代算法的演进:每一代解决上一代的悬案

四、同台对比:GBDT 与 XGBoost 跑同一个数据集

空口无凭,直接对比。用 sklearn 的乳腺癌二分类数据,同轮数同深度,比较测试精度与训练耗时:

import time import numpy as np from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split from sklearn.ensemble import GradientBoostingClassifier from sklearn.metrics import accuracy_score import xgboost as xgb X, y = load_breast_cancer(return_X_y=True) Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=42) t0 = time.perf_counter() gbdt = GradientBoostingClassifier(n_estimators=200, max_depth=3, learning_rate=0.1, random_state=42) gbdt.fit(Xtr, ytr) t1 = time.perf_counter() print(f"GBDT 精度={accuracy_score(yte, gbdt.predict(Xte)):.4f} 耗时={t1-t0:.3f}s") # 输出: GBDT 精度=0.9650 耗时=0.612s t0 = time.perf_counter() xgb_clf = xgb.XGBClassifier(n_estimators=200, max_depth=3, learning_rate=0.1, subsample=0.8, colsample_bytree=0.8, eval_metric='logloss', random_state=42) xgb_clf.fit(Xtr, ytr) t1 = time.perf_counter() print(f"XGBoost 精度={accuracy_score(yte, xgb_clf.predict(Xte)):.4f} 耗时={t1-t0:.3f}s") # 输出: XGBoost 精度=0.9720 耗时=0.187s

小数据集上两者精度差距不大(0.965 对 0.972),这符合预期——XGBoost 的优势随数据规模、稀疏程度、并行资源扩大而放大。耗时上 XGBoost 已快三倍以上,若再开多线程差距更大。注意代码里 XGBoost 多了两个参数 subsample 与 colsample_bytree(行、列采样),这是它从随机森林借来的方差控制手段,GBDT 经典实现里没有。

⚠️ 常见坑:把"XGBoost 精度永远更高"当成结论。在几千行的小数据上,调好参的 GBDT、随机森林甚至逻辑回归完全可能打平甚至反超。XGBoost 的护城河是"大规模数据上的速度 + 内建正则带来的稳健性",不是玄学精度。

本节要点回顾

  • AdaBoost 调样本权重聚焦错例,但缺显式目标函数,无处安放正则
  • GBDT 统一拟合负梯度,残差只是平方损失下的特例,但仅用一阶信息
  • XGBoost 二阶展开加显式正则,树的生长必须"证明收益超过惩罚"
  • 工程层面:特征块预排序、并行分裂查找、稀疏感知是速度优势来源
  • 同台实验印证:小数据差距小,规模越大 XGBoost 优势越明显

家族谱系走完,XGBoost 的改进点都已点名但尚未展开。第 2 章进入全册的数学核心:目标函数如何写出、如何二阶展开、叶子权重如何闭式求解、树如何凭增益公式生长。

补一个十秒就能跑完的谱系体验,用 sklearn 自带的 AdaBoost 与 GBDT 在同一份小数据上对照:

from sklearn.ensemble import AdaBoostClassifier, GradientBoostingClassifier from sklearn.datasets import load_wine from sklearn.model_selection import cross_val_score Xw, yw = load_wine(return_X_y=True) ada = AdaBoostClassifier(n_estimators=100, random_state=0) gbt = GradientBoostingClassifier(n_estimators=100, random_state=0) print("AdaBoost 5折均值:", cross_val_score(ada, Xw, yw, cv=5).mean().round(4)) print("GBDT 5折均值:", cross_val_score(gbt, Xw, yw, cv=5).mean().round(4)) # 运行输出: # AdaBoost 5折均值: 0.8492 # GBDT 5折均值: 0.9603

同轮数下 GBDT 明显占优——"统一负梯度框架"的表达力优势在此可见一斑,也是谱系继续向 XGBoost 演进的中间证据。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U