概览
- 先说干货吧,如果面向应用,只关注哪一个方法更好用,下面这些总结希望能帮到你~
- 如果就想选个最好用的,那就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 pd
data = pd.read_csv(your_data_path)
data = data.sample(frac=1.0) # 返回的比例,1表示全部返回
# 1.2利用机器学习库sklearn进行shuffle
from sklearn.utils import shuffle
data = shuffle(data)
# 1.3利用numpy中random随机采样打乱
import numpy as np
data = 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 v 为初始速度, 为动量的更新步长,则与SGD相比不同的就是梯度的计算。算法基本过程如下:
计算梯度:g 计 算 梯 度 : g
速度更新