《GBDT 梯度提升树》回归原理

梯度提升树(GBDT,Gradient Boosting Decision Tree)回归是一种集成学习方法,它通过逐步构建多个决策树来优化预测结果,尤其适用于回归问题。GBDT 通过“加法模型”逐步提高模型的预测能力,每一步都在减少前一步模型的误差。

1. 训练过程

其中:

  • \( \hat{y}(x) \) 表示模型对输入 \( x \) 的最终预测值
  • \( \hat{y}_{0}(x) \) 表示初识模型,初步的预测值
  • \( f_{m}(x) \) 是第 \( m \) 棵树的输出值
  • \( \eta \) 表示学习率,控制每棵子树的贡献
  • \( M \) 表示 GBDT 中回归决策树的数量

特征1特征2特征3目标值
-0.908024081.46564877-1.4123037-106.40638687
-0.54438272-1.150993580.11092259-46.41270092
0.24196227-1.72491783-1.91328024-88.70824211
0.37569802-0.29169375-0.60063869-4.29054154
1.52302986-0.23413696-0.2341533791.04704754
0.54256004-0.46572975-0.4634176911.04262105
0.496714150.64768854-0.138264335.80536172
-0.562287530.31424733-1.01283112-79.21856515
1.57921282-0.469474390.76743473136.10440487
-0.2257763-1.424748190.0675282-29.86346474

1.1 初始模型

GBDT 的初始模型 \( \hat{y}_{0}(x) \) 作用是在训练开始时,为每个样本提供一个基础的预测值,后续每棵子树都在此基础上,通过拟合当前残差(或负梯度)来进一步优化预测结果。

注意:初始模型 \( \hat{y}_{0}(x) \) 的输出通常是一个常数(即对所有样本预测同一个值)

那么,这个初始模型的输出值 c 怎么确定?对于回归预测问题,我们一般使用的 squared error 损失函数,其公式如下:

对于初始模型来讲,看看 c 输出什么时,损失函数最小。我们对 c 求导,设导数为 0:

由此可得,初始模型的输出结果为:-8.09005

1.2 子决策树

在 GBDT 中,前面所有子模型(通常是决策树)组合起来,形成当前整体模型的输出,接下来第 m 棵树的目标是:学习如何在当前模型的基础上,进一步降低整体损失函数 L。

特征1特征2特征3目标值预测值负梯度
-0.908024081.46564877-1.4123037-106.40638687-8.09005-98.31634
-0.54438272-1.150993580.11092259-46.41270092-8.09005-38.32265
0.24196227-1.72491783-1.91328024-88.70824211-8.09005-80.61820
0.37569802-0.29169375-0.60063869-4.29054154-8.090053.79951
1.52302986-0.23413696-0.2341533791.04704754-8.0900599.13709
0.54256004-0.46572975-0.4634176911.04262105-8.0900519.13267
0.496714150.64768854-0.138264335.80536172-8.0900543.89541
-0.562287530.31424733-1.01283112-79.21856515-8.09005-71.12852
1.57921282-0.469474390.76743473136.10440487-8.09005144.19445
-0.2257763-1.424748190.0675282-29.86346474-8.09005-21.77342

GBDT 拟合的时候,会把 负梯度当作这一步的目标值。为什么?因为负梯度就是告诉模型,现在我的预测差在哪里,怎么改才能让损失更小。决策树就按照这个方向去分裂、去学习。

想象你在修一座花园的小路。第一遍铺的时候(初始模型),大致铺好,但路面不够平整,有很多坑和高低不平的地方(预测误差)。接下来,每一遍修路(每棵树),你不会重新铺整个路,而是专门针对坑和高处去修,负梯度就像是告诉你哪里需要修。

接下来,以负梯度作为目标值,构建决策树。

我们在用负梯度训练决策树时,叶子节点输出的其实是 往哪个方向去修正,也就是一种调整方向。它并不是最终的预测值,最终的输出还需要通过叶子节点修正公式来确定。

对于 squared_error 损失函数,叶子节点不需要修正,因为修正的公式正好和叶子节点所有样本均值等价。

接下来,继续构建第二棵树:

特征1特征2特征3目标值初始输出当前输出预测输出负梯度
-0.9080241.465648-1.41230-106.406-8.09-83.35-16.42-89.98
-0.544382-1.1509930.110922-46.4127-8.09-18.77-9.97-36.45
0.241962-1.724917-1.913280-88.7082-8.09-83.35-16.43-72.28
0.375698-0.291693-0.600638-4.29054-8.09-18.77-9.975.68
1.523029-0.234136-0.23415391.0470-8.09121.674.0886.97
0.5425600-0.465729-0.46341711.04262-8.0931.51-4.9415.98
0.49671410.647688-0.1382635.80536-8.0931.51-4.9440.74
-0.5622870.3142473-1.012831-79.21856-8.09-83.35-16.43-62.79
1.5792128-0.4694740.767434136.1044-8.09121.674.08132.03
-0.22577-1.4247480.0675282-29.8634-8.09-18.77-9.97-19.90

注意:预测输出 = 初始输出 + 子树输出 * 学习率 。

学习率是用来控制每棵树对最终预测结果的贡献程度,从而能够避免某些性能不佳的子树,导致对预测的结果产生太大的影响。

接下来,基于当前得到的负梯度构建第二棵子树:

第二棵树

以此类推… 构建出指定数量的子树,完整 GBDT 的训练。

2. 预测过程

假设:我们基于训练数据得到 GBDT 分类模型。这个模型包括一个初始输出模型 + 三棵决策树。

第一棵树
第二棵树
第三棵树

现在基于这个模型,对新数据 [0.64768854, -0.1382643, 0.49671415] 进行预测:
计算该样本的初始分数:-8.09005

  • 第一个决策树输出值:31.51
  • 第二个决策树输出值:28.36
  • 第三个决策树输出值:19.47

假设学习率为 0.1,则最终输出值:-8.09005 + (31.51 + 28.36 + 19.47) * 0.1 = -0.155。

from sklearn.ensemble import GradientBoostingRegressor
from sklearn.datasets import make_regression
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
import numpy as np


def demo():
    np.random.seed(0)
    x, y = make_regression(n_samples=10, n_features=3, random_state=42)
    gbdt = GradientBoostingRegressor(n_estimators=3, max_depth=2, criterion='squared_error', loss='squared_error')
    gbdt.fit(x, y)

    # 可视化
    # for estimator in gbdt.estimators_:
    #     plt.figure(figsize=(10, 10))
    #     plot_tree(estimator[0], filled=True, rounded=True, fontsize=14, precision=2)
    #     plt.show()

    # 新数据预测
    new_x, new_y = make_regression(n_samples=1, n_features=3, random_state=42)
    print('输入数据:', new_x)

    print('模型计算:', gbdt.predict(new_x)[0])


    # 手动计算
    # 1. 计算初始输出值
    total_score = -8.0900466155

    # 2. 累加每个树输出值
    learning_rate = 0.1
    for estimator in gbdt.estimators_:
        # 每棵树计算得分
        score = estimator[0].predict(new_x)
        # 累加每棵树得分
        total_score += score[0] * learning_rate

    # 3. 最终输出值
    print('手动计算:', total_score)


if __name__ == '__main__':
    demo()

程序输出结果:

输入数据: [[ 0.64768854 -0.1382643   0.49671415]]
模型计算: -0.15512286528967145
手动计算: -0.1551228652868506

未经允许不得转载:一亩三分地 » 《GBDT 梯度提升树》回归原理
评论 (0)

6 + 1 =