《GBDT 梯度提升树》分类原理

GBDT(梯度提升树)不仅能解决回归问题,也同样适用于分类任务。它的核心思想与回归 GBDT 一致:通过不断地迭代修正和逐步优化,使模型在分类场景中持续提升预测性能。

接下来,我们将通过一个具体案例,直观地展示 GBDT 在分类任务中的训练与预测全过程。我们会一步步揭示它的内部机制,看看 GBDT 是如何将多个弱学习器逐层叠加,最终形成一个强大的分类模型,并理解其背后的数学原理。

1. 训练过程

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

特征 1特征 2特征 3标签
-0.587231-1.971718-1.0577110
1.068339-0.9700730.2088641
-1.140215-0.8387920.8225450
-0.077445-1.599711-1.2208440
1.727259-1.185827-1.9596701
-2.8953971.9768620.1968610
-1.962874-0.992251-1.3281860
1.8996930.8344450.1713681
-0.720634-0.960593-0.0134970
-1.7154642.1737060.7384670

1.1 初始模型

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

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

那么,这个初始模型的输出值 c 怎么确定?对于经典的二分类问题,我们一般使用的 log loss 损失函数,其公式如下:

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

得到初始模型输出计算公式(省略中间推导过程):

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

1.2 子决策树

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

  • \( F \) :模型当前输出值
  • \( p \) :模型预测的概率
  • \( y \) :真实标签(0 或 1)

计算模型的优化方向(负梯度):

特征 1特征 2特征 3标签预测分数预测概率负梯度
-0.587231-1.971718-1.0577110-0.84720.3-0.3
1.068339-0.9700730.2088641-0.84720.30.7
-1.140215-0.8387920.8225450-0.84720.3-0.3
-0.077445-1.599711-1.2208440-0.84720.3-0.3
1.727259-1.185827-1.9596701-0.84720.30.7
-2.8953971.9768620.1968610-0.84720.3-0.3
-1.962874-0.992251-1.3281860-0.84720.3-0.3
1.8996930.8344450.1713681-0.84720.30.7
-0.720634-0.960593-0.0134970-0.84720.3-0.3
-1.7154642.1737060.7384670-0.84720.3-0.3

叶子节点的输出值需要进行修正,公式如下:

  • 左叶子节点(7 个 0 样本):-0.3 / (0.3 * 0.7) = -1.43
  • 右叶子节点(3 个 1 样本):0.7 / (0.3 * 0.7) = 3.33

最终得到第一个子决策树模型:

接下来,按照前面的思路继续构建第二棵子决策树:

特征 1特征 2特征 3标签预测分数预测概率负梯度
-0.587231-1.971718-1.0577110-0.990160.27-0.27
1.068339-0.9700730.2088641-0.513960.370.63
-1.140215-0.8387920.8225450-0.990160.27-0.27
-0.077445-1.599711-1.2208440-0.990160.27-0.27
1.727259-1.185827-1.9596701-0.513960.370.63
-2.8953971.9768620.1968610-0.990160.27-0.27
-1.962874-0.992251-1.3281860-0.990160.27-0.27
1.8996930.8344450.1713681-0.513960.370.63
-0.720634-0.960593-0.0134970-0.990160.27-0.27
-1.7154642.1737060.7384670-0.990160.27-0.27

注意:预测分数 = 初始模型输出分数 + 学习率 * 第一课树输出分数

接下来,创建有一棵新的决策树去拟合负梯度,然后再修正叶子节点输出值。反复执行该动作,直到创建出指定数量的决策树,GBDT 分类模型训练完毕。

from sklearn.ensemble import GradientBoostingClassifier
from sklearn.datasets import make_classification


def demo():
    x, y = make_classification(n_samples=10, n_features=3, n_redundant=0, n_repeated=0, weights=[0.7, 0.3], random_state=42)
    gbdt = GradientBoostingClassifier(n_estimators=3, criterion='squared_error', loss='log_loss')
    gbdt.fit(x, y)


if __name__ == '__main__':
    demo()

2. 预测过程

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

现在基于这个模型,对新数据 [0.49671415 -0.1382643 0.64768854] 的类别进行预测:

首先,计算该样本的初始分数:-0.84729786

然后,我们将新样本输入到每个决策树,分别得到的每个决策树的输出分数:

  • 第一棵树:3.33
  • 第二棵树:2.67
  • 第三棵树:2.28

接着,假设学习率为 0.1,将所有的分数加起来:-0.85 + (3.33+2.67+2.28) * 0.1 = -0.0187849

最后,,使用 sigmoid 函数计算得到该样本属于 1 类别的概率:sigmoid(-0.0187849) = 0.5046

新样本属于 1 类别的概率为:0.5046,属于 0 类别的概率为 0.4954。由于 1 类别概率较高,最终新样本归类到 1 类别。

from sklearn.ensemble import GradientBoostingClassifier
from sklearn.datasets import make_classification
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
import numpy as np


def demo():
    x, y = make_classification(n_samples=10, n_features=3, n_redundant=0, n_repeated=0, weights=[0.7, 0.3], random_state=42)
    gbdt = GradientBoostingClassifier(n_estimators=3, criterion='squared_error', loss='log_loss')
    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()

    # 新数据预测
    np.random.seed(42)
    new_x = np.random.randn(1, 3)
    print('未知数据:', new_x)
    print('直接计算类别概率:', gbdt.predict_proba(new_x)[0])


    # 手动计算
    # 1. 初始得分
    total_score = -0.84729786

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

    # 3. 将得分输出概率
    sigmoid = lambda x: 1 / (1 + np.exp(-x))
    proba = sigmoid(total_score)
    print('手动计算类别概率:', np.array([1 - proba, proba]))


if __name__ == '__main__':
    demo()

程序输出结果:

未知数据: [[ 0.49671415 -0.1382643   0.64768854]]
直接计算类别概率: [0.50469609 0.49530391]
手动计算类别概率: [0.50469609 0.49530391]

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

1 + 9 =