【机器学习第一期(Python)】梯度提升决策树 GBDT

发布于:2025-06-28 ⋅ 阅读:(21) ⋅ 点赞:(0)

梯度提升决策树(GBDT:Gradient Boosting Decision Tree)是一种集成学习方法,广泛用于分类和回归任务。它结合了多个 弱学习器(通常是决策树) 的预测结果,通过梯度下降策略不断优化模型残差,从而提升整体预测性能。

基础知识

决策树(Decision Tree)

决策树(Decision Tree) 是一种基于树形结构进行决策分析的方法。它利用树形结构来表示各种决策结果之间的关系,并且可以用于 分类和回归分析等任务。其关键思想是通过递归地划分数据集,找到最优的特征及其划分点,使得子数据集尽可能纯净(即目标变量具有较小的方差或熵)。

决策树的构建过程可以分为两个步骤:

  • 第一步是选择一个合适的划分属性,将数据集划分成多个子集;
  • 第二步是针对每个子集递归地重复进行第一步,直到所有的子集都属于同一类别或者达到了预定义的停止条件。

在决策树的构建过程中,需要选择一个合适的划分属性。常见的划分属性选择方法有信息增益、信息增益比、基尼指数等。在选择划分属性时,通常选择对分类结果的影响最大的属性,以达到最优的划分效果。

决策树的优点包括易于理解和解释、计算复杂度低等。同时,决策树也有一些缺点,比如容易过拟合、对数据的噪声和异常值敏感等。
在这里插入图片描述

回归树

回归树(Regression Tree) 是决策树的回归版本,主要用于预测连续变量。它的核心思想是:

  • 选择一个特征及其划分点,使得划分后子数据集的均方误差(MSE)最小。
  • 递归执行上述过程,直到达到停止条件(如叶子节点样本数小于某个阈值)。
  • 预测时,输入样本沿着树结构从根节点到叶子节点,并返回叶子节点的均值作为最终预测值。

📌 一、GBDT 原理概述

1.1 Boosting 思想

Boosting 是一种序列学习方法,它通过加法模型将多个弱学习器组合成一个强学习器。
每一次迭代,模型都会试图拟合上一次模型的预测误差(残差)。

1.2 GBDT 的关键思想

使用CART回归树作为弱学习器。
每一步都在现有模型的基础上,通过最小化损失函数的负梯度方向来构建新的决策树。

GBDT 特性总结

特性 描述
基学习器 回归树(CART)
适用任务 回归、二分类、多分类
优点 高准确率、无需特征归一化、可处理非线性关系
缺点 训练时间较长、难以并行

🧠 二、GBDT 算法流程

设训练数据为 ( x i , y i ) i = 1 n {(x_i, y_i)}_{i=1}^{n} (xi,yi)i=1n,我们的目标是学习一个函数 F ( x ) F(x) F(x) 来预测 y y y

2.1 初始化模型

选择一个常数值 F 0 ( x ) F_0(x) F0(x) 作为初始模型,通常是使损失函数最小的常数:

F_0(x) = \arg\min_\gamma \sum_{i=1}^n L(y_i, \gamma)

2.2 每一轮迭代步骤(共 M M M 轮)

m = 1 m = 1 m=1 M M M

1、计算残差(负梯度):

r_{im} = -\left[\frac{\partial L(y_i, F(x_i))}{\partial F(x_i)}\right]_{F=F_{m-1}}

实际上,这就是损失函数在当前模型处的梯度。

2、拟合一棵回归树 h m ( x ) h_m(x) hm(x) 来预测残差 r i m r_{im} rim

3、计算每个叶子节点的输出值 γ j m \gamma_{jm} γjm,使得损失最小:

\gamma_{jm} = \arg\min_\gamma \sum_{x_i \in R_{jm}} L(y_i, F_{m-1}(x_i) + \gamma)

4、更新模型:

F_m(x) = F_{m-1}(x) + \nu \cdot h_m(x)

其中 ν \nu ν 是学习率,控制每次迭代的步长。

🐍 三、Python 实现步骤(以回归为例)

我们使用 sklearn 中的 GradientBoostingRegressor 实现一个简单的 GBDT 模型。

✨ 可调参数总结(调参重点)

参数 含义 建议
n_estimators 树的数量 越大模型越复杂,需配合早停
learning_rate 学习率 小学习率需较多树
max_depth 树的最大深度 控制模型复杂度
subsample 子样本比例 小于1可防止过拟合
min_samples_split 内部节点再划分所需的最小样本数 控制过拟合

3.1 数据准备

from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split

X, y = make_regression(n_samples=1000, n_features=10, noise=0.1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

3.2 模型训练

from sklearn.ensemble import GradientBoostingRegressor

model = GradientBoostingRegressor(
    n_estimators=100,    # 弱学习器数量
    learning_rate=0.1,   # 学习率
    max_depth=3,         # 每棵树的深度
    random_state=42
)

model.fit(X_train, y_train)

3.3 模型评估

from sklearn.metrics import mean_squared_error

y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)

print(f"Test MSE: {mse:.4f}")

四、完整案例(Python)

任务:使用 GBDT 拟合一个非线性函数
数据:人工生成的非线性回归数据
模型:Gradient Boosting Regressor

可视化:

  • 模型拟合曲线
  • 残差分析图
  • 训练集 vs 测试集预测效果

在这里插入图片描述

左图:拟合效果:拟合曲线很好地捕捉了数据的非线性趋势。

  • 蓝点:训练数据
  • 红点:测试数据
  • 黑线:GBDT 拟合曲线

右图:残差图:残差应随机分布在 y=0 附近,没有明显模式,表明模型拟合良好。

输出结果为:

Train MSE: 0.0110
Test MSE: 0.0465

完整Python实现代码如下:(可以尝试调整参数如 n_estimators, learning_rate, max_depth 来观察拟合效果的变化)

import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

# 1. 生成非线性数据
np.random.seed(42)
X = np.linspace(0, 10, 200).reshape(-1, 1)
y = np.sin(X).ravel() + np.random.normal(0, 0.2, X.shape[0])  # 添加噪声

# 2. 拆分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 3. 拟合 GBDT 模型
model = GradientBoostingRegressor(
    n_estimators=100,
    learning_rate=0.1,
    max_depth=3,
    random_state=42
)
model.fit(X_train, y_train)

# 4. 预测
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)

# 5. 模型评估
train_mse = mean_squared_error(y_train, y_train_pred)
test_mse = mean_squared_error(y_test, y_test_pred)

print(f"Train MSE: {train_mse:.4f}")
print(f"Test MSE: {test_mse:.4f}")

# 6. 可视化:模型拟合效果
plt.figure(figsize=(12, 6))

# 原始数据 + 拟合线
plt.subplot(1, 2, 1)
plt.scatter(X_train, y_train, color='lightblue', label='Train Data', alpha=0.6)
plt.scatter(X_test, y_test, color='lightcoral', label='Test Data', alpha=0.6)

# 绘制平滑预测曲线
X_all = np.linspace(0, 10, 1000).reshape(-1, 1)
y_all_pred = model.predict(X_all)
plt.plot(X_all, y_all_pred, color='black', label='GBDT Prediction', linewidth=2)

plt.title("GBDT Model Fit")
plt.xlabel("X")
plt.ylabel("y")
plt.legend()
plt.grid(True)

# 7. 可视化:残差图
plt.subplot(1, 2, 2)
train_residuals = y_train - y_train_pred
test_residuals = y_test - y_test_pred

plt.scatter(y_train_pred, train_residuals, color='blue', alpha=0.6, label='Train Residuals')
plt.scatter(y_test_pred, test_residuals, color='red', alpha=0.6, label='Test Residuals')
plt.axhline(y=0, color='black', linestyle='--')
plt.xlabel("Predicted y")
plt.ylabel("Residuals")
plt.title("Residual Plot")
plt.legend()
plt.grid(True)

plt.tight_layout()
plt.show()

参考