发布日期:2024-05-06 来源: 网络 阅读量()
作者丨苏学算法 来源丨苏学算法 编辑丨极市平台 如何在复杂的目标函数中找到最优解,衍生出了一系列优化算法,本文则主要以优化器为着笔点展开。 关于优化器(Optimizer),TensorFlow与PyTorch框架中都进行了封装,为了对这些优化器进行更加深入的了解,本文以经典综述文献 An overview of gradient descent optimization algorithms 为线索,结合一些优秀的文章,对目前主流的优化算法进行汇总。 当然,仅从面试的角度,优化器也是面试官最爱考察的内容之一。 当前使用的许多优化算法,是对梯度下降法的衍生和优化。 在微积分中,对多元函数的参数 求偏导数,把求得的各个参数的导数以向量的形式写出来就是梯度。梯度就是函数变化最快的地方。梯度下降是迭代法的一种,在求解机器学习算法的模型参数 时,即无约束问题时,梯度下降是最常采用的方法之一。 顾名思义,梯度下降法的计算过程就是沿梯度下降的方向求解极小值,也可以沿梯度上升方向求解最大值。 为了便于后续讨论, 在此统一定义变量。假设, 待优化的模型参数为 , 目标函数(损失函数)为 , 学习率为 , 迭代的周期 epoch 为 , 因此可以得到下面的式子 损失函数 关于当前参数 的梯度: 梯度下降法是最基本的优化算法之一。梯度下降法就是沿着梯度下降最快的方向求极小值。 梯度下降法参数更新为: 梯度下降算法中,沿着梯度的方向不断减小模型参数,从而最小化损失函数。基本策略可以理解为”在你目光所及的范围内,不断寻找最陡最快的路径下山“ 标准的梯度下降主要有两个缺点: 训练速度慢:每走一步都要计算调整下一步的方向,下山的速度变慢。在应用于大型数据集中,每输入一个样本都要更新一次参数,且每次迭代都要遍历所有的样本。会使得训练过程及其缓慢,需要花费很长时间才能得到收敛解。 容易陷入局部最优解:由于是在有限视距内寻找下山的方向。当陷入平坦的洼地,会误以为到达了山地的最低点,从而不会继续往下走。所谓的局部最优解就是鞍点。落入鞍点,梯度为0,使得模型参数不在继续更新。“鞍部” 如下图所示 因此,真正在使用时,主要是经过改进的以下三类方法,区别在于每次参数更新时计算的样本数据量不同: 这里的“批量”其实指的就是整个训练集的数据,后面 MBGD 中的 mini-batch 才是真正意义上的 “一批” BGD相对于标准GD进行了改进, 所谓“批量", 也就是不再像标准GD一样, 对每个样本输入都进行参数更新, 而是针对所有的数据输入进行参数更新。我们假设所有的训练样本总数为 , 样本为 , 模型参数为 , 在对第 个样本 上的损失函数关于参数的梯度为 , 则使用 更新参数的式子为: 优点: 由于每一步迭代使用了全部样本,每次下降的方向为总体的平均梯度,因此损失函数收敛过程会比较稳定。对于凸函数可以收敛到全局最小值,对于非凸函数可以收敛到局部最小值。 缺点: 每一步更新中,都要利用全部样本计算梯度,计算起来非常慢,遇到很大量的数据集也会非常棘手,而且不能投入新数据实时更新模型。 随机梯度下降法, 不像BGD每一次参数更新, 需要计算整个数据样本集的梯度, 而是每次参数更新 时, 仅仅选取一个样本 计算其梯度, 参数更新公式为 公式看起来和上面标准GD一样,但不同点在于,这里的样本是从所有样本中随机选取一个,而标准GD是所有的样本都输入进行计算。 可以看到BGD和SGD是两个极端,SGD由于每次参数更新仅仅需要计算一个样本的梯度,训练速度很快,即使在样本量很大的情况下,可能只需要其中一部分样本就能迭代到最优解,由于每次迭代并不是都向着整体最优化方向,导致梯度下降的波动非常大(如下图),更容易从一个局部最优跳到另一个局部最优,准确度下降。 论文中提到,当缓慢降低学习率时,SGD会显示与BGD相同的收敛行为,几乎一定会收敛到局部(非凸优化)或全局最小值(凸优化) 优点: 缺点: 小批量梯度下降法就是结合BGD和SGD的折中, 对于含有 个训练样本的数据集, 每次参数更 新, 选择一个大小为 小批量梯度下降法即保证了训练的速度,又能保证最后收敛的准确率,目前的SGD默认是小批量梯度下降算法。常用的小批量尺寸范围在50到256之间,但可能因不同的应用而异。 优点: 可以降低参数更新时的方差,收敛更稳定,另一方面可以充分地利用深度学习库中高度优化的矩阵操作来进行更有效的梯度计算 缺点: 梯度下降法容易被困在局部最小的沟壑处来回震荡,可能存在曲面的另一个方向有更小的值;有时候梯度下降法收敛速度还是很慢。动量法就是为了解决这两个问题提出的 momentum算法思想:参数更新时在一定程度上保留之前更新的方向,同时又利用当前batch的梯度微调最终的更新方向,简言之就是通过积累之前的动量来 (previous_sum_of_gradient) 加速当前的梯度。 SGD只使用了当步参数的梯度,随机性较大。如果将历次迭代的梯度按比例融合,可能更加稳定、更有利于跳出局部最优。 假设 表示 时刻的动量, 表示动量因子, 通常取值 或者近似值, 在SGD的基础上增加动量, 则参数更新公式为 一阶动量 是各个时刻梯度方向的指数移动平均值, 约等于最近 个时刻的梯度向量和的 平均值。也就是说, 时刻的下降方向, 不仅由当前点的梯度方向决定, 而且由此前累积的下降方向决定。 动量因子 的经验值为 , 这就意味着下降方向主要是此前累积的下降方向, 并略微偏向当前时刻的下降方向。在梯度方向改变时, momentum能够降低参数更新速度, 从而减少震荡, 在梯度方向相同时, momentum可以加速参数更新, 从而加速收敛, 如下图 动量主要解决SGD的两个问题: 优点: 前后梯度一致的时候能够加速学习;前后梯度不一致的时候能够抑制震荡,越过局部极小值(加速收敛,减小震荡) 缺点: 增加了一个超参数 为了增强探索性, 进一步引入了 nesterov 动量。momentum保留了上一时刻的梯度 , 对 其没有进行任何改变, NAG是momentum的改进版, 在梯度更新时做了一个矫正, 具体做法是在当前的梯度 上添加上一个时刻的动量 , 梯度改变为 加上nesterov项后,梯度在大的跳跃后,进行计算对当前梯度进行校正。 下图是momentum和nesterrov的对比表述图 momentum首先计算一个梯度(短的蓝色向量),然后在加速更新梯度的方向进行一个大的跳跃(长的蓝色向量),nesterov项首先在之前加速的梯度方向进行一个大的跳跃(棕色向量),计算梯度然后进行校正(绿色向量) Nesterov动量梯度的计算在模型参数施加当前速度之后, 因此可以理解为往标准动量中添加了一个校正因子。在凸批量梯度的情况下, Nesterov动量将额外误差收敛率从 (k步后) 改进到 , 然而, 在随机梯度情况下, Nesterov动量对收敛率的作用却不是很大。 传统的优化算法要么将学习率设置为常数要么根据训练次数调节学习率。往往忽视了学习率其他变化的可能性。然而,学习率对模型的性能有着显著的影响,因此需要采取一些策略来想办法更新学习率,从而提高训练速度 自适应学习率算法主要有:AdaGrad算法,RMSProp算法,Adam算法以及AdaDelta算法等 Adagrad其实是对学习率进行了一个约束,对于经常更新的参数,我们已经积累了大量关于它的知识,不希望被单个样本影响太大,希望学习速率慢一些;对于偶尔更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本(稀疏特征的样本)身上多学一些,即学习速率大一些。而该方法中开始使用二阶动量,才意味着“自适应学习率”优化算法时代的到来。 AdaGrad 算法,独立地适应所有模型参数的学习率,缩放每个参数反比于其所有梯度历史平均值总和的平方根。 其梯度更新公式为 其中, 为 时刻参数 的梯度 如果是普通的 SGD, 那么每一个时刻的梯度更新公式为 但是 AdaGrad 的更新公式中, 矫正的学习率 也随着 和 而变化, 也就是所谓的“自适应"。(其中分母加了一个小的平滑项 是为了防止分母为 0 上式中的 为对角矩阵, 元素就是到 时刻为止, 参数 的累积梯度平方和, 也就是“二 阶动量"。从上述公式可以看出, 是恒大于 0 的, 而且参数更新越步繁, 二阶动量就 越大, 学习率 就越小, 所以在稀疏的数据场景下表现比较好。 优点: 自适应的学习率,无需人工调节 缺点: 由于AdaGrad调整学习率变化过于激进,我们考虑一个改变二阶动量计算方法的策略:不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度,即Adadelta只累加固定大小的项,并且也不直接存储这些项,仅仅是近似计算对应的平均值(指数移动平均值),这就避免了二阶动量持续累积、导致训练过程提前结束的问题了,参数更新公式如下 与 Adagrad 相比, 就是分母的 G 变成了过去的梯度平方的衰减平均值(指数衰减平均值) 其中, 的计算公式如下, 时刻的值依赖于前一时刻的平均和当前的梯度: 这个分母相当于梯度的均方根(root mean squared, RMS), 所以可以用 RMS 简写成 此外, 还可以将学习率 换成 , 这样的话, 就不需要提前设定学习率了 优点: 缺点: 训练后期,反复在局部最小值附近抖动 RMSprop 和 Adadelta 都是为了解决 Adagrad 学习率急剧下降问题的,但是RMSProp算法修改了AdaGrad的梯度平方和累加为指数加权的移动平均,使得其在非凸设定下效果更好。 指数加权平均,旨在消除梯度下降中的摆动,与Momentum的效果一样,某一维度的导数比较大,则指数加权平均就大,某一维度的导数比较小,则其指数加权平均就小,这样就保证了各维度导数都在一个量级,进而减少了摆动。 另外,指数衰减平均的方式可以淡化遥远过去的历史对当前步骤参数更新量的影响,衰减率表明的是只是最近的梯度平方有意义,而很久以前的梯度基本上会被遗忘 并且RMSprop允许使用一个更大的学习率 设定参数: 全局初始学习率 , decay_rate , 极小常量 , 参数更新如下: 优点: 缺点: Adam 结合了前面方法的一阶动量和二阶动量,相当于 Ada + Momentum,SGD-M和NAG在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量。 Adam 除了像 Adadelta 和 RMSprop 一样存储了过去梯度的平方 的指数衰减平均值, 也像 momentum 一样保持了过去梯度 的指数衰减平均值: 如果 和 被初始化为0向量, 那么它们就会向0偏置, 所以做了偏差校正, 通过计算偏差校正后的 和 来抵消这些偏差 最终的更新公式为 通常情况下,默认值为 优点: 缺点: Adam 使用动量的滑动平均,可能会随着训练数据变化而抖动比较剧烈,在online场景可能波动较大,在广告场景往往效果不如 AdaGrad 其实如果说要集成所有方法的优点于一身的话,Nadam应该就是了,Adam遗漏了啥?没错,就是Nesterov项,我们在Adam的基础上,加上Nesterov项就是Nadam了,参数更新公式如下: 其中 可以看出,Nadam对学习率有更强的约束,同时对梯度的更新也有更直接的影响。一般而言,在使用带动量的RMSprop或Adam的问题上,使用Nadam可以取得更好的结果。 Adam有很多的优点,但是在很多数据集上的最好效果还是用SGD with Momentum细调出来的。可见Adam的泛化性并不如SGD with Momentum。Decoupled Weight Decay Regularization 提出其中一个重要原因就是 Adam中L2正则化项并不像在SGD中那么有效 而在常见的深度学习库中只提供了L2正则,并没有提供权重衰减的实现。这可能就是导致Adam跑出来的很多效果相对SGD with Momentum有偏差的一个原因 AdamW 使用了严谨的 weight decay(非L2正则),即权重衰减不参与一、二动量计算,只在最后的更新公式中使用。其更新公式如下: 下图描述了在一个曲面上,6种优化器的表现 下图在一个存在鞍点的曲面,比较6中优化器的性能表现: 下图图比较了6种优化器收敛到目标点(五角星)的运行过程 目前,最流行并且使用很高的优化器(算法)包括SGD、具有动量的SGD、RMSprop、具有动量的RMSProp、AdaDelta和Adam。在实际应用中,选择哪种优化器应结合具体问题。在充分理解数据的基础上,依然需要根据数据特性、算法特性进行充分的调参实验,找到最优解。 参考文献: 数据集汇总: 顶会资源: 技术综述: 理论深挖: 论文盘点: 实践/面经/求学: Github优质资源: 首先,为什么需要优化器(Optimizer)这个东西呢,因为许多问题都是需要“优化”的(当然也包括未来35岁的你我 )。人生中,你经历的很多事都可以有一个目标函数(诸如买到房子,找到对象,生个娃,再“鸡”个娃,再买个房子,再帮他找个对象...),那么有了目标,就需要进行求解,也就是优化。如果你的目标很简单,就像一个沙盘大小,那你可以一眼就看出沙盘的最低点(或者最高点)在哪,也就是最优解;但是,如果你的目标函数是一个撒哈拉沙漠,你想找到最优解(最低点)的话,那就没有那么容易了...
简单理解就是,上面提到的场景中,你要在撒哈拉沙漠中找最低点,你根本不知道最低点在哪,但是在你所站的位置上,总有一个方向的坡度最陡,你沿着这个方向往下滑是有可能到最低点的。
在梯度下降公式 中,能改进的两个点,一是学习率,二是梯度。其分别衍生出自适应(adaptive)学习率方法与动量(momentum)方法。后面讲展开介绍
动量优化法引入了物理之中的概念。动量 ,当一个小球从山顶滚下,速度越来越快,动量越来越大,开始加速梯度下降,当跨越了山谷,滚到对面的坡上时,速度减小,动量减小。
带动量的小球不仅可以加速梯度;还可以借着积累的动量,冲过小的山坡,以避免落入局部最优点。
Momentum和Nexterov都是为了使梯度更新更灵活。但是人工设计的学习率总是有些生硬,下面介绍几种自适应学习率的方法。
先来看一下使用统一的全局学习率的缺点可能出现的问题