现代的机器学习系统均利用大量的数据,利用梯度下降算法或者相关的变体进行训练。传统上,最早出现的优化算法是SGD,之后又陆续出现了AdaGrad、RMSprop、ADAM等变体,那么这些算法之间又有哪些区别和联系呢?本文试图对比的介绍目前常用的基于一阶梯度的优化算法,并给出它们的(PyTorch)实现。

SGD

算法描述

随机梯度下降法(Stochastic Gradient Descent,SGD)是对传统的梯度下降算法(Gradient Descent,GD)进行的一种改进。在应用GD时,我们需要对整个训练集进行一次反向传播计算梯度后再进行参数更新,对系统的计算能力和内存的需求较高,而SGD在计算梯度更新参数时刚好相反,每次只使用整个训练集中的一个样本,因此具有更快地计算速度和较少的内存占用。同时,因为每次只使用一个样本更新参数,使得参数更新更加频繁,更新的参数间具有更高的方差,损失函数会朝不同的方向有较大的波动,这有助于发现新的极值点,避免优化器陷入一个局部极值点。但是也由于这种频繁的震荡,出现了一种折中的方法,即小批量(mini-batch)梯度下降法,每次只取训练集中一个batch的样本进行梯度的计算与参数更新,一般batch的大小为4的倍数。原始SGD的更新法则如下:θ=θ−η⋅∇θJ(θ)(1)(1)θ=θ−η⋅∇θJ(θ)

传统SGD面临的问题

传统的SGD在训练的过程中主要存在以下几个问题:

  1. 很难选择一个合适的学习速率,太小的学习速率导致算法收敛很慢,而太大的学习速率会导致在极值点附近震荡甚至错过,因此需要经过多次尝试。
  2. Learning rate schedules往往实现定义一个学习速率衰减表,比如每过多少step对学习速率进行decay,但是这些策略往往没法按照某个数据集的具体参数特性进行定制。
  3. 对于比较稀疏的数据,不同的特征出现的频率差别很大,如果所有的参数均使用一个相同的学习速率进行更新,这样做是不合理的。对于出现频率的特征,我们应该使用一个较大的学习速率。
  4. 深度神经网络之所以难以训练,并不是因为容易陷入局部最小值,而是在学习的过程中陷入到鞍点(saddle point),此时往各个方向的梯度几乎均为0。如果以二维平面为例,y=x3y=x3中x=0处即为一个鞍点。对于传统的SGD而言,一旦优化的过程中进入鞍点,就很难再离开这一位置。

Momentum

针对以上提到的第四点问题,可以通过增加动量(Momentum)的SGD进行缓解,加速优化函数的收敛。vt=γvt−1−η⋅∇θJ(θ)θ=θ+vt(2)(2)vt=γvt−1−η⋅∇θJ(θ)θ=θ+vt所谓的添加动量项,即在一定程度上保留上一次梯度更新的方向,γ,ηγ,η分别用来控制上次梯度方向和本次梯度方向对最终更新方向的贡献程度,其中γ∈(0,1]γ∈(0,1]在开始阶段常常被设置为0.5,当学习趋向稳定后,逐渐增加到0.9甚至更高。 可以把待优化的目标函数想象成一座山,在山顶将一个小球推下,小球在山坡上滚动的位置即系统的loss值,在往下滚动的过程中小球的动量不断增加,由于动量的存在,当小球滚动到山坡中较为平坦的地带时,小球将更容易越过这片地带继续往下滚而不是陷在这一区域停滞不前,并最终到达山谷。

图1 左:原始SGD 右:SGD+Momentum

Nesterov Accelerated Gradient

Its better to correct a mistake after you have made it!

目前我们有了一个带有动量的小球,但是这个小球在滚动的过程中总是随着山势的变化滚动,因此其行进的路径极不稳定。因此我们希望有一个更加“聪明”的小球,它不但拥有动量,而且能够知道自己将要去哪,这样当前面出现上坡小球能够进行减速。比如说,当接近坡底时,小球应该提前减速避免错过坡底。vt=γvt−1−η∇θJ(θ+γvt−1)θ=θ+vt(3)(3)vt=γvt−1−η∇θJ(θ+γvt−1)θ=θ+vt具体的实现也非常的直接,就是将传统的Momentum方法对θθ计算梯度变为对θ+γvt−1θ+γvt−1求梯度,这一项可以看做对小球下一步将会往哪运动的一个粗略估计。也就是说,我们的小球有了一定的对未来的“预测”能力。就像本节开头说的,如果我们知道了小球之后会犯什么错误,那么是否更容易更正错误呢?下图上半部分是传统Momentum求下一次梯度更新方向,下半部分则是使用NAG求下一次更新方向的方法。

图2 Momentum与NAG更新的区别

当然,在具体实现时,直接计算θ+γvt−1θ+γvt−1项的梯度比较麻烦,希望更新参数时计算能和传统的SGD或者Momentum方法类似,因此需要对上式的计算步骤做一些改进。

v_prev = v  #备份vt-1项
v = mu*v - lr * g  #这一步和传统的Momentum计算一样
p += -mu*v_prev + (1+mu)*v  #更新时真实的p应该为p-mu*v_prev,更新后为p-mu*v_prev+v,但是为了方便计算加上上次动量项的梯度,这里的p直接保存为p-mu*v_prev+v+mu*v,也就是p(小球)的“未来位置”。

PyTorch实现

Momentum/NAG的实现和原始论文中的实现有些许的不用,具体的,在PyTorch实现中按照如下的公式更新梯度,其中ηη为learning rate,gg为θθ的梯度。目前尚不清楚为什么要做出这样的改变?vt=γvt−1+gθ=θ−η⋅vt(4)(4)vt=γvt−1+gθ=θ−η⋅vt具体代码如下: > class torch.optim.SGD(params, lr=required, momentum=0, dampening=0, weight_decay=0, nesterov=False)

def step(self, closure=None):"""Performs a single optimization step.Arguments:closure (callable, optional): A closure that reevaluates the modeland returns the loss."""loss = Noneif closure is not None:loss = closure()for group in self.param_groups:weight_decay = group['weight_decay']momentum = group['momentum']dampening = group['dampening']nesterov = group['nesterov']for p in group['params']:if p.grad is None:continued_p = p.grad.dataif weight_decay != 0:d_p.add_(weight_decay, p.data)if momentum != 0:   #动量项添加param_state = self.state[p]if 'momentum_buffer' not in param_state:buf = param_state['momentum_buffer'] = d_p.clone()else:buf = param_state['momentum_buffer']buf.mul_(momentum).add_(1 - dampening, d_p)if nesterov:    #如果使用NAG,则为t+1步先保存可能到达的“位置”d_p = d_p.add(momentum, buf)else:d_p = bufp.data.add_(-group['lr'], d_p)return loss

AdaGrad

算法描述

AdaGrad为的是解决传统的SGD对所有参数使用相同的学习速率的问题(即1.2节中提到的第三点问题)。它使用参数的历史梯度累计和去归一化该参数对应的学习速率。具体的,对于经常出现的参数,那么其梯度累积和较大,归一化的学习速率就较小。而对于不常见的参数,往往包含更多关于特征的信息,累积和较小,归一化后的学习速率较大,也即是学习算法应该更加关注这些罕见的特征的出现。Gt,ii=Gt−1,ii+g2t,iθt+1,i=θt,i−η√Gt,ii+ϵ⋅gt,i(5)(5)Gt,ii=Gt−1,ii+gt,i2θt+1,i=θt,i−ηGt,ii+ϵ⋅gt,i当然,通过观察式(5),我们也发现AdaGrad在学习速率的调整上存在过于激进的问题,随着时间的累积,Gt,iiGt,ii这一项会越来越大,导致归一化的学习速率越来越小,这有可能导致优化函数在收敛之前就停止更新。

PyTorch实现

class torch.optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0)

def step(self, closure=None):"""Performs a single optimization step.Arguments:closure (callable, optional): A closure that reevaluates the modeland returns the loss."""loss = Noneif closure is not None:loss = closure()for group in self.param_groups:for p in group['params']:if p.grad is None:continuegrad = p.grad.datastate = self.state[p]state['step'] += 1if group['weight_decay'] != 0:if p.grad.data.is_sparse:raise RuntimeError("weight_decay option is not compatible with sparse gradients ")grad = grad.add(group['weight_decay'], p.data)clr = group['lr'] / (1 + (state['step'] - 1) * group['lr_decay'])if p.grad.data.is_sparse:grad = grad.coalesce()  # the update is non-linear so indices must be uniquegrad_indices = grad._indices()grad_values = grad._values()size = torch.Size([x for x in grad.size()])def make_sparse(values):constructor = type(p.grad.data)if grad_indices.dim() == 0 or values.dim() == 0:return constructor()return constructor(grad_indices, values, size)state['sum'].add_(make_sparse(grad_values.pow(2)))std = state['sum']._sparse_mask(grad)std_values = std._values().sqrt_().add_(1e-10)p.data.add_(-clr, make_sparse(grad_values / std_values))else:state['sum'].addcmul_(1, grad, grad)    #更新核心部分std = state['sum'].sqrt().add_(1e-10)p.data.addcdiv_(-clr, grad, std)return loss

Adadelta

为了避免AdaGrad存在的学习过早停止的问题,Adadelta不再保存过去所有时刻的梯度和,而是采用decaying average的方法平滑过去的梯度值和参数值。

算法描述

图3 Adadelta伪代码描述

其中E[g2]tE[g2]t存储的是历史梯度平方的平滑值,此外,这里还需要对历史的参数值的平方进行decaying average,也就是E[Δx2]t=ρE[Δx2]t−1+(1−ρ)Δx2tE[Δx2]t=ρE[Δx2]t−1+(1−ρ)Δxt2,然后分别进行开方处理得到RMS[Δx]t=√E[Δx2]t+ϵRMS[Δx]t=E[Δx2]t+ϵ和RMS[g]t=√E[g2]t+ϵRMS[g]t=E[g2]t+ϵ。最后按照xt

梯度下降优化算法综述与PyTorch实现源码剖析相关推荐

  1. 梯度下降优化算法综述(转载)

    原文地址:http://www.cnblogs.com/ranjiewen/p/5938944.html 对梯度下降进行详细解释,以及总结不同的梯度下降优化算法的优劣,可以作为参考. 上两张图,简直不 ...

  2. 深度学习中的梯度下降优化算法综述

    1 简介 梯度下降算法是最常用的神经网络优化算法.常见的深度学习库也都包含了多种算法进行梯度下降的优化.但是,一般情况下,大家都是把梯度下降系列算法当作是一个用于进行优化的黑盒子,不了解它们的优势和劣 ...

  3. 梯度下降优化算法综述,梯度下降法 神经网络

    梯度下降法是什么? 梯度下降法(英语:Gradientdescent)是一个一阶最优化算法,通常也称为最陡下降法. 要使用梯度下降法找到一个函数的局部极小值,必须向函数上当前点对应梯度(或者是近似梯度 ...

  4. 基于机器学习梯度下降优化算法来寻找最佳的线性回归模型

    https://www.toutiao.com/a6638782437587419652/ 幻风的AI之路 2018-12-25 18:12:27 线性回归模型 线性回归模型是一个非常简单的算法模型, ...

  5. 梯度下降优化算法总结

    写在前面 梯度下降(Gradient descent)算法可以说是迄今最流行的机器学习领域的优化算法.并且,基本上每一个深度学习库都包括了梯度下降算法的实现,比如Lasagne.cafe.keras等 ...

  6. 深度学习-各类梯度下降优化算法回顾

    本文是根据 链接 进行的翻译,回顾了深度学习的各种梯度下降优化算法.*已获得原作者的翻译许可. 文章目录 一.概述 二.引言 三.Gradient Descent Variants(梯度下降法变体) ...

  7. 【优化算法】蚁狮优化算法(ALO)【含Matlab源码 1307期】

    ⛄一.获取代码方式 获取代码方式1: 完整代码已上传我的资源:[优化算法]蚁狮优化算法(ALO)[含Matlab源码 1307期] 点击上面蓝色字体,直接付费下载,即可. 获取代码方式2: 付费专栏M ...

  8. 【优化算法】基于matlab象鼻虫损害优化算法 (WDOA)【含Matlab源码 2228期】

    ⛄一.获取代码方式 获取代码方式1: 完整代码已上传我的资源:[优化算法]基于matlab象鼻虫损害优化算法 (WDOA)[含Matlab源码 2228期] 点击上面蓝色字体,直接付费下载,即可. 获 ...

  9. 梯度下降优化算法概述

    本文原文是 An overview of gradient descent optimization algorithms,同时作者也在 arXiv 上发了一篇同样内容的 论文. 本文结合了两者来翻译 ...

最新文章

  1. 编程能力差,学不好Python、AI、Java等技术,90%是输在了这点上!
  2. Python web 开发:部署一个3行代码的wsgi app
  3. python unpack函数_Python numpy.unpackbits函数方法的使用
  4. oracle, to_char(), to_number, ORA_01722
  5. Java并发编程实战(chapter_3)(线程池ThreadPoolExecutor源码分析)
  6. 树莓派应用实例3:环境光照强度测量
  7. 南方日报专访 | 网易云信沈青松: 企业上云正热,PaaS将迎来爆发
  8. python中遍历结构可以是哪些数据类型_全!Python基础之原生数据类型、判断和循环、函数和文件操作合集...
  9. 【转】页(page),用户控件(userControl),窗口(window)区别
  10. 记录用户转成超级用户的文件名字_一分钟了解Linux用户
  11. PHP通知弹窗代码_公告弹窗
  12. 多线程,并发,异步,死锁
  13. Nginx 性能调优
  14. [EMNLP2017]Global Normalization of Convolutional Neural Networks for Joint Entity and Relation(阅读笔记)
  15. ArcMap2SLD添加中文支持
  16. Chrome去除新标签页最近访问
  17. 同步电路与跨时钟域电路设计2——多bit信号的跨时钟域传输(FIFO)
  18. 3dsmax顶点死活焊接不上的原因!
  19. 将其他人物模型动画导入Carla使用
  20. 次世代zbrush骷颅头高模雕刻 艺术头骨SP模型材质贴图讲解

热门文章

  1. 2021-10-27 我与地坛
  2. 【C#】数组的最大最小值
  3. 2021年大数据Hive(五):Hive的内置函数(数学、字符串、日期、条件、转换、行转列)
  4. 2021年大数据Flink(二):Flink用武之地
  5. 【CV】Numpy|Python中矩阵和数组乘法及向量相关问题
  6. HarmonyOS 修改App的默认加载的界面
  7. HarmonyOS Unknown HarmonyOS XML attribute
  8. Windows 10系统中如何显示文件后缀名
  9. 微信小程序scroll-view的使用
  10. android studio 常量表达式错误