深度学习训练策略--学习率预热Warmup

2022-10-09 13:46:59

理解学习率和batch_size大小的关系 - 知乎

【AI不惑境】学习率和batchsize如何影响模型的性能? - 知乎

神经网络中 warmup 策略为什么有效;有什么理论解释么? - 知乎

背景

学习率是最影响性能的超参数之一,如果我们只能调整一个超参数,那么最好的选择就是它。 其实在我们的大多数情况下,遇到 loss 变成 NaN 的情况大多数是由于学习率选择不当引起的

一、Warmup的最初提出?

Warmup是在ResNet论文中提到的一种学习率预热的方法,它在训练开始的时候先选择使用一个较小的学习率,训练了一些steps(15000steps,见最后代码1)或者epoches(5epoches,见最后代码2),再修改为预先设置的学习来进行训练。

代码查看:深度学习训练策略-学习率预热Warmup_there2belief的博客-CSDN博客

二、为什么使用Warmup?

由于刚开始训练时,模型的权重(weights)是随机初始化的,此时若选择一个较大的学习率,可能带来模型的不稳定(振荡),选择Warmup预热学习率的方式,可以使得开始训练的几个epoches或者一些steps内学习率较小,在预热的小学习率下,模型可以慢慢趋于稳定,等模型相对稳定后再选择预先设置的学习率进行训练,使得模型收敛速度变得更快,模型效果更佳。

Example:Resnet论文中使用一个110层的ResNet在cifar10上训练时,先用0.01的学习率训练直到训练误差低于80%(大概训练了400个steps),然后使用0.1的学习率进行训练。

三、Warmup的改进

学习率的设置 — “上升 -> 正常学习率 -> 下降”
上述二中所述的Warmup是constant warmup,它的不足之处在于从一个很小的学习率一下变为比较大的学习率可能会导致训练误差突然增大。于是18年Facebook提出了gradual warmup来解决这个问题,即从最初的小学习率开始,每个step增大一点点,直到达到最初设置的比较大的学习率时,采用最初设置的学习率进行训练。

分析:由于神经网络在刚开始训练的时候是非常不稳定的,因此刚开始的学习率应当设置得很低很低,这样可以保证网络能够具有良好的收敛性。但是较低的学习率会使得训练过程变得非常缓慢,因此这里会采用以较低学习率逐渐增大至较高学习率的方式实现网络训练的“热身”阶段,称为 warmup stage。但是如果我们要使得网络训练的 loss 最小,那么一直使用较高学习率是不合适的,因为它会使得权重的梯度一直来回震荡,很难使训练的损失值达到全局最低谷。因此当 warmup 阶段的学习率直到达到最初设置的比较大的学习率时,采用最初设置的学习率进行训练。下面的代码采用了cosine 的衰减方式,这个阶段可以称为 consine decay stage。

 tf-yolov3作者的相关源码

with tf.name_scope('learn_rate'):
    self.global_step = tf.Variable(1.0, dtype=tf.float64, trainable=False, name='global_step')
    warmup_steps = tf.constant(self.warmup_periods * self.steps_per_period,
                               dtype=tf.float64, name='warmup_steps') # warmup_periods epochs
    train_steps = tf.constant((self.first_stage_epochs + self.second_stage_epochs) * self.steps_per_period,
                              dtype=tf.float64, name='train_steps')
    self.learn_rate = tf.cond(
        pred=self.global_step < warmup_steps,
        true_fn=lambda: self.global_step / warmup_steps * self.learn_rate_init,
        false_fn=lambda: self.learn_rate_end + 0.5 * (self.learn_rate_init - self.learn_rate_end) * (
                    1 + tf.cos((self.global_step - warmup_steps) / (train_steps - warmup_steps) * np.pi)))
    global_step_update = tf.assign_add(self.global_step, 1.0)
    """
    训练分为两个阶段,第一阶段里前面又划分出一段作为“热身阶段”:
    热身阶段:learn_rate = (global_step / warmup_steps) * learn_rate_init
    其他阶段:learn_rate_end + 0.5 * (learn_rate_init - learn_rate_end) * (
             1 + tf.cos((global_step - warmup_steps) / (train_steps - warmup_steps) * np.pi))
	"""

学习率变化曲线:


四、应用场景

(1)训练出现NaN:当网络非常容易nan时候,采用warm up进行训练,可使得网络正常训练;

(2)过拟合:训练集损失很低,准确率高,但测试集损失大,准确率低,可用warm up;具体可看: Resnet-18-训练实验-warm up操作wu有效原因

这个问题目前还没有被充分证明,目前效果有:
(1)有助于减缓模型在初始阶段对mini-batch的提前过拟合现象,保持分布的平稳;
(2)有助于保持模型深层的稳定性。

在训练期间有如下情况:
(1)在训练的开始阶段,模型权重迅速改变;
(2)mini-batch size较小,样本方差较大。

有效原因分析:
(1)第一因为刚刚开始的时候,模型对数据的“分布”理解为零,或者是说“均匀分布”(初始化一般都是以均匀分布来初始化);在第一轮训练的时候,每个数据对模型来说都是新的,随着训练模型会很快地进行数据分布修正,这时候学习率就很大,很有可能在刚刚开始就会导致过拟合,后期需要要通过多轮训练才能拉回来。当训练了一段时间(比如两轮、三轮)后,模型已经对每个数据过几遍了,或者说对当前的batch而言有了一些正确的先验,较大的学习率就不那么容易会使模型学偏,所以可以适当调大学习率。这个过程就也就是warmup。

那后期为什么学习率又要减小呢?这就是我们正常训练时候,学习率降低有助于更好的收敛,当模型学习到一定的程度,模型的分布就学习的比较稳定了。如果还用较大的学习率,就会破坏这种稳定性,导致网络波动比较大,现在已经十分接近了最优了,为了靠近这个最优点,就要用很小的学习率了。
(2)第二原因是如果有mini-batch内的数据分布方差特别大,这就会导致模型学习剧烈波动,使其学得的权重很不稳定,这在训练初期最为明显,最后期较为缓解。
所以由于上面这两个原因,我们不能随便成倍减少学习率

在resnet文章中,有说到如果一开始就用大的学习率,虽然最终会收敛,但之后测试准确率还是不会提高;如果用了warmup,在收敛后还能有所提高。也就是说,用warm up和不用warm up达到的收敛点,对之后模型能够达到最优点有影响。这说明不用warm up收敛到的点比用warm up收敛到的点更差。这也说明,如果刚刚开始学偏了的权重后面都拉不回来。

那么为什么以前神经网络没用warm up技巧呢?
主要原因是:
(1)以前的网络不够大、不够深;
(2)数据集普遍较小。

 五、总结
使用Warmup预热学习率的方式,即先用最初的小学习率训练,然后每个step增大一点点,直到达到最初设置的比较大的学习率时(注:此时预热学习率完成),采用最初设置的学习率进行训练(注:预热学习率完成后的训练过程,学习率是衰减的),有助于使模型收敛速度变快,效果更佳。

pytorch DistributedDataParallel 多卡训练结果变差的问题分析

  • 作者:ytusdc
  • 原文链接:https://blog.csdn.net/ytusdc/article/details/119891584
    更新时间:2022-10-09 13:46:59