Hi,我是Johngo~
这些天有一个同学在字节一面的时候,在 GBDT 交流的时候,感觉差点点挂掉。好在后面的面试中表现还算可以。
现在在等待offer中,据说是问题不大。
趁这个机会,我也和大家分享一下关于 GBDT 一些理论内容。
熟悉的同学全当复习,不熟悉的同学可以学习一番。
一点介绍
梯度提升决策树(Gradient Boosting Decision Trees, GBDT)是一种常用的机器学习集成方法。
通过逐步构建一系列决策树(通常是弱学习器),每个新树都试图纠正之前所有树的误差。GBDT主要用于回归和分类任务,能够处理复杂的非线性关系和多种数据类型。
历史背景:
GBDT的发展可以追溯到1999年,当时杰罗姆·弗里德曼(Jerome H. Friedman)发表了一篇关于梯度提升算法的开创性论文。梯度提升是由他提出的一种通用方法,旨在提高预测模型的性能。最初的梯度提升框架被称为“Gradient Boosting Machines”(GBM),其中决策树是最常用的基学习器。
GBDT自提出以来,已经被广泛应用于各种机器学习任务中,并且在许多实际问题中表现出色。近年来,GBDT也得到了许多优化和扩展,例如XGBoost、LightGBM和CatBoost等变种。
理论基础
GBDT的核心思想是通过逐步构建多个决策树(弱学习器),每棵新树都试图纠正之前所有树的误差。GBDT的训练过程是一个迭代优化过程,目标是最小化目标函数(通常是损失函数)。
- 初始化模型:
初始时,模型是一个常数函数,通常选择目标变量的均值作为初始预测值:
其中, 是损失函数, 是第 个样本的实际值, 是常数值。
- 迭代构建模型:
对于 :
a. 计算残差: 计算当前模型的残差(即误差):
其中, 是第 轮的模型, 是第 个样本在第 轮的残差。
b. 拟合新树: 拟合一个新的决策树 来预测残差:
c. 更新模型: 更新模型,通过对之前模型和新拟合的树的加权和来更新模型:
其中, 是学习率(step size),控制模型更新的步伐。
- 最终模型:
经过 轮迭代后,得到最终的预测模型:
算法流程:
- 输入:
- 训练数据集
- 损失函数
- 最大迭代次数
- 学习率
- 初始化:
- 初始化模型 为常数:
- 迭代过程: 对于 到 :
a. 计算残差 :
b. 拟合新的决策树 来预测残差 :
c. 更新模型 :
- 输出:
- 最终的预测模型 :
GBDT通过迭代地构建一系列决策树,并逐步减少误差,最终得到一个强大的预测模型。
每一步中,通过计算残差并拟合新的树来捕捉数据中的剩余信息,从而不断优化模型的性能。
案例
整个案例包括数据预处理、模型训练、预测、可视化以及一些优化技巧。
我们将使用加利福尼亚住房数据集(California Housing Dataset)来进行回归任务,即预测房屋的中位数价格。
导入库
代码语言:javascript复制import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error
加载和预处理数据
代码语言:javascript复制# 加载数据集
housing = fetch_california_housing()
X = pd.DataFrame(housing.data, columns=housing.feature_names)
y = housing.target
# 分割数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 标准化特征
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
训练GBDT模型
代码语言:javascript复制# 初始化并训练GBDT模型
gbdt = GradientBoostingRegressor(n_estimators=500, learning_rate=0.1, max_depth=4, random_state=42)
gbdt.fit(X_train, y_train)
预测和评估
代码语言:javascript复制# 预测
y_train_pred = gbdt.predict(X_train)
y_test_pred = gbdt.predict(X_test)
# 计算误差
mse_train = mean_squared_error(y_train, y_train_pred)
mse_test = mean_squared_error(y_test, y_test_pred)
mae_train = mean_absolute_error(y_train, y_train_pred)
mae_test = mean_absolute_error(y_test, y_test_pred)
print(f"Training MSE: {mse_train:.4f}, Training MAE: {mae_train:.4f}")
print(f"Test MSE: {mse_test:.4f}, Test MAE: {mae_test:.4f}")
预测值与实际值的对比
代码语言:javascript复制plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_test_pred, alpha=0.5)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.title('Actual vs Predicted Values')
plt.show()
特征重要性
代码语言:javascript复制feature_importance = gbdt.feature_importances_
sorted_idx = np.argsort(feature_importance)
pos = np.arange(sorted_idx.shape[0]) 0.5
plt.figure(figsize=(12, 6))
plt.barh(pos, feature_importance[sorted_idx], align='center')
plt.yticks(pos, np.array(housing.feature_names)[sorted_idx])
plt.xlabel('Feature Importance')
plt.title('Feature Importance of GBDT')
plt.show()
算法优化
- 参数调优:使用网格搜索(Grid Search)或随机搜索(Random Search)来寻找最佳参数组合。
- 早停法:使用验证集来监控模型的性能,防止过拟合。
- 特征选择:移除无关或冗余的特征,减少模型的复杂度。
参数调优示例
代码语言:javascript复制from sklearn.model_selection import GridSearchCV
param_grid = {
'n_estimators': [100, 200, 300],
'learning_rate': [0.01, 0.05, 0.1],
'max_depth': [3, 4, 5],
'subsample': [0.8, 1.0]
}
grid_search = GridSearchCV(estimator=GradientBoostingRegressor(random_state=42), param_grid=param_grid, cv=3, n_jobs=-1, scoring='neg_mean_squared_error')
grid_search.fit(X_train, y_train)
print("Best parameters found: ", grid_search.best_params_)
早停法示例
代码语言:javascript复制from sklearn.model_selection import train_test_split
# 将训练数据再分割为训练集和验证集
X_train_sub, X_val, y_train_sub, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42)
# 初始化并训练GBDT模型
gbdt_early_stopping = GradientBoostingRegressor(n_estimators=1000, learning_rate=0.1, max_depth=4, random_state=42)
gbdt_early_stopping.fit(X_train_sub, y_train_sub)
# 记录验证集上的误差
val_errors = [mean_squared_error(y_val, y_pred) for y_pred in gbdt_early_stopping.staged_predict(X_val)]
# 找到最佳的迭代次数
best_n_estimators = np.argmin(val_errors) 1
# 使用最佳的迭代次数重新训练模型
gbdt_best = GradientBoostingRegressor(n_estimators=best_n_estimators, learning_rate=0.1, max_depth=4, random_state=42)
gbdt_best.fit(X_train, y_train)
# 评估
y_test_pred_best = gbdt_best.predict(X_test)
mse_test_best = mean_squared_error(y_test, y_test_pred_best)
print(f"Test MSE after early stopping: {mse_test_best:.4f}")
这个完整的示例展示了如何使用GBDT进行回归任务,包括数据预处理、模型训练、预测、评估和可视化。此外,还有一些优化技巧,如参数调优和早停法,以提高模型的性能和泛化能力。
算法评估
1. 准确性指标
回归任务
- 均方误差(Mean Squared Error, MSE): MSE 是预测值与实际值之间差的平方的平均值。它是一个常见的回归性能指标,用来衡量模型的预测误差。
其中, 是实际值, 是预测值, 是样本数量。
- 均绝对误差(Mean Absolute Error, MAE): MAE 是预测值与实际值之间差的绝对值的平均值。它是另一个常见的回归性能指标,用来衡量模型的预测误差。
- **决定系数(R-squared, ):** 是一个统计量,表示模型的解释力。它介于0和1之间,越接近1表示模型越好。
其中, 是实际值的平均值。
分类任务
准确率(Accuracy):
- 准确率是正确预测的样本数量与总样本数量之比。
精确率(Precision)和召回率(Recall):
- 精确率是正确预测的正类样本数量与预测为正类的样本数量之比。
- 召回率是正确预测的正类样本数量与实际正类样本数量之比。
F1分数:
- F1分数是精确率和召回率的调和平均数,用于衡量分类模型的性能。
2. 训练效率和预测效率
- 训练时间: 训练时间是模型训练所需的时间,通常会受模型复杂度和数据集大小的影响。
- 预测时间: 预测时间是模型进行一次预测所需的时间,通常在实时预测任务中需要考虑。
3. 模型复杂度和泛化能力
- 过拟合与欠拟合:
- 过拟合(Overfitting)是指模型在训练集上表现很好,但在测试集上表现很差。可以通过交叉验证和正则化等方法来检测和防止过拟合。
- 欠拟合(Underfitting)是指模型在训练集和测试集上都表现不好,通常是由于模型过于简单导致的。
- 交叉验证(Cross-Validation): 交叉验证是一种评估模型泛化能力的方法,通过将数据集分成多个折叠(fold),然后多次训练和测试模型,以获取模型性能的稳定估计。
4. 特征重要性
- 特征重要性(Feature Importance): 特征重要性度量每个特征对模型预测的影响。GBDT可以自然地提供每个特征的重要性评分,有助于理解模型并进行特征选择。
5. ROC曲线和AUC
- ROC曲线(Receiver Operating Characteristic Curve): ROC曲线是反映分类模型性能的图形,横轴为假阳性率(False Positive Rate),纵轴为真阳性率(True Positive Rate)。
- AUC(Area Under the Curve): AUC是ROC曲线下面积的大小,表示分类模型的整体性能。AUC值越接近1,模型性能越好。
综合实例
使用GBDT进行回归任务并评估其性能:
代码语言:javascript复制import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import matplotlib.pyplot as plt
import seaborn as sns
# 加载数据集
housing = fetch_california_housing()
X = pd.DataFrame(housing.data, columns=housing.feature_names)
y = housing.target
# 分割数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 标准化特征
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# 初始化并训练GBDT模型
gbdt = GradientBoostingRegressor(n_estimators=500, learning_rate=0.1, max_depth=4, random_state=42)
gbdt.fit(X_train, y_train)
# 预测
y_train_pred = gbdt.predict(X_train)
y_test_pred = gbdt.predict(X_test)
# 计算误差
mse_train = mean_squared_error(y_train, y_train_pred)
mse_test = mean_squared_error(y_test, y_test_pred)
mae_train = mean_absolute_error(y_train, y_train_pred)
mae_test = mean_absolute_error(y_test, y_test_pred)
r2_train = r2_score(y_train, y_train_pred)
r2_test = r2_score(y_test, y_test_pred)
print(f"Training MSE: {mse_train:.4f}, Training MAE: {mae_train:.4f}, Training R^2: {r2_train:.4f}")
print(f"Test MSE: {mse_test:.4f}, Test MAE: {mae_test:.4f}, Test R^2: {r2_test:.4f}")
# 可视化预测值与实际值的对比
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_test_pred, alpha=0.5)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.title('Actual vs Predicted Values')
plt.show()
# 可视化特征重要性
feature_importance = gbdt.feature_importances_
sorted_idx = np.argsort(feature_importance)
pos = np.arange(sorted_idx.shape[0]) 0.5
plt.figure(figsize=(12, 6))
plt.barh(pos, feature_importance[sorted_idx], align='center')
plt.yticks(pos, np.array(housing.feature_names)[sorted_idx])
plt.xlabel('Feature Importance')
plt.title('Feature Importance of GBDT')
plt.show()
代码中,展示了如何训练GBDT模型并使用MSE、MAE和R²等关键指标来评估其性能。同时,还展示了如何可视化预测值与实际值的对比以及特征重要性。