Pytorch中的學習率衰減及其用法

學習率衰減是一個非常有效的煉丹技巧之一,在神經(jīng)網(wǎng)絡的訓練過程中,當accuracy出現(xiàn)震蕩或loss不再下降時,進行適當?shù)膶W習率衰減是一個行之有效的手段,很多時候能明顯提高accuracy。

Pytorch中有兩種學習率調(diào)整(衰減)方法:

  1. 使用庫函數(shù)進行調(diào)整;
  2. 手動調(diào)整。

1. 使用庫函數(shù)進行調(diào)整:

Pytorch學習率調(diào)整策略通過 torch.optim.lr_sheduler 接口實現(xiàn)。pytorch提供的學習率調(diào)整策略分為三大類,分別是:
(1)有序調(diào)整:等間隔調(diào)整(Step),多間隔調(diào)整(MultiStep),指數(shù)衰減(Exponential),余弦退火(CosineAnnealing);
(2)自適應調(diào)整:依訓練狀況伺機而變,通過監(jiān)測某個指標的變化情況(loss、accuracy),當該指標不怎么變化時,就是調(diào)整學習率的時機(ReduceLROnPlateau);
(3)自定義調(diào)整:通過自定義關(guān)于epoch的lambda函數(shù)調(diào)整學習率(LambdaLR)。

在每個epoch的訓練中,使用scheduler.step()語句進行學習率更新,此方法類似于optimizer.step()更新模型參數(shù),即一次epoch對應一次scheduler.step()。但在mini-batch訓練中,每個mini-bitch對應一個optimizer.step()。即用法如下:

optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)

def train(...):
    for i, data in enumerate(train_loader):
        ......
        y_ = model(x)
        loss = criterion(y_,y)
        loss.backward()
        optimizer.step()
        ......
 
for epoch in range(epochs):
    train(...)
    test(...)
    scheduler.step()
(1) 等間隔調(diào)整學習率 StepLR
torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)

每訓練step_size個epoch,學習率調(diào)整為lr=lr*gamma.
以下內(nèi)容中都將epoch和step對等,因為每個epoch中只進行一次scheduler.step(),實則該step指scheduler.step()中的step, 即step_size指scheduler.step()進行的次數(shù)。
參數(shù):

  • optimizer: 神經(jīng)網(wǎng)絡訓練中使用的優(yōu)化器,如optimizer=torch.optim.SGD(...)
  • step_size(int): 學習率下降間隔數(shù),單位是epoch,而不是iteration.
  • gamma(float): 學習率調(diào)整倍數(shù),默認為0.1
  • last_epoch(int): 上一個epoch數(shù),這個變量用來指示學習率是否需要調(diào)整。當last_epoch符合設定的間隔時,就會對學習率進行調(diào)整;當為-1時,學習率設置為初始值。


    等間隔調(diào)整學習率:step_size=30, gamma=0.1
(2) 多間隔調(diào)整學習率 MultiStepLR

跟(1)類似,但學習率調(diào)整的間隔并不是相等的,如epoch=10時調(diào)整一次,epoch=30時調(diào)整一次,epoch=80時調(diào)整一次…

torch.optim.lr_sheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)

參數(shù):
milestone(list): 一個列表參數(shù),表示多個學習率需要調(diào)整的epoch值,如milestones=[10, 30, 80].
其它參數(shù)同(1)。


多間隔調(diào)整學習率:milestones=[10, 30, 80], gamma=0.1
(3) 指數(shù)衰減調(diào)整學習率 ExponentialLR

學習率呈指數(shù)型衰減,每訓練一個epoch,lr=lrgamma*epoch,即lr×gamma^{epoch}

torch.optim.lr_sheduler.ExponentialLR(optimizer, gamma, last_epoch)

參數(shù):

  • gamma(float):學習率調(diào)整倍數(shù)的底數(shù),指數(shù)為epoch,初始值我lr, 倍數(shù)為gamma^{epoch}
    其它參數(shù)同上。
    指數(shù)衰減調(diào)整學習率:gamma=0.9
(4) 余弦退火函數(shù)調(diào)整學習率:

學習率呈余弦函數(shù)型衰減,并以2*T_max為余弦函數(shù)周期,epoch=0對應余弦型學習率調(diào)整曲線的x=0,y_{max}=lr,epoch=T_max對應余弦型學習率調(diào)整曲線的x=\pi,y_{min}=eta_min處,隨著epoch>T_max,學習率隨epoch增加逐漸上升,整個走勢同cos(x)。

torch.optim.lr_sheduler.CosineAnnealingLR(optimizer, T_max, eta_min=0, last_epoch=-1)

參數(shù):

  • T_max(int): 學習率下降到最小值時的epoch數(shù),即當epoch=T_max時,學習率下降到余弦函數(shù)最小值,當epoch>T_max時,學習率將增大;
  • eta_min: 學習率調(diào)整的最小值,即epoch=T_max時,lr_{min}=eta_min, 默認為0.
    其它參數(shù)同上。
    學習率余弦衰減:T_max=100
(5) 根據(jù)指標調(diào)整學習率 ReduceLROnPlateau

當某指標(loss或accuracy)在最近幾個epoch中都沒有變化(下降或升高超過給定閾值)時,調(diào)整學習率。
如當驗證集的loss不再下降是,調(diào)整學習率;或監(jiān)察驗證集的accuracy不再升高時,調(diào)整學習率。

torch.optim.lr_sheduler.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)

參數(shù):

  • mode(str): 模式選擇,有min和max兩種模式,min表示當指標不再降低(如監(jiān)測loss),max表示當指標不再升高(如監(jiān)測accuracy)。
  • factor(float): 學習率調(diào)整倍數(shù),同前面的gamma,當監(jiān)測指標達到要求時,lr=lr×factor。
  • patience(int): 忍受該指標多少個epoch不變化,當忍無可忍時,調(diào)整學習率。
  • verbose(bool): 是否打印學習率信息,print( 'Epoch {:5d} reducing learning rate of group {} to {:.4e}.'.format(epoch, i, new_lr), 默認為False, 即不打印該信息。
  • threshold_mode (str): 選擇判斷指標是否達最優(yōu)的模式,有兩種模式:rel 和 abs.
    當threshold_mode == rel, 并且 mode == max時,dynamic_threshold = best * (1 + threshold);
    當threshold_mode == rel, 并且 mode == min時,dynamic_threshold = best * (1 - threshold);
    當threshold_mode == abs, 并且 mode == max時,dynamic_threshold = best + threshold;
    當threshold_mode == abs, 并且 mode == min時,dynamic_threshold = best - threshold;
  • threshold(float): 配合threshold_mode使用。
  • cooldown(int): “冷卻時間”,當調(diào)整學習率之后,讓學習率調(diào)整策略冷靜一下,讓模型在訓練一段時間,再重啟監(jiān)測模式。
  • min_lr(float or list): 學習率下限,可為float,或者list,當有多個參數(shù)組時,可用list進行設置。
  • eps(float): 學習率衰減的最小值,當學習率的變化值小于eps時,則不調(diào)整學習率。
optimizer = torch.optim.SGD(model.parameters(), args.lr,
 momentum=args.momentum, weight_decay=args.weight_decay)

scheduler = ReducelROnPlateau(optimizer,'min')
for epoch in range( args.start epoch, args.epochs ):
    train(train_loader , model, criterion, optimizer, epoch )
    result_avg, loss_val = validate(val_loader, model, criterion, epoch)
    # Note that step should be called after validate()
    scheduler.step(loss_val )
(6) 自定義調(diào)整學習率 LambdaLR

為不同參數(shù)組設定不同學習率調(diào)整策略。調(diào)整規(guī)則為:
lr = base_lr * lambda(self.last_epoch)
在fine-tune中特別有用,我們不僅可以為不同層設置不同的學習率,還可以為不同層設置不同的學習率調(diào)整策略。

torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1)

參數(shù):

  • lr_lambda(function or list): 自定義計算學習率調(diào)整倍數(shù)的函數(shù),通常時epoch的函數(shù),當有多個參數(shù)組時,設為list.
    其它參數(shù)同上。
    例:
ignored_params = list(map(id, net.fc3.parameters()))
base_params = filter(lambda p: id(p) not in ignored_params, net.parameters())
optimizer = optim.SGD([
        {'params': base_params},
        {'params': net.fc3.parameters(), 'lr': 0.001*100}], 0.001,          momentum=0.9,weight_decay=1e-4)
 # Assuming optimizer has two groups.
lambda1 = lambda epoch: epoch // 3
lambda2 = lambda epoch: 0.95 ** epoch
scheduler = LambdaLR(optimizer, lr_lambda=[lambda1, lambda2])
for epoch in range(100):
    train(...)
    validate(...)
    scheduler.step()
    print('epoch: ', i, 'lr: ', scheduler.get_lr())
    
輸出:
epoch: 0 lr: [0.0, 0.1]
epoch: 1 lr: [0.0, 0.095]
epoch: 2 lr: [0.0, 0.09025]
epoch: 3 lr: [0.001, 0.0857375]
epoch: 4 lr: [0.001, 0.081450625]
epoch: 5 lr: [0.001, 0.07737809374999999]
epoch: 6 lr: [0.002, 0.07350918906249998]
epoch: 7 lr: [0.002, 0.06983372960937498]
epoch: 8 lr: [0.002, 0.06634204312890622]
epoch: 9 lr: [0.003, 0.0630249409724609]
為什么第一個參數(shù)組的學習率會是 0 呢? 來看看學習率是如何計算的。
第一個參數(shù)組的初始學習率設置為 0.001, 
lambda1 = lambda epoch: epoch // 3,
第 1 個 epoch 時,由 lr = base_lr * lmbda(self.last_epoch),
可知道 lr = 0.001 *(0//3) ,又因為 1//3 等于 0,所以導致學習率為 0。
第二個參數(shù)組的學習率變化,就很容易看啦,初始為 0.1, lr = 0.1 * 0.95^epoch ,當
epoch 為 0 時, lr=0.1 , epoch 為 1 時, lr=0.1*0.95。

附:給出畫上述學習率變化圖的程序:

# -*- coding:utf-8 -*-
'''本文件用于測試pytorch學習率調(diào)整策略'''

__author__ = 'puxitong from UESTC'

import torch
import torch.optim as optim
from torch.optim import lr_scheduler
from torchvision.models import AlexNet
import matplotlib.pyplot as plt 


model = AlexNet(num_classes=2)
optimizer = optim.SGD(params=model.parameters(), lr=0.1)

# 等間隔調(diào)整學習率,每訓練step_size個epoch,lr*gamma
# scheduler = lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)

# 多間隔調(diào)整學習率,每訓練至milestones中的epoch,lr*gamma
# scheduler = lr_scheduler.MultiStepLR(optimizer, milestones=[10, 30, 80], gamma=0.1)

# 指數(shù)學習率衰減,lr*gamma**epoch
# scheduler = lr_scheduler.ExponentialLR(optimizer, gamma=0.9)

# 余弦退火學習率衰減,T_max表示半個周期,lr的初始值作為余弦函數(shù)0處的極大值逐漸開始下降,
# 在epoch=T_max時lr降至最小值,即pi/2處,然后進入后半個周期,lr增大
# scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=100, eta_min=0)

plt.figure()
x = list(range(100))
y = []
for epoch in range(100):
    scheduler.step()
    y.append(scheduler.get_lr()[0])

plt.plot(x, y)
plt.show()

2. 手動調(diào)整學習率:

手動調(diào)整學習率,通常可以定義如下函數(shù):

def adjust_learning_rate(optimizer, epoch):
    """Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""
    lr = args.lr * (0.1 ** (epoch // 30))
    for param_group in optimizer.param_groups:
        param_group['lr'] = lr

又如:

def adjust_learning_rate(epoch, lr):
    if epoch <= 81:  # 32k iterations
      return lr
    elif epoch <= 122:  # 48k iterations
      return lr/10
    else:
      return lr/100

該函數(shù)通過修改每個epoch下,各參數(shù)組中的lr來進行學習率手動調(diào)整,用法如下:

for epoch in range(epochs):
    lr = adjust_learning_rate(optimizer, epoch)  # 調(diào)整學習率
    optimizer = optim.SGD(net.parameters(), lr=lr, momentum=0.9, weight_decay=5e-4)
    ......
    optimizer.step()  # 采用新的學習率進行參數(shù)更新
什么是param_groups?

optimizer通過param_group來管理參數(shù)組.param_group中保存了參數(shù)組及其對應的學習率,動量等等.所以我們可以通過更改param_group['lr']的值來更改對應參數(shù)組的學習率。

# 例1:有兩個`param_group`即,len(optim.param_groups)==2
optim.SGD([
                {'params': model.base.parameters()},
                {'params': model.classifier.parameters(), 'lr': 1e-3}
            ], lr=1e-2, momentum=0.9)
 
# 例2:一個參數(shù)組
optim.SGD(model.parameters(), lr=1e-2, momentum=.9)

上面第一個例子中,我們分別為 model.base 和 model.classifier 的參數(shù)設置了不同的學習率,即此時 optimizer.param_grops 中有兩個不同的param_group:
param_groups[0]: {'params': model.base.parameters()},
param_groups[1]: {'params': model.classifier.parameters(), 'lr': 1e-3}
每一個param_group都是一個字典,它們共同構(gòu)成了param_groups,所以此時len(optimizer.param_grops)==2,aijust_learning_rate() 函數(shù)就是通過for循環(huán)遍歷取出每一個param_group,然后修改其中的鍵 'lr' 的值,稱之為手動調(diào)整學習率。

第二個例子中l(wèi)en(optimizer.param_grops)==1,利用for循環(huán)進行修改同樣成立。

如果想要每次迭代都實時打印學習率,這樣可以每次step都能知道更新的最新學習率,可以使用

scheduler.get_lr()

它返回一個學習率列表,由參數(shù)組中的不同學習率組成,可通過列表索引來得到不同參數(shù)組中的學習率。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內(nèi)容