🏠 总目录📚 本教程 04b · XGBoost 的推导
📑 本页目录(点开跳转)

04b · XGBoost 的推导:从负梯度到闭式解

52 分钟 | ⭐⭐ 上一章的「为什么」——Kaggle 主力算法的引擎室


🎯 一句话

XGBoost 只做了一件事:把「下一棵树该怎么长」从一堆手工规则,改写成一个能求出闭式解的目标函数。 一旦目标函数写对了,最优叶子权重、分裂增益、预剪枝、缺失值处理全都是它的推论——LightGBM 和 CatBoost 后来所有的优化,也都挂在这两个公式上。

📍 你是从哪来的04 章停在「GBDT 的每棵新树拟合损失函数的负梯度」。 这一章只是接着往下问一句:负梯度只告诉你往哪个方向走,那到底该走多远? ⚠️ 没读过 04 章就先回去——这里默认你已经知道 Boosting 是串行纠错、GBDT 家族的树都是 CART 回归树、learning_rate 和树数是一对。

🎁 这一章可以跳吗只想会用 → 可以跳,去 Kaggle 第 1 章按模板调参就行。 但如果你要面试、或者要自定义损失函数、或者想知道 LightGBM 凭什么快 —— 答案全在这一章,别处没有。


🧮 一、XGBoost 到底改了 GBDT 的什么

「每棵新树拟合负梯度」这句话是对的,但它只讲了一半

梯度告诉你往哪个方向走没告诉你该走多远

GBDT 的做法是用 learning_rate 这个人工旋钮把步子统一压小——小步慢走,绕开了这个问题,而不是解决它。XGBoost 补上的就是缺的那一半。

先给直觉:一阶导给方向,二阶导给步长

一句话一阶导(斜率)告诉你「往哪边走」,二阶导(曲率)告诉你「这个方向该迈多大步」。 坡陡但马上要拐弯 → 小步试探;坡缓而且笔直很远 → 可以大胆迈。

这正是梯度下降 vs 牛顿法的差别:梯度下降只知道方向,步长得你自己调;牛顿法多用一个二阶导,步长是算出来的。XGBoost 把这一招搬进了 Boosting。

第一步:把正则项写进目标函数

训练第 $t$ 棵树时,模型的预测是「前面所有树的和 + 这棵新树」:

$$\text{Obj}^{(t)}=\sum_{i=1}^{n} l\big(y_i,\ \hat y_i^{(t-1)}+f_t(x_i)\big)+\Omega(f_t),\qquad \Omega(f)=\gamma T+\frac{1}{2}\lambda\sum_{j=1}^{T}w_j^2$$

这一步就已经和 GBDT 分岔了:GBDT 的正则(树深、最小样本数、事后剪枝)是长树时另外加的外挂规则,不在目标函数里;XGBoost 直接把它写进了要优化的式子。

第二步:二阶泰勒展开

$f_t(x_i)$ 是待求的未知量,套在损失函数 $l$ 里面没法直接优化。泰勒展开就是干这个用的:把 $f_t(x_i)$ 当成一个「小增量」,在 $\hat y^{(t-1)}$ 这一点把损失展成多项式。记:

$$g_i=\frac{\partial\, l(y_i,\hat y^{(t-1)})}{\partial\, \hat y^{(t-1)}}\ (\text{一阶}),\qquad h_i=\frac{\partial^2 l(y_i,\hat y^{(t-1)})}{\partial\, (\hat y^{(t-1)})^2}\ (\text{二阶})$$

展开到二阶,再扔掉与 $f_t$ 无关的常数项 $l(y_i,\hat y^{(t-1)})$:

$$\text{Obj}^{(t)}\approx\sum_{i=1}^{n}\Big[\,g_i\, f_t(x_i)+\tfrac{1}{2}h_i\, f_t(x_i)^2\,\Big]+\gamma T+\frac{1}{2}\lambda\sum_j w_j^2$$

停下来看看这一步换到了什么:式子里再也没有 $l$ 了,只剩 $g_i$、$h_i$ 两串数。也就是说——你换任何损失函数,只要它二阶可导,后面的推导一个字都不用改。这就是 XGBoost 能让你自定义 objective(只要求你提供 g 和 h 两个函数)的原因,也是「为什么用二阶展开」最实在的一条回答。

第三步:从「每个样本」归并到「每个叶子」

树是分段常数函数:落进同一个叶子的样本,输出值完全一样。所以按叶子把样本归并,记 $I_j$ 为落进叶子 $j$ 的样本集合,$G_j=\sum_{i\in I_j}g_i$,$H_j=\sum_{i\in I_j}h_i$:

$$\text{Obj}^{(t)}=\sum_{j=1}^{T}\Big[\,G_j w_j+\tfrac{1}{2}(H_j+\lambda)w_j^2\,\Big]+\gamma T$$

这是整套推导的关键一步:$T$ 个叶子的项互相独立了。每个叶子单独看就是一条开口向上的抛物线(开口向上是因为 $H_j+\lambda>0$),可以各求各的最小值。

第四步:顶点公式一代,两个闭式解就出来了

一元二次 $aw^2+bw$ 的顶点在 $w=-\frac{b}{2a}$,直接套:

$$w_j^{*}=-\frac{G_j}{H_j+\lambda}\qquad\text{(最优叶子权重)}$$

代回目标函数:

$$\text{Obj}^{*}=-\frac{1}{2}\sum_{j=1}^{T}\frac{G_j^{2}}{H_j+\lambda}+\gamma T\qquad\text{(结构分数)}$$

「结构分数」(structure score)就是:给定一棵树长成什么样,它到底有多好——一个数字说了算,越小越好。 XGBoost 里所有关于「要不要这么分」的决定,裁判都是它。

💡 拿 MSE 验一下这两个公式:$l=\frac12(y-\hat y)^2$ 时 $g_i=\hat y_i-y_i$(就是负残差),$h_i=1$,于是

$$w_j^{*}=\frac{\sum_{i\in I_j}(y_i-\hat y_i)}{n_j+\lambda}=\frac{\text{这个叶子里残差的和}}{\text{样本数}+\lambda}$$

$\lambda=0$ 时就是残差的平均值——正是 GBDT 在做的事。⭐ 所以在 MSE 下 XGBoost 和 GBDT 是同一个东西;差别全在别的损失上(logloss、pairwise 排序、Poisson、你自己写的……)。

第五步:分裂增益——预剪枝直接长在判据里

把一个叶子拆成左右两个,收益就是拿新的结构分数减旧的:

$$\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$ 每多一个叶子固定扣 $\gamma$ 分。⭐ Gain 为负就别分——预剪枝直接写在判据里,不是长完再剪
$\lambda$ 在分母 叶子里样本少($H$ 小)时,$\lambda$ 把权重往 0 拽。样本越少越不敢给大值——天然的收缩
XGBoost 的推导链:六步,每一步只是把上一步再往前推一格① 目标函数:损失 + 正则(叶子数罚 γ,叶子权重罚 λ)Obj = Σ l( y , ŷ + f(x) ) + γT + ½λ Σ w²② 对 f 做二阶泰勒展开,扔掉常数项 —— 从此式子里没有 l 了Obj ≈ Σ [ g·f(x) + ½ h·f(x)² ] + γT + ½λ Σ w²③ 树是分段常数 → 按叶子归并:Gj = 叶内 g 之和,Hj = 叶内 h 之和Obj = Σ 叶子 j [ Gj·wj + ½ (Hj+λ)·wj² ] + γT④ 每个叶子都是一条开口向上的抛物线 → 顶点就是最优wj* = − Gj / (Hj + λ)  ⭐ 最优叶子权重⑤ 代回去,得到「这棵树的结构值多少分」Obj* = − ½ Σ Gj² / (Hj + λ) + γT  ⭐ 结构分数⑥ 拆一个叶子的收益 = 拆后分数 − 拆前分数 − 新叶子的入场费 γGain = ½ [ GL²/(HL+λ) + GR²/(HR+λ) − (GL+GR)²/(HL+HR+λ) ] − γ⭐ Gain 为负就不分裂 —— 预剪枝直接长在判据里,不是长完再剪
⭐ 真正要记的是第 ④ 步:一旦按叶子归并,每个叶子就退化成一条抛物线,顶点公式一代,最优叶子权重和结构分数同时掉出来——XGBoost 后面所有工程优化(缺失值默认方向、加权分位数、直方图)都是在给这两个公式提速。

和 GBDT 到底差在哪:三条

传统 GBDT XGBoost
分裂判据 用负梯度当「伪残差」,再拿平方误差准则长一棵回归树去拟合它——是个代理目标 直接对真实目标(含正则)二阶展开,判据就是目标函数的下降量,没有代理
叶子值 树长完之后,对每个叶子再做一次一维线搜索补上 和分裂判据出自同一个闭式解 $-G/(H+\lambda)$,一步到位
正则 树深 / 最小样本数 / 事后剪枝这些外挂规则 $\gamma$、$\lambda$ 在目标函数里,分裂那一刻就在算

「为什么二阶收敛更快」的答案在这: 一阶方法只知道方向,步长要靠 learning_rate 试; 二阶方法把每个叶子的最优步长直接解出来($-G_j/(H_j+\lambda)$ 就是一步牛顿法)。 同样的树数下,走对步长的那个当然降得更多。 ⚠️ 但别把这句话说过头learning_rate 在 XGBoost 里照样要调、默认照样是 0.1。二阶解出来的是「这一步理论上的最优」,仍然要打个折才不会过拟合。

顺带解决的两个工程问题

① 缺失值:不填,而是学一个「默认方向」

XGBoost 不给你填缺失值。它在每一个候选分裂点上,把缺失样本先全扔左边算一次 Gain、再全扔右边算一次,哪边 Gain 大就把这个分裂点的「默认方向」定成哪边

💡 所以缺失不是被猜出来的,是被当成一种信息学出来的(「这个字段为空」本身常常就是强信号——第 16 章那条 income_missing附录 B 那条 amt_missing,都是同一个道理)。这套机制顺带让它在 one-hot 之后的稀疏矩阵上也跑得快:0 可以和缺失一样被跳过。

② 不试遍所有切分点:加权分位数草图

📐 为什么候选分位点要用 h 加权(想看再点)

精确贪心要把每个特征的每个取值都当候选切分点试一遍,数据一大就顶不住。近似算法改成只取若干个分位点当候选。但分位点该按什么加权? 把第 ② 步那个式子配个方:

$$\sum_i\Big[g_i f+\tfrac12 h_i f^2\Big]=\sum_i \tfrac12 h_i\Big(f-\big(-\tfrac{g_i}{h_i}\big)\Big)^2+\text{常数}$$

⭐ 右边是一个以 $h_i$ 为权重的加权平方误差——也就是说,$h_i$ 天然就是「这个样本有多重要」。所以候选点要按 $h$ 的分位数取,而不是按样本个数平均取。这就是论文里的 weighted quantile sketch。

💡 顺带解释了一个现象:logloss 下 $h=p(1-p)$,预测越靠近 0.5(越犹豫)的样本 $h$ 越大、越被当回事;已经很确定的样本 $h\to 0$,几乎不影响候选点的选取。

⚔️ 二、XGBoost / LightGBM / CatBoost:各自在优化哪一段

三者都是 GBDT 的工业级实现:

XGBoost LightGBM CatBoost
生长方式 按层生长(level-wise) 按叶生长(leaf-wise,选增益最大的叶子) 对称树
速度 最快
精度
类别特征 需自己编码 原生支持 原生且最好
过拟合倾向 较稳 需注意(leaf-wise 更易过拟合,要控 num_leaves 较稳
默认参数 一般 一般 开箱即用最好

🔑 实用建议大数据/要快 → LightGBM;类别特征多 → CatBoost;不确定 → 先 LightGBM。 三者精度差距通常在 0.1–0.5% 以内,选型不如调参和特征工程重要

有了上一节,这张表就能从「结论」读成「为什么」了

三者的目标函数是同一个(都是「二阶展开 + 正则 + 结构分数」那一套)。它们的区别在于各自在优化训练流程的不同段

优化的是哪一段 XGBoost LightGBM CatBoost
怎么找候选切分点 精确贪心 / 加权分位数草图 直方图:每个特征先分成 255 个 bin 直方图
下一步该长哪个节点 level-wise:整层一起长 leaf-wise:全树挑 Gain 最大的那个叶子长 对称树:整层共用同一个 (特征, 阈值)
类别特征怎么办 自己 one-hot 原生(按统计量排序后二分) 有序目标编码
主要在防什么 过拟合($\gamma$ / $\lambda$) 过拟合(leaf-wise 更凶,靠 num_leaves 刹车) 目标泄漏 / 预测偏移

LightGBM 的三板斧(「为什么最快」的答案)

⚠️ leaf-wise 的代价:同样的叶子数下它降损失更多,但树会长得又深又不平衡,小数据集上极易过拟合。刹车是 num_leaves(别超过 $2^{\texttt{max\_depth}}$)和 min_data_in_leaf。这就是那张表里「需注意」三个字背后的机理。

CatBoost 在防的是另一件事:普通的目标编码(拿这个类别的历史平均标签当特征)用到了当前样本自己的标签——这就是第 16 章讲的目标泄漏。CatBoost 的 ordered target statistics(有序目标编码) 先给样本安排一个随机顺序,算某个样本的编码时只用排在它前面的样本,从结构上把泄漏堵死。同样的思路用在 boosting 上就是 ordered boosting,防的是「同一批数据既用来算残差又用来拟合残差」带来的预测偏移。

对称树(oblivious tree):同一层所有节点共用一个 (特征, 阈值)。整棵树因此退化成一张 $2^{\text{depth}}$ 的查找表,推理快得离谱(几条位运算就定位到叶子);而且这个限制本身就是很强的正则——这是 CatBoost「默认参数最好用」的一部分原因。

🔑 一句话版本(可以直接当面试答案)XGBoost 定义了这套框架(二阶展开 + 正则进目标函数 + 结构分数); LightGBM 优化「怎么算得更快」(直方图 + 做差 + GOSS + EFB + leaf-wise); CatBoost 优化「怎么不泄漏」(有序目标编码 + ordered boosting + 对称树)。 三者精度差距通常 0.1–0.5%,所以才说选型远不如调参和特征工程重要


🔗 这一章连到哪里

去哪 为什么要去那里
04 · 决策树与集成学习 回去看被这一章接住的那半句:GBDT 的每棵新树拟合的是负梯度,以及为什么这个家族只能用 CART
16 · 特征工程基础 ⭐ 那里的 OOF 目标编码就是 CatBoost「有序目标编码」的手工版——想真正搞懂 CatBoost 在防什么,去看那段泄漏演示
03 · 线性模型 目标函数里的 $\frac12\lambda\sum w_j^2$ 就是那一章的 L2 正则,只是惩罚对象从「特征权重」换成了「叶子输出值」
09 · 优化器与学习率 「一阶给方向、二阶给步长」这条线在那里有另一半:为什么深度学习用二阶方法(Hessian 太大)
数学原理 · 02 MAP 与正则化的真身 $\lambda$ 不是随手加的一项,它对应一个先验分布
Kaggle 第 1 章 推导看完了想真的去打榜:那里有 LightGBM 的实战参数模板和特征工程套路

✅ 检查点

  1. XGBoost 为什么要做二阶泰勒展开?只用一阶(GBDT)差在哪?
  2. 二阶展开之后,目标函数里还剩下什么?这件事带来了哪个直接的工程好处?
  3. 写出最优叶子权重和分裂增益,并说出 $\gamma$ 和 $\lambda$ 各在管什么。
  4. 用 MSE 损失时 XGBoost 的叶子权重退化成什么?这说明了什么?
  5. XGBoost 怎么处理缺失值?
  6. LightGBM 凭什么快(说出三样)?leaf-wise 的代价是什么、拿什么刹车?
  7. CatBoost 的「有序目标编码」在防什么?对称树带来哪两个好处?
👀 答案
  1. 一阶导只给方向,二阶导给步长(梯度下降 vs 牛顿法)。GBDT 是拿负梯度当伪残差、再用平方误差代理准则长一棵树去拟合,叶子值靠事后一维线搜索补上——判据和叶子值不是同一个东西;XGBoost 直接对真实目标(含正则)二阶展开,分裂判据和叶子值出自同一个闭式解
  2. 只剩 $g_i$ 和 $h_i$,损失函数 $l$ 消失了。好处:换任何二阶可导的损失,后面的推导一个字都不用改——这就是自定义 objective 只要求你提供 g 和 h 两个函数的原因。(另一个好处是按叶子归并后每个叶子是抛物线,有闭式最优解。)
  3. $w_j^*=-\dfrac{G_j}{H_j+\lambda}$;$\text{Gain}=\frac12\Big[\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}\Big]-\gamma$。$\gamma$ 是每多一个叶子的入场费,所以 Gain 为负就不分裂 = 预剪枝写进了判据(不是长完再剪);$\lambda$ 在分母,叶子样本少($H$ 小)时把权重往 0 拽,是天然的收缩。
  4. $g_i=\hat y_i-y_i$、$h_i=1$,$w_j^*$ 退化成这个叶子里残差的平均值($\lambda=0$ 时)——正是 GBDT 在做的事。说明 MSE 下两者等价,二阶展开的价值全在别的损失函数上(logloss、pairwise 排序、Poisson、自定义)。
  5. 不填。每个候选分裂点上把缺失样本全扔左边算一次 Gain、全扔右边再算一次,哪边 Gain 大就把默认方向定成哪边——缺失是被当成信息出来的,顺带让稀疏 one-hot 也跑得快。
  6. 直方图(每特征 255 个 bin,复杂度从 O(样本数 × 特征数) 降到 O(bin 数 × 特征数))+ 直方图做差(父 − 左 = 右,省一半)+ GOSS(梯度大的全留、梯度小的采样并放大权重)+ EFB(互斥特征捆绑)。代价:leaf-wise 的树又深又不平衡,小数据集极易过拟合;刹车是 num_leaves(别超过 $2^{\texttt{max\_depth}}$)和 min_data_in_leaf
  7. 目标泄漏:普通目标编码用到了当前样本自己的标签,有序目标编码给样本排一个随机顺序、只用排在它前面的样本算编码(同思路用在 boosting 上就是 ordered boosting,防预测偏移)。对称树的两个好处:① 整棵树退化成一张 $2^{\text{depth}}$ 的查找表,推理极快;② 这个限制本身就是很强的正则,是它「默认参数最好用」的一部分原因。

🛑 可以停在这里

走神救援

补的是 04 章缺的那半句:⭐负梯度只给方向,不给步长——GBDT 用 learning_rate 统一压小步子,是绕开不是解决。⭐一阶导给方向、二阶导给步长(坡陡要拐弯就小步,坡缓笔直就大胆迈),即梯度下降 vs 牛顿法六步推导:① 正则写进目标函数 Obj = Σl(y, ŷ+f) + γT + ½λΣw²γ=每多一个叶子的入场费,λ=叶子权重的 L2;⭐GBDT 的正则是长树时另加的外挂规则、不在目标函数里,这一步就分岔了);② 二阶泰勒展开扔掉常数项,⭐式子里再也没有 l 了,只剩 g 和 h——换任何二阶可导的损失推导一字不改,这就是自定义 objective 只要你给 g 和 h 的原因;③ 树是分段常数,按叶子归并成 Gⱼ、Hⱼ,⭐T 个叶子的项互相独立了;④ 每个叶子是开口向上的抛物线(H+λ>0),顶点公式一代,w*=−G/(H+λ);⑤ 代回去得结构分数 Obj*=−½ΣG²/(H+λ)+γT,⭐给定树结构一个数字说了算,越小越好;⑥ 分裂增益=左分数+右分数−不拆的分数−γ:中括号里是「纯度提升」的 XGBoost 版(量的是目标函数真降了多少)、⭐Gain 为负就不分裂=预剪枝写进判据λ 在分母=叶子样本少就把权重往 0 拽。💡MSE 验算:g=ŷ−y、h=1,w* 退化成残差平均值——正是 GBDT 在做的事,所以 MSE 下两者等价,二阶的价值全在别的损失上。⚠️别说过头:learning_rate 照样要调、默认照样 0.1缺失值:不填,每个分裂点把缺失全扔左、全扔右各算一次 Gain,哪边大就定哪边当默认方向加权分位数:目标函数能配方成以 h 为权的加权平方误差,所以候选点按 h 的分位数取。⭐三巨头目标函数同源,各优化一段XGBoost 定义框架LightGBM 优化「算得快」——255 个 bin 的直方图把复杂度从 O(样本×特征) 降到 O(bin×特征)、做差「父−左=右」省一半、GOSS、EFB、leaf-wise(⚠️小数据极易过拟合,刹车是 num_leavesmin_data_in_leaf);CatBoost 优化「不泄漏」——有序目标编码只用排在前面的样本、ordered boosting、对称树退化成 2^depth 查找表所以推理极快,本身也是强正则。⭐三者精度差 0.1–0.5%,选型远不如调参和特征工程重要

下一节 👉 05-评估与过拟合.md ⭐⭐⭐ 全教程最重要的一章

打卡记录保存在你的浏览器里,首页能看到总进度