在本例中,比较了随机森林(RF)和直方图梯度提升(HGBT)模型在回归数据集上的得分和计算时间。尽管这里介绍的所有概念也适用于分类问题,但将重点放在回归问题上。通过调整控制每估计器中树的数量的参数来进行比较:n_estimators 控制森林中的树的数量,它是一个固定数字;max_iter 是梯度提升模型中的最大迭代次数。对于回归和二元分类问题,迭代次数对应于树的数量。此外,模型实际需要的树的数量取决于停止标准。
HGBT 使用基于直方图的算法来处理分箱特征值,可以高效地处理大型数据集(数万个样本或更多)和具有大量特征(参见为什么更快)。scikit-learn 实现的 RF 不使用分箱,依赖于精确分割,这可能在计算上非常昂贵。
注意到,HistGradientBoostingClassifier 和 HistGradientBoostingRegressor 的许多实现部分默认是并行化的。RandomForestRegressor 和 RandomForestClassifier 的实现也可以通过使用 n_jobs 参数在多个核心上运行,这里设置为与主机机器上的物理核心数量相匹配。与 RF 不同,HGBT 模型提供了一个早期停止选项(参见梯度提升中的早期停止),以避免添加新的不必要的树。在内部,算法使用一个样本外集合来计算每次添加树时模型的泛化性能。因此,如果泛化性能在超过 n_iter_no_change 次迭代中没有改善,它将停止添加树。
使用 plotly.express.scatter 可视化计算时间和平均测试得分之间的权衡。将光标悬停在给定点上会显示相应的参数。误差条对应于在交叉验证的不同折叠中计算的标准差。
直方图梯度提升模型和随机森林模型在增加集成中的树的数量时都会有所改善。然而,得分达到一个平台,在这个平台上添加新的树只会使拟合和评分变慢。随机森林模型更早地达到这样的平台,并且永远无法达到最大 HGBDT 模型的测试得分。请注意,上述图表上显示的结果在每次运行时可能会略有变化,甚至在其他机器上运行时变化更大:尝试在自己的本地机器上运行此示例。
通常,人们应该经常观察到,基于直方图的梯度提升模型在“测试得分与训练速度权衡”方面普遍优于随机森林模型(HGBDT 曲线应该在 RF 曲线的左上方,永远不会交叉)。“测试得分与预测速度权衡”也可能更有争议,但通常对 HGBDT 更有利。检查这两种类型的模型(带有超参数调整)并比较它们在特定问题上的性能以确定哪个模型最适合总是一个好主意,但 HGBT 几乎总是提供比 RF 更有利的速度-准确性权衡,无论是使用默认超参数还是包括超参数调整成本。
然而,这个经验法则有一个例外:当使用大量可能的类别训练多类分类模型时,HGBDT 在每次提升迭代中内部拟合每个类别的一棵树,而 RF 模型使用的树自然是多类的,这应该可以改善 RF 模型在这种情况下的速度-准确性权衡。
from sklearn.datasets import fetch_california_housing
from sklearn.ensemble import HistGradientBoostingRegressor, RandomForestRegressor
from sklearn.model_selection import GridSearchCV, KFold
import pandas as pd
import joblib
import plotly.express as px
import plotly.colors as colors
# 加载数据集
X, y = fetch_california_housing(return_X_y=True, as_frame=True)
n_samples, n_features = X.shape
# 定义模型和参数网格
models = {
"Random Forest": RandomForestRegressor(min_samples_leaf=5, random_state=0, n_jobs=joblib.cpu_count(only_physical_cores=True)),
"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)
grid_search.fit(X, y)
result = {"model": name, "cv_results": pd.DataFrame(grid_search.cv_results_)}
results.append(result)
# 可视化结果
fig = px.scatter(pd.concat([result["cv_results"] for result in results]), x="mean_fit_time", y="mean_test_score", error_x="std_fit_time", error_y="std_test_score", color="model")
fig.show()