分享好友 最新动态首页 最新动态分类 切换频道
AdamW
2024-12-26 18:36

本文思想来自下面这篇大佬的文章:

AdamW

Juliuszh:一个框架看懂优化算法之异同 SGD/AdaGrad/Adam

https://zhuanlan.zhihu.com/p/32230623

主要是对深度学习各种优化器 (从SGD到AdamW) 使用统一的框架做一次整理,本文相比于链接从源代码的角度理解这些优化器的思路。

代码来自 PyTorch1.7.0 官方教程:

https://pytorch.org/docs/1.7.0/optim.html

首先我们来回顾一下各类优化算法。

深度学习优化算法经历了 SGD -> SGDM -> NAG ->AdaGrad -> AdaDelta -> Adam -> Nadam -> AdamW 这样的发展历程。Google一下就可以看到很多的教程文章,详细告诉你这些算法是如何一步一步演变而来的。在这里,我们换一个思路,用一个框架来梳理所有的优化算法,做一个更加高屋建瓴的对比。

  • 统一框架:

首先定义:待优化参数: ,目标函数: ,初始学习率 。

而后,开始进行迭代优化。在每个epoch :

1 计算目标函数关于当前参数的梯度:

2 根据历史梯度计算一阶动量和二阶动量:

3 计算当前时刻的下降梯度:

4 根据下降梯度进行更新:

掌握了这个框架,你可以轻轻松松设计自己的优化算法。

我们拿着这个框架,来照一照各种玄乎其玄的优化算法的真身。步骤3, 4对于各个算法都是一致的,主要的差别就体现在1和2上,也就是计算一阶动量 和二阶动量 时采用不同的套路。当计算好二者之后,都是使用固定的学习率 与二者作用得到当前时刻的下降梯度 ,进而最后更新参数。

在所有优化器的代码里面有一些函数的作用是相通的:

共性的方法有:

  • (param_group):把参数放进优化器中,这在 Fine-tune 预训练网络时很有用,因为可以使冻结层可训练并随着训练的进行添加到优化器中。
  • (state_dict):把优化器的状态加载进去。
  • ():返回优化器的状态,以dict的形式返回。
  • (closure=None):优化一步参数。
  • (set_to_none=False):把所有的梯度值设为0。

使用方法:

下面正式开始。

先来看SGD。SGD没有动量的概念,也就是说:

代入步骤3,可以看到下降梯度就是最简单的

SGD最大的缺点是下降速度慢,而且可能会在沟壑的两边持续震荡,停留在一个局部最优点。

为了抑制SGD的震荡,SGDM认为梯度下降过程可以加入惯性。下坡的时候,如果发现是陡坡,那就利用惯性跑的快一些。SGDM全称是SGD with momentum,在SGD基础上引入了一阶动量:

一阶动量是各个时刻梯度方向的指数移动平均值,约等于最近 个时刻的梯度向量和的平均值。

也就是说, 时刻的下降方向,不仅由当前点的梯度方向决定,而且由此前累积的下降方向决定。 的经验值为0.9,这就意味着下降方向主要是此前累积的下降方向,并略微偏向当前时刻的下降方向。想象高速公路上汽车转弯,在高速向前的同时略微偏向,急转弯可是要出事的。

SGD 还有一个问题是困在局部最优的沟壑里面震荡。想象一下你走到一个盆地,四周都是略高的小山,你觉得没有下坡的方向,那就只能待在这里了。可是如果你爬上高地,就会发现外面的世界还很广阔。因此,我们不能停留在当前位置去观察未来的方向,而要向前一步、多看一步、看远一些。

NAG全称Nesterov Accelerated Gradient,是在SGD、SGD-M的基础上的进一步改进,改进点在于步骤1。我们知道在时刻 的主要下降方向是由累积动量决定的,自己的梯度方向说了也不算,那与其看当前梯度方向,不如先看看如果跟着累积动量走了一步,那个时候再怎么走。因此,NAG在步骤1,不计算当前位置的梯度方向,而是计算如果按照累积动量走了一步,那个时候的下降方向:

然后用下一个点的梯度方向,与历史累积动量相结合,计算步骤2中当前时刻的累积动量。

定义优化器:

参数:

  • params (iterable) – 优化器作用的模型参数。
  • lr (float) – learning rate,相当于是统一框架中的 。
  • momentum (float, optional) – 动量参数。(默认值:0)
  • weight_decay (float, optional) – 权重衰减系数 weight decay (L2 penalty) (默认值:0)
  • dampening (float, optional) – dampening for momentum (默认值:0)
  • nesterov (bool, optional) – 允许 Nesterov momentum (默认值:False)
FLOAT:https://docs.python.org/3/library/functions.html#float
bool:https://docs.python.org/3/library/functions.html#bool

源码解读:

这里通过 d_p=p.grad 得到每个参数的梯度,也就是1式的 。

如果使用 weight_decay 的话,那么相当于目标函数加上 ,所以相当于是梯度相当于要再加上 ,所以使用了 d_p = d_p.add(p, alpha=weight_decay)。

通过 buf.mul_(momentum).add_(d_p, alpha=1 - dampening) 来计算动量,momentum参数 一般取0.9,就相当于是之前的动量buf乘以 ,再加上此次的梯度d_p乘以 。

如果不通过nesterov方式更新参数,那么3式中的 就相当于是上一步计算出的动量 了。如果通过nesterov方式更新参数,那么3式中的 就相当于 ,和不用nesterov方式相比,相差了

最后通过 p.add_(d_p, alpha=-group['lr']) 更新梯度,相当于是上面的 3 式。

此前我们都没有用到二阶动量。二阶动量的出现,才意味着“自适应学习率”优化算法时代的到来。SGD及其变种以同样的学习率更新每个参数,但深度神经网络往往包含大量的参数,这些参数并不是总会用得到(想想大规模的embedding)。对于经常更新的参数,我们已经积累了大量关于它的知识,不希望被单个样本影响太大,希望学习速率慢一些;对于偶尔更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本身上多学一些,即学习速率大一些。

怎么样去度量历史更新频率呢?那就是二阶动量——该维度上,迄今为止所有梯度值的平方和:

我们再回顾一下步骤3中的下降梯度:

可以看出,此时实质上的学习率由 变成了 。一般为了避免分母为0,会在分母上加一个小的平滑项。因此 是恒大于0的,而且参数更新越频繁,二阶动量越大,学习率就越小。

这一方法在稀疏数据场景下表现非常好。但也存在一些问题:因为 是单调递增的,会使得学习率单调递减至0,可能会使得训练过程提前结束,即便后续还有数据也无法学到必要的知识。

定义优化器:

参数:

  • params (iterable) – 优化器作用的模型参数。
  • lr (float) – learning rate – 相当于是统一框架中的 。
  • lr_decay(float,optional) – 学习率衰减 (默认值:0)
  • weight_decay (float, optional) – 权重衰减系数 weight decay (L2 penalty) (默认值:0)
  • eps(float,optional):防止分母为0的一个小数 (默认值:1e-10)

源码解读:

由于AdaGrad单调递减的学习率变化过于激进,我们考虑一个改变二阶动量计算方法的策略:不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度。这也就是AdaDelta名称中Delta的来历。

修改的思路很简单。前面我们讲到,指数移动平均值大约就是过去一段时间的平均值,因此我们用这一方法来计算二阶累积动量:

接下来还是步骤3:

这就避免了二阶动量持续累积、导致训练过程提前结束的问题了。

定义优化器:

参数:

  • params (iterable) – 优化器作用的模型参数。
  • lr (float) – learning rate – 相当于是统一框架中的 。
  • momentum (float, optional) – 动量参数。(默认值:0)。
  • alpha(float,optional) – 平滑常数 (默认值:0.99)。
  • centered(bool,optional) – if, compute the centered RMSProp, the gradient is normalized by an estimation of its variance,就是这一项是 True 的话就把方差使用梯度作归一化。
  • weight_decay (float, optional) – 权重衰减系数 weight decay (L2 penalty) (默认值:0)
  • eps(float,optional):防止分母为0的一个小数 (默认值:1e-10)

源码解读:

这里通过 grad = p.grad 得到每个参数的梯度,也就是1式的 。

如果使用 weight_decay 的话,那么相当于目标函数加上 ,所以相当于是梯度相当于要再加上 ,故使用了 grad = grad.add(p, alpha=group['weight_decay'])。

square_avg.mul_(alpha).addcmul_(grad, grad, value=1 - alpha) 对应10式,计算当前步的 。

centered 这一项是 False 的话直接 square_avg.sqrt().add_(group['eps']) 对 开根号。
centered 这一项是 True 的话就把方差使用梯度作归一化。

最后通过 p.addcdiv_(grad, avg, value=-group['lr']) 更新梯度,相当于是上面的 3 式。
RMSprop算是Adagrad的一种发展,和Adadelta的变体,效果趋于二者之间

定义优化器:

参数:

  • params (iterable) – 优化器作用的模型参数。
  • lr (float) – learning rate – 相当于是统一框架中的 。
  • rho(float,optional) – 计算梯度平方的滑动平均超参数 (默认值:0.9)
  • weight_decay (float, optional) – 权重衰减系数 weight decay (L2 penalty) (默认值:0)
  • eps(float,optional):防止分母为0的一个小数 (默认值:1e-10)

源码解读:

这里通过 grad = p.grad 得到每个参数的梯度,也就是1式的 。

如果使用 weight_decay 的话,那么相当于目标函数加上 ,所以相当于是梯度相当于要再加上 ,故使用了 grad = grad.add(p, alpha=group['weight_decay'])。

square_avg.mul_(rho).addcmul_(grad, grad, value=1 - rho) 对应10式,计算当前步的 。std = square_avg.add(eps).sqrt_() 对 开根号。

最后通过 p.add_(delta, alpha=-group['lr']) 更新梯度,相当于是上面的 3 式。
delta 的分子项是 ,分母项是 开根号。acc_delta 是对 delta 的滑动平均。

谈到这里,Adam和Nadam的出现就很自然而然了——它们是前述方法的集大成者。我们看到,SGD-M在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量。把一阶动量和二阶动量都用起来,就是Adam了——Adaptive + Momentum。

SGD的一阶动量:

加上AdaDelta的二阶动量:

优化算法里最常见的两个超参数 就都在这里了,前者控制一阶动量,后者控制二阶动量。

最后是Nadam。我们说Adam是集大成者,但它居然遗漏了Nesterov,这还能忍?必须给它加上,按照NAG的步骤1:

这就是Nesterov + Adam = Nadam了。

定义优化器:

参数:

  • params (iterable) – 优化器作用的模型参数。
  • lr (float) – learning rate – 相当于是统一框架中的 。
  • betas(Tuple[float,float],optional) – coefficients used for computing running averages of gradient and its square ((默认值:(0.9, 0.999))
  • weight_decay (float, optional) – 权重衰减系数 weight decay (L2 penalty) (默认值:0)
  • eps(float,optional):防止分母为0的一个小数 (默认值:1e-10)

源码解读:

这里通过 grad = p.grad 得到每个参数的梯度,也就是1式的 。

如果使用 weight_decay 的话,那么相当于目标函数加上 ,所以相当于是梯度相当于要再加上 ,故使用了 grad = grad.add(p, alpha=group['weight_decay'])。

exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1) 计算12式。
exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2) 计算13式。
因为15式的缘故,要给分母除以 math**.**sqrt(bias_correction2)。
因为14式的缘故,要给分子除以 bias_correction1。
最后通过 p.addcdiv_(exp_avg, denom, value=-step_size) 更新梯度,相当于是上面的 3 式。

下图1所示为Adam的另一个改进版:AdamW。

简单来说,AdamW就是Adam优化器加上L2正则,来限制参数值不可太大,这一点属于机器学习入门知识了。以往的L2正则是直接加在损失函数上,比如这样子:加入正则,损失函数就会变成这样子:

所以在计算梯度 时要加上粉色的这一项。

但AdamW稍有不同,如下图所示,将正则加在了绿色位置。

图1:AdamW

至于为何这么做?直接摘录BERT里面的原话看看:

Just adding the square of the weights to the loss function is *not* the correct way of using L2 regularization/weight decay with Adam, since that will interact with the m and v parameters in strange ways. Instead we want to decay the weights in a manner that doesn't interact with the m/v parameters. This is equivalent to adding the square of the weights to the loss with plain (non-momentum) SGD. Add weight decay at the end (fixed version).

这段话意思是说,如果直接将L2正则加到loss上去,由于Adam优化器的后序操作,该正则项将会与和产生奇怪的作用。因而,AdamW选择将正则项加在了Adam的和等参数被计算完之后、在与学习率相乘之前,所以这也表明了weight_decay和正则虽目的一致、公式一致,但用法还是不同,二者有着明显的差别。以 PyTorch1.7.0 中的AdamW代码为例:

定义优化器:

参数:

  • params (iterable) – 优化器作用的模型参数。
  • lr (float) – learning rate – 相当于是统一框架中的 。
  • betas(Tuple[float,float],optional) – coefficients used for computing running averages of gradient and its square ((默认值:(0.9, 0.999))
  • weight_decay (float, optional) – 权重衰减系数 weight decay (L2 penalty) (默认值:0)
  • eps(float,optional):防止分母为0的一个小数 (默认值:1e-10)

源码解读:

与 Adam 不一样的地方是:
Adam 如果使用 weight_decay 的话,那么相当于目标函数加上 ,所以相当于是梯度相当于要再加上 ,故使用了 grad = grad.add(p, alpha=group['weight_decay'])。

最新文章
最好的手机管家排行榜 手机管家排行榜前十名2022
手机管家app哪个好?手机管家就是一种用来进行手机文件以及信息安全管理的软件。随着科技的发展,网络安全成为了一大隐患,各种各样的不良信息骚扰着我们。手机管家就是这方面的清道夫,它具有极强的安全防护功能,让你的手机安全上网,摆脱
以“重”为壁垒获高估值,神马专车成网约车市场实力悍将
近年来有关汽车生态颠覆性变革的话题被行业热议。随着互联网技术、新能源技术的迅猛发展以及国家可持续发展战略的实施,移动出行和新能源汽车成为未来汽车产业新的利润池。众多汽车生产商、网约车服务商、有些流量平台也抓紧布局,力图抢占
使用Google身份验证进行ssh二次验证
谷歌身份验证器,即Google Authenticator(Google身份验证器)v2.33 谷歌推出的一款动态口令工具,解决大家的google账户遭到恶意攻击的问题。1、透过QR图码自动设定2、支持多个账户3、支援多种语言1、编辑/etc/selinux/config文件,关闭sel
亚洲尺码和欧洲尺码专线:从正常衬衫到“紧身装”-这次真的大开眼界!
穿衣服选错尺码是什么体验?有的网友买大了一号,勉强还能靠皮带撑撑场面;但买小了呢?你可能就会经历一场“奇幻时尚之旅”。尤其当外国朋友遭遇亚洲尺码,那画面简直让人哭笑不得!最近,一位暴躁老外在网上吐槽:他在亚洲网站买了一条L号裤
脂肪醇十强企业名单(2023年第三季度概念股票营收排名)
  华鲁恒升发布2023年第三季度财报,实现营业收入69.87亿元,同比增长7.91%,归母净利润12.18亿,同比18.84%;每股收益为0.57元。  第二、神马股份37.63亿元  神马股份发布2023年第三季度财报,实现营业收入37.63亿元,同比增长-2.17
熊掌号关键词优化,助力品牌在互联网浪潮中脱颖而出
随着互联网的飞速发展,越来越多的企业和个人开始关注搜索引擎(SEO)的重要性。熊掌号作为一款新型搜索引擎产品,以其独特的优势受到了广泛关注。优化作为SEO的核心策略,对于熊掌号的运营至关重要。本文将围绕熊掌号关键词优化展开,探讨
粉笔(02469)垂域大模型落地面试场景 AI考官1:1模拟真实考场
2025年度国考笔试已经落幕,广大考生即将投入到紧张的面试备考复习中。公考行业龙头粉笔(02469)宣布,基于公司自研垂域大模型,推出精品面试AI点评产品,于12月13日正式上线,用户可以以1元/次的价格限时进行体验。据了解,精品面试AI点
邵氏经典电视全面升级,呈现色彩极致之美
《舍不得星星》是一部备受欢迎的电视剧,讲述了青春爱情的跌宕起伏。为了满足广大观众的需求,许多网站提供该剧的免费观看选项。观众只需在网络上搜索,即可找到相关资源,欣赏这部充满感动与欢乐的作品。蛇倒刺蛇是中国民间传说中的一种神
基于微信小程序的图书管理系统
对图书管理的流程进行科学整理、归纳和功能的精简,通过软件工程的研究方法,结合当下流行的互联网技术,最终设计并实现了一个简单、易操作的图书管理小程序。内容包括系统的设计思路、系统模块和实现方法。系统使用过程
常用脚本语法记录
1、获取脚本当前所在路径 2、数组接收多个值演示数组调用案例 3、比较浮点值演示 4、删除匹配文本所在的行演示返回 5、替换文件中最后一行的数据演示6、删除文本最后一行的数据  7、ss.sh: line 7: [: ==: unary operator expecte
相关文章
推荐文章
发表评论
0评