炼丹手册——学习率设置

深度神经网络的参数学习主要是通过梯度下降方法来寻找一组可以最小化结构风险的参数。而学习率在深度学习的训练过程中是一个很重要的超参数,指导我们该如何通过损失函数的梯度调整网络权重的超参数。学习率越低损失函数的变化速度就越慢。虽然使用低学习率可以确保我们不会错过任何局部极小值,但也意味着我们将花费更长的时间来进行收敛,特别是在被困在高原区域的情况下。下面就是梯度下降法的公式:

\theta_{1} := \theta_{1} - \alpha\frac{\partial}{\partial\theta_{1}}J(\theta_{1}) ,其中
\alpha 表示学习率。目前的各种优化算法本质上还是对梯度下降法的各种变形,所以我们还是以典型的梯度下降法来说明。借用下图:

v2-b3ea33a1b25d9c3b485ddf673725d6ed_b.jpg
  • 当学习率设置的较小,训练收敛较慢,需要更多的epoch才能到达一个较好的局部最小值;
  • 当学习率设置的较大,训练可能会在接近局部最优的附件震荡,无法更新到局部最优处;
  • 当学习率设置的非常大,正如上一篇文章提到可能直接飞掉,权重变为NaN;

那么如何去设置学习率这个超参数呢?总体上可以分为两种:人工调整或策略调整。

人工调整学习率一般是根据我们的经验值进行尝试,首先在整个训练过程中学习率肯定不会设为一个固定的值,原因如上图描述的设置大了得不到局部最优值,设置小了收敛太慢也容易过拟合。通常我们会尝试性的将初始学习率设为:0.1,0.01,0.001,0.0001等来观察网络初始阶段epoch的loss情况:

  • 如果训练初期loss出现梯度爆炸或NaN这样的情况(暂时排除其他原因引起的loss异常),说明初始学习率偏大,可以将初始学习率降低10倍再次尝试;
  • 如果训练初期loss下降缓慢,说明初始学习率偏小,可以将初始学习率增加5倍或10倍再次尝试;
  • 如果训练一段时间后loss下降缓慢或者出现震荡现象,可能训练进入到一个局部最小值或者鞍点附近。如果在局部最小值附近,需要降低学习率使训练朝更精细的位置移动;如果处于鞍点附件,需要适当增加学习率使步长更大跳出鞍点。
  • 如果网络权重采用随机初始化方式从头学习,有时会因为任务复杂,初始学习率需要设置的比较小,否则很容易梯度飞掉带来模型的不稳定(振荡)。这种思想也叫做Warmup,在预热的小学习率下,模型可以慢慢趋于稳定,等模型相对稳定后再选择预先设置的学习率进行训练,使得模型收敛速度变得更快,模型效果更佳。形状如下:

v2-00334cc2c20f0fe1f50f98fde6097c7b_b.jpg
  • 如果网络基于预训练权重做的finetune,由于模型在原数据集上以及收敛,有一个较好的七点,可以将初始学习率设置的小一些进行微调,比如0.0001。

这里只说了如果设置学习率,至于学习率降低到什么程序可以停止训练,理论上训练loss和验证loss都达到最小的时候就可以了。这里再说下去就是过拟合等内容,会再写一篇详细描述。

策略调整学习率包括固定策略的学习率衰减和自适应学习率衰减,由于学习率如果连续衰减,不同的训练数据就会有不同的学习率。当学习率衰减时,在相似的训练数据下参数更新的速度也会放慢,就相当于减小了训练数据对模型训练结果的影响。为了使训练数据集中的所有数据对模型训练有相等的作用,通常是以epoch为单位衰减学习率。

固定学习率衰减包括:

分段减缓:每N轮学习率减半或者在训练过程中不同阶段设置不同的学习率,便于更精细的调参。TF的接口函数为:

global_step_op = tf.train.get_or_create_global_step()
base_learning_rate = 0.01
decay_boundaries = [2000, 4000] # 学习率衰减边界;
learning_rate_value = [base_learning_rate, base_learning_rate/10., base_learning_rate/100.] # 不同阶段对应学习率。
learning_rate = tf.train.piecewise_constant_decay(global_step_op, boundaries=decay_boundaries,values=learning_rate_value)


分数减缓:将学习率随着epoch的次数进行衰减,

\alpha = \frac{1} {(1 + decay\underline {}rate * epoch)} * lr ,其中
\alpha 表示学习率,
decay\underline {}rate 表示衰减率(可尝试设为0.1,根据数据集/迭代次数调整),
epoch 表示迭代次数,
lr 表示初始学习率。

指数减缓:与分数减缓类似,只是采用指数形式做了表达,

\alpha=gamma^{epoch}*lr ,其中gamma表示指数的底(通常会设置为接近于1的数值,如0.95),随着训练批次epoch的增加,学习率呈指数下降。TF的接口函数为:
global_step_op = tf.train.get_or_create_global_step()
base_learning_rate = 0.01
decay_rate = 0.98
decay_steps = 2000
learning_rate_no_stair = tf.train.exponential_decay(learning_rate=base_learning_rate,
                                                        decay_rate=decay_rate,
                                                        decay_steps=decay_steps,
                                                        staircase=True,
                                                        global_step=global_step_op,
                                                        name="exponential_decay_use_stair")

余弦周期减缓:余弦周期减缓也叫余弦退火学习率,不同于传统的学习率,随着epoch的增加,学习率先急速下降,再陡然提升,然后不断重复这个过程。其目的在于跳出局部最优点。

v2-7d3197b82efb1df6cbc372df35e843ef_b.jpg

之前介绍的几种学习率调节方式在神经网络训练过程中学习率会逐渐减小,所以模型逐渐找到局部最优点。这个过程中,因为一开始的学习率较大,模型不会踏入陡峭的局部最优点,而是快速往平坦的局部最优点移动。随着学习率逐渐减小,模型最终收敛到一个比较好的最优点。如下图所示:

v2-e651dc92a3f8786a4c5b936338e8cd88_b.jpg

而余弦退火学习率由于急速下降,所以模型会迅速踏入局部最优点(不管是否陡峭),并保存局部最优点的模型。⌈快照集成⌋中⌈快照⌋的指的就是这个意思。保存模型后,学习率重新恢复到一个较大值,逃离当前的局部最优点,并寻找新的最优点。因为不同局部最优点的模型则存到较大的多样性,所以集合之后效果会更好。如下图所示:

v2-ccaa2ae624ad8063570e4979bf4466e0_b.jpg

两种方式比较起来,可以理解为模型训练的“起点”和“终点”是差不多的。不同的是,余弦退火学习率使得模型的训练过程比较“曲折”。TF的接口函数为:

# total_decay_step = 15000          总的学习率衰减步数
# base_learning_rate = 0.01         基学习率
# warmup_learning_rate = 0.0001     warm-up 学习率
# warmup_steps = 2000               warm-up 迭代次数
# hold_base_rate_steps_2000 = 2000  保持基学习率的步数
# hold_base_rate_steps_0 = 0
# alpha = 0.00001                   最小学习率
global_step_op = tf.train.get_or_create_global_step()
learning_rate = cosine_decay_with_warmup(learning_rate_base=base_learning_rate,
                                             total_decay_steps=total_decay_step,
                                             alpha = alpha,
                                             warmup_learning_rate=warmup_learning_rate,
                                             warmup_steps=warmup_steps,
                                             hold_base_rate_steps=hold_base_rate_steps_2000,
                                             global_step=global_step_op)
def cosine_decay_with_warmup(global_step,
                             learning_rate_base,
                             total_decay_steps,
                             alpha = 0.0,
                             warmup_learning_rate=0.0,
                             warmup_steps=0,
                             hold_base_rate_steps=0):
  """Cosine decay schedule with warm up period.
  In this schedule, the learning rate grows linearly from warmup_learning_rate
  to learning_rate_base for warmup_steps, then transitions to a cosine decay
  schedule."""
  def eager_decay_rate():
    """Callable to compute the learning rate."""
    learning_rate = tf.train.cosine_decay(learning_rate=learning_rate_base,
                                          decay_steps=total_decay_steps - warmup_steps - hold_base_rate_steps,
                                          global_step= global_step - warmup_steps - hold_base_rate_steps,
                                          alpha=alpha)
    if hold_base_rate_steps > 0:
      learning_rate = tf.where(
          global_step > warmup_steps + hold_base_rate_steps,
          learning_rate, learning_rate_base)
    if warmup_steps > 0:
      if learning_rate_base < warmup_learning_rate:
        raise ValueError('learning_rate_base must be larger or equal to '
                         'warmup_learning_rate.')
      slope = (learning_rate_base - warmup_learning_rate) / warmup_steps
      warmup_rate = slope * tf.cast(global_step,
                                    tf.float32) + warmup_learning_rate
      learning_rate = tf.where(global_step < warmup_steps, warmup_rate,
                               learning_rate)
    return tf.where(global_step > total_decay_steps, alpha, learning_rate,
                    name='learning_rate')

  if tf.executing_eagerly():
    return eager_decay_rate
  else:
    return eager_decay_rate()

自适应学习率衰减包括:

AdaGrad、 RMSprop、 AdaDelta等。此处更偏向于优化算法,暂时不在该篇介绍。

备注:推荐这篇文章“Cyclical Learning Rates for Training Neural Networks”介绍如何找到适合当前网络的初始学习率。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值