《Optimizer》
本文總結Pytorch中的Optimizer
Optimizer是深度學習模型訓練中非常重要的一個模塊,它決定參數參數更新的方向,快慢和大小,好的Optimizer算法和合適的參數使得模型收斂又快又準
但本文不會討論什麼任務用什麼Optimizer,及其參數設置,只是總結下Pytorch中的Optimizer
文章目錄
0 博客目錄
Pytorch模型訓練(0) - CPN源碼解析
Pytorch模型訓練(1) - 模型定義
Pytorch模型訓練(2) - 模型初始化
Pytorch模型訓練(3) - 模型保存與加載
Pytorch模型訓練(4) - Loss Function
Pytorch模型訓練(5) - Optimizer
Pytorch模型訓練(6) - 數據加載
1 torch.optim
Pytorch的torch.optim是包含各種優化器算法的包,支持通用優化器方法,接口通用性好,也方便集成更加複雜的算法
怎樣使用一個Optimizer ???
要使用Optimizer,我們首先要創建一個Optimizer對象,該對象會保持當前狀態,並根據計算梯度來更新參數
1.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
1.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適用所有參數
1.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)
2 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()
清空優化器參數的梯度
3 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部分,我羅列的比較簡單,因爲在應用層,無非是他們的參數,而這些參數就關乎算法原理,不是本文重點,有興趣可以參見梯度下降算法原理的博客
4 學習率調節
這些優化器中往往需要多個參數,共同控制才能達到優化目的,但大多數參數都有默認參考值,這些值都是大牛們經過多方驗證得出的,所以我們在訓練模型時,需要手動設置的參數並不多
其中最需要我們手動調節的就是學習率,關於學習率衰減理論部分可參見個人博客;而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)
5 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()