本文总结Pytorch中的Optimizer
Optimizer是深度学习模型训练中非常重要的一个模块,它决定参数参数更新的方向,快慢和大小,好的Optimizer算法和合适的参数使得模型收敛又快又准
但本文不会讨论什么任务用什么Optimizer,及其参数设置,只是总结下Pytorch中的Optimizer
torch.optim
Pytorch的torch.optim是包含各种优化器算法的包,支持通用优化器方法,接口通用性好,也方便集成更加复杂的算法
怎样使用一个Optimizer ???
要使用Optimizer,我们首先要创建一个Optimizer对象,该对象会保持当前状态,并根据计算梯度来更新参数
1)创建Optimizer
创建Optimizer时,需要为其提供一些需要迭代的参数进行迭代,还可以指定一些可选的,特定的,用于优化的参数,如学习率,权重衰减等参数
Example:
optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)
optimizer = optim.Adam([var1, var2], lr = 0.0001)
注意1:如果需要将模型移到GPU,可以通过".cuda"来实现
optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9).cuda()
optimizer = optim.Adam([var1, var2], lr = 0.0001).cuda()
注意2:在训练中,最好保持模型和优化在相同位置,即要在GPU,都在GPU
2)Optimizer参数
Optimizer支持特殊参数指定选项,这样需要用一个字典(dict)类型的可迭代参数代替变量(Variable)可迭代参数;它们有各自的参数组,用"params"关键字将他们独立开(包含属于它的参数列表)
在需要不同层不同参数时,非常有用,如:
optim.SGD([
{'params': model.base.parameters()},
{'params': model.classifier.parameters(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)
也就是,classifier.parameters的学习率为1e-3,base.parameters的学习率为le-2,动量0.9适用所有参数
3)Optimizer迭代
迭代,更新参数,一般有下面2中方式:
方式1:
optimizer.step()
该方式能满足大多需求,一般只要进行梯度需要,如backward(),这个step()函数就需要被召唤
Example:
for input, target in dataset:
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
方式2:
optimizer.step(closure)
一些特殊算法,如共轭梯度(Conjugate Gradient) 和 LBFGS 需要多次重新评估函数,所以需要传入一个允许重新计算模型的闭包(closure),来清理梯度,计算loss并返回
Example:
for input, target in dataset:
def closure():
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
return loss
optimizer.step(closure)
Algorithms
Optimizer基类
torch.optim.Optimizer(params, defaults)
参数:
1)params:可迭代对象,需要被优化的参数对象,一般为张量(torch.Tensor)或字典(dict)
2)defaults:字典类型,一些优化选项,基本都有默认值
方法:
1)add_param_group(param_group)
增加需要优化的参数到param_groups,如在使用预训练模型进行微调时,很有用,可以将冻结层参数添加到训练中
2)load_state_dict(state_dict)
加载优化器参数
3)state_dict()
返回优化器状态,字典类型,包括优化器状态和参数组
4)step(closure)
单步更新
5)zero_grad()
清空优化器参数的梯度
Optimizer
torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0)
torch.optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0, initial_accumulator_value=0)
torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)
torch.optim.SparseAdam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08)
torch.optim.Adamax(params, lr=0.002, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
torch.optim.ASGD(params, lr=0.01, lambd=0.0001, alpha=0.75, t0=1000000.0, weight_decay=0)
torch.optim.ASGD(params, lr=0.01, lambd=0.0001, alpha=0.75, t0=1000000.0, weight_decay=0)
torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)
torch.optim.Rprop(params, lr=0.01, etas=(0.5, 1.2), step_sizes=(1e-06, 50))
torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False)
这些Optimizer部分,我罗列的比较简单,因为在应用层,无非是他们的参数,而这些参数就关乎算法原理,不是本文重点,有兴趣可以参见梯度下降算法原理的博客
学习率调节
这些优化器中往往需要多个参数,共同控制才能达到优化目的,但大多数参数都有默认参考值,这些值都是大牛们经过多方验证得出的,所以我们在训练模型时,需要手动设置的参数并不多
其中最需要我们手动调节的就是学习率,关于学习率衰减理论部分可参见个人博客;而Pytorch中怎么调用呢?
torch.optim.lr_scheduler
提供了基于epochs调节学习率的方法;主要有以下几种:
torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1)
torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)
torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)
torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1)
torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max, eta_min=0, last_epoch=-1)
torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10, verbose=False, threshold=0.0001, threshold_mode='rel', cooldown=0, min_lr=0, eps=1e-08)
Example:
optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
scheduler = ReduceLROnPlateau(optimizer, 'min')
for epoch in range(10):
train(...)
val_loss = validate(...)
# Note that step should be called after validate()
scheduler.step(val_loss)
CPN Optimizer
1)实例化Adam优化器
optimizer = torch.optim.Adam(model.parameters(),
lr = cfg.lr,
weight_decay=cfg.weight_decay)
2)若resume,加载优化器状态
if args.resume:
if isfile(args.resume):
print("=> loading checkpoint '{}'".format(args.resume))
checkpoint = torch.load(args.resume)
pretrained_dict = checkpoint['state_dict']
model.load_state_dict(pretrained_dict)
args.start_epoch = checkpoint['epoch']
optimizer.load_state_dict(checkpoint['optimizer'])
print("=> loaded checkpoint '{}' (epoch {})"
.format(args.resume, checkpoint['epoch']))
logger = Logger(join(args.checkpoint, 'log.txt'), resume=True)
else:
print("=> no checkpoint found at '{}'".format(args.resume))
else:
logger = Logger(join(args.checkpoint, 'log.txt'))
logger.set_names(['Epoch', 'LR', 'Train Loss'])
3)训练时(学习率调节,train,优化器迭代,模型保存)
for epoch in range(args.start_epoch, args.epochs):
#调节学习率
lr = adjust_learning_rate(optimizer, epoch, cfg.lr_dec_epoch, cfg.lr_gamma)
print('\nEpoch: %d | LR: %.8f' % (epoch + 1, lr))
# train for one epoch
#将优化器给train函数
train_loss = train(train_loader, model, [criterion1, criterion2], optimizer)
print('train_loss: ',train_loss)
# append logger file
logger.append([epoch + 1, lr, train_loss])
#保存模型,也保存优化器状态
save_model({
'epoch': epoch + 1,
'state_dict': model.state_dict(),
'optimizer' : optimizer.state_dict(),
}, checkpoint=args.checkpoint)
细节1:学习率调节函数adjust_learning_rate
def adjust_learning_rate(optimizer, epoch, schedule, gamma):
"""Sets the learning rate to the initial LR decayed by schedule"""
if epoch in schedule:
for param_group in optimizer.param_groups:
param_group['lr'] *= gamma
return optimizer.state_dict()['param_groups'][0]['lr']
细节2:train函数,优化器迭代
# compute gradient and do Optimization step
optimizer.zero_grad()
loss.backward()
optimizer.step()