PyTorch教程-19.2. 超参数优化 API

网友投稿 136 2024-01-25

在我们深入研究该方法之前,我们将首先讨论一个基本的代码结构,它使我们能够有效地实现各种 HPO 算法一般来说,这里考虑的所有 HPO 算法都需要实现两个决策原语,即搜索和调度首先,他们需要对新的超参数配置进行采样,这通常涉及对配置空间的某种搜索。

其次,对于每个配置,HPO 算法需要安排其评估并决定为其分配多少资源一旦我们开始评估配置,我们就会将其称为试用我们将这些决定映射到两个类,HPOSearcher和 HPOScheduler除此之外,我们还提供HPOTuner执行优化过程的类。

这种调度器和搜索器的概念也在流行的 HPO 库中实现,例如 Syne Tune (Salinas等人,2022 年)、Ray Tune (Liaw等人,2018 年)或 Optuna (Akiba等人,2019 年)。

import time from scipy import stats from d2l import torch as d2l 19.2.1搜寻器 下面我们定义一个搜索器的基类,通过函数提供一个新的候选配置sample_configuration。

实现此功能的一种简单方法是随机对配置进行统一采样,就像我们在 第 19.1 节中对随机搜索所做的那样更复杂的算法,例如贝叶斯优化,将根据先前试验的表现做出这些决定因此,随着时间的推移,这些算法能够对更有希望的候选人进行抽样。

我们添加该update 功能是为了更新以前试验的历史,然后可以利用它来改进我们的抽样分布class HPOSearcher(d2l.HyperParameters): #@save def sample_configuration() -> dict: raise NotImplementedError def update(self, config: dict, error: float,

additional_info=None): pass 以下代码显示了如何在此 API 中实现我们上一节中的随机搜索优化器作为一个轻微的扩展,我们允许用户通过 指定要评估的第一个配置 initial_config,而随后的配置是随机抽取的。

class RandomSearcher(HPOSearcher): #@save def __init__(self, config_space: dict, initial_config=None): self.save_hyperparameters() def sample_configuration(self) -> dict: if self.initial_config is not None: result = self.initial_config self.initial_config = None else: result = { name: domain.rvs() f

or name, domain in self.config_space.items() } return result 19.2.2调度程序 除了新试验的采样配置外,我们还需要决定何时进行试验以及进行多长时间。

实际上,所有这些决定都是由 完成的HPOScheduler,它将新配置的选择委托给HPOSearcher. suggest只要某些训练资源可用,就会调用该方法除了调用sample_configuration搜索器之外,它还可以决定诸如max_epochs(即训练模型的时间)之类的。

参数update每当试验返回新观察时调用该方法class HPOScheduler(d2l.HyperParameters): #@save def suggest(self) -> dict: raise NotImplementedError def update(self, config: dict, error: float, info=None): raise NotImplementedError 。

要实现随机搜索以及其他 HPO 算法,我们只需要一个基本的调度程序,它可以在每次新资源可用时调度新的配置class BasicScheduler(HPOScheduler): #@save def __init__(self, searcher: HPOSearcher): self.save_hyperparameters() def suggest(self) -> dict: return self.searcher.sample_configuration() def update(self, config: dict, error: float, info=None): self.searcher.update(config, error, additional_info=info) 。

19.2.3调谐器 最后,我们需要一个组件来运行调度器/搜索器并对结果进行一些簿记下面的代码实现了 HPO 试验的顺序执行,在下一个训练作业之后评估一个训练作业,并将作为一个基本示例我们稍后将使用 Syne Tune来处理更具可扩展性的分布式 HPO 案例。

class HPOTuner(d2l.HyperParameters): #@save def __init__(self, scheduler: HPOScheduler, objective: callable): self.save_hyperparameters() # Bookeeping results for plotting self.incumbent = None self.incumbent_error = None self.incumbent_trajectory = [] self.cumulative_runtime = [] self.current_runtime = 0 self.records = [] def run(self, number_of_trials): for i in range(number_of_trials): start_time = time.time() config = self.scheduler.suggest() print(f"Trial {i}: config = {config}") error = self.objective(**config) error = float(error.

cpu().detach().numpy()) self.scheduler.update(config, error) runtime = time.time() - start_time self.bookkeeping(config, error, runtime) print(f" error = {error}, runtime = {runtime}")

19.2.4簿记 HPO 算法的性能 对于任何 HPO 算法,我们最感兴趣的是性能最佳的配置(称为incumbent)及其在给定挂钟时间后的验证错误这就是我们跟踪runtime每次迭代的原因,其中包括运行评估的时间(调用 objective)和做出决策的时间(调用 scheduler.suggest)。

在续集中,我们将绘制 cumulative_runtimeagainstincumbent_trajectory以可视化根据( 和) 定义的 HPO 算法的任何时间性能这使我们不仅可以量化优化器找到的配置的工作情况,还可以量化优化器找到它的速度。

schedulersearcher@d2l.add_to_class(HPOTuner) #@save def bookkeeping(self, config: dict, error: float, runtime: float): self.records.append({"config": config, "error": error, "runtime": runtime}) # Check if the last hyperparameter configuration pe

rforms better # than the incumbent if self.incumbent is None or self.incumbent_error > error: self.incumbent = config self.incumbent_error = error # Add current best observed performance to the optimization trajectory self.incumbent_trajectory.append(self.incumbent_error) # Update runtime self.current_runtime += runtime self.cumulative_runtime.append(self.current_runtime)

19.2.5示例:优化卷积神经网络的超参数 我们现在使用随机搜索的新实现来优化 第 7.6 节中卷积神经网络的批量大小和学习率我们通过定义目标函数,这将再次成为验证错误LeNetdef hpo_objective_lenet(learning_rate, batch_size, max_epochs=10): #@save model = d2l.LeNet(lr=learning_rate, num_classes=10) trainer = d2l.HPOTrainer(max_epochs=max_epochs, num_。

gpus=1) data = d2l.FashionMNIST(batch_size=batch_size) model.apply_init([next(iter(data.get_dataloader(True)))[0]], d2l.init_cnn) trainer.fit(model=model, data=data) validation_error = trainer.validation_error() return validation_error

我们还需要定义配置空间此外,要评估的第一个配置是 第 7.6 节中使用的默认设置config_space = { "learning_rate": stats.loguniform(1e-2, 1), "batch_size": stats.randint(32, 256), } initial_config = { "learning_rate": 0.1, "batch_size": 128, } 。

现在我们可以开始随机搜索了:searcher = RandomSearcher(config_space, initial_config=initial_config) scheduler = BasicScheduler(searcher=searcher) tuner = HPOTuner(scheduler=scheduler, objective=hpo_objective_lenet) tuner.run(number_of_trials=5)

error = 0.17130666971206665, runtime = 125.33143877983093

下面我们绘制了现任者的优化轨迹,以获得随机搜索的任何时间性能:board = d2l.ProgressBoard(xlabel="time", ylabel="error") for time_stamp, error in zip( tuner.cumulative_runtime, tuner.incumbent_trajectory ): board.draw(time_stamp, error, "random search", every_n=1)

19.2.6. 比较 HPO 算法 正如训练算法或模型架构一样,了解如何最好地比较不同的 HPO 算法非常重要每次 HPO 运行取决于随机性的两个主要来源:训练过程的随机效应,例如随机权重初始化或小批量排序,以及 HPO 算法本身的内在随机性,例如随机搜索的随机抽样。

因此,在比较不同的算法时,至关重要的是多次运行每个实验并报告基于随机数生成器的不同种子的算法多次重复的总体统计数据,例如平均值或中值 为了说明这一点,我们比较随机搜索(参见第 19.1.2 节)和贝叶斯优化(Snoek等人,2012 年)在调整前馈神经网络的超参数方面的作用。

每个算法都经过评估50次使用不同的随机种子实线表示现任者在这些方面的平均表现 50重复和虚线标准偏差我们可以看到随机搜索和贝叶斯优化在大约 1000 秒内的表现大致相同,但贝叶斯优化可以利用过去的观察来识别更好的配置,从而在之后迅速超越随机搜索。

图 19.2.1示例任意时间性能图来比较两种算法 A 和 B 19.2.7概括 本节列出了一个简单而灵活的接口来实现我们将在本章中看到的各种 HPO 算法在流行的开源 HPO 框架中可以找到类似的接口。

我们还研究了如何比较 HPO 算法,以及需要注意的潜在陷阱 19.2.8练习 本练习的目标是为一个更具挑战性的 HPO 问题实现目标函数,并运行更真实的实验我们将使用第 5.6 节DropoutMLP 中实现的两个隐藏层 MLP 。

编写目标函数,它应该取决于模型的所有超参数和batch_size使用 max_epochs=50GPU 在这里无济于事,所以num_gpus=0. 提示:修改hpo_objective_lenet.。

选择一个合理的搜索空间,其中num_hiddens_1, num_hiddens_2是整数[8,1024], dropout 值位于[0,0.95], 而batch_size在于 [16,384]. 为 提供代码config_space,使用来自 的合理分布scipy.stats。

对此示例运行随机搜索number_of_trials=20并绘制结果确保首先评估第 5.6 节的默认配置,即 .initial_config = {num_hiddens_1: 256, num_hiddens_2: 256, dropout_1: 0.5, dropout_2: 0.5, lr: 0.1, batch_size: 256}。

在本练习中,您将实现一个新的搜索器( 的子类 HPOSearcher),它根据过去的数据做出决策这取决于参数probab_local, num_init_random它的 sample_configuration。

工作原理如下对于第一次 num_init_random调用,执行与 相同的操作 RandomSearcher.sample_configuration否则,以概率 ,执行与 相同的操作 否则,选择迄今为止达到最小验证错误的配置,随机选择其超参数之一,并像中一样随机采样其值,但保持所有其他值相同。

返回此配置,除了这个超参数外,它与迄今为止的最佳配置相同1 - probab_localRandomSearcher.sample_configurationRandomSearcher.sample_configuration。

编写这个新的LocalSearcher. 提示:您的搜索者需要 config_space作为构造参数随意使用 type 的成员RandomSearcher您还必须实施该update方法 重新运行上一个练习中的实验,但使用新的搜索器而不是RandomSearcher. 对,尝试不同的值probab_local。

num_init_random但是,请注意,不同 HPO 方法之间的适当比较需要多次重复实验,并且理想情况下要考虑许多基准任务 Discussions

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:使用vRealize Automation REST API置备虚拟机以进行软件开发
下一篇:现代异步存储访问API探索:libaio、io_uring和SPDK
相关文章

 发表评论

暂时没有评论,来抢沙发吧~