深度学习中的学习率是一个非常重要的超参数,对模型的训练和结果影响极大。在深度学习模型中,学习率决定了参数更新的步长,因此合理设置学习率对于优化算法的收敛速度、模型的训练效果以及泛化性能都有很大的影响。本文将介绍深度学习中的学习率设置技巧,包括常用的学习率衰减方法、自适应学习率方法以及学习率预热等。
学习率衰减是一种常见的优化算法,它可以随着训练的进行,逐渐减小学习率,从而使得模型在训练初期能够快速地收敛,而在训练后期能够更加稳定地更新参数。学习率衰减的方法有很多种,包括Step Decay、Exponential Decay、Polynomial Decay等。
Step Decay是一种常见的学习率衰减方法,它是在训练的过程中,根据固定的步数对学习率进行逐步地降低。例如,假设初始学习率为0.1,每隔10个epoch将学习率降低10倍,那么当训练到第11个epoch时,学习率将变为0.01,当训练到第21个epoch时,学习率将变为0.001,以此类推。这种方法简单易行,但是需要手动设置衰减的步数和衰减的幅度,不太灵活。
Exponential Decay是一种常见的指数衰减方法,它可以根据训练的epoch数来逐渐减小学习率。具体而言,Exponential Decay方法的学习率衰减规则如下:
α = α 0 ⋅ e − k t \alpha=\alpha_0 · e^{-kt} α=α0⋅e−kt
其中, α 0 \alpha_0 α0表示初始学习率, k k k为衰减系数, t t t表示训练的epoch数。随着训练的进行, t t t会不断增大,因此学习率会不断减小。Exponential Decay方法可以通过设置不同的 k k k值来控制学习率的衰减速度,从而达到更好的训练效果。
Polynomial Decay是一种常见的多项式衰减方法,它可以通过多项式函数来逐渐减小学习率。具体而言,Polynomial Decay方法的学习率衰减规则如下:
α = α ⋅ ( 1 − t T ) p \alpha=\alpha\cdot (1 - \frac{t}{T})^p α=α⋅(1−Tt)p
其中, α 0 \alpha_0 α0表示初始学习率, p p p为多项式的幂次数, t t t表示训练的epoch数, T T T为总的训练epoch数。随着训练的进行, t t t会不断增大,因此学习率会不断减小,同时随着 p p p的增大,学习率的衰减速度也会加快。
余弦退火(Cosine Annealing)是一种新兴的学习率衰减方法,它通过余弦函数来逐渐减小学习率,从而达到更好的训练效果。具体而言,余弦退火方法的学习率衰减规则如下:
α = α 0 ⋅ 1 + cos ( π ⋅ t T ) 2 \alpha = \alpha_0 \cdot \frac{1 + \cos(\frac{\pi \cdot t}{T})}{2} α=α0⋅21+cos(Tπ⋅t)
其中, α 0 \alpha_0 α0表示初始学习率, t t t表示训练的epoch数, T T T为总的训练epoch数。随着训练的进行, t t t会不断增大,因此学习率会不断减小,同时余弦函数的周期也会不断缩小,从而使得学习率在训练过程中逐渐降低。
One Cycle Learning Rate是一种比较新的学习率衰减方法,它通过在训练初期使用一个较大的学习率,从而快速地收敛到一个局部最优解,然后在训练后期使用一个较小的学习率,从而逐步地优化模型。具体而言,One Cycle Learning Rate方法的学习率变化规则如下:
除了学习率衰减方法之外,深度学习中还有很多自适应学习率方法,包括Adagrad、Adadelta、Adam等。这些方法都是基于梯度信息来自适应地调整学习率,从而在训练过程中更加稳定和高效。
Adagrad是一种自适应学习率方法,它可以根据参数梯度的大小来动态地调整学习率。具体而言,Adagrad方法的学习率更新规则如下:
其中, α 0 \alpha_0 α0表示初始学习率, g i g_i gi表示参数的梯度, ϵ \epsilon ϵ是一个非常小的常数,用于防止分母为0。Adagrad方法的优点在于它可以根据参数的梯度大小自适应地调整学习率,从而更好地适应不同的数据分布和参数更新。但是Adagrad方法也有一些缺点,比如需要存储梯度平方和的累积值,导致内存占用较大;另外,由于学习率是逐渐减小的,因此可能会导致模型在后期训练时收敛速度变慢。
Adadelta是一种自适应学习率方法,它可以根据参数梯度的大小和历史梯度信息来动态地调整学习率Adadelta方法的优点在于它可以动态地调整学习率,并且不需要存储梯度平方和的累积值,因此内存占用较小。但是Adadelta方法也有一些缺点,比如需要手动设置一些超参数,例如平均梯度的衰减率和初始的平均梯度值等。
Adam是一种自适应学习率方法,它可以根据参数梯度的大小和历史梯度信息来动态地调整学习率,并且还可以适应不同的数据分布和参数更新。具体而言,Adam方法的学习率更新规则如下:
m t = β 1 ⋅ m t − 1 + ( 1 − β 1 ) ⋅ g t v t = β 2 ⋅ v t − 1 + ( 1 − β 2 ) ⋅ g t 2 m ^ t = m t 1 − β 1 t v ^ t = v t 1 − β 2 t Δ x t = − α v ^ t + ϵ ⋅ m ^ t \begin{aligned}m_t &= \beta_1 \cdot m_{t-1} + (1 - \beta_1) \cdot g_t \\ v_t &= \beta_2 \cdot v_{t-1} + (1 - \beta_2) \cdot g_t^2 \\ \hat{m}_t &= \frac{m_t}{1 - \beta_1^t} \\ \hat{v}_t &= \frac{v_t}{1 - \beta_2^t} \\ \Delta x_t &= -\frac{\alpha}{\sqrt{\hat{v}_t}+\epsilon} \cdot \hat{m}_t\end{aligned} mtvtm^tv^tΔxt=β1⋅mt−1+(1−β1)⋅gt=β2⋅vt−1+(1−β2)⋅gt2=1−β1tmt=1−β2tvt=−v^t+ϵα⋅m^t
其中, g t g_t gt表示参数的梯度, m t m_t mt和 v t v_t vt分别表示梯度的一阶和二阶矩, β 1 \beta_1 β1和 β 2 \beta_2 β2是衰减率, m ^ t \hat{m}_t m^t和 v ^ t \hat{v}_t v^t分别表示修正后的一阶和二阶矩, α \alpha α表示初始学习率, ϵ \epsilon ϵ是一个非常小的常数,用于防止分母为0。
Adam方法的优点在于它不仅可以动态地调整学习率,还可以适应不同的数据分布和参数更新,从而在训练过程中更加稳定和高效。但是Adam方法也有一些缺点,比如需要手动设置一些超参数,例如衰减率和初始学习率等。
三、学习率预热
学习率预热是一种常见的训练技巧,它可以在训练初期使用一个较小的学习率,从而避免模型在训练初期过度更新参数,导致模型不稳定。具体而言,学习率预热的方法是在训练前先使用一个较小的学习率进行一些预热操作,例如在训练初期进行一些预热的epoch,然后再逐步地增加学习率,从而使得模型更加稳定和高效。
学习率预热的方法可以有效地避免模型在训练初期过度更新参数,导致模型不稳定,同时也可以加速模型的收敛速度,提高训练效率和泛化性能。
下面是使用PyTorch实现常见的学习率衰减方法和自适应学习率方法的代码示例:
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.1, momentum=0.9)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)
for epoch in range(num_epochs):
# train
train_loss, train_acc = train(...)
# update learning rate
scheduler.step()
# validation
val_loss, val_acc = validate(...)
# print results
print('Epoch [{}/{}], Train Loss: {:.4f}, Train Acc: {:.4f}, Val Loss: {:.4f}, Val Acc: {:.4f}'.format(epoch+1, num_epochs, train_loss, train_acc, val_loss, val_acc))
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.1, momentum=0.9)
scheduler = optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.95)
for epoch in range(num_epochs):
# train
train_loss, train_acc = train(...)
# update learning rate
scheduler.step()
# validation
val_loss, val_acc = validate(...)
# print results
print('Epoch [{}/{}], Train Loss: {:.4f}, Train Acc: {:.4f}, Val Loss: {:.4f}, Val Acc: {:.4f}'.format(epoch+1, num_epochs, train_loss, train_acc, val_loss, val_acc))
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.1, momentum=0.9)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)
for epoch in range(num_epochs):
# train
train_loss, train_acc = train(...)
# update learning rate
scheduler.step()
# validation
val_loss, val_acc = validate(...)
# print results
print('Epoch [{}/{}], Train Loss: {:.4f}, Train Acc: {:.4f}, Val Loss: {:.4f}, Val Acc: {:.4f}'.format(
epoch+1, num_epochs, train_loss, train_acc, val_loss, val_acc))
import torch.optim as optim
optimizer = optim.Adagrad(net.parameters(), lr=0.1)
for epoch in range(num_epochs):
# train
train_loss, train_acc = train(...)
# update learning rate
optimizer.step()
optimizer.zero_grad()
# validation
val_loss, val_acc = validate(...)
# print results
print('Epoch [{}/{}], Train Loss: {:.4f}, Train Acc: {:.4f}, Val Loss: {:.4f}, Val Acc: {:.4f}'.format(
epoch+1, num_epochs, train_loss, train_acc, val_loss, val_acc))
import torch.optim as optim
optimizer = optim.Adadelta(net.parameters(), lr=0.1, rho=0.9, eps=1e-6)
for epoch in range(num_epochs):
# train
train_loss, train_acc = train(...)
# update learning rate
optimizer.step()
optimizer.zero_grad()
# validation
val_loss, val_acc = validate(...)
# print results
print('Epoch [{}/{}], Train Loss: {:.4f}, Train Acc: {:.4f}, Val Loss: {:.4f}, Val Acc: {:.4f}'.format(
epoch+1, num_epochs, train_loss, train_acc, val_loss, val_acc))
import torch.optim as optim
optimizer = optim.Adam(net.parameters(), lr=0.1, betas=(0.9, 0.99), eps=1e-8)
for epoch in range(num_epochs):
# train
train_loss, train_acc = train(...)
# update learning rate
optimizer.step()
optimizer.zero_grad()
# validation
val_loss, val_acc = validate(...)
# print results
print('Epoch [{}/{}], Train Loss: {:.4f}, Train Acc: {:.4f}, Val Loss: {:.4f}, Val Acc: {:.4f}'.format(epoch+1, num_epochs, train_loss, train_acc, val_loss, val_acc))
本文介绍了深度学习中常见的学习率设置技巧,包括学习率衰减方法和自适应学习率方法。学习率衰减方法可以根据训练的进展情况动态地调整学习率,从而提高模型的训练效率和泛化性能;自适应学习率方法可以根据参数梯度的大小和历史梯度信息来动态地调整学习率,从而在训练过程中更加稳定和高效。此外,学习率预热也是一种常见的训练技巧,它可以在训练初期使用一个较小的学习率,从而避免模型在训练初期过度更新参数,导致模型不稳定。
在代码实现方面,PyTorch提供了许多内置的学习率调度器和自适应学习率优化器,可以方便地实现各种学习率设置技巧。通过合理地选择和使用这些工具,可以帮助我们更加高效地训练深度学习模型,并获得更好的训练效果和泛化性能。