梯度提升树(GBDT)分类原理

GBDT 是一种强大的集成学习方法,广泛用于分类和回归问题。它属于提升(Boosting)算法的一种,通过多个弱学习器(通常是决策树)结合起来提高模型的准确性。

1. 二分类

对于 GBDT 应用到二分类的场景,其预测过程如下公式所示:

其中:

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

对于新的样本,首先为其计算属于 1 类别的一个初始分数(分数越大,属于 1 类别的可能性就越大)。初始的分数通常是训练集标签为 1 的样本比例。例如:假设训练集中 1 类别样本占比 30%,则新样本的初始概率为 0.3,将概率值转换为分数:

为什么需要这个公式?这是因为 0.3 是概率,而 GBDT 中每一个决策树输出的是分数(logit),通过上面公式就可以将概率转换为分数,反之,将上面分数送入到 Sigmoid 函数可以得到概率 0.3。

接下来,将待预测的样本送入到每一个子决策树中,得到一系列的分数,将这些分数累加起来,别忘了加上初始的分数,这样就得到了该样本属于 1 类别的预测分数。最后,将分数送入到 Sigmoid 函数得到该样本属于 1 类别的概率:

1.1 计算案例

假设:数据集中有 20 条数据, 70% 为 0 标签,30% 为 1 标签,接下来使用 GBDT 预测新样本:[0.49671415 -0.1382643 0.64768854] 的类别标签。

首先,计算该样本的初始分数:logit(0.3) = -0.8472978603872036

假设:经过训练我们得到 GBDT 中多个决策树分别如下,这里需要注意两点:

  • 每棵决策树输出结果为分数,而不是概率。
  • 另外下面的分类器是基于 log loss 损失函数,对于 GBDT 其他常用的损失函数是指数损失函数,这个整体的思想是一样的,只是在进行具体计算的时候有所不同。

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

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

假设:学习率为 0.1,将所有的分数加起来:-0.85 + (3.33 + 2.67 + 2.28) * 0.1 = -0.02 。这里补充一点:GBDT 中的学习率表示训练得到的每棵决策树对最终结果的贡献程度。

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

即:sigmoid(-0.02) = 0.495,则新样本属于 1 类别的概率为:0.495,属于 0 类别的概率为 0.505。由于 0 类别概率较高,最终新样本归类到 0 类别。

1.2 代码演示

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


# 生成随机数据
x, y = make_classification(n_samples=20,
                           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)

# 可视化 GBDT
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])

# 分布计算类别概率
def test():

    # 1. 初始得分
    # 将初始概率转换为分数,该分数通过 sigmoid 函数可以得到其原始概率
    total_score = np.log(0.3 / (1 - 0.3))

    # 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__':
    test()

程序输出结果:

输入的新样本数据: [[ 0.49671415 -0.1382643   0.64768854]]
直接计算类别概率: [0.50469609 0.49530391]
分步计算类别概率: [0.50469609 0.49530391]

2. 多分类

GBDT 在应用到多分类问题场景时,通常会使用 OVR 的方式。例如:数据有 3 个类别,那么会训练得到 3 个二分类的 GBDT 分类器,分别是:0 类别和其他、1 类别和其他、2 类别和其他。

当对新的样本进行预测时,分别送入到不同的 GBDT 二分类器中,得到属于每个类别的分数,最后使用 Softmax 得到该样本属于不同类别的概率。

这里需要注意的是,对于每个二分类的 GBDT 也是需要一个初始分数,这里根据每个类别数据的占比,使用 np.log 函数得到初始分数。这里没有使用前面二分类场景的 logit 函数,而是直接计算对数值的原因是,多分类使用 Softmax 激活函数,而二分类使用的是 Sigmoid 激活函数。

2.1 计算案例

假设:数据集中有 20 条数据, 50% 为 0 标签,30% 为 1 标签,20% 为 2 标签。接下来使用 GBDT 预测新样本:[0.49671415 -0.1382643 0.64768854] 的类别标签。

对于新样本它属于不同类别的分数的计算如下:

  • 初始第 0 类别的分数:np.log(0.5) = -0.6931471805599453
  • 初始第 1 类别的分数:np.log(0.3) = -1.2039728043259361
  • 初始第 2 类别的分数:np.log(0.2) = -1.6094379124341003

由于是多分类,GBDT 得到的模型结构(3 个二分类 GBDT 分类器)如下:

基于 0 类别和其他类别数据训练的 GBDT,用于预测样本属于 0 类别的分数。

基于 1 类别和其他类别数据训练的 GBDT,用于预测样本属于 1 类别的分数。

基于 2 类别和其他类别数据训练的 GBDT,用于预测样本属于 2 类别的分数。

将待预测的样本分别输入到不同类别的 GBDT 中得到分数(学习率 0.1):

  • 输入到 0 类别 GBDT 得到的分数总和:(1.33 + 1.21 + 1.11) * 0.1 = 0.365
  • 输入到 1 类别 GBDT 得到的分数总和:(-0.73 – 0.69 – 0.66) * 0.1 = -0.208
  • 输入到 2 类别 GBDT 得到的分数总和:(-0.83 – 0.81 – 0.79) * 0.1 = -0.243

再分别加上属于不同类别的初始分数:

  • 属于 0 类别分数:-0.6931471805599453 + 0.365 = -0.3281471805599453
  • 属于 1 类别分数:-1.2039728043259361 – 0.208 = -1.411972804325936
  • 属于 2 类别分数:-1.6094379124341003 – 0.243 = -1.8524379124341004

最后使用 Softmax 得到输出不同类别的概率:

  • 属于 0 类别的概率:0.64
  • 属于 1 类别的概率:0.22
  • 属于 2 类别的概率:0.14

最后将输入样本归类到 0 类别。

2.2 代码演示

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


# 生成随机数据
x, y = make_classification(n_samples=20,
                           n_classes=3,
                           n_features=3,
                           n_redundant=0,
                           n_informative=3,
                           n_repeated=0,
                           weights=[0.5, 0.3, 0.2],
                           random_state=42)

# 训练分类模型
gbdt = GradientBoostingClassifier(n_estimators=3,
                                  criterion='squared_error',
                                  loss='log_loss')
gbdt.fit(x, y)

# 可视化 GBDT
for estimators in gbdt.estimators_:
    plt.figure(figsize=(50, 10))
    for idx, estimator in enumerate(estimators):
        plt.subplot(1, 3, idx + 1)
        plot_tree(estimator, filled=True, rounded=True, fontsize=12, 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])

# 分布计算类别概率
def test():

    # 1. 初始得分
    total_score = [np.log(0.5), np.log(0.3), np.log(0.2)]

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

    # 3. 将得分输出概率
    def sofmax(x):
        numerator = np.exp(x).reshape(-1)
        denominator = sum(numerator)
        return numerator / denominator

    print('分步计算类别概率:', sofmax(total_score))


if __name__ == '__main__':
    test()

程序输出结果:

输入的新样本数据: [[ 0.49671415 -0.1382643   0.64768854]]
直接计算类别概率: [0.64261892 0.21755397 0.13982711]
分步计算类别概率: [0.64261892 0.21755397 0.13982711]
未经允许不得转载:一亩三分地 » 梯度提升树(GBDT)分类原理
评论 (0)

4 + 7 =