注意
转到最后下载完整示例代码,或通过 JupyterLite 或 Binder 在浏览器中运行此示例。
比较随机森林和直方图梯度提升模型#
在此示例中,我们比较了随机森林(RF)和直方图梯度提升(HGBT)模型在回归数据集上的得分和计算时间方面的性能,尽管**此处提出的所有概念也适用于分类问题**。
比较通过改变控制每个估计器树数量的参数进行
n_estimators控制森林中的树数量。这是一个固定值。max_iter是基于梯度提升模型的最大迭代次数。对于回归和二元分类问题,迭代次数对应于树的数量。此外,模型所需的实际树数量取决于停止标准。
HGBT 使用梯度提升通过将每棵树拟合到损失函数对预测值的负梯度来迭代改进模型的性能。另一方面,RF 基于装袋,使用多数投票来预测结果。
有关集成模型的更多信息,请参阅用户指南,或参阅直方图梯度提升树中的特征以获取展示 HGBT 模型其他功能的示例。
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
加载数据集#
from sklearn.datasets import fetch_california_housing
X, y = fetch_california_housing(return_X_y=True, as_frame=True)
n_samples, n_features = X.shape
HGBT 使用基于直方图的算法对分箱特征值进行操作,可以有效处理具有大量特征(数万个样本或更多)的大型数据集(参阅为什么它更快)。scikit-learn 中 RF 的实现不使用分箱,依赖于精确分割,这在计算上可能很昂贵。
print(f"The dataset consists of {n_samples} samples and {n_features} features")
The dataset consists of 20640 samples and 8 features
计算得分和计算时间#
请注意,HistGradientBoostingClassifier 和 HistGradientBoostingRegressor 的许多实现部分默认是并行化的。
RandomForestRegressor 和 RandomForestClassifier 的实现也可以通过使用 n_jobs 参数在多个核心上运行,这里设置为与主机上的物理核心数匹配。有关更多信息,请参阅并行性。
import joblib
N_CORES = joblib.cpu_count(only_physical_cores=True)
print(f"Number of physical cores: {N_CORES}")
Number of physical cores: 2
与 RF 不同,HGBT 模型提供了一个早停选项(参阅梯度提升中的早停),以避免添加不必要的树。在内部,算法使用一个样本外集来计算模型在每次添加树时的泛化性能。因此,如果泛化性能在超过 n_iter_no_change 次迭代后没有改善,它就会停止添加树。
这两个模型的其他参数都经过了调整,但为了使示例保持简单,此处未显示该过程。
import pandas as pd
from sklearn.ensemble import HistGradientBoostingRegressor, RandomForestRegressor
from sklearn.model_selection import GridSearchCV, KFold
models = {
"Random Forest": RandomForestRegressor(
min_samples_leaf=5, random_state=0, n_jobs=N_CORES
),
"Hist Gradient Boosting": HistGradientBoostingRegressor(
max_leaf_nodes=15, random_state=0, early_stopping=False
),
}
param_grids = {
"Random Forest": {"n_estimators": [10, 20, 50, 100]},
"Hist Gradient Boosting": {"max_iter": [10, 20, 50, 100, 300, 500]},
}
cv = KFold(n_splits=4, shuffle=True, random_state=0)
results = []
for name, model in models.items():
grid_search = GridSearchCV(
estimator=model,
param_grid=param_grids[name],
return_train_score=True,
cv=cv,
).fit(X, y)
result = {"model": name, "cv_results": pd.DataFrame(grid_search.cv_results_)}
results.append(result)
注意
调整 RF 的 n_estimators 通常会导致计算机资源浪费。实际上,只需确保它足够大,以至于将其值加倍不会导致测试得分显着提高。
绘制结果#
我们可以使用 plotly.express.scatter 来可视化计算时间和平均测试得分之间的权衡。将光标悬停在给定点上会显示相应的参数。误差棒对应于交叉验证中不同折叠计算的一个标准差。
import plotly.colors as colors
import plotly.express as px
from plotly.subplots import make_subplots
fig = make_subplots(
rows=1,
cols=2,
shared_yaxes=True,
subplot_titles=["Train time vs score", "Predict time vs score"],
)
model_names = [result["model"] for result in results]
colors_list = colors.qualitative.Plotly * (
len(model_names) // len(colors.qualitative.Plotly) + 1
)
for idx, result in enumerate(results):
cv_results = result["cv_results"].round(3)
model_name = result["model"]
param_name = next(iter(param_grids[model_name].keys()))
cv_results[param_name] = cv_results["param_" + param_name]
cv_results["model"] = model_name
scatter_fig = px.scatter(
cv_results,
x="mean_fit_time",
y="mean_test_score",
error_x="std_fit_time",
error_y="std_test_score",
hover_data=param_name,
color="model",
)
line_fig = px.line(
cv_results,
x="mean_fit_time",
y="mean_test_score",
)
scatter_trace = scatter_fig["data"][0]
line_trace = line_fig["data"][0]
scatter_trace.update(marker=dict(color=colors_list[idx]))
line_trace.update(line=dict(color=colors_list[idx]))
fig.add_trace(scatter_trace, row=1, col=1)
fig.add_trace(line_trace, row=1, col=1)
scatter_fig = px.scatter(
cv_results,
x="mean_score_time",
y="mean_test_score",
error_x="std_score_time",
error_y="std_test_score",
hover_data=param_name,
)
line_fig = px.line(
cv_results,
x="mean_score_time",
y="mean_test_score",
)
scatter_trace = scatter_fig["data"][0]
line_trace = line_fig["data"][0]
scatter_trace.update(marker=dict(color=colors_list[idx]))
line_trace.update(line=dict(color=colors_list[idx]))
fig.add_trace(scatter_trace, row=1, col=2)
fig.add_trace(line_trace, row=1, col=2)
fig.update_layout(
xaxis=dict(title="Train time (s) - lower is better"),
yaxis=dict(title="Test R2 score - higher is better"),
xaxis2=dict(title="Predict time (s) - lower is better"),
legend=dict(x=0.72, y=0.05, traceorder="normal", borderwidth=1),
title=dict(x=0.5, text="Speed-score trade-off of tree-based ensembles"),
)
HGBT 和 RF 模型在增加集成中的树数量时都会有所改善。然而,得分达到了一个平台期,此时添加新的树只会使拟合和得分变慢。RF 模型更早达到这个平台期,并且永远无法达到最大 HGBDT 模型的测试得分。
请注意,上面图表中显示的结果在不同运行之间可能会略有变化,在其他机器上运行时变化会更显著:尝试在您自己的本地机器上运行此示例。
总的来说,人们通常会观察到基于直方图的梯度提升模型在“测试得分 vs 训练速度权衡”方面统一优于随机森林模型(HGBDT 曲线应该位于 RF 曲线的左上方,从不交叉)。“测试得分 vs 预测速度”权衡也可能更有争议,但它通常对 HGBDT 有利。检查两种模型(带超参数调整)并比较它们在您的特定问题上的性能以确定哪个模型最合适总是明智之举,但**HGBT 几乎总能提供比 RF 更有利的速度-准确性权衡**,无论是使用默认超参数还是包括超参数调整成本。
尽管如此,这条经验法则有一个例外:当训练具有大量可能类别的多类分类模型时,HGBDT 在每次提升迭代中内部拟合一棵树,而 RF 模型使用的树天生就是多类的,这在这种情况下应该会提高 RF 模型的速度准确性权衡。
脚本总运行时间: (0 minutes 39.620 seconds)
相关示例