使用-XGBoost-的超参数调整的边际效应
使用 XGBoost 的超参数调整的边际效应
towardsdatascience.com/marginal-effect-of-hyperparameter-tuning-with-xgboost/
通过在不同项目中构建 XGBoost 模型的工作,我发现了这个宝贵的资源《有效的 XGBoost》,作者:马特·哈里森,这是一本涵盖 XGBoost 的教科书,包括如何调整超参数。书中第十二章专门介绍了使用 hyperopt 库调整超参数;然而,在阅读这一节时产生了一些自然的问题。章节的介绍提供了使用 hyperopt 和贝叶斯优化如何提供比网格搜索更指导性的调整超参数方法的概述。然而,我很好奇,这里到底发生了什么?
此外,正如许多关于调整 XGBoost 超参数的教程一样,超参数的范围似乎有些随意。哈里森解释说,他从数据科学家布拉德利·博姆克的一次演讲中提取了要调整的超参数列表(这里)。哈里森和博姆克都提供了使用 hyperopt 的教程,他们使用相同的一组超参数,尽管他们在寻找最佳组合时使用了略微不同的搜索空间。在博姆克的情况下,他的搜索空间要大得多;例如,他建议每个树的深度(max_depth)可以在 1 到 100 之间变化。哈里森在他的书中对呈现的范围进行了某种程度的缩小,但这两个案例引发了一个问题:在调整 XGBoost 模型时,增加超参数搜索空间所带来的边际增益与增加时间的边际增加相比是什么?
本文的目的是围绕这两个问题展开。首先,我们将深入探讨 hyperopt 在调优超参数时的工作原理,以帮助获得一些对底层发生情况的直觉。其次,我们将以严谨的方式探讨大搜索空间和窄搜索空间之间的权衡。我希望能够回答这些问题,以便这些内容可以作为未来理解超参数调优的资源。
项目中所有的代码都可以在我的 GitHub 页面找到:github.com/noahswan19/XGBoost-Hyperparameter-Analysis
使用树结构帕累托估计器进行超参数调优的 hyperopt
在他的教科书中涵盖 hyperopt 的章节中,Harrison 描述了使用 hyperopt 进行超参数调优的过程,将其描述为使用“贝叶斯优化”来识别在调优过程中尝试的序列超参数组合。
高级描述清楚地说明了为什么使用 hyperopt 比网格搜索方法更优越,但我很好奇这是如何实现的。当我们使用树结构帕累托估计器(TPE)算法运行 fmin 函数时,实际上发生了什么?
基于序列模型的优化
首先,TPE 算法起源于 2011 年由 James Bergstra、Rémi Bardenet、Yoshua Bengio 和 Balázs Kégl 撰写的论文,他们是 hyperopt 包的作者,论文标题为“Hyper-Parameter Optimization Algorithms”。论文首先介绍了基于序列模型的优化(SMBO)算法,其中 TPE 算法是更广泛 SMBO 方法的一个版本。SMBO 提供了一种系统的方式来选择下一个要评估的超参数,避免了网格搜索的暴力性质和随机搜索的低效性。它涉及到为我们要优化的底层模型(即我们案例中的 XGBoost)开发一个“代理”模型,我们可以用它来指导搜索最优超参数,这种方式在计算上比评估底层模型更便宜。SMBO 的算法在以下图像中描述:

图片由作者提供,来自“Hyper-Parameter Optimization Algorithms” (Bergstra 等人)的图 1
这里有很多符号,让我们逐一解释:
-
x和 x:x代表在给定试验中正在测试的超参数组合,而 x 代表一个通用的超参数组合。
-
f:这是“适应度函数”,是我们正在优化的底层模型。在这个算法中,f(x)将超参数组合 x映射到该组合在验证数据集上的性能。
-
M_0:算法中的 M 项对应于我们用来近似 f 的“代理”模型。由于 f 通常运行成本较高,我们可以使用更便宜的估计 M 来帮助识别哪些超参数组合可能会提高性能。
-
H:弯曲的 H 代表迄今为止搜索的超参数的历史。它会在每次迭代后更新。它还用于在每次迭代后开发一个更新的代理模型。
-
T:这对应于我们用于超参数调整的试验次数。这一点相当直观,但与 hyperopt 中的 fmin 函数的 max_evals 参数相对应。
-
S:S 对应于在给定代理模型的情况下选择要检查的一组超参数组合的准则。在 hyperopt 对 TPE 算法的实现中,S 对应于以下图像中描述的期望改进(EI)准则。

图片由作者提供,来自“超参数优化算法”(Bergstra 等人)的第 1 个公式
每次迭代,都会抽取一定数量的可能超参数组合(在 python hyperopt 包中,默认设置为 24)。我们稍后将讨论 TPE 如何指示这些 24 个组合的抽取方式。这些 24 个超参数组合将使用 EI 准则和代理模型(这相对便宜)进行评估,以确定最有可能具有最高性能的组合。这就是我们看到代理模型的好处:我们不需要训练和评估 24 个 XGBoost 模型来评估最佳超参数组合,我们可以用计算成本较低的代理模型来近似。正如其名所示,上面的公式对应于超参数组合 x 的预期性能改进:
-
max(y-y,0):这代表超参数组合 x 的实际性能提升。y 对应于我们迄今为止获得的最佳验证损失;我们旨在最小化验证损失,因此我们寻找小于 y* 的 y 值。这意味着我们正在寻找最大化算法中 EI 的值。
-
p_M(x|y):这是使用代理模型和 TPE 将会适应的部分的准则。这是给定一个超参数组合 x 的 y 可能值的概率密度。
因此,在每一轮中,我们选取一组 24 个超参数组合,然后选择最大化 EI 准则的那个,该准则使用我们的代理模型 M。
TPE 算法适合在哪里
SMBO 算法中可以跨实现方式变化的要点是代理模型或我们如何近似超参数函数的成功。使用 EI 标准,代理模型需要估计密度函数 p(y|x)。上述论文介绍了一种称为高斯过程方法的方法,它直接对 p(y|x)进行建模,但 TPE 方法(通常用于 XGBoost 超参数优化)则近似 p(x|y)和 p(y)。这种方法遵循贝叶斯定理:

图片由作者提供
TPE 算法将 p(x|y)分割为两个分布的片段组合:
-
当 y < y*时,l(x)
-
当 y ≥ y*时,g(x)
这两个分布有一个直观的理解:l(x)指的是与具有比迄今为止最佳模型更低损失(更好)的模型相关的超参数的分布,而 g(x)指的是与具有比迄今为止最佳模型更高损失(更差)的模型相关的超参数的分布。这个关于 p(y|x)的表达式被代入论文中 EI 方程,并通过一个数学推导(这里过于冗长,无法全部展开)得出最大化 EI 等同于选择在 l(x)下更可能且在 g(x)下不太可能的点。
那么这在实践中是如何工作的呢?当使用 hyperopt 时,我们使用 fmin 函数并供应 tpe.suggest 算法来指定我们想要使用 TPE 算法。我们提供一个超参数空间,其中每个参数都与均匀分布或对数均匀分布相关联。这些初始分布用于初始化 l(x)和 g(x),并在少量初始试验中为 l(x)和 g(x)提供一个先验分布。默认情况下(tpe.suggest 中的参数 n_startup_jobs),hyperopt 通过从为 fmin 的空间参数提供的分布中随机采样超参数组合来运行 20 次试验。对于这 20 次试验中的每一次,都会运行一个 XGBoost 模型并获取验证损失。
然后,将 20 个观测值分成两部分,以便使用其中两个子集构建 l(x) 和 g(x) 的非参数密度。后续观测值用于更新这些分布。使用涉及每个超参数(我们指定的)的先验分布和来自试验历史的每个观测值的单独分布的非参数方法来估计密度(我不具备全面描述其的资格)。观测值使用随总试验次数变化的方法分成子集;具有最低损失的“n”个观测值用于 l(x),其余观测值用于 g(x)。“n”是通过将参数伽马(tpe.suggest 的默认值为 0.25)乘以试验数的平方根并向上取整来确定的;然而,“n”的最大值设置为 25,因此 l(x) 将最多用 25 个值进行参数化。如果我们使用 tpe.suggest 的默认设置,那么从初始试验中使用的最佳两个观测值(0.25 * sqrt(20) = 1.12 四舍五入为 2)用于参数化 l(x),其余的 18 个用于 g(x)。0.25 的值指的是 tpe.suggest 中的伽马参数,如果需要可以修改。回顾 SMBO 算法的伪代码和 EI 的公式,如果使用 n 个观测值来参数化 l(x),那么第 (n+1) 个观测值是阈值值 y*。
一旦使用初始试验实例化了 l(x) 和 g(x),我们就可以继续对 fmin 指定的 max_evals 数量的目标函数进行评估。对于每次迭代,通过从 l(x) 中随机抽取来生成一组候选超参数组合(默认情况下在 tpe.suggest 中为 24,但可以通过 n_EI_candidates 指定)。这些组合中的每一个都使用 l(x)/g(x) 的比率进行评估;选择最大化该比率的组合作为迭代中使用的组合。对于超参数组合,比率增加的情况是:(1) 可能与低损失相关联,或者(2) 不太可能与高损失相关联(这推动了探索)。选择最佳候选者的这个过程对应于使用具有 EI 的代理模型,正如在查看 SMBO 的伪代码时讨论的那样。
然后,使用该迭代的最佳候选者训练 XGBoost 模型;获得一个损失值,并使用数据点 (x, f(x)) 更新代理模型(l(x) 和 g(x))以继续优化。
超参数调优的边际效应
现在,有了关于如何使用 hyperopt 库在超参数调整过程中进行操作的背景知识,我们转向探讨使用更广泛的分布如何影响模型性能的问题。当尝试比较在大搜索空间上训练的模型与在较窄搜索空间上训练的模型的性能时,首要问题是如何创建较窄的搜索空间。例如,Boehmke 的演示文稿建议为 max_depth 超参数使用从 1 到 100 的均匀分布。XGBoost 模型在结合大量弱学习器时往往具有更好的泛化能力,但这是否意味着我们应该将分布缩小到最小值为 1 和最大值为 50?我们可能从其他人完成的工作中获得某种一般理解,从而直观地缩小空间,但我们能否找到一种方法来分析性地缩小搜索空间?
本文提出的解决方案涉及运行一系列较短的超参数调整试验,基于对更广泛超参数空间进行浅层搜索来缩小搜索空间。我们使用的更广泛的搜索空间来自 Boehmke 上述演示文稿的第 20 页(这里)。我们不会在广泛的搜索空间上运行 1,000 轮超参数测试,而是将运行 20 个独立的试验,每个试验 25 轮超参数测试。我们将使用每个超参数的百分位数值来缩小搜索空间。使用百分位数,我们将使用更窄的超参数搜索空间进行 200 轮的最终搜索,其中每个超参数的分布将根据试验中观察到的百分位数值给出最大值和最小值。
例如,假设我们运行了 20 次试验,并使用浅层搜索得到了 20 个 max_depth 的最佳值。我们选择将 max_depth 的搜索空间从 1 到 100 的均匀分布缩小到从试验中得到的 max_depth 的第 10 百分位数值到第 90 百分位数值的均匀分布。我们将运行几个不同的模型,改变我们使用的百分位数,以比较激进的缩小策略。
使用基于试验的方法生成的模型需要评估 700 个超参数组合(500 个来自试验,200 个来自最终评估)。我们将比较这些模型的性能与在更广泛的搜索空间上调整了 1,000 个超参数评估的模型以及在一个更广泛的搜索空间上调整了 700 个超参数评估的模型的性能。我们很好奇这种方法缩小超参数搜索空间是否会更快地收敛到最佳超参数组合,或者这种缩小是否会对结果产生负面影响。
我们在一个涉及模拟网球比赛结果的任务上测试了这种方法,该任务来自一个先前的项目(更多信息请参阅我写的文章这里)。项目的一部分涉及使用每个比赛的详细信息以及后续比赛的玩家统计数据(这些统计数据遵循截断正态分布)来构建比赛后的获胜概率模型;这是此处测试超参数调整方法的任务。关于具体任务的更多信息可以在文章和文章开头链接的代码中找到。从高层次来看,我们试图利用比赛发生的信息来预测比赛的胜负;可以使用比赛后的获胜概率模型来识别可能超出其统计表现的任何球员,他们可能是回归的候选人。为了训练每个 XGBoost 模型,我们使用对数损失/交叉熵损失作为损失函数。该任务的数据来自 Jeff Sackmann 的 GitHub 页面:github.com/JeffSackmann/tennis_atp。任何对网球或网球数据感兴趣的人都应该查看他的 GitHub 和优秀的网站:tennisabstract.com。
对于这个任务和我们的方法,我们共有六个模型,其中两个在完整搜索空间上训练,另外四个在较窄的空间上训练。这些模型在图表中的标题如下:
-
“完整搜索”:这是在完整超参数搜索空间上针对 1000 个超参数评估训练的模型。
-
“XX-XX 百分位”:这些模型是在对完整超参数搜索空间进行 500 轮试验评估后的 200 次评估中在较窄搜索空间上训练的。例如,“10-90 百分位”模型是在超参数搜索空间中训练的,其中每个超参数的分布由 20 次试验的 10 百分位和 90 百分位值确定。
-
“缩短搜索”:这是在完整超参数搜索空间上训练的,针对 700 个超参数评估的模型。我们使用这个模型来比较当两种方法分配相同数量的超参数评估时,试验方法的性能与更宽搜索空间的比较。
模型的训练日志包含在文章顶部的 GitHub 页面github.com/noahswan19/XGBoost-Hyperparameter-Analysis/tree/main/logs中,该日志包括在给定随机种子的情况下,每个步骤中找到的超参数以及在我的笔记本电脑上运行每个模型所需的时间。它还提供了 20 次试验的结果,以便了解每个缩小的搜索空间将如何参数化。这些时间如下所示:
-
完整搜索:约 6000 秒
-
10-90 百分位:约 4300 秒(试验为 3000 秒,较窄搜索为 1300 秒)
-
20-80 百分位:约 3800 秒(试验为 3000 秒,较窄搜索为 800 秒)
-
30–70 百分位:约 3650 秒(试验约 3000 秒,更窄的搜索约 650 秒)
-
40–60 百分位:约 3600 秒(试验约 3000 秒,更窄的搜索约 600 秒)
-
更短的搜索:约 4600 秒
时间并不总是与总评估次数成 1:1 的比例;试验方法模型在相同的评估次数下通常训练时间更短,更窄的搜索甚至更短。下一个问题是这种节省时间是否会影响模型性能。我们将首先查看模型之间的验证日志损失。

图片由作者提供
模型之间的日志损失几乎没有区别,但我们将稍微放大一点,以获得差异的视觉观察。我们首先展示完整的 y 轴范围,以说明日志损失中的微小差异。

图片由作者提供
好吧,做得更好,但我们将再次放大以更清楚地看到趋势。

图片由作者提供
我们发现,20–80 百分位模型达到了最佳的验证日志损失,略好于全搜索和更短搜索方法。其他百分位模型的整体表现略差于更宽搜索模型,但差异很小。我们现在将查看模型之间的准确率差异。

图片由作者提供
与日志损失一样,我们看到非常小的差异,并选择放大以查看更明确的趋势。

图片由作者提供
全搜索模型达到了任何模型中最好的准确率,但 10–90 百分位和 20–80 百分位在相同的评估次数下都优于更短的搜索模型。这正是我希望通过这个注意事项来识别的权衡,前提是这是特定于任务并且规模非常小。
使用日志损失和准确率的结果表明,在决定如何宽泛地设置 XGBoost 超参数搜索空间时,可能存在效率-性能权衡的可能性。我们发现,在更窄的搜索空间中训练的模型可以优于或与在更宽搜索空间中训练的模型相媲美,同时整体训练时间更短。
进一步工作
上一节提供的代码应该能够提供模块化,以便轻松地对不同任务进行测试;这个分类任务的成果可能与其他任务的成果不同。调整在探索超参数搜索空间时运行的评估次数或获取百分位范围时运行的试验次数,可能会得出与这里找到的不同的结论。这项工作还假设了要调整的超参数集;另一个我想探索的问题是,包括额外的超参数调整(即 colsample_bylevel)对 XGBoost 模型性能的边际影响。
参考文献
[2] M. Harrison, 有效的 XGBoost (2023), MetaSnake
[3] B. Boehmke, “在 Databricks 上进行高级 XGBoost 超参数调优” (2021), GitHub
[4] J. Bergstra, R. Bardenet, Y. Bengio, B. Kégl, “超参数优化的算法” (2011), NeurIPS 2011

浙公网安备 33010602011771号