本篇文章为https://github.com/google-research/tuning_playbook的翻译。
Deep Larning Tuning Playbook:深度学习调参指南
- 为什么会有调参指南?
- 指南:从一个新项目开始
-
- 选择模型架构
- 选择优化器
- 选择batch size
-
- 确定batch size和预估训练吞吐量
- 选择一个最小化训练时间的batch size
- 选择一个最小化资源消耗的batch size
- 改变batch size意味着要调整大部分超参数
- batch norm与batch size
- 选择初始配置
- 提升模型表现的科学方法
-
- 增量调整策略
- exploration vs exploitation
- 选择下一轮实验的目的
- 设计下一轮实验
-
- 区分三种超参数
- 创建一系列研究
- 实验中收获与支持的平衡
- 从实验结果中获得见解
-
- 分辨搜索空间边界的好坏
- 搜索空间是否有足够采样点
- 检验训练曲线
- 使用isolation plots检测改动是否有用
- 自动化通用绘图
- 决定是否在pipeline应用一个改变或者超参数设置
- after exploration concludes
- 决定每次训练的步数
-
- 当训练是不受限时,决定用多长时间训练
-
- 算法:学习率扫描以选择max_train_steps的初始值
- 当训练是受限的,决定用多长时间训练
-
- 对训练pipeline的一些额外的建议
-
- 优化输入pipeline
- 评估模型性能
-
- 保存检查点并回溯选择最优
- 设置实验追踪
- BN的实现细节
- 对多主机pipeline的考虑
为什么会有调参指南?
为了让深度神经网络的表现变好,在实际操作中有大量的重复性工作和没有根据的尝试。更糟糕的是,一些能帮助使用者取得好结果的方式,都是没有文本记载总结的。论文中通常也不会特别清楚的描述它们取得最终效果的过程和处理方式,而致力于解决商业性问题的工程师们也很少有时间进行复盘。教科书更重视基础原理而不是实操上的指导,即使它们的作者本身非常有经验并且能给出有用的建议。
当开始准备这个指南的时候,作者们还没有找到试图解释“how to get good results with deep learning”这个问题同类作品,相反地,他们只能从各个博客、社交媒体、论文的角落里找到一些混乱的零碎的片段。深度学习专家取得的结果和水平较低的使用相似方法的从业者取得的结果上有着巨大的差异。而且有时候,专家们采用的一些方法也是没有理论支持的。
随着深度学习技术越来越成熟,并且对整个世界开始产生更大的影响,我们需要更多的有用的指南方法,来帮助获得更好的结果。
这篇指南的作者们都有着多年的深度学习经验,最早的甚至是从06年就开始从事这一行,他们使用深度学习来解决各种各样的问题。这篇指南来自于他们自己的训练经验和对同事们提供的建议,并希望这个文档能帮助到别的人。
这篇指南的核心是作者们的调参经验,此外也包括了一些他们在工作中遇到的问题。并且这篇指南之后也会更新。
指南:从一个新项目开始
很多调参相关的决策,都是在项目开始时做好的,并且只会在情况发生改变时考虑重新调整。
作者们的指南是建立在以下假设上的:
- 你已经完成了基础的类似数据处理这样的工作。如果这些工作都没做好,那你执着于模型结构和参数是没有意义的。
- 这里已经有一个设计好的用于训练和测试的pipeline,这样你在不同模型上进行训练和预测的过程才简单省时。
- 你已经选好并实现了一个合适的指标,用于衡量在部署环境中表现。
选择模型架构
Summary: when starting a new project, try to reuse a model that already works.
当开始一个新项目时,尽量复用一个已经效果不错的模型。
- 选择一个完善的通用的模型结构来展开工作。你可以在之后再构建你的custome model。
- 通常会有一些超参数决定模型的大小和其他细节(比如层数,宽度,激活函数)
- 因此,选择一个模型架构意味着选择了一个系列的不同的模型(每个都有着不同的超参数配置)
- 在后面的部分会介绍如何选择超参数。
- 如果可能的话,尽量找一篇解决了相似问题的论文,并尝试复现其中的模型,作为你的任务的起点。
选择优化器
Summary: start with the most popular optimizer for the type of problem at hand.
优先选择当前类型的任务中最流行的优化器。
选择batch size
Summary: the batch size governs the training speed and shouldn’t be used to directly tune the validation set performance. often, the ideal batch size wll be the largest batch size supported by the available hardware.
你的batch size决定了训练的速度,而且batch size是不应该被用来调整验证集的表现得。通常大家会选择硬件能支持的最大的batch size。
- batch size决定你的训练时间和计算资源消耗的关键因素。
- 增大batch size可以减少训练时间,它能带来很多好处,比如:
- 在给定时间里,你有更多的机会调整你的超参数,从而得到一个更好的结果。
- 减少开发周期,让你能尝试新idea。
- 增大batch size可能会减少、增加、或者不改变资源消耗。
- 对于验证集来说,batch size不是一个可以调整的参数。
- 只要所有超参数都经过良好调整(尤其是学习率和regularizetion)并且训练步数足够,使用任何batch size都应该可以获得相同的最终性能。
- Why shouldn’t the batch size be tuned to directly imporve validation set performance
确定batch size和预估训练吞吐量
- 对于一个给定的模型和优化器,可选的batch size有一大把,限制因素是你的可用的内存。
- 不幸的是,不运行或者不编译完成的训练程序,是很难直接计算batch size的大小是。
- 最简单的方法是使用不同的batch size训练比较小的步数,每次都将btach size扩大两倍,直到其中一次尝试超出了内存占用。
- 对于每一个batch size,我们需要训练足够长的时间才能得出一个比较可靠的训练吞吐。
t r a i n i n g t h r o u g h p u t = ( # e x a m p l e s p r o c e s s e d p e r s e c o n d e ) o r , e q u i v a l e n t l y , t h e t i m e p e r s t e p . t i m e p e r s t e p = ( b a t c h s i z e ) / ( t r a i n i n g t h r o u g h p u t ) \begin{aligned} training throughput = (\# examples processed per seconde) \\ or, equivalently , the time per step.\\ time per step = (batch size)/(training throughput) \end{aligned} trainingthroughput=(#examplesprocessedperseconde)or,equivalently,thetimeperstep.timeperstep=(batchsize)/(trainingthroughput)
- 在资源不饱和的情况下,batch size翻倍,那你的训练吞吐量也会翻倍(至少接近翻倍)。即使batch size增长,time per step也是稳定的(接近稳定的)。
- 如果实际情况和上述不符合,说明这里有一个I/O或者计算节点同步的瓶颈,你需要诊断并解决问题。
- 当吞吐量达到最大时,我们不要再增加batch size,即使我们的硬件还能支持。
- 所有使用更大的batch size带来的好处,都有一个前提假设是你的训练吞吐量能增加。如果没有的话,要么解决一下问题,要么使用小一点的batch size。
- 梯度累积模拟了更大的batch size的情况,因此不能带来吞吐量上的提升。你应该避免这种操作。
- 每次模型或优化器改变,你可能都需要重复这个步骤。
选择一个最小化训练时间的batch size
t r a i n i n g t i m e = ( t i m e p e r s t e p ) × ( t o t a l n u m b e r o f s t e p s ) training time = (time per step) \times (total number of steps) trainingtime=(timeperstep)×(totalnumberofsteps)
- 不管使用多大的batch size,time per steps 都是稳定的(在没有并行计算的开销和训练瓶颈的情况下)。实际上,增加batch size一般都会带来一点开销。
- 随着batch size增加,你所需要的训练的步数是下降的(别的超参数在改变batch size后也进行了re-tuned的情况下)。
- 比如说,将batch size翻倍后你需要的训练步数变成了之前的一半,这被称为 perfect scaling。
- 在batch size超过某个值后,perfect scaling不会再带来这样明显的效果。
- 最终,持续增加batch size不会再带来训练步数的减少,但肯定也不会让步数增加。
- 因此,能最小化训练时间的batch size指的是能带来步数减少的优点的最大的batch size。
- 这个batch size取决于你的数据集,模型和优化器。
- 当比较batch size时,你还要考虑epoch budget和step budget的差异。
- 硬件能支持的最大的batch size通常会比临界batch size小。所以尽可能使用最大的batch size就好了。
选择一个最小化资源消耗的batch size
-
增大batch size会带来两种资源损耗:
- upfront costs. purchasing new hardware or rewriting the training pipeline to implement multi-GPU / multi-TPU training.
- usage costs. billing against the team’s resource budgets, billing from a cloud provider, electricity / maintenance costs.
-
假如说增大batch size带来了明显的unfront costs,最好先去整改你的项目流程。使用一个多主机并行的训练流程可能会带来很多问题,所以最后从一个简单的pipeline开始。
-
作者把usage costs称为“resource consumption"。
r e s o u r c e _ c o n s u m p t i o n = ( r e s o u r c e _ c o n s u m p t i o n _ p e r _ s t e p ) × ( t o t a l _ n u m b e r _ o f _ s t e p s ) resource\_consumption = (resource\_consumption\_per\_step)\times(total\_number\_of\_steps) resource_consumption=(resource_consumption_per_step)×(total_number_of_steps)
-
增大batch size会带来number of steps的下降。resource consumption的变化则取决于每一个step中的consumption是怎么改变的。
- 增大batch size可能会减少resource consumption。比如说,每个拥有更大的batch size的训练步骤可以再同一个硬件上完成,那么训练步数下降的程度是比每一步的consumption增加量要多的。
- 增大batch size可能不会改变resource consumption。比如说,将batchsize翻倍后需要的训练步数变成了之前的一半,如果你这是和增加gpu的数量,那么整体的consumption是不变的。
- 增大batch size可能会增加resource consumption。比如说,如果增加batch size要求你更新硬件,那么每一步的consumption都会比较大。
改变batch size意味着要调整大部分超参数
- 超参数的最优值对batch size是很敏感的。因此每次batch size的改变都意味着你的调参之路要重新开始了。
- 有些超参数受batch size影响比较大,因此对于每个batch size都要分开调整,比如说优化器的超参数。
- **在项目开始前决定好你的batch size。**如果你之后想换一个别的batch size,那会是十分费时费力的。
batch norm与batch size
可以直接看:batch norm section
后面的章节中也会对这部分进行翻译。
选择初始配置
- 在开始调参之前,我们必须要决定开始的点。这包括了:
- 模型配置,比如说网络的层数。
- 优化器超参,比如说learning rate。
- 训练步数。
- 决定初始配置需要你手动进行一些训练和试错。
- 我们的原则是找到一个简单,快速,低消耗的配置,并获得一个reasonable的结果。
- 简单意味着避免花里胡哨的操作。 这些操作啥的你可以在之后再添加进入。虽然有些花里胡哨会带来比较好的效果,但是把它们加到初始配置里面容易造成时间的浪费或一些不必要的麻烦。
- 比如说,使用一个固定的学习率而不是采用一些学习率衰减机制。
- 选择快速和低消耗的配置可以让调参过程更高效。
- “reasonable” 的表现由你要解决的问题而定,但至少你的表现要比在验证集上随机选择要好。
- 选择训练步数时要考虑到下面几个因素:
- 一方面,训练更多的步数可以提高表现,并让调参更容易。
- 另一方吧,训练更少的步数意味着训练过程更快、使用更少的资源、允许更多的尝试。如果最开始选了很大的step,后面调整起来也会比较麻烦。
提升模型表现的科学方法
就这个指南来说,机器学习发展的最终目的是最大化的利用模型。尽管很多应用的开发过程都不尽相同(比如不同的时间,不同的计算资源,不同的模型),单通常都可以使用一样的基本步骤和原理来解决问题。
指南做出了以下两个假设:
- 这里已经有一个完整的能取得reasonable结果的训练pipeline和初始配置。
- 这里有足够的资源来进行调参的任务,甚至可以并行进行多组任务。
增量调整策略
Summary: start with a simple configuration and incrementally make improvements while building up insight into the problem. Maske sure that any improvement is based on strong evidence to avoid adding unnecessary complexity.
从一个简单的配置开始,循序改进地获得提升同时增加你对这个问题的理解。要确定每一步提升都要基于一个强有力的证据来避免增加不必要的复杂性。
- 我们的最终目的是找到最大化模型表现的配置。
- 有时候我们会有一个固定的deadline。
- 有时候我们能希望能持续地进行模型的提升。
- 在原则上,我们希望能够使用一个算法来自动地搜索可能的配置空间,从而最大化模型的表现,但这是很不切合实际的。
- 配置空间可能会非常大,没有一个算法能够在不需要人工介入的情况下高效地完成搜索。
- 大部分自动搜索算法都依赖于手动设置的搜索空间,并且搜索空间的设置对结果影响很大。
- 最有效的方法时从一个简单的配置开始,并循序渐进地增加新特性。
- 作者会在每轮调参中使用自动搜索方法, 并且会随着对任务的理解加深来更新这个搜索空间。
- 因此作者们总能找到越来越好的配置,所以他们的最好的模型能够持续性地更新。
- 作者们把每次更新最好配置称为一次上线(launch)。
- 每一个launch,都要保证做出的改变是有强有力的证据制成的,而不是随机选择的配置,并且没有带来额外的复杂性。
宏观来讲,增量地调参策略是在重复进行以下四个步骤:
- 为下一轮试验选择一个目标。
- 设计并进行一些试验来向目标靠近。
- 从结果中学习。
- 考虑是否launch一个新配置。
接下来的部分会带来一些更细节的解释。
exploration vs exploitation
Summary: most of the time, our primary goal is to gain insight into the problem.
大部分情况下,我们的主要目标都是提升对当前问题的理解。
- 可能别人会认为作者们大部分时间都是用来尝试提升模型表现的,实际上,他们的时间大多用在了提升对问题的理解,相对的比较少的时间被用在关注验证集结果的错误上。
- 换句话说,他们的时间大多用在exploration,只有一小部分用在exploitation。
- 从长远来看,理解一个问题是非常重要的,它应该被放到比短期提升更高的优先级上。因为它能帮助我们:
- 避免推出一些偶然事件带来的良好表现。
- 分辨验证集错误对哪些超参数更敏感,哪些超参数需要一起进行调整,哪些超参数对别的变化不敏感因此可以在以后的实验中保持固定。
- 提示接下来能使用的新特性。比如说当存在过拟合是,可以考虑使用新的regularizers。
- 分辨哪些特性是没有帮助的可以被移除的,减少未来实验的复杂度。
- 确认是否调参带来的提升已经饱和。
- 收缩调参的搜索空间,提高调参效率。
- 当我们准备好的时候,我们可以开始专注于验证集的错误。
选择下一轮实验的目的
Summary: Each round of experiments should have a clear goal and be sufficiently narrow in scope that the experiments can actually make progress towards the goal.
每一轮实验都要有一个明确的范围比较小的目标,并且你的实验能够向这个目标结果靠近。
- 如果我们一次增加多个新特效或者解决多个问题,我们可能分不出来是哪一个部分在影响这个结果。
- 样例目标包括:
- 尝试一个pipeline上的可能的提升。
- 理解一个模型超参数的影响。
- 减少验证集错误。
设计下一轮实验
Summary: Identify which hyperparameters are scientific, nuisance, and fixed hyperparameters for the experimental goal. Create a sequence of studies to compare different values of the scientific hyperparameters while optimizing over the nuisance hyperparameters. Choose the search space of nuisance hyperparameters to balance resource costs with scientific value.
要区分清楚超参数的种类,对于当前的实验目标来说,哪些超参数是科学的,哪些是干扰的,哪些是可以固定的。进行一系列的研究来比较科学的超参数的不同的值,同时优化干扰的超参数。对干扰超参数的搜索空间进行选择,以平衡资源成本与科学价值。
区分三种超参数
- 对于一个给定的目标,所有的超参数可以分为scientific,nuisance和fixed三类。
- scientific 科学超参数是我们需要衡量的会影响模型表现的超参数。
- nuisance干扰超参数是需要被优化的从而能公平地进行科学超参数的比较的值。 给出一个wiki的解释nuisance parameters
- fixed固定超参数是可以在本轮训练过程中被值被固定的参数。在进行科学超参数的比较时,它们的值不需要被改变。
- 在固定这种超参数时,我们需要接受在这种配置下得到的实验结果不一定能扩展到别的配置上。
- 如果我们的目标是“确定一个有更多隐藏层的模型会不会表现更好” ,那么隐层的数量就是我们的科学超参数。
- 学习率是一个干扰超参数,因为对于拥有不同数量的隐藏层的神经网络,学习率是需要调整的。
- 激活函数可以是一个固定超参数。如果我们确定了激活函数对模型深度不敏感,或者把我们的结论范围限制在指定激活函数上。如果我们打算对不同数量的隐藏层都进行激活函数的调参的话,它也可以是一个干扰超参数。
- 一个超参数属于什么类型,和超参数本身无关,而是取决于你的实验目的。
- 比如说,激活函数的选择可以是一个科学超参数(ReLU和tanh哪一个是更好的选择呢?),也可以是一个干扰超参数(当我们使用不同的激活函数时,五层的模型会比六层的好吗?)也可以是一个固定超参数(BN放在哪个位置才会有用?)。
- 当我们设计一个新实验时,我们首先要确定当前目标下的科学超参数。
- 接下来,我们把一些干扰超参数改成固定超参数。
- 在资源无限的情况下,我们可以把剩下所有的超参数都当作干扰超参数来用,所以我们得出的结论不会被某些固定超参数限制。
- 然而,干扰超参数越多,我们把它调整好的难度就越大,并且可能得到错误的实验结论。
- 我们选择把一些干扰超参数转为固定超参数,如果我们认为这个固定这个超参数带来的潜在问题比把它作为干扰超参数带来的消耗要更容易接受时。
- 干扰超参数和科学超参数交互比较大时,固定它会带来比较多的损失。比如说权重衰减的系数受模型大小影响比较大,所以比较不同大小的模型却使用固定的衰减系数是不合理的。
- 虽然我们分配给每个超参数的类型取决于实验目的,我们也可以遵循下面的分类方法。
-
优化器的超参数是需要作为干扰超参数的,因为它们会和大部分改变做交互。
- 它们很少作为科学超参数使用,因为类似于“当前pipeline中最好的学习率”的问题并对我们的任务没有什么帮助。
- 尽管在资源受限或者我们有强有力的证据支持它们并不和科学超参数进行交互时,我们可以固定它们。我们通常还是要认为对于不同的配置,优化器的超参数都需要进行调整,它们不应该是固定的。
- 此外我们没有理由优先选择优化器的超参数值而不是另一个更有用的值,毕竟它们完全不会影响前向过程的损失或梯度。
-
相反地,优化器的选择通常是一个科学超参数或者一个固定超参数。
- 如果我们的实验目标是在不同的优化器上进行比较的话,它就是一个科学超参数。
- 我们也会出于某些原因考虑,把它作为一个固定超参数。1. 之前的实验让我们确信我们用的优化器对现在的科学超参数的变化是不敏感的。2. 我们想使用这个优化器来对比科学超参数的值的效果。3. 我们想用这个优化器因为它比别的占的内存小。
-
规范化regularization引入的超参数通常是干扰超参数,但我们是否使用这个方法则是科学超参数或固定超参数。
- 比如说,dropout来带了代码的复杂度,所以我们决定是否使用它会带来两个科学超参数“ no dropout”和“dropout”,而dropout rate则是一个干扰超参数。
- 如果我们决定基于本次实验在pipeline中加入dropout,那么它会成为未来的实验的干扰超参数。
-
模型架构超参数通常是科学超参数或者固定超参数,因为结构的改变会带来很多影响。
- 在一些情况下,干扰超参数和固定超参数将取决于科学超参数的值。
- 比如说,假设我们要决定Nesterov momentum和adam哪个优化器会带来更少的验证集错误。那我们的科学超参数就是optimizer,包含 {’Nesterov momentum‘,’adam’}。而这个科学超参数又带来了干扰/固定超参数,比如Nesterov momentum带来了learning rate,momentum,而adam带来了learning rate,beta1, beta2, epsilon。
- 特定科学超参数带来的超参数又被称为条件超参数。
- 我们不能假设两个条件超参数是一样的即使它们有一样的名字。
创建一系列研究
- 当我们确定好了超参数的类别,我们可以设置一个或者一系列的研究来向我们的目标靠齐。
- 一个研究明确了一组要用于后续分析的超参数的配置。每个配置称为一个trial。
- 创建一个研究包括:选择不同的超参数,选择超参数的搜索空间,选择trials的数量,选择自动搜索算法。当然我们也可以手动决定参数配置。
- 研究的目的是使用不同的科学超参数的值来运行pipeline,同时优化所有的干扰超参数,这样不同的科学超参数之间的比较才是公平的。
- 在最简单情况下,我们对科学超参数的每一个配置进行研究,每个研究都会进行干扰超参数的调整。
- 比如说,如果我们的目标是选择最好的优化器a和b,我们可以使用a进行一次研究,再使用b进行一次研究。对于每个研究选择最好的trial进行比较。
- 我们可以使用各种方法来优化干扰超参数。
- 在比较复杂的例子中,我们可能想要比较一大堆科学超参数,而对每个参数都进行独立的研究是不太实际的。我们可以把科学超参数包括在和干扰超参数一样的解空间里,并且使用搜索算法进行在一次研究中进行采样。
- 使用这个方法时,条件超参数可能会带来一些问题,可能很难确定它的解空间。除非干扰超参数在所有科学超参数的值下都相同。
- 在这种情况下,作者们倾向于使用准随机搜索。因为它能够保证我们获得相对均匀的采样结果。不管使用什么搜索算法,都要保证科学超参数是被均匀地搜索的。
实验中收获与支持的平衡
- 当设计一个研究或者一系列研究时,我们需要对有限的预算进行分配,来实现以下三个小目标:
- 比较足够数量的科学超参数的值。
- 在比较大的搜索空间中进行干扰超参数的调整。
- 对干扰超参数在搜索空间中进行足够的采样。
- 我们实现的越好,我们能从实验中得到的对任务的理解也就越多。
- 比较尽可能多的科学超参数值可以扩大我们从实验中获得的见解的范围。
- 增加尽可能多的干扰超参数能够允许每个干扰超参数在比较大的范围内变化, 从而增强我们对获得的结果的自信。(好的结果对应的点被包在了我们的范围内)
- 否则的话,我们对科学超参数做的比较可能是不公平的,因为搜索区域中没有对科学超参数比较友好的干扰超参数的值。
- 尽可能地从搜索空间进行多次采样也能增加我们对于当前结果的信心。(好的结果对应的点被我们成功搜索到)
- 然而,实现三个小目标都要求我们要么增加trials的次数,要么增加资源损耗,或者寻找一个节省资源的方法。
- 资源分配问题要求更多的知识储备去优化解决。
- 每次完成一个研究,作者们都会做一个评估:当前的研究是否对干扰超参数进行了比较好的调整。在下面一节会有详细介绍。
从实验结果中获得见解
Summary: in addition to trying to achieve the original scientific goal fo each group of experiments, go through a checklist of additional questions and, if issues are discovered, revise the experiment and rerun them.
在实现每组实验的原始科学目标外,也需要检查一个记录了额外问题的清单,如果发现问题,就修改实验并重新运行。
- 每组实验都有一个目标,我们希望评估面向该目标的实验给出的证据。
- 然而,如果我们提出了正确的问题,我们经常会发现一些在取得目标前必须纠正的问题。
- 如果我们做实验不是基于问题上,那么可能会得出错误的结论。
- 进行实验可能是昂贵的,我们希望在每组实验中获得有用的简介,即使这个见解和当前的目标并不相关。
- 在分析一组实验向目标靠近之前,我们需要问一些额外的问题:
- 搜索空间足够大吗?
- 如果最优解靠近搜索空间的边缘,那么这个搜索是不够大的,我们需要进行新的实验并使用更大的搜索空间。
- 采样的次数足够多吗?
- 每次研究中有多少trials是不可行的?(不收敛的,loss很糟糕的,难以运行的)
- 当有比较多的trials是不可行的时,就需要调整解空间来避免这些点,有时需要解空间的重参数化。
- 在一些情况下,很多的trials失败意味着你的训练代码中存在bug。
- 模型存在优化问题吗?
- 我们能从最好的trials的训练曲线上学到什么?
- 如果有必要,可以基于对上面这些问题的回答,调整最近的研究,来提高你的搜索空间或者进行更多的trials,或一些别的正确的行动。
- 当我们成功回答了以上问题,就可以来评估我们的实验面向目标给出的证据了(比如说,evaluating whether a change is useful)。
分辨搜索空间边界的好坏
- 如果获得的最好的采样点靠近搜索空间的边界,那么这个搜索空间是不可靠的。如果我们沿着边界方向进行搜索空间的扩展,大概率能找到一个更好的点。
- 为了检查我们的搜索空间的边界,我们要绘制一个名为 “基本超参数轴图 basic hyperparameter axis plots” 的图来描绘我们的验证集结果和某个超参数之间的关系。图上的每个点都对应了一个trial。
- 每个trial的验证集结果通常是在一次训练中它能取得的最好的值。
- 图中展示了验证集上错误率和初始learning rate的关系。
- 如果最好的点的聚集区在搜索空间的边界,那么这个搜索空间的边界可能是需要被扩展的,直到最好的点不再靠近边界。
- 一次研究中也会包含一些不可行的trials(要么不收敛要么结果很差)。
- 如果超过某个阈值的learning rate对应的所有的trials都是不可行的,或者表现最好的点在边界区域, 那么这个模型可能存在一些稳定性的问题让它不能达到更高的学习率。
搜索空间是否有足够采样点
- 通常情况下,你是很难确认在这个搜索空间中的采样是否足够密集。
- 进行更多的trials毫无意外是个好办法,但是也会带来明显的开销。
- 因为很难知道我们是不是采了足够多的点,所以我们通常在能力范围内采样 sample what we can afford,并且通过重复观察超参数轴图并尝试建立对搜索空间内良好点的数量的认识,来增强我们的信心。
检验训练曲线
Summary: examing the training curves is an easy way to identify common failure modes and can help up prioritize what actions to task next.
对训练曲线的检查是一个帮助我们分辨常见错误模型的简单方法,并能帮助我们确定下一步行动的优先级。
- 虽然在大部分情况下,我们实验的主要目标著需要我们考虑每次trial的验证集误差,当但我们的trial减少到某个比较小的数量级时我们更需要小心,因为它可能会隐藏一些重要的信息。
- 每一次研究,我们都需要关注至少最好的几次trials的训练曲线 training curves ,它记录了训练误差和验证误差随着训练步数的变化。
- 虽然对于达成实验目标来说是不必要,检查训练曲线仍是一个帮我们分辨常见错误模式的简单方法,并能帮我们确定下一步行动的优先级。
- 当我们检查训练曲线的时候,我们对以下几步比较感兴趣:
- 是否有trials中存在过拟合?
- 训练过程中,如果验证集误差开始增长,我们认为出现了过拟合。
- 对于每一个科学超参数的设置,我们都选取结果最好的trial,以优化干扰超参数, 我们应该检查至少每个科学超参数设置相对应的最佳试验中是否存在过拟合的问题。
- 如果某个最好的trial中存在过拟合的问题,我们通常想要重新跑这个实验,并加上额外的regularization方法或者调整已有的regularization参数。
- 如果科学超参数包括了regularization参数的话,这个方法就不适用了。因为这种情况下出现过拟合是很正常的,这只是我们对比实验中low-strength setting。
- 通常使用常见的regularization方法来直接减少过拟合,它只会带来比较少的代码负责度和额外的计算量,比如dropout,label smoothing,weight decay。所以在下一次实验中增加一个regularization并不是啥大事。
- 比如说,这个科学超参数是“隐藏层的个数”并且使用了最大的值的最好的trial存在过拟合问题,那我们通常更容易加一个regularization后再次重复训练,而不是直接选择一个层数比较小的结果。
- 即使所有最好的trials都没有出现过拟合,但如果在别的实验中出现过,那可能也是存在问题的。
- 选择最佳试验会抑制出现过拟合问题的配置,并偏向那些不会出现过拟合问题的配置。换句话说,它将有利于具有更多regularization的配置。
- 然而,任何一个让训练变差的因素都可以作为一个regularization因素来看。比如说,选择一个更小的学习率可以通过阻碍优化的过程来达到对训练的规范化,但我们一般不会这么做。
- 我们要存在这种意识:某个科学超参数设置得到的最优的trial,可能会偏向于另一些科学超参数或者干扰超参数的坏值。
- 训练后期的训练误差和验证误差上是否有比较大的逐步方差?
- 如果有的话,它可能会干扰我们对科学超参数的不同取值的比较,并且会影响我们对最优结果的复现,因为你的每个trial只是随机的停在了幸运或者不幸的一步。
- 最有可能导致这种问题的原因是batch variance(随机采样),small validation sets和使用一个比较高的learning rate。
- 比较好的补救方法包括增大batch size,或者更多的验证集或者使用学习率衰减方法。
- 在训练结束的时候效果是否还在提升?
- 如果是的话, 说明我们还可以增加训练的步数来获得更好的结果,或者修改学习率机制。
- ** 训练集和验证集上的表现是否早就饱和了?**
- 总之就是检查训练曲线能帮助我们看出很多问题,比如说训练过程中训练损失在增长,说明你的pipeline里存在bug。
使用isolation plots检测改动是否有用
- 通常情况下,一组实验的目的是对一个科学超参数的不同取值进行比较。
- 比如说我们想要确定能带来最好的验证集结果的weight decay的值。
- 一个isolation plot是一种特殊的基础超参数轴图。每个在isolation plot上的点都对应了一些干扰超参数的最优trial的表现。
- 换句话说,我们绘制的是干扰超参数被优化后的模型表现。
- 一个isolation plot帮助我们在不同科学超参数的取值上进行同类比较。
- 比如说,图二代表了weight decay的值和它对应的最优表现。
- 如果我们的目的是决定是否使用weight decay,那我们需要比较这个图上最好的点和不使用weight decay的baseline结果。为了公平, 这个baseline的learning rate也需要调参过。
- 当我们拥有近乎随机搜索生成的数据,并考虑isolation plot的连续超参数时,我们可以通过对基本超参数轴图的 x 轴值进行分桶,并且选择每个桶里的垂直切片上最好的trial,来近似地得到我们的isolation plot。
自动化通用绘图
- 你在画图上费事越多,你越不想去看它,因此你需要尽可能建立一个自动化生成工具。
- 至少,你需要自动化生成基础超参数轴图。
- 此外,也需要对每一个trials自动绘制训练曲线, 并找到其中最好的几个trials和检查它们的曲线。
- 这里也有很多别的有用的可视化操作可以添加进去。
决定是否在pipeline应用一个改变或者超参数设置
Summary: when deciding whether to make a change to our model or training procedure or adopt a new hyperparameter configuration going forward, we need to be aware of the different sources of variation in our result
【这部分下面的差异,在原文中都是variance,感觉翻译成方差不太合适】
在决定是否在我们的训练中应用某个变化或者采用一个新的超参数设置时,我们需要对实验结果的变化原因有一个认知。
- 当我们想要去提升模型的能力,我们可能有一堆能带来更好的效果的备选改动,但在经过一些重复实验后我们又发现没有并不是每次都变好。我们把造成这种不稳定结果的变化的原因归类为以下几类:
- 训练阶段差异,重训练茶差异或者trial差异:训练中使用一样的参数但是不一样的随机种子导致的差异。
- 比如说不一样的随机初始化,数据洗牌,dropout,数据增强操作,甚至是并行计算操作的顺序等,都可能导致这种差异。
- 超参数搜索差异或者研究差异: 我们搜索超参数的过程导致的结果上的差异。
- 比如说,我们可能在一个特定的搜索空间跑一样的实验,但是在随机搜索上用的不同的种子,选择了不同的超参数值。
- 数据收集和采样差异:数据随机划分的顺序或者数据生成过程导致的差异。
- 使用严格的统计测试对有限验证集上估计的验证错误率进行比较是很好的,但通常情况下,使用同样超参数配置的两个不同的模型就能有比较大的trial差异。
- 在考虑超参数空间的多个点相关的结论时,我们会更关注研究差异。
- 研究差异取决于trials的数量和搜索空间。有时候研究差异会比trial差异大,有时候会小。
- 因此,在决定采用一个改动前,考虑重跑多次最好的trial来对trial差异进行表征。
- 通常, 我们只需要在pipeline上有比较大的改动时才需要重新表征。
- 在别的应用中,表征trial差异的开销太大,不值得这么做。
- 尽快我们只是想要应用一些确实有用的新变化(比如说新的超参数设置),我们也需要知道一些东西有用并不代表它就是正确的。
- 如果一个新的超参数点比baseline取得了更好的结果,我们应该把它加进去并作为新的baseline。
- 然而,这个前提是,它带来的好处比它带来的复杂度要多。
after exploration concludes
Summary: Bayesian optimization tools are a compelling option once we’re done exploring for good search spaces and have decided what hyperparameters even should be tuned at all.
在我们完成了对搜索空间的探索并决定了应该调整哪些超参数后,贝叶斯优化工具是一个不错的方案。
- 有时候我们的优先级将从学习更多的调参来取得最好的配置转向launch或者别的使用方式。
- 这里需要有一个精细化的搜索空间,它需要包括最优点附近的区域,并经过了合理的采样得到。
- 我们的探索工作已经揭示了那些需要调参的基本超参数和它们的对应范围,我们可以用来构建一个搜索空间,以使用尽可能大的调参预算进行最终的自动调参研究。
- 当我们不再需要获得更多的见解,准随机化搜索的优点我们也用不上,贝叶斯优化工具可以被用来自动寻找最优的超参数配置。
- open-source Vizier中实现了一些算法帮助调参,就包括了贝叶斯优化工具。
- 如果搜索空间有很多离散点,可以使用黑和优化攻击来解决这个离散问题。Vizier可以将不收敛的trials标记为infeasible。
- 到这时,我们也需要考虑在测试集上的效果。
- 原则上来说,我们可以将验证集折叠到训练集中,并用贝叶斯优化找到的最佳配置重新训练。然而,只有在未来不会发布具有这种特定工作负载的版本(例如Kaggle 竞赛)时,这才适用。
决定每次训练的步数
- 存在两种类型的工作:一种是计算受限compute-bound的,一种不是。
- 当你的训练是计算受限的时,训练受到我们愿意的等待时间的限制,而不是数据的多少或者别的因素。
- 如果我们想要训练地久一点或者更高效一点,我们的训练loss就会更低;如果我们调参调的好,验证loss也会有改善。
- 换句话说,“加速”训练相当于“提升”训练,“最优”的训练时间是我们能承受的最长的训练时间。
- 一种工作是计算受限的并不意味着训练更久/更快是唯一一个提升结果的方法。
- 当你的训练是不受限的,我们能训练足够久直到某个临界点,超过这个点后训练得更久也不会有帮助。
- 在这种情况下,我们应该期望得到一个非常低的训练损失, 并到达一个临界点,超过这个点后,你的训练loss还可能继续降低,但你的验证集loss不会再降低。
- 尤其在你的训练是不受限的时候,充足的训练时间能让调参更简单,尤其是在调整学习率衰减机制相关的参数时。
- 换句话说, 一个比较紧张的训练时间需要你将学习率衰减相关的参数调的非常好,才能取得比较不错的效果。
- 不管任务是计算受限的还是不受限的,增大梯度方法的方法都会带来更低效的训练,因此增加了你所需要的训练步数。这些方法包括:
- 使用小batch size
- 增加数据增强方法
- 使用一些regularization
当训练是不受限时,决定用多长时间训练
- 我们的主要目的是保证我们训练的时间足够成,来使得模型能达到一个可能的最好的结果,同时也要避免浪费资源。
- 如果使用了恰当的checkpoint筛选方法并且checkpoint保存的频率也足够,那么训练地久也永远不会带来模型表现上的退化。
- 永远不要在一次研究中调整
max_train_steps
这个数。选择一个数,并在所有的trials中都使用这个数。在这些trials,通过对checkpoint选择点的绘制来帮助找到max_train_steps
的更合适的值。
- 比如说,如果最好的step总是在训练的10%,那么说明你训练最大值的设定太高了。
- 如果最好的步数总是在最后25%,则说明训练的更久可能带来好处,并且需要重新调整衰减机制。
- 当你的数据或者模型结构改变的时候,是可以对训练步数进行调整的。
- 如何使用恒定学习率来选择一个
max_train_steps
的初始值?
- 接下来我们会介绍如何根据使用恒定学习率“完美拟合”训练集所需的步数来选择 max_train_steps 的初始候选值。
- 我们并没有以精确或数学上明确定义的方式使用“完美拟合训练集”这一短语。它只是作为一个非正式的描述符来表示非常低的训练损失。
- 比如说,当使用log loss进行训练并且不带regularization项时, 我们可以看到我们的训练损失一直在缓慢改善,直到达到浮点限制,而我们的模型的权重是不受限的增长的,在训练集上的预测结果也越来越可信。在这种情况下,我们会说我们说模型在训练集上的误分类误差达到零时“完美拟合”了训练集。
- 如果在训练过程中梯度噪声增加了,那么需要增大
max_train_steps
。
- 比如说使用了新的augmentation或者dropout。
- 如果训练过程被改进了,那么需要减少
max_train_steps
。
算法:学习率扫描以选择max_train_steps的初始值
- 此过程假设不仅可以“完美”地拟合训练集,而且可以使用恒定的学习率来实现。
- 如果完美拟合整个训练集是可行的,那这里必须存在一个包括了
max_train_steps
相关参数的配置。找到这个配置,并使用对应的max_train_steps
值作为起点。
- 在没有数据增强和regularization的情况下进行一个学习率扫描,每个trial训练N次。
- 在学习率扫描中,最快的trials对应的步数是作为
max_train_steps
的初始值。
- 注意: 坏的搜索空间可能带来欺骗性的结果。
- 比如说,如果所有的学习率都很小,那我们可能得到一个很大的
max_train_steps
。
- 我们要确定最优学习率不在搜索空间的边界上。
当训练是受限的,决定用多长时间训练
- 在某些情况下,训练损失是一直会改善的,而我们的耐心和计算资源成为了限制它的因素。
- 如果训练loss,甚至是验证loss一直在改善,我们应该尽可能训练足够久吗?答案是不必要的。
- 我们可以进行更多更短的实验来高效地调参。并为我们希望launch的模型保存最长的“production length”。
- 随着训练时间逐渐增加到达了我们的容忍上限,调参实验对我们潜在的推出候选变得更加相关,但我们可以完成的实验更少。
- 我们可以只训练10%的生产长度,但是这样会带来风险,就是我们在这种情况下得到的结论不一定能用在更长的生产长度上。
- 使用训练步数限制每轮递增的多轮调参方法是一个不错的选择。
- 我们可以做很多轮训练,但通常1-3轮是比较实际的。
- Essentially, try to obtain as much understanding of the problem as possible using trials with a very quick turnaround time, trading off tuning thoroughness with relevance to the final, longest runs.
- 假如一个给定的训练时间限制给我们带来了有用的见解,我们可以增加训练时间并继续调参,重复验证我们的结论。
- 作为起点,我们建议进行两轮调参。
- 第一轮:更快的训练来找到模型和优化器的参数。
- 第二轮:比较长的训练来找到比较好的超参数。
- 最大的问题是从roun i到round i+1的过程中如何调整你的学习率衰减机制。
- 调整学习率机制的一个常见险境是在多出来的训练步数中使用了很小的学习率。
第一轮
- 在短的、不完整的训练中找到的超参数不一定在训练时长增加后仍然好用,不过也有一些超参数和第一轮结果的相关性足够强。
- 哪些超参数我们希望可以迁移到更长时的训练中去呢?作者们仍需要更多的研究,但根据已有的知识也可以给出一点结果,以下是按照可能性降序排列的:
- 非常可能迁移的
- warmup length
- initialization
- 一般可能迁移的
- 可能迁移的
- 优化器算法/超参数
- 数据增强
- regularization
- 如果不能完美拟合训练集,则该模型可能处于正则化不太可能提供很大帮助的状态
- 不太能迁移的
第二轮
对训练pipeline的一些额外的建议
优化输入pipeline
Summary: ths causes and interventions of input-bound pipelines are highly task-depedent; use a profiler and look out for common issues.
输入受限的pipeline是高度依赖任务类型的,使用分析器来查找原因。
- 使用一个合适的分析工具对你的输入受限的pipeline进行诊断。比如tensorflow profiler
- Ultimately, the specific causes and interventions will be highly task-dependent. Broader engineering considerations (e.g. minimizing disk footprint) may warrant worse input pipeline performance.
- 常见问题:
- 数据和训练进程不一致,导致IO延迟。
- 昂贵的在线数据预处理
- Unintentional synchronization barriers that interfere with data pipeline prefetching. For example, when synchronizing metrics between the device and host in CommonLoopUtils
- 常用方法:
- 构建输入pipeline进行样本预取。
- 移除无用的特性。
- 增加生成样本的工作的复用。
评估模型性能
Summary: Run evaluation at larger batch sizes than training. Run evaluations at regular step intervals, not regular time intervals.
使用比训练时更大的batch size进行评估。以固定的步数间隔而不是时间间隔进行评估。
评估设置
- 这里有一些用来检验模型表现的设置选项
- online evaluation:当模型在生产环境运行时可以得到的指标。
- offline evaluation:当模型在贴近生成环境的场景中线下/验证集/测试集使用时可以得到的指标。
- periodic evaluation:当模型在训练时得到的指标。
- 在线评估是一个黄金指标,但在模型开发阶段是不能使用的。
- 基于要解决的问题,离线评估可能又复杂又损耗大。
- 周期性评估是最实际最经济的选择,但是不能充分代表生产环境。
- 我们周期性评估的目标是又能让它代表我们的离线评估结果,同时不会牺牲可靠性。
建立周期性评估
- 我们在训练的过程中进行周期性评估以实时管理它的进程,以便于模型检查点选择,这样我们在训练结束后可以检查它的训练曲线。
- 最简单的配置是在同一个计算实例中既进行训练又进行周期性评估,训练和评估交替进行。
- 在这种情况下,用于评估的batch size至少要和用于训练的一样大, 因为评估阶段模型的计算量要求会更低。
- 周期性评估应该以固定的步数间隔进行,而不是时间间隔。
- 基于时间间隔做出的评估很难演绎成训练曲线, 特别是当训练可能会受到训练作业抢占、网络延迟问题等影响时。
- 周期性的有效测试可以揭露一些实现上的bug,比如测试集和训练集有重叠,比如训练集没有完全洗牌。基于固定步数间隔的评估可以让人更容易抓到这些问题。
- 当验证集不能被batchsize整除时可能出现一些问题,要保证填充示例的权重正确,以防止损失函数受到它们的影响。通常,这些填充示例的权重可以为零。
- 每次评估时都要保存足够的信息,我们可以保留对一些特定事例的预测,因为它们对于调试可能比较有帮助。
从周期性评估中选择样本
- 周期性评估的工作没有足够的时间在所有的离线验证集上进行,所以需要你进行一个合理的采样。
- 在构建样本数据集我们考虑一下的因素:
- 样本大小
- 要保证在这个样本数据集上获得的模型表现和你整个验证集是匹配的。
- 这个数据集不能太大,这样才能快速高效的完成模型预测,但是它也要足够大,能够正确地衡量模型效果的改进。
- It should be large enough to accommodate multiple such evaluations across trials in sequence, and still produce accurate estimates. That is, to avoid adaptively “fitting” to the validation set over time, in a way that doesn’t generalize to a held-out test set. However, this consideration is rarely a practical concern.
- 不均衡的数据集
- 对于一个不均衡的数据,在数量比较少的样本类型上的结果会比较嘈杂。
- 对于样本数量较少的数据集,记录正确预测的示例数量,以更深入地了解准确性改进。
保存检查点并回溯选择最优
Summary: run training for a fixed number of steps and retrospectively choose the best checkpoint from the run.
- 很多深度学习框架都支持模型检查点。模型的当前状态会周期性地保存在你的硬盘中,允许训练作业对计算实例中断具有弹性。
- 最好的检查点通常不是最后的检查点,尤其在你的验证集上表现已经不在随着时间提升甚至有点下降的时候。
- 建立一个pipeline来保存最好的N个检查点。在训练结束后,模型的选择就是选择训练过程中最好的检查点,我们把它称为retrospective optimal checkpoint selection。
- early stopping方法通常是没有必要的,因为我们会保存N个最好的检查点。
设置实验追踪
Summary: when tracking different experiments, make sure to note a number of essentials like the best performance of a checkpoint in the study, and a short description of the study.
在追踪不同的实验时,一定要注意一些要点,比如说检查点的最好表现,或者对当前研究的一个简短描述。
- 我们发现,在电子表格中跟踪实验结果对于我们处理的各种建模问题很有帮助。它通常具有以下列:
- 研究名称
- 配置文件的保存路径
- 简短的描述
- trials的次数
- 最好检查点对应的模型验证集表现。
- 复现任务的命令。或者未提交的注释的记录。
- 找到一个至少可以记录上面这些信息并且比较好操作的跟踪系统。未跟踪的实验跟不存在没什么差别。
BN的实现细节
Summary: nowadays batch norm can often be replaced with LayerNorm, but in cases where it cannot, there are trickly details when changing the batch size or number of hosts.
- BN使用均值和方差对batch进行归一化,而在一个多设备的配置环境下,每个设备上的数据都是不一样的。
- 使用64大小的 batch size进行bn操作的效果在实际上会更好一点。
- 将batch size与用于计算BN的严样本数量解耦对于比较batch size很有帮助。
- Ghost BN在每设备batch size> 虚拟batch size的情况并不总是正确处理。在这种情况下,我们实际上需要对每个设备上的batch进行二次采样,以获得正确数量的用于BN的目标。
- Exponential moving averages used in test mode batch norm are just a linear combination of training statistics, so these EMAs only need to be synchronized before saving them in checkpoints. However, some common implementations of batch norm do not synchronize these EMAs and only save the EMA from the first device.
对多主机pipeline的考虑
Summary: for logging, evals, RNGs, checkpointing, and data sharding, multi-host training can make it very easy to introduce bugs!
多主机的训练方式是更容易引入bug的
- 保证你的pipeline只在一个主机上进行记录和保存操作。
- 保证在评估或者保存前,bn已经进行了同步。
- 不同主机之间的RNG种子必须是一样的,用于数据洗牌和预处理的种子可以是不一样的。
- 在不同主机上的数据文件分片通常可以提高表现。