索和利用,但随着时间的推移,在其决策的聚合中,该策略将能够充分探索空间并缩小高性能区域。
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 序列生成单位立方体内的点,并将它们缩放到目标空间。
贝叶斯优化实战(二)(2)https://developer.aliyun.com/article/1516466