字节一面,差点跪在 GBDT !!

2024-07-12 13:37:39 浏览数 (2)

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的训练过程是一个迭代优化过程,目标是最小化目标函数(通常是损失函数)。

  1. 初始化模型:

初始时,模型是一个常数函数,通常选择目标变量的均值作为初始预测值:

其中, 是损失函数, 是第 个样本的实际值, 是常数值。

  1. 迭代构建模型:

对于 :

a. 计算残差: 计算当前模型的残差(即误差):

其中, 是第 轮的模型, 是第 个样本在第 轮的残差。

b. 拟合新树: 拟合一个新的决策树 来预测残差:

c. 更新模型: 更新模型,通过对之前模型和新拟合的树的加权和来更新模型:

其中, 是学习率(step size),控制模型更新的步伐。

  1. 最终模型:

经过 轮迭代后,得到最终的预测模型:

算法流程:

  1. 输入:
  • 训练数据集
  • 损失函数
  • 最大迭代次数
  • 学习率
  1. 初始化:
  • 初始化模型 为常数:
  1. 迭代过程: 对于 到 :

a. 计算残差 :

b. 拟合新的决策树 来预测残差 :

c. 更新模型 :

  1. 输出:
  • 最终的预测模型 :

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()

算法优化

  1. 参数调优:使用网格搜索(Grid Search)或随机搜索(Random Search)来寻找最佳参数组合。
  2. 早停法:使用验证集来监控模型的性能,防止过拟合。
  3. 特征选择:移除无关或冗余的特征,减少模型的复杂度。
参数调优示例
代码语言: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²等关键指标来评估其性能。同时,还展示了如何可视化预测值与实际值的对比以及特征重要性。

0 人点赞