1. 残差(Residual)
在学习决策提升数之前,我们需要先了解一个基本的概念——残差。残差是预测值和真实值之间的误差。
例如,我们要预测一个学生A的成绩,预测值为70,真实值为50,那么残差就是80-50=30。
我们可以很容易构建一个残差树:
- 满分100分,学生A成绩70分
- 第一次预测:取满分中值50分,残差为70-50=20
- 第二次预测:根据上一轮残差20,取残差中值10分,残差为20-10=10
- 第三次预测:根据上一轮残差10,取残差中值5分,残差为10-5=5
- 第四次预测:根据上一轮残差5,取残差中值2分,残差为5-2=3
- 第五次预测:根据上一轮残差3,取残差中值1分,残差为3-1=2
- 第六次预测:根据上一轮残差2,取残差中值1分,残差为2-1=1
- 我们可以往复预测,直到残差为0,也可以设定一个阈值,比如现在我们设定阈值为1,当残差小于等于1时,我们停止预测。
- 将以上预测结果相加,我们得到:50+10+5+2+1+1=69分,作为我们的最终预测结果。
我们可以看到通过多轮残差提升,可以得到一个比较精确的决策树,但是很明显,它是过于拟合且难以处理复杂问题。
基于集成学习的思想,我们可以进行改进,通过集成多个残差树,进行改进集成多个残差树来提升模型的表现。
这种方法称为梯度提升决策树(GBDT)。GBDT通过将多个弱学习器(即简单的决策树)集成在一起,从而形成一个强大的预测模型。
2. GBDT原理
GBDT 是一种迭代的优化算法,它通过逐步减少预测误差来提升模型性能。具体来说,GBDT 的工作流程如下:
- 初始模型:首先,使用一个简单的决策树模型来进行初始预测。这个模型通常是通过最小化损失函数来构建的。例如,对于回归问题,初始模型可能是一个常数模型,使得损失函数(如均方误差)最小化。
- 计算残差:计算初始模型的残差,即预测值与真实值之间的差异。这些残差将作为下一轮学习的目标。
- 构建新树:使用残差作为目标变量,构建一个新的决策树。这个新树的目的是拟合残差,从而进一步减少预测误差。
- 更新模型:将新树的预测结果与当前模型的预测结果结合起来,形成一个新的组合模型。通常,结合方法是通过加权和的方式,其中新树的预测结果乘以一个学习率参数,然后与当前模型的预测结果相加。
- 迭代更新:重复步骤 2-4,构建多个决策树,每次都在减少残差的基础上进行改进。迭代次数(即树的数量)和学习率是 GBDT 的超参数,需要通过交叉验证等方法进行调优。
具体数学公式如下:
-
损失函数:设定要最小化的损失函数 L(y, F(x)),其中 y 是真实值,F(x) 是模型的预测值。
-
初始化模型:使用常数值初始化模型,以最小化损失函数。通常选取所有目标值的平均值。
F_0(x) = \arg\min_c \sum_{i=1}^N L(y_i, c) -
迭代训练:在每一轮迭代中,执行以下步骤:
-
计算残差:计算当前模型的负梯度(对于回归问题就是残差)。
r_{im} = -\left[ \frac{\partial L(y_i, F(x_i))}{\partial F(x_i)} \right]_{F(x)=F_{m-1}(x)} -
拟合残差:使用残差作为目标变量,训练一个新的决策树。
h_m(x) = \arg\min_{h} \sum_{i=1}^N (r_{im} - h(x_i))^2 -
更新模型:将新决策树的输出乘以学习率后加到当前模型上。
F_m(x) = F_{m-1}(x) + \eta h_m(x)其中 \eta 是学习率,用于控制每次更新的幅度。
-
3. GBDT构建
假设我们有一个简单的回归数据集:
x | y |
---|---|
1 | 2 |
2 | 3 |
3 | 6 |
4 | 8 |
步骤 1:初始化模型
使用平方误差损失函数 L(y, F(x)) = (y - F(x))^2 计算所有目标值的平均值作为初始模型:
步骤 2:第一轮迭代
-
计算残差:
r_{i1} = y_i - F_0(x_i)x y F_0(x) r_{i1} 1 2 4.75 -2.75 2 3 4.75 -1.75 3 6 4.75 1.25 4 8 4.75 3.25 -
拟合残差:训练一个决策树来拟合残差。
假设训练的树的输出如下(这一步,可以通过对每一个节点进行分裂,通过计算残差均值,选取最佳分裂点。为了简化,我们假设输出为残差的平均值):
h_1(x) = \begin{cases} -2.25 & \text{if } x \leq 2 \\ 2.25 & \text{if } x > 2 \end{cases}计算得到的值分别为:
- 对于 x \leq 2 的部分,平均残差为:
\frac{-2.75 + (-1.75)}{2} = -2.25
- 对于 x > 2 的部分,平均残差为:
\frac{1.25 + 3.25}{2} = 2.25
- 对于 x \leq 2 的部分,平均残差为:
-
更新模型:
F_1(x) = F_0(x) + \eta h_1(x)设定学习率 \eta = 0.1。
x F_0(x) h_1(x) \eta h_1(x) F_1(x) 1 4.75 -2.25 -0.225 4.525 2 4.75 -2.25 -0.225 4.525 3 4.75 2.25 0.225 4.975 4 4.75 2.25 0.225 4.975
步骤 3:重复迭代
在这一步,我们将进行迭代,重复步骤 2 和步骤 3,直到残差小于等于阈值或迭代次数达到上限。
但出于简化考虑,我们假设迭代次数为2进行演示。
-
计算残差:
r_{i2} = y_i - F_1(x_i)x y F_1(x) r_{i2} 1 2 4.525 -2.525 2 3 4.525 -1.525 3 6 4.975 1.025 4 8 4.975 3.025 -
拟合新的残差:训练新的决策树来拟合新的残差。
假设新的树的输出如下:
h_2(x) = \begin{cases} -2.025 & \text{if } x \leq 2 \\ 2.025 & \text{if } x > 2 \end{cases}计算得到的值分别为:
- 对于 x \leq 2 的部分,平均残差为:
\frac{-2.525 + (-1.525)}{2} = -2.025
- 对于 x > 2 的部分,平均残差为:
\frac{1.025 + 3.025}{2} = 2.025
- 对于 x \leq 2 的部分,平均残差为:
-
更新模型:
F_2(x) = F_1(x) + \eta h_2(x)x F_1(x) h_2(x) \eta h_2(x) F_2(x) 1 4.525 -2.025 -0.2025 4.3225 2 4.525 -2.025 -0.2025 4.3225 3 4.975 2.025 0.2025 5.1775 4 4.975 2.025 0.2025 5.1775
步骤 4:验证模型
在完成两轮迭代后,我们可以通过验证数据集来验证模型的性能。假设我们有一个验证数据集:
x | y |
---|---|
1.5 | 2.5 |
2.5 | 4.0 |
3.5 | 6.5 |
4.5 | 7.5 |
使用更新后的模型 F_2(x) 来进行预测:
x | F_2(x) | 预测\hat{y} |
---|---|---|
1.5 | 4.3225 | 4.3225 |
2.5 | 4.3225 | 4.3225 |
3.5 | 5.1775 | 5.1775 |
4.5 | 5.1775 | 5.1775 |
计算预测误差:
x | y | 预测\hat{y} | 误差(y - \hat{y})^2 |
---|---|---|---|
1.5 | 2.5 | 4.3225 | (2.5 - 4.3225)^2 = 3.33 |
2.5 | 4.0 | 4.3225 | (4.0 - 4.3225)^2 = 0.10 |
3.5 | 6.5 | 5.1775 | (6.5 - 5.1775)^2 = 1.75 |
4.5 | 7.5 | 5.1775 | (7.5 - 5.1775)^2 = 5.39 |
通过上述步骤,我们已经进行了一次完整的GBDT构建,由于数据的简单性,以及迭代的简化,我们并没有得到很理想的结果。
但我们应该了解并关注,GBDT的构建过程需要多次迭代,直到残差小于阈值或迭代次数达到上限,从而实现理想的结果。
4. GBDT适用场景
要了解GBDT的适用场景,需要了解其优缺点:
优点
- 高准确性:
GBDT 在处理各种任务(如分类、回归)时表现出色,通常能够提供高准确性的预测。 - 良好的泛化能力:
通过逐步减少误差,GBDT 能够有效地提高模型的泛化能力,避免过拟合。 - 处理非线性关系:
GBDT 能够捕捉数据中的复杂非线性关系,适用于多种复杂场景。 - 特征重要性评估:
GBDT 提供了特征重要性评估功能,可以帮助理解哪些特征对模型的预测结果贡献最大。 - 鲁棒性:
对缺失值和异常值具有一定的鲁棒性,模型能够较好地处理这些问题。
缺点
- 训练时间长:
由于需要逐步训练多棵树,GBDT 的训练过程可能较慢,尤其在大规模数据集上。 - 难以并行化:
传统 GBDT 的训练过程是串行的,每棵树的训练依赖于前一棵树的结果,难以实现并行计算。 - 复杂的超参数调优:
GBDT 包含多个超参数(如学习率、树的数量、树的深度等),需要仔细调优才能获得最佳性能。 - 内存消耗大:
GBDT 在训练过程中需要存储大量中间结果,对内存消耗较大。
基于优缺点,GBDT通常适用于以下场景:
-
分类问题:
- 如垃圾邮件分类、客户流失预测、欺诈检测等。
-
回归问题:
- 如房价预测、销售额预测、风险评估等。
-
排序任务:
- 如搜索引擎的结果排序、推荐系统中的内容排序等。
-
多模态数据处理:
- 能够处理多种类型的特征(如数值型、类别型),适用于特征类型多样的数据集。
现实中著名的使用案例
- Yahoo! 排序算法:
Yahoo! 在其搜索引擎排序算法中使用了 GBDT,用于提高搜索结果的相关性。 - LinkedIn:
LinkedIn 使用 GBDT 进行推荐系统和广告点击率预测,通过优化用户体验和广告投放效果。 - Facebook:
Facebook 使用 GBDT 进行广告点击率预测和内容推荐,帮助提升用户参与度和广告收入。 - 保险公司:
许多保险公司使用 GBDT 进行风险评估和欺诈检测,通过更准确地预测风险和识别欺诈行为,提高业务效率。
5. 代码实现
5.1 python函数
from sklearn.ensemble import GradientBoostingClassifier, GradientBoostingRegressor
clf = GradientBoostingClassifier(
""" 常用于分类 """
# 树的数量,即迭代次数。默认值100。调整范围(50-500)。
# 增加提高模型拟合能力,但也会增加训练时间和过拟合风险。
# 一般与 `learning_rate` 结合调整。
n_estimators=200,
# 学习率,每棵树对最终结果的贡献。默认值0.1。调整范围(0.01-0.3)。
# 较低的学习率需要更多的树,通常与 `n_estimators` 一起调整。
learning_rate=0.05,
# 每棵树的最大深度。默认值3。调整范围(3-10)。
# 较大的深度可能导致过拟合,较小的深度可能导致欠拟合。一般在3到6之间选择。
max_depth=4,
# 分裂内部节点所需的最小样本数。默认值2。调整范围(2-100)。
# 增大 `min_samples_split` 可以防止模型过拟合。
min_samples_split=5,
# 叶节点所需的最小样本数。默认值1。调整范围(1-50)。
# 增加 `min_samples_leaf` 可以防止模型过拟合。一般在1到10之间选择。
min_samples_leaf=3,
# 每棵树拟合时抽取的样本比例。默认值1.0。调整范围(0.5-1.0)。
# 较低的 `subsample` 值可以增加模型的随机性,防止过拟合。常见值为0.8。
subsample=0.8,
# 寻找最佳分裂时要考虑的特征数量。默认值None。
# 调整范围('auto', 'sqrt', 'log2', 或特定数值)。回归sqrt,分类log2
max_features='log2',
# 随机种子。默认值None。调整范围任意整数。
# 设置 `random_state` 可以保证结果的可重复性,在调参和实际应用中非常重要。
random_state=42
)
reg = GradientBoostingRegressor(
""" 常用于回归 """
# 同上
n_estimators=200,
# 同上
learning_rate=0.05,
# 同上
max_depth=4,
# 同上
min_samples_split=5,
# 同上
min_samples_leaf=3,
# 同上
subsample=0.8,
# 同上
max_features='sqrt',
# 同上
random_state=42
)
- 经常需要调整的参数
- n_estimators:调整范围为50到500。
- learning_rate:调整范围为0.01到0.3。
- max_depth:调整范围为3到10。
- subsample:引入随机性,防止过拟合,通常设置为0.8。调整范围为0.5到1.0。
- max_features:在寻找最佳分裂时考虑的特征数量,通常设置为'sqrt'或'log2'。调整范围为'sqrt', 'log2' 或特定数值。
- random_state:用于保证结果可重复性,通常设置为一个固定值。
5.2 代码实现
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
# generate toy dataset
X, y = make_classification(n_samples=1000, n_features=20, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# training model
clf = GradientBoostingClassifier(
n_estimators=200,
learning_rate=0.05,
max_depth=4,
min_samples_split=5,
min_samples_leaf=3,
subsample=0.8,
max_features='sqrt',
random_state=42
)
clf.fit(X_train, y_train)
# predict
y_pred = clf.predict_proba(X_test)[:, 1]
print("AUC Score:", roc_auc_score(y_test, y_pred))
到此,我们对GBDT已经有了一定的了解。原计划中,本想详细介绍XGBT,但考虑到篇幅原因,我决定将其作为下一篇文章进行介绍。