深度学习的目标是通过不断改变网络参数,使得参数能够对输入做各种非线性变换拟合输出,本质上就是一个函数去寻找最优解,所以如何去更新参数是深度学习研究的重点。通常将更新参数的算法称为优化器,字面理解就是通过什么算法去优化网络模型的参数。常用的优化器就是梯度下降。接下来讲的就是梯度下降和进一步优化梯度下降的各种算法。
优化器的参数优化提供很多种方法的梯度下降,这些算法都是为了能够更快的迭代到参数的最优解,比如 Adam
、RMSprop
、SGD
等等算法,下面我来介绍几种效果比较好的算法。
Adam 算法同时获得了 AdaGrad 和 RMSProp 算法的优点,适应性梯度算法(AdaGrad)为每一个参数保留一个学习率以提升在稀疏梯度(即自然语言和计算机视觉问题)上的性能,均方根传播(RMSProp)基于权重梯度最近量级的均值为每一个参数适应性地保留学习率。这意味着算法在非稳态和在线问题上有很有优秀的性能。
pytorch中的API如下所示:
torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False, *, foreach=None, maximize=False, capturable=False)
params
:要优化的参数
lr
:初始学习率
如果要讲全部参数的含义恐怕要扯出很多的原理了,一般情况下只需要设置上面两个参数即可。
特点:
Ada收敛快,但是容易陷入局部解中。Radam使用预热的方法来解决Adam容易收敛到局部最优解的问题,前期选用比较稳的SGD + Momentum来进行训练,来稳定缩小方差。
pytorch中的API如下所示:
torch.optim.RAdam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, foreach=None)
参数的同样也只需要掌握前面两个参数即可。
特点:
AdamW与Adam相比,加入了加入weight decay,通常会给模型带来更低的训练loss和测试误差。除此之外,在固定相同的训练loss的情况下,AdamW也有更好的泛化性能。
AdamW与Adam相比,具有更优异的性能。
pytorch中函数的API如下:
torch.optim.AdamW(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0.01, amsgrad=False, *, maximize=False, foreach=None, capturable=False)
参数的同样也只需要掌握前面两个参数即可。
保存
保存优化器可以使用 Optimizer.state_dict()
,将其中的参数以字典形式返回出来,然后使用 torch.save()
进行保存。保存的代码如下示例所示
torch.save(optimizer.state_dict(), PATH)
读取
可以使用 Optimizer.load_state_dict()
方法对存储的数据进行,读取的代码如下所示:
optimizer.load_state_dict(torch.load(PATH))
在深度学习中,最重要的超参数要数学习率了,学习率的调整对整个模型的训练起到至关重要的作用,学习率的更新方法十分多,下面我简要的介绍一下。有关下面各种算法的介绍、用处以及作用可以参考这篇文章。
torch.optim.lr_scheduler
中含有一些学习率更新的方法,这些方法在一定程度上能够对整个模型的训练起到一些作用。从经验上看,学习率在一开始要保持大些来保证收敛速度,在收敛到最优点附近时要小些以避免来回振荡。
pytorch中允许自定义学习率的更新函数,该函数的API如下所示:
torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1, verbose=False)
参数解释如下:
optimizer
:传入的优化器
lr_lambda
:自定义的学习率改变规则,可以是lambda表达式,也可以是函数,其含有一个参数 epoch
,是 lr_scheduler
自己传入的参数
last_epoch
:epoch计数器,表示当前已经完成多少个epoch,默认-1,表示还未开始训练,对于加载checkpoint继续训练,那么这里要传入对应的已迭代次数
verbose
:是否在学习率更新时打印学习率
更新学习率的方法如下:
l r n e w = l a m b d a ∗ l r i n i t i a l lr_{new} = lambda * lr_{initial} lrnew=lambda∗lrinitial其中 l a m b d a lambda lambda 是lr_lambda
参数返回的数值, l r i n i t i a l lr_{initial} lrinitial 是最初设置的学习率,不是上一轮的学习率。
代码示例:
import torch
from torch import nn
import math
class Net(nn.Module):
def __init__(self):
super().__init__()
self.conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=2, stride=1, padding=0)
def forward(self, x):
out = self.conv(x)
return out
net = Net()
def set_lr(epoch):
lamda = math.pow(0.5, int(epoch / 3))
return lamda
optimizer = torch.optim.Adam(net.parameters(), lr=1e-2)
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=set_lr)
for i in range(9):
print("lr of epoch", i, "=>", scheduler.get_last_lr())
# 更新模型参数
optimizer.step()
# 更新学习率
scheduler.step()
输出如下:
lr of epoch 0 => [0.01]
lr of epoch 1 => [0.01]
lr of epoch 2 => [0.01]
lr of epoch 3 => [0.005]
lr of epoch 4 => [0.005]
lr of epoch 5 => [0.005]
lr of epoch 6 => [0.0025]
lr of epoch 7 => [0.0025]
lr of epoch 8 => [0.0025]
即每经过一定的epoch,学习率变为之前的多少倍,该函数的API如下所示:
torch.optim.lr_scheduler.StepLR(optimizer,step_size,gamma=0.1,last_epoch=-1,verbose=False)
参数解释如下:
step_size
:经过 step_size
个epoch后学习率进行衰减
gamma
:每次学习率衰减为上次学习率的多少倍
其余同名参数参照上面的解释。
更新学习率的方法为:
l r n e w = γ e p o c h s t e p _ s i z e ∗ l r i n i t i a l lr_{new}=\gamma^{\frac{epoch}{step\_size}}*lr_{initial} lrnew=γstep_sizeepoch∗lrinitial其中 γ \gamma γ 为上面函数的 gamma
参数。
代码示例
这里我们仅改变变量 scheduler
,即学习率管理器,其余代码不变,查看结果。
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 3, 0.1)
输出如下:
lr of epoch 0 => [0.01]
lr of epoch 1 => [0.01]
lr of epoch 2 => [0.01]
lr of epoch 3 => [0.001]
lr of epoch 4 => [0.001]
lr of epoch 5 => [0.001]
lr of epoch 6 => [0.0001]
lr of epoch 7 => [0.0001]
lr of epoch 8 => [0.0001]
当前迭代的次数达到指定的epoch后,学习率按照指定倍数进行衰减。
函数API如下所示
torch.optim.lr_scheduler.MultiStepLR(optimizer,milestones,gamma=0.1,last_epoch=-1,verbose=False)
milestones
:为一个列表,当epoch到达列表中指定的epoch时,发生衰减。
代码示例:
这里我们仅改变变量 scheduler
,即学习率管理器,其余代码不变,查看结果。
scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[3,7], gamma=0.1)
输出如下:
lr of epoch 0 => [0.01]
lr of epoch 1 => [0.01]
lr of epoch 2 => [0.01]
lr of epoch 3 => [0.001]
lr of epoch 4 => [0.001]
lr of epoch 5 => [0.001]
lr of epoch 6 => [0.001]
lr of epoch 7 => [0.0001]
lr of epoch 8 => [0.0001]
指数衰减的API如下所示:
torch.optim.lr_scheduler.ExponentialLR(optimizer,gamma,last_epoch=-1,verbose=False)
学习率更新的规则如下:
l r n e w = γ e p o c h ∗ l r i n i t i a l lr_{new}=\gamma^{epoch}*lr_{initial} lrnew=γepoch∗lrinitial
其中 γ \gamma γ 参数为上面的 gamma
。
代码示范:
这里我们仅改变变量 scheduler
,即学习率管理器,其余代码不变,查看结果。
scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.99)
输出如下:
lr of epoch 0 => [0.01]
lr of epoch 1 => [0.0095]
lr of epoch 2 => [0.009025]
lr of epoch 3 => [0.00857375]
lr of epoch 4 => [0.0081450625]
lr of epoch 5 => [0.007737809374999999]
lr of epoch 6 => [0.007350918906249998]
lr of epoch 7 => [0.006983372960937498]
lr of epoch 8 => [0.006634204312890623]
torch.optim.lr_scheduler.CyclicLR(optimizer, base_lr, max_lr, step_size_up=2000, step_size_down=None, mode='triangular', gamma=1.0, scale_fn=None, scale_mode='cycle', cycle_momentum=True, base_momentum=0.8, max_momentum=0.9, last_epoch=- 1, verbose=False)
参数解释:
base_lr
:LR下界
max_lr
:LR上界
step_size_up
:每个循环分为两部分,一部分上升,一部分下降,构成一个三角形,这部分设置上升区间的迭代次数(注意是迭代,每一个batch算一次,而不是epoch)。
step_size_down
:下降区间的迭代次数。