贝叶斯优化实战(二)
原文:
zh.annas-archive.org/md5/5ea9246d4fedff957a1c887275e38f01译者:飞龙
第六章:使用老虎机风格策略探索搜索空间
本章涵盖了
-
多臂老虎机问题及其与 BayesOpt 的关系
-
在 BayesOpt 中的上限置信度策略
-
在 BayesOpt 中的 Thompson 抽样策略
在赌场应该玩哪台老虎机以最大化你的收益?你如何制定一个策略,智能地尝试多台老虎机并缩小最有利可图的机器?这个问题与 BayesOpt 有什么关系?这些是本章将帮助我们回答的问题。
第四章是我们对 BayesOpt 策略的介绍,它决定了如何探索和检查搜索空间。BayesOpt 策略的探索策略应该指导我们朝着我们想要优化的目标函数的最优解前进。我们学到的两个特定策略是改进概率(PoI)和期望改进(EI),它们利用了我们希望从到目前为止看到的最佳目标值中改进的想法。这种基于改进的思维方式只是一种启发式方法,因此并不构成 BayesOpt 的唯一方法。
在本章中,我们学习了另外两个直接来自于与决策制定问题密切相关的 多臂老虎机(MAB)的 BayesOpt 策略。作为在赌场玩哪台老虎机最有利可图的问题,MAB 问题为许多决策不确定性问题设置了舞台。MAB 有着悠久的历史和广泛的研究,为这个问题开发了许多良好的解决方案。正如我们在本章中所学到的,MAB 和 BayesOpt 是非常相似的问题——都处理决策不确定性的优化——因此预期是 MAB 问题的解决方案也将在 BayesOpt 上表现良好。
首先,我们简要讨论 MAB 问题及其与 BayesOpt 的关系。这个讨论提供了一些关于问题的背景,并将 BayesOpt 与 AI 中的其他问题联系起来。然后,我们学习了 MAB 中两个最流行的策略,并将它们应用到 BayesOpt 中。第一个是 上限置信度 策略,它使用 面对不确定性的乐观主义 原则来推断其决策。第二个策略被称为 Thompson 抽样,它是一种主动利用我们预测模型的概率性质的随机化解决方案。然后,我们在我们的运行示例上实现并运行这些策略,并分析它们的性能。
通过本章末尾,我们对 MAB 问题及其与 BayesOpt 的关系有了了解。更重要的是,我们将 BayesOpt 策略的投资组合增加了两项,使我们暴露于在黑盒优化问题中探索搜索空间的更多方式。
5.1 MAB 问题简介
在本节中,我们从高层次学习 MAB 问题。我们首先讨论它的问题陈述和设置在第一小节。
重要提示 在 MAB 问题中,我们需要在一个长时间段内每一步选择一个要采取的动作。每个动作根据未知的奖励率产生奖励,我们的目标是在长时间段结束时最大化我们获得的总奖励。
我们还探讨了它与 BayesOpt 的关系,以及 AI 和 ML 中的其他问题。这为我们提供了背景,将 MAB 与文本的其余部分联系起来。
5.1.1 寻找赌场最好的老虎机
虽然多臂老虎机可能在你的脑海中勾起神秘的形象,但这个术语实际上指的是一个赌徒在赌场选择玩哪些老虎机以获取最大奖励的问题。想象一下,你在一家赌场,有一台可以拉动“手臂”的老虎机。
拉动老虎机的手臂时,你可能会得到硬币作为奖励;但是,这个过程中存在随机性。具体来说,在这台老虎机的内部机制中编程了一个奖励概率p。每次拉动机器的手臂时,机器都会根据该概率返回硬币。这台老虎机在图 5.1 中可视化。如果p=0.5,则该机器向其玩家奖励一半的时间。如果p=0.01,则大约只有 100 次拉动中的 1 次会导致返回硬币。由于这个概率被编程在机器内部——因此隐藏在我们看不见的地方——我们无法确定这个概率是多少。
图 5.1 一个带有可以拉动手臂的老虎机。当拉动手臂时,根据其奖励概率,该机器可能返回硬币。
在这个假设的情景中,赌场调整其老虎机的程序,以便这些机器向玩家奖励硬币的速度低于玩家所玩的机器和收到硬币的机器。换句话说,即使偶尔会有赢家从这些老虎机中获得奖励,平均而言,赚取利润的是赌场。
定义 特别不成功的玩家,他们比赢得硬币的速度更快地失去硬币,可能会不情愿地称呼他们一直在玩的老虎机为“强盗”,认为机器在偷他们的钱。由于这台机器有一个可以拉动的手臂,因此可以称为单臂强盗。
现在,想象一下,不只是一个老虎机,而是一排我们可以选择玩的老虎机,每个都有自己的奖励概率p,如图 5.2 所示。
图 5.2 一个带有可以拉动手臂的老虎机。当拉动手臂时,根据其奖励概率,该机器可能返回硬币。
对于这一排老虎机,一个有战略眼光的玩家可能会将这种设置转化为一个决策挑战,并试图以某种智能的方式尝试这些老虎机,以便尽快确定哪台老虎机具有最高的奖励概率。他们的目的是在只能拉动这些机器的手臂特定次数的情况下最大化他们获得的奖励量。
定义 这个决策问题被称为多臂老虎机(或 MAB),因为我们可以拉动多个手臂。目标是设计一个策略,决定接下来应该拉动哪个机器的手臂,以最大化我们最终获得的总奖励。
我们发现 MAB 具有许多不确定性优化问题的特征,例如 BayesOpt:
-
我们可以采取具体的行动。每个行动对应于拉动特定老虎机的手臂。
-
我们有限的预算。我们只能在特定次数内拉动这些手臂,直到我们不得不停止。
-
在可能采取的行动的结果中存在不确定性。我们不知道每台老虎机的奖励概率是多少,甚至在我们多次拉动其手臂之前我们也无法估计。此外,拉动手臂后,我们不能确定是否会收到硬币,因为奖励中存在随机性。
-
我们想要为一个目标进行优化。我们的目标是最大化累积奖励,即我们在拉动这些机器的手臂直到停止时收到的硬币总数。
或许最重要的是,在 MAB 中,我们面临与我们在 4.1.2 节中讨论的探索-利用权衡相同的问题。特别是,每次我们决定拉动一台机器的手臂时,我们需要在迄今为止给我们较好成功率的机器(利用)和其他奖励概率我们了解不多的机器(探索)之间做出选择。
我们面临的问题是一个权衡,因为通过探索,我们可能会冒着将我们的拉动浪费在奖励概率低的机器上的风险,但过度利用意味着我们可能会完全错过一个奖励率比我们目前观察到的更高的机器。图 5.3 展示了一个例子,其中以下情况为真:
-
我们在第一台机器上拉了 100 次手臂,收集了 70 枚硬币。也就是说,迄今为止第一台机器提供的经验成功率最高,为 70%。
-
我们从第二台机器收集了更多数据,因此我们对其奖励率的不确定性最小,大约为 50%。
-
尽管第三台机器的经验成功率最低(0%),但我们可能需要尝试更多次拉动它的手臂,以更确定其奖励率。
图 5.3 一个展示探索-利用困境的 MAB 数据集示例。MAB 策略必须在一个成功率持续高的机器和一个奖励率不确定的机器之间做出选择。
与贝叶斯优化策略类似,多臂老虎机策略的工作是查看过去奖励的数据,并决定我们接下来应该拉动哪个臂,同时平衡探索和开发之间的权衡。多臂老虎机问题模拟了你可能在现实世界中看到的广泛应用的一系列应用场景:
-
在产品推荐中,引擎需要从商店中的许多产品中选择一个向用户推荐。每个产品都可以看作是一个老虎机的臂,拉动臂意味着引擎选择该产品向用户展示。如果用户点击了该产品的广告,我们可以视为收到了奖励,因为用户的点击是我们想要实现的目标。
-
许多资源管理问题可以被构建为多臂老虎机问题,其中我们需要考虑如何最好地将不同资源分配给不同的组织,以最佳地优化一些高级目标(例如,利润或生产力),而不知道每个组织的运作效果。投资组合管理也可以以同样的方式构建为多臂老虎机问题。
-
多臂老虎机问题也在临床试验设计中得到应用,其中每个患者需要被分配到特定的治疗中。我们希望优化所有患者的治疗结果,但需要处理有限的资源以及确定每个患者从给定治疗中受益的可能性。
在这些应用中,我们可以采取一组行动——也就是说,一组可以拉动的臂——以在不确定性下优化一个目标。
5.1.2 从多臂老虎机到贝叶斯优化
我们已经看到多臂老虎机和贝叶斯优化具有许多共同的特征。在这两个问题中,我们需要思考我们应该采取什么决策,以便我们可以最大化我们关心的数量。此外,每个行动的结果都不是确定的。这意味着我们不知道一个行动是否会产生好的结果,直到我们真正采取行动为止。
但是,这两个问题并不相等。在多臂老虎机问题中,我们的目标是随着时间的推移最大化累积奖励——也就是说,接收到的硬币总数。而在贝叶斯优化中,我们只是寻找一个导致高价值的函数输入;只要我们收集的数据集中有一个良好的目标值,我们就会成功优化。这种单值目标有时被称为简单奖励。这种差异意味着我们需要确保在多臂老虎机问题中频繁地获得奖励,以维持良好的累积奖励,而在贝叶斯优化中,我们可以更加勇于探索,以潜在地找到一个良好的目标值。
定义术语简单奖励并不意味着目标更容易或更简单地优化,而是目标是一个单一的数字,而不是累积奖励的总和。
此外,在多臂老虎机中只有有限数量的动作可供选择(我们可以拉动的手臂数量有限)。在 BayesOpt 中,由于我们试图优化连续域中的目标函数,有无限多个动作。由于我们假设使用 GP 时接近点的函数值彼此相似,我们可以将这看作是彼此接近的动作产生类似的奖励率。这在图 5.4 中有所说明,其中每个微不足道的点都是我们可以拉动其手臂的老虎机(即,查询目标函数值),彼此接近的机器具有相似的颜色。
图 5.4 BayesOpt 类似于具有无限多个动作的 MBA 问题。每个微不足道的点都是一个老虎机,我们可以拉动其手臂。此外,彼此接近的机器在某种意义上是相关的,因为它们具有类似的奖励率。
大多数多臂老虎机问题的形式化考虑了二进制设置,即拉动老虎机时,要么返回一个硬币,要么什么也不返回。另一方面,在 BayesOpt 中我们可能观察到的函数值可以取任意实数值。
多臂老虎机和 BayesOpt 之间的主要区别在表 5.1 中总结。虽然这些是根本性的差异,但在决策中探索和利用之间的权衡在两个问题中都存在,因此将多臂老虎机策略重新用于 BayesOpt 是合理的。
表 5.1 多臂老虎机和 BayesOpt 之间的区别
| 标准 | 多臂老虎机 | BayesOpt |
|---|---|---|
| 要最大化的目标 | 累积奖励 | 简单奖励 |
| 观察/奖励类型 | 二进制 | 实值 |
| 动作数量 | 有限 | 无限 |
| 动作之间的相关性 | 否 | 是对于相似的动作 |
在本章的其余部分,我们将学习两种这样的策略,它们背后的动机以及如何使用 BoTorch 实现它们。我们将在下一节从上置信界限策略开始。
5.2 在上置信界限策略下面对不确定性的乐观主义
我们应该如何考虑在特定位置评估目标函数时可能观察到的无限多种可能性的值?此外,我们应该如何以简单、高效的方式推理这些可能性,以促进决策?在本节中,我们探讨了多臂老虎机中的上置信界限(UCB)策略,该策略导致了同名的 BayesOpt 策略。
UCB 遵循面对不确定性的乐观主义原则。在多臂老虎机中,思想是使用每个老虎机奖励率的估计上界作为真实的未知奖励率的替代品。也就是说,我们乐观地估计每台机器的奖励率,使用我们认为的奖励率的上界,最后选择具有最高上界的机器。
我们首先讨论这个原则以及它如何辅助我们的决策推理。然后,我们学习如何使用 BoTorch 实现 UCB 策略并分析其行为。
5.2.1 不确定情况下的乐观主义
让我们举个简单的例子来具体说明这个想法。假设你某一天醒来发现外面虽然是晴天,但地平线上有乌云。你查看手机上的天气应用程序,看看今天是否会一直晴朗,以及是否应该带伞上班以防下雨。不幸的是,该应用程序无法确切地告诉你天气是否会晴朗。相反,你只能看到晴天的概率估计在 30%到 60%之间。
你心里想,如果晴天的概率低于 50%,那么你会带伞。然而,这里并不是一个单一值的估计,而是一个介于 30%到 60%之间的范围。那么,你应该如何决定是否需要带伞呢?
悲观主义者可能会说,因为晴天的概率可能只有 30%,所以你应该采取保险措施,做最坏的打算。考虑平均情况的人可能会进一步研究应用程序,看看晴天的平均估计概率是多少,然后做出相应的决定。而从乐观主义者的角度来看,60%的机会足以让人相信天气会晴朗,所以这个人不会打算带伞去上班。这些思考方式如图 5.5 所示。
在图 5.5 中,第三个人的推理对应于 UCB 策略背后的思想:对未知事件的结果持乐观态度,并根据这种信念做出决策。在多臂老虎机问题中,UCB 会构建每个老虎机奖励率的上限,并选择具有最高上限的老虎机。
图 5.5 关于未来思考和做出决策的不同方式。最后一个人对应于 UCB 策略,以乐观的方式推理一个未知量。
注:在 BayesOpt 中实现这一策略的方式特别简单,因为我们使用高斯过程作为目标函数的预测模型,我们已经有了每个动作的奖励率的上限。也就是说,我们已经有了在任何给定输入位置的目标值的上限。
具体来说,我们知道给定位置的目标值遵循正态分布,而量化正态分布不确定性的常用度量是 95% CI,其中包含分布的 95% 的概率质量。使用 GP,我们将该 95% CI 在输入空间中可视化为图 5.6 中粗线。该 95% CI 的上限,也就是图 5.6 中突出显示的阴影区域的上边界,正是 UCB 策略将用作搜索空间中数据点的获取分数的上边界,并且给出最高分数的点是我们将评估下一个目标函数的位置,这在本例中是由虚线围绕的位置约为 –1.3。
图 5.6 对应于 95% CI 的 GP 的 UCB。此边界可用作 UCB 策略的获取分数。
获取分数 的定义量化了数据点在引导我们朝着目标函数的最优值的过程中的价值。我们首次在 4.1.1 节了解到了获取分数。
您可能认为这种决策方式可能不合适,特别是在决策成本高昂的高风险情况下。在我们的伞例子中,通过乐观地低估下雨的概率,您可能会冒着没有伞就被雨淋的风险。
然而,这是一种特别高效的推理方式,因为我们只需要提取我们对感兴趣数量的估计的一个上界,并将该边界用于决策。此外,正如我们在下一小节中看到的那样,通过选择我们想要使用的 CI(而不是坚持使用 95% CI),我们完全控制 UCB 有多乐观。这种控制还允许策略平衡探索和利用,这是任何 BayesOpt 策略需要解决的核心问题。
5.2.2 平衡探索和利用
在本小节中,我们进一步讨论了我们,BayesOpt 用户,如何调整 UCB 策略。这提供了一种在高不确定性区域(探索)和高预测均值区域(利用)之间平衡的控制水平。这次讨论旨在在下一小节 BoTorch 实现之前更深入地理解 UCB。
请记住,对于正态分布,从平均值(即,均值 μ 加/减 2 倍标准差 σ)偏离两个标准偏差会给我们带来 95% CI。这个区间的上限(μ + 2σ)就是我们在上一小节中看到的 UCB 的获取分数。
95% 置信区间(CI)不是正态分布的唯一 CI。通过在公式 μ + βσ 中设置标准差 σ 的乘法器(表示为 β),我们可以获得其他 CI。例如,在一维正态分布中,如图 5.7 所示,以下结论成立:
-
离均值上一个标准差(μ + σ)——即设置β = 1——给出了 68%的置信区间:正态分布的 68%的概率质量位于μ - σ和μ + σ之间。
-
同样,距离均值三个标准差(β = 3)给出了 99.7%的置信区间。
图 5.7 展示了标准正态分布的不同置信区间。从均值偏离一个、两个和三个标准差,我们得到了 68%、95%和 99.7%的置信区间。UCB 策略使用这些间隔的上界。
实际上,任何β的值都可以给我们一个唯一的正态分布置信区间。由于 UCB 只指示我们应该使用来自预测模型的上界来做决策,所以形如μ + βσ的任何值都可以作为 UCB 中使用的上界。通过设置此参数β,我们可以控制 UCB 策略的行为。
图 5.8 展示了对应于β = 1、2、3 的三个不同的上界,而事实上,均值函数对应于设置β = 0. 我们可以看到,尽管上界的形状大致相同,但这些上界以不同的速率上下波动。此外,由于最大化此上界的数据点是 UCB 选择进行优化查询的点,不同的上界,即不同的β值,将导致不同的优化行为。
图 5.8 展示了 GP 的不同上界,对应不同的置信区间和β值。β越大,UCB 策略就越趋向于探索性。
重要的是,β越小,UCB 就越趋向于剥削性。相反,β越大,UCB 就越趋向于探索性。
我们可以通过检查获取分数的公式μ + βσ来看到β值控制 UCB 行为的方式。当β值很小时,平均值μ对获取分数做出的贡献最大。因此,具有最高预测均值的数据点将最大化此分数。此选择对应纯粹的剥削性,因为我们只是选择具有最大预测值的点。另一方面,当β值很大时,标准差σ,即量化我们的不确定性,在 UCB 分数中变得更加重要,强调了探索的需求。
我们可以从图 5.8 中看出这个差异,其中 0 附近是我们实现最高预测均值的地方,表明这是开发区域。随着β的增加,不同上界峰值所在的点逐渐向左移动,在这里我们的预测更加不确定。
注意有趣的是,在β → ∞的极限情况下,最大化 UCB 获取分数的点是使标准差σ最大化的点,即我们的不确定性最大化的点。这种行为对应纯粹的探索性,因为我们选择具有高度不确定性的数据点。
最后,UCB 正确地为提供在探索和开发之间提供更好平衡的数据点分配更高的分数:
-
如果两个数据点具有相同的预测均值但不同的预测标准差,则具有较高不确定性的数据点将具有更高的分数。因此,该策略奖励探索。
-
如果两个数据点具有相同的预测标准差但不同的预测均值,则具有较高均值的数据点将具有更高的分数。因此,该策略奖励利用。
记住,在第四章讨论的策略中,EI 也具有这种特性,这是任何贝叶斯优化策略的期望。
总的来说,这个参数 β 控制 UCB 以及策略如何探索和利用搜索空间。这意味着通过设置该参数的值,我们可以直接控制其行为。不幸的是,除了 β 的值对应于探索程度这一事实之外,没有一种直观的、原则性的方法来设置该参数,某些值可能在某些问题上有效,但在其他问题上却无效。本章的练习 1 进一步讨论了一种更为复杂的设置 β 的方法,该方法可能通常工作得足够好。
注意 BoTorch 的文档通常显示 UCB 的 β = 0.1,而许多使用 UCB 进行贝叶斯优化的研究论文选择 β = 3。所以,如果你更偏向利用,0.1 可以是你使用该策略的首选值,如果你更倾向于探索,3 应该是默认值。
5.2.3 在 BoTorch 中实现
在充分讨论了 UCB 的动机和数学之后,我们现在学习如何使用 BoTorch 实现该策略。我们在这里看到的代码包含在 CH05/01 - BayesOpt loop.ipynb 中。请记住,虽然我们可以手动实现 PoI 策略,但声明 BoTorch 策略对象,将其与 GP 模型一起使用,并使用 BoTorch 的辅助函数 optimize_acqf() 优化获取分数,这样可以更轻松地实现我们的贝叶斯优化循环。
出于这个原因,我们在这里做同样的事情,并使用内置的 UCB 策略类,尽管我们可以简单地自己计算 μ + βσ 的数量。这可以通过以下方式完成
policy = botorch.acquisition.analytic.UpperConfidenceBound(
model, beta=1
)
在这里,BoTorch 中的 UCB 类实现将 GP 模型作为其第一个输入,并将正值作为其第二个输入 beta。正如你所料,这第二个输入表示 UCB 参数 β 在评分公式 μ + βσ 中的值,该公式在探索和利用之间进行权衡。在这里,我们暂时将其设置为 1。
注意 信不信由你,这就是我们需要从上一章的贝叶斯优化代码中更改的全部内容,以在我们拥有的目标函数上运行 UCB 策略。这展示了 BoTorch 模块化的好处,它允许我们将任何我们想要使用的策略插入我们的贝叶斯优化流程中。
使用 β = 1 运行我们的 UCB 策略在我们熟悉的 Forrester 目标函数上生成图 5.9,它显示了在 10 个函数评估过程中的优化进展。我们看到,与 PoI 策略发生的情况类似,对于 Forrester 函数来说,β = 1 的 UCB 未能充分探索搜索空间,并且在一个局部最优解上停滞不前。这意味着 β 的值太小。
图 5.9 UCB 策略在权衡参数 β = 1 时的进展。参数值不足以鼓励探索,导致进展停滞在局部最优解。
注意:我们在第 4.2 节了解到了改进概率策略。
让我们再试一次,这次将这个权衡参数设置为一个更大的值:
policy = botorch.acquisition.analytic.UpperConfidenceBound(
model, beta=2
)
这个版本的 UCB 策略的进展如图 5.10 所示。这次,由于更大的 β 值引起的探索水平更高,UCB 能够找到全局最优解。然而,如果 β 太大,以至于 UCB 只花费预算来探索搜索空间,那么我们的优化性能也可能会受到影响。(我们稍后在本章练习中会看到一个例子。)
图 5.10 UCB 策略在权衡参数 β = 2 时的进展。该策略成功找到了全局最优解。
总的来说,使用 UCB 策略时,使用一个好的权衡参数值的重要性是明显的。然而,同样地,很难说什么值会对给定的目标函数起作用良好。本章的练习 1 探索了一种调整这个参数值的策略,随着搜索的进行。
这标志着我们对 UCB 的讨论结束了。我们已经看到,通过从多臂赌博问题中采用面对不确定性的乐观态度思维模式,我们得到了一个贝叶斯优化策略,其探索行为可以直接通过一个权衡参数进行控制和调整。在下一节中,我们将继续介绍从多臂赌博问题中采用的第二个策略,它具有完全不同的动机和策略。
5.3 使用 Thompson 采样策略进行智能采样
在这一节中,我们将学习关于多臂赌博问题中的另一种启发式方法,它直接转化为一个被广泛使用的贝叶斯优化策略,称为Thompson 采样(TS)。正如我们将看到的,这个策略使用了与 UCB 完全不同的动机,因此会引发不同的优化行为。与第 5.2 节中的 UCB 类似,我们先学习这个贝叶斯优化策略的一般思想,然后再进入其代码实现。
5.3.1 用一个样本来代表未知
使用 UCB,我们根据我们关心的未知量的乐观估计来做出决策。这提供了一种简单的方式来推理我们所采取的行动和我们所获得的奖励的后果,这种方式在探索和开发之间进行权衡。那么 TS 呢?
定义 Thompson sampling 的理念是首先维持我们关心的数量的概率信念,然后从该信念中抽样,并将该样本视为我们感兴趣的真实未知量的替代。然后使用这个抽样替代物来选择我们应该做出的最佳决策。
让我们回到我们的天气预报示例,看看这是如何运作的。同样,我们对是否应该带雨伞去工作的问题感兴趣,在这个问题中,我们得到了一份天气将全天保持晴朗的概率估计。请记住,UCB 依赖于这个估计的上限来指导其决策,但 TS 策略会做什么呢?
TS 首先从我们用来模拟未知数量的概率分布中抽取一个样本——在这种情况下,是否会晴朗——然后根据这个样本做出决策。假设我们手机上的天气应用现在宣布有 66%(大约三分之二)的机会天气会保持晴朗。这意味着在遵循 TS 策略时,我们首先抛一枚硬币,其正面有三分之二的倾向:
-
如果硬币是正面(有 66% 的几率),那么我们会把它视为天气全天晴朗,并得出我们不需要雨伞的结论。
-
如果硬币是反面(有 34% 的几率),那么我们会把它视为会下雨,并得出我们应该带雨伞去工作的结论。
这个 TS 过程在图 5.11 中被可视化为一个决策树,在开始时,我们抛一个有偏的硬币,以获取晴天的概率分布样本。根据硬币是正面(代表晴天的样本)还是反面(代表下雨的样本),我们决定是否要带雨伞去工作。
图 5.11 TS 策略作为决策树。我们抛一个有偏的硬币以获取晴天的概率分布样本,并根据此样本决定是否带雨伞。
虽然这乍一看可能是一种任意的决策方法,但 TS 在 MAB 和 BayesOpt 中特别有效。首先,鉴于表示感兴趣数量的概率分布,该分布的样本是该数量的可能实现,因此该样本可以用作分布的表示。在不确定性优化问题中,TS 提供了与 UCB 相同的好处,即从概率分布中抽样通常很容易。就像 UCB 对奖励率的乐观估计可能以高效的方式生成一样,抽样也可以同样高效地完成。
让我们考虑 TS 在 BayesOpt 中的工作原理。从在当前观察到的数据上训练的 GP 中绘制一个样本。从第三章记得,从 GP 中绘制的样本是表示我们的 GP 信念下目标函数特定实现的函数。然而,与在没有观察到数据的区域中未知的真实目标函数不同,从 GP 中绘制的样本是完全已知的。这意味着我们可以找到使得这个样本被最大化的位置。
图 5.12 显示了我们在 Forrester 函数上训练的 GP 及其绘制的三个样本作为虚线。此外,沿着每个样本,我们使用一个菱形来指示最大化样本的位置。正如我们所看到的,一个样本在 –3.2 处最大化,另一个在 –1.2 处最大化,第三个在 5 处。
图 5.12 绘制自 GP 的样本和最大化相应样本的数据点。无论绘制哪个样本,TS 都会选择最大化该样本的数据点作为下一个要评估目标函数的点。
当使用 TS 策略时,完全是由偶然决定我们从 GP 中绘制这三个样本中的哪一个(或者一个完全不同的样本)。然而,无论我们绘制哪个样本,最大化样本的数据点都是我们将要查询的下一个点。也就是说,如果我们绘制出在 –3.2 处最大化的样本,那么我们将在 –3.2 处评估目标函数。如果我们绘制出在 5 处最大化的样本,那么我们将查询点 x = 5。
定义 TS 计算的获取分数是从 GP 中绘制的随机样本的值。最大化此样本的数据点是我们将要查询的下一个点。
与我们迄今为止见过的其他策略不同,TS 是一种随机策略,这意味着当面对相同的训练数据和 GP 时,不保证策略会做出相同的决定(除非我们在计算上设置了随机种子)。然而,这种随机性并不是一种不利。我们已经说过,从 GP 中绘制样本是很容易的,因此可以有效地计算 TS 获取分数。此外,对随机样本的最大化本质上在探索和利用之间取得平衡,这正是我们在 BayesOpt 中的主要关注点:
-
如果数据点有很高的预测均值,那么该数据点处的随机样本的值很可能很高,使其更有可能是最大化样本的那个数据点。
-
如果数据点具有较高的预测标准偏差(即不确定性),那么该数据点处的随机样本也将具有较高的变异性,因此更有可能具有较高的值。因此,这种更高的变异性也使得更有可能选择该数据点作为下一个要查询的点。
TS 可能利用具有高预测均值的区域中最大化的随机样本,但在相同情况下,该策略也可能利用另一个样本进行探索。我们在图 5.12 中看到了这一点,其中一个样本在–1.2 左右最大化,具有相对较高的预测均值。如果这是我们抽取的样本,那么通过在–1.2 处评估目标函数,我们将在利用函数。然而,如果我们抽取另外两个样本中的任何一个,那么我们将在探索,因为样本最大化的区域具有高的不确定性。
这是一个相当优雅的权衡方案。通过使用样本的随机性,TS 直接利用了我们预测模型 GP 的概率性质来探索和利用搜索空间。TS 的随机性意味着在 BayesOpt 循环的任何给定迭代中,该策略可能不会做出最佳决策以权衡探索和利用,但随着时间的推移,在其决策的聚合中,该策略将能够充分探索空间并缩小高性能区域。
5.3.2 使用 BoTorch 实现
现在让我们转而在 Python 中实现这个 TS 策略。再次提醒,代码包含在 CH05/01 - BayesOpt loop.ipynb 笔记本中。请记住,对于我们见过的 BayesOpt 策略,实现归结为声明一个 BoTorch 策略对象并指定相关信息。然后,为了找到下一个应该查询的数据点,我们优化策略计算出的获取分数。然而,这与 TS 不同。
实现 TS 作为通用的 PyTorch 模块的主要挑战是,从 GP 中取样只能使用有限数量的点。过去,我们用密集网格上的高维 MVN 分布的样本来表示 GP 的样本。当绘制这些密集高斯的样本时,它们看起来像是具有平滑曲线的实际函数,但实际上它们是在网格上定义的。
所有这些都是为了说,从 GP 中绘制函数样本在计算上是不可能的,因为这将需要无限数量的比特。一个典型的解决方案是在跨越输入空间的大量点上绘制相应的 MVN,以便搜索空间中的所有区域都得到表示。
重要提示:这正是我们实现 TS 的方法:在搜索空间中生成大量点,并从这些点上的 GP 预测中绘制 MVN 分布的样本。
TS 的流程总结在图 5.13 中,我们使用 Sobol 序列 作为跨度搜索空间的点。 (我们稍后讨论为什么 Sobol 序列优于其他采样策略。)然后,我们从这些点上的 GP 中抽取一个样本,并挑选出从样本中产生的值最高的点。 然后,该样本用于表示 GP 本身的样本,并且我们在下一步中评估目标函数在采样值最大化的位置处。
图 5.13 BoTorch 中 TS 实现的流程图。我们使用 Sobol 序列填充搜索空间,在序列上从 GP 中抽取一个样本,并选择使样本最大化的点以评估目标函数。
定义 Sobol 序列 是欧几里得空间中一个无限点列表,旨在均匀覆盖该区域。
让我们首先讨论为什么我们需要使用 Sobol 序列来生成跨度搜索空间的点。 一个更简单的解决方案是使用密集网格。 但是,随着搜索空间维数的增长,生成密集网格很快变得难以处理,因此这种策略是不可行的。 另一个潜在的解决方案是从该空间均匀采样,但是统计理论表明均匀采样实际上不是生成均匀覆盖空间的最佳方法,而 Sobol 序列则做得更好。
图 5.14 显示了一个由 100 个点组成的 Sobol 序列与在二维单位正方形内均匀采样的相同数量的点的比较。 我们看到 Sobol 序列覆盖正方形更均匀,这是我们希望使用 TS 实现的目标。 在更高维度中,这种对比更加明显,这更加增加了我们更喜欢 Sobol 序列而不是均匀采样数据点的理由。 我们这里不详细讨论 Sobol 序列; 对我们重要的是要知道 Sobol 序列是覆盖空间均匀的标准方法。
图 5.14 Sobol 序列中的点与二维单位正方形中均匀采样的点的比较。Sobol 序列覆盖正方形更均匀,因此应该被 TS 使用。
PyTorch 提供了 Sobol 序列的实现,可以如下使用:
dim = 1 ❶
num_candidates = 1000 ❷
sobol = torch.quasirandom.SobolEngine(dim, scramble=True)
candidate_x = sobol.draw(num_candidates)
❶ 空间的维数
❷ 要生成的点的数量
在这里,sobol 是 SobolEngine 类的一个实例,它实现了单位立方体中 Sobol 序列的采样逻辑,而 candidate_x 是形状为 (num_candidates, dim) 的 PyTorch 张量,其中包含了具有正确维度的生成点。
注意 重要的是要记住 SobolEngine 生成覆盖单位立方体的点。 要使 candidate_x 覆盖我们想要的空间,我们需要相应地调整此张量的大小。
Sobol 序列应该包含多少个点(即 num_ candidates 的值)由我们(用户)决定;前面的例子展示了我们使用的是 1,000。在典型情况下,您会想要一个足够大的值,以便搜索空间被足够覆盖。然而,一个值太大会使从后验 GP 中采样数值上不稳定。
绘制 GP 样本时的数值不稳定问题
在绘制 GP 样本时的数值不稳定性可能会导致我们在运行 TS 时出现以下警告:
NumericalWarning: A not p.d., added jitter of 1.0e−06 to the diagonal
warnings.warn(
这个警告表明,代码遇到了数值问题,因为 GP 的协方差矩阵不是正定 (p.d.) 的。然而,这个代码也应用了自动修复,其中我们向这个协方差矩阵中添加了 1e–6 的“抖动”,使矩阵成为正定的,所以我们用户不需要再做任何事情。
与我们在 4.2.3 节中所做的一样,我们使用 warnings 模块来禁用此警告,使我们的代码输出更干净,如下所示:
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=RuntimeWarning)
... ❶
❶ TS 代码
您可以玩耍数千个点来找到最适合您的用例、目标函数和训练 GP 的数量。然而,您应该至少使用 1,000 个点。
接下来,我们转向 TS 的第二个组成部分,即从我们的后验 GP 中采样 MVN 并最大化它的过程。首先,实现取样的采样器对象可以被声明为:
ts = botorch.generation.MaxPosteriorSampling(model, replacement=False)
BoTorch 中的 MaxPosteriorSampling 类实现了 TS 的逻辑:从 GP 后验中采样并最大化该样本。这里,model 指的是所观察数据上训练的 GP。重要的是将 replacement 设为 False,确保我们是无替换采样(替换采样不适用于 TS)。最后,为了获得在 candidate_x 中最大样本值的数据点,我们将其传递给采样器对象:
next_x = ts(candidate_x, num_samples=1)
返回的值确实是最大化样本的点,这是我们下一个查询的点。有了这一点,我们的 TS 策略实现就完成了。我们可以将这个代码插入到迄今为止我们用于 Forrester 函数的贝叶斯优化循环中,并使用以下代码:
for i in range(num_queries):
print("iteration", i)
print("incumbent", train_x[train_y.argmax()], train_y.max())
sobol = torch.quasirandom.SobolEngine(1, scramble=True) ❶
candidate_x = sobol.draw(num_candidates) ❶
candidate_x = 10 * candidate_x − 5 ❷
model, likelihood = fit_gp_model(train_x, train_y)
ts = botorch.generation.MaxPosteriorSampling(model,
➥replacement=False) ❸
next_x = ts(candidate_x, num_samples=1) ❸
visualize_gp_belief_and_policy(model, likelihood, next_x=next_x) ❹
next_y = forrester_1d(next_x)
train_x = torch.cat([train_x, next_x])
train_y = torch.cat([train_y, next_y])
❶ 从 Sobol 引擎生成点
❷ 调整生成的点的大小为 −5 到 5,即我们的搜索空间。
❸ 生成下一个要查询的 TS 候选
❹ 在没有采集函数的情况下可视化我们的当前进度
请注意,我们的 BayesOpt 循环的总体结构仍然相同。不同的是,我们现在有一个 Sobol 序列来生成涵盖我们搜索空间的点集,然后将其馈送到实现 TS 策略的 MaxPosteriorSampling 对象中,而不是 BoTorch 策略对象。变量 next_x,就像之前一样,包含我们将查询的数据点。
注意:由于在使用 visualize_gp_belief_and_policy() 辅助函数可视化过程中,我们没有 BoTorch 策略对象,因此不再指定 policy 参数。因此,该函数仅显示每个迭代中的训练好的 GP,而没有获取分数。
图 5.15 TS 策略的进展。该策略探索搜索空间一段时间后逐渐将关注点锁定在全局最优解上。
图 5.15 显示了 TS 的优化进展,我们可以观察到该策略成功地将关注点锁定在全局最优解上但不是不花费探索空间查询次数。这展示了 TS 在 BayesOpt 中协调探索和开发的能力。
我们讨论了基于 MAB 设置的策略所启发的 BayesOpt 策略。我们已经看到,我们学习的两个策略,UCB 和 TS,每个都使用自然启发式,以高效的方式推理未知量并相应地做出决策。BayesOpt 中的一个挑战,即探索和开发之间的平衡问题,也由这两种策略解决,使这些策略具有良好的优化性能。在本书第二部分的下一章节中,我们将学习另一种常用的启发式决策方法,即使用信息论。
5.4 练习
本章有两个练习:
-
第一个练习探索了为 UCB 策略设置权衡参数的潜在方法,该方法考虑了我们在优化过程中的进展情况。
-
第二个练习将本章中学习到的两个策略应用于先前章节中看到的超参数调整问题。
5.4.1 练习 1: 为 UCB 设置探索计划
此练习在 CH05/02 - Exercise 1.ipynb 中实现,讨论了一种自适应设置 UCB 策略的权衡参数 β 的方法。正如 UCB 部分中提到的那样,策略的表现严重依赖于此参数,但我们不清楚该如何设置其值。一个值在某些目标函数上可能效果很好,但在其他目标函数上效果很差。
BayesOpt 从业者已经注意到,随着我们收集越来越多的数据,UCB 可能会过于开发。这是因为随着训练数据集的大小增加,我们对目标函数的了解更多,GP 预测的不确定性也会减少。这意味着 GP 产生的 CI 将变得更紧,将 UCB 使用的获取分数的上限移动到了平均预测附近。
然而,如果 UCB 收获分数与平均预测相似,则该策略是开发性的,因为它只查询具有高预测平均值的数据点。这种现象表明,我们观察到的数据越多,我们对 UCB 的探索应该越多。这里,一种渐进的鼓励 UCB 更多探索的自然方式是慢慢增加权衡参数 β 的值,这是我们在这个练习中学习的,按照以下步骤进行:
-
重新创建 CH04/02 - Exercise 1.ipynb 中的 BayesOpt 循环,将一维 Forrester 函数用作优化目标。
-
我们旨在通过在循环的每个迭代中将其乘以一个常量来逐渐增加权衡参数 β 的值。也就是说,在每次迭代结束时,我们需要使用
beta *= multiplier更新参数。假设我们希望β的值从 1 开始,并在搜索结束时(第十次迭代)达到 10。乘数β的值是多少?
-
实现这个调度逻辑,并观察所得的优化性能:
- 特别是,尽管这个版本的 UCB 从β=1 开始,但它是否会像参数固定在 1 的版本一样陷入局部最优?
5.4.2 练习 2:BayesOpt 用于超参数调整
这个练习在 CH05/03 - Exercise 2.ipynb 中实现,将 BayesOpt 应用于超参数调整任务中支持向量机模型的准确率表面。x-轴表示罚项参数C的值,y-轴表示 RBF 核参数γ的值。有关更多详细信息,请参见第三章和第四章的练习。按照以下步骤进行:
-
重新创建 CH04/03 - Exercise 2.ipynb 中的 BayesOpt 循环,包括实施重复实验的外层循环。
-
运行 UCB 策略,并将权衡参数的值设置为 β ∈ { 1, 3, 10, 30 },观察结果的总体表现:
- 哪个值导致了过度开发,哪个导致了过度探索?哪个值效果最好?
-
运行 UCB 的自适应版本(参见练习 1):
-
权衡参数应该从 3 开始,并在 10 结束。
-
注意,将结束值从 10 改为 30 不会对优化性能产生太大影响。因此,我们认为这种策略对于该结束值的值是健壮的,这是一个期望。
-
比较此自适应版本与其他具有固定 β 的版本的性能。
-
-
运行 TS 策略,并观察其总体表现。
总结
-
MAB 问题由可以执行的一组操作(可以拉动的老虎机的臂)组成,每个操作根据其特定的奖励率返回奖励。目标是在给定一定数量的迭代之后最大化我们接收到的奖励总和(累积奖励)。
-
MAB 策略根据过去的数据选择下一步要采取的行动。好的策略需要在未被探索的行动和高性能行动之间保持平衡。
-
与 MAB 问题不同,在 BayesOpt 中我们可以采取无限多的行动,而不是有限个部分。
-
BayesOpt 的目标是最大化观察到的最大回报,这通常被称为简单回报。
-
BayesOpt 的回报是相关的:相似的行动会产生相似的回报。这在 MAB 中不一定成立。
-
UCB 策略使用对感兴趣数量的乐观估计来做决策。这种在面对不确定性的乐观主义启发式方法可以平衡探索和利用,其中的权衡参数由我们用户设定。
-
UCB 策略的权衡参数越小,策略就越倾向于停留在已知有高回报的区域,变得更加自利。权衡参数越大,策略就越倾向于查询远离观测数据的区域,变得更加寻求探索。
-
TS 策略从感兴趣的数量的概率模型中抽取样本并使用这个样本来做决策。
-
TS 的随机性质使得策略可以适当地探索和利用搜索空间:TS 有可能选择高不确定性和高预测平均值的区域。
-
出于计算原因,在实现 TS 时需要更加谨慎。我们首先生成一组点以均匀覆盖搜索空间,然后为这些点从 GP 后验中抽取样本。
-
为了均匀地覆盖空间,我们可以使用 Sobol 序列生成单位立方体内的点,并将它们缩放到目标空间。
第七章:使用基于熵的策略和信息论的知识
本章内容包括
-
作为衡量不确定性的信息论量度的熵
-
通过信息增益减少熵的方法
-
使用信息理论进行搜索的 BayesOpt 策略
我们在第四章中看到,通过力争从迄今为止取得的最佳值改进,我们可以设计基于改进的 BayesOpt 策略,如改进概率 (POI) 和期望改进 (EI)。在第五章中,我们使用多臂老虎机 (MAB) 策略获得了上限置信度 (UCB) 和汤普森抽样 (TS),每种策略都使用独特的启发式方法来平衡在搜索目标函数全局最优解时的探索和开发。
在本章中,我们学习了另一种启发式决策方法,这次是利用信息理论来设计我们可以在优化流程中使用的 BayesOpt 策略。与我们所见过的启发式方法(寻求改进、面对不确定性的乐观和随机抽样)不同,这些方法可能看起来独特于与优化相关的任务,信息理论是数学的一个主要子领域,其应用涵盖广泛的主题。正如我们在本章中讨论的,通过诉诸信息理论或更具体地说是熵,一种以信息量衡量不确定性的量,我们可以设计出以一种有原则和数学上优雅的方式来减少我们对待优化的目标函数不确定性的 BayesOpt 策略。
基于熵的搜索背后的想法非常简单:我们看看我们关心的数量的信息将最大增加的地方。正如我们在本章后面所讨论的,这类似于在客厅寻找遥控器,而不是在浴室里。
本章的第一部分是对信息理论、熵以及在执行动作时最大化我们所接收的信息量的方法的高层级阐述。这是通过重新解释熟悉的二分查找示例来完成的。具备信息理论的基础知识后,我们继续讨论最大化关于目标函数全局最优解的信息的 BayesOpt 策略。这些策略是将信息理论应用于 BayesOpt 任务的结果。与往常一样,我们还学习如何在 Python 中实现这些策略。
在本章结束时,你将对信息理论是什么、熵作为不确定性度量是如何量化的以及熵如何转化为 BayesOpt 有一个工作理解。本章为我们的 BayesOpt 工具包增加了另一个策略,并结束了关于 BayesOpt 策略的本书第二部分。
使用信息论测量知识
信息论是数学的一个子领域,研究如何以原则性和数学性的方式最佳表示、量化和推理信息。在本节中,我们从信息论的角度重新审视了二分查找的思想,这是计算机科学中一个常用的算法。这次讨论随后允许我们将信息论与 BayesOpt 相连接,并为优化问题提出信息论策略。
6.1.1 使用熵来度量不确定性
信息论在计算机科学中特别常见,其中数字信息被表示为二进制(0 和 1)。你可能还记得计算表示给定数量所需的比特数的例子,例如,一个比特足以表示两个数字,0 和 1,而五个比特则需要表示 32 个(2 的五次方)不同数字。这些计算是信息论在实践中的例子。
决策不确定性下,信息论中的重要概念是熵。熵度量了我们对未知数量的不确定程度。如果将这个未知数量建模为随机变量,熵度量的是随机变量可能取值的变异性。
注意: 这个不确定性度量,熵,与迄今为止我们所称的高斯过程预测中的不确定性有点相似,后者简单地是预测分布的标准差。
在本小节中,我们将进一步了解熵作为一个概念以及如何计算二元事件的伯努利分布的熵。我们展示熵如何成功地量化了对未知数量的不确定性。
让我们回到第一个概率论课的例子:抛硬币。假设你准备抛一枚有偏差的硬币,硬币以概率p(介于 0 和 1 之间)正面朝上,你想要推理这个硬币正面朝上的事件。用二进制随机变量X表示这个事件是否发生(即,如果硬币正面朝上,X = 1,否则X = 0)。那么,我们说X符合参数为p的伯努利分布,并且X = 1 的概率等于p。
在这里,X的熵定义为–p log p – (1 – p) log(1 – p),其中log是以 2 为底的对数函数。我们看到这是一个关于硬币正面概率p的函数。图 6.1 展示了p在(0, 1)区间内熵函数的形状,从中我们可以得出一些见解:
-
熵始终为非负数。
-
当p小于 0.5 时,熵随p的增加而增加,在p = 0.5 时达到最高点,然后逐渐下降。
图 6.1 伯努利随机变量的熵作为成功概率的函数。当成功概率为 0.5 时,熵最大化(不确定性达到最高)。
当我们研究我们对硬币是否落在正面的不确定性时,这两种见解都是宝贵的。首先,我们不应该对某事物有负面不确定性,因此熵永远不是负数。更重要的是,熵在中间处达到最大值,当 p = 0.5 时。这是非常合理的:随着 p 离 0.5 越来越远,我们对事件结果的确定性越来越大—硬币是否落在正面。
例如,如果 p = 0.7,则我们更确定它会落在正面上—这里我们的熵约为 0.9。如果 p = 0.1,则我们对结果更确定(这次是它会落在反面上)—这里的熵大约是 0.5。虽然由于对数函数的原因,熵在端点处未定义,但当我们接近任一端点时,熵接近零,表示零不确定性。另一方面,当 p = 0.5 时,我们的不确定性达到最大值,因为我们对硬币是落正面还是反面最不确定。这些计算表明熵是不确定性的合适度量。
熵 vs. 标准差
当我们在之前的章节中使用术语 不确定性 时,我们指的是由 GP 产生的预测正态分布的标准差。分布的 标准差 正如其名称所示,衡量了分布内的值与平均值偏离的程度,因此是一种有效的不确定性度量。
熵,另一方面,是受到信息理论概念的启发,它也是一种有效的不确定性度量。事实上,它是一种更加优雅和通用的方法来量化不确定性,并且能更准确地模拟许多情况下的边缘情况中的不确定性。
定义 对于给定的概率分布,熵被定义为 –Σ*[i]* p[i] log p[i],其中我们对不同可能的事件按 i 索引进行求和。
我们看到我们用于前述伯努利分布的公式是这个公式的一个特殊情况。我们在本章后面处理均匀分布时也使用了这个公式。
6.1.2 使用熵寻找遥控器
由于熵衡量了我们对感兴趣的数量或事件的知识中有多少不确定性,因此它可以指导我们的决策,帮助我们最有效地减少我们对数量或事件的不确定性。我们在本小节中看一个例子,我们在其中决定在哪里最好地寻找丢失的遥控器。虽然简单,但这个例子呈现了我们在后续讨论中使用的信息论推理,在那里熵被用于更复杂的决策问题。
想象一下,有一天,当你试图在客厅里打开电视时,你意识到找不到通常放在桌子上的遥控器。因此,你决定对这个遥控器进行搜索。首先,你推理说它应该在客厅的某个地方,但你不知道遥控器在客厅的哪个位置,所以所有的位置都是同样可能的。用我们一直在使用的概率推断的语言来说,你可以说遥控器位置的分布在客厅内是均匀的。
图 6.2 是寻找遥控器示例的一个样本平面图。客厅均匀阴影表示遥控器位置在客厅内的分布是均匀的。
图 6.2 可视化了你对遥控器位置的信念,用阴影标示的客厅表示遥控器所在的位置(根据你的信念)。现在,你可能会问自己这个问题:在这个房子里,你应该在哪里找遥控器?认为你应该在客厅里找到遥控器是合理的,而不是在浴室里,因为电视就在那里。但是,如何量化地证明这个选择是正确的呢?
信息理论,特别是熵,通过允许我们推理在客厅与浴室中寻找遥控器后还剩多少熵来提供了一种方法。也就是说,它允许我们确定在客厅里寻找遥控器后与在浴室里寻找遥控器后我们对遥控器位置的不确定性有多少。
图 6.3 显示了在搜索了客厅的一部分后遥控器位置的熵。如果找到遥控器(右上角),则不再存在不确定性。否则,熵仍然会减少(右下角),因为遥控器位置的分布现在更窄了。
图 6.3 显示了一旦搜索了客厅的上部分,遥控器位置的熵如何减少。我们可以推理如下:
-
如果在搜索的区域内找到遥控器,那么你将不再对其位置有任何不确定性。换句话说,熵将为零。
-
如果没有找到遥控器,那么我们对遥控器位置的后验信念将被更新为右下角阴影区域。这个分布跨越的区域比图 6.2 中的区域要小,因此不确定性(熵)更小。
无论哪种方式,查找客厅指定部分的内容都会减少熵。那么,如果你决定在浴室里寻找遥控器会发生什么呢?图 6.4 显示了相应的推理:
-
如果在浴室里找到了遥控器,那么熵仍然会降到零。然而,根据你对遥控器位置的信念,这种情况不太可能发生。
-
如果遥控器在浴室中找不到,那么你对遥控器位置的后验信念不会从图 6.2 中改变,结果熵保持不变。
图 6.4 在搜索完浴室后,遥控器位置的熵。由于遥控器在浴室中找不到,遥控器位置的后验分布中的熵不变。
在浴室里搜索而没有找到遥控器并不会减少遥控器位置的熵。换句话说,在浴室里寻找并不提供有关遥控器位置的任何额外信息,因此根据信息论,这是次优的决定。
如果遥控器位置的先验分布(关于它在哪里的你的初始猜测)涵盖整个房子而不仅仅是客厅,那么这种比较就不会那么明显了。毕竟,遥控器被误放在客厅外的概率总是很小的。然而,决定在哪里寻找的过程——即能够为你提供有关遥控器位置最大信息的房子部分——仍然是相同的:
-
考虑如果找到了遥控器的话,遥控器位置的后验分布,并计算该分布的熵。
-
计算如果遥控器没有找到时的熵。
-
计算两种情况下的平均熵。
-
为考虑寻找的所有位置重复此计算,并选择给出最低熵的位置。
熵提供了一种用信息论方法量化我们对感兴趣的数量的不确定性的方法,利用其在概率分布中的信息。此过程使用熵来识别最大程度减少熵的行动。
注意 这是一个在许多不确定性下决策情况下适用的数学上优雅的过程。我们可以将这种基于熵的搜索过程看作是一种搜索真相的过程,我们的目标是通过最大程度地减少不确定性来尽可能地接近真相。
6.1.3 利用熵进行二进制搜索
为了进一步理解基于熵的搜索,我们现在看看这个过程如何在计算机科学中的经典算法之一:二分查找中体现。您很可能已经熟悉这个算法,所以我们在这里不会详细介绍。对于对二分查找有很好且适合初学者的解释,我推荐阅读 Aditya Bhargava 的《Grokking Algorithms》(Manning,2016)的第一章。从高层次上来说,当我们想要在一个排序列表中查找特定目标数字的位置,使得列表中的元素从第一个到最后一个元素递增时,我们使用二分查找。
提示 二分搜索的思想是查看列表的中间元素并将其与目标进行比较。 如果目标小于中间元素,则我们只查看列表的前一半;否则,我们查看后一半。 我们重复这个列表减半的过程,直到找到目标。
考虑一个具体的例子,我们有一个排序过的 100 个元素列表 [x[1],x[2],...,x[100]],我们想要找到给定目标 z 的位置,假设 z 确实在排序列表中。
图 6.5 在 100 个元素列表上执行二分搜索的示例。 在搜索的每次迭代中,目标被与当前列表的中间元素进行比较。 根据这个比较的结果,我们将第一半或第二半列表从搜索空间中移除。
正如图 6.5 所示,二分搜索通过将列表分为两半进行工作:前 50 个元素和后 50 个元素。 由于我们知道列表是排序过的,我们知道以下内容:
-
如果我们的目标 z 小于第 50 个元素 x[50],那么我们只需要考虑前 50 个元素,因为最后 50 个元素都大于目标 z。
-
如果我们的目标大于 x[50],那么我们只需要查看列表的后一半。
终止搜索
对于图 6.5 中的每次比较,我们忽略z等于被比较的数字的情况,这种情况下我们可以简单地终止搜索。
平均而言,这个过程可以帮助我们更快地在列表中找到 z 的位置,比在列表中顺序搜索从一端到另一端要快得多。 如果我们从概率的角度来处理这个问题,二分搜索是在排序列表中的数字位置搜索游戏中基于信息理论作出最佳决策的实现目标的方法。
注意 二分搜索策略是寻找 z 的最佳解决方案,使我们能够比其他任何策略更快地找到它,平均来看。
首先,让我们用随机变量 L 表示我们目标 z 在排序列表中的位置。 这里,我们想要使用一个分布来描述我们对这个变量的信念。 由于从我们的角度来看,列表中的任何位置都同样可能包含 z 的值,我们使用均匀分布进行建模。
图 6.6 可视化了这种分布,再次表示我们对 z 位置的信念。 由于每个位置都与其他任何位置一样可能,特定位置包含 z 的概率是均匀的 1 ÷ 100,即 1%。
图 6.6 给出了 100 个元素列表中目标 z 位置的先验分布。 由于每个位置一样可能包含 z,特定位置包含 z 的概率为 1%。
注意 让我们尝试计算这个均匀分布的熵。记住,熵的公式是 –Σ[i] p[i] log p[i],其中我们对不同可能事件 i 进行求和。这等于
因此,我们对 L 先验分布的不确定性大约为 6.64。
接下来,我们解决同样的问题:我们应该如何在这个 100 元素列表中搜索以尽快找到 z?我们通过遵循第 6.1.2 节描述的熵搜索过程来做到这一点,我们的目标是尽量减少我们关心的量的后验分布的熵,也就是说,在这种情况下是位置 L。
在检查了给定位置后,我们如何计算后验分布 L 的熵?这个计算要求我们推理在检查了给定位置后我们对 L 可以得出什么结论,这是相当容易做到的。假设我们决定检查第一个位置 x[1]。根据我们对 L 的信念,L 在这个位置的可能性为 1%,而在其余位置的可能性为 99%:
-
如果 L 确实在这个位置,那么关于 L 的后验熵将降为 0,因为对于这个量再也没有不确定性了。
-
否则,L 的分布将更新以反映出观察到 z 不是列表的第一个数字这一事实。
图 6.7 将这个过程显示为一个图表,我们需要更新 L 的分布,以便每个位置都有 1 ÷ 99,或大约 1.01% 的概率包含 z。每个位置仍然同样可能,但每个位置的概率稍微增加了一些,因为在这种假设的情况下,我们已经排除了第一个位置。
图 6.7 给出了在检查第一个元素后目标 z 在 100 元素列表中位置的后验分布。在每种情景中,z 在给定位置的概率会相应更新。
注意 再次说明,我们只考虑 z 存在于列表中的情况,所以要么列表中最小的元素 x[1] 等于 z,要么前者小于后者。
按照同样的计算,我们可以得到这个新分布的熵为
再次说明,这是在第二种情况下 z 不在第一个位置的情况下 L 的后验熵。我们需要采取的最后一步来计算在检查第一个位置后的总后验熵是取这两种情况的平均值:
-
如果 z 在第一个位置,这个可能性是 1%,那么后验熵为 0。
-
如果 z 不在第一个位置,这个可能性是 99%,那么后验熵为 6.63。
取平均值,我们有 0.01(0)+0.99(6.63)=6.56。因此,平均而言,当我们选择查看数组的第一个元素时,我们期望看到的后验熵为 6.56。现在,为了确定是否查看第一个元素是最佳决定,或者是否有更好的位置可以获取更多关于L的信息,我们需要重复此过程以检查列表中的其他位置。具体而言,对于给定的位置,我们需要
-
在检查位置时迭代每个潜在的情况
-
计算每个场景中L的后验熵
-
基于每个场景的可能性来计算跨场景的平均后验熵
让我们再次为第 10 个位置x[10]做一次;相应的图示在图 6.8 中显示。虽然这种情况与我们刚刚讨论的稍有不同,但基本思想仍然相同。首先,当我们查看x[10]时,可能会出现各种情况:
-
第 10 个元素x[10]可能大于z,在这种情况下,我们可以排除列表中的最后 91 个元素,并将搜索集中在前 9 个元素上。在这里,每个位置都有 11%的机会包含z,并且通过使用相同的公式,可以计算后验熵约为 3.17。
-
第十个元素x[10]可能正好等于z,在这种情况下,我们的后验熵再次为零。
-
第十个元素x[10]可能小于z,在这种情况下,我们将搜索范围缩小到最后 90 个元素。在这种情况下,后验熵约为 6.49。
在检查第 10 个元素时,目标z在 100 个元素列表中的后验分布。在每种情况下,z在给定位置的概率会相应更新。
注意确保自己尝试熵计算,以了解我们是如何得到这些数字的。
最后,我们使用相应的概率对这些熵进行加权平均:0.09(3.17)+0.01(0)+0.9(6.49)=6.13。这个数字表示了预期的后验熵——即,在检查第 10 个元素x[10]后,我们对L的位置z的预期后验不确定性。
与第一个元素x[1]的相同数量,6.56,相比,我们得出结论:平均而言,查看x[10]比查看x[1]给我们更多关于L的信息。换句话说,从信息理论的角度来看,检查x[10]是更好的决定。
但从信息理论的角度来看,什么是最佳决定——哪个给我们L的最多信息?为了确定这一点,我们只需为列表中的其他位置重复刚刚对x[1]和x[10]执行的计算,并挑选出具有最低预期后验熵的位置。
图 6.9 展示了我们所寻找目标的预期后验熵随我们选择的检查位置而变化的情况。我们首先注意到曲线的对称性:只看最后一个位置与只看第一个位置的预期后验熵(不确定性)是相等的;同样地,第 10 个位置和第 90 个位置给我们提供的信息是一样的。
图 6.9 展示了随着检查列表中位置的变化,目标的预期后验熵如何变化。检查中间位置是最优的,可以最小化预期熵。
更为重要的是,我们可以看到只有检查中间位置,也就是列表的第 50 个或第 51 个位置,才能提供最大的信息量。这是因为,一旦我们这样做了,无论我们的目标数字大于还是小于中间数字,我们都能排除一半的列表。但对于其他位置并非如此。如前所述,当我们检查第 10 个数字时,可能能排除列表中的 90 个数字,但这只有 0.1 的概率。而当我们检查第一个数字时,99%的概率是只能排除一个数字。
注意在平均上,只检查中间的数字最大化我们获得的信息。
寻找目标的其余流程遵循同样的程序:计算每个决策可能导致的预期后验熵,然后选择使得后验熵最小的决策。由于每次更新后我们所处理的概率分布总是一个均匀分布,因此最优的检查位置总是还没有被排除的中间位置。
这正是二分查找的策略!从信息论角度来看,二分查找是在有序列表中查找数字的最优解决方案。
用信息论证明二分查找
当我第一次学习这个算法时,我记得曾经认为在数组中间搜索的策略似乎是独特的且来源神秘。然而,我们刚刚从信息理论的角度推导出了相同的解决方案,这明确量化了在服务于获得尽可能多的信息或减少尽可能多的熵的目的下,排除一半的搜索空间的想法。
信息论和熵的应用不仅限于二分搜索。正如我们所见,我们经历的过程可以推广到其他决策问题:如果我们能够用概率分布来建模我们感兴趣的问题,包括未知数量、我们可以采取的行动以及在采取行动时如何更新分布,那么我们可以再次选择在信息理论的框架下的最优行动,这是能够最大程度减少我们对感兴趣的数量的不确定性的行动。在本章的剩余部分,我们将学习如何将这个想法应用到 BayesOpt,并使用 BoTorch 实现由此产生的熵搜索策略。
6.2 BayesOpt 中的熵搜索
采用前一节提出的相同方法,我们在 BayesOpt 中获得了熵搜索策略。主要思想是选择我们的行动,我们的实验,以便我们可以在我们关心的后验分布中减少最大数量的熵。在本节中,我们首先讨论如何在高层次上做到这一点,然后转移到在 BoTorch 中实现。
6.2.1 使用信息理论搜索最优解
在我们的遥控器示例中,我们的目标是在公寓内搜索遥控器,因此希望减少遥控器位置分布的熵。在二分搜索中,过程类似:我们的目标是在列表中搜索我们想要搜索的特定数字的位置,并且我们希望减少该数字的分布的熵。现在,要设计一个熵搜索策略,我们必须确定在 BayesOpt 中使用什么作为我们的目标以及如何使用信息理论来辅助搜索过程,这是我们在这里要学习的。
回想一下我们在使用 BayesOpt 时的最终目标:在黑盒函数的定义域D内搜索函数最大化的位置。这意味着我们的自然搜索目标是使函数的目标值最大化的位置x,即f = f(x*)* ≥ f(x),对于D中的所有x。
定义 最优位置x通常称为目标函数f的优化器。
给定对目标函数f的 GP 信念,存在对优化器x的相应概率信念,它被视为随机变量。图 6.10 显示了一个经过训练的 GP 的示例以及从 GP 中导出的目标优化器x的分布。需要牢记这个分布的一些有趣特征:
-
分布复杂且多峰(具有几个局部最优解)。
-
优化器x最有可能的位置在零的左边一点。这是 GP 的预测均值最大化的地方。
-
优化器x位于端点-5 或 5 的概率不可忽略。
-
x大约为 2 的概率几乎为零,这对应于我们已经观察到的高于f(2)的目标值。
图 6.10 GP 信念(顶部)和函数优化器x的分布(底部)。优化器的分布是非高斯的,相当复杂,这对建模和决策提出了挑战。
这些特征使得建模优化器x的分布变得非常具有挑战性。衡量这个量x分布的最简单方法就是从 GP 中简单地抽取许多样本,并记录每个样本被最大化的位置。事实上,这就是图 6.10 生成的方法。
更糟糕的是,当目标函数的维度(输入向量x的长度或每个x具有的特征数量)增加时,我们需要指数级别的样本来估计x的分布。
定义 这是维度诅咒的一个例子,在机器学习中,它经常用来指代与感兴趣对象的维度相关的许多过程的指数成本。
要在 BayesOpt 中使用熵搜索,我们需要对最优解的位置x的信念建模,使用概率分布。然而,我们无法精确地建模优化器x的分布;相反,我们必须使用从 GP 中抽取的样本来近似它。不幸的是,随着目标函数中维度的增加(x的长度),这个过程很快就变得计算昂贵起来。
注意 实际上,有研究论文在 BayesOpt 中寻求使用熵来搜索优化器x的位置。然而,由此产生的策略通常运行起来成本过高,且未在 BoTorch 中实现。
但这并不意味着我们需要完全放弃在 BayesOpt 中使用信息理论的努力。这只意味着我们需要修改我们的搜索过程,使其更适合计算方法。一个简单的方法是针对除了优化器x之外的其他量,一方面与寻找x相关联,另一方面更容易进行推理。
在优化中的一个感兴趣的量,除了优化器x之外,就是在优化器处实现的最优值f = f(x),它也是一个随机变量,根据我们对目标函数f的 GP 信念而定。正如大家可以想象的那样,了解最优值f可能会告诉我们很多关于优化器x的信息;也就是说,这两个量在信息理论上是相关联的。然而,最优值f比优化器x更容易处理,因为前者只是一个实数值,而后者是一个长度等于目标函数维度的向量。
图 6.11 右侧面板显示了由 GP 诱导的最优值f**的分布示例。我们看到,这个分布大致截断在 1.6 左右,这恰好是我们训练数据集中的现任值;这是有道理的,因为最优值f**必须至少是现任值 1.6。
图 6.11 GP 信念(左上)、函数优化器x**的分布(底部)以及最优值f**的分布(右)。最优值的分布始终是一维的,因此比优化器的分布更容易处理。
注意 将我们的努力集中在*f**的分布上的主要优势在于,不管目标函数的维度如何,该分布始终是一维的。
具体来说,我们可以从这个一维分布中抽样来近似查询后验熵的期望。从这一点出发,我们遵循熵搜索背后的相同思想:选择(近似)最小化期望后验熵的查询,换句话说,最大化熵的减少。
定义 通过使用这个期望熵减少量作为获取得分,我们得到了最大值熵搜索(MES)策略。术语最大值表示我们正在使用信息理论来搜索最大值,或目标函数的最优值*f**。
图 6.12 在底部面板显示了我们运行示例中的 MES 获取得分,根据这个信息理论基础的准则,我们应该在约为-2 的位置查询下一个点。MES 策略更喜欢这个位置,因为它既有相对较高的均值又有较高的 CI,因此平衡了探索和利用。
图 6.12 GP 信念(左上)、最优值*f**的分布(右)以及用作获取函数得分的近似期望熵减少量。最优值的分布始终是一维的,因此比优化器的分布更容易处理。
有趣的是,这里的收益情况看起来与优化器*x**的分布有些相似,如图 6.11 所示,我们看到曲线
-
在中间某处达到顶峰
-
在端点处达到一个非零值
-
在 2 附近达到 0,我们确切知道该位置不是最佳的
这表明最优值f**与优化器x密切相关,虽然我们通过改变我们的目标失去了一些信息,但搜索*f是搜索*x**的一个良好代理,并且计算上更容易处理。
6.2.2 使用 BoTorch 实现熵搜索
讨论了 MES 背后的高层思想后,我们现在准备使用 BoTorch 实现它,并将其插入我们的优化管道中。MES 策略由 botorch.acquisition.max_value_entropy_search 中的 qMaxValueEntropy 类实现为一个 PyTorch 模块,类似于我们之前见过的大多数贝叶斯优化策略。当初始化时,这个类接受两个参数:一个 GPyTorch GP 模型和一组将用作前面部分描述的近似过程中样本的点。
尽管有许多方法可以生成这些样本点,但我们从 5.3.2 节学到的一种特殊方式是使用 Sobol 序列,它更好地覆盖了目标空间。总的来说,MES 策略的实现如下所示:
num_candidates = 1000 ❶
sobol = torch.quasirandom.SobolEngine(1, scramble=True) ❶
candidate_x = sobol.draw(num_candidates) ❶
candidate_x = 10 * candidate_x - 5 ❷
policy = botorch.acquisition.max_value_entropy_search
➥.qMaxValueEntropy( ❸
model, candidate_x ❸
) ❸
with torch.no_grad(): ❹
acquisition_score = policy(xs.unsqueeze(1)) ❹
❶ 使用 Sobol 序列在 0 和 1 之间生成样本
❷ 重新缩放样本以使其位于域内
❸ 声明 MES 策略对象
❹ 计算获取分数
这里,num_candidates 是一个可调参数,它设置了在 MES 计算中您想要使用的样本数量。较大的值意味着更高保真度的近似,但这将带来更高的计算成本。
现在让我们将此代码应用于我们正在解决的优化一维 Forrester 函数的问题中,该函数在 CH06/01 - BayesOpt loop.ipynb 笔记本中实现。我们已经熟悉大部分代码,所以我们不在这里详细介绍。
图 6.13 展示了 MES 对 10 个查询的进展,其中策略在五次查询后迅速找到了 Forrester 函数的全局最优解。有趣的是,随着优化的进行,我们对于查看搜索空间中的其他区域不会导致任何实质性熵减少越来越确信,这有助于我们保持接近最优位置。
图 6.13 MES 策略的进展。策略在五次查询后迅速找到全局最优解。
我们已经看到信息理论为我们提供了一个基于数学的、优雅的决策框架,围绕着尽可能多地了解感兴趣的数量。这归结为减少模型我们关心的数量的预期后验熵。
在贝叶斯优化中,我们看到直接将这个过程进行转换在模型化目标函数的最优值的位置方面存在计算挑战,这是我们的主要搜索目标。相反,我们将重点转移到目标函数本身的最优值,使计算更易处理。幸运的是,所有这些数学内容都被 BoTorch 很好地抽象出来,留下了一个方便的、模块化的接口,我们可以将其插入任何优化问题中。
这也是关于 BayesOpt 策略的书的第二部分的结束。最后三章涵盖了一些最常用的启发式方法来进行 BayesOpt 中的决策以及相应的策略,从寻求改进到借用多臂老虎机方法,以及在本章中使用信息理论。
本书的剩余部分将我们的讨论提升到一个新的水平,介绍了与我们迄今所见不同的特殊优化设置,在这些设置中,我们在优化的每一步都会顺序观察一个单独的数据点。这些章节表明了我们学到的方法可以被转化为现实世界中的实际设置,以加速优化。
6.3 练习
本章有两个练习:
-
第一个练习涵盖了一种二分搜索的变体,在这种搜索中可以考虑先前的信息来做决策。
-
第二个练习讲解了在先前章节中出现的超参数调整问题中实施 MES 的过程。
6.3.1 练习 1:将先验知识纳入熵搜索
我们在第 6.1.3 节中看到,通过在数组中目标位置上放置均匀先验分布,最优的信息理论搜索决策是将数组切分一半。如果均匀分布不能忠实地表示你的先验信念,你想使用不同的分布会发生什么?这个练习在 CH06/02 - Exercise 1.ipynb 笔记本中实现,向我们展示了这个例子以及如何推导出结果的最优决策。解决这个练习应该会帮助我们进一步欣赏熵搜索作为一种在不确定性下的通用决策过程的优雅和灵活性。
想象一下以下情景:你在一个电话制造公司的质量控制部门工作,你目前的项目是对公司最新产品的外壳的耐用性进行压力测试。具体来说,你的团队想要找出从一个 10 层楼的建筑的哪一层楼可以将手机扔到地上而不会摔坏。有一些规则适用:
-
制造手机的工程师确定如果从一楼扔下手机,它不会摔坏。
-
如果手机从某个楼层掉下来时摔坏了,那么它也会在更高的楼层掉下时摔坏。
你的任务是找出可以从中扔手机而不会摔坏的最高楼层 — 我们将这个未知楼层称为 X — 通过进行试验。也就是说,你必须从特定楼层扔真正的手机来确定 X。问题是:你应该如何选择从哪些楼层扔手机以找到 X?由于手机很昂贵,你需要尽量少进行试验,并且希望使用信息理论来辅助搜索:
-
假设通过考虑物理学、手机的材料和构造,工程师们对可能的楼层有一个初始猜测。
具体来说,X 的先验分布是指数型的,即X 等于一个数字的概率与该数字的倒数成指数关系:Pr(X = n) = 1 / 2^(n),对于n = 1, 2, ..., 9;与最高(第十)层对应的概率是Pr(X = 10) = 1 / 2⁹。因此,X = 1 的概率为 50%,随着数字的增加,这个概率减半。这个概率分布在图 6.14 中可视化。
图 6.14 X 等于 1 到 10 之间的数字的概率(即,当手机从高楼掉落时不会摔碎的最高楼层的概率)
确认这是一个有效的概率分布,方法是证明概率之和等于一。也就是说,证明Pr(X = 1) + Pr(X = 2) + ... + Pr(X = 10) = 1。
-
使用第 6.1.1 节末尾给出的公式计算这个先验分布的熵。
-
给定在 1 到 10 之间定义的先验分布,从第二层掉落时手机摔碎的概率是多少?第五层呢?第一层呢?
-
假设在观察任何试验结果之后,X 的后验分布再次是指数型的,并且在最低和最高可能的楼层之间定义。
例如,如果观察到手机从五楼掉落时不会摔碎,那么我们知道X至少为 5,X 的后验分布是这样的,Pr(X = 5) = 1 / 2,Pr(X = 6) = 1 / 4,...,Pr(X = 9) = 1 / 32,Pr(X = 10) = 1 / 32。另一方面,如果手机从五楼掉落时摔碎了,那么我们知道X至多为 4,后验分布是这样的,Pr(X = 1) = 1 / 2,Pr(X = 2) = 1 / 4,Pr(X = 3) = 1 / 8,Pr(X = 4) = 1 / 8。图 6.15 显示了这两种情况。
图 6.15 当手机从五楼掉落时X的两种情况下的后验概率分布。每个后验分布仍然是指数型的。
计算这个虚构的后验分布在这两种情况下的熵。
-
给定先验分布,在你对第五层进行一次试验之后(即,你从第五层扔手机并观察是否摔碎),计算期望后验熵。
-
计算其他楼层的预期后验熵。哪一层楼的熵减少最多?这仍然与二分搜索的结果相同吗?如果不是,发生了什么变化?
6.3.2 练习 2:贝叶斯优化用于超参数调整
此练习在 CH06/03 - Exercise 2.ipynb 笔记本中实现,将 BayesOpt 应用于模拟超参数调整任务中支持向量机模型的准确度表面的目标函数。 x轴表示惩罚参数C的值,而y轴表示 RBF 核参数γ的值。有关更多详细信息,请参阅第三章和第四章的练习。完成以下步骤:
-
在 CH05/03 - Exercise 2.ipynb 笔记本中重新创建 BayesOpt 循环,包括实现重复实验的外部循环。
-
运行 MES 策略。由于我们的目标函数是二维的,我们应该增加 MES 使用的 Sobol 序列的大小。例如,您可以将其设置为 2,000。观察其聚合性能。
BayesOpt 中的重复实验
参考第四章练习 2 的第 9 步,看看我们如何在 BayesOpt 中运行重复实验。
摘要
-
信息论研究信息的表示、量化和传输。该领域的核心概念之一是熵,它量化了我们对随机变量的不确定性,根据变量的概率分布。
-
熵搜索过程考虑通过采取行动减少量化的期望熵(因此减少不确定性)的感兴趣数量。我们可以将这个通用过程应用于许多不确定性下的决策问题。
-
二进制搜索可能是将熵搜索应用于在排序数组中找到特定数字位置的问题的结果。
-
维度诅咒是指与所关注对象的维度相对应的许多 ML 过程的指数成本。随着维度的增加,完成该过程所需的时间呈指数增长。
-
在 BayesOpt 中,虽然熵搜索可以应用于寻找函数优化器位置的问题,但由于维度诅咒,它的计算成本很高。
-
为了克服维度诅咒,我们修改了找到函数优化值的目标,将其变为一维搜索问题。由此产生的 BayesOpt 策略称为最大值熵搜索(MES)。
-
由于由 GP 模型的全局最优的复杂行为,计算 MES 的收购分数的封闭形式是不可行的。但是,我们可以从概率分布中抽取样本来近似获得收购分数。
-
在 BoTorch 中实现 MES 遵循与实现其他 BayesOpt 策略相同的过程。为了促进收购分数近似中的采样过程,我们在初始化策略对象时使用 Sobol 序列。
第三部分:将贝叶斯优化扩展到专门的设置
我们学习到的贝叶斯优化循环代表了广泛的优化问题。然而,现实生活场景通常不遵循这种高度理想化的模型。如果您可以同时运行多个函数评估,这在多 GPU 可用的超参数调整应用中很常见,会怎样?如果您有多个竞争的目标,您想要优化?本部分介绍了您可能在现实世界中遇到的一些最常见的优化情景,并讨论了如何将贝叶斯优化扩展到这些情景中。
为了增加吞吐量,许多设置允许实验并行运行。第七章介绍了批量贝叶斯优化框架,在这种框架中进行函数评估是批量进行的。我们学习如何扩展第二部分学到的决策策略到这个设置中,同时确保充分利用系统的并行性。
在关键安全用例中,我们不能自由地探索搜索空间,因为某些函数评估可能会产生有害效果。这促使出现了在所讨论的函数应如何行为上存在约束,并且在优化策略的设计中需要考虑这些约束的情况。第八章处理了这种情境,称为受限制的优化,并开发了必要的机制来应用贝叶斯优化。
第九章探讨了我们可以以不同成本和精度水平观察函数值的多种方式的情境;这通常被称为多信度贝叶斯优化。我们讨论了熵的自然扩展,以量化在不同保真度水平上进行评估的价值,并将该算法应用于平衡信息和成本。
已经证明,两两比较比数字评估或评分更准确地反映了一个人的偏好,因为它们更简单,对标注者的认知负荷更轻。第十章将贝叶斯优化应用于这种情境,首先使用特殊的 GP 模型,然后修改现有策略以适应这种两两比较工作流程。
多目标优化是一个常见的用例,我们旨在同时优化多个可能存在冲突的目标。我们研究了多目标优化问题,并开发了一个可以共同优化我们的多个目标的贝叶斯优化解决方案。
在这一系列特殊的优化设置中,有一个共同的主题:在考虑问题的结构时权衡探索和利用。通过看到在本部分如何将贝叶斯优化应用于各种设置中,我们不仅巩固了对这一技术的理解,而且使该技术在实际场景中更加适用。这些章节中开发的代码将帮助您立即解决您在现实生活中可能遇到的任何优化问题。
第八章:在批处理优化中最大化吞吐量
本章涵盖了
-
批量进行函数评估
-
扩展贝叶斯优化到批处理设置
-
优化难以计算的获取分数
到目前为止,我们一直使用贝叶斯优化循环,该循环一次处理一个查询,并在进行下一个查询之前返回查询的函数评估结果。我们在函数评估必须按顺序进行的情况下使用该循环。但是,很多黑盒优化的实际场景都允许用户批量评估目标函数。例如,在调整 ML 模型的超参数时,如果我们可以访问多个处理单元或计算机,我们可以并行尝试不同的超参数组合,而不是逐个运行单独的组合。通过利用所有可用资源,我们可以增加进行的实验数量并最大限度地提高贝叶斯优化循环的函数评估吞吐量。
我们将同时可以进行多个查询的 BayesOpt 变体称为批处理贝叶斯优化。除了超参数调整之外,批处理 BayesOpt 的其他例子包括药物发现,科学家使用实验室中的多台机器来合成单个药物原型,以及产品推荐,推荐引擎同时向客户呈现多个产品。总的来说,在多个实验可以同时运行的黑盒优化情景下,批处理 BayesOpt 是一个非常常见的设置。
鉴于计算和物理资源通常可以并行化,批处理 BayesOpt 是贝叶斯优化在实际应用中最常见的设置之一。在本章中,我们介绍了批处理 BayesOpt,并了解到我们在前几章中学习的策略如何扩展到此设置。我们讨论了为什么将 BayesOpt 策略扩展到批处理设置不是一个简单的任务,为什么它需要仔细考虑。然后,我们学习了各种策略,以便在 Python 中使用 BoTorch 实现 BayesOpt 策略。
本章结尾时,你将理解批处理贝叶斯优化的概念,了解批处理贝叶斯优化的适用场景,以及如何在这种情况下实现贝叶斯优化策略。掌握在批处理贝叶斯优化中如何并行化贝叶斯优化,我们可以使贝叶斯优化在实际应用中更加实用和适用。
7.1 同时进行多个函数评估
在黑箱问题中同时进行多个函数评估的能力在许多实际场景中很常见,批次 BayesOpt 是 BayesOpt 的一种设置,其中考虑了函数评估的并行性。在本节中,我们将介绍批量 BayesOpt 的确切设置以及在使用 BayesOpt 策略向目标函数提出多个查询时可能面临的挑战。本节将激发将 BayesOpt 策略扩展到批量设置的各种策略。我们将在本章后面的章节中介绍这些策略,从第 7.2 节开始。
7.1.1 充分利用所有可用资源并行处理
昂贵的黑箱优化问题的一个定义性特征是进行函数评估可能成本过高。在第 1.1 节中,我们研究了在许多应用中进行函数评估的高成本;调整神经网络的超参数需要大量时间和计算资源,而创建新药物的成本近年来呈指数增长,这是两个例子。在黑箱优化中查询目标函数的成本促使我们需要使查询目标函数的过程更有效率。我们可以通过并行性来实现这一点的方式之一。
定义并行性指的是同时运行独立进程,以便完成这些进程所需的总时间缩短。
并行性的好处总结在图 7.1 中,其中三个过程(可以是要运行的程序,要完成的计算等)要么顺序运行,要么并行运行。当并行运行时,三个过程只需原本顺序运行所需总时间的三分之一。
图 7.1 并行性的好处示意图。三个通用过程可以顺序运行(左)或并行运行(右)。当并行运行时,三个过程只需原本顺序运行所需总时间的三分之一。
并行性在计算机科学中特别常见,计算机可以并行使用多个处理单元同时处理多个程序。如果这些程序彼此独立(它们不使用彼此的数据或写入相同的文件),它们可以并行运行而不会出现任何问题。
相同的理念适用于使用 BayesOpt 的优化设置。例如,一位 ML 工程师调整神经网络可以利用他们可以访问的多个 GPU 同时训练多个模型。试图发现新药物的科学家可以使用实验室中的设备同时合成多个配方。同时进行多个查询可以在相同的学习目标函数所花费的时间内获取更多信息。
什么是 GPU?
GPU,即图形处理单元,是优化执行并行矩阵乘法的硬件。因此,它们通常用于训练神经网络。
以烤一批饼干为例。如果你愿意的话,你可以一次只烤一个饼干,但这样做会浪费诸如烤箱能源和时间等资源。相反,你更有可能一次性同时烤多个饼干。
烘焙饼干的贝叶斯优化
关于烘焙的话题,事实上,有一篇关于批次贝叶斯优化的研究论文(static.googleusercontent.com/media/research.google.com/en//pubs/archive/46507.pdf)讨论了通过找到制作饼干面团时使用的最佳鸡蛋、糖和肉桂的量来优化饼干配方的问题。
在贝叶斯优化中,批次设置允许同时评估目标函数的多个输入。也就是说,我们可以一次性向评估目标的黑盒发送多个查询,x[1],x[2],...,x[k],并一次性接收到相应的目标值f(x[1]),f(x[2]),...,f(x[k])。相比之下,在经典的顺序贝叶斯优化设置中,只有在观察到f(x[1])后,我们才能继续在另一个位置x[2]处进行查询。
在每一次批次贝叶斯优化循环的迭代中,我们挑选出多个输入位置来评估目标函数,而不是像我们之前一直做的那样只评估单个位置。图 7.2 显示了这个批次贝叶斯优化循环。一次性需要多次查询的要求意味着我们需要新的贝叶斯优化策略来评分这些输入位置的有用性。我们将在下一节更多地讨论为什么我们学到的贝叶斯优化策略不能轻易扩展到批次设置中。贝叶斯优化循环的另一个组成部分,GP,保持不变,因为我们仍然需要一个产生概率预测的机器学习模型。换句话说,贝叶斯优化的决策组件需要修改以适应批次设置。
图 7.2 批次贝叶斯优化循环。与顺序贝叶斯优化相比,批次贝叶斯优化需要在步骤 2 中识别多个查询点,并在步骤 3 中同时评估这些点上的目标函数。
策略的收获分数
贝叶斯优化策略为搜索空间中的每个输入位置分配一个称为收获分数的分数,该分数量化了输入在寻找目标函数全局最优解过程中的有用程度。每个策略使用不同的启发式来计算这个分数,详情见第 4 到 6 章。
可以同时进行的查询数量——即批次的大小——取决于应用程序。例如,您可以同时烘烤多少块饼干取决于您的烤箱和烤盘的大小。您可用的计算资源(CPU 和 GPU 的数量)决定了调整模型超参数时可以并行训练多少神经网络。图 7.1 显示了同时运行三个进程的示例,因此批次大小为 3。
7.1.2 为什么我们不能在批处理设置中使用常规 BayesOpt 策略?
我们在上一节中说过,在顺序设置下学习的 BayesOpt 策略(其中对目标函数的查询是顺序地进行的,一个接一个地进行)不能重新用于批处理设置中而不经过修改。在本节中,我们将更详细地讨论为什么会出现这种情况,以及为什么我们需要专门针对批处理设置的策略。
从第 4.1 节我们知道,BayesOpt 策略会为搜索空间中的每个点分配一个分数,该分数量化了该点对我们寻找目标函数全局最优解的有用程度。然后,我们寻找给出最高分数的点,并将其选择为下一个查询的目标函数。图 7.3 显示了由预期改进(EI)策略(在第 4.3 节介绍)计算的分数,作为底部面板中的曲线,其中 1.75,如下所示的垂直标记在较低曲线上,最大化了分数,是我们的下一个查询。
图 7.3 BayesOpt 的一个例子。顶部面板显示了 GP 预测和地面真实目标函数,而底部面板显示了 EI 产生的采集分数,这在第 4.3 节中讨论过。在较低曲线上的垂直刻度为 1.75,表示下一个查询。
如果我们使用相同的 EI 分数,即图 7.3 中的较低曲线,来挑选不止一个点来查询目标函数,会发生什么?我们需要确定许多给出最高 EI 分数的点。然而,这些给出高 EI 分数的点会简单地聚集在顺序设置下选择的点周围。这是因为沿着较低曲线,如果我们从 1.75 移动一个无穷小的距离,我们仍然会得到一个高的 EI 分数。也就是说,接近给出最高采集分数的点也会给出高的采集分数。
如果我们简单地挑选出获得分数最高的点,我们的查询将聚集在搜索空间的一个区域,实质上是把所有的鸡蛋放在一个篮子里。这在图 7.4 中有所说明,那些给出最高 EI 分数的查询聚集在 1.75 附近。这种聚集效应是不可取的,因为我们在本质上浪费了宝贵的资源,评估了目标函数在基本上是一个输入位置的值。这些聚集点比分散的点不那么有用。
图 7.4 如果我们仅仅选择具有最高获得分数的点,并通过水平刻度线在下方图表上标识出来,那么在批量设置中所进行的查询会很接近,并且不如更分散排布的情况有用。
选择所有查询的点都聚集在一个位置附近会阻碍我们从批量设置中固有的并行性中受益。到目前为止,我们的讨论表明,设计一批查询并不像选择具有最高贝叶斯优化策略获得分数的顶点那样简单。在本章的剩余部分中,我们将讨论专门为批量设置设计的贝叶斯优化策略。方便的是,对我们来说,这些策略是对我们在第四章到第六章中所学习的贝叶斯优化策略的扩展,因此我们只需要学习如何将我们所学到的优化启发式扩展到批量设置中。
7.2 计算一批点的改进和上限置信度
我们将要扩展到批量设置的第一类策略是基于改进的策略,这是第四章的主题,以及在第 5.2 节讨论的 UCB 策略。这些策略使用的启发式方法可以被修改为在批量设置中工作,我们稍后会看到。
在下一节中,我们介绍这些启发式的数学修改,并讨论生成的批量策略的工作原理。之后,我们将学习如何使用 BoTorch 声明和运行这些批量策略。
7.2.1 将优化启发式扩展到批量设置
在第 7.1.2 节的讨论中,我们可以看出,选择一批点来评估目标函数并不像找到最大化顺序策略获得分数的顶点那样简单。相反,我们需要重新定义这些顺序策略的数学公式,以使它们适用于批量设置。
适用于我们所学到的三种贝叶斯优化策略(PoI,EI 和 UCB)的策略有一种策略,它们将其获取得分公式定义为各个正态分布上的平均值。也就是说,这三种策略中,每种策略分配给给定点的分数都可以写为顺序设置中数量的平均值。对于 PoI,这个数量是我们是否能观察到改进;对于 EI,这个数量是改进的大小。
图 7.5 将贝叶斯优化策略的数学表达扩展到批量设置中。在两种情况下,我们使用的是感兴趣数量的平均值。在批量设置中,我们会在对整个批次的效用进行平均之前先选取批次中点的最大值来表示整个批次的效用。
正如图 7.5 顶部所示,顺序 BayesOpt 策略使用某个数量 G(f(x)) 在我们对目标函数 f(x) 的值的信念的正态分布上的平均值来对候选查询 x 进行评分。这个数量 G 取决于 BayesOpt 策略用于平衡探索和利用的启发式方法。对于查询批次 x[1],x[2],...,x[k],我们相反地计算批次中点的数量 G 的最大值的平均值,如图 7.5 底部所示。这个平均值是在对应于目标值 f(x[1]),f(x[2]),...,f(x[k]) 的多元高斯分布上计算的。
探索与利用的平衡
所有的 BayesOpt 策略都需要解决在搜索空间中找到高性能区域(利用)和检查未探索区域(探索)之间的折衷。更深入地讨论此折衷,请参阅第 4.1.2 节。
这种使用兴趣量 G 的最大值来表示整个批次效用的策略在优化的背景下是直观的。G 的最大值越高,整个查询批次的价值就越高。有了一种方法来量化任何给定查询批次的价值,我们现在可以继续寻找最大化该数量的批次。我们使用的启发式方法类似于奥运会等体育比赛中的做法:每个国家可能在整年中训练很多运动员,但当时机成熟时,只选择最优秀的个人参加比赛。图 7.6 可视化了这个过程。
图 7.6 批量 BayesOpt 启发式选择具有最高 G 值的最佳元素来表示整个批次(底部)。这种策略类似于奥运会中的团队选拔,只选择最优秀的运动员代表一个国家。
如何利用上述三种政策实现这种策略?让我们首先讨论前两种:基于改进的策略。请记住,在第四章中,PoI 使用下一个查询将从最佳点(现任者)改进的概率作为获取分数。一个点更有可能比现任者产生更好的结果,PoI 给予该点的分数就越高。另一方面,EI 政策考虑改进的幅度,给出的获取分数较高,表明这些点很可能从现任者那里改进,而且改进幅度较大。
这两种策略的区别在图 7.7 中得到了可视化,其中不同的结果位于 x 轴上,y 轴显示了要优化的目标值。PoI 将所有在 x 轴上产生比现任者更高值的点视为平等,而 EI 则考虑每个点的改进程度。
图 7.7 PoI(左)和 EI(右)之间的区别。前者仅考虑我们是否从现有值中提高,而后者考虑了提高多少。
在批次设置中,我们可以类似地推理出在 BayesOpt 循环的当前迭代后观察到的提高。与针对一批查询中的多个点推理不同,我们可以在这些点的函数评估中单独找出最大值。也就是说,如果我们的查询批次分别为 x[1],x[2],...,x[k],我们不需要使用所有函数评估 f(x[1]),f(x[2]),...,f(x[k]) 来推理我们观察到的提高。我们只需使用最大值 max {*f*(*x*[1]), *f*(*x*[2]), ... ,*f*(*x[k]*)},因为这个最大值定义了我们观察到的提高。
按照图 7.7 中的例子,假设我们的现有值具有 20 的目标值,并考虑图 7.8 中右侧可视化的以下情景:
-
如果我们的查询批次大小为 3,且返回的值全都低于 20(右板块中的 X[1]),那么我们将不会观察到提高。在 X[1] 中的最高函数评估是 3,意味着本批次中的函数评估都没有从现有值中提高。
-
如果所有返回值都超过了现有值(对应于 X[2]),那么我们将会观察到一个从现有值中提高的情况。特别地,这个批次中的最大值 X[2] 等于 30,导致了一个 10 的提高。
-
更重要的是,即使只有一些而不是所有返回的函数评估优于现有值(例如 X[3]),我们仍然会观察到一个提高。X[3] 的最大值是 22,这是从现有值 20 中的确有所提高的。
通过关注从一批次查询中返回的最大评估值,我们可以立即确定这个批次是否从现有值中提高。图 7.8 显示了 PoI 的这种基于提高的推理,其中批次 X[2] 和 X[3] 被平等地处理,因为它们(或更具体地说,它们的最大值)都导致了提高。现在,我们有了一种方法,将计算提高的概率从顺序扩展到批量设置。
图 7.8 查询(左)还是一批查询(右)是否会从现有值中提高。在右侧的批量设置中,我们仅考虑每个批次中的最大值,以确定是否存在提高。
收购分数(acquisition score) 是指给定候选查询批次的概率,即返回的函数评估中最大值是否会超过现有值。
从计算函数评估 f(x) 将超出现有值 f* 的概率,记为 Pr(f(x) > f*),在顺序设置下,我们推导出计算最大函数评估将超出现有值 f* 的概率,Pr(max {f(x[1]), f(x[2]), ..., f(x[k])} > f*)。然后,我们将使用该概率 Pr(max {f(x[1]), f(x[2]), ..., f(x[k])} > f*) 作为批处理查询 x[1]、x[2]、...、x[k] 的 PoI 采集分数。
正如本节前面提到的,这些概率 Pr(f(x) > f*) 和 Pr(max {f(x[1]), f(x[2]), ..., f(x[k])} > f*) 可以被视为高斯分布下对我们的优化进展重要性的量的平均值。具体而言,这些概率分别是二进制随机变量的平均值,指示 f(x) > f* 和 max {f(x[1]), f(x[2]), ..., f(x[k])} > f* 是否为真。该比较在图 7.9 中可视化。
图 7.9 将 POI 政策扩展到批处理设置中。在顺序情况下(上),我们考虑下一个查询相比现有值是否有改进。在批处理设置下(下),我们考虑批处理中所有点的最大值相比现有值是否有改进。
要完成具有这种 PoI 政策的批处理 BayesOpt 循环,我们需要找到批处理 x[1]、x[2]、...、x[k] 来最大化采集分数 Pr(max {f(x[1]), f(x[2]), ..., f(x[k])} > f*)。正如我们在 4.1.1 节中所学到的,我们可以使用 BoTorch 的 optim.optimize 模块中的辅助函数 optimize_acqf() 来促进批处理 x[1]、x[2]、...、x[k] 的搜索,以优化采集分数,我们将在 7.2.2 节中看到。
我们现在进入 EI 政策,它计算从查询特定点得出的相对于现有值的改进的预期值。由于我们已经有了一种方式来推理出在观察到一批函数评估后相对于现有值的改进,因此我们可以扩展 EI。即,我们只计算从返回批处理中的最大函数评估所得到的相对于现有值的改进的预期值,即 max {f(x[1]), f(x[2]), ..., f(x[k])}。与 PoI 计算最大值是否超出现有值的概率不同,EI 则考虑最大值超出改进的程度。EI 和其批处理变体之间的差异在图 7.10 中可视化。
图 7.10 将 EI 政策扩展到批处理设置中。在顺序情况下(上),我们使用下一个查询相比现有值的平均提升量。在批处理设置下(下),我们计算批处理中所有点的最大值相比现有值的平均提升量。
为了说明这种推理,图 7.11 显示了顺序(左侧面板)和批处理设置(右侧面板)中 EI 评分不同结果的区别。右侧面板中以下内容为真:
-
不具有任何点能从 20 的现有值改进的批次(以X[1]为例)将构成零改进。
-
批次X[2]中的最大值为 22,所以即使这批次中的某些值低于现有值,我们也观察到了 2 的改进。
-
尽管批次X[3]中的值都高于现有值,但我们观察到的改进完全是由最大值 30 决定的。
-
最后,即使大多数批次X[4]低于现有值 20,X[4]的最大值为 50,使得这个批次成为一个非常好的结果。
图 7.11 查询(左)或一批查询(右)是否导致从现有值的改进。在右侧的批处理设置中,我们只考虑每个批次中的最大值,以确定是否有改进。
要继续进行批量 EI,我们计算批次内最大值比现有值高多少的期望值。这种改进的期望值或预期改进是 EI 用于评估给定批次x[1]、x[2]、...、*x[k]*的价值的收购分数批次。辅助函数optimize_acqf()可以再次用于找到提供最高预期改进的批次。
到目前为止的讨论帮助我们将基于改进的两个政策,PoI 和 EI,扩展到批处理设置。我们现在剩下的是 UCB 政策。幸运的是,选择从一批查询中挑选出最大值的策略也适用于 UCB。为了将与兴趣函数G有关的批次的最大值挑选出来以计算改进的相同策略应用到 UCB 上,我们需要将 UCB 收购得分重新构建为正态分布的平均值。
UCB 政策的数学细节
在 5.2.2 节中,我们讨论了 UCB 收购分数为μ + βσ。在这里,术语μ和σ是f(x)的预测均值和标准差,β是一个可调参数,用于权衡勘探和开发。我们现在需要将μ + βσ 重写为正态分布N(μ, σ²)上某个数量的平均值,以扩展 UCB 到批处理设置。虽然可以进行这种重塑,但我们不在此处讨论数学。感兴趣的读者可以参考本文附录 A(arxiv.org/pdf/1712.00424.pdf),其中详细介绍了数学细节。
将 UCB 扩展到批处理设置的其余部分遵循相同的流程:
-
我们取被重写的数量μ + βσ 在整个批次中的最大值的平均值,并将其用作批次 UCB 收购得分。
-
然后,我们使用辅助函数
optimize_acqf()找到给出最高分数的批次。
这就是我们需要了解如何将这三种 BayesOpt 策略扩展到批量设置的全部内容。我们将在下一节中学习如何在 BoTorch 中实现这些策略。
7.2.2 实施批量改进和 UCB 策略
与我们在第 4 至 6 章中看到的情况类似,BoTorch 使得在 Python 中实现和使用 BayesOpt 策略变得简单,并且前一节讨论的三种策略(PoI、EI 和 UCB)的批量变体也不例外。虽然我们需要了解这三种策略的数学公式,但我们将看到,使用 BoTorch,我们只需在我们的 Python 程序中替换一行代码就可以运行这些策略。本节中使用的代码可以在名为 CH07/01 - Batch BayesOpt loop.ipynb 的 Jupyter 笔记本中找到。
在新的设置下,我们现在将查询目标函数的操作批量执行,您可能会认为我们需要修改实现 BayesOpt 循环的代码(同时获取多个函数评估值,将多个点附加到训练集,训练 GP 模型)。然而,由于 BoTorch 能够无缝支持批处理模式,所需的修改很小。特别是,在使用辅助函数 optimize_acqf() 来找到最大化获取分数的下一个查询时,我们只需要指定参数 q = k 为批量大小(即可以并行运行的函数评估的数量)。
图 7.12 显示了批量 BayesOpt 循环的步骤及相应的代码。与顺序设置相比,当转移到批处理设置时,我们的代码需要最少的修改。
整个批量 BayesOpt 循环总结在图 7.12 中,它与图 4.4 非常相似。对少量更改进行了注释:
-
当使用辅助函数
optimize_acqf()时,我们指定q=k为批量大小 k。 -
此辅助函数返回包含 k 个点的
next_x。变量next_x是一个 k-by-d PyTorch 张量,其中 d 是我们搜索空间中的维数(即数据集中的特征数)。 -
然后,我们在
next_x指定的位置查询目标函数,并获得包含函数评估值的next_y。与顺序设置不同,这里的next_y是一个包含 k 个元素的张量,对应于next_x的函数评估。
注意:在图 7.12 的第 1 步中,我们仍然需要一个 GP 模型的类实现和辅助函数 fit_gp_model(),该函数对训练数据进行训练。幸运的是,在顺序设置中使用的相同代码可以在不做任何修改的情况下重用。有关此代码的完整讨论,请参阅第 4.1.1 节。
为了方便我们的代码演示,我们使用了一个二维合成目标函数来模拟超参数调整应用程序的模型准确性。该函数首次出现在第三章的练习中,并且被实现如下,我们指定函数域,即我们的搜索空间,在两个维度的每一个上都在 0 和 2 之间:
def f(x): ❶
return ( ❶
torch.sin(5 * x[..., 0] / 2 - 2.5) * torch
➥.cos(2.5 - 5 * x[..., 1]) ❶
+ (5 * x[..., 1] / 2 + 0.5) ** 2 / 10 ❶
) / 5 + 0.2 ❶
lb = 0 ❷
ub = 2 ❷
bounds = torch.tensor([[lb, lb], [ub, ub]], dtype=torch.float) ❷
❶ 函数定义。
❷ 函数域,每个维度在 0 和 2 之间。
此目标函数在图 7.13 中可视化,我们可以看到全局最优点位于空间的右上角附近,给出的准确性为 90%。
图 7.13 SVM 模型在测试数据集上的准确性,作为惩罚参数 C 和 RBF 核参数 γ 的函数。这是我们在本章中要优化的目标函数。
要设置我们的批量优化问题,我们假设我们可以同时在四个不同的进程中训练模型。换句话说,我们的批次大小是 4。此外,我们只能重新训练模型五次,因此我们批处理 BayesOpt 循环的迭代次数为 5,我们可以进行的总查询次数为 4 × 5 = 20:
num_queries = 20
batch_size = 4
num_iters = num_queries // batch_size ❶
❶ 此变量等于 5。
现在,唯一要做的就是运行批处理 BayesOpt 策略。我们使用以下代码来完成此操作,首先在搜索空间中随机选择一个点作为训练集:
torch.manual_seed(0)
train_x = bounds[0] + (bounds[1] - bounds[0]) * torch.rand(1, 2) ❶
train_y = f(train_x) ❷
❶ 在搜索空间中随机选择一个点。
❷ 在随机选择的点处评估目标函数。
然后,我们对五个迭代中的每一个执行以下操作:
-
记录迄今为止见过的最佳准确性。
-
使用当前训练集重新训练 GP 模型。
-
初始化一个批处理 BayesOpt 策略。
-
使用辅助函数
optimize_acqf()找到最佳的查询批次。 -
在由查询批次指定的位置评估目标函数。
-
将新的观察结果附加到训练集并重复:
incumbents = torch.zeros(num_iters)
for i in tqdm(range(num_iters)):
incumbents[i] = train_y.max() ❶
model, likelihood = fit_gp_model(train_x, train_y) ❷
policy = ... ❸
next_x, acq_val = botorch.optim.optimize_acqf( ❹
policy,
bounds=bounds,
q=batch_size, ❺
num_restarts=40,
raw_samples=100,
)
next_y = f(next_x) ❻
train_x = torch.cat([train_x, next_x]) ❼
train_y = torch.cat([train_y, next_y]) ❼
❶ 跟踪优化进展。
❷ 对当前训练集进行 GP 训练。
❸ 初始化一个即将讨论的批处理 BayesOpt 策略。
❹ 找到下一个要查询的批次。
❺ 将参数 q 设置为批处理大小。
❻ 在所选批次上评估目标函数。
❼ 更新训练数据。
再次,此代码几乎与我们在第四章的第 4.1.1 节中使用的代码相同,该代码实现了 BayesOpt 的顺序设置。我们需要注意的是将辅助函数 optimize_acqf() 的参数 q 设置为正确的批量大小。
要运行批处理 BayesOpt 策略,我们使用 BoTorch 的类实现该策略进行初始化。对于 PoI 策略,我们使用
policy = botorch.acquisition.monte_carlo.qProbabilityOfImprovement(
model, best_f=train_y.max()
)
同样地,对于 EI 策略,我们使用
policy = botorch.acquisition.monte_carlo.qExpectedImprovement(
model, best_f=train_y.max()
)
注意类名前面的 q,它表示这些类实现了批处理 BayesOpt 策略。类似于顺序 PoI 和 EI 所采用的参数 best_f,这里的参数 best_f 指定了当前的现任值,我们将其设置为 train_y.max()。
对于 UCB,我们使用等效的 API,其中参数 beta 设置收获分数 μ + βσ 中的权衡参数 β,其中 μ 和 σ 是给定点处预测的均值和标准差:
policy = botorch.acquisition.monte_carlo.qUpperConfidenceBound(
model, beta=2
)
参数 BayesOpt 策略所需
我们在相应的章节 4.2.2、4.3 和 5.2.3 中了解了顺序 POI、EI 和 UCB 的实现。这些策略的每个参数在其批次对应策略中是相同的,这使得在 BoTorch 中过渡到批次设置变得简单。
由于我们现在可以运行 PoI、EI 和 UCB 的批次版本,让我们花一点时间来检查这些策略的行为。特别是,假设我们当前的 BayesOpt 进展与图 7.3 中的一维目标函数相同。该图还显示了底部面板中 EI 计算的单点收获分数。我们感兴趣的是看看 EI 对两个点的批次的收获分数是什么样的——也就是说,对给定一对查询的现任者的预期改进。
我们在图 7.14 中用热图展示这些收获分数,其中方格上每个位置的亮度表示给定一对查询的预期改进,给出最高收获分数的位置标有星号。(热图的横纵坐标显示了观察到的数据和热图轴上目标函数的当前 GP 信念。)我们观察到一些有趣的趋势:
-
热图上有两条直线带,表示高收获分数。这些带接近数据点 x = 2,意味着任何一个接近 x = 2 的查询批次(大小为 2)都会获得高分。这是有道理的,因为在 x = 2 附近是 GP 的后验均值最大化的地方。
-
热图的对角线很暗,意味着查询批次 x[1] 和 x[2],其中 x[1] 大致等于 x[2],很可能会产生低改进。这一观察验证了我们在第 7.1.2 节中所说的内容:选择在一起聚集的查询批次是一种不好的策略,本质上是把所有的蛋都放在一个篮子里。
-
最后,由星号标出的两个最佳查询批次是相同的批次,因为位置相对于彼此对称。该批次包含 1.68 和 2.12,仍在 x = 2 的邻域内,GP 告诉我们目标函数在这里产生高值。此外,所选的两个查询 1.68 和 2.12 相距甚远,因此帮助我们摆脱了将查询聚集在一起的陷阱。
图 7.14 显示了一个热图,显示了一维目标函数的批处理 EI 策略的收获分数,批处理大小为 2。顶部和右侧面板显示了热图的轴上观察到的数据以及目标函数的当前 GP 信念。两个最优查询对,表示为两个星星,包含 1.68 和 2.12,它们彼此之间相对较远。
图 7.14 显示,批处理 EI 的批次版本以合理的方式评估给定的一批查询,优先考虑那些可能产生高目标值且足够分散的批次。
批处理与顺序 EI
有趣的是,批处理 EI 选择的两个点,1.68 和 2.12,与最大化顺序 EI 收获分数的点 1.75 不同。顺序设置中的最佳决策与批处理设置中的最佳决策不一定相同,这种差异展示了。
回到我们的超参数调整示例,我们准备使用这些初始化来运行批处理策略。在保存每个策略实现的运行现任值并将它们相互绘制后,我们可以生成图 7.15,该图显示了我们示例中每个策略所做的优化进展。首先我们观察到,这个进展是以四个一批进行绘制的,这是有道理的,因为我们使用的批处理大小为 4。在性能方面,我们看到 EI 和 UCB 能够在开始时比 PoI 更快地取得进展,但三者最终收敛到大致相同的准确性。
图 7.15 显示了在超参数调整示例中各种批处理 BayesOpt 策略所取得的进展。进展以四个一批进行,这是使用的批处理大小。
贝叶斯优化中的重复实验
要准确比较这些策略在超参数调整应用程序中的性能,我们需要使用随机生成的不同初始训练集重复此实验。请参阅第四章练习 2 的第 9 步,了解如何在 BayesOpt 中运行重复实验。
我们现在已经学会了如何在 BoTorch 中实现 PoI、EI 和 UCB 的批处理版本,并且已经看到从顺序到批处理设置的过渡需要对我们的代码进行最少的修改。现在让我们转向剩下的 BayesOpt 策略,TS 和 MES,它们需要不同的策略才能扩展到批处理设置。
7.3 练习 1:通过重新抽样将 TS 扩展到批处理设置
与其他贝叶斯优化策略不同,汤普森抽样(TS)由于其抽样策略的原因,可以很容易地扩展到批处理设置中。我们将在这个练习中探讨这个扩展是如何实现的。请记住,在顺序设置中,TS 会从当前高斯过程(GP)对目标函数的信念中抽取一个样本,并查询最大化该样本的数据点,正如我们在第 5.3 节中所学的那样。
在批量设置中,我们只需重复从 GP 中抽样并多次最大化样本,以组装出所需大小的一批查询。例如,如果我们的批量 BayesOpt 问题的批量大小为 3,则我们从 GP 中抽取三个样本,并且我们最终得到的查询批包含了这三个样本的极大值(每个样本一个极大值)。这一逻辑在图 7.16 中有所说明,在该图中我们不断从 GP 中抽样并将最新样本的极大点添加到运行批次中,直到批次满为止——也就是说,直到达到适当的批量大小。
图 7.16 批量 TS 实现的流程图。我们不断从 GP 中抽样并将最新样本的极大点添加到运行批次中,直到批次满为止。
每次我们从 GP 中抽样,我们都得到目标函数可能的不同实现。通过优化从 GP 中抽取的多个样本,我们有一种简单的方法来选择多个可能引导我们到达目标函数全局最优解的点。为了在超参数调整示例中实现和运行此策略,我们采取如下步骤,这些步骤在 CH07/02 - 练习 1.ipynb 笔记本中实现:
-
重新在 CH07/01 - 批量 BayesOpt 循环.ipynb 中重现批量 BayesOpt 循环。
-
按照第 5.3 节中的描述,实现带有 Sobol 抽样器的 TS:
-
使用 2,000 个候选点进行 Sobol 抽样。
-
在调用 TS 对象时,请指定样本数等于批量大小:
ts = botorch.generation.MaxPosteriorSampling(model, replacement=False) next_x = ts(candidate_x, num_samples=batch_size) -
-
在超参数调整目标函数上运行此 TS 策略,并观察其性能。
Sobol 序列
Sobol 抽样器生成 Sobol 序列,该序列可以比均匀抽样序列更好地覆盖空间。关于 Sobol 序列的更多讨论可以在第 5.3.2 节中找到。
7.4 使用信息论计算一批点的值
现在我们学习如何将我们工具包中的最终 BayesOpt 策略扩展到批量设置中,即最大值熵搜索(MES)。与基于改进和老虎机的策略不同,MES 在批量设置中需要更加谨慎的考虑才能有效运行。我们将在下一节中讨论 MES 的批量版本以及在将其扩展到批量设置时遇到的问题,最后讨论如何在 BoTorch 中实现该策略。
注意 MES 是第六章的主题,在该章中我们学习有关信息论的基础知识以及如何使用 BoTorch 实现 MES 策略。
7.4.1 使用循环精化找到最具信息量的一批点
在顺序设置中,MES 根据我们在查询候选点后将获得的关于目标函数最大值 f 的信息量来评估每个候选查询的分数。候选点提供的关于最大值的信息量越多,该点引导我们朝向目标函数的全局最优解 x 的可能性就越大。
我们希望在批处理设置中使用相同的策略。也就是说,我们想要计算在查询候选点批处理之后我们将获得多少关于最大目标值f的信息。一批点的信息论价值是一个明确定义的数学数量,我们可以在理论上计算和使用它作为批处理设置中的采集得分。但是,在实践中计算这个信息理论数量是非常昂贵的。
主要的瓶颈在于我们必须考虑批处理中所有可能的函数评估,以知道我们将获得有关f的信息量有多少。尽管这些函数评估遵循多元高斯分布,这提供了许多数学上的便利,但计算* f 的信息增益是高斯性无法简化的任务之一。这种计算成本意味着,尽管我们可以计算一批点的采集分数,但这种计算成本昂贵而且不易优化。也就是说,找到最大化f*信息的批次点是非常困难的。
注意 查找最大化采集得分的查询是通过 L-BFGS 完成的, L-BFGS 是一种拟牛顿优化方法,通常比梯度下降更好,在辅助函数optimize_acqf()中完成。但是,由于信息理论采集分数的批处理版本计算方式,既不是 L-BFGS 也不是梯度下降能够有效地优化该得分。
BayesOpt 中的采集得分
请记住,采集得分量化了查询或一批查询的价值,以帮助我们找到目标函数的全局最优解,因此在 BayesOpt 循环的每次迭代中,我们需要识别最大化采集得分的查询或查询批次。请查看第 4.1 节以讨论最大化采集得分。
如果我们通常用来根据信息理论找到下一个最佳查询的方法 L-BFGS,在顺序设置中仅适用于一个候选点,那么我们如何在批设置中仍然使用它?我们的策略是以循环方式,一次一个成员地使用该方法找到批次的各个成员,直到收敛。具体而言,我们执行以下操作:
-
我们从起始批次x[1],x[2],...,*x[k]*开始。这个批次可以从搜索空间随机选择。
-
由于 L-BFGS 无法同时运行x[1],x[2],...,x[k]的所有成员,因此我们仅在固定批处理的其他成员x[2],x[3],...,x[k]时在x[1]上运行它。 L-BFGS 确实可以单独优化x[1],因为这个任务类似于在顺序设置中最大化采集得分。
-
一旦 L-BFGS 返回x[1]的值,我们在固定x[1]和其他成员x[3],x[4],...,x[k]的情况下运行 L-BFGS 在x[2]上。
-
我们重复这些单独的例程,直到我们完成处理批处理的最后一个成员x[k],此时我们返回到x[1]并重复整个过程。
-
我们运行这些优化循环直到收敛,即,直到我们获得的收获分数不再增加为止。这些步骤总结在图 7.17 中。
图 7.17 循环优化流程图,用于找到最大化批处理 MES 中最大目标值信息的批处理。该过程是循环的,因为我们按顺序在循环中逐步完善批处理的每个成员,直到收敛于良好的收获分数。
定义 整个过程称为cyclic optimization,因为我们按顺序在循环中逐步完善批处理的每个成员,直到收敛于良好的收获分数。
循环优化策略使我们能够避开在多个点的批处理上运行 L-BFGS 的挑战,相反,我们只在单个点上运行 L-BFGS,对收获分数进行个别的完善。借助此优化策略,我们可以在批处理设置中实现 MES 策略。
注意 我们可以将循环优化与艺术家绘画的方式进行类比。艺术家可能会分别处理绘画的各个部分,并随着进展而切换。他们可能会先处理前景,然后暂时转向背景,然后再回到前景,每次对每个部分进行小幅改进。
7.4.2 使用 BoTorch 实现批量熵搜索
现在我们学习如何在 BoTorch 中声明批量 MES 策略,并将其连接到我们的批处理 BayesOpt 循环中。幸运的是,前一节讨论的循环优化细节被 BoTorch 抽象化了,我们可以以直观的方式初始化批量 MES。以下代码包含在 CH07/03 - Max-value Entropy Search.ipynb 笔记本中。
我们仍然使用超参数调整示例。首先,我们需要对我们的 GP 模型进行一些微小修改。具体来说,为了推理后验 GP 的熵(即“幻想”未来观察结果),我们的 GP 模型的类实现需要从botorch.models.model模块中继承FantasizeMixin类:
class GPModel(
gpytorch.models.ExactGP,
botorch.models.gpytorch.GPyTorchModel,
botorch.models.model.FantasizeMixin ❶
):
_num_outputs = 1
... ❷
❶ 从 FantasizeMixin 继承使我们能够更有效地推理后验 GP。
❷ 其余代码保持不变。
此类实现的其余代码保持不变。现在,在实现 BayesOpt 的for循环内部,我们以与顺序设置相同的方式声明 MES:
-
我们从 Sobol 序列中抽取样本,并将它们用作 MES 策略的候选集。这些样本最初在单位立方体内抽取,然后调整大小以跨越我们的搜索空间。
-
MES 策略使用 GP 模型和先前生成的候选集初始化:
num_candidates = 2000
sobol = torch.quasirandom.SobolEngine(2, scramble=True) ❶
candidate_x = sobol.draw(num_candidates)
candidate_x = (bounds[1] - bounds[0]) * candidate_x +
➥bounds[0] ❷
policy = botorch.acquisition.max_value_entropy_search.qMaxValueEntropy(
model, candidate_x
)
❶ 我们的搜索空间是二维的。
❷ 调整候选集的大小以跨越搜索空间
Sobol 序列
Sobol 序列首次在第 5.3.2 节中讨论了 TS 策略。 MES 策略的实现还需要 Sobol 序列,我们在第 6.2.2 节中了解到了它。
虽然批量 MES 策略的初始化与我们在顺序设置中所做的完全相同,但我们需要一个辅助函数来替代optimize_acqf(),以便进行前一节中描述的循环优化过程,以识别最大化关于f的后验信息的批次。
具体来说,我们使用辅助函数optimize_acqf_cyclic(),可以从相同的 BoTorch 模块botorch.optim中访问。在这里,我们只需将optimize_acqf()替换为optimize_acqf_cyclic();其余的参数,例如边界和批量大小,保持不变:
next_x, acq_val = botorch.optim.optimize_acqf_cyclic(
policy,
bounds=bounds,
q=batch_size,
num_restarts=40,
raw_samples=100,
)
BoTorch 维度警告
在运行批量 MES 的代码时,您可能会遇到警告:
BotorchTensorDimensionWarning:
Non-strict enforcement of botorch tensor conventions. Ensure that target
tensors Y has an explicit output dimension.
此警告表示,我们没有根据 BoTorch 的约定格式化包含观察值 train_y 的张量。但是,这不是一个导致代码错误的错误,因此为了能够继续使用与其他策略相同的 GP 实现,我们简单地使用warnings模块忽略此警告。
由于其算法复杂性,批量 MES 策略可能需要相当长的时间来运行。可以跳过运行优化循环的代码部分并继续进行章节。
有了这些,我们现在准备在我们的超参数调整示例中运行批量 MES。使用相同的初始训练数据,批量 MES 的进展在图 7.18 中可视化,该图显示该策略与本次运行中的其他策略相当。
图 7.18:超参数调整示例中各种批量 BayesOpt 策略的进展,包括 MES
我们现在已经学会了将 BayesOpt 策略转换为批量设置,在该设置中,可以并行进行多个查询。根据策略的不同,此转换需要考虑各种不同的级别。对于基于改进的策略和 UCB,我们使用这样一个启发式方法:表现最好的成员应该代表整个批次。在练习 1 中,我们看到 TS 可以通过简单地重复抽样过程来扩展到批量设置,以组装所需大小的批次。另一方面,MES 需要一个修改后的例程,该例程使用循环优化来搜索最大化其收购得分的批次。在下一章中,我们将学习另一种专门的 BayesOpt 设置,在该设置中,在优化目标函数时需要考虑约束。
7.5 练习 2:优化飞机设计
在这个练习中,我们在物理学中的一个模拟优化问题上运行了本章中探讨的批处理贝叶斯优化策略。这个问题是我们遇到的维度最高的问题,将为我们提供一个机会观察贝叶斯优化如何处理一个高维度的通用黑盒优化问题。更具体地说,我们将看到各种批处理贝叶斯优化策略在一个真实世界优化问题上的表现。
我们对飞机工程师常常处理的一种气动结构优化问题感兴趣。在这种优化问题中,我们有各种可调参数(每个参数都构成了我们搜索空间中的一个维度),这些参数控制着飞机的工作方式。这些参数可能是飞机的长度和宽度,翼与机身的形状和角度,或者涡轮叶片的角度和旋转速度。优化工程师的工作是调整这些参数的值,使飞机正常运行或优化某些性能指标,如速度或能源效率。
尽管工程师们可能对某些变量如何影响飞机性能有一定了解,但测试一个实验飞机设计的好方法是运行各种计算机模拟并观察飞机的模拟行为。通过这些模拟,我们根据飞机在各种性能指标上的表现来评分。有了模拟程序,我们可以将这个调整过程视为一个黑盒优化问题。也就是说,我们不知道每个可调参数如何影响模拟飞机的最终性能,但我们希望优化这些参数以获得最佳结果。
这个练习提供了一个模拟飞机设计性能基准测试过程的目标函数。代码在 CH07/04 - Exercise 2.ipynb 笔记本中提供。有多个步骤:
-
实现模拟性能基准测试的目标函数。这是一个四参数函数,其代码如下,用于计算以四个输入参数指定的飞机的效用的分数。由于我们将这个函数视为黑盒,我们假设我们不知道函数内部的运行方式和输出是如何产生的:
def flight_utility(X): X_copy = X.detach().clone() X_copy[:, [2, 3]] = 1 - X_copy[:, [2, 3]] X_copy = X_copy * 10 - 5 return -0.005 * (X_copy**4 - 16 * X_copy**2 + 5 * X_copy).sum(dim=-1) + 3四个参数是飞机的各种设置,缩放到 0 和 1 之间。也就是说,我们的搜索空间是四维单位超立方体。虽然这对我们的黑盒优化方法并不重要,但这些参数的名称如下:
labels = [ "scaled body length", "scaled wing span", "scaled ρ", "scaled ω" ]虽然很难可视化一个完整的四维函数,但我们可以展示这个函数在二维空间中的行为。图 7.19 展示了我们的目标函数在我们可以调整的各种参数对中的行为,显示了这些二维空间中的复杂非线性趋势。
图 7.19 在不同的二维子空间中,虚拟飞机设计优化问题的目标函数对应于可调参数对,显示为轴标签。明亮的点表示高目标值,即我们的优化目标;黑暗的点表示低目标值。
再次强调,我们的目标是使用 BayesOpt 找到该函数的最大值。
-
使用一个具有恒定均值函数和 Matérn 2.5 核函数的 GP 模型,输出尺度为一个
gpytorch.kernels.ScaleKernel对象:- 在初始化核函数时,我们需要指定参数
ard_num_dims=4,以考虑到我们的目标函数是四维的。
注意:在第 3.4.2 节以及第三章练习中,我们学习了如何使用 Matérn 核函数。
- 在初始化核函数时,我们需要指定参数
-
实现一个辅助函数,该函数在给定的训练数据集上训练 GP。该函数应该接收一个训练集,在使用梯度下降最小化负对数似然的同时训练 GP,并返回该 GP 及其似然函数。有关如何实现这个辅助函数的刷新,请参见第 4.1.1 节。
-
定义我们优化问题的设置:
-
搜索空间是四维单位超立方体,因此我们应该有一个名为
bounds的变量,其中存储以下张量:tensor([[0., 0., 0., 0.], [1., 1., 1., 1.]])我们将这些边界传递给我们在本练习后面运行的 BayesOpt 策略。
-
在每次运行中,BayesOpt 策略可以在总共 100 次查询目标函数(即 100 次函数评估)中进行,每次批量为 5 次。我们还对每个策略重复实验五次。
-
-
在刚刚实现的目标函数上运行本章学习到的每个批次的 BayesOpt 策略:
-
每个实验应该以一个随机选择的函数评估作为训练集开始。
-
记录在搜索中找到的最佳值。
-
使用一个 5,000 点的 Sobol 序列进行 TS 和 MES。
-
在高维问题中运行 MES 计算代价很高。缓解这一负担的常见策略是限制循环优化的次数。例如,要在五个周期后终止 MES 采集分数的优化,我们可以将
cyclic_options={"maxiter":5}传递给辅助函数optimize_acqf_cyclic()。在实验中运行这个更轻量化的 MES 版本。
-
-
绘制我们运行过的 BayesOpt 策略的优化进程,并观察它们的性能。每个策略应该有一条曲线,显示作为查询次数的函数的平均最佳观测点及其标准误差。有关如何进行这种可视化的更多详细信息,请参见第四章练习 2 的最后一步。
总结
-
在现实世界中,许多黑盒优化设置允许多个实验(函数评估)同时并行进行。通过利用这种并行性,我们可以在 BayesOpt 中进行更多的实验,并可能获得更好的性能。
-
在批处理 BayesOpt 设置的每次迭代中,会选择一批查询,并在这些查询上评估目标函数。这种设置要求所使用的 BayesOpt 策略能够根据查询在帮助我们定位全局最优解方面的效用来评分一批查询。
-
将 BayesOpt 策略扩展到批处理设置并不像在顺序设置中选择得分最高的顶部数据点那样简单。这样做会导致所选查询之间的距离非常接近,从而违背了并行性的目的。
-
三种 BayesOpt 策略——PoI、EI 和 UCB——可以使用相同的策略扩展到批处理设置。该策略使用批次查询中的最大值来量化整个批次的价值。从数学上讲,使用最大值来代表整个批次的策略需要将收益分数重写为某种感兴趣数量的平均值。
-
由于其随机性质,TS 策略可以很容易地扩展到批处理设置中。批处理 TS 不是仅从 GP 中抽样并仅最大化一次样本,而是重复这个抽样和最大化过程,直到达到目标批次大小。
-
计算多个点的信息论价值在计算上是具有挑战性的。这一困难阻碍了助手函数
optimize_acqf()所使用的算法 L-BFGS 在批处理设置中与 MES 策略一起找到最大化给定策略的收益分数的点或批次的使用。 -
为了避免使用 L-BFGS 与批处理 MES 的计算挑战,我们使用循环优化。这种策略涉及以循环方式优化我们当前查询批次中的各个成员,直到收益分数收敛。在 BoTorch 中,可以使用助手函数
optimize_acqf_cyclic()来使用循环优化。 -
为了最大化我们的优化吞吐量,在使用助手函数
optimize_acqf()和optimize_acqf_cyclic()搜索最大化给定策略的收益分数的批次时,设置正确的批次大小非常重要。我们通过将参数q设置为所需的批次大小来做到这一点。 -
大多数 BayesOpt 策略的 BoTorch 实现都遵循与顺序设置中的实现相同的接口。这种一致性使程序员可以在不需要显着修改其代码的情况下转换到批处理设置。
第九章:满足额外约束的满意条件优化
本章包括
-
带约束的黑盒优化问题
-
在 BayesOpt 中考虑约束时做出决策
-
实施考虑约束的 BayesOpt 策略
在前几章中,我们解决了黑盒优化问题,其中我们仅旨在最大化客观函数,没有其他考虑因素。这被称为无约束优化问题,因为我们可以自由地探索搜索空间以寻找客观函数的全局最优解。然而,许多现实情况并不遵循这种无约束的制定,客观函数的全局最优解可能存在成本,使实践中无法实现这种最优解。
例如,当调整神经网络的架构时,您可能会发现增加网络层数通常会产生更高的准确性,并且拥有数百万和数十亿层的网络将表现最佳。然而,除非我们有昂贵的、强大的计算资源,否则运行这样的大型神经网络是不切实际的。也就是说,在这种超参数调整任务中,运行大型神经网络会有成本,这在实践中可能对应于客观函数的全局最优解。因此,在调整此神经网络时,我们需要考虑这种计算成本,并且只寻找实际可实现的架构。
我们需要考虑额外约束的另一个黑盒优化问题是科学发现,例如在化学和材料科学中。例如,科学家的目标是设计出优化所需特性的化学品和材料,比如对抗疾病有效的药物,抵御压力的玻璃,或者易于操作的可塑金属。不幸的是,对抗疾病最有效的药物可能会有许多副作用,使其使用起来有危险,或者最具韧性的玻璃可能在大规模生产上成本过高。
这些都是受限优化问题的示例,我们需要在满足其他约束的同时优化客观函数。仅寻求优化客观函数可能会导致我们找到的解决方案违反重要约束,使我们找到的解决方案在实践中无用。相反,我们需要识别搜索空间中的其他区域,这些区域既能产生高客观值,又能满足这些重要约束。
在本章中,我们了解约束优化问题,并看到在某些情况下,额外的约束可能会完全改变优化问题的解。考虑到这些约束的需要引发了 BayesOpt 中的约束感知优化策略。我们介绍了一种考虑约束的预期改进(EI)策略的变体,并学习了如何在 BoTorch 中实现它。到本章结束时,您将了解约束优化问题,学习如何使用 BayesOpt 解决它,并看到我们使用的约束感知策略要比不考虑约束的策略表现得更好。本章所学将帮助我们在现实生活中解决更多实际的 BayesOpt 问题,并因此做出更有效的决策。
8.1 在约束优化问题中考虑约束
正如介绍中提到的,现实世界中存在许多约束优化问题:制造具有高效性和最小副作用的药物,寻找最大化理想特性并且廉价生产的材料,或者在保持计算成本低的同时进行超参数调整。
注意 我们关注不等式约束,其中我们要求结果y在预定的数值范围a ≤ y ≤ b内。
我们首先在接下来的一节中更仔细地看看约束优化问题,并了解为什么它在数学上与我们在前几章中看到的无约束问题不同。然后,我们重新定义了迄今为止一直使用的 BayesOpt 框架以考虑额外的约束条件。
8.1.1 约束条件可能改变优化问题的解
约束如何使黑盒函数的优化变得复杂?在许多情况下,搜索空间内部给出高目标值的区域可能会违反随优化问题而来的约束条件。
注意 在优化问题中,我们的目标是找到给出高目标值的区域,因为我们想要最大化目标函数的值。
如果高目标值区域违反给定的约束条件,我们需要排除这些违反约束的区域,并仅在满足约束的其他区域内进行搜索。
违反预定义约束条件的数据点在约束优化问题中被称为不可行点,因为将该点作为优化问题的解是不可行的。另一方面,满足约束条件的数据点被称为可行点。我们的目标是找到最大化目标函数值的可行点。
约束条件可能会影响无约束优化问题的最优解质量,或者完全改变最优解。考虑图 8.1 中的示例,我们的目标函数(实线)是我们在前几章中使用过的福雷斯特函数。除了这个目标函数之外,我们还有一个成本函数,如虚线所示。假设在这个约束优化问题中,约束是成本需要最大为零——也就是说,成本 c ≤ 0. 这个约束意味着只有图 8.1 右侧面板中阴影区域内的可行点可以作为优化结果使用。
图 8.1 一维约束优化问题的示例。实线是我们希望最大化的目标函数,虚线是约束优化问题的成本函数。只有产生负成本的阴影区域(右侧面板)是可行的。在这里,非正成本的约束导致最高目标值从超过 8 减少到 4 左右。
注意:我们在 BayesOpt 的 2.4.1 节中首次使用了福雷斯特函数作为示例目标函数。
因此,包含 x > 4 的区域,其中包含目标值的真实全局最优解(在右侧面板中用钻石标记)被切断。也就是说,产生目标值超过 8 的全局最优解是不可行的,而约束最优解(用星号标记)只能达到大约 4 的目标值。这种“截断”情况的一个例子是当有效药物有太严重的副作用时,药品公司决定使用同一化学成分的效果较差的变体来使产品安全。
另一个具有相同目标函数但成本函数略有不同的示例显示在图 8.2 中,这个额外的成本约束改变了我们优化问题的最优解。在没有约束的情况下,目标函数的全局最优解位于 x = 4.6. 然而,这个点是一个不可行的点,会产生正成本,因此违反了我们的约束。约束问题的最优解在 x = 1.6. 这种现象可能会发生,例如当某种高效药物的整个家族对患者有危险而不能生产时,因此我们需要寻找与危险药物化学成分不同的其他解决方案。
图 8.2 一维约束优化问题的示例。在这里,由于非正成本约束排除了 x > 3 的区域,最优解变为不同的局部最优解。
总的来说,不等式约束可能对优化问题施加复杂的要求,并改变其最优解。也就是说,约束可能排除函数的全局最优解作为不可行点——这在现实世界中是常见的情况:
-
成本过高的神经网络倾向于实现良好的预测性能,但在实践中无法实现。
-
最有效的药物通常太过激进和危险,无法生产。
-
最好的材料价格太高,无法使用。
我们需要修改我们的优化策略来考虑约束并找到最佳可行解,而不是使用违反我们约束的无约束最优点。也就是说,我们需要追求两个目标:优化目标函数并满足给定的约束。单纯优化目标函数而不考虑约束会导致无法使用的不可行解。相反,我们需要找到既产生高目标值又满足约束的点。
8.1.2 约束感知的贝叶斯优化框架
我们应该如何从贝叶斯优化的角度解决这个受约束的优化问题?在本节中,我们学习如何修改我们的贝叶斯优化框架以考虑在受约束优化问题中给定的约束。
在贝叶斯优化中,我们使用高斯过程(GP)来训练我们从目标函数观察到的数据点,并对未见数据进行预测。在受约束优化中,除了我们需要满足的一个或多个定义约束的函数之外,我们还有一个或多个定义约束的函数。例如,在第 8.1.1 节中,成本函数如图 8.1 和 8.2 中的虚线所示,定义了解决方案需要具有非正成本的约束。
注意你可以参考第 1.2.3 节和第 4.1.1 节来重新了解贝叶斯优化框架。
我们假设,就像目标函数一样,我们不知道真实的成本函数是什么样的。换句话说,成本函数是一个黑盒。我们只能观察到我们查询目标函数的数据点处的成本值,从那里,我们确定这些数据点是否满足约束。
注意如果我们知道定义约束的函数的样子,我们可以简单地确定可行区域,并将我们的搜索空间限制在这些可行区域内。在我们的受约束优化问题中,我们假设我们的约束也是黑盒。
由于我们只能黑盒访问定义约束的函数,我们还可以使用 GP 来模拟每个这些函数。也就是说,除了模拟我们目标函数的 GP 外,我们使用更多的 GP,每个函数定义一个约束,以指导我们下一步在哪里查询目标函数。我们遵循相同的程序来训练每个 GP——只是使用每个 GP 的适当训练集:
-
模拟目标函数的 GP 是在观察到的目标数值上进行训练。
-
模拟定义约束函数的 GP 是在观察到的成本数值上进行训练。
我们的受限贝叶斯优化框架,是图 1.6 的修改版本,在图 8.3 中进行了可视化:
-
在步骤 1 中,我们在来自目标函数的数据和定义约束函数的每个函数的数据上训练一个 GP。
-
在步骤 3 中,我们使用贝叶斯优化策略确定的点查询目标函数和定义约束函数。
图 8.3 的步骤 1 和 3 很容易实现:我们只需要同时维护多个 GP,跟踪相应的数据集,并保持这些数据集的更新。更有趣的问题出现在步骤 2 中:决策制定。也就是说,我们应该如何设计一个贝叶斯优化策略,以指导我们朝向产生高客观价值的可行区域?这是我们在下一节讨论的主题。
8.2 贝叶斯优化中的约束感知决策
一个有效的受限贝叶斯优化策略需要同时追求优化和满足约束条件。设计这样一个策略的一种简单方法是将约束条件纳入非受限贝叶斯优化策略做出决策的方式中。也就是说,我们希望修改一个我们已经知道的策略,以考虑受限制条件的约束优化问题,并得出一种约束感知的决策程序。
我们选择用于这种修改的策略是 EI,我们在第 4.3 节学到了它。(我们稍后在本节中讨论其他贝叶斯优化策略。)请记住,EI 策略将每个未见过的数据点的预期值得到评分,以表明如果我们在这个未见点查询目标函数,我们会观察到多少改进。
图 8.3 受限贝叶斯优化循环。一个单独的 GP 模型对目标函数或定义约束的函数进行建模。一个贝叶斯优化策略推荐下一个点,我们可以查询目标函数和定义约束的函数。
定义 在任职者 这个术语指的是我们训练集中具有最高客观价值的点,这是我们需要“超越”的点,以便在优化过程中取得进展。
EI 使用的收获得分,再次,计算每个潜在查询的改进的平均值,忽略约束优化问题中的不等式约束,因此在优化受约束的目标函数时我们不能直接使用 EI。幸运的是,有一种简单的方法来考虑这些约束:我们可以通过未见点满足约束的概率来缩放每个未见点的 EI 收购得分,即数据点是可行点的概率:
-
如果数据点可能满足约束条件,则其 EI 分数将乘以一个大数(可行性的高概率),从而保持 EI 分数较高。
-
如果数据点不太可能满足约束条件,那么它的 EI 得分将乘以一个较小的值(较低的可行性概率),从而降低该数据点的优先级。
提示 约束变种的 EI 获取得分是正常的 EI 得分和数据点满足约束条件的概率的乘积。
约束感知的 EI 获取得分的公式如图 8.4 所示。这个获取得分是两个术语的乘积:EI 得分鼓励优化目标函数,而可行性概率鼓励停留在可行区域内。正是这种在优化目标函数和满足约束条件之间平衡的方式,正如 8.1.1 节所述,我们希望实现的。
图 8.4 所示的公式是约束 EI 获取得分的公式,它是正常的 EI 得分和可行性概率的乘积。这个策略旨在同时优化目标值并满足约束条件。
我们已经知道如何计算 EI 得分,但是如何计算第二个术语-给定数据点是可行点的概率?正如图 8.4 所述,我们使用对约束进行建模的高斯过程完成此操作。具体而言,每个高斯过程提供有关定义约束函数形状的概率信念。从这种概率信念中,我们可以计算未见过数据点满足相应不等式约束的概率。
例如,当解决图 8.2 中定义的约束优化问题时,假设我们已经在x=0,x=3 和x=4 处观察到了目标函数和成本函数。从此培训集中,我们训练了一个用于目标函数和另一个用于成本函数的高斯过程,并获得了图 8.5 中可视化的预测结果。
图 8.5 所示的是相应高斯过程的目标函数和成本函数的预测。每个高斯过程都可以让我们以概率方式推理相应函数的形状。
现在,假设我们想计算x=1 的约束 EI 得分。我们已经找到了计算任何数据点的正常 EI 得分的方法,现在我们需要做的就是计算x=1 是可行数据点的概率。为了做到这一点,我们查看表示我们对x=1 成本值的预测的正态分布,如图 8.6 所示。
图 8.6 展示了x=1 是可行点的概率,用较深的颜色突出显示。左侧显示整个高斯过程,右侧则仅显示与x=1(误差条在两个面板中相同)预测相对应的正态分布。在此,可行性遵循一个被截断的正态分布。
图 8.6 的左面板包含与图 8.5 底部面板相同的 GP,它被截断在约束阈值 0,另外还显示了 x = 1 处正态分布预测的 CI。在这一点 x = 1 垂直切割 GP,我们得到图 8.6 的右面板,在这两个面板中的 CI 是相同的。换句话说,从图 8.6 的左面板到右面板,我们已经放大了垂直刻度,而不是显示成本函数,我们只保留了成本约束(虚线)和 x = 1 处的 GP 预测,它是一个正态分布。我们看到右面板中正态分布的突出部分表示 x = 1 遵守成本约束的概率,这是我们关心的内容。
如果图 8.6 让你想起了图 4.9 和 4.10,涵盖了 PoI 策略,那是因为我们在这两种情况下的思考过程是相同的:
-
对于 PoI,我们计算给定数据点产生的目标值高于现任的概率。因此,我们使用现任值作为下界,指定我们只关心目标值高于现任的情况。
-
通过可行性概率,我们计算给定数据点产生的成本值低于 0 的概率。我们使用 0 作为上界来指定我们只针对成本值低于阈值的情况(以遵守我们的成本约束)。
处理不同的不等式约束
在我们当前的示例中,约束要求成本低于 0。如果我们有一个要求函数值高于某个阈值的约束条件,那么可行性的概率将是给定点产生函数值高于某个阈值的概率,而图 8.6 中的阴影区域将位于截止线的右侧。
如果存在一个约束条件,要求数值在一个范围内(a ≤ y ≤ b),那么可行性的概率将是数据点给出一个在范围的下限和上限之间的值的概率。
在我们的情况下,我们想要计算 x = 1 处成本值低于 0 的概率,这是图 8.6 中右侧面板下曲线阴影区域的面积。正如我们在第 4.2.2 节的第四章中看到的,正态分布允许我们使用累积密度函数(CDF)计算曲线下面积。在图 8.6 中,x = 1 可行的概率大约为 84% —— 这是我们用于计算受约束 EI 采集分数的图 8.4 中的第二项。
此外,我们可以计算搜索空间内任何点的可行性概率。例如,图 8.7 显示了 x = –1(中心面板)和 x = 2(右侧面板)的截断正态分布。正如我们所见,给定点可行的概率取决于该点的预测正态分布:
-
在 x = –1 处,几乎整个预测正态分布都位于成本阈值 0 以下,因此这里的可行性概率很高,几乎为 98%。
-
在 x = 2 处,只有一小部分正态分布落在成本阈值以下,导致可行性概率较低,大约为 6%。
图 8.7 在 x = –1 和 x = 2 处突出显示的可行性概率,呈深色。左侧面板显示了整个 GP,中间面板显示了 x = –1 的预测,右侧面板显示了 x = 2 的预测。突出显示的部分显示了可行性概率,这取决于给定点的正态分布。
有了计算任意给定点可行性概率的能力,我们现在可以计算图 8.4 中描述的受约束 EI 采集分数。再次强调,此分数在潜在的高目标值(由常规 EI 分数量化)和满足不等式约束(由可行性概率量化)之间平衡。
图 8.8 在右下面板显示了此分数,以及常规 EI 分数和当前 GPs。我们看到受约束 EI 意识到我们需要满足的成本约束,并且对空间右侧(其中 x > 2)的区域分配了大约零分。这是因为成本 GP(右上面板)认为这是一个应该避免的不可行区域。最终,常规 EI 策略建议将具有不可行点 x = 4 作为下一个要查询的点。受约束 EI,另一方面,建议 x = –0.8,这确实满足了我们的成本约束。
图 8.8 EI 的采集分数(左下)和受约束 EI 的采集分数(右下),以及我们对目标函数(左上)和成本函数(右上)的当前信念。通过意识到成本约束,受约束 EI 可以避免不可行的区域,并建议从常规 EI 完全不同的点进行查询。
我们已经找到了从常规策略中推导出具有约束感知的 BayesOpt 策略的一个很好的启发式方法:将策略的收购分数与可行性概率相乘以考虑不等式约束。有趣的是,将可行性概率因子添加到 EI 中并不简单是一种启发式方法——图 8.4 中的公式可以从一个无启发式、更具数学严谨性的过程中得到。感兴趣的读者可以参考一篇定义了约束 EI 策略的研究论文以获取更多细节(proceedings.mlr.press/v32/gardner14.pdf)。
虽然我们可以将相同的启发式方法应用于我们已学习的其他 BayesOpt 策略,例如 UCB、TS 和 Entropy Search,但数学上严格的过程将不再适用。此外,在撰写本文时,BoTorch 仅支持受约束 EI,这也被广泛用于实践中解决受约束优化问题。因此,我们只关注受约束 EI 及其优化结果在本章的其余部分。
8.3 练习 1:手动计算受约束的 EI
我们在图 8.4 中看到,受约束 EI 策略的收购得分是 EI 得分和可行性概率的乘积。虽然 BoTorch 的ConstrainedExpectedImprovement类提供了受约束 EI 得分的实现,但实际上我们可以手动进行计算。在这个练习中,我们将探索这种手动计算,并将我们的结果与ConstrainedExpectedImprovement类的结果进行验证。此练习的解决方案在 CH08/02 - Exercise 1.ipynb 笔记本中:
-
重新创建在 CH08/01 - Constrained optimization.ipynb 中使用的约束 BayesOpt 问题,包括目标函数、成本函数、GP 实现以及训练 GP 的辅助函数
fit_gp_model()。 -
使用例如
torch.linspace()方法创建一个在-5 到 5 之间的密集网格的 PyTorch 张量。此张量将作为我们的测试集。 -
通过从我们的搜索空间(在-5 和 5 之间)随机抽样 3 个数据点,创建一个玩具训练数据集,并评估这些点的目标和成本函数。
-
使用辅助函数
fit_gp_model()在目标函数数据和成本函数数据上训练一个 GP。 -
使用从成本函数数据中训练的 GP 来计算测试集中每个点的可行性概率。您可以使用
torch.distributions的.Normal类来初始化一个正态分布对象,并在 0 上调用此对象的cdf()方法(实现为torch.zeros(1))来计算每个数据点产生低于 0 的成本的概率。 -
使用
model参数初始化一个常规 EI 策略,其中 GP 是由数据从目标函数训练的,并且best_f参数是当前可行的候选者:-
计算测试集中每个点的 EI 得分。
-
有关实现 EI 策略的更多详细信息,请参见 4.3 节。
-
-
初始化受约束 EI 策略,为测试集中的每个点计算受约束 EI 分数。
-
计算 EI 分数和可行性概率的乘积,并验证此手动计算是否导致与 BoTorch 实现相同的结果。您可以使用
torch.isclose(a,b,atol = 1e-3),它在两个张量a和b之间执行逐元素比较,指定atol = 1e-3以考虑数值不稳定性,以验证所有相应分数是否匹配。 -
在图表中绘制 EI 分数和受约束 EI 分数,并通过图 8.4 视觉验证前者始终大于或等于后者。
8.4 在 BoTorch 中实现受约束 EI
虽然我们可以手动乘以两个量,即 EI 分数和可行性概率,以生成新的收购分数,但 BoTorch 已经处理了低级别的簿记。这意味着我们可以从 BoTorch 中导入受约束 EI 策略,并像使用任何其他 BayesOpt 策略一样使用它,而没有太多开销。我们在本节中学习如何这样做,并且我们使用的代码已包含在 CH08/01-Constrained optimization.ipynb 笔记本中。
首先,我们需要实现图 8.2 中定义约束的目标函数和成本函数。在以下代码中,目标函数实现为objective(),成本函数为cost()。我们的搜索空间介于-5 和 5 之间,并且我们制作包含这些数字的变量bounds,将在以后传递给 BayesOpt 策略:
def objective(x): ❶
y = -((x + 1) ** 2) * torch.sin(2 * x + 2)
➥/ 5 + 1 + x / 3 ❶
return y ❶
def cost(x): ❷
return -(0.1 * objective(x) + objective(x - 4))
➥/ 3 + x / 3 - 0.5 ❷
lb = -5 ❸
ub = 5 ❸
bounds = torch.tensor([[lb], [ub]], dtype=torch.float) ❸
❶ 要最大化的目标函数
❷ 成本函数
❸ 搜索空间的边界
我们还需要 GP 模型的类实现和一个 helper 函数fit_ gp_model(),该函数训练给定训练数据集的 GP。由于受限制的优化不需要对 GP 以及我们如何训练它进行任何更改,因此我们可以重复使用之前章节中使用的类实现和助手函数。有关此实现的更深入讨论,请参见 4.1.1 节。
为了基准测试我们使用的策略的优化性能,我们指定每个 BayesOpt 运行具有 10 个查询,并且总共有 10 个运行:
num_queries = 10
num_repeats = 10
注意 我们会多次运行每个 BayesOpt 策略,以全面了解策略的表现。有关重复实验的讨论,请参见第四章练习 2。
最后,我们需要修改我们的 BayesOpt 循环以考虑图 8.3 中可视化的更改。在图 8.3 的第 1 步(即 BayesOpt 循环中的每个步骤开始时),我们需要重新训练多个 GP:一个用于目标函数,另一个(s)用于约束条件。
由于我们已经有了训练高斯过程的辅助函数fit_gp_model(),这一步只需要将适当的数据集传递给该辅助函数即可。在我们的当前例子中,我们只有一个定义约束的成本函数,所以总共有两个可以用以下代码重新训练的高斯过程:
utility_model, utility_likelihood = fit_gp_model( ❶
train_x, train_utility.squeeze(-1) ❶
) ❶
cost_model, cost_likelihood = fit_gp_model( ❷
train_x, train_cost.squeeze(-1) ❷
) ❷
❶ 在目标函数的数据上训练一个高斯过程(GP)。
❷ 在成本函数的数据上训练一个高斯过程(GP)。
在这里,变量train_x包含我们评估目标和成本函数的位置;train_utility是相应的目标值,train_cost是成本值。
图 8.3 的步骤 2 是指运行 BayesOpt 策略,我们很快就会学习到如何运行。图 8.3 的步骤 3 中,我们评估由所选 BayesOpt 策略推荐的数据点处的目标和成本函数,该数据点存储在变量next_x中。我们通过在next_x处评估目标和成本函数来完成这一点:
next_utility = objective(next_x) ❶
next_cost = cost(next_x) ❶
train_x = torch.cat([train_x, next_x]) ❷
train_utility = torch.cat([train_utility, next_utility]) ❷
train_cost = torch.cat([train_cost, next_cost]) ❷
❶ 在推荐点处评估目标和成本函数
❷ 更新各种数据集
我们还需要做一个额外的记账步骤,跟踪我们的优化进展。与无约束优化问题不同,在每个步骤中,我们只需记录最优解(迄今为止见到的最高目标值),而在这里,我们需要在取最大值之前过滤掉不可行的观察结果。我们首先创建一个张量,张量具有num_repeats行(每次重复运行一个)和num_queries列(每个时间步骤一个)。此张量默认只包含一个值,如果 BayesOpt 期间未找到可行点,则表示我们的效用:
检查图 8.2,我们可以看到在我们的搜索空间内(在-5 到 5 之间),我们的目标函数在任何地方都大于-2,所以我们将-2 作为默认值:
default_value = -2 ❶
feasible_incumbents = torch.ones((num_repeats, num_queries)) * default_value
❶ 检查是否找到可行点
然后,在每个 BayesOpt 循环的步骤中,我们只记录通过取筛选后观测结果的最大值找出的可行的最优解:
feasible_flag = (train_cost <= 0).any() ❶
if feasible_flag:
feasible_incumbents[trial, i] = train_utility[train_cost <= 0].max()
❶ 检查是否找到可行点
上述代码完成了我们的约束 BayesOpt 循环。我们只需要声明我们要用来解决约束优化问题的 BayesOpt 策略。我们使用在第 8.2 节中讨论的约束 EI 策略,使用 BoTorch 类ConstrainedExpectedImprovement。该类需要一些重要参数:
-
model-ModelListGP(utility_model,cost_model)-目的函数的 GP 模型列表(在我们的例子中是utility_model)和定义约束的函数(在我们的例子中是cost_model)。我们使用 BoTorch 的models模块中的model_list_gp_regression.ModelListGP类来创建此列表。 -
objective_index- model 列表model中模型目标函数的索引。由于utility_model是我们传递给ModelListGP的第一个 GP,所以在我们的例子中该索引为 0。 -
constraints— 将定义约束函数的每个函数的索引映射到存储约束的下限和上限的两元素列表的字典。如果一个约束没有下限或上限,我们使用None代替实际数值。我们的示例要求与cost_model对应的成本最大为 0,因此我们设置constraints={1:[None,0]}。 -
best_f— 当前可行的最佳解决方案,如果我们找到至少一个可行点,则为train_utility[train_cost <= 0].max(),否则为默认值 -2。
总的来说,我们如下初始化受约束 EI 策略:
policy = botorch.acquisition.analytic.ConstrainedExpectedImprovement(
model=botorch.models.model_list_gp_regression.ModelListGP( ❶
utility_model, cost_model ❶
), ❶
best_f=train_utility[train_cost <= 0].max(), ❷
objective_index=0, ❸
constraints={1: [None, 0]} ❹
)
❶ GP 模型列表
❷ 当前可行的最佳解决方案。
❸ 目标函数在模型列表中的索引
❹ 将每个约束的索引映射到下限和上限的字典。
现在我们手头有了受约束 EI 的实现,让我们在一维受约束优化问题上运行这个策略并观察其性能。作为基线,我们还可以运行不考虑约束的常规 EI 版本。
图 8.9 显示了这两种策略找到的平均可行最优解值以及时间函数的误差条。我们看到,与常规 EI 相比,约束变体平均找到更好的可行解,并且几乎总是收敛到最佳解。图 8.9 强调了我们的受约束优化策略相对于不考虑约束的方法的好处。
图 8.9 一维约束 EI 优化问题的优化进展。与常规 EI 相比,约束变体平均找到更好的可行解。
对于不考虑优化问题的约束的 EI 策略,往往会偏离不可行最优解。检查此策略找到的最佳解值,我们注意到在许多运行中,该策略未能从其初始值中取得进展:
torch.set_printoptions(precision=1)
print(ei_incumbents)
Output:
tensor([[ 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8],
[-2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0],
[ 2.2, 2.2, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7],
[ 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5],
[-2.0, 0.2, 1.9, 2.3, 2.6, 2.7, 2.7, 2.7, 2.7, 2.7],
[-2.0, 0.5, 2.1, 2.4, 2.5, 2.5, 2.5, 2.5, 2.7, 2.7],
[-2.0, 1.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5],
[-2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0],
[ 1.9, 1.9, 2.5, 2.5, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7],
[ 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7, 2.7]])
本章我们学习了黑盒约束优化问题,并且了解到它与前几章讨论的经典黑盒优化问题有何不同。我们知道一个有效的优化策略需要追求优化目标函数和满足约束条件两者兼顾。然后我们设计了这样一种策略,即将一个等于可行性概率的因子添加到获取分数中的方法的变体。这个新的获取分数会偏向于可行区域,从而更好地引导我们朝着可行最优解前进。
在下一章中,我们将讨论一个新的 BayesOpt 设置,即多信任度优化,其中查询目标函数的成本不同。这种设置要求我们平衡寻找高目标值和保留查询预算。
8.5 练习 2:飞机设计的受约束优化
在这个练习中,我们使用了第七章练习 2 中的飞机效用目标函数来解决受约束的优化问题。这个过程允许我们在一个高维问题上运行受约束的 BayesOpt,其中不明显的是可行的最优解在哪里。这个练习的解决方案包括在 CH08/03 - Exercise 2.ipynb 笔记本中。
-
重新创建在 CH07/04 - Exercise 2.ipynb 笔记本中使用的 BayesOpt 问题,包括名为
flight_utility()的飞机效用目标函数,我们搜索空间的边界(四维单位超立方体),GP 实现以及训练一些训练数据的 GP 的辅助函数fit_gp_model()。 -
实现以下成本函数,模拟通过四维输入指定的飞机设计的成本:
def flight_cost(X): X = X * 20 - 10 part1 = (X[..., 0] - 1) ** 2 i = X.new(range(2, 5)) part2 = torch.sum(i * (2.0 * X[..., 1:] ** 2 - X[..., :-1]) ** 2, dim=-1) return -(part1 + part2) / 100_000 + 2图 8.10 可视化了我们可以调整的各个参数对应的成本函数,显示了跨越这些二维空间的复杂非线性趋势。
图 8.10 在各种二维子空间中模拟飞机设计优化问题的成本函数,对应于可调参数的成对显示为坐标轴标签。
-
我们的目标是在遵循
flight_cost()计算出的成本小于或等于 0 的约束条件的情况下,最大化目标函数flight_utility():-
为此,我们将每次实验中 BayesOpt 策略可以进行的查询次数设置为 50,并指定每个策略需要运行 10 次重复实验。
-
如果找不到可行解决方案,刻画优化进展的默认值应该设置为-2。
-
-
在此问题上运行受约束的 EI 策略以及常规 EI 策略,然后可视化并比较它们的平均进展(以及误差条)。绘图应该类似于图 8.9。
总结
-
约束优化是一种优化问题,除了优化目标函数外,我们需要满足其他约束条件以获得实际解决方案。约束优化在材料和药物发现以及超参数调整中很常见,其中目标函数的最优解在实践中太难或太危险而无法使用。
-
在受约束的优化问题中满足约束的数据点称为可行点,而违反约束的数据点称为不可行点。我们的目标是在可行点中找到最大化目标函数的点。
-
约束条件可以极大地改变优化问题的解,切断或者排除具有高目标值的区域。因此,在优化目标函数时,我们需要积极考虑约束条件。
-
在约束 BayesOpt 框架中,我们对定义约束的每个函数训练一个 GP。这些 GP 允许我们以概率方式推理出数据点是否满足约束。具体来说,由于 GP 的预测分布是正态分布,因此很容易计算给定数据点可行性的概率。
-
我们可以通过将可行性的概率添加到 EI 策略的获取得分中来修改 EI 策略以考虑约束。约束 EI 策略可以平衡优化目标函数和满足约束条件。
-
BoTorch 提供了一个约束 EI 策略的类实现。在实现约束 EI 时,我们需要传入建模目标和约束函数的 GP,以及声明约束的下界和上界。
第十章:在多信度优化中平衡效用和成本
本章涵盖了
-
变成本的多信度优化问题
-
对来自多个来源的数据进行高斯过程训练
-
实施一个考虑成本的多信度贝叶斯优化策略
考虑以下问题:
-
你是否应该相信在线评论,说你最喜欢的电视剧的最新季度不如以前的好,你应该停止观看这部剧,还是应该花费下个周末的时间观看,以便自己找出你是否会喜欢这个新季度?
-
在看到他们的神经网络模型经过几个时期的训练后表现不佳之后,机器学习工程师是否应该放弃,转而使用其他模型,还是应该继续训练更多时期,希望能够获得更好的性能?
-
当物理学家想要理解一个物理现象时,他们能否使用计算机模拟来获得见解,或者真实的物理实验对于研究这一现象是必要的?
这些问题相似,因为它们要求被问及的人在两种可能的行动之间选择,这些行动可以帮助他们回答他们感兴趣的问题。一方面,这个人可以采取一个相对低成本的行动,但从这个行动中产生的答案可能会被噪声破坏,因此不一定是真实的。另一方面,这个人可以选择成本更高的行动,这将帮助他们得出更确定的结论:
-
阅读有关你喜欢的电视剧的最新季度的在线评论只需几分钟,但评论者可能和你的口味不同,你仍然可能喜欢这部剧。确定的方法是亲自观看,但这需要巨大的时间投入。
-
经过几个时期的训练后,神经网络的性能可能会表现出来,但不一定能反映出其真正的性能。然而,更多的训练意味着更多的时间和资源花费在一个可能没有价值的任务上,如果模型最终表现不佳的话。
-
计算机模拟可以告诉物理学家有关现象的许多信息,但不能捕捉到现实世界中的一切,因此模拟可能无法提供正确的见解。另一方面,进行物理实验肯定会回答物理学家的问题,但会耗费大量的金钱和精力。
这些情况属于一类称为多保真度决策的问题,我们可以决定以各种粒度和成本观察某些现象。以浅层次观察现象可能廉价且易于实现,但它并不能给我们尽可能多的信息。另一方面,仔细检查现象可能需要更多的努力。这里的保真度一词指的是一个观察如何与所讨论现象的真相密切相关。廉价的、低保真度的观察是嘈杂的,因此可能导致我们得出错误的结论,而高质量(或高保真度)的观察是昂贵的,因此不能随意进行。黑盒优化有自己的多保真度变体。
多保真度优化的定义是一个优化问题,除了要最大化的真实目标函数之外,我们还可以观察到并不完全匹配但仍然提供关于目标函数的信息的近似值。这些低保真度的近似值可以以比真实目标函数更低的成本进行评估。
在多保真度优化中,我们需要同时使用这些多源数据来获取关于我们感兴趣的内容(即目标函数的最优值)的最多信息。在本章中,我们将更详细地探讨多保真度优化问题以及如何从贝叶斯优化的角度来解决它。我们将了解一种平衡对目标函数和成本学习的策略,这导致了多保真度设置下的成本感知贝叶斯优化策略。然后我们看看如何在 Python 中实现这个优化问题和成本感知策略。通过本章的学习,我们将学会如何进行多保真度贝叶斯优化,并看到我们的成本感知策略比仅使用基本真相函数的算法更有效。
9.1 使用低保真度近似值研究昂贵现象
我们首先讨论了多保真度贝叶斯优化问题的动机、设置以及问题的实际示例。这个讨论将有助于澄清我们在这种情况下寻找决策策略的目标。
在贝叶斯优化的最简单设置中,我们在每个搜索迭代中评估目标函数,每次都仔细考虑在哪里进行这个评估,以取得最大的优化进展。这种仔细的推理的需要源于进行函数评估的高成本,这在昂贵的黑盒优化问题中是典型的。这个成本可以指的是我们花费在等待一个大型神经网络完成训练时所花费的时间,同时在寻找最佳网络架构,或者在药物发现过程中,合成实验药物和进行测试其有效性所需的金钱和精力。
但是,如果有办法在不实际评估目标函数的情况下评估函数评估结果,表示为f(x)?也就是说,除了目标函数外,我们可以查询一个廉价的替代 f̄(x)。这个替代 f̄(x) 是目标函数的一个不精确的近似,因此评估它并不能告诉我们关于真实目标函数f(x)的一切。然而,由于 f̄(x) 是 f(x) 的近似,对前者的了解仍然为我们提供了对后者的洞察。我们需要问自己的问题是:我们应该如何平衡使用昂贵但提供精确信息的真实目标函数 f(x) 和使用不精确但查询成本低的替代 f̄(x)?这种平衡在图 9.1 中有所体现,其中地面真实数据源 f(x) 是高保真度,而替代 f̄ (x) 是低保真度的近似。
图 9.1 多保真度决策问题模型,在该模型中,代理需要在查询真实目标函数 f(x) 获取准确信息和查询廉价替代 f̄(x) 之间进行平衡。
正如在引言中所指出的,在现实世界中使用低保真度近似是很常见的,如以下示例所示:
-
只对神经网络进行少数次数的训练,以评估其在某个数据集上的性能。 例如,在 5 个时期内神经网络的性能是其在 50 个时期后可以实现的性能的低保真逼近。
-
以计算机模拟代替真实实验来研究某些科学现象。 这种计算机模拟模仿了真实世界中发生的物理过程,并近似了物理学家想要研究的现象。然而,这种近似是低保真度的,因为计算机不能准确地模拟真实世界。
在多保真度优化问题中,我们旨在优化目标函数 f(x),我们可以选择查询高保真度 f(x) 还是低保真度 f̄(x) 来最好地了解和优化 f(x)。当然,查询 f(x) 将提供更多关于 f(x) 本身的信息,但查询成本阻止我们多次进行这样的查询。相反,我们可以选择利用低保真度近似 f̄(x) 尽可能多地了解我们的目标 f(x),同时最小化查询成本。
拥有多个低保真逼近
为了保持简单,我们在本章的示例中只使用一个低保真度近似 f̄(x)。然而,在许多实际场景中,我们提供多个低保真度近似 f̄ 1,f̄ 2,...,f̄[k](x) 给目标函数,每个近似都有自己的查询成本和准确性。
我们在下一节学习的贝叶斯方法不限制我们可以访问的低保真近似数量,并且我们在本章练习 2 中解决了一个具有两个低保真近似f̄ 1 和f̄ 2 的多保真优化问题。
当例如近似实验的计算机模拟具有控制近似质量的设置时,拥有多个低保真近似是适用的。如果将模拟质量设置为低,计算机程序将运行对实际世界进行粗略模拟并更快返回结果。另一方面,如果模拟质量设置为高,程序可能需要运行更长时间才能更好地近似实验。目前,我们只使用一个目标函数和一个低保真近似。
考虑图 9.2,除了作为贝叶斯优化示例目标函数的弗雷斯特函数,表示为实线外,我们还有一个对目标的低保真近似,表示为虚线。在这里,虽然低保真近似不完全匹配实际情况,但它捕捉到了后者的大致形状,因此在搜索目标最优解时可能会有所帮助。
图 9.2 弗雷斯特函数(实线)和函数的低保真近似(虚线)。尽管低保真近似不完全匹配实际情况,但前者提供了关于后者的信息,因为这两个函数大致具有相同的形状。
例如,由于低保真近似对真实目标函数具有信息性,我们可以多次查询近似以研究其在搜索空间中的行为,只有在想要“缩小”目标最优解时才查询真实情况。本章的目标是设计一个贝叶斯优化策略,以便我们导航这个搜索,并决定在哪里以及查询哪个函数以尽快和尽可能廉价地优化我们的目标函数。
多保真贝叶斯优化循环在图 9.3 中总结,与图 1.6 中传统的贝叶斯优化循环相比,有以下显著变化:
-
在步骤 1 中,高保真或实际情况下的函数以及低保真近似的数据都用于训练高斯过程。也就是说,我们的数据分为两组:在实际情况下评估的数据点集f(x)和在近似情况下评估的点集f̄(x)。在两个数据集上进行训练可以确保预测模型能够推理出在低保真数据但没有高保真数据的区域的目标函数情况。
-
在第 2 步中,贝叶斯优化策略为搜索空间中的每个数据点生成一个获取分数,以量化数据点在帮助我们识别目标最优解方面的价值。然而,不仅仅对数据点进行评分,而是对数据点-保真度对进行评分;也就是说,策略量化查询给定数据点在特定函数(高保真或低保真函数)上的价值。此分数需要平衡目标的优化和查询成本。
-
在第 3 步中,我们查询与最大化贝叶斯优化策略获取分数相对应的保真度上的数据点。然后,我们使用新观察更新我们的训练数据集,并回到第 1 步继续我们的贝叶斯优化过程。
在本章的剩余部分,我们将学习多保真度贝叶斯优化循环的组成部分以及如何在 Python 中实现它们,从训练一个包含高保真和低保真观测的数据集的 GP 开始。
9.2 使用 GP 进行多保真度建模
如图 9.3 所示,我们的 GP 模型是在包含多个保真度观测的组合数据集上进行训练的。这种组合训练使得 GP 能够对目标函数做出预测,即使在只有低保真观测的区域也是如此,这随后会通知贝叶斯优化策略做出与优化相关的决策。在接下来的一节中,我们将学习如何表示多保真度数据集并在数据集上训练 GP 的特殊变体;我们使用的代码包含在 CH09/01 - Multifidelity modeling.ipynb 中。
图 9.3 多保真度贝叶斯优化循环。GP 在高保真函数和低保真函数的数据上进行训练,并且贝叶斯优化策略决定循环的每次迭代在哪里以及查询哪个函数。
9.2.1 格式化多保真数据集
为了建立多保真度优化问题,我们使用以下代码来描述我们的一维福瑞斯特目标函数及其在图 9.2 中的低保真近似;我们的搜索空间介于 -5 和 5 之间:
def objective(x): ❶
y = -((x + 1) ** 2) * torch.sin(2 * x + 2) / 5 + 1 + x / 3 ❶
return y ❶
def approx_objective(x): ❷
return 0.5 * objective(x) + x / 4 + 2 ❷
lb = -5 ❸
ub = 5 ❸
bounds = torch.tensor([[lb], [ub]], dtype=torch.float) ❸
❶ 真实的目标函数
❷ 目标函数的低保真近似
❸ 搜索空间的边界,供后续优化策略使用
特别重要的是一个 PyTorch 张量,它存储我们可以访问的每个保真度函数与我们试图最大化的真实目标函数之间的相关信息。我们假设我们知道这些相关性的值,并声明此张量 fidelities 如下所示:
fidelities = torch.tensor([0.5, 1.0])
此张量有两个元素,对应于我们可以访问的两个保真度:0.5,我们用它来表示福瑞斯特函数 f(x) 与其低保真近似 f̄(x) 之间的相关性(图 9.2 中的实线和虚线),以及确切地是 1,这是福瑞斯特函数与其自身之间的相关性。
这些相关性值很重要,因为它们告诉我们 GP(高斯过程)在后续训练中应该对来自特定关联性的数据依赖多少:
-
如果低保真度近似值与真实目标函数的相关性很高,则该近似值提供了关于目标的大量信息。一个极端的例子是目标函数本身,它提供了完美的关于我们感兴趣的内容的信息,因此具有相关性值等于 1。
-
我们在示例中使用了一个相关性值为 0.5 的低保真度近似值,它提供了关于目标的不精确但仍然有价值的信息。
-
在相关性值为 0 的近似值的另一端,它对目标函数不提供任何信息;一个完全水平的线就是一个例子,因为这个“近似值”在整个定义域上都是常数。
图 9.4 展示了相关性的尺度:相关性越高,低保真度近似值提供的关于真实情况的信息越多。
设置关联性变量
通常,fidelities是一个具有k个元素的张量,其中k是我们可以查询的函数数目,包括目标函数。这些元素是介于 0 和 1 之间的数字,表示函数与目标之间的相关性。对于后续的学习和决策任务来说,将真实目标与自身之间的相关性 1 放置在张量的末尾更加方便。
不幸的是,关于如何设置这些关联性值并没有明确的规定;这个决定留给 BayesOpt 工程师决定。如果在您自己的用例中不知道这些值,您可以根据图 9.4 做出粗略估计,估计您的低保真度函数位于高保真度函数(真实情况)和无信息数据源之间的位置。
图 9.4 展示了低保真度近似值与真实情况之间的 0 到 1 之间的相关性尺度。相关性越高,低保真度近似值提供的关于真实情况的信息越多。
有了函数和相关性值,现在让我们创建一个示例训练数据集。我们首先在搜索空间内随机选择 10 个位置,并将它们存储为张量train_x:
n = 10 ❶
torch.manual_seed(0) ❷
train_x = bounds[0] + (bounds[1] - bounds[0]) * torch.rand(n, 1) ❸
❶ 训练集的大小
❷ 为了可重复性固定随机种子
❸ 从空间中均匀随机抽取点
张量train_x有 10 行 1 列,因为我们在一维空间中有 10 个数据点。其中的每个数据点都与一个关联性相关,表明观测结果来自高保真度还是低保真度(也就是说,每个数据点是高保真度或低保真度观测)。我们通过在train_x中添加一列来表示每个数据点的关联性来将这些信息编码到我们的数据集中,如图 9.5 所示。
图 9.5 是对多保真度数据集中的特征进行格式化说明。每个数据点都与正确度相关联;这些正确度的值存储在训练集的额外列中。
注意要记住,我们的目标是对来自两个来源的数据进行高斯过程训练:地面真实信息和低保真度函数。为此,我们将为我们拥有的 10 个数据点随机分配每个数据点的正确度。
我们使用torch.randint(2)来随机选择介于 0(包含)和 2(不包含)之间的整数,有效地在 0 和 1 之间进行选择。这个数字确定每个数据点来自于哪个函数:0 表示数据点在低保真度近似f̄(x)上进行评估;1 表示数据点在目标函数f(x)上进行评估。然后,我们提取fidelities中每个数据点对应的相关值,并将这个相关值的数组连接到我们的训练数据中:
train_f = fidelities[torch.randint(2, (n, 1))] ❶
train_x_full = torch.cat([train_x, train_f], dim=1) ❷
❶ 随机选择每个数据点的正确度(因此也是相关值)
❷ 将相关值添加到训练数据中
查看完整的训练数据train_x_full,我们可以看到前两个数据点是
tensor([[-0.0374, 1.0000], ❶
[ 2.6822, 0.5000], ❷
...
❶ 第一个数据点在 f(x)上进行评估。
❷ 第二个数据点在f̄ (x)上进行评估。
train_x_full的第一列包含数据点在-5 到 5 之间的位置,而第二列包含相关值。这个输出意味着我们的第一个训练点在-0.0374 处,并且在f(x)上进行评估。另一方面,第二个训练点在 2.6822 处,这次在f̄(x)上进行评估。
现在,我们需要适当地生成观测值train_y,以便使用正确的函数计算观测值:train_y的第一个元素等于f(–0.0374),第二个元素等于f̄ (2.6822),依此类推。为了做到这一点,我们编写了一个辅助函数,该函数接受完整的训练集,其中最后一列包含相关值,并调用适当的函数来生成train_y。即,如果相关值为 1,则调用objective(),即f(x),如前所定义;如果相关值为 0.5,则调用approx_objective()求解f̄(x):
def evaluate_all_functions(x_full):
y = []
for x in x_full: ❶
if torch.isclose(x[-1], torch.ones(1)): ❷
y.append(objective(x[:-1])) ❷
else: ❸
y.append(approx_objective(x[:-1])) ❸
return torch.tensor(y).unsqueeze(-1) ❹
❶ 迭代遍历数据点
❷ 如果相关值为 1,则查询 f(x)
❸ 如果相关值为 0.5,则查询f̄ (x)
❹ 重新调整观测张量的形状以符合正确的形状要求
在train_x_full上调用evaluate_all_functions()会给我们提供通过适当的函数评估得到的观测值train_y。我们的训练集在图 9.6 中可视化,其中包含三个高保真观测结果和七个低保真度观测结果。
图 9.6 是一个从 Forrester 函数及其低保真度近似中随机抽样得到的训练数据集。此训练集包含三个高保真观测结果和七个低保真度观测结果。
这就是我们在多精度贝叶斯优化中生成和格式化训练集的方法。我们的下一个任务是以一种同时使用基本事实和低精度近似的方式在这个数据集上训练 GP。
9.2.2 训练多精度 GP
我们在本节的目标是拥有一个接收一组多精度观测并输出关于目标函数的概率预测的 GP——即要最大化的目标函数f(x)。
请记住,在第 2.2 节中,GP 是无限多个变量的 MVN 分布。GP 使用协方差函数模拟任意一对变量之间的协方差(因此也是相关性)。正是通过任意两个变量之间的这种相关性,GP 可以在观察到另一个变量的值时对一个变量进行预测。
关于变量的相关性和更新信念的提醒
假设有三个变量A、B和C,用三元高斯分布联合建模,其中A和B之间的相关性很高,但A和C以及B和C之间的相关性都很低。
现在,当我们观察到A的值时,我们关于B的更新信念的不确定性(表示为B值的后验分布)显著减少。这是因为A和B之间的相关性很高,因此观察到A的值给了我们关于B值的很多信息。然而,对于C来说情况并非如此,因为A和C之间的相关性很低,所以对C的更新信念仍然存在相当大的不确定性。请参阅第 2.2.2 节,了解关于房价的类似且详细的讨论。
正如我们在第 2.2.2 节中学到的,只要我们有一种方法来模拟任意一对变量之间的相关性(即任意两个给定位置的函数值),我们就可以相应地更新 GP,以反映我们关于域中任何位置函数的更新信念。在多精度设置中,这仍然是正确的:只要我们有一种方法来模拟两个观察之间的相关性,即使其中一个来自高精度f(x),另一个来自低精度f̄(x),我们也可以更新 GP 上的目标函数f(x)。
我们需要使用一个协方差函数,它可以计算两个给定观测之间的协方差,这些观测可能来自同一精度,也可能不是。幸运的是,对于我们来说,BoTorch 提供了一个修改过的 Matérn 核函数,考虑了我们训练集中每个数据点关联的精度相关值:
-
如果数据点的相关值很高,核函数将在观察到的数据点和任何附近点之间产生高协方差,从而使我们能够通过一个信息丰富的观察来减少 GP 的不确定性。
-
如果相关值很低,核函数将输出低协方差,后验不确定性将保持较高。
注意:我们首次在第 3.4.2 节了解到 Matérn 内核。虽然我们不会在这里详细介绍多保真度 Matérn 内核,但感兴趣的读者可以在 BoTorch 的文档中找到更多信息(mng.bz/81ZB)。
由于具有多保真度内核的 GP 被实现为特殊的 GP 类,我们可以从 BoTorch 中导入它,而不必编写自己的类实现。具体来说,这个 GP 是SingleTaskMultiFidelityGP类的一个实例,它接受一个多保真度训练集train_x_full和train_y。初始化还有一个data_fidelity参数,应设置为包含相关值的train_x_full中的列的索引;在我们的情况下,这是1:
from botorch.models.gp_regression_fidelity
➥import SingleTaskMultiFidelityGP ❶
model = SingleTaskMultiFidelityGP(
➥train_x_full, train_y, data_fidelity=1) ❷
❶ 导入 GP 类实现
❷ 初始化多保真度 GP
初始化模型后,我们现在需要通过最大化观察数据的似然来训练它。(有关为什么选择最大化似然来训练 GP 的更多信息,请参见第 3.3.2 节。)由于我们拥有的 GP 是来自 BoTorch 的一个特殊类的实例,我们可以利用 BoTorch 的辅助函数fit_gpytorch_mll(),它在幕后促进了训练过程。我们需要做的就是初始化一个(对数)似然对象作为我们的训练目标,并将其传递给辅助函数:
from gpytorch.mlls.exact_marginal_log_likelihood import ❶
➥ExactMarginalLogLikelihood ❶
from botorch.fit import fit_gpytorch_mll ❶
mll = ExactMarginalLogLikelihood(model.likelihood,
➥model) ❷
fit_gpytorch_mll(mll); ❸
❶ 导入对数似然目标和用于训练的辅助函数
❷ 初始化对数似然目标
❸ 训练 GP 以最大化对数似然
这些令人惊讶的几行代码是我们需要训练一组观测的多保真度 GP 的全部内容。
BoTorch 关于数据类型和缩放的警告
当运行上述代码时,较新版本的 GPyTorch 和 BoTorch 可能会显示两个警告,第一个警告是
UserWarning: The model inputs are of type torch.float32\. It is strongly
recommended to use double precision in BoTorch, as this improves both
precision and stability and can help avoid numerical errors. See
https:/ /github.com/pytorch/botorch/discussions/1444
warnings.warn(
此警告指示我们应该为train_x和train_y使用不同的数据类型,默认为torch.float32,以提高数值精度和稳定性。为此,我们可以在代码中添加以下内容(在脚本开头):
torch.set_default_dtype(torch.double)
第二个警告涉及将输入特征train_x缩放到单位立方体(每个特征值介于 0 和 1 之间)以及将响应值train_y标准化为零均值和单位方差:
InputDataWarning: Input data is not
contained to the unit cube. Please consider min-max scaling the input data.
warnings.warn(msg, InputDataWarning)
InputDataWarning: Input data is not standardized. Please consider scaling
the input to zero mean and unit variance.
warnings.warn(msg, InputDataWarning)
缩放train_x和train_y有助于我们更容易地适应 GP,并且更加数值稳定。为了保持我们的代码简单,我们不会在这里实现这样的缩放,而是使用warnings模块过滤掉这些警告。感兴趣的读者可以参考第二章的练习以获取更多细节。
现在,为了验证这个训练过的 GP 能否学习关于训练集的信息,我们使用均值和 95% CI 可视化 GP 对 f(x) 在 -5 和 5 之间的预测。
我们的测试集xs是一个密集网格(超过 200 个元素),位于 -5 和 5 之间:
xs = torch.linspace(−5, 5, 201)
与我们在之前章节中看到的情况不同,我们需要用额外的一列来增强这个测试集,表示我们想要预测的保真度。换句话说,测试集xs需要与训练集train_x_full的格式相同。由于我们对 GP 对f(x)的预测感兴趣,所以我们添加了一列额外的全为 1 的列(因为 1 是f(x)的相关值):
with torch.no_grad(): ❶
pred_dist = model(torch.vstack([xs, torch.ones_like(xs)]).T) ❷
pred_mean = pred_dist.mean ❸
pred_lower, pred_upper = pred_dist.confidence_region() ❹
❶ 禁用梯度跟踪
❷ 用保真度列增强测试集并将其传递给模型
❸ 计算均值预测
❹ 计算 95%置信区间
这些预测在图 9.7 中进行了可视化,该图展示了关于我们的多保真度 GP 的一些重要特征:
-
关于f(x)的均值预测大致经过大约–3.6,0 和 1.3 的高保真度观测点。这种插值是有意义的,因为这些数据点确实是在f(x)上评估的。
-
在我们只有低保真度观测但没有高保真度观测的区域(例如,在–2 和大约 2.7 附近),我们对f(x)的不确定性仍然减少了。这是因为低保真度观测提供了关于f(x)的信息,即使它们没有在f(x)上进行评估。
-
在这些低保真度观测中,我们发现在 4 处的数据点可能为优化策略提供有价值的信息,因为该数据点捕捉到了该区域目标函数的上升趋势。通过利用这些信息,优化策略可以在附近发现全局最优点,大约在 4.5 附近。
图 9.7 多保真度 GP 对客观函数(地面真相)的预测。均值预测适当地经过高保真度观测,但在低保真度观测周围的不确定性仍然减少。
图 9.7 显示,GP 成功地从多保真度数据集中学习。为了将我们从低保真度观测中学习和预测f(x)的能力推向极限,我们可以修改生成训练集的方式,使其只包含低保真度观测。我们通过将train_x_full中的额外列中的相关值设置为0.5来实现这一点:
train_f = torch.ones_like(train_x) * fidelities[0] ❶
train_x_full = torch.cat([train_x, train_f], dim=1) ❷
❶ 所有相关值均为 0.5。
❷ 将相关值添加到训练集
重新运行迄今为止的代码将生成图 9.8 的左侧面板,在那里我们看到所有数据点确实来自低保真度逼近f̄(x)。与图 9.7 相比,我们在这里对我们的预测更不确定,这是适当的,因为仅观察到低保真度观测,GP 对客观函数f(x)的学习不如图 9.7 那样多。
图 9.8 由只基于低保真度观测训练的 GP 对客观函数(地面真相)的预测。左侧显示相关值为 0.5 时的结果;右侧显示相关值为 0.9 时的结果,表现出较少的不确定性。
为了进一步展示我们多适应性高斯过程的灵活性,我们可以玩弄存储在fidelities中的相关值(假设我们知道如何适当地设置这些相关值)。正如我们在第 9.2.1 节中所学到的,这个张量中的第一个元素表示f(x)和f̄(x)之间的相关性,大致对应于高斯过程应该“相信”低保真度观测的程度。通过将这个第一个元素设置为 0.9(而不是我们当前的 0.5),我们可以更重视低保真度的观测。也就是说,我们告诉高斯过程从低保真度数据中学到更多,因为它提供了关于f(x)的大量信息。图 9.8 的右侧显示了产生的高斯过程,其中我们的不确定性确实比左侧面板低。
除了多适应性高斯过程模型的灵活性之外,图 9.8 还展示了在fidelities张量中具有正确相关值的重要性。比较图 9.8 中的两个面板,我们发现 0.5 是两个保真度之间的相关值的较好值,而不是 0.9:
-
在右面板中,由于我们过度依赖和信任低保真度的观测,我们的预测在大部分空间中错过了真实目标f(x)。
-
在左面板中,95%的置信区间适当地更宽以反映我们对f(x)的不确定性。
换句话说,我们不希望过高估计低保真度近似f̄(x)对目标f(x)的信息量。
到目前为止,我们已经学会了如何用多适应性高斯过程建模一个函数。在本章的其余部分,我们讨论多适应性优化问题的第二部分:决策。更具体地说,我们学习如何设计一个多适应性优化策略,该策略在贝叶斯优化循环的每一步选择在哪个位置和查询哪个函数。
9.3 在多适应性优化中平衡信息和成本
为了能够在查询的信息性(低或高保真度)和运行该查询的成本之间进行权衡,我们需要一种方法来对查询成本进行建模和推理。在下一节中,我们学习如何用线性模型表示查询给定保真度的成本。使用这个成本模型,我们然后实现一个多适应性贝叶斯优化策略,平衡成本和进行优化进展。我们使用的代码存储在 CH09/02 - Multi-fidelity optimization.ipynb 笔记本中。
9.3.1 建模不同保真度查询的成本
在多保真度优化问题中,我们假设我们知道查询每个我们可以访问的函数的成本,无论是目标函数f(x)本身还是低保真度近似f̄(x)。为了促进模块化的优化工作流程,我们需要将关于查询每个函数成本的信息表示为一个成本模型。该模型接受一个给定的数据点(其中包含一个额外的特征,包含相关值,正如我们在第 9.2.1 节中看到的那样),并返回在指定保真度上查询该数据点的已知成本。
注意,由于已知在保真度上查询的成本,因此这个成本模型中没有涉及预测。我们只需要这个模型公式来保持我们在下一节学习的优化过程。
BoTorch 提供了一个名为AffineFidelityCostModel的线性成本模型的类实现,来自于botorch.models.cost模块。这个线性成本模型假设查询成本遵循图 9.9 所示的关系,其中查询在保真度上的成本与该保真度和地面真实值f(x)之间的相关性呈线性关系。这个线性趋势的斜率是图 9.9 中的权重参数,而进行任何查询都有一个固定成本。
图 9.9 多保真度优化的线性成本模型。在保真度上查询数据点的成本与该保真度和地面真实值f(x)之间的相关性呈线性关系。
我们使用以下代码初始化这个线性成本模型,其中我们将固定成本设为 0,权重设为 1。这意味着查询低保真度数据点将花费我们确切的低保真度近似的相关值,即 0.5(成本单位)。类似地,查询高保真度数据点将花费 1(成本单位)。在这里,fidelity_weights参数接受一个字典,将train_x_full中包含相关值的列的索引映射到权重(在我们的案例中为1):
from botorch.models.cost import AffineFidelityCostModel
cost_model = AffineFidelityCostModel(
fixed_cost=0.0, ❶
fidelity_weights={1: 1.0}, ❷
)
❶ 固定的查询成本
❷ 与相关值相乘的线性权重
注意,正在使用的成本单位取决于具体的应用。这个成本归结为查询目标和查询低保真度近似之间的“便利性”差异,这可以是时间(单位可以是分钟、小时或天)、金钱(以美元计算)或某种努力的度量,并应由用户设置。
线性趋势捕捉了相关值与成本之间的关系:具有高相关值的高信度函数应具有较高的查询成本,而低信度函数的查询成本应较低。两个可设置的参数——固定成本和权重——允许我们灵活地建模许多类型的查询成本。(我们将看到不同类型的查询成本如何导致下一节做出不同的决策。)有了这个成本模型,我们现在准备好学习如何在多信度优化问题中平衡成本和进展了。
建模非线性查询成本
本章中我们只使用线性成本模型。如果您的用例要求将查询成本建模为非线性趋势(例如二次或指数趋势),您可以实现自己的成本模型。
这是通过扩展我们正在使用的AffineFidelityCostModel类并重写其forward()方法来完成的。AffineFidelityCostModel类的实现在 BoTorch 的官方文档中显示(botorch.org/api/_modules/botorch/models/cost.xhtml),在那里我们看到forward()方法实现了查询成本与相关值之间的线性关系,如图 9.9 所示:
def forward(self, X: Tensor) -> Tensor:
lin_cost = torch.einsum( ❶
"...f,f", X[..., self.fidelity_dims], self.weights.to(X) ❶
) ❶
return self.fixed_cost + lin_cost.unsqueeze(-1) ❷
❶ 将相关值与权重相乘
❷ 添加了固定成本
在自定义成本模型的新类中,您可以重写此forward()方法来实现您需要的查询成本与相关值之间的关系。即使使用自定义成本模型,我们在本章中使用的其他代码也不需要修改,这说明了 BoTorch 的模块化设计的好处。
9.3.2 优化每美元的信息量以指导优化
我们现在回到本章开头提出的问题:我们应该如何平衡通过查询函数获得的信息量和查询该函数的成本?在多信度优化中,高信度函数(真实值)为我们提供了关于要优化的目标f(x)的精确信息,但查询成本很高。另一方面,低信度近似评估成本低廉,但只能提供关于f(x)的不精确信息。多信度贝叶斯优化策略的工作是决定如何平衡这一点。
我们已经从第 9.3.1 节得到了一个模型,计算了查询任何给定数据点的成本。至于另一方面,我们需要一种方法来量化我们将从给定查询中学到的目标函数的信息量,这可以来自于目标f(x)本身,也可以来自于低信度近似f̄(x)。
注意:关于 f(x) 或者更具体地说,关于 f(x) 最优解的信息量,正是我们在第六章学到的 Max-value Entropy Search(MES)策略用来对其查询进行排序的。MES 选择给出最多关于 f(x) 最高值的信息的查询,在单保真度设置中,我们只能查询目标。
由于这个信息增益度量是一个通用的信息论概念,因此它也可以应用于多信度设置。换句话说,我们使用 MES 作为基本策略来计算在优化过程中每个查询中关于 f(x) 最优解的信息量。现在,有了成本和信息增益这两个组成部分,我们现在需要设计一种方法来平衡这两者,从而得到一个成本感知的查询效用度量。
投资回报数量
为了量化查询的成本感知效用,我们使用经济学中的一个常见指标,称为投资回报(ROI)度量,该度量是通过将投资的利润除以投资成本来计算的。在多信度优化中使用 MES 时,利润是从查询数据点中获得的信息量,成本是查询成本。
记住,贝叶斯优化策略的收购分数是指策略为量化搜索空间内的每个数据点分配的分数,以量化该点在帮助我们优化目标函数方面的价值。在这里我们使用的 ROI 收购分数,是通过每个数据点提供关于目标最优解的信息量来评分,每个单位成本计算。这个计算在图 9.10 中可视化。
图 9.10 多信度优化的 ROI 收购分数公式。该分数量化了每个单位成本中查询提供的关于目标最优解的信息量。
我们看到,这个 ROI 分数是一个适当的度量,通过查询所获得的信息量与进行该查询的成本加权:
-
如果我们可以潜在地进行的两个查询具有相同的成本但产生不同的信息增益,我们应该选择提供更多信息的那一个。
-
如果两个查询提供了相同数量的关于目标最优解的信息,我们应该选择成本较低的那一个。
这种权衡允许我们从廉价的、低保真度的近似中获取关于目标函数 f(x) 的信息,如果这些近似确实是有信息的。另一方面,如果低保真度查询停止提供关于 f(x) 的信息,我们将转向高保真度数据点。基本上,我们始终选择最佳的成本感知决策,以确保“物有所值”。
要实现这种成本感知的 MES 变体,我们可以利用 BoTorch 的 qMultiFidelityMaxValueEntropy 类实现。该实现需要一些组件作为参数传入:
-
在图 9.10 中进行 ROI 计算的成本效用对象。该对象使用
InverseCostWeightedUtility类实现,通过其成本的倒数对查询的效用进行加权。初始化时需要我们之前创建的成本模型:from botorch.acquisition.cost_aware import InverseCostWeightedUtility cost_aware_utility = InverseCostWeightedUtility(cost_model=cost_model) -
*用作 MES 的熵计算候选集的 Sobol 序列。*我们首次了解到在第 6.2.2 节中使用 Sobol 序列与 MES,并且这里的过程与之前相同,我们从单位立方体中抽取一个 1,000 元素的 Sobol 序列(在我们的一维情况下,它只是从 0 到 1 的段),并将其缩放到我们的搜索空间。在多保真设置中,我们需要做的另一件事是用额外的列来增强此候选集,以表示我们想要在目标 f(x) 中测量熵的相关值为 1:
torch.manual_seed(0) ❶ sobol = SobolEngine(1, scramble=True) ❷ candidate_x = sobol.draw(1000) ❷ candidate_x = bounds[0] + (bounds[1] - bounds[0]) * ➥candidate_x ❸ candidate_x = torch.cat([candidate_x, torch.ones_like( ➥candidate_x)], dim=1) ❹❶ 为了可重现性而固定随机种子
❷ 在单位立方体内从 Sobol 序列中抽取 1,000 个点
❸ 将样本缩放到我们的搜索空间
❹ 用地面实况的指数增强样本
-
*最后,将给定数据点从任何保真度投影到地面实况的辅助函数。*此投影在我们的策略用于计算采集分数的熵计算中是必要的。在这里,BoTorch 提供了该辅助函数
project_to_target_fidelity,如果我们的训练集中的最后一列包含相关值,并且地面实况的相关值为 1,那么它就不需要任何进一步的参数化,这两个条件在我们的代码中都成立。
使用上述组件,我们实现我们的成本感知、多保真 MES 策略如下所示:
from botorch.acquisition.utils import project_to_target_fidelity
policy = qMultiFidelityMaxValueEntropy(
model,
candidate_x, ❶
num_fantasies=128,
cost_aware_utility=cost_aware_utility, ❷
project=project_to_target_fidelity, ❸
)
❶ 来自 Sobol 序列的样本
❷ 通过成本的倒数对效用进行加权的成本效用对象
❸ 投影辅助函数
到此为止,我们可以使用此策略对象根据任何保真度对每个数据点进行评分,评估其在帮助我们找到目标最优解方面的成本调整值。拼图的最后一块是我们用来优化此策略的采集分数,以找到每次搜索迭代中 ROI 分数最高的点的辅助函数。在以前的章节中,我们使用 botorch.optim.optimize 模块中的 optimize_acqf 来优化单一保真度情况下的采集分数,这仅在我们的搜索空间是连续的情况下有效。
注意:在我们当前的多保真设置中,用于查询位置的搜索空间仍然是连续的,但用于查询的函数选择是离散的。换句话说,我们的搜索空间是混合的。幸运的是,BoTorch 为混合搜索空间提供了类似的辅助函数:optimize_acqf_mixed。
除了optimize_acqf通常接受的参数外,新的辅助函数optimize_acqf_mixed还有一个fixed_features_list参数,它应该是一个字典列表,每个字典将train_x_的一个离散列的索引映射到列包含的可能值。在我们的情况下,我们只有一个离散列,即包含相关值的最后一列,因此我们使用[{1: cost.item()} for cost in fidelities]作为fixed_features_list参数。此外,我们通常传递给辅助函数的bounds变量现在也需要包含相关值的边界。总的来说,我们使用以下方式优化我们的多保真 MES 获取分数:
from botorch.optim.optimize import optimize_acqf_mixed
next_x, acq_val = optimize_acqf_mixed(
policy,
bounds=torch.cat( ❶
[bounds, torch.tensor([0.5, 1.0]).unsqueeze(-1)], dim=1 ❶
), ❶
fixed_features_list=[{1: cost.item()} for cost in fidelities], ❷
q=1,
num_restarts=20,
raw_samples=50,
)
❶ 搜索空间的边界,包括相关值的边界
❷ 相关列可能包含的离散值
这个辅助函数完成了我们需要使用多保真 MES 策略的代码。图 9.11 的底部面板可视化了通过我们在第 9.2.2 节中训练的多保真 GP 计算的获取分数。在这个底部面板中,阴影区域的边界(表示低保真查询的分数)超过了斜纹图案区域的边界(高保真查询的分数),这意味着在当前知识条件下,低保真查询比高保真查询更具成本效益。最终,我们进行的最佳查询,表示为星号,约为低保真近似f̄(x)的 3.5。
图 9.11 对于目标函数的当前 GP 信念(顶部)和通过多保真 MES 策略计算的获取分数(底部)。在这个例子中,由于其低成本,低保真查询优于高保真查询。
在图 9.11 中,低保真查询是最佳的类型,因为相对于任何高保真查询,它提供了更多的信息。然而,情况并非总是如此。通过修改我们从第 9.2.2 节中的数据生成过程以生成所有低保真观测训练集并重新运行我们的代码,我们得到了图 9.12 的左面板,这次,最优决策是查询高保真函数f(x)。这是因为根据我们的高斯过程信念,我们已经充分从低保真数据中学到了东西,现在是时候我们检查地面真相f(x)了。
图 9.12 高保真查询优于低保真查询的情况。左侧,训练集仅包含低保真观测。右侧,低保真查询的成本几乎与高保真查询相同。
通过研究查询成本对决策的影响来最终分析我们策略的行为。为此,我们改变了查询成本,使低保真度的查询比高保真度的查询便宜得不多,具体方法是将第 9.3.1 节中描述的固定查询成本从 0 增加到 10。这种改变意味着低保真度的查询现在需要 10.5 个成本单位,而高保真度的查询现在需要 11 个。与之前的 0.5 和 1 的成本相比,10.5 和 11 更接近,使得图 9.10 中的两个保真度的分母几乎相等。这意味着低保真度的近似值f̄(x)的查询成本几乎和目标函数f(x)本身一样高。鉴于这些查询成本,图 9.12 的右面板显示了 MES 策略如何对潜在查询打分。这一次,因为高保真度查询并不比低保真度查询昂贵得多,所以更倾向于前者,因为它们能够给我们更多关于f(x)的知识。
这些示例表明,MES 可以确定在信息和成本适当平衡的情况下的最优决策。也就是说,当低保真度查询成本低且能够提供关于目标函数的实质性信息时,策略会给低保真度查询分配更高的分数。另一方面,如果高保真度查询要么显著更具信息量,要么成本差不多,那么策略将更倾向于高保真度查询。
9.4 在多保真度优化中测量性能
我们之前的讨论表明,多保真度 MES 策略能够在选择两个保真度进行查询时做出恰当的决策。但是这个策略比只查询地面真相f(x)的常规 BayesOpt 策略好吗?如果是这样,它究竟好多少呢?在本节中,我们学习如何在多保真度设置中对 BayesOpt 策略的性能进行基准测试,这需要额外的考虑。我们显示的代码可以在 CH09/03-测量性能.ipynb 笔记本中找到。
注意在前面的章节中,为了衡量优化进展,我们记录了训练集中收集到的最高目标值(即“现有值”)。如果策略A收集的现有值超过了策略B收集的现有值,我们就说策略A在优化方面比策略B更有效。
在多保真度的设置中,记录现有值是不起作用的。首先,如果我们要在训练集中记录现有值,只有选择最大标记值的高保真度数据点才有意义。然而,这个策略忽略了低保真度查询对于学习目标f(x)的任何贡献。例如,以图 9.13 中可视化的两种可能场景为例:
-
在左边的第一种情况中,我们进行了三次高保真度的观察,而最高观察值大约为 0.8。客观地说,在这种情况下我们没有进行优化进展;我们甚至没有探索过 x 大于 0 的区域。
-
在右边的第二种情况中,我们只进行了低保真度的观察,因此记录高保真度现任值甚至都不适用。然而,我们看到我们非常接近找到目标函数的最优解,因为我们的查询已经发现了函数在 4.5 左右的峰值。
图 9.13 在多保真度优化中使用高保真度现任者来衡量性能是不合适的。在左边,高保真度现任者大约为 0.8,而我们还没有发现目标的最优解。在右边,即使我们接近找到目标的最优解,也没有高保真度查询来记录现任值。
换句话说,我们应该更喜欢第二种情况而不是第一种,因为第二种情况表明了接近优化成功,而第一种情况则几乎没有优化进展。然而,使用高保真度的现任者作为进展度量并不能帮助我们区分这两种情况。因此,我们需要另一种衡量优化进展的方法。
BayesOpt 社区中常见的进展度量标准是当前给出最高后验均值的位置处的目标函数值。这个度量标准对应着以下问题的答案:如果我们停止运行 BayesOpt 并推荐一个点作为优化问题的解,我们应该选择哪个点?直觉上,我们应该选择在最合理的情况下(根据我们的 GP 信念),能够给出最高值的点,也就是后验均值最大化者。
我们看到后验均值最大化者促使我们在图 9.13 中进行的比较,左边的情况下,后验均值最大化者为 0,而目标值为 0.8(在单保真度情况下,均值最大化者通常对应于现任者),而右边的情况下,均值最大化者在 4.5 左右。换句话说,后验均值最大化者度量成功地帮助我们区分了这两种情况,并显示出左边的情况不如右边的情况。
为了实现这个度量标准,我们制作了一个辅助策略,该策略使用后验均值作为其收购分数。然后,就像我们在 BayesOpt 中优化常规策略的收购分数一样,我们使用这个辅助策略来优化后验均值。这个策略需要两个组件:
-
使用后验均值作为其收购分数的 BayesOpt 策略的类实现。这个类是
PosteriorMean,可以从botorch.acquisition导入。 -
一个包装策略,仅优化高保真度指标。这个包装器是必需的,因为我们的 GP 模型是多保真度的,当将该模型传递给优化策略时,我们总是需要指定要使用哪个保真度。此包装器策略实现为来自
botorch.acquisition.fixed_feature的FixedFeatureAcquisitionFunction的一个实例。
总的来说,我们用以下辅助策略制定后验均值最大化器度量标准,其中包装策略接受PosteriorMean的一个实例,并且我们将其他参数指定如下:
-
搜索空间的维度是
d=2—我们的实际搜索空间是一维的,并且还有一个附加维度用于相关值(即查询的保真度)。 -
要在优化期间固定的维度的索引,
columns=[1]及其固定值values=[1]—由于我们只想找到对应于目标函数、即高保真函数的后验均值最大化器,我们指定第二列(索引1)始终应为值 1:
from botorch.acquisition.fixed_feature
➥import FixedFeatureAcquisitionFunction
from botorch.acquisition import PosteriorMean
post_mean_policy = FixedFeatureAcquisitionFunction(
acq_function=PosteriorMean(model), ❶
d=2, ❷
columns=[1], ❸
values=[1], ❹
)
❶ 优化后验均值
❷ 搜索空间的维度数
❸ 固定列的索引
❹ 固定列的数值
然后,我们使用熟悉的辅助函数optimize_acqf来找到最大化收获分数的点,即目标函数的后验均值(我们在 4.2.2 节首次了解到此辅助函数):
final_x, _ = optimize_acqf(
post_mean_policy, ❶
bounds=bounds, ❷
q=1, ❷
num_restarts=20, ❷
raw_samples=50, ❷
)
❶ 优化后验均值。
❷ 其他参数与我们优化另一个策略时相同。
这个final_x变量是最大化目标函数后验均值的位置。在我们的 Jupyter 笔记本中,我们将这段代码放在一个辅助函数中,该函数返回final_x,并增加了一个相关值为 1,表示真实的目标函数:
def get_final_recommendation(model):
post_mean_policy = FixedFeatureAcquisitionFunction(...) ❶
final_x, _ = optimize_acqf(...) ❷
return torch.cat([final_x, torch.ones(1, 1)], dim=1) ❸
❶ 制作包装策略
❷ 优化收获分数
❸ 使用相关值为 1 增强最终推荐
现在,在 BayesOpt 循环期间,我们不再记录 incumbent 值作为优化进度的指示,而是调用此get_final_recommendation辅助函数。此外,我们现在没有每次运行中要进行的最大查询次数,而是有一个最大预算,可以用于低保真度或高保真度查询。换句话说,我们会一直运行我们的优化算法,直到累计成本超过我们的预算限制。我们的多保真 BayesOpt 循环的框架如下:
budget_limit = 10 ❶
recommendations = [] ❷
spent_budget = [] ❸
... ❹
current_budget = 0
while current_budget < budget_limit:
... ❺
rec_x = get_final_recommendation(model) ❻
recommendations.append(evaluate_all_functions ❻
➥(rec_x).item()) ❻
spent_budget.append(current_budget) ❻
... ❼
current_budget += cost_model(next_x).item() ❽
... ❾
❶ 每次优化运行的最大成本
❷ 跟踪整个优化过程中最大化后验均值的推荐
❸ 跟踪每次迭代中已花费的预算
❹ 生成一个随机的起始观察
❺ 对当前数据进行 GP 训练
❻ 使用最新推荐更新记录
❼ 初始化策略并优化其收获分数
❽ 跟踪已花费的预算
❾ 更新训练数据
我们现在准备运行多保真度 MES 来优化 Forrester 目标函数。作为基准,我们还将运行单保真度 MES 策略,该策略仅查询地面实况 f(x)。我们拥有的 GP 模型是多保真度的,因此,要使用此模型运行单保真度的 BayesOpt 策略,我们需要 FixedFeatureAcquisitionFunction 类的包装策略来限制策略可以查询的保真度:
policy = FixedFeatureAcquisitionFunction(
acq_function=qMaxValueEntropy(model, candidate_x, num_fantasies=128), ❶
d=2,
columns=[1], ❷
values=[1], ❷
)
next_x, acq_val = optimize_acqf(...) ❸
❶ 包装策略是单保真度 MES。
❷ 将第二列(索引 1)中的相关值固定为 1
❸ 使用辅助函数 optimize_acqf 优化收购分数
运行这两种策略会生成图 9.14 中的结果,我们观察到多保真度 MES 明显优于单保真度版本。多保真度 MES 的成本效益性说明了在信息和成本之间取得平衡的好处。然而,我们注意到这只是一次运行的结果;在练习 1 中,我们多次以不同的初始数据集运行此实验,并观察这些策略的平均性能。
图 9.14 后验均值最大化器的目标值作为两种 BayesOpt 策略花费预算的函数。在这里,多保真度 MES 明显优于单保真度版本。
在本章中,我们学习了多保真度优化问题,即在优化目标函数与获取关于目标的知识成本之间取得平衡。我们学习了如何实现一个能够从多个数据源中学习的 GP 模型。然后,这个模型允许我们根据信息理论价值来推理查询的优化值。通过将这个信息理论量与查询成本结合在一起形成的投资回报率度量,我们设计了一个成本感知的多保真度 MES 策略的变体,可以自动权衡知识和成本。
9.5 练习 1:可视化多保真度优化中的平均性能
为了比较我们的策略的性能,图 9.14 可视化了在优化过程中后验均值最大化器的目标值与花费预算的关系。然而,这只是一次优化运行的结果,我们想要展示每个策略在多次实验中的平均性能。(我们在第四章的练习 2 中首次讨论了重复实验的想法。)在这个练习中,我们多次运行优化循环,并学习如何取得平均性能以获得更全面的比较。到练习结束时,我们将看到,多保真度 MES 在信息和成本之间取得了良好的平衡,并比其单保真度的对应物更有效地优化了目标函数。解决方案存储在 CH09/04 - Exercise 1.ipynb 笔记本中。
按照以下步骤进行:
-
复制 CH09/03 - Measuring performance.ipynb 笔记本中的问题设置和多信度优化循环,并添加另一个变量表示我们要运行的实验次数(默认为 10)。
-
为了方便重复实验,在优化循环代码中添加一个外部循环。这应该是一个有 10 次迭代的
for循环,每次生成一个不同的随机观察结果。(这个随机生成可以通过将 PyTorch 的随机种子设置为迭代编号来完成,这样可以确保随机数生成器在具有相同种子的不同运行中返回相同的数据。) -
CH09/03 - Measuring performance.ipynb 笔记本中的代码使用两个列表,
recommendations和spent_budget,来跟踪优化进度。将这些变量中的每一个变量都变成一个列表的列表,其中每个内部列表都承担与 CH09/03 - Measuring performance.ipynb 笔记本中相应列表相同的目的。这些列表的列表允许我们跟踪 10 次实验的优化进度,并在后续步骤中比较不同的优化策略。 -
在我们的优化问题上运行多信度 MES 策略及其单信度版本。
-
由于查询低信度函数的成本与查询高信度函数的成本不同,
spend_budget中的列表可能与彼此不完全匹配。换句话说,在图 9.14 中曲线中的点在不同运行中没有相同的x坐标。这种不匹配阻止我们在多个运行中获取存储在recommendations中的平均进度。为了解决这个问题,我们对每个进度曲线使用线性插值,这使我们能够在规则网格上“填充”进度值。就是在这个规则网格上,我们将对每个策略在运行中的表现进行平均。对于线性插值,使用 NumPy 中的
np.interp,它以规则网格作为其第一个参数;这个网格可以是一个介于 0 和budget_limit之间的整数数组:np.arange(budget_limit)。第二个和第三个参数是组成每个进度曲线的点的x和y坐标,即spend_budget和recommendations中的每个内部列表。 -
使用线性插值的值来绘制我们运行的两种策略的平均性能和误差条,并比较它们的性能。
-
由于我们当前正在测量优化性能的方式,我们可能会发现我们在每次运行中跟踪的推荐列表不是单调递增的。(也就是说,我们可能会得到一个比上一次迭代的推荐性能更差的推荐。)为了检查这种现象,我们可以绘制代表各个运行的优化进度的线性插值曲线,以及平均性能和误差条。为我们运行的两种策略实现此可视化,并检查结果曲线的非单调性。
9.6 练习 2:使用多个低保真度近似进行多保真度优化
我们在本章中学到的方法可以推广到存在多个我们可以查询的目标函数的低保真度近似的场景中。我们的策略是相同的:将我们从每个查询中获得的信息量除以其成本,然后选择提供最高投资回报率的查询。这个练习向我们展示了我们的多保真度 MES 策略可以在多个低保真度函数之间取得平衡。解决方案存储在 CH09/05 - Exercise 2.ipynb 笔记本中。
接下来执行以下步骤:
-
对于我们的目标函数,我们使用名为
Branin的二维函数,它是优化的常见测试函数,就像 Forrester 函数一样。BoTorch 提供了 Branin 的多保真度版本,因此我们使用frombotorch.test_functions.multi_fidelityimportAugmentedBranin将其导入到我们的代码中。为了方便起见,我们使用以下代码对该函数的域和输出进行缩放,这使objective成为我们评估查询时要调用的函数:problem = AugmentedBranin() ❶ def objective(X): X_copy = X.detach().clone() ❷ X_copy[..., :-1] = X_copy[..., :-1] * 15 - 5 ❷ X_copy[..., -2] = X_copy[..., -2] + 5 ❷ return (-problem(X_copy) / 500 + 0.9).unsqueeze(-1) ❷❶ 从 BoTorch 中导入 Branin 函数
❷ 处理函数的输入和输出,将值映射到一个好的范围内
-
将我们的搜索空间的边界定义为单位正方形。也就是说,两个下界为 0,两个上界为 1。
-
声明存储我们可以查询的不同函数的相关值的
fidelities变量。在这里,我们可以访问两个相关值分别为 0.1 和 0.3 的低保真度近似,因此fidelities应包含这两个数字和最后一个元素为 1。图 9.15 目标函数 Branin(右侧)和两个低保真度近似
这三个函数在图 9.15 中可视化,明亮的像素表示高目标值。我们可以看到,两个低保真度的近似都遵循地面真相展示的一般趋势,并且与真实情况的相似程度随着保真度值的增加而增加。也就是说,保真度为 0.3 的第二个近似(中间)与真实的目标函数(右侧)更相似,比保真度为 0.1 的第一个近似(左侧)更相似。
-
将线性成本模型的固定成本设置为 0.2,权重设置为 1。这意味着在图 9.15 左侧查询低保真度函数的成本为 0.2 + 1 × 0.1 = 0.3。类似地,中间函数的成本为 0.5,真实目标函数的成本为 1.2。将每个实验的预算限制设置为 10,并将重复实验的次数也设置为 10。
-
从 Sobol 序列中抽取的候选人数设置为 5,000,并且在使用辅助函数优化给定策略的获取分数时,使用 100 次重启和 500 个原始样本。
-
重新定义助手函数
get_final_recommendation,以便为我们的二维目标函数设置适当的参数:d=3和columns=[2]。 -
运行多保真度最大值熵搜索策略及其单保真度版本的优化问题,并使用练习 1 中描述的方法绘制每个策略的平均优化进展和误差线。注意,在为单保真度策略创建包装器策略时,参数
d和columns需要与上一步骤中设置的方式相同。验证多保真度策略的表现优于单保真度策略。
摘要
-
多保真度优化是一种优化设置,我们可以访问多个信息源,每个信息源具有自己的准确度和成本水平。在这种情况下,我们需要平衡从行动中获得的信息量和采取该行动的成本。
-
在多保真度优化中,高保真度函数提供确切的信息,但评估代价高,而低保真度函数查询成本低,但可能提供不准确的信息。在优化循环的每次迭代中,我们需要决定在哪里和哪个函数进行查询,以尽快找到目标函数的最优值。
-
每个保真度提供的信息水平通过其与真实值之间的相关性来量化,该相关性是一个介于 0 和 1 之间的数字。相关性越高,保真度与真实值越接近。在 Python 中,我们将每个数据点的相关性值存储为特征矩阵中的额外列。
-
基于可以处理训练数据点的相关性值的内核,可以对多保真度数据集进行训练,我们可以使用多保真度变体的 Matérn 内核来完成此任务。GP 对于预测的不确定性取决于每个观测点所来自的函数,以及如果它来自于低保真度函数,则该函数的相关性值如何。
-
我们使用线性模型来编码优化过程中每个保真度的查询成本。通过设置此模型的参数-固定成本和权重,我们可以建模查询成本与数据质量之间的正相关关系。也可以使用 BoTorch 实现非线性成本模型。
-
为了在信息性和成本之间取得平衡,我们使用 MES 策略的变体,通过查询成本的倒数对每个查询的信息量进行加权。该度量类似于经济学中的投资回报概念,并使用 BoTorch 中的
InverseCostWeightedUtility类进行实现。 -
对于目标函数的最优值的信息增益的精确计算,即 MES 的核心任务,由于最优值的非高斯分布,因此是棘手的。为了近似计算信息增益,我们使用 Sobol 序列来表示整个搜索空间,减轻计算中的计算负担。
-
多信度 MES 策略成功平衡了信息和成本,并优先考虑成本效益高的查询。
-
为了优化多信度策略的收购分数,我们使用
optimize_ acqf_mixed辅助函数,该函数可以处理混合搜索空间,其维度可以是连续的或离散的。 -
在多信度设置中准确测量性能,我们使用每次迭代中后验均值的最大化者作为终止前的最终建议。这一数量比高保真现有值更好地捕捉了我们对目标函数的了解。
-
在多信度设置中优化单信度收购分数,我们使用
FixedFeatureAcquisitionFunction类的一个实例作为包装策略。为了初始化一个包装策略,我们声明搜索空间中的哪个维度是固定的,以及其值是多少。