优化器整理
梯度下降优化算法
标准梯度下降法(GD)
标准梯度下降法主要有两个缺点:
- 训练速度慢:每走一步都要要计算调整下一步的方向,下山的速度变慢。在应用于大型数据集中,每输入一个样本都要更新一次参数,且每次迭代都要遍历所有的样本。会使得训练过程及其缓慢,需要花费很长时间才能得到收敛解。
- 容易陷入局部最优解:由于是在有限视距内寻找下山的反向。当陷入平坦的洼地,会误以为到达了山地的最低点,从而不会继续往下走。所谓的局部最优解就是鞍点。落入鞍点,梯度为0,使得模型参数不在继续更新。
批量梯度下降法(BGD)
梯度下降中的困难
小小批量梯度下降不能保证良好的收敛性,还存在一些其他的问题:
- 很难选择一个合适的学习率,如果学习率太小,将会导致收敛非常缓慢;如果学习率太大,也会阻碍收敛,导致损失函数值在最小值附近波动甚至发散。
- 上述问题可以通过提前定义一个学习速率表,当达到相应轮数或者阈值时根据表改变学习率,但是这样无法适应训练数据本身特征。
- 对于所有参数我们使用同一个学习速率,如果我们的数据是稀疏的或者我们特征具有不同的频率,我们可能不希望将它们更新到同样的程度,并且我们希望对那些出现频率低的特征更新更快。
- 在神经网络中,普遍是具有非凸的误差函数,这使得在优化网络过程中,很容易陷入无数的局部最优点,而且更大困难往往也不是陷入局部最优点,而是来自鞍点(也就是在一个维度上其梯度是递增,另一个维度其梯度是递减,而在鞍点处其梯度为0),这些鞍点附近往往被相同误差点所包围,且在任意维度梯度近似为0,所以随机梯度下降很难从这些鞍点逃出。
梯度下降优化算法
用于解决上述困难的算法,这些算法有个共同之处,一般是求一阶动量(m)和二阶动量(V),然后利用一阶、二阶动量本身或者他们组合来优化梯度下降(其中一阶动量为与梯度相关函数,二阶动量为与梯度平方相关的函数)
动量(momentum)
随机梯度下降的方法很难通过峡谷区域(也就是在一个维度梯度变化很大,另一个维度变化较小),这个很好理解,因为梯度下降是梯度更新最大的反方向,如果这个时候一个维度梯度变化很大,那么就很容易在这个方向上振荡,另一个方向就更新很慢
- 没有加动量
加了动量的方法
有动量可以在变化小的维度上加快更新,使得加快收敛。该方法是通过添加一个参数β构建一个一阶动量m
“从本质上说,动量法,就像我们从山上推下一个球,球在滚下来的过程中累积动量,变得越来越快(直到达到终极速度,如果有空气阻力的存在,则γ<1)。同样的事情也发生在参数的更新过程中:对于在梯度点处具有相同的方向的维度,其动量项增大,对于在梯度点处改变方向的维度,其动量项减小。因此,我们可以得到更快的收敛速度,同时可以减少摇摆。”
根据表达式2,梯度方向是一直向下的,自然参数更新幅度也就是一直累加的,也就变得越来越大;而当遇到山沟,越过山沟此时就在另一边山坡,这个时候梯度方向是跟之前相反的,此时由于之前梯度大小的累加,在两个山坡间的变化就会被互相抵消掉,也就不会一直在两个山坡振荡,容易朝山沟向下走,也就是减少摇摆了
NAG(Nesterov accelerated gradient)
Adagrad
用一批数据训练网络,这个数据中只有少部分数据含有某个特征,另一个特征几乎全部数据都具有,当这些数据通过训练时,对于不同特征我们假设对应于不同的神经元权重,对于都含有的特征,这些神经元对应参数更新很快,但是对于那些只有少部分数据含有的特征,对应神经元权重获得更新机会就少,但是由于学习率一样,这样可能导致神经网络训练的不充分。
adagrad算法就是为了解决这个问题,让学习率学习数据的特征自动调整其大小,adagrad算法引入了二阶动量
但是adagrad同样也有问题,就是其分母随着训练数增加,也会跟着增加,这样会导致学习速率越来越小,最终变的无限小,从而无法有效更新参数
#Adagrad
v_w+=tf.square(grads[0])
v_b+=tf.square(grads[1])
w1.assign_sub(lr*grads[0]/tf.sqrt(v_w))
b1.assign_sub(lr*grads[1]/tf.sqrt(v_b))
adadelta
对于adagrad算法带来的分母越来越大的问题就可以解决了,但是,此算法以及之前提到的算法(SGD、动量、adagrad)的参数之间单位并不匹配,而按理说参数更新应该具有与参数相同的单位。
对随机梯度下降算法(SGD)来进行参数之间单位关系讨论
SGD算法的参数更新方程式为
假设loss的单位为b,而参数的单位为c,学习率没有单位,设为1,这个时候我们就发现,上面的等式的单位运算为:c=c-1*(b/c),这明显单位不匹配
解决方法就是利用与二阶动量V(t)类似的运算对参数变化量deta(θ)做运算求它的二阶动量
RMSprop
#RMSProp
v_w=beta*v_w+(1-beta)*tf.square(grads[0])
v_b=beta*v_b+(1-beta)*tf.square(grads[1])
w1.assign_sub(lr*grads[0]/tf.sqrt(v_w))
b1.assign_sub(lr*grads[1]/tf.sqrt(v_b))
Adam
Adam(Adaptive Moment Estimation)自适应矩估计,是另一种自适应学习率的算法,它是一种将动量和Adadelta或RMSprop结合起来的算法,也就引入了两个参数β1和β2,其一阶和二阶动量公式为:
发现一阶和二阶动量初始训练时很小,接近为0,因为β值很大,重新计算一个偏差来校正:
#Adam
m_w=0
m_b=0
v_w=0
v_b=0
beta1=0.9
beta2=0.999
delta_w=0
delta_b=0
global_step=0
m_w=beta1*m_w+(1-beta1)*grads[0]
m_b=beta1*m_b+(1-beta1)*grads[1]
v_w=beta2*v_w+(1-beta2)*tf.square(grads[0])
v_b=beta2*v_b+(1-beta2)*tf.square(grads[1])
m_w_correction=m_w/(1-tf.pow(beta1,int(global_step)))
m_b_correction=m_b/(1-tf.pow(beta1,int(global_step)))
v_w_correction=v_w/(1-tf.pow(beta2,int(global_step)))
v_b_correction=v_b/(1-tf.pow(beta2,int(global_step)))
w1.assign_sub(lr*m_w_correction/tf.sqrt(v_w_correction))
b1.assign_sub(lr*m_b_correction/tf.sqrt(v_b_correction))
AdaMax
Nadam
Adam将RMSprop和动量结合起来,我们也可以看到NAG其实比动量表现更好。
Nadam(Nesterov-accelerated Adaptive
Moment Estimation),Nesterov加速的自适应矩估计,将adam和NAG结合起来,为了将NAG添加到Adam,我们需要对动量部分进行一些改变
将NAG梯度更新公式变为:
AMSGrad
随着自适应学习速率的方法成为训练神经网络的规范,研究者就发现,在一些情况下比如目标识别、机器翻译领域,自适应学习速率的方法无法收敛到最佳情况,并且弱于基于动量的随机梯度下降。
其梯度更新公式为:
梯度下降变形形式
批量归一化(BGD)
每次经过完整一轮训练后更新一次参数,这使得梯度下降过程变得比较慢,并且需要很大内存保存中间结果。
for i in range(nb_epochs):
params_grad = evaluate_gradient(loss_function, data, params)
params =params - learning_rate * params_grad
随机梯度下降(SGD)
随机梯度下降是对每个训练样本就更新一次网络参数,这样使得网络更新参数速度很快,但是问题就是由于训练数据多样,容易朝偏离网络最优点方向训练,网络训练不稳定。
for i in range(nb_epochs):
np.random.shuffle(data)
for example in data:
params_grad= evaluate_gradient(loss_function, example, params)
params =params - learning_rate * params_grad
优点
- 虽然SGD需要走很多步的样子,但是对梯度的要求很低(计算梯度快)。而对于引入噪声,大量的理论和实践工作证明,只要噪声不是特别大,SGD都能很好地收敛。
- 应用大型数据集时,训练速度很快。比如每次从百万数据样本中,取几百个数据点,算一个SGD梯度,更新一下模型参数。相比于标准梯度下降法的遍历全部样本,每输入一个样本更新一次参数,要快得多。
缺点
- SGD在随机选择梯度的同时会引入噪声,使得权值更新的方向不一定正确。
- SGD也没能单独克服局部最优解的问题。
SGDM
SGDM是在SGD的基础上增加了一阶动量,二阶动量仍为1
#SGDM
m_w=beta*m_w+(1-beta)*grads[0]
m_b=beta*m_b+(1-beta)*grads[1]
w1.assign_sub(lr*m_w)
b1.assign_sub(lr*m_b)
小批量梯度下降(MBGD)
小批量梯度下降是批量梯度下降与随机梯度下降之间的一个折中,即经过一个小批量的训练数据更新一次参数,可以保证网络训练速度不太慢,也能使训练方向不至于偏离太多,具有一定稳定性。当使用小批量梯度下降时,通常也使用SGD这个术语。
for i in range(nb_epochs):
np.random.shuffle(data)
for batch in get_batches(data, batch_size=50):
params_grad= evaluate_gradient(loss_function, batch, params)
params =params - learning_rate * params_grad
应用于简单分类问题的比较
Tensorflow中封装了一系列的优化器
采用选取几种优化器应用于UCI数据集iris.data简单的分类问题
使用SGD优化器
train_step = tf.train.GradientDescentOptimizer(0.2).minimize(loss)
第1个训练周期训练集的准确率为:33.3%, 测试集的准确率为:32.2%
第2个训练周期训练集的准确率为:33.3%, 测试集的准确率为:32.2%
第3个训练周期训练集的准确率为:38.9%, 测试集的准确率为:35.6%
第4个训练周期训练集的准确率为:62.2%, 测试集的准确率为:62.7%
第5个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第6个训练周期训练集的准确率为:66.7%, 测试集的准确率为:67.8%
第7个训练周期训练集的准确率为:66.7%, 测试集的准确率为:67.8%
第8个训练周期训练集的准确率为:67.8%, 测试集的准确率为:67.8%
第9个训练周期训练集的准确率为:67.8%, 测试集的准确率为:67.8%
第10个训练周期训练集的准确率为:67.8%, 测试集的准确率为:67.8%
使用AdaGrad优化器
train_step = tf.train.AdagradOptimizer(0.01).minimize(loss)
第1个训练周期训练集的准确率为:33.3%, 测试集的准确率为:32.2%
第2个训练周期训练集的准确率为:35.6%, 测试集的准确率为:32.2%
第3个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第4个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第5个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第6个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第7个训练周期训练集的准确率为:66.7%, 测试集的准确率为:67.8%
第8个训练周期训练集的准确率为:66.7%, 测试集的准确率为:67.8%
第9个训练周期训练集的准确率为:67.8%, 测试集的准确率为:69.5%
第10个训练周期训练集的准确率为:68.9%, 测试集的准确率为:69.5%
从运行结果上可以看出使用AdaGrad优化器相比于SGD似乎没有较大的提升。
使用Momentum优化器
train_step = tf.train.MomentumOptimizer(learning_rate=0.01, momentum=0.9).minimize(loss)
第1个训练周期训练集的准确率为:58.9%, 测试集的准确率为:61.0%
第2个训练周期训练集的准确率为:40.0%, 测试集的准确率为:42.4%
第3个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第4个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第5个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第6个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第7个训练周期训练集的准确率为:67.8%, 测试集的准确率为:67.8%
第8个训练周期训练集的准确率为:67.8%, 测试集的准确率为:67.8%
第9个训练周期训练集的准确率为:67.8%, 测试集的准确率为:69.5%
第10个训练周期训练集的准确率为:70.0%, 测试集的准确率为:67.8%
Momentum优化器略优于AdaGrad优化器和SGD,但收敛速度要比后两者快(第1-2个训练周期就可以达到60%的准确率)。
使用NAG优化器
train_step = tf.train.MomentumOptimizer(learning_rate=0.01, momentum=0.9, use_nesterov=True).minimize(loss)
第1个训练周期训练集的准确率为:37.8%, 测试集的准确率为:39.0%
第2个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第3个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第4个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第5个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第6个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第7个训练周期训练集的准确率为:66.7%, 测试集的准确率为:67.8%
第8个训练周期训练集的准确率为:67.8%, 测试集的准确率为:69.5%
第9个训练周期训练集的准确率为:70.0%, 测试集的准确率为:69.5%
第10个训练周期训练集的准确率为:71.1%, 测试集的准确率为:69.5%
NAG和Momentum结果差不多,NAG好一点点
使用RMSProp优化器
train_step = tf.train.RMSPropOptimizer(0.01).minimize(loss)
(RMSProp运行结果不稳定,下面是出现比较多的相近结果)
第1个训练周期训练集的准确率为:33.3%, 测试集的准确率为:32.2%
第2个训练周期训练集的准确率为:33.3%, 测试集的准确率为:32.2%
第3个训练周期训练集的准确率为:35.6%, 测试集的准确率为:35.6%
第4个训练周期训练集的准确率为:48.9%, 测试集的准确率为:49.2%
第5个训练周期训练集的准确率为:35.6%, 测试集的准确率为:35.6%
第6个训练周期训练集的准确率为:38.9%, 测试集的准确率为:42.4%
第7个训练周期训练集的准确率为:51.1%, 测试集的准确率为:50.8%
第8个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第9个训练周期训练集的准确率为:60.0%, 测试集的准确率为:62.7%
第10个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
RMSProp优化器的效果不理想,而且不稳定
使用Adam优化器
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
第1个训练周期训练集的准确率为:33.3%, 测试集的准确率为:33.9%
第2个训练周期训练集的准确率为:36.7%, 测试集的准确率为:37.3%
第3个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第4个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第5个训练周期训练集的准确率为:66.7%, 测试集的准确率为:67.8%
第6个训练周期训练集的准确率为:67.8%, 测试集的准确率为:67.8%
第7个训练周期训练集的准确率为:67.8%, 测试集的准确率为:67.8%
第8个训练周期训练集的准确率为:71.1%, 测试集的准确率为:67.8%
第9个训练周期训练集的准确率为:71.1%, 测试集的准确率为:69.5%
第10个训练周期训练集的准确率为:71.1%, 测试集的准确率为:69.5%
使用AdaDelta优化器
train_step = tf.train.AdadeltaOptimizer(1).minimize(loss)
第1个训练周期训练集的准确率为:60.0%, 测试集的准确率为:64.4%
第2个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第3个训练周期训练集的准确率为:66.7%, 测试集的准确率为:66.1%
第4个训练周期训练集的准确率为:66.7%, 测试集的准确率为:67.8%
第5个训练周期训练集的准确率为:67.8%, 测试集的准确率为:69.5%
第6个训练周期训练集的准确率为:70.0%, 测试集的准确率为:69.5%
第7个训练周期训练集的准确率为:72.2%, 测试集的准确率为:71.2%
第8个训练周期训练集的准确率为:73.3%, 测试集的准确率为:76.3%
第9个训练周期训练集的准确率为:75.6%, 测试集的准确率为:84.7%
第10个训练周期训练集的准确率为:76.7%, 测试集的准确率为:88.1%
AdaDelta优化器效果很好
可视化比较
参考博客
https://blog.csdn.net/qq_42109740/article/details/105401197?utm_source=app&app_version=4.5.2