深度学习实践向之优化方法

来源:互联网 发布:程序授权系统源码 编辑:程序博客网 时间:2024/06/14 00:08

概览

  • 先说干货吧,如果面向应用,只关注哪一个方法更好用,下面这些总结希望能帮到你~
    • 如果就想选个最好用的,那就Adam(算是当前算法背景下很通用的,实质就是具动量(或者动量变种Nestrov)的RMSProp)
    • 最流行并且使用率很高的算法包括SGD、具动量(Momentum)的SGD、RMSProp、具动量的RMSProp、AdaDelta和Adam。
    • 具有自适应学习率的算法(以AdaDelta和RMSProp为代表)收敛速度更快一些,表现得更加鲁棒,当然最通用的就是Adam啦,说白了就是具有动量变种的AdaDelta或RMSProp,只不过算法的抽象程度更高(统一到一阶和二阶的矩估计啦)。
    • 基于SGD的算法相对而言收敛速度更慢一些,但是精调以后准确率可能会提高,所谓精调就是具动量(不过说实话还是慢。。。比起自适应学习率),还有就是不能忽视也不能容忍的SGD无法逃离局部极值点(或鞍点),所以有时候模型精度不如自适应学习率的。
    • 前面都有提到动量,它的目的就是为了加速学习过程。和物理上的动量类似,这里的动量是指在梯度下降时仍在一定程度上维持之前下降的方向。动量方法及其变种(Nestrov)一般与别的方法结合起来使用,比如具动量的SGD、具动量的RMSProp(你可以把这当做Adam)。
    • 17年又有新的paper阐述优化方法的进步了,看完了再来总结。
  • 之前写过一篇深度学习优化方法的阅读笔记,这次从理论与实践结合的角度来对优化方法做一个梳理。
  • 对各种优化方法的原理进行简单阐述,并结合tensorflow和keras进行对应的讲解。(目前先写TensorFlow的)

1.优化的认识

  • 纯优化最小化目标函数本身(从学习的角度来看,只关注训练过程中的损失函数,而不关注测试集的代价函数及模型的泛化能力)
  • 纯优化不同的是,学习关注的是对模型泛化能力度量的损失函数的最小化,而这个损失函数不可以直接解出来,因此通过一种间接的方式,即对训练集上的性能作出评估的损失函数再加上额外的正则化项,来近似测试集上的代价函数(模型的泛化能力的评估)。
  • 深度模型相比浅层模型的复杂性,以及理论上欠缺的完备性,优化方法种类也是比较多。总的原则仍然是反向过程的误差反传,无论是梯度下降的变种还是一系列自适应学习率的算法,都遵循这个总的原则。

2.非严格的优化方法分类

根据训练过程中使用的数据集的多少对优化方法进行一个不严格的分类。

  • 批量或确定性算法—batch
    一次训练过程中的迭代就使用了整个训练集的优化方法,可以称之为批量或确定性算法。容易想到的是,这个方法的缺陷就是计算资源和消耗和效率问题。(我自己的台式2G显存一次处理整个数据集,都没试过,甚至于mini—batch大点就奔溃了。。。当你经过一周跑一个模型的时候就可以体会这个效率问题。。。)
    这里需要注意的是这里的批量一般和我们理解的不一样,我们现在所说的批量一般指mini-batch。
  • 随机或者在线算法
    一次迭代过程中只使用一个样本的优化算法。‘在线’一般指从连续产生的数据流中抽取样本的情况,而不是一个数据集的多次遍历多次采样。这个方法最大的缺陷就是不确定性,模型的收敛也是看这个不确定性的运气了。。。
  • 小批量算法—mini-batch
    我们现在说的批量一般就是指这个小批量算法,也就是mini-batch。使用一个以上而又不是全部的训练样本。这个多少一般就是我们在模型中设定的超参数batch_size,习惯性的设置为64、128诸如此类的。同时这个批量的大小是值得研究的一个问题,关系到计算资源和训练效率等等。
    现在基本上优化方法都是基于这个小批量算法,传统上这么称呼,但现在更多的称为随机方法(工业界和应用界),即做了统一,但是我们需要明白的是严格意义上的和传统意义上的随机方法是指单个样本。
    从小批量这个意义上来说,自适应学习率算法也可以归结到这一类(单从数据规模来考虑)。

3.梯度下降变种之SGD

  • 上一节中提到了目前所说的随机算法就是指基于小批量的算法。这里的SGD也是如此,是一般意义上的梯度下降算法应用到小批量(mini-batch)上。
  • 具体地,在一次训练的迭代过程中,随机选取一批数据(小批量的大小)进行一次训练,这里小批量的大小在编程中就是指batch_size
  • TensorFlow
    tf中并没有明显的SGD,但其实GDO就相当于SGD的实现。有一个类tf.train.GradientDescentOptimizer,结合上我们指定的batch_size以及数据处理过程中的shuffle-随机打乱,就实现了随机小批量的梯度下降,也即SGD。
    一般编程过程中我们先对数据进行随机打乱,然后依次按照batch_size提取数据。下面介绍随即打乱数据的几种方法。
# data shuffle 对数据进行打乱# 1.1利用pandas处理数据import pandas as pddata = pd.read_csv(your_data_path)data = data.sample(frac=1.0) # 返回的比例,1表示全部返回# 1.2利用机器学习库sklearn进行shufflefrom sklearn.utils import shuffledata = shuffle(data)# 1.3利用numpy中random随机采样打乱import numpy as npdata = a[np.random.permutation(len(data))]
  • 对数据进行随机打乱以后,就要产生batch_size大小的数据。用一段代码简单模拟这个过程。同时对epoch和iteration的概念进行说明。epoch,其实是指全部使用了一次训练集中的数据才完成了一次epoch。而我们通常所说的迭代即iteration是指完成了一次训练使用的数据集大小是batch_size。代码里借助生成器(generator)来实现连续不断产生batch_size大小个数据,直到使用完全部的数据集,此时即完成了一次epoch,而每次产生的batch_size大小的数据进行训练,相当于完成了一次iteration。
# 利用yield连续不断产生batch_size大小个数据,直至使用完一次数据集def gen_batch(x,y,batch_size=64):    data_len = len(x)    num_batch = (data_len-1)//batch_size + 1 # 一共可以产生多少个batch_size的数据    for i in range(num_batch):        start_index = i*batch_size        end = min((i+1)*batch_size, data_len)        yield x[start, end], y[start, end]# 后续使用数据的方式for i in range(epoch_nums):    batch_gen = gen_batch(x, y, batch_size =128)    step = 0    for batch_x, batch_y in batch_gen: # 对生成器中的数据访问方式,越界时自动停止。此时进行了len(data)-1/batch_size+1轮迭代,进行了一次epoch。    loss_, acc_, train = sess.run([loss, acc, train_op], feed_dict={x:batch_x, y:batch_y})    print(step+1, loss_, acc_)    step += 1 
  • 接下来介绍TensorFlow中GDO官方文档说明,首先从整体上了解一下。tf.train.Optimizer是基类,具体地,目前实现的优化方法有6种。
    这里写图片描述
# 重点关注的参数就是learning_rate,学习率,其实就是梯度下降的步长。optimizer = tf.train.GradientDescentOptimizer(learning_rate=1e-3, use_locking=False, name='GD')# 编程中一般更关注的是 minimize()函数train_op = optimizer.minimize(loss)
  • SGD的优点
    相比批量梯度下降
    • 收敛更稳定,减少了参数更新导致的方差。
    • 收敛速度更快,尤其是初始的快速更新。
  • SGD的缺点
    • 学习过程相对比较慢
    • 初始学习率的设置
    • 无法逃离局部极值点或者鞍点

4.梯度下降方法的优化

4.1 动量方法-Momentum以及变种Nesterov

  • SGD学习过程可能比较慢,而动量方法提出的初衷就是加速学习。动量算法积累了之前梯度指数级衰减的移动平均,是梯度下降沿这个方向继续进行。
  • 这个方法来自物理学中动量的类比,希望在梯度更新时能够考虑到之前梯度更新的方向。即一定程度保持之前的梯度方向。动量方法中的速度担任这一角色,被设定为负梯度的指数衰减平均。

  • Momentum
    假定α为学习率,v为初始速度,β为动量的更新步长,则与SGD相比不同的就是梯度的计算。算法基本过程如下:
    g
    v=αvβg
    θ=θ+v

  • Nesterov
    与Momentum方法不同在于梯度的计算上,先对参数进行更新(施加当前速度)再进行梯度的计算。从这一角度考虑,相当于在标准动量方法中添加了一个矫正因子。算法基本过程如下:
    θ=θ+αv
    g
    v=αvβg
    θ=θ+v

  • tf.train.MomentumOptimizer
    TensorFlow中用这一方法实现了这两种方法,其实严格来说实现的是具动量的SGD或具Nestrov动量的SGD。

tf.train.MomentumOptimizer(learning_rate=lr, momentum=0.9, name=name,use_locking=False,use_nesterov=False)# args:# learning_rate-学习率# momentum-动量参数# use_nesterov-是否使用nesterov。

这里非一般意义上总结,一般而言SGD-Nesterov > SGD-Momentum > SGD。在实际编程过程中可以对这几种方法都进行尝试一下。


4.2自适应学习率方法

动量算法在一定程度上缓解了SGD方法的问题,但也引入了另一个超参数。同时这些方法在每一次进行梯度更新时,步长是一致的,即学习率。自适应学习率从这一点出发,为每个参数设置不同的学习率,在学习的过程中自动适应这些学习率。

4.2.1AdaGrad

AdaGrad旨在应用凸问题时快速收敛。

  • 维持一个累积变量:梯度的平方和 r+=gigi
  • 自适应学习率的计算:初始设定值反比于累积的梯度平方和的平方根 learning_rate=αr+σ
  • 考虑一个参数的更新
    • 1.计算梯度gi
    • 2.累积梯度平方和:r=r+gi2
    • 3.参数更新:θi=θiαr+σgi
      这里就体现了自适应学习率αr+σ,其实就是学习率的衰减。
  • 整体算法流程
    这里写图片描述

  • tf.train.AdagradOptimizer

tf.train.AdagradOptimizer(initial_accumulator_value=1e-6, learning_rate=1e-3) # 关键参数# initial_accumulator_value 为了除小数时的数值稳定
  • 缺陷
    • 梯度平方的积累会导致有效学习率过早的减小和减小的太多
    • 非凸情况下效果不好

4.2.3RMSProp

RMSProp算法修改AdaGrad使其在非凸设定下效果更好。改变梯度积累为指数加权的移动平均。

  • 与AdaGrad不同点就在于平方梯度的累积
    r=ρr+(1ρ)gi2
    通过ρ来控制过去的梯度积累的影响,时间越久影响越小,从而解决AdaGrad的问题。
  • 算法流程
    这里写图片描述
  • 使用Nesterov动量的RMSProp算法
    这里写图片描述

  • tf.train.RMSProp
    同样地,在TensorFlow中用这个类实现了基本的和具动量的RMSProp方法。

tf.train.RMSPropOptimizer(centered=False, decay=0.9, epsilon=1e-10,learning_rate=1e-3, momentum=0.)# decay-即梯度积累的衰减系数# centered-梯度计算的正则化方式,默认为False# 使用具动量的RMSProp时:# 设置momentum系数和epsilon=1e-10

4.2.4AdaDelta

这一方法与RMSProp方法十分类似,同样地也是针对AdaGrad方法的梯度问题(衰减过快,衰减过早)提出的。

  • 与RMSProp的不同点在于这里积累的是指数衰减的梯度平方的期望。E(r)=γE(r)+(1ρ)g2
    其余基本类似,不再赘述。

  • tf.train.AdadeltaOptimizer

tf.train.AdadeltaOptimizer(epsilon=1e-9, learning_rate=1e-3,rho=0.95)# 与RMSProp类比

4.2.5Adam

在当前算法背景下,Adam最好被看做结合RMSProp和动量算法的变种。

  • 首先在Adam中动量直接并入了梯度一阶矩(指数加权)的估计。
  • 其次加入了偏置修正,修正从原点初始化的一阶矩(动量项)和(非中心)的二阶矩(指数加权的梯度累计)的估计。
  • Adam通常认为对超参数的选择相当鲁棒(robust),有时候学习率可能需要从默认的1e-3修改。
  • 算法流程
    这里写图片描述

  • tf.train.AdamOptimize

tf.train.AdamOptimize(beta1=0.9, beta2=0.999, learning_rate=1e-3,epsilon=1e-8)# beta1:一阶矩估计的指数衰减速率 # beta2:二阶矩估计的指数衰减速率# epsilon:极小的数当除数时的值稳定性

参考资料

[1]深度学习中文翻译版
[2]TensorFlow官方文档

原创粉丝点击