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)来进行回归任务,即预测房屋的中位数价格。
导入库
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
加载和预处理数据
# 加载数据集
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模型
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)
print(f"Training MSE: {mse_train:.4f}, Training MAE: {mae_train:.4f}")
print(f"Test MSE: {mse_test:.4f}, Test MAE: {mae_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()
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_)
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进行回归任务,包括数据预处理、模型训练、预测、评估和可视化。此外,还有一些优化技巧,如参数调优和早停法,以提高模型的性能和泛化能力。
其中, 是实际值, 是预测值, 是样本数量。
其中, 是实际值的平均值。
准确率(Accuracy):
精确率(Precision)和召回率(Recall):
F1分数:
使用GBDT进行回归任务并评估其性能:
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²等关键指标来评估其性能。同时,还展示了如何可视化预测值与实际值的对比以及特征重要性。