自适应学习率算法
在使用基本的梯度下降算法时,会遇到一个常见的问题—-要优化的各个参数对于目标函数的依赖各不相同。 比较合理的做法是:对每个参与训练的参数设置不同的学习率,在整个学习的过程中通过一些算法自动适应这些参数的学习率。
1.AdaGrad(Adaptive Gradient)
AdaGrad的基本思想是对每个变量用不同的学习率,这个学习率在一开始比较大,用于快速梯度下降。随着优化过程的进行,对于已经下降很多的变量,则减缓学习率,对于还没怎么下降的变量,则保持一个较大的学习率。
r += dw**2
w += - learning_rate * dw / (np.sqrt(r) + le-7)
(1)从AdaGrad算法中可以看出,随着算法不断迭代,r会越来越大,整体的学习率会越来越小。所以,一般来说AdaGrad算法一开始是激励收敛,到了后面就慢慢变成惩罚收敛,速度越来越慢。
(2)在SGD中,随着梯度的增大,我们的学习步长应该是增大的。但是在AdaGrad中,随着梯度g的增大,我们的r也在逐渐的增大,且在梯度更新时r在分母上,也就是整个学习率是减少的.
随着更新次数的增大,我们希望学习率越来越慢。因为我们认为在学习率的最初阶段,我们距离损失函数最优解还很远,随着更新次数的增加,越来越接近最优解,所以学习率也随之变慢。
(3)经验上已经发现,对于训练深度神经网络模型而言,从训练开始时积累梯度平方会导致有效学习率过早和过量的减小。AdaGrade在某些深度学习模型上效果不错,但不是全部
AdaGrad使得对每个参数自适应不同的学习速率,对稀疏特征,得到大的学习更新,对非稀疏特征,得到较小的学习更新,因此该优化算法适合处理稀疏特征数据。
Adagrad在垂直方向上的梯度会加到r中,然后相应的会除以越来越大的数,所以在垂直方向上会得到越来越小的更新。当我们在垂直方向上看到许多大的梯度,Adagrad就会衰减学习速率,使垂直方向的更新步长越来越小。在水平方向上的梯度是很小的,所以分母会小,相比于垂直方向,水平方向更新更快。这就是对每个参数自适应不同的学习速率,针对不同梯度方向的补偿措施。
对应的优化器tf.train.AdagradOptimizer
def__init__(self,learning_rate, initial_accumulator_value=0.1, use_locking=False, name=’Adagrad’)
learning_rate: A Tensor or a floating point value. The learning rate. initial_accumulator_value: A floating point value. Starting value for the accumulators, must be positive. use_locking: If True use locks for update operations. name: Optional name prefix for the operations created when applying gradients. Defaults to "Adagrad".
train_step = tf.train.AdagradOptimizer(learning_rate=lr).minimize(loss)
import tensorflow as tf
import numpy as np
# create data
x_data = np.random.rand(100).astype(np.float32)
y_data = x_data*0.1 + 0.3
# tf.Variable 来创建描述 y 的参数.#
Weights = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
biases = tf.Variable(tf.zeros([1]))
#搭建模型
y = Weights*x_data + biases
#计算误差
loss = tf.reduce_mean(tf.square(y-y_data))
#Gradient Descent 让后我们使用 optimizer 来进行参数的更新.#
#optimizer = tf.train.GradientDescentOptimizer(0.5)
#train = optimizer.minimize(loss)
#train_step = tf.train.AdagradOptimizer(learning_rate=0.5).minimize(loss)
train_step = tf.train.RMSPropOptimizer(learning_rate=0.5).minimize(loss)
#到目前为止, 我们建立了神经网络的结构, 还没有使用这个结构. 在使用这个结构之前, 必须先初始化所有之前定义的Variable#
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for step in range(201):
sess.run(train_step)
if step % 20 == 0:
print(step, sess.run(Weights), sess.run(biases))
在Adagrad算法中,不断有正数加到分母的r变量中,步长就会逐渐衰减到0,最后完全停止学习。
但是在神经网络中,我们需要整个网络不断变化从而修正数据,这才是正确的思考方式。它需要持续的活力来不断更新数据,而不是衰退到停止。所以,针对这一问题,Hinton对Adagrad做了个小小的改变,也被称之为RMSProp算法。RMSprop解决了 Adagrad 学习率急剧下降问题
RMSProp(root mean square prop)
RMSProp算法采用了指数衰减平均的方式淡化遥远过去的历史对当前步骤参数更新量的影响,引入了一个新的参数ρ—历史梯度值的衰减速率。
r += pt*r + (1-p)*dw**2
w += - learning_rate * dw / (np.sqrt(r) + le-7)
因为RMSProp算法的状态变量r是对平方项g⊙g的指数加权移动平均,我们仍然保持了在梯度较大或较小方向上,对于更新步长的补偿效果,但是不会再发生更新停止的情况。
优化器tf.train.RMSPropOptimizer
def__init__(
learning_rate,
decay=0.99,
momentum=0.0,
epsilon=1e-10,
use_locking=False,
centered=False,
name="RMSProp"
learning_rate: (学习率) decay=0.9,衰减率,默认值为0.9 epsilon=1e-10,小常数,保持数值稳定性 use_locking=False,
除学习率可调整外,建议保持优化器的其他默认参数不变
train_step = tf.train.RMSPropOptimizer(learning_rate=lr).minimize(loss)
在优化表现上和Adagrad差不多,但是它的初始学习率比Adagrad 要小很多,而更新的速度也比Adagrad 快。
Momentum(动量法)
加上动量项就像从山顶滚下一个球,球往下滚的时候累积了前面的动量(动量不断增加),因此速度变得越来越快,直到到达终点。同理,在更新模型参数时,对于那些当前的梯度方向与上一次梯度方向相同的参数,那么进行加强,即这些方向上更快了;对于那些当前的梯度方向与上一次梯度方向不同的参数,那么进行削减,即这些方向上减慢了。即在陡峭的方向上削弱这些动荡,在一致的平坦的方向激励此过程。因此可以获得更快的收敛速度与减少振荡。
v = p * v + p * dw
w -= v
dw 是我们计算出来的原始梯度,v则是用指数加权平均计算出来的梯度。这相当于对原始梯度做了一个平滑,然后再用来做梯度下降。
优化函数的梯度下降最快的方向不一定是接近函数最小值的方向,例如: z=x^2 + 50y^2. y方向的梯度一直占据主要优势,函数的等高线图为:
在使用2中的梯度下降的时候,肯定会在y方向上不断的跳跃。例如:
在优化的过程中,朝向正确优化方向前进的步伐比较小,而无效的重复步伐比较多,优化的效率就很低。 如果在梯度下降中加入动量,这样y方向带来的震荡的梯度就会抵消,而x方向的梯度持续增强(因为x方向没有震荡),优化速度就会越来越快。效果如下:
Momentum通过对原始梯度做了一个平滑,正好将纵轴方向的梯度抹平了,使得参数更新方向更多地沿着横轴进行,因此速度更快。
train_step = tf.train.MomentumOptimizer(learning_rate=lr).minimize(loss)
Adam:Adaptive Moment Estimation
相当于 RMSprop + Momentum
s = p1 * s + (1-p) * dw
r = p2 * r + (1-p2) * (dw**2)
s /= 1-p1**t
r /= 1-p2**t
w += - learning_rate * s / (np.sqrt(r)) + le-7)
Adam算法相当于先把原始梯度做一个指数加权平均,再做一次归一化处理,然后再更新梯度值。
优化器 tf.train.AdamOptimizer
def__init__(
learning_rate=0.001,
beta1=0.9,
beta2=0.999,
epsilon=1e-08,
use_locking=False,
name='Adam'
)
learning_rate: (学习率)张量或者浮点数
•beta1: 浮点数或者常量张量 ,表示 The exponential decay rate for the 1st moment estimates.第一时刻的指数衰减率估计。
•beta2: 浮点数或者常量张量 ,表示 The exponential decay rate for the 2nd moment estimates.
•epsilon: A small constant for numerical stability. This epsilon is "epsilon hat" in the Kingma and Ba paper (in the formula just before Section 2.1), not the epsilon in Algorithm 1 of the paper.数值稳定性
•use_locking: 为True时锁定更新
•name: 梯度下降名称,默认为 "Adam".
train_step = tf.train.AdamOptimizer(learning_rate=lr).minimize(loss)
在实际的使用过程中,一般会选择Adam和RmsProp两个优化器。
学习率的独立设置(逐步减小已经设置好的学习率,非自适应)
#指数衰减的学习率
decayed_learning_rate:当前使用的学习率
learning_rate:初始学习率
decay_rate:衰减系数
global_step:当前训练进行的轮数
decay_steps:衰减速度(decay_steps值越大,衰减的越慢;在阶梯衰减中表示每训练多少轮衰减一次)
import tensorflow as tf
import numpy as np
# create data
x_data = np.random.rand(100).astype(np.float32)
y_data = x_data*0.1 + 0.3
# tf.Variable 来创建描述 y 的参数.#
Weights = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
biases = tf.Variable(tf.zeros([1]))
#搭建模型
y = Weights*x_data + biases
#计算误差
loss = tf.reduce_mean(tf.square(y-y_data))
#-------------------------------------------------#
training_step = tf.Variable(0)
#使用exponential_decay()函数设置学习率,global_step的值为training_step
#exponential_decay(learning_rate,global_step,decay_steps,decay_rate,staircase,name)
#staircase的默认值为False,表示连续衰减;True,阶梯衰减
decayed_learning_rate = tf.train.exponential_decay(0.8,training_step,100,0.9,False) #False
train_step = tf.train.GradientDescentOptimizer(learning_rate=decayed_learning_rate).minimize(loss,global_step=training_step)
#---------------#
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for step in range(201):
sess.run(train_step)
if step % 20 == 0:
print(step, sess.run(Weights), sess.run(biases),sess.run(loss),"学习率:",sess.run(decayed_learning_rate))
#反时限学习率衰减
train.inverse_time_decay()函数实现了反时限学习率衰减。
inverse_time_decay(learning_rate, global_step, decay_steps, decay_rate,staircase=False,name=None)
import tensorflow as tf
import numpy as np
# create data
x_data = np.random.rand(100).astype(np.float32)
y_data = x_data*0.1 + 0.3
# tf.Variable 来创建描述 y 的参数.#
Weights = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
biases = tf.Variable(tf.zeros([1]))
#搭建模型
y = Weights*x_data + biases
#计算误差
loss = tf.reduce_mean(tf.square(y-y_data))
#-------------------------------------------------#
training_step = tf.Variable(0)
#使用train.inverse_time_decay()函数设置学习率,global_step的值为training_step
decayed_learning_rate = tf.train.inverse_time_decay(0.8,training_step,100,0.9,False)
train_step = tf.train.GradientDescentOptimizer(learning_rate=decayed_learning_rate).minimize(loss,global_step=training_step)
#---------------#
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for step in range(201):
sess.run(train_step)
if step % 20 == 0:
print(step, sess.run(Weights), sess.run(biases),sess.run(loss),"学习率:",sess.run(decayed_learning_rate))
自然指数学习率衰减
计算公式: decayed_learning_rate = learning_rate * exp(-decay_rate * global_step)
natural_exp_decay(learning_rate, global_step, decay_steps, decay_rate, staircase=False, name=None)
#分片常数学习率衰减
前5000轮迭代使用0.8作为学习率,5000轮到1W轮使用0.6作为学习率,以后使用0.2作为学习率。
piecewise_constant(x, boundaries, values, name=None)
boundaries = [5000, 10000]
values = [0.8, 0.6, 0.2]
#多项式学习率衰减
特点是确定结束的学习率。
polynomial_decay(learning_rate, global_step, decay_steps,end_learning_rate, power=1.0,cycle=False, name=None)
•end_learning_rate:最低的最终学习率.
•power:多项式的幂,默认为1.0.
•cycle:学习率下降后是否重新上升.
如果cycle为False, global_step = decay_steps时,衰减停止,学习率不再变化。
如果cycle为True,则学习率下降后在global_step = decay_steps时重新上升;decay_steps参数值使用初始decay_steps的倍数.
ceil表示向上取整. decay_steps = decay_steps * ceil(global_step / decay_steps)
使用cycle = True的目的:防止神经网络训练后期学习率过小导致网络一直在某个局部最小值中振荡;这样,通过增大学习率可以跳出局部极小值.
红色:下降后不再上升;绿色:下降后重新上升:
import tensorflow as tf
import numpy as np
# create data
x_data = np.random.rand(100).astype(np.float32)
y_data = x_data*0.1 + 0.3
# tf.Variable 来创建描述 y 的参数.#
Weights = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
biases = tf.Variable(tf.zeros([1]))
#搭建模型
y = Weights*x_data + biases
#计算误差
loss = tf.reduce_mean(tf.square(y-y_data))
#-------------------------------------------------#
training_step = tf.Variable(0)
#使用train.inverse_time_decay()函数设置学习率,global_step的值为training_step
decayed_learning_rate = tf.train.polynomial_decay(0.9,training_step,100,end_learning_rate=0.01, power=1.0,cycle=True, name=None)
train_step = tf.train.GradientDescentOptimizer(learning_rate=decayed_learning_rate).minimize(loss,global_step=training_step)
#---------------#
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for step in range(201):
sess.run(train_step)
if step % 20 == 0:
print(step, sess.run(Weights), sess.run(biases),sess.run(loss),"学习率:",sess.run(decayed_learning_rate))
欢迎交流指正!