AdaBound算法:像Adam一样快,又像SGD一样好的优化器

论文地址:

https://openreview.net/pdf?id=Bkg3g2R9FX

GitHub地址:(Pytorch)

https://github.com/Luolc/AdaBound

GitHub地址:(Tensorflow)

https://github.com/taki0112/AdaBound-Tensorflow

SGD的缺点:

SGD现在后期调优时还是经常使用到,但SGD的问题是前期收敛速度慢。SGD前期收敛慢的原因: SGD在更新参数时对各个维度上梯度的放缩是一致的,并且在训练数据分布极不均很时训练效果很差。而因为收敛慢的问题应运而生的自适应优化算法Adam、AdaGrad、RMSprop 等,但这些自适应的优化算法虽然可以在训练早期展现出快速的收敛速度,但其在测试集上的表现却会很快陷入停滞,并最终被 SGD 超过。

Adam等自适应学习率算法缺点:

这就是目前很多大牛任然喜欢SGD的原因。这篇文章对于Adam后期的毛病进行了分析,原因出在自适应方法训练后期不稳定的极端学习率。换句话说,就是自适应学习率训练到后期,学习率出现极端情况,更新参数时有些维度上学习率特别大,有些维度学习率特别小。

采样参数的学习率,每个单元格包含一个通过对学习率进行数值运算得到的值,颜色越浅代表学习率越小。

我们可以看到,当模型接近收敛时,学习率中有大量的极端值(包含许多小于 0.01 和大于 1000 的情况)。这一现象表明在实际训练中,极端学习率是实际存在的。

发现这个问题怎么解决?如何融合上面两种方法的优点?

那就对自适应学习率加一下限制吧。具体做法是对学习率进行动态裁剪,在这一设置下,在训练早期由于上下界对学习率的影响很小,算法更加接近于 Adam;而随着时间增长裁减区间越来越收紧,模型的学习率逐渐趋于稳定,在末期更加贴近于 SGD。AMSBound 可以对 AMSGrad 采用类似的裁剪得到。

换句话说,Adam和SGD是AdaBound的特殊情况。

在这一设置下,在训练早期由于上下界对学习率的影响很小,算法更加接近于 Adam;而随着时间增长裁减区间越来越收紧,模型的学习率逐渐趋于稳定,在末期更加贴近于 SGD。AMSBound 可以对 AMSGrad 采用类似的裁剪得到。

Adam作者大革新, 联合Hinton等人推出全新优化方法Lookahead

论文地址:

https://arxiv.org/abs/1907.08610v1

GitHub地址:(Pytorch)

https://github.com/alphadl/lookahead.pytorch

GitHub地址:(Tensorflow)

https://github.com/Janus-Shiau/lookahead_tensorflow

Lookahead的思路很朴素,准确来说它并不是一个优化器,而是一个使用现有优化器的方案。简单来说它就是下面三个步骤的循环执行:

附:《机器之心的Lookahead的介绍》

LazyAdam、MaskedAdamOptimize

LazyAdam

和图像等领域不同,对 NLU 之类的任务,每个 batch 采样到的词有限,每次更新对 Embedding 的梯度估计都是稀疏的。非 momentum-based 的 Optimizer 每步只会更新采样到的词,而对于所有带动量的优化器(自然也就包括Adam以及带动量的SGD)都存在一个问题:当前batch中没被采样到的词,依然会使用历史动量来更新,这可能导致Embedding层过拟合。具体来说,当一个词的被采样过后,它的Embedding的梯度不为0,这个梯度也会被记录在动量中,实际更新是用动量去更新的;在后面的batch中,假如该词没有被采样到,它的Embedding的梯度为0,但是它的动量并不为0,所以该词还是被更新了。这样一来就算没有被反复采样的词,对应的Embedding也被反复更新了,就导致了过拟合。

所以,一个改进的方案是只有当该词被采样过才更新,这就是LazyOptimizer的基本原理了。

LazyAdam是Adam的变体,可以更有效地处理稀疏更新。原始的Adam算法为每个可训练变量维护两个移动平均累加器,累加器在每一步都会更新**。 而此类为稀疏变量提供了更加懒惰的梯度更新处理,它仅更新当前batch中出现的稀疏变量索引的移动平均累加器,而不是更新所有索引的累加器。 与原始的Adam优化器相比,它可以为某些应用提供模型训练吞吐量的大幅改进。 但是它的语义与原始的Adam算法略有不同,可能会导致不同的实验结果。

在实现上,我们要如何判断一个词有没有被采样过呢?当然终极方法肯定是传入被采样过的词的index了,但这使用上不够友好。我这里使用了一个近似的方法:判断该词的Embedding对应的梯度是否为0,如果为0意味着它“很可能”在当前batch没有被采样到。背后的原理在于,如果它没有被采样到,那么梯度一定为0,如果它被采样到了,那么梯度为0的概率是非常小的,毕竟那么多分量,同时为0的可能性很小,所以这样实现也够用了。

AdamOptimizer源码中函数_apply_sparse和_resource_apply_sparse 主要用在稀疏向量的更新操作上,而具体的实现是在函数_apply_sparse_shared中

LazyAdam的源码:

def _apply_sparse(self, grad, Var):beta1_power, beta2_power = self._get_beta_accumulators()beta1_power = math_ops.cast(beta1_power, Var.dtype.base_dtype)beta2_power = math_ops.cast(beta2_power, Var.dtype.base_dtype)lr_t = math_ops.cast(self._lr_t, Var.dtype.base_dtype)beta1_t = math_ops.cast(self._beta1_t, Var.dtype.base_dtype)beta2_t = math_ops.cast(self._beta2_t, Var.dtype.base_dtype)epsilon_t = math_ops.cast(self._epsilon_t, Var.dtype.base_dtype)lr = (lr_t * math_ops.sqrt(1 - beta2_power) / (1 - beta1_power))# \\(m := beta1 * m + (1 - beta1) * g_t\\)m = self.get_slot(Var, "m")m_t = state_ops.scatter_update(m, grad.indices,beta1_t * array_ops.gather(m, grad.indices) +(1 - beta1_t) * grad.Values,use_locking=self._use_locking)#一阶动量# \\(V := beta2 * V + (1 - beta2) * (g_t * g_t)\\)V = self.get_slot(Var, "V")V_t = state_ops.scatter_update(V, grad.indices,beta2_t * array_ops.gather(V, grad.indices) +(1 - beta2_t) * math_ops.square(grad.Values),use_locking=self._use_locking) #二阶动量# \\(Variable -= learning_rate * m_t / (epsilon_t + sqrt(V_t))\\)m_t_slice = array_ops.gather(m_t, grad.indices)V_t_slice = array_ops.gather(V_t, grad.indices)denominator_slice = math_ops.sqrt(V_t_slice) + epsilon_tVar_update = state_ops.scatter_sub(Var, grad.indices,lr * m_t_slice / denominator_slice,use_locking=self._use_locking)return control_flow_ops.group(Var_update, m_t, V_t)

可以看出公式与Adam都相同,不同的是每次迭代根据当前batch的indices来对一阶动量和二阶动量进行更新。

Madam

from tensorflow.python.ops import array_ops
from tensorflow.python.training import adam
from tensorflow.python.framework import ops
from tensorflow.python.ops import control_flow_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import resource_variable_ops
from tensorflow.python.ops import state_ops
from tensorflow.python.ops import variable_scope
from tensorflow.python.training import optimizerclass MaskedAdamOptimizer(adam.AdamOptimizer):def _apply_sparse_shared(self, grad, var, indices, scatter_add):beta1_power, beta2_power = self._get_beta_accumulators()beta1_power = math_ops.cast(beta1_power, var.dtype.base_dtype)beta2_power = math_ops.cast(beta2_power, var.dtype.base_dtype)lr_t = math_ops.cast(self._lr_t, var.dtype.base_dtype)beta1_t = math_ops.cast(self._beta1_t, var.dtype.base_dtype)beta2_t = math_ops.cast(self._beta2_t, var.dtype.base_dtype)epsilon_t = math_ops.cast(self._epsilon_t, var.dtype.base_dtype)lr = (lr_t * math_ops.sqrt(1 - beta2_power) / (1 - beta1_power))# m_t = beta1 * m + (1 - beta1) * g_tm = self.get_slot(var, "m")m_scaled_g_values = grad * (1 - beta1_t)m_t = state_ops.assign(m, m * beta1_t,use_locking=self._use_locking)with ops.control_dependencies([m_t]):m_t = scatter_add(m, indices, m_scaled_g_values)# v_t = beta2 * v + (1 - beta2) * (g_t * g_t)v = self.get_slot(var, "v")v_scaled_g_values = (grad * grad) * (1 - beta2_t)v_t = state_ops.assign(v, v * beta2_t, use_locking=self._use_locking)with ops.control_dependencies([v_t]):v_t = scatter_add(v, indices, v_scaled_g_values)gather_m_t = array_ops.gather(m_t, indices)gather_v_t = array_ops.gather(v_t, indices)gather_v_sqrt = math_ops.sqrt(gather_v_t)var_update = scatter_add(var, indices, -lr * gather_m_t / (gather_v_sqrt + epsilon_t))return control_flow_ops.group(*[var_update, m_t, v_t])

两者在计算移动平均累加器时(一阶动量和二阶动量)有所不同:

LazyAdam:

m_t = state_ops.scatter_update(m, grad.indices,beta1_t * array_ops.gather(m, grad.indices) +(1 - beta1_t) * grad.Values,use_locking=self._use_locking)

Madam:

m_scaled_g_Values = grad * (1 - beta1_t)m_t = state_ops.assign(m, m * beta1_t,use_locking=self._use_locking)  with ops.control_dependencies([m_t]):m_t = scatter_add(m, indices, m_scaled_g_Values)

Madam其实是介于Lazy Adam和 Adam之间的一种方法,其与Lazy Adam唯一的不同在于对一阶动量m和二阶动量 V 进行 decay 的操作,Madam是全部都要 decay,即当前batch没有采样到的变量所对应的之前动量的累积值也要考虑。 而LazyAdam 是只 decay 采样到的embedding。(在计算指数加权平均时,LazyAdam只对当前采样到的变量之前的平均值进行累加,没有采样到的样本不累加,而Madam要全部累加)。

LazyAdam存在的一个问题是当梯度为0时不更新对应的m和v。实际上当其他权重改变时m和v应该更新。Madam应该是解决了这个问题所以性能变得更好。

为了更形象的说明它们的差异,通过一个假设的例子来说明,用一阶动量来举例:

参考文章:https://kexue.fm/archives/6869

Lookahead、LazyOptimizer、MaskedAdamOptimizer、AdaBound相关推荐

  1. etcd 笔记(05)— etcd 代码结构、各模块功能、整体架构、各模块之间的交互、请求和应答流程

    1. etcd 项目结构和功能 etcd 项目代码的目录结构如下: $ tree ├── auth ├── build ├── client ├── clientv3 ├── contrib ├── ...

  2. VS Code 配置调试参数、launch.json 配置文件属性、task.json 变量替换、自动保存并格式化、空格和制表符、函数调用关系、文件搜索和全局搜索、

    1. 生成配置参数 对于大多数的调试都需要在当前项目目录下创建一个 lanch.json 文件,位置是在当前项目目录下生成一个 .vscode 的隐藏文件夹,在里面放置一些配置内容,比如:settin ...

  3. 计图(Jittor) 1.1版本:新增骨干网络、JIT功能升级、支持多卡训练

    计图(Jittor) 1.1版本:新增骨干网络.JIT功能升级.支持多卡训练 深度学习框架-计图(Jittor),Jittor的新版本V1.1上线了.主要变化包括: • 增加了大量骨干网络的支持,增强 ...

  4. 对端边缘云网络计算模式:透明计算、移动边缘计算、雾计算和Cloudlet

    对端边缘云网络计算模式:透明计算.移动边缘计算.雾计算和Cloudlet 概要 将数据发送到云端进行分析是过去几十年的一个突出趋势,推动了云计算成为主流计算范式.然而,物联网时代设备数量和数据流量的急 ...

  5. Shiro01 功能点框图、架构图、身份认证逻辑、身份认证代码实现

    基本功能点 功能点框图 功能点说明 1.Authentication:身份认证/登录,验证用户是不是拥有相应的身份: 2.Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个 ...

  6. PyTorch 笔记(16)— torch.nn.Sequential、torch.nn.Linear、torch.nn.RelU

    PyTorch 中的 torch.nn 包提供了很多与实现神经网络中的具体功能相关的类,这些类涵盖了深度神经网络模型在搭建和参数优化过程中的常用内容,比如神经网络中的卷积层.池化层.全连接层这类层次构 ...

  7. 机器学习入门(15)— 全连接层与卷积层的区别、卷积神经网络结构、卷积运算、填充、卷积步幅、三维数据卷积、多维卷积核运算以及批处理

    卷积神经网络(Convolutional Neural Network,CNN)CNN 被用于图像识别.语音识别等各种场合,在图像识别的比赛中,基于深度学习的方法几乎都以 CNN 为基础. 1. 全连 ...

  8. Go 学习笔记(33)— Go 自定义类型 type(自定义结构体、结构体初始化、结构体内嵌、自定义接口)

    1. 自定义类型格式 用户自定义类型使用关键字 type ,其语法格式是: type newType oldType oldType 可以是自定义类型.预声明类型.未命名类型中的任意一种. newTy ...

  9. Go 学习笔记(32)— 类型系统(命名类型、未命名类型、底层类型、类型强制转换、类型别名和新声明类型)

    1. 命名类型和未命名类型 1.1 命名类型 类型可以通过标识符来表示,这种类型称为命名类型( Named Type ). Go 语言的基本类型中有 20 个预声明简单类型都是命名类型, Go 语言还 ...

最新文章

  1. 全国首个5G+AI的智慧交通管理示范应用成功开通!
  2. wukong搜索引擎源码解读
  3. CVPR 2019 | PoolNet:基于池化技术的显著性目标检测
  4. 软件项目经理职责[转](
  5. SAP CRM和Cloud for Customer的数据同步一例
  6. 用Java 编写菜单价格和的程序_使用JAVA 编写一个程序,显示5中商品价格,用户可以选择多种商品并在其后的文本框输入购买的数量。...
  7. 测试龙芯 LoongArch .NET之 使用 FastTunnel 做内网穿透远程计算机
  8. python远程监控jvm_python和jstat命令采集jvm数据导入zabbix监控-阿里云开发者社区
  9. BayaiM__SQLLDR_linux_shell高级版
  10. 使用.NET框架自带的Json序列化类
  11. 启动Matlab安装包时出现Unable to open the requested feature.
  12. 基于WF4的新平台-流程格式转换架构
  13. Xshell4、Xftp4注册码
  14. Status code :200 OK (from disk cache)
  15. 在centOS系统上python导入baidu-aip
  16. 远程协作从“特殊”到“常态”,你可能需要的会议平板测评
  17. java开发中遇到的问题_java开发中遇到的问题及解决方法(持续更新)
  18. python对真假的判断方式
  19. c#字符型转化为asc_C#中使用强制类型实现字符串和ASCII码之间的转换
  20. CVBS-复合视频广播信号[转]

热门文章

  1. mysql 3358端口_好用的MySQL抓包工具:sniffer-agent
  2. GD32开发资料汇总下载软件硬件工具
  3. 《 C语言程序设计(第2版)》 读书笔记
  4. 一站放心购全球:亚马逊海外购开启2022年黑五全球购物季
  5. 走进微软亚洲研究院随笔
  6. 微信表情与输入法无缝切换(原理篇)
  7. Yii Framework 开发教程(41) Zii组件-Tabs示例
  8. java web实现套打,web 如何实现精准打印
  9. 物联网中使用了哪些技术?
  10. 市场调研报告-全球与中国AI加速器的渗透和路线市场现状及未来发展趋势