模型训练技巧:warmup学习率策略
1.什么是warmup
学习率的设置 — 不同阶段不同值:上升 -> 平稳 -> 下降
由于神经网络在刚开始训练的时候是非常不稳定的,因此刚开始的学习率应当设置得很低很低,这样可以保证网络能够具有良好的收敛性。但是较低的学习率会使得训练过程变得非常缓慢,因此这里会采用以较低学习率逐渐增大至较高学习率的方式实现网络训练的“热身”阶段,称为 warmup stage。
但是如果我们使得网络训练的 loss 最小,那么一直使用较高学习率是不合适的,因为它会使得权重的梯度一直来回震荡,很难使训练的损失值达到全局最低谷。因此需要在经过一些steps之后学习率再慢慢变小
2.为什么使用Warmup?
由于刚开始训练时,模型的权重(weights)是随机初始化的,此时若选择一个较大的学习率,可能带来模型的不稳定(振荡),选择Warmup预热学习率的方式,可以使得开始训练的几个epoches或者一些steps内学习率较小,在预热的小学习率下,模型可以慢慢趋于稳定,等模型相对稳定后再选择预先设置的学习率进行训练,使得模型收敛速度变得更快,模型效果更佳。
3.warmup为什么有效
warmup有助于减缓模型在初始阶段对mini-batch的提前过拟合现象,保持分布的平稳;有助于保持模型深层的稳定性
可以认为,刚开始模型对数据的“分布”理解为零,或者是说“均匀分布”(当然这取决于你的初始化);在第一轮训练的时候,每个数据点对模型来说都是新的,模型会很快地进行数据分布修正,如果这时候学习率就很大,极有可能导致开始的时候就对该数据“过拟合”,后面要通过多轮训练才能拉回来,浪费时间。当训练了一段时间(比如两轮、三轮)后,模型已经对每个数据点看过几遍了,或者说对当前的batch而言有了一些正确的先验,较大的学习率就不那么容易会使模型学偏,所以可以适当调大学习率。这个过程就可以看做是warmup。那么为什么之后还要decay呢?当模型训到一定阶段后(比如十个epoch),模型的分布就已经比较固定了,或者说能学到的新东西就比较少了。如果还沿用较大的学习率,就会破坏这种稳定性,用我们通常的话说,就是已经接近loss的local optimal了,为了靠近这个point,我们就要慢慢来。
4. keras代码实现
带有warmup的指数衰减学习率
代码实现
from tensorflow.keras.callbacks import Callback
import tensorflow.keras.backend as K
class WarmupExponentialDecay(Callback):def __init__(self,lr_base=0.0002,lr_min=0.0,decay=0,warmup_epochs=0):self.num_passed_batchs = 0 #一个计数器self.warmup_epochs=warmup_epochs self.lr=lr_base #learning_rate_baseself.lr_min=lr_min #最小的起始学习率,此代码尚未实现self.decay=decay #指数衰减率self.steps_per_epoch=0 #也是一个计数器def on_batch_begin(self, batch, logs=None):# params是模型自动传递给Callback的一些参数if self.steps_per_epoch==0:#防止跑验证集的时候被更改了if self.params['steps'] == None:self.steps_per_epoch = np.ceil(1. * self.params['samples'] / self.params['batch_size'])else:self.steps_per_epoch = self.params['steps']if self.num_passed_batchs < self.steps_per_epoch * self.warmup_epochs:K.set_value(self.model.optimizer.lr,self.lr*(self.num_passed_batchs + 1) / self.steps_per_epoch / self.warmup_epochs)else:K.set_value(self.model.optimizer.lr,self.lr*((1-self.decay)**(self.num_passed_batchs-self.steps_per_epoch*self.warmup_epochs)))self.num_passed_batchs += 1def on_epoch_begin(self,epoch,logs=None):#用来输出学习率的,可以删除print("learning_rate:",K.get_value(self.model.optimizer.lr))
使用示例
optimizer=Adam() #学习率会被回调函数覆盖,所以这里可以不填
model=tf.keras.models.Model(inputs=inputs,outputs=outputs)
model.compile(optimizer=optimizer,loss="categorical_crossentropy",metrics=["acc"])
trained_model=model.fit(train_dataset,steps_per_epoch=12031/batch_size,validation_steps=20,validation_data=test_dataset, shuffle =True,epochs=160,callbacks=[WarmupExponentialDecay(lr_base=0.0002,decay=0.00002,warmup_epochs=2)])
带有warmup的余弦衰减(consine decay)学习率
consine decay原理如下:
学习率减小:ηt=ηmini+12(ηmaxi−ηmini)(1+cos(TcurTiπ)))\eta _{t}=\eta _{min}^{i}+\frac{1}{2}(\eta _{max}^{i}-\eta _{min}^{i})(1+cos(\frac{T_{cur}}{T_{i}}\pi )))ηt=ηmini+21(ηmaxi−ηmini)(1+cos(TiTcurπ)))
表达式中的字符含义:
- iii就是第几次run(索引值);
- ηmaxi\eta _{max}^{i}ηmaxi和ηmini\eta _{min}^{i}ηmini分别表示学习率的最大值和最小值,定义了学习率的范围。论文中提到在每次restart之后,减少ηmaxi\eta _{max}^{i}ηmaxi和ηmini\eta _{min}^{i}ηmini的值会是有趣的,但是为了简单,论文中也保持ηmaxi\eta _{max}^{i}ηmaxi和ηmini\eta _{min}^{i}ηmini在每次restart之后仍然保持不变。
- TcurT_{cur}Tcur则表示当前执行了多少个epoch,但是TcurT_{cur}Tcur是在每个batch运行之后就会更新,而此时一个epoch还没有执行完,所以TcurT_{cur}Tcur的值可以为小数。例如总样本为80,每个batch的大小是16,那么在一个epoch中就会循环5次读入batch,那么在第一个epoch中执行完第一个batch后,TcurT_{cur}Tcur的值就更新为1/5=0.2,以此类推。
- TiT_iTi表示第i次run中总的epoch数。当涉及到重启时,论文中提到为了提高性能表现,开始会初始化一个比较小的TiT_iTi,在每次restart后,TiT_iTi会以乘以一个TmultT_{mult}Tmult的方式增加,但是本文不涉及重启也就不需要考虑,即把TiT_iTi固定为我们训练模型的epoch数。
为了简单,这里稍微修改一下TcurT_{cur}Tcur和TiT_{i}Ti的定义,原本表示的是epoch的数量,但是因为TcurT_{cur}Tcur是在每个batch之后都会更新,所以将TiT_{i}Ti定义为总的batch需要执行的步数,而TcurT_{cur}Tcur定义为当前对当前已执行的batch的计数,即每执行一个batch,TcurT_{cur}Tcur就加一。举个例子,样本总数为80,每个batch的大小为16,那么一共有5个batch,再令训练模型总的epoch为30,假设当前执行到第二个epoch的第二个batch结束,那么此时TcurTi=1∗5+230∗5=7150\frac{T_{cur}}{T_{i}}=\frac{1*5+2}{30*5}=\frac{7}{150}TiTcur=30∗51∗5+2=1507,按照之前的定义TcurTi=1+2/530=7150\frac{T_{cur}}{T_{i}}=\frac{1+2/5}{30}=\frac{7}{150}TiTcur=301+2/5=1507,两者是等价的,但是因为之前的定义存在小数,如果1除以batch的总数除不尽,就会存在精度损失的情况。
代码实现
import numpy as np
from tensorflow import keras
from keras import backend as K
def cosine_decay_with_warmup(global_step,learning_rate_base,total_steps,warmup_learning_rate=0.0,warmup_steps=0,hold_base_rate_steps=0):"""参数:global_step: 上面定义的Tcur,记录当前执行的步数。learning_rate_base:预先设置的学习率,当warm_up阶段学习率增加到learning_rate_base,就开始学习率下降。total_steps: 是总的训练的步数,等于epoch*sample_count/batch_size,(sample_count是样本总数,epoch是总的循环次数)warmup_learning_rate: 这是warm up阶段线性增长的初始值warmup_steps: warm_up总的需要持续的步数hold_base_rate_steps: 这是可选的参数,即当warm up阶段结束后保持学习率不变,知道hold_base_rate_steps结束后才开始学习率下降"""if total_steps < warmup_steps:raise ValueError('total_steps must be larger or equal to ''warmup_steps.')#这里实现了余弦退火的原理,设置学习率的最小值为0,所以简化了表达式learning_rate = 0.5 * learning_rate_base * (1 + np.cos(np.pi *(global_step - warmup_steps - hold_base_rate_steps) / float(total_steps - warmup_steps - hold_base_rate_steps)))#如果hold_base_rate_steps大于0,表明在warm up结束后学习率在一定步数内保持不变if hold_base_rate_steps > 0:learning_rate = np.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_stepswarmup_rate = slope * global_step + warmup_learning_rate# 只有当global_step 仍然处于warm up阶段才会使用线性增长的学习率warmup_rate,否则使用余弦退火的学习率learning_ratelearning_rate = np.where(global_step < warmup_steps, warmup_rate,learning_rate)return np.where(global_step > total_steps, 0.0, learning_rate)
class WarmUpCosineDecayScheduler(keras.callbacks.Callback):"""继承Callback,实现对学习率的调度keras通过继承Callback实现余弦退火。通过继承Callback,当我们训练的时候传入我们的就函数,就可以在每个batch开始训练前以及结束后回调我们重写的on_batch_end和on_batch_begin函数。"""def __init__(self,learning_rate_base,total_steps,global_step_init=0,warmup_learning_rate=0.0,warmup_steps=0,hold_base_rate_steps=0,verbose=0):super(WarmUpCosineDecayScheduler, self).__init__()self.learning_rate_base = learning_rate_baseself.total_steps = total_stepsself.global_step = global_step_initself.warmup_learning_rate = warmup_learning_rateself.warmup_steps = warmup_stepsself.hold_base_rate_steps = hold_base_rate_stepsself.verbose = verbose#learning_rates用于记录每次更新后的学习率,方便图形化观察self.learning_rates = []#更新global_step,并记录当前学习率def on_batch_end(self, batch, logs=None):self.global_step = self.global_step + 1lr = K.get_value(self.model.optimizer.lr)self.learning_rates.append(lr)# 更新学习率def on_batch_begin(self, batch, logs=None):lr = cosine_decay_with_warmup(global_step=self.global_step,learning_rate_base=self.learning_rate_base,total_steps=self.total_steps, warmup_learning_rate=self.warmup_learning_rate,warmup_steps=self.warmup_steps, hold_base_rate_steps=self.hold_base_rate_steps)K.set_value(self.model.optimizer.lr, lr)if self.verbose > 0:print('\nBatch %05d: setting learning ''rate to %s.' % (self.global_step + 1, lr))
使用示例
from keras.models import Sequential
from keras.layers import Dense
# Create a model.
model = Sequential()
model.add(Dense(32, activation='relu', input_dim=100))
model.add(Dense(10, activation='softmax'))
model.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])#样本总数
sample_count = 12608
# Total epochs to train.
epochs = 50
# Number of warmup epochs.
warmup_epoch = 10
# Training batch size, set small value here for demonstration purpose.
batch_size = 16
# Base learning rate after warmup.
learning_rate_base = 0.0001total_steps = int(epochs * sample_count / batch_size)
# Compute the number of warmup batches.
warmup_steps = int(warmup_epoch * sample_count / batch_size)# Generate dummy data.
data = np.random.random((sample_count, 100))
labels = np.random.randint(10, size=(sample_count, 1))# Convert labels to categorical one-hot encoding.
one_hot_labels = keras.utils.to_categorical(labels, num_classes=10)# Compute the number of warmup batches.
warmup_batches = warmup_epoch * sample_count / batch_size# Create the Learning rate scheduler.
warm_up_lr = WarmUpCosineDecayScheduler(learning_rate_base=learning_rate_base,total_steps=total_steps,warmup_learning_rate=4e-06,warmup_steps=warmup_steps,hold_base_rate_steps=5,)# Train the model, iterating on the data in batches of 32 samples
model.fit(data, one_hot_labels, epochs=epochs, batch_size=batch_size,verbose=0, callbacks=[warm_up_lr])import matplotlib.pyplot as plt
plt.plot(warm_up_lr.learning_rates)
plt.xlabel('Step', fontsize=20)
plt.ylabel('lr', fontsize=20)
plt.axis([0, total_steps, 0, learning_rate_base*1.1])
plt.xticks(np.arange(0, epochs, 1))
plt.grid()
plt.title('Cosine decay with warmup', fontsize=20)
plt.show()
运行结果
4.应用场景
(1)训练出现NaN:当网络非常容易nan时候,采用warm up进行训练,可使得网络正常训练;
(2)过拟合:训练集损失很低,准确率高,但测试集损失大,准确率低,可用warm up;
参考博客:https://www.freesion.com/article/5312772061/
https://blog.csdn.net/CarryLvan/article/details/104394960
https://blog.csdn.net/qq_19865329/article/details/106425734
参考论文:https://arxiv.org/abs/1608.03983
模型训练技巧:warmup学习率策略相关推荐
- 大模型训练技巧|单卡多卡|训练性能评测
原视频:[单卡.多卡 BERT.GPT2 训练性能[100亿模型计划]] 此笔记主要参考了李沐老师的视频,感兴趣的同学也可以去看视频- 视频较长,这里放上笔记,与大家分享- 大模型对于计算资源的要求越 ...
- 计算机视觉中的数据预处理与模型训练技巧总结
来源丨机器学习小王子,转载自丨极市平台 导读 针对图像分类任务提升准确率的方法主要有两条:一个是模型的修改,另一个是各种数据处理和训练的技巧.本文在精读论文的基础上,总结了图像分类任务的11个tric ...
- 【干货】计算机视觉中的数据预处理与模型训练技巧总结
来源丨机器学习小王子 编辑丨极市平台 针对图像分类任务提升准确率的方法主要有两条:一个是模型的修改,另一个是各种数据处理和训练的技巧.本文在精读论文的基础上,总结了图像分类任务的11个tricks. ...
- 模型训练技巧——warm up
1. pytorch 中学习率的调节策略 (1)等间隔调整学习率 StepLR (2)按需调整学习率 MultiStepLR (3)指数衰减调整学习率 ExponentialLR (4)余弦退火调整学 ...
- 高效又稳定的ChatGPT大模型训练技巧总结,让训练事半功倍!
文|python 前言 近期,ChatGPT成为了全网热议的话题.ChatGPT是一种基于大规模语言模型技术(LLM, large language model)实现的人机对话工具.现在主流的大规模语 ...
- 【Pytorch神经网络理论篇】 24 神经网络中散度的应用:F散度+f-GAN的实现+互信息神经估计+GAN模型训练技巧
1 散度在无监督学习中的应用 在神经网络的损失计算中,最大化和最小化两个数据分布间散度的方法,已经成为无监督模型中有效的训练方法之一. 在无监督模型训练中,不但可以使用K散度JS散度,而且可以使用其他 ...
- 李宏毅老师《机器学习》课程笔记-2.1模型训练技巧
注:本文是我学习李宏毅老师<机器学习>课程 2021/2022 的笔记(课程网站 ),文中图片除了两幅是我自己绘制外,其余图片均来自课程 PPT.欢迎交流和多多指教,谢谢! 文章目录 Le ...
- 深度学习模型训练技巧
博主以前都是拿别人的模型别人的数据做做分类啊,做做目标检测,搞搞学习,最近由于导师的工程需求,自己构造网络,用自己的数据来跑网络,才发现模型训练真的是很有讲究,很有技巧在里面,最直接的几个超参数的设置 ...
- ML(10) - 模型训练技巧
模型技巧 交叉验证 Pipeline 网格搜索 偏差(Bias)和方差(Variance) 模型正则化(Regularization) 正则化基本概念 正则化种类(scikit-learn) 交叉验证 ...
最新文章
- windowSoftInputMode属性详解
- Android eMMC 分区详解(转载)
- Android之图形图像之使用Path类总结
- bash shell函数的定义及变量的简单说明
- 写滚动字幕html5源码,[转载]滚动字幕的源代码
- 这个小众副业,一次200,有人月入3万!
- 相对免赔额和绝对免赔额是什么意思,有什么区别?
- 采用普里姆算法求最小生成树
- 计算机开机了进入不到桌面,电脑开机后进不了桌面,小编教你电脑开机后无法进入桌面怎么办...
- NSN HLR simulator for provisioning in expect
- 社交电商模式优化之报单产品
- 打开matlab闪退的原因
- linux 2.4内核编译,linux 2.4内核编译详解
- 信息收集详情(高能集锦)
- 2021年期货公司分类排名一览
- 26.分布式限流设计
- Unity之多场景自动化打包研究
- react-umeditor react富文本
- GNSS数据自动下载脚本(使用CDDIS账号)
- mysql建库建表(一)