《设计深度学习系统》第五章:超参数优化服务

本章内容包括:

 超参数及其重要性

 两种常见的超参数优化方法(HPO)

 设计一个HPO服务

 三个流行的HPO库:Hyperopt、Optuna和Ray Tune

在前两章中,我们了解了模型的训练方式:一个训练服务在给定的模型算法下,管理远程计算集群中的训练过程。但是模型算法和训练服务并不是模型训练的全部内容。还有一个组成部分我们尚未讨论过,那就是超参数优化(HPO)。数据科学家通常忽视了超参数选择会对模型训练结果产生重大影响的事实,尤其是当这些决策可以通过工程方法自动化时。

超参数是在模型训练过程开始之前就需要设置的参数。学习率、批量大小和隐藏层数量都是超参数的示例。与模型参数的值(例如权重和偏置)不同,超参数在训练过程中无法学习。

在之前的两章中,我们看到了模型的训练方式:训练服务管理着远程计算集群中的训练过程,使用给定的模型算法。但模型算法和训练服务并不是模型训练的全部内容。还有一个组成部分我们尚未讨论过,那就是超参数优化(HPO)。数据科学家通常忽视了超参数选择会对模型训练结果产生重大影响的事实,尤其是当这些决策可以通过工程方法自动化时。

研究表明,超参数的选择值既会影响模型训练的质量,也会影响训练算法的时间和内存需求。因此,超参数必须进行调整,以实现模型训练的最优化。如今,超参数优化已经成为深度学习模型开发过程中的标准步骤。 作为深度学习的一个组成部分,超参数优化对软件工程师来说非常重要。这是因为超参数优化不需要对深度学习算法有深入的理解,因此通常由工程师负责这个任务。大部分情况下,超参数优化可以像一个黑盒子一样运行,训练代码不需要进行修改。此外,工程师有能力构建自动的超参数优化机制,使得超参数优化成为可能。由于有许多超参数需要调整(学习率、训练轮数、数据批量大小等),以及需要尝试许多不同的取值,手动调整每个超参数的取值是不现实的。软件工程师在微服务、分布式计算和资源管理方面拥有专业知识,非常适合创建自动化系统。

在本章中,我们将重点讨论自动化超参数优化的工程问题。我们首先介绍了理解超参数优化所需的背景信息。我们深入了解超参数以及调整或优化超参数的过程。我们还将介绍一些流行的超参数优化算法,并比较两种常见的自动化超参数优化方法:使用库和构建服务。 然后,我们将开始设计。我们将探讨如何设计一个超参数优化服务,包括创建超参数优化服务的五个设计原则,以及在此阶段特别重要的一个总体设计建议。最后,我们将向您展示三个受欢迎的开源超参数优化框架,如果您想在本地优化您的训练代码,它们将是一个完美的选择。

与之前的章节不同,本章中我们不会构建一个全新的示例服务。相反,我们建议您使用开源的Kubeflow Katib(在附录C中讨论过)。Katib是一个设计良好、可扩展和高度可移植的超参数优化服务,几乎可以用于任何超参数优化项目。因此,如果您认为这是一个低成本的选择,我们就不需要再构建一个新的。 本章将为您提供超参数优化领域的整体视角,同时为您提供如何根据您的具体需求运行超参数优化的实用理解。无论您决定使用远程服务还是在本地机器上使用像Hyperopt、Optuna或Ray Tune这样的库/框架运行超参数优化,我们都为您提供了支持。

理解超参数

在我们深入讨论如何调整超参数之前,让我们更清楚地了解一下什么是超参数以及它们为什么重要。

什么是超参数?

深度学习模型的训练过程使用了两种类型的参数或值:模型参数和超参数。模型参数是可训练的,也就是说它们的值会在模型训练过程中学习并随着模型的迭代而改变。相反,超参数是静态的;这些配置在训练开始之前就被定义和设置好。例如,我们可以将训练的轮数设置为30,将神经网络的激活函数设置为ReLU(修正线性单元)作为输入参数来启动模型训练过程。

换句话说,任何影响模型训练性能但无法从数据中估计的模型训练配置都是超参数。一个模型训练算法中可能有数百个超参数,包括例如模型优化器的选择(如ADAM或RMSprop)、神经网络中的层数、嵌入维度、小批量大小和学习率等。

为什么超参数很重要?

超参数的取值选择对模型训练结果有巨大影响。通常情况下,超参数是手动设置的,它们控制着训练算法的执行行为,决定了模型训练的速度和准确度。

为了亲自体验超参数对模型训练的影响,你可以在 TensorFlow Playground(playground.tensorflow.org)中尝试不同的超参数取值。在这个在线平台上,你可以设计自己的神经网络,并训练它来识别四种不同的图案。通过设置不同的超参数,比如学习率、正则化方法、激活函数、神经网络层数和神经元数等,你不仅可以观察到模型性能的变化,还可以看到学习过程中的行为,比如训练时间和学习曲线。如果想要训练一个能够识别复杂数据模式(比如螺旋形状)的模型,在这个平台上需要仔细选择超参数。例如,尝试将隐藏层设置为6层,每层神经元数设置为5,激活函数选择ReLU,数据批量大小设置为10,正则化方法选择L1。经过近500个训练周期后,你将看到模型能够对螺旋状图案进行准确分类预测。

在研究领域中,超参数选择对模型性能的影响已经有了很多研究成果。以自然语言处理中的嵌入训练为例,Levy等人的一篇论文《Improving Distributional Similarity with Lessons Learned from Word Embeddings》(aclanthology.org/Q15-1016.pd…)揭示了词嵌入的性能提升很大程度上是由于系统设计选择和超参数优化的结果,而不仅仅是嵌入算法本身的贡献。在自然语言处理中,这些作者发现超参数的选择对模型性能的影响比训练算法的选择更为重要!由于超参数选择对模型训练性能的重要性,超参数调优现在已成为模型训练过程中的标准步骤。

理解超参数优化

现在你已经对超参数是什么以及它们对模型训练的重要性有了清晰的了解,让我们转向为你的模型优化超参数的过程。在本节中,我们将向你介绍HPO的步骤,并探讨用于优化超参数的HPO算法,以及执行HPO的常见方法。

什么是HPO?

HPO(超参数调优)是发现一组使模型达到最佳状态的超参数的过程。这里的最佳指的是在给定数据集上最小化预定义损失函数的模型。在图5.1中,你可以看到HPO在模型训练过程中的一般工作流程的高级视图。

截屏2023-07-08 00.09.52.png

从图5.1中,我们可以看到HPO工作流程可以被视为由四个步骤组成的循环。它向我们展示了HPO过程是一个重复的模型训练过程,只是每次使用不同的超参数集合来训练神经网络。在这个过程中将发现最优的超参数集合。通常,我们将模型训练的每一次运行称为一次试验。整个HPO实验是一个试验循环,我们会一次又一次地运行试验,直到满足结束条件。

注意:为了进行公正的评估,每个HPO试验使用相同的数据集。

每个试验有四个步骤,如图5.1所示。第一步是使用一组超参数值训练神经网络。第二步是评估训练输出(模型)。

在第三步,HPO过程会检查是否满足结束条件,例如是否耗尽了试验预算,或者该试验产生的模型是否达到了我们的性能评估目标。如果试验结果满足结束条件,试验循环中断,实验结束。产生最佳模型评估结果的超参数值被视为最优超参数。

如果未满足结束条件,则进入第四步:HPO过程将生成一组新的超参数值,并通过触发模型训练运行一个新的试验。每个试验中使用的超参数值可以通过手动或自动的HPO算法生成。让我们在接下来的两个部分中更详细地看看这两种方法和HPO算法。

手动HPO

作为数据科学家,我们通常会手动选择超参数值来运行图5.1所示的HPO过程。尽管可以承认,手动选择最优超参数值更像是即兴表演而不是科学。但我们也会借鉴自己的经验和由经验带来的直觉。我们往往会从使用相关发表论文中的经验超参数值开始训练模型,然后进行一些小的调整并测试模型。经过几次试验后,我们会手动比较模型性能,并从这些试验中选择表现最好的模型。图5.2说明了这个工作流程。

截屏2023-07-08 00.11.16.png

手动HPO的最大问题是我们无法确定我们选择的超参数值是否是最优的,因为我们只是选择了一些经验值并进行微调。要获得最优值,我们需要尝试所有可能的超参数值,也就是搜索空间。在图5.2的例子中,我们希望优化两个超参数:学习率(learning rate)和数据批次大小(dataset batch size)。在HPO过程中,目标是找到能够产生最佳模型的batch_size和learning_rate的组合。假设我们为batch_size定义了一个搜索空间{8, 16, 32, 64, 128, 256},并为learning_rate定义了另一个搜索空间{0.1, 0.01, 0.001, 0.5, 0.05, 0.005}。那么我们需要验证的超参数值的总数是36个(6的平方)。

由于我们手动运行HPO,我们必须运行36次模型训练过程(HPO试验),并记录每次试验中的模型评估结果和使用的超参数值。在完成所有36次试验并比较结果(通常是模型准确性)之后,我们找到了最优的batch_size和learning_rate。

手动运行整个超参数搜索空间的HPO过程往往耗时、容易出错且繁琐,正如您所见。此外,深度学习超参数通常具有复杂的配置空间,通常由连续、分类和条件超参数的组合以及高维度组成。目前,深度学习行业正在向自动化HPO发展,因为手动HPO根本不可行。

自动化HPO是利用计算能力和算法自动找到训练代码的最佳超参数的过程。其思想是使用高效的搜索算法,在没有人为干预的情况下发现最佳超参数。

我们还希望自动化HPO以黑盒方式运行,即对它要优化的训练代码是无知的,因此我们可以轻松将现有的模型训练代码引入到HPO系统中。图5.3展示了自动化HPO的工作流程。

截屏2023-07-08 00.13.19.png

在第一步中,数据科学家将HPO请求提交给自动HPO系统,该系统以黑盒方式运行HPO过程(图5.3)。他们将要优化的超参数及其值搜索空间输入到黑盒(图5.3中的“自动HPO”方框)中,例如学习率的搜索空间可以是[0.005, 0.1],数据集批大小的搜索空间可以是{8, 16, 32, 64, 128, 256}。数据科学家还需要配置训练执行,如训练代码、评估方法、结束目标和试验预算,例如此实验的总试验次数为24次。

一旦用户提交了HPO请求,HPO实验(第二步)就开始了。HPO系统安排所有试验并管理其训练执行,同时运行HPO算法为每个试验生成超参数值(从搜索空间中选择值)。当试验预算用尽或达到训练目标时,系统会返回一组最佳超参数值(第三步)。

自动HPO依赖于两个关键组件:HPO算法和试验训练执行管理。通过使用高效的HPO算法,我们可以用较少的计算资源找到最佳超参数值。通过使用先进的训练管理系统,数据科学家可以在整个HPO过程中无需操作。

注意:由于手动HPO的低效性,自动HPO是主流方法。为了简洁起见,本章剩余部分中我们将使用术语“HPO”来指代“自动超参数优化”。

流行的HPO算法

大多数HPO算法可以分为三类:无模型优化、贝叶斯优化和多重精度优化。

注意:由于本章的主要目标是教授HPO工程,所以这里讨论的HPO算法将保持在较高的层面。本节的目标是为您提供足够的HPO算法背景知识,以便能够构建或设置一个HPO系统。如果您想了解算法背后的数学原理,请参阅《AutoML: Methods, Systems, Challenges》一书的第1章“Hyperparameter Optimization”(mng.bz/AlGx),以及Bergstra等人的论文“Algorithms for Hyper-Parameter Optimization”(mng.bz/Zo9A)。

无模型优化方法

在无模型方法中,数据科学家对训练代码不做任何假设,并忽略HPO试验之间的相关性。最常用的方法是网格搜索和随机搜索。

在网格搜索中,用户为每个超参数指定一组有限的值,然后从这些值的笛卡尔积中选择试验的超参数。例如,我们可以首先为学习率指定值集(搜索空间)为{0.1, 0.005, 0.001},数据批次大小为{10, 40, 100},然后用这些集合的笛卡尔积(作为网格值)构建网格,例如(0.1, 10),(0.1, 40)和(0.1, 100)。构建完成网格后,我们可以使用网格值开始HPO试验。

网格搜索在超参数数量较多或参数的搜索空间较大时存在问题,因为在这种情况下,所需的评估次数将呈指数增长。网格搜索的另一个问题是效率低下。因为网格搜索将每组超参数候选值视为平等,所以它会在非最优的配置空间中浪费大量计算资源,而没有足够的计算资源用于最优的空间。

随机搜索通过在超参数配置空间中随机采样,直到搜索的某个预算用完为止。例如,我们可以将学习率的搜索空间设置为[0.001, 0.1],数据批次大小的搜索空间设置为[10, 100],然后将搜索预算设置为100,这意味着将运行总共100个HPO试验。在每次试验中,从0.001到0.1之间随机选择一个值作为学习率,从10到100之间随机选择一个值作为数据批次大小。

与网格搜索相比,随机搜索具有两个优点。首先,随机搜索可以对每个超参数评估更多的值,从而增加找到最优超参数集的机会。其次,随机搜索具有更容易并行化的要求;因为所有的评估工作可以完全并行运行,它们不需要彼此通信,而且失败的工作不会在搜索空间中留下空白。但是在网格搜索中,一个失败的工作可能会跳过分配给该工作的试验超参数。

随机搜索的缺点是不确定性;在有限的计算预算内无法保证找到最优的超参数集。从理论上讲,如果我们允许足够的资源,随机搜索可以在搜索中添加足够的随机点,从而如预期地找到最优的超参数集。在实践中,随机搜索被用作基准方法。

截屏2023-07-08 00.17.50.png

图5.4说明了网格搜索和随机搜索之间的比较。网格搜索中的试验超参数候选值(黑点)是重要参数值(行)和不重要值点(列)的笛卡尔积。它们的分布可以看作是搜索空间(白色方形画布)中的网格。随机搜索算法从搜索空间中随机获取超参数候选值。当给定足够的搜索预算时,它的搜索点更有可能接近最优位置。

基于模型的贝叶斯优化

贝叶斯优化是一种用于全局优化昂贵黑盒函数的先进优化框架。它在各种问题设置中被广泛应用,如图像分类、语音识别和神经语言建模。

贝叶斯优化方法可以使用不同的采样器,例如高斯过程回归(参见Jie Wang的《Gaussian Processes Regression的直观教程》)和树状Parzen估计器方法(TPE),来计算搜索空间中的超参数候选值。简单地说,贝叶斯优化方法使用统计方法从过去试验中使用的值及其评估结果中计算新的超参数值建议。

为什么它被称为贝叶斯优化?贝叶斯分析是一种广泛使用的统计推断方法,以英国数学家托马斯·贝叶斯命名,它允许将关于总体参数的先验信息与样本中包含的信息证据相结合,以指导统计推断过程。基于这种方法,约纳斯·莫库斯在他在20世纪70年代和80年代关于全局优化的工作中引入了贝叶斯优化这个术语。

贝叶斯优化方法的概念是,如果算法能够从过去的试验中学习,那么寻找最优超参数集的效率将更高。实践中,贝叶斯优化方法可以在较少的评估运行(试验)中找到最优的超参数集,并且比其他搜索方法更稳定。图5.5显示了随机搜索和贝叶斯方法之间的数据采样差异。

截屏2023-07-08 00.19.51.png

让我们假设最优的超参数值为(x, y) = (0.5, 1),并尝试使用随机搜索和贝叶斯搜索来找到它。在图5.5(a)中,我们可以看到数据在搜索空间中进行了随机采样,其中x := [–1.0, 1.0],y := [1, 5]。在图5.5(b)中,我们可以看到数据在区域(x := [0.3, 0.7],y := [1, 1.5])中进行了大量采样,而这正是最优值所在的区域。这个比较表明,在给定的搜索空间中,贝叶斯搜索更有可能找到最优的超参数,并且在有限的执行预算下,经过每次实验后,所选的(采样的)超参数值会越来越接近最优值。

还有其他一些先进的HPO算法,如Hyperband、TPE和协方差矩阵适应进化策略(CMA-ES)。虽然它们的数学理论不完全与贝叶斯-高斯过程方法相同,但它们共享相同的超参数选择策略:通过考虑历史评估结果来计算下一个建议值。

多重保真度优化

多重保真度方法改进了无模型和贝叶斯优化方法的效率。如今,在大型数据集上调整超参数可能需要数小时甚至数天的时间。为了加速HPO,开发了多重保真度方法。使用这种方法,我们使用称为实际损失函数的低保真度近似值来最小化损失函数。因此,在HPO过程中可以跳过许多计算。

需要注意的是,在机器学习背景下,损失函数是评估训练算法对数据集建模效果的一种方法。如果模型输出(预测结果)与预期结果相差较大,损失函数应输出较高的值;否则,应输出较低的值。损失函数是ML算法开发的关键组成部分;损失函数的设计直接影响模型准确性。

尽管近似引入了优化性能和运行时间之间的权衡,但在实践中,速度提升通常超过近似误差。有关更多详细信息,请参考Matthias Feurer和Frank Hutter的《Hyperparameter Optimization》。

为什么类似贝叶斯的HPO算法有效?

Michael McCourt的博客文章《Gaussian Processes的直觉》提供了一个很好的解释,说明为什么类似贝叶斯的优化算法可以在不检查搜索空间中的每个可能值的情况下找到最优的超参数集。在某些情况下,我们观察到的实验是独立的,例如抛硬币50次;一个实验的结果并不能推断其他实验的结果。但是,幸运的是,许多情况具有更有用的结构,从中以往的观察结果可以为未观察到的结果提供见解。

在机器学习的背景下,我们假设历史实验(训练试验)结果与未来实验结果之间存在某种关系。更具体地说,我们认为存在一个数学模型来描述这种关系。尽管使用贝叶斯方法(例如高斯过程)来建模这种关系是一个非常强的假设,但我们可以获得可证明的最优预测能力。另一个好处是我们现在有一种处理模型预测结果的不确定性的方法。

需要注意的是,如果您对将贝叶斯优化应用于深度学习项目感兴趣,Quan Nguyen的书《Bayesian Optimization in Action》(Manning,2022)是一个很好的资源。

哪种HPO算法效果最好?

没有一种单一的HPO算法能够最好地适用于所有情况。不同的优化算法可能适用于不同的调整任务,具体取决于不同的约束条件。其中一些变量可能包括搜索空间的特征(例如超参数类型、值范围),试验预算的情况以及目标是最终优化还是任意时间的最优性能。图5.6展示了Optuna HPO框架中的HPO算法选择指南。

截屏2023-07-08 00.22.12.png

在图5.6中,我们可以看到一个决策图,用于确定何时使用以下三种HPO算法:高斯过程、TPE和CMA-ES。由于HPO是一个快速发展的领域,新的高效算法随时可能被发布,因此像这样的算法选择备忘单很快就会过时。例如,FLAML(github.com/microsoft/F…)是一个新开发的Python HPO库,在HPO过程中检查超参数之间的相关性,值得一试。因此,请与您的数据科学团队核实最新的HPO算法选择指南。

注意:HPO算法并不是HPO工程的主要关注点。HPO算法背后的数学可能会让人望而生畏,但幸运的是,这并不是工程师的重点。通常,确定在特定训练任务中使用哪种HPO算法是数据科学家的工作。作为工程师,我们的角色是构建一个灵活、可扩展的、以黑盒方式运行的HPO系统,以便数据科学家可以轻松地使用任意的HPO算法运行他们的模型训练代码。

常见的自动HPO方式

幸运的是,现在已经有许多成熟的框架和系统可用于进行HPO。根据使用方式,它们可以分为两种不同的类别:HPO库方法和HPO服务方法。图5.7说明了这两种方法。现在我们逐一讨论它们。

截屏2023-07-08 00.25.36.png

HPO库方法

在图5.7(a)中的库方法中,我们可以看到数据科学家自行管理HPO过程,从编码到执行。他们使用HPO库(如Hyperopt,一个开源的Python HPO库)编写整个HPO流程,并将其与训练代码集成到一个训练应用程序中。接下来,数据科学家在本地机器或直接访问的服务器上运行此应用程序。应用程序中的HPO库将执行我们在图5.3中看到的HPO工作流程。

库方法的最大优势在于灵活性和敏捷性。您可以选择任何您喜欢的HPO算法/库,将其集成到您的训练代码中,并立即启动HPO过程,因为所有的训练和超参数计算都发生在您的本地机器上。一些HPO库(例如Ray Tune,第5.4.3节)还支持并行分布式执行,但不是完全自动化的。这需要设置一个具有特定软件的分布式计算组,允许跨机器通信,并且需要手动在每台服务器上启动并行进程。

库方法面临的最大挑战是可扩展性、可重用性和稳定性。HPO需要大量计算资源来执行其试验,因此单个服务器通常无法胜任HPO的任务。即使具备分布式功能,它仍然无法进行横向扩展。想象一下,我们想要在20台服务器上运行一个需要进行10,000次试验的HPO任务;我们需要手动在20台服务器上设置HPO流程,并在每次训练或HPO代码更改时重新进行设置。此外,如果20个并行工作中的1个失败,整个HPO工作组将停止运行。为了解决这些问题,引入了HPO服务方法。

HPO服务方法

现在让我们更详细地看看HPO服务方法;我们在这里重复使用图5.7,以图5.8的形式呈现。在图5.8(b)中,即服务方法,我们可以看到HPO是在由一个服务管理的远程计算集群中进行的。数据科学家只需提供训练代码和选定的HPO算法配置给该服务,并启动HPO作业。该服务管理计算资源分配和HPO工作流程(图5.3)的执行;它跟踪每个试验的结果(模型性能指标,如准确率),并在所有试验完成时将最终的最佳超参数返回给数据科学家。

截屏2023-07-08 00.32.03.png

服务方法提供了真正的黑盒体验。数据科学家不需要担心管理自己的服务器、设置试验工作程序以及学习如何修改训练代码以适应不同的HPO算法。HPO服务会处理所有这些任务。作为HPO服务的用户,我们只需将参数传递给服务,然后服务会自动运行HPO并在最后返回最佳超参数。该服务还负责自动扩展和处理试验作业失败的恢复。由于这些优势,服务方法现在是深度学习生产环境中主流的HPO方法。既然您已经熟悉了HPO的概念和方法,接下来让我们看一下如何设计一个HPO服务以及如何在下两节中使用HPO库。

注意,HPO不是一次性的工作。如果使用不同的数据集进行训练,即使模型架构没有改变,您也需要重新进行HPO。如果数据集发生变化,适应给定数据的最佳模型权重也会发生变化,因此需要进行新的HPO搜索。

设计一个HPO服务

既然您对HPO库方法有了很好的了解,那么让我们来回顾一下HPO服务方法。在本节中,我们将介绍如何设计一个HPO服务,以支持自动和黑盒式的任意模型训练的HPO过程。

HPO设计原则

在我们看具体的设计方案之前,让我们首先了解构建HPO服务的五个设计原则。

原则1:训练代码无关性

HPO服务需要对训练代码和模型训练框架保持无关性。除了支持诸如TensorFlow、PyTorch和MPI等任意机器学习框架外,我们希望该服务能够调整任何编程语言编写的训练代码的超参数。

原则2:支持不同HPO算法的可扩展性和一致性

从5.2.2节中的HPO算法讨论中,我们知道超参数搜索算法是HPO过程的核心。超参数搜索的效率决定了HPO的性能。一个好的HPO算法可以在较少的试验次数中找到大量超参数和任意搜索空间下的最优超参数。

由于HPO算法研究是一个活跃的领域,每隔几个月就会发布一个新的有效算法。我们的HPO服务需要与这些新算法轻松集成,并将它们作为算法选项提供给客户(数据科学家)。此外,新添加的算法在用户体验方面应与现有算法保持一致。

原则3:可扩展性和容错性

除了HPO算法之外,HPO服务的另一个重要职责是管理用于HPO的计算资源,即使用各种超参数值进行模型训练。从HPO实验的角度来看,我们希望在实验级别和试验级别进行分布式执行。具体而言,我们希望不仅可以以分布式和并行的方式运行试验,还可以以分布式方式运行单个训练试验,例如在一个试验中运行分布式训练。从资源利用的角度来看,系统需要支持自动扩展,即允许计算集群的大小根据当前工作负载自动调整,以确保资源的合理利用,避免资源的过度或不足利用。 容错性也是HPO试验执行管理的另一个重要方面。容错性很重要,因为某些HPO算法要求按顺序执行试验。例如,试验2必须在试验1之后进行,因为算法需要过去的超参数值和结果来推断下一个试验开始之前的超参数。在这种情况下,如果一个试验意外失败,例如由于节点重启或网络问题,整个HPO过程将失败。系统应自动从之前的故障中恢复。常见的做法是记录每个试验的最新状态,这样我们就可以从最后记录的检查点恢复。

原则4:多租户性

HPO过程本质上是一组模型训练执行。与模型训练类似,HPO服务必须为各个用户或组提供资源隔离,以确保不同用户的活动保持在各自的边界内。

原则5:可移植性

如今,“云中立”(cloud neutral)的概念变得非常流行。人们希望在不同的环境中运行其模型训练作业,如亚马逊网络服务(Amazon Web Services)、谷歌云平台(Google Cloud Platform)和Azure,因此我们构建的HPO服务需要与底层基础设施解耦。在这里,将HPO服务运行在Kubernetes上是一个很好的选择。

一个通用的HPO服务设计

由于HPO工作流程(图5.3)相当标准且变化不大,HPO服务系统设计(图5.9)可以适用于大多数HPO场景。它由三个主要组件组成:API接口、HPO作业管理器和超参数(HP)建议生成器(在图5.9中分别标记为A、B和C)。

API接口(组件A)是用户提交HPO作业的入口点。要启动一个HPO实验,用户通过接口提交API请求(步骤1),请求提供模型训练代码(例如Docker镜像)、超参数及其搜索空间,以及HPO算法。

超参数建议生成器(组件C)是不同HPO算法的包装器/适配器。它为用户提供了一个统一的接口,用于运行不同的HPO算法,因此用户可以选择算法而不必担心执行细节。

截屏2023-07-08 00.38.14.png

要添加新的HPO算法,必须在这个建议生成器组件中注册,以成为用户的算法选项。

HPO作业管理器(组件B)是HPO服务的核心组件;它负责管理客户请求的HPO实验。对于每个HPO请求,作业管理器启动一个HPO试验循环(步骤2)。在循环中,它首先调用超参数建议生成器来获取一组建议的超参数值(步骤2.a),然后创建一个试验来使用这些超参数值进行模型训练(步骤2.b和2.c)。

对于每个训练试验,HPO作业管理器创建一个试验对象。该对象具有两个责任:首先,它收集试验执行的输出,如训练进度、模型指标、模型准确度和尝试的超参数;其次,它管理训练过程。它处理训练过程的启动、分布式训练设置和故障恢复。

HPO服务端到端的工作流

让我们按照图5.9所示的端到端用户工作流程来进行介绍。为了方便起见,我们在这里重复显示图5.9,作为图5.10。 首先,用户向API接口提交HPO请求(步骤1)。请求定义了训练代码、一组超参数及其值搜索空间、训练目标和一个HPO算法。然后,HPO作业管理器为该请求启动一个HPO试验循环(步骤2)。该循环启动一组试验来确定哪组超参数值效果最好。最后,当试验预算用完或满足训练目标时,试验循环中断,最佳超参数被返回(步骤3)。

截屏2023-07-08 00.39.59.png

在试验循环内部,作业管理器首先查询HP建议生成器以推荐超参数候选值(步骤2.a)。建议生成器将运行所选的HPO算法来计算一组超参数值,并将其返回给作业管理器(步骤2.b)。然后,作业管理器创建一个试验对象,使用建议的超参数值启动模型训练过程(步骤2.c)。试验对象还将监视训练过程,并继续将训练指标报告给试验历史数据库,直到训练完成(步骤2.d)。当作业管理器注意到当前试验已完成时,它提取试验历史(试验指标和过去试验中使用的超参数值),并将其传递给HP建议生成器以获得一组新的HP候选值(步骤2.e)。

由于HPO的用例非常标准和通用,并且已经有多个开源HPO项目可以直接使用,我们认为学习如何使用它们比重新构建一个没有附加价值的新系统更好。因此,在附录C中,我们将向您介绍一个强大且高度可移植的基于Kubernetes的HPO服务——Kubeflow Katib。

开源HPO库

对于小型数据科学团队来说,HPO服务可能会显得过于繁琐,尤其是如果他们的所有模型都是在他们自己管理的几台服务器上训练的。在这种情况下,使用HPO库来优化在本地机器或受管理的集群(小规模,1-10台服务器)上的模型训练是一个更好的选择。

在本节中,我们将介绍三个有用的开源HPO库:Optuna、Hyperopt和Ray Tune。它们都作为HPO库运行,易于学习和使用。由于Optuna、Hyperopt和Ray Tune都有清晰的入门文档和适当的示例,我们将重点介绍它们的概述和特性,以便您根据自己的情况决定使用哪个库。

在关于不同HPO库的讨论中,特别是在“如何使用”部分,您会经常看到“目标函数”这个术语。什么是目标函数?图5.11演示了这个过程。

截屏2023-07-08 00.41.38.png

对于HPO算法(例如贝叶斯搜索)来生成超参数建议,以便下一个试验效果更好,它需要了解先前的HPO试验的表现如何。因此,HPO算法要求我们定义一个函数来对每个训练试验进行评分,并在随后的试验中继续最小化或最大化函数的返回值(评分)。我们将这个函数称为目标函数。

在图5.11中,我们看到目标函数接收超参数作为输入,并返回一个浮点值,即评分。目标函数使用给定的超参数执行模型训练,并在训练完成时评估输出模型。

Hyperopt

Hyperopt是一个轻量级且易于使用的Python库,用于串行和并行的HPO(hyperopt.github.io/hyperopt/#g…)。Hyperopt实现了随机搜索、TPE和自适应TPE这三种HPO算法。基于高斯过程的贝叶斯优化算法和回归树也已经设计出来,但在本书编写时尚未实现。

怎么使用

使用Hyperopt来确定在你的深度学习案例中哪种分类器效果最好,可以通过三个步骤来获取答案。

首先,我们创建一个目标函数,它基本上是实际训练代码的一个包装函数,但是从args变量中读取超参数的值。第二,我们为选定的超参数定义搜索空间。第三,我们选择一个HPO算法,该算法从搜索空间中选择超参数值,并将它们传递给目标函数,启动优化过程。代码清单5.1实现了这个场景。

在这个示例中,我们想确定哪种分类器可以得到最佳的模型准确率,所以我们选择在三个候选项中优化classifier_type超参数:naive_bayes、svm和dtree。你可能还注意到,每个分类器都有自己的值搜索空间,例如svm分类器的hp.lognormal(‘svm_rbf_width’, 0, 1)。在fmin函数(第3步)中,我们指定TPE作为HPO算法,设置最大试验次数为10,并将目标函数和搜索空间作为必需参数传递进去。

# Step 1: define an objective function
def objective(args):
    model = train(args)
    return evaluate(model)
    
# Step 2 define search space for hyperparameters 
space = hp.choice('classifier_type', [
       {
          'type': 'naive_bayes',
       }, 
       {
          'type': 'svm',
          'C': hp.lognormal('svm_C', 0, 1),
          'kernel': hp.choice('svm_kernel', [
                    {'ktype': 'linear'},
                    {'ktype': 'RBF',
                     'width': hp.lognormal('svm_rbf_width', 0, 1)}, ]),
       },
       {
          'type': 'dtree',
          'criterion': hp.choice('dtree_criterion',['gini', 'entropy']),
          'max_depth': hp.choice('dtree_max_depth',[None, hp.qlognormal('dtree_max_depth_int', 3, 1, 1)]),
          'min_samples_split': hp.qlognormal('dtree_min_samples_split', 2, 1, 1),
       }
   ])
   
# Step 3 start the hpo process execution
     best = fmin(objective, space, algo=tpe.suggest,max_evals=100)
       

并行化

虽然Hyperopt是一个独立的库,但我们可以在机群中并行运行它。基本思路是在不同的机器上运行Hyperopt工作节点,并让它们与一个中央数据库进行协调。Hyperopt还可以利用Spark计算来实现HPO的并行化。你可以参考以下两篇文章获取更多详细信息:“On Using Hyperopt: Advanced Machine Learning”(链接:mng.bz/PxwR)和“Scaling Out Search with Apache Spark”(链接:hyperopt.github.io/hyperopt/sc…)。

何时使用

Hyperopt是小型或早期阶段模型训练项目的一个不错选择。首先,它易于使用。您可以在本地机器上或具有直接访问权限的服务器上通过三个步骤运行HPO。其次,它易于修改。由于采用库的方法,HPO代码与训练代码放置在同一个代码项目中。因此,尝试不同的优化方案,例如选择不同的要调整的超参数,非常方便。

Optuna

与Hyperopt类似,Optuna也是一个轻量级的Python库,旨在自动化超参数搜索。它支持对大空间进行搜索,并能够在不修改代码的情况下对不太有希望的试验进行早期修剪,还可以在多个线程或进程上进行并行化。

在我们看来,Optuna是Hyperopt的一个高级版本,它的可视化能力更好。通过检查图形中参数之间的交互作用,在超参数搜索中的可视化能够给您带来很多见解,因此您可以轻松确定哪些参数比其他参数更有效。Optuna的可视化效果美观且交互性强。

Optuna在文档方面也比Hyperopt具有优势。Optuna的文档非常出色。除了详细的API文档和组织良好的教程外,它还有维护良好的源代码。如果您查看其GitHub项目的问题部分,您会发现一个非常活跃和壮大的社区,并且仍有许多出色的功能和GitHub拉取请求即将推出。

怎么使用

在Listing 5.2中展示了如何使用Optuna的快速三步骤示例:第一步,定义目标函数;第二步,创建一个Study对象来代表HPO过程;第三步,使用最大试验次数配额启动HPO过程。

与Hyperopt相比,Optuna要求大部分HPO逻辑在目标函数中定义。一般的代码模式如下所示。首先,定义搜索空间,并通过trial.suggest_xxx函数生成超参数值。接下来,使用采样的超参数值开始模型训练。然后运行评估方法计算模型性能并返回目标值。在下面的示例中,评估分数是通过mean_squared_error计算的。您可以在github.com/optuna/optu…找到更多Optuna示例。

# Step 1: define an objective function
def objective(trial):
    regressor_name = trial.suggest_categorical( 'classifier'['SVR', 'RandomForest'])
    
if regressor_name == 'SVR': svr_c = trial.suggest_float('svr_c', 1e-10, 1e10, log=True)
   regressor_obj = sklearn.svm.SVR(C=svr_c)
else:
   rf_max_depth = trial.suggest_int('rf_max_depth', 2, 32)      
   regressor_obj = sklearn.ensemble.RandomForestRegressor(max_depth=rf_max_depth)
X_train, X_val, y_train, y_val = \ sklearn.model_selection.train_test_split(X, y, random_state=0)

regressor_obj.fit(X_train, y_train)
y_pred = regressor_obj.predict(X_val)
error = sklearn.metrics
  .mean_squared_error(y_val, y_pred)

return error

# Step 2: Set up HPO by creating a new study.
study = optuna.create_study()

# Step 3: Invoke HPO process
study.optimize(objective, n_trials=100)

并行化

我们可以使用Optuna在一台机器或机器集群上运行分布式HPO。分布式执行设置非常简单,可以分为三个步骤:首先,启动一个关系型数据库服务器,如MySQL;第二,创建一个带有storage参数的study;第三,在多个节点和进程之间共享study。与Hyperopt相比,Optuna的分布式执行设置更简单,并且可以在不修改代码的情况下从单台机器扩展到多台机器。

何时使用

Optuna可以被看作是Hyperopt的继任者;它拥有更好的文档、可视化和并行执行能力。对于任何可以在一台或多台机器上运行的深度学习模型训练项目,您可以使用Optuna来寻找最优的超参数。

Optuna在支持大型数据科学团队或多个HPO项目时会受到限制,因为它需要管理一个中央机器集群来提供计算资源。但是Optuna的并行/分布式执行是手动的;用户需要将代码分发到每台服务器并逐个服务器手动执行。为了以自动化和编程方式管理分布式计算作业,我们可以使用Kubeflow Katib(附录C)或Ray Tune。

Ray Tune

Ray(docs.ray.io/en/latest/i…)提供了一个简单的、通用的API来构建分布式应用程序。Ray Tune(docs.ray.io/en/latest/t…)是建立在Ray之上的用于任意规模的HPO的Python库。Ray Tune库支持几乎所有的机器学习框架,包括PyTorch、XGBoost、MXNet和Keras。它还支持最先进的HPO算法,如Population Based Training(PBT)、BayesOptSearch和HyperBand/ASHA。此外,Tune提供了一种机制,可以集成来自其他HPO库的HPO算法,例如与Hyperopt的集成。 通过使用Ray作为其分布式执行支持,我们可以用几行代码启动一个多节点的HPO实验。Ray会处理代码分发、分布式计算管理和容错性。

怎么使用

使用Ray Tune执行HPO任务非常简单。首先,定义一个目标函数。在该函数中,从config变量中读取超参数的值,开始模型训练,并返回评估分数。其次,定义超参数及其值的搜索空间。然后,通过将目标函数和搜索空间关联起来启动HPO执行。下面的代码清单展示了上述三个步骤的实现。

# Step 1: define objective_function
def objective_function(config): 
   model = ConvNet()
   model.to(device)
   
   optimizer = optim.SGD( model.parameters(), lr=config["lr"],momentum=config["momentum"])

   for i in range(10):
       train(model, optimizer, train_loader)
       acc = test(model, test_loader)
       tune.report(mean_accuracy=acc)
       
# Step 2: define search space for each hyperparameter
search_space = {
   "lr": tune.sample_from(lambda spec:
      10**(-10 * np.random.rand())),
   "momentum": tune.uniform(0.1, 0.9) 
 }
 
# Uncomment this to enable distributed execution
# `ray.init(address="auto")`

# Step 3: start the HPO execution
analysis = tune.run(
  objective_function,
  num_samples=20, 
  scheduler=ASHAScheduler(metric="mean_accuracy", mode="max"),  
  config=search_space)

# check HPO progress and result
# obtain a trial dataframe from all run trials
# of this `tune.run` call.
dfs = analysis.trial_dataframes

你可能会注意到,在第三步中,tune.run函数中传递了一个scheduler对象,即ASHAScheduler。ASHA(参考链接:mng.bz/JlwZ)是一种可扩展的早停算法,用于有原则的提前终止(参考文献:“Massively Parallel Hyperparameter Optimization” by Liam Li; 链接:mng.bz/wPZ5)。在高层次上,ASHA会终止那些表现较差的试验,并将时间和资源分配给更有潜力的试验。通过适当调整参数num_samples,搜索过程可以更加高效,并支持更大的搜索空间。

并行性

相比于Optuna,Ray Tune的最大优势是分布式执行能力。Ray Tune允许您在多个GPU和多个节点上进行透明的并行化操作(参考Ray文档:mng.bz/qdRx)。Tune甚至提供了无缝的容错和云支持。与Optuna和Hyperopt不同,我们无需手动设置分布式环境并逐台执行工作节点脚本。Ray Tune会自动处理这些步骤。图5.12展示了Ray Tune如何将HPO的Python代码分发到一组机器集群中。

截屏2023-07-08 01.13.35.png

首先,我们使用命令 “ray up tune-cluster.yaml” 来设置一个Ray集群;tune-cluster.yaml 是一个声明集群计算资源的集群配置文件。然后,我们运行以下命令将HPO代码从本地机器提交到集群的主节点:”raysubmittune-cluster.yamltune_script.py–start—-ray-address={server_address}”。接下来,Ray会分配资源,将HPO代码复制到服务器上,并开始分布式执行。更多详细信息,请参考“Tune Distributed Experiments”(mng.bz/71QQ。除了分布式HPO执行,Ray Tune还支持单次试验的分布式训练、自动检查点管理和TensorBoard日志记录。这些功能为Ray Tune增加了很大的价值,具有高容错性和简单的故障排除能力。

何时使用

与其他HPO库相比,Ray Tune是否是进行HPO的首选方法呢?暂时而言,是的。在本书编写时,Ray提供了底层训练框架(如TensorFlow和PyTorch)与先进的HPO算法(如贝叶斯搜索和TPE)以及提前停止(ASHA)之间的集成。它使我们能够以简单而可靠的方式分布式运行HPO搜索。

对于大多数不想拥有自己的HPO服务的数据科学团队来说,Ray Tune是推荐的方法。它易于使用,并满足几乎所有模型训练项目的HPO需求:出色的文档、先进的HPO算法以及高效而简单的分布式执行管理。

注意:我们推荐使用Ray Tune而不是其他HPO库,有以下五个原因:(1)它使用简单;(2)它有出色的文档和示例;(3)它的分布式执行是自动化和编程化的;(4)Ray Tune支持单次试验的分布式训练;(5)Ray Tune具有调度器功能(例如ASHAScheduler),可以通过提前终止不太有希望的试验来大大降低计算成本。

RAY TUNE的局限性

当我们需要在一个共享的HPO系统中支持不同团队和不同深度学习项目时,Ray Tune和其他HPO库会受到限制。Ray Tune缺乏计算隔离,这导致了两个重大问题。

首先,不同训练代码的软件包版本可能会导致Ray worker之间的冲突。在Ray Tune中进行分布式HPO时,我们将HPO代码提交到Ray集群的头节点服务器,然后在集群的worker节点中并行运行此代码。这意味着每个Ray worker服务器都需要安装每个训练代码所需的依赖库。想象一下,当您需要在一个Ray集群中运行10个不同的HPO任务时,如何管理软件包的安装和潜在的版本冲突;worker机器需要为这10个不同的训练代码安装数百个软件包,并解决它们之间的版本冲突。

其次,Ray Tune不强制用户隔离。在Ray Tune中建立不同数据科学团队之间的虚拟边界以限制其计算资源使用是非常困难的。

下一步

当您遇到上述的HPO库问题时,就是时候转向使用HPO服务了。我们强烈建议在考虑构建自己的HPO系统之前阅读附录C。它介绍了一个可靠的开源HPO服务,名为Kubeflow Katib,它是一个设计良好的通用HPO服务。

总结

  • 超参数是用于控制学习过程的参数。这种类型的参数在模型训练中无法学习,因此我们需要进行调优。
  • HPO 是一种发现能够使预定义损失函数在给定数据集上最小化的一组超参数的过程,以得到最优模型的过程。
  • 自动 HPO 是利用计算资源和算法(HPO 算法)自动寻找训练代码的最优超参数的过程。
  • 自动 HPO 现在已成为模型训练的标准步骤。
  • 大多数 HPO 算法可以分为三大类:无模型优化、贝叶斯优化和多层次优化。
  • 没有单一的最佳 HPO 算法。在不同的约束条件下,不同的优化算法可能适用于不同的 HPO 任务。
  • HPO 可以使用库或远程服务运行。库方法简单灵活,适用于小团队和原型阶段的项目,而服务方法适用于大型组织和生产用例。
  • HPO 服务方法提供了完全自动化的黑盒 HPO 体验,包括计算资源管理;因此,我们建议如果您正在为大型团队构建深度学习系统,则采用服务方法。
  • 构建 HPO 服务的五个设计原则是:训练代码无关性、高可扩展性、高可伸缩性和可靠性、HPO 执行和资源消耗隔离性,以及高可移植性。
  • 为了加快 HPO 实验,我们可以并行化不同试验的训练执行,引入分布式训练,并提前终止无望的试验。
  • 我们鼓励您选择 Kubeflow Katib 作为您的 HPO 服务,而不是自己构建新的服务。
  • 在三个常用的开源 HPO 库中,Optuma、Hyperopt 和 Ray Tune 中,Ray Tune 目前被证明是最好的。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYPX00aN' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片