组件#
除了入门中提到的基本组件外,以下段落将解释所有其他组件,以更好地展示 SMAC。这些组件都用于指导优化过程,简单的改变可能会极大地影响结果。
在深入研究组件之前,我们简要解释一下 SMAC 中的主要贝叶斯优化循环。 SMBO 从外观接收所有实例化的组件,逻辑在这里发生。一般来说,使用一个 while 循环来请求下一个试验,将其提交给运行器,并等待运行器完成评估。由于运行器和 SMBO
对象是解耦的,while 循环会继续并请求更多试验(例如,在多线程情况下),这些试验也可以提交给运行器。如果所有工作器都被占用,SMAC 将会等待,直到新的工作器再次可用。此外,在每次迭代中都会检查诸如墙钟时间 (wallclock time) 和剩余试验次数等限制。
代理模型#
代理模型用于近似配置的目标函数。在以前的版本中,该模型被称为经验性能模型 (EPM)。贝叶斯优化主要使用/与高斯过程相关联。然而,SMAC 也将随机森林纳入作为代理模型,这使得在高维和复杂空间中进行优化成为可能。
用于训练代理模型的数据由运行历史编码器收集(接收运行历史记录中的数据并进行转换)。如果涉及预算,则使用满足 smac.main.config_selector 中 min_trials
(默认为 1)的最高预算。如果不使用预算,则使用所有观测值。
如果您使用实例,建议使用实例特征。模型在与每个实例及其特征关联的数据上进行训练。假设您有两个超参数、两个实例且没有实例特征,模型将在以下数据上进行训练:
超参数 1 | 超参数 2 | 目标值 |
---|---|---|
0.1 | 0.8 | 0.5 |
0.1 | 0.8 | 0.75 |
505 | 7 | 2.4 |
505 | 7 | 1.3 |
您可以看到,由于存在两个实例,相同的输入会导致不同的目标值。如果您将每个实例与一个特征关联起来,您将得到以下数据点:
超参数 1 | 超参数 2 | 实例特征 | 目标值 |
---|---|---|---|
0.1 | 0.8 | 0 | 0.5 |
0.1 | 0.8 | 1 | 0.75 |
505 | 7 | 0 | 2.4 |
505 | 7 | 1 | 1.3 |
接收数据的步骤如下:
- 增强器通过
next(self.config_generator)
请求新的配置。 - 配置选择器通过运行历史编码器收集数据,该编码器遍历运行历史记录中的试验。
- 运行历史编码器仅收集处于
considered_states
状态的试验和超时试验。此外,如果使用预算,仅考虑最高预算。在此步骤中,使用normalize_costs
函数(使用运行历史记录中的objective_bounds
)和多目标算法对多目标值进行标量化。例如,当使用 ParEGO 时,每次训练中的标量化都会不同。 - 选定的试验目标值会被转换(例如,对数转换,取决于选定的编码器)。
- 超参数可能仍具有非活动值。模型在收集的数据传递给模型后会处理这些值。
采集函数#
采集函数是一种数学技术,指导贝叶斯优化期间如何探索参数空间。它们使用代理模型生成的预测均值和预测方差。
采集函数由采集最大化器使用(见下一节)。此外,SMAC 提供了多种不同的采集函数(下置信区间 Lower Confidence Bound, 预期改进 Expected Improvement, 概率改进 Probability Improvement, Thompson, 集成采集函数 integrated acquisition functions 和先验采集函数 prior acquisition functions)。关于采集函数的更多信息,请参考相关文献。
注意
采集函数计算每个配置的采集值。然而,配置由采集最大化器提供。因此,采集最大化器负责接收下一个配置。
采集最大化器#
采集最大化器是采集函数的包装器。它返回下一个配置。SMAC 支持局部搜索、(排序) 随机搜索、局部和 (排序) 随机搜索以及差分进化。局部搜索检查最佳配置的邻居,而随机搜索确保探索配置空间。使用排序随机搜索时,随机配置会根据采集函数的值进行排序。
警告
请注意挑战者的数量:如果您在采集函数中遇到内存问题或计算时间过长,您可能需要降低挑战者的数量。
采集最大化器也包含 随机设计。更多信息请参阅 ChallengerList。
初始设计#
代理模型需要数据进行训练。因此,初始设计用于生成初始数据点。我们提供了随机、拉丁超立方、Sobol、因子和默认的初始设计。默认初始设计使用配置空间中的默认配置,而因子初始设计生成配置空间的角点。Sobol 序列是准随机低偏差序列的一个例子,而拉丁超立方设计是一种从多维分布中生成接近随机的参数值样本的统计方法。
初始设计配置首先由配置选择器生成。此外,配置选择器会跟踪已返回的配置,以确保同一配置不会返回两次。
随机设计#
随机设计用于采集最大化器中,以判断下一个配置应该是随机的还是从采集函数中采样得到。例如,如果我们使用概率为 50% 的随机设计,我们有 50% 的机会采样一个随机配置,有 50% 的机会从采集函数中采样一个配置(尽管采集函数已经包含了探索与利用的权衡)。这种设计确保了优化过程不会陷入局部最优,并且我们**保证**随着时间的推移找到最佳配置。
除了简单的概率随机设计,我们还提供了退火和模数随机设计。
增强器#
增强器根据迄今为止已评估的 :term:试验<Trial>
来比较不同的配置。它决定哪个配置应该被“强化”,换句话说,某个配置是否值得花费更多时间(例如,评估另一组种子对、在另一个实例上评估或在更高预算下评估)。
警告
请始终注意 max_config_calls
或 n_seeds
:如果此参数设置得很高,增强器可能会在单个配置上花费大量时间。
根据组件和参数的不同,增强器会告诉您在整个优化过程中使用了哪些种子、预算和/或实例。您可以使用方法 uses_seeds
、uses_budgets
和 uses_instances
(可直接通过外观调用)来(健全性)检查增强器是否使用了这些参数。
另一个重要的事实是,增强器会跟踪当前的最佳配置(也称为迄今为止找到的最佳配置)。在多目标情况下,可能会找到多个最佳配置。
所有增强器都支持多目标、多保真度和多线程:
- 多目标:同时跟踪多个最佳配置。
- 多保真度:包含实例或预算。
- 多线程:增强器实现为生成器,以便可以根据需要多次调用增强器上的
next
。增强器不需要接收结果,因为结果直接取自运行历史记录。
注意
所有增强器都在运行历史记录上工作,并识别之前记录的试验(例如,用户是否已事先评估了某些内容)。先前的配置(在最佳情况下,也包括完整的试验)会再次添加到队列/跟踪器中,以便它们集成到强化过程中。
这意味着原生支持继续运行以及包含用户输入。
配置选择器#
配置选择器使用初始设计、代理模型、采集最大化器/函数、运行历史记录、运行历史编码器和随机设计来选择下一个配置。配置选择器由增强器直接使用,并在每次请求新配置时被调用。
配置选择器背后的思想很简单:
- 生成初始设计配置。
- 使用运行历史编码器的数据训练代理模型。
- 从采集函数/最大化器获取接下来的
retrain_after
个配置并生成它们。 - 生成所有
retrain_after
个配置后,返回步骤 2。
注意
配置选择器是一个生成器,用于生成配置。因此,选择器的当前状态会被保存,当增强器调用 next
时,选择器会在停止的地方继续。
注意
每次训练代理模型时,都会通过 update_on_iteration_start
更新多目标算法。
多目标算法#
多目标算法用于将多目标值进行标量化。多目标算法接收标准化的目标值并返回一个单一值。然后将结果值(由运行历史编码器调用)用于训练代理模型。
警告
根据多目标算法的不同,每次训练代理模型时,运行历史编码器所需的值可能会有所不同。以 ParEGO 为例:每次采样新的配置时(参见配置选择器),目标权重都会更新。因此,标量化的值是不同的,采集最大化器可能会返回完全不同的配置。
运行历史记录#
运行历史记录保存了优化运行中的所有(未)评估试验。您可以使用运行历史记录获取(正在运行的)配置、(正在运行的)试验、特定配置的试验等。运行历史编码器遍历运行历史记录以接收用于代理模型的数据。以下代码展示了如何遍历运行历史记录:
smac = HPOFacade(...)
# Iterate over all trials
for trial_info, trial_value in smac.runhistory.items():
# Trial info
config = trial_info.config
instance = trial_info.instance
budget = trial_info.budget
seed = trial_info.seed
# Trial value
cost = trial_value.cost
time = trial_value.time
status = trial_value.status
starttime = trial_value.starttime
endtime = trial_value.endtime
additional_info = trial_value.additional_info
# Iterate over all configs
for config in smac.runhistory.get_configs():
# Get the cost of all trials of this config
average_cost = smac.runhistory.average_cost(config)
警告
每当有新的试验添加到运行历史记录时,增强器都会使用回调来更新最佳配置。
运行历史编码器#
运行历史编码器用于将运行历史记录数据编码成代理模型可以使用的格式。仅考虑状态为 considered_states
的试验和超时试验。使用 normalize_costs
函数(使用运行历史记录中的 objective_bounds
)对多目标值进行标量化。之后,标准化后的值由多目标算法进行处理。
回调#
回调提供了在贝叶斯优化循环之前、之中和之后轻松执行代码的能力。要添加回调,您必须继承 smac.Callback
并覆盖相应的方法(如果需要)。之后,您可以将回调传递给任何外观。
from smac import MultiFidelityFacade, Callback
class CustomCallback(Callback):
def on_start(self, smbo: SMBO) -> None:
pass
def on_end(self, smbo: SMBO) -> None:
pass
def on_iteration_start(self, smbo: SMBO) -> None:
pass
def on_iteration_end(self, smbo: SMBO, info: RunInfo, value: RunValue) -> bool | None:
# We just do a simple printing here
print(info, value)
smac = MultiFidelityFacade(
...
callbacks=[CustomCallback()]
)
smac.optimize()