如何为 GradientDescentOptimizer 设置自适应学习率?

Posted

技术标签:

【中文标题】如何为 GradientDescentOptimizer 设置自适应学习率?【英文标题】:How to set adaptive learning rate for GradientDescentOptimizer? 【发布时间】:2016-02-28 10:42:23 【问题描述】:

我正在使用 TensorFlow 来训练神经网络。这就是我初始化GradientDescentOptimizer的方式:

init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)

mse        = tf.reduce_mean(tf.square(out - out_))
train_step = tf.train.GradientDescentOptimizer(0.3).minimize(mse)

这里的问题是我不知道如何设置学习率的更新规则或衰减值。

如何在此处使用自适应学习率?

【问题讨论】:

在指定优化器之后初始化所有变量是一个好习惯,因为像 AdamOptimizer 这样的优化器使用自己的变量,这些变量也需要初始化。否则,您可能会收到如下所示的错误:FailedPreconditionError (see above for traceback): Attempting to use uninitialized value beta2_power 当我尝试通过tf.train.GradientDescentOptimizer(new_lr).minimize(loss) 在 Tensorflow 中设置新的学习率时,我遇到了上述错误。似乎,设置一个新的学习率需要用已经训练的变量初始化模型。但不知道该怎么做。 【参考方案1】:

首先,tf.train.GradientDescentOptimizer 旨在为所有步骤中的所有变量使用恒定的学习率。 TensorFlow 还提供了开箱即用的自适应优化器,包括 tf.train.AdagradOptimizertf.train.AdamOptimizer,这些都可以用作替代品。

然而,如果你想用普通的梯度下降来控制学习率,你可以利用learning_rate 参数tf.train.GradientDescentOptimizer constructor 可以是Tensor 对象这一事实。这允许您在每个步骤中计算不同的学习率值,例如:

learning_rate = tf.placeholder(tf.float32, shape=[])
# ...
train_step = tf.train.GradientDescentOptimizer(
    learning_rate=learning_rate).minimize(mse)

sess = tf.Session()

# Feed different values for learning rate to each training step.
sess.run(train_step, feed_dict=learning_rate: 0.1)
sess.run(train_step, feed_dict=learning_rate: 0.1)
sess.run(train_step, feed_dict=learning_rate: 0.01)
sess.run(train_step, feed_dict=learning_rate: 0.01)

或者,您可以创建一个标量 tf.Variable 来保存学习率,并在每次要更改学习率时分配它。

【讨论】:

很好的答案。可以将相同的技术用于渐变剪裁吗? tf.clip_by_norm 不接受剪辑范数的张量,那么 [(tf.minimum(gv[0], ct), gv[1]) for gv in optimizer.compute_gradients(cost, vars)] 怎么样,ct = tf.placeholder('float32', shape=[]) 应该可以,是的。 (尽管查看tf.clip_by_norm,唯一阻止它接受张量作为输入的是constant_op.constant(1.0 / clip_norm)。用math_ops.inv(clip_norm) 替换该表达式将使其与占位符(或任何其他张量)输入一起使用。)跨度> @mrry 我按照你说的做了,还有一些训练速度慢得多。请问这是预期的吗?【参考方案2】:

Tensorflow 提供了一个自动将指数衰减应用于学习率张量的操作:tf.train.exponential_decay。有关它的使用示例,请参阅this line in the MNIST convolutional model example。然后使用上面@mrry 的建议将此变量作为 learning_rate 参数提供给您选择的优化器。

要查看的关键摘录是:

# Optimizer: set up a variable that's incremented once per batch and
# controls the learning rate decay.
batch = tf.Variable(0)

learning_rate = tf.train.exponential_decay(
  0.01,                # Base learning rate.
  batch * BATCH_SIZE,  # Current index into the dataset.
  train_size,          # Decay step.
  0.95,                # Decay rate.
  staircase=True)
# Use simple momentum for the optimization.
optimizer = tf.train.MomentumOptimizer(learning_rate,
                                     0.9).minimize(loss,
                                                   global_step=batch)

注意global_step=batch 参数以最小化。这会告诉优化器在每次训练时帮助您增加“批处理”参数。

【讨论】:

通常,您调用batch 的变量称为global_step,并且有几个方便的函数,一个用于创建它tf.train.create_global_step()(它只是创建一个整数tf.Variable 并将其添加到tf.GraphKeys.GLOBAL_STEP 收藏)和tf.train.get_global_step()【参考方案3】:

梯度下降算法使用您可以在during the initialization 中提供的恒定学习率。您可以按照 Mrry 展示的方式传递各种学习率。

但你也可以用more advanced optimizers代替它,它收敛速度更快,适应情况。

以下是根据我的理解做一个简单的解释:

动量 helpsSGD 沿着相关方向导航并软化无关的波动。它只是将前一步的方向的一部分添加到当前步骤。这实现了在正确方向上的速度放大并软化了错误方向上的振荡。这个分数通常在 (0, 1) 范围内。使用自适应动量也很有意义。在开始学习时,大动量只会阻碍您的进步,因此使用 0.01 之类的值是有意义的,一旦所有高梯度消失,您就可以使用更大的动量。动量有一个问题:当我们非常接近目标时,大多数情况下我们的动量非常高,它不知道应该放慢速度。这可能会导致它错过或围绕最小值振荡 nesterov 加速梯度 通过提前开始减速来克服这个问题。在动量中,我们首先计算梯度,然后在这个方向上跳跃,这个方向被我们之前拥有的任何动量放大。 NAG 做同样的事情,但顺序不同:首先我们根据我们存储的信息做了一个大跳跃,然后我们计算梯度并做一个小的修正。这种看似无关紧要的变化带来了显着的实际加速。 AdaGrad 或自适应梯度允许学习率根据参数进行调整。它对不频繁的参数执行较大的更新,对频繁的参数执行较小的更新。因此,它非常适合稀疏数据(NLP 或图像识别)。另一个优点是它基本上消除了调整学习率的需要。每个参数都有自己的学习率,由于算法的特殊性,学习率是单调递减的。这导致了最大的问题:在某些时候学习率太小以至于系统停止学习 AdaDelta 解决了 AdaGrad 中学习率单调递减的问题。在 AdaGrad 中,学习率的计算近似为一除以平方根之和。在每个阶段,您都会在总和中添加另一个平方根,这会导致分母不断减少。在 AdaDelta 中,它不是对所有过去的平方根求和,而是使用允许总和减少的滑动窗口。 RMSprop 与 AdaDelta 非常相似

Adam 或自适应动量是一种类似于 AdaDelta 的算法。但除了存储每个参数的学习率之外,它还分别存储每个参数的动量变化

few visualizations:

【讨论】:

要比较 TensorFlow 中的不同优化器,请查看以下 ipython 笔记本:github.com/vsmolyakov/experiments_with_python/blob/master/chp03/… for 更高级的优化器不会被“替代”,但另外,请参阅stats.stackexchange.com/questions/200063/… @DimaLituiev 你能同时使用两个优化器吗?如果不是,那么您使用的是优化器 1 而不是优化器 2。 这不是我要说的,也不是这里的问题。您建议使用高级优化器而不是自适应学习率。我是说除了自适应学习率之外,您更愿意使用高级优化器【参考方案4】:

来自tensorflow官方文档

global_step = tf.Variable(0, trainable=False)
starter_learning_rate = 0.1
learning_rate = tf.train.exponential_decay(starter_learning_rate, global_step,
                                       100000, 0.96, staircase=True)

# Passing global_step to minimize() will increment it at each step.
learning_step = (
tf.train.GradientDescentOptimizer(learning_rate)
.minimize(...my loss..., global_step=global_step))

【讨论】:

【参考方案5】:

如果您想为像0 < a < b < c < ... 这样的时期间隔设置特定的学习率。然后,您可以将学习率定义为条件张量,以全局步骤为条件,并将其正常提供给优化器。

您可以使用一堆嵌套的 tf.cond 语句来实现这一点,但递归构建张量更容易:

def make_learning_rate_tensor(reduction_steps, learning_rates, global_step):
    assert len(reduction_steps) + 1 == len(learning_rates)
    if len(reduction_steps) == 1:
        return tf.cond(
            global_step < reduction_steps[0],
            lambda: learning_rates[0],
            lambda: learning_rates[1]
        )
    else:
        return tf.cond(
            global_step < reduction_steps[0],
            lambda: learning_rates[0],
            lambda: make_learning_rate_tensor(
                reduction_steps[1:],
                learning_rates[1:],
                global_step,)
            )

然后要使用它你需要知道单个epoch有多少个训练步骤,这样我们就可以使用全局步骤在合适的时间切换,最后定义你想要的epochs和learning rate。因此,如果我想分别在[0, 19], [20, 59], [60, 99], [100, \infty] 的纪元间隔内获得[0.1, 0.01, 0.001, 0.0001] 的学习率,我会这样做:

global_step = tf.train.get_or_create_global_step()
learning_rates = [0.1, 0.01, 0.001, 0.0001]
steps_per_epoch = 225
epochs_to_switch_at = [20, 60, 100]
epochs_to_switch_at = [x*steps_per_epoch for x in epochs_to_switch_at ]
learning_rate = make_learning_rate_tensor(epochs_to_switch_at , learning_rates, global_step)

【讨论】:

以上是关于如何为 GradientDescentOptimizer 设置自适应学习率?的主要内容,如果未能解决你的问题,请参考以下文章

如何为下拉菜单制作 CSS 边框?

如何为 CAShapeLayer 路径和填充颜色设置动画

iPhone - 如何为图像着色?

如何为 RecyclerView 设置 onItemClickListener? [复制]

WCF - 如何为 NTLM 身份验证配置 netTcpBinding?

如何为 UIToolbar 上的按钮添加滚动?