Boosting Over Bagging:利用梯度提升回归器提高预测精度

【精选优质专栏推荐】


每个专栏均配有案例与图文讲解,循序渐进,适合新手与进阶学习者,欢迎订阅。

在这里插入图片描述

前言

集成学习技术主要分为两类:袋装(Bagging)和提升(Boosting)。袋装通过汇总独立预测结果来提高稳定性和准确性,而提升则通过顺序纠正前一模型的错误,在每次迭代中提升性能。本文将开启对提升方法的深入探讨,首先介绍梯度提升回归器(Gradient Boosting Regressor)。通过在 Ames 房价数据集上的应用,我们将展示提升方法如何独特地增强模型性能,并为后续文章中探讨各种提升技术奠定基础。

什么是提升(Boosting)?

提升是一种集成技术,通过组合多个模型来创建一个强学习器。与可能并行构建模型的其他集成方法不同,提升按顺序添加模型,每个新模型专注于改进前一模型表现不佳的部分。通过这种方法,每次迭代都能系统性地提高集成模型的准确性,使其在处理复杂数据集时尤为有效。

提升的关键特性

  • 顺序学习:提升一次构建一个模型。每个新模型从前一个模型的不足中学习,从而逐步提升对数据复杂性的捕捉能力。
  • 错误纠正:新模型重点关注之前预测错误的样本,持续增强集成模型捕捉难以预测模式的能力。
  • 模型复杂度:随着模型数量的增加,集成模型的复杂度上升,从而能够有效捕捉复杂的数据结构。

提升 vs. 袋装(Bagging)

袋装通过构建多个模型(通常是独立的)并结合它们的输出,提高集成模型的整体性能,主要是通过降低训练数据噪声导致的过拟合风险。相比之下,提升通过顺序学习前一模型的错误,专注于提高预测准确性,使其能够更精细地适应数据。

Scikit-learn 提供了多种提升实现,以适应不同需求和数据场景:

  • AdaBoost 回归器:使用一系列弱学习器,并根据前一模型的错误调整其关注点,改进前一模型表现不足的部分。
  • 梯度提升回归器(Gradient Boosting Regressor):一次构建一个模型,每个新模型训练以纠正前一个模型的残差(错误),通过精细调整提高准确性。
  • HistGradient Boosting 回归器:梯度提升的优化版本,针对大数据集设计,通过使用直方图近似梯度加快计算速度。

每种方法都利用提升的核心原则来增强组成模型的性能,展示了该方法在预测建模中应对挑战的多样性和威力。在本文后续部分,我们将通过 Ames 房价数据集演示梯度提升回归器的实际应用。

模型性能比较:从决策树基线到梯度提升集成

在从提升的理论概念过渡到实际应用时,本节将使用精心预处理的 Ames 房价数据集演示梯度提升回归器。我们在各种基于树的模型上保持一致的预处理步骤,确保观察到的性能提升能够直接归因于模型本身,为有效比较奠定基础。

以下代码建立了比较分析框架:首先使用单棵决策树(非集成方法)作为基线。这一基线可以清晰展示集成方法带来的渐进性优势。

随后,我们配置了袋装(Bagging)、随机森林(Random Forest)和梯度提升回归器的两种版本,分别使用 100 棵树和 200 棵树,以探索这些集成技术相对于基线的性能提升。

# 导入预处理与建模所需库
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import OrdinalEncoder, OneHotEncoder, FunctionTransformer
from sklearn.ensemble import GradientBoostingRegressor, BaggingRegressor, RandomForestRegressor

# 载入数据集
Ames = pd.read_csv('Ames.csv')

# 调整分类变量的数据类型
for col in ['MSSubClass', 'YrSold', 'MoSold']:
    Ames[col] = Ames[col].astype('object')

# 排除 'PID' 和 'SalePrice' 作为特征,单独处理 'Electrical' 列
numeric_features = Ames.select_dtypes(include=['int64', 'float64']).drop(columns=['PID', 'SalePrice']).columns
categorical_features = Ames.select_dtypes(include=['object']).columns.difference(['Electrical'])
electrical_feature = ['Electrical']

# 根据数据字典手动指定有序编码类别
ordinal_order = {
    'Electrical': ['Mix', 'FuseP', 'FuseF', 'FuseA', 'SBrkr'],
    'LotShape': ['IR3', 'IR2', 'IR1', 'Reg'],
    'Utilities': ['ELO', 'NoSeWa', 'NoSewr', 'AllPub'],
    'LandSlope': ['Sev', 'Mod', 'Gtl'],
    'ExterQual': ['Po', 'Fa', 'TA', 'Gd', 'Ex'],
    'ExterCond': ['Po', 'Fa', 'TA', 'Gd', 'Ex'],
    'BsmtQual': ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'],
    'BsmtCond': ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'],
    'BsmtExposure': ['None', 'No', 'Mn', 'Av', 'Gd'],
    'BsmtFinType1': ['None', 'Unf', 'LwQ', 'Rec', 'BLQ', 'ALQ', 'GLQ'],
    'BsmtFinType2': ['None', 'Unf', 'LwQ', 'Rec', 'BLQ', 'ALQ', 'GLQ'],
    'HeatingQC': ['Po', 'Fa', 'TA', 'Gd', 'Ex'],
    'KitchenQual': ['Po', 'Fa', 'TA', 'Gd', 'Ex'],
    'Functional': ['Sal', 'Sev', 'Maj2', 'Maj1', 'Mod', 'Min2', 'Min1', 'Typ'],
    'FireplaceQu': ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'],
    'GarageFinish': ['None', 'Unf', 'RFn', 'Fin'],
    'GarageQual': ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'],
    'GarageCond': ['None', 'Po', 'Fa', 'TA', 'Gd', 'Ex'],
    'PavedDrive': ['N', 'P', 'Y'],
    'PoolQC': ['None', 'Fa', 'TA', 'Gd', 'Ex'],
    'Fence': ['None', 'MnWw', 'GdWo', 'MnPrv', 'GdPrv']
}

# 提取所有有序特征
ordinal_features = list(ordinal_order.keys())
ordinal_except_electrical = [feature for feature in ordinal_features if feature != 'Electrical']

# 定义不同特征类型的转换器
electrical_transformer = Pipeline(steps=[
    ('impute_electrical', SimpleImputer(strategy='most_frequent')),
    ('ordinal_electrical', OrdinalEncoder(categories=[ordinal_order['Electrical']]))
])

numeric_transformer = Pipeline(steps=[
    ('impute_mean', SimpleImputer(strategy='mean'))
])

categorical_imputer = SimpleImputer(strategy='constant', fill_value='None')

ordinal_transformer = Pipeline([
    ('impute_ordinal', categorical_imputer),
    ('ordinal', OrdinalEncoder(categories=[ordinal_order[feature] for feature in ordinal_features if feature in ordinal_except_electrical]))
])

nominal_features = [feature for feature in categorical_features if feature not in ordinal_features]
categorical_transformer = Pipeline([
    ('impute_nominal', categorical_imputer),
    ('onehot', OneHotEncoder(handle_unknown='ignore'))
])

# 合并数值、有序、名义以及特定电气特征的预处理器
preprocessor = ColumnTransformer(
    transformers=[
        ('electrical', electrical_transformer, ['Electrical']),
        ('num', numeric_transformer, numeric_features),
        ('ordinal', ordinal_transformer, ordinal_except_electrical),
        ('nominal', categorical_transformer, nominal_features)
])

# 定义模型管道,包括梯度提升回归器
models = {
    'Decision Tree (1 Tree)': DecisionTreeRegressor(random_state=42),
    'Bagging Regressor (100 Decision Trees)': BaggingRegressor(base_estimator=DecisionTreeRegressor(random_state=42),
                                                               n_estimators=100, random_state=42),
    'Bagging Regressor (200 Decision Trees)': BaggingRegressor(base_estimator=DecisionTreeRegressor(random_state=42),
                                                               n_estimators=200, random_state=42),
    'Random Forest (Default of 100 Trees)': RandomForestRegressor(random_state=42),
    'Random Forest (200 Trees)': RandomForestRegressor(n_estimators=200, random_state=42),
    'Gradient Boosting Regressor (Default of 100 Trees)': GradientBoostingRegressor(random_state=42),
    'Gradient Boosting Regressor (200 Trees)': GradientBoostingRegressor(n_estimators=200, random_state=42)
}

# 使用交叉验证评估模型并输出结果
results = {}
for name, model in models.items():
    model_pipeline = Pipeline([
        ('preprocessor', preprocessor),
        ('regressor', model)
    ])
    scores = cross_val_score(model_pipeline, Ames.drop(columns='SalePrice'), Ames['SalePrice'], cv=5)
    results[name] = round(scores.mean(), 4)
    print(f"{name}: Mean CV R² = {results[name]}")

交叉验证结果(平均 R²):

  • Decision Tree (1 Tree): 0.7663
  • Bagging Regressor (100 Decision Trees): 0.8957
  • Bagging Regressor (200 Decision Trees): 0.897
  • Random Forest (100 Trees 默认): 0.8954
  • Random Forest (200 Trees): 0.8969
  • Gradient Boosting Regressor (100 Trees 默认): 0.9027
  • Gradient Boosting Regressor (200 Trees): 0.9061

结果分析:

1.基线与提升:
以单棵决策树为基线(R²=0.7663),引入更复杂的模型后性能显著提升。Bagging 和随机森林通过增加树的数量都显示了集成方法降低误差的能力。

2.梯度提升回归器优势:
梯度提升回归器(GBR)表现尤为突出。默认 100 棵树即可达到 R²=0.9027,增加到 200 棵树进一步提升到 0.9061,显示其顺序优化策略的有效性。

3.树数量的边际效益:
增加树数量通常提升性能,但增量收益递减。Bagging、随机森林和梯度提升模型均呈现此趋势,提示在资源有限时需权衡计算成本与性能收益。

这些结果显示梯度提升回归器的稳健性能,充分利用了完整预处理和顺序学习策略。接下来可通过调整学习率进一步优化模型的预测精度。

通过调整学习率优化梯度提升

学习率(learning_rate)是提升模型(如梯度提升回归器)的特有参数,不同于决策树或随机森林等模型,它们没有直接对应的参数。调整学习率可以更深入理解提升的机制,并通过精细控制每棵树的学习幅度来增强模型的预测能力。

在梯度提升回归器及其他基于梯度下降的算法中,学习率是一个关键超参数,用于控制模型学习的速度。它核心上影响模型在训练过程中朝最优解前进的步长大小:

  • 步长大小:学习率决定了每次训练时对模型权重更新的幅度。较高的学习率更新幅度大,使模型学习更快,但可能会跳过最优解;较低的学习率更新幅度小,学习更慢,但精度可能更高。

  • 对模型训练的影响

    收敛性:过高的学习率可能导致训练过快收敛到次优解,甚至无法收敛。
    准确性与过拟合:过低的学习率可能导致模型学习过慢,需要更多树才能达到类似准确性,若未监控可能引发过拟合。

  • 调优:合理选择学习率需要在速度与准确性之间权衡,通常通过尝试或系统化方法(如 GridSearchCV、RandomizedSearchCV)完成。

调整学习率可以控制提升模型适应误差复杂性的速度,是提升算法中微调模型性能的重要工具。

为了优化learning_rate,我们使用 GridSearchCV,这是一种系统方法,它将探索预定义值([0.001, 0.01, 0.1, 0.2, 0.3])以确定最有效的设置,以提高我们模型的准确性:

from sklearn.model_selection import GridSearchCV

param_grid = {
    'regressor__learning_rate': [0.001, 0.01, 0.1, 0.2, 0.3]
}

grid_search = GridSearchCV(model_pipeline, param_grid, cv=5, scoring='r2', verbose=1)
grid_search.fit(Ames.drop(columns='SalePrice'), Ames['SalePrice'])

print("最佳参数(Grid Search):", grid_search.best_params_)
print("最佳评分(Grid Search):", round(grid_search.best_score_, 4))

GridSearchCV 结果

  • 最佳参数:{‘regressor__learning_rate’: 0.1}
  • 最佳评分:0.9061

结果显示,学习率 0.1 与默认值相符,对于我们的数据集和预处理设置,微调此值变化不大。

与 GridSearchCV 不同,RandomizedSearchCV 从连续区间中随机选择值,有助于在标准值之间进行更精确的优化,从而更全面地了解学习率微小变化对性能的影响:

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform

param_dist = {
    'regressor__learning_rate': uniform(0.001, 0.299)  # 在 0.001 到 0.3 之间均匀分布
}

random_search = RandomizedSearchCV(model_pipeline, param_distributions=param_dist,
                                   n_iter=50, cv=5, scoring='r2', verbose=1, random_state=42)
random_search.fit(Ames.drop(columns='SalePrice'), Ames['SalePrice'])

print("最佳参数(Random Search):", random_search.best_params_)
print("最佳评分(Random Search):", round(random_search.best_score_, 4))

与 GridSearchCV 对比,RandomizedSearchCV 发现了略微不同的最优学习率,约为 0.158,从而提升了模型性能。这一改进凸显了随机搜索的价值,尤其在模型微调中,因为它可以探索更为多样的可能性,并可能产生更优配置。

Fitting 5 folds for each of 50 candidates, totalling 250 fits
最佳参数(Random Search):{'regressor__learning_rate': 0.1579021730580391}
最佳评分(Random Search):0.9134

通过 RandomizedSearchCV 的优化显示了其有效性,确定了一个学习率,使模型性能达到新高度,R² 达到 0.9134。这些通过 GridSearchCV 和 RandomizedSearchCV 调整学习率的实验,展示了调优梯度提升模型所需的精细平衡,同时也强调了结合系统化搜索和随机搜索策略以全面优化模型的好处。

受这些优化策略取得的收益鼓舞,我们接下来将同时微调 learning_rate 和 n_estimators。此阶段旨在通过探索这两个关键参数的组合影响,进一步挖掘最优设置,从而提升梯度提升回归器的性能。

最终优化:同时调节学习率和树的数量

在前期发现的基础上,我们现在进入更全面的优化方法,即同时调节 learning_rate 和 n_estimators。此双参数调优旨在探索两者协同作用,从而可能进一步提升梯度提升回归器的性能。

我们首先使用 GridSearchCV 系统性地探索 learning_rate 与 n_estimators 的组合,提供一种结构化方式来评估两参数变化对模型准确性的影响。

# 基于之前的代码块
# 'preprocessor' 已经设置为预处理管道
model_pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('regressor', GradientBoostingRegressor(random_state=42))
])

# GridSearchCV 参数网格
param_grid = {
    'regressor__learning_rate': [0.001, 0.01, 0.1, 0.2, 0.3],
    'regressor__n_estimators': [100, 200, 300, 400, 500]
}

grid_search = GridSearchCV(model_pipeline, param_grid, cv=5, scoring='r2', verbose=1)
grid_search.fit(Ames.drop(columns='SalePrice'), Ames['SalePrice'])

print("最佳参数(Grid Search):", grid_search.best_params_)
print("最佳评分(Grid Search):", round((grid_search.best_score_), 4))

GridSearchCV 共评估了 25 种组合,5 折交叉验证,总计 125 次拟合:

Fitting 5 folds for each of 25 candidates, totalling 125 fits
最佳参数(Grid Search):{'regressor__learning_rate': 0.1, 'regressor__n_estimators': 500}
最佳评分(Grid Search):0.9089

结果确认学习率 0.1(默认设置)仍然有效,但增加到 500 棵树可略微提升模型性能,R² 提升至 0.9089。相比之前 200 棵树和 learning_rate 0.1 时的 R² 0.9061,这是小幅提升。有趣的是,之前的随机搜索仅用 200 棵树和约 0.158 学习率便得到了更好的 0.9134,说明探索更广泛的参数空间可带来优化潜力。

为确保充分探索参数空间并发掘潜在更优配置,我们接下来使用 RandomizedSearchCV。该方法通过从连续参数分布采样,提供更具探索性和不确定性的优化方式。

from scipy.stats import uniform, randint

param_dist = {
    'regressor__learning_rate': uniform(0.001, 0.299),  # 在 0.001 到 0.3 均匀分布
    'regressor__n_estimators': randint(100, 501)  # 100 到 500 的整数均匀分布
}

random_search = RandomizedSearchCV(model_pipeline, param_distributions=param_dist,
                                   n_iter=50, cv=5, scoring='r2', verbose=1, random_state=42)
random_search.fit(Ames.drop(columns='SalePrice'), Ames['SalePrice'])

print("最佳参数(Random Search):", random_search.best_params_)
print("最佳评分(Random Search):", round((random_search.best_score_), 4))

RandomizedSearchCV 扩展了搜索范围,评估了 50 种配置,5 折交叉验证,总计 250 次拟合:

Fitting 5 folds for each of 50 candidates, totalling 250 fits
最佳参数(Random Search):{'regressor__learning_rate': 0.12055843054286139, 'regressor__n_estimators': 287}
最佳评分(Random Search):0.9158

随机搜索找到更优组合,学习率约为 0.121,n_estimators 为 287,使 R² 达到 0.9158,凸显随机调参在发现最优设置上的潜力。

为了验证通过调参获得的性能提升,我们将对配置为最佳参数(n_estimators=287,learning_rate≈0.121)的梯度提升回归器进行最终 5 折交叉验证。

model_pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('regressor', GradientBoostingRegressor(n_estimators=287, learning_rate=0.12055843054286139, random_state=42))
])

X = Ames.drop(columns='SalePrice')
y = Ames['SalePrice']

cv_scores = cross_val_score(model_pipeline, X, y, cv=5, scoring='r2')
print("Performance of Gradient Boosting Regressor with tuned parameters:", round(cv_scores.mean(), 4))

最终输出验证了调参后的梯度提升回归器性能:

Performance of Gradient Boosting Regressor with tuned parameters: 0.9158

通过同时优化 learning_rate 和 n_estimators,我们实现了 R²=0.9158 的成绩。这不仅验证了调参带来的性能提升,也强调了梯度提升回归器在整个数据集上稳定适应和表现的能力。

总结

本文探讨了梯度提升回归器(GBR)的功能,从理解提升的基础概念到使用 Ames 房价数据集进行高级优化技术。重点分析了 GBR 的关键参数,如树的数量和学习率,这些参数对于提升模型的准确性和效率至关重要。通过系统化和随机化方法,本文演示了如何使用 GridSearchCV 和 RandomizedSearchCV 对这些参数进行微调,从而显著提升模型性能。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋说

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值