pytorch優(yōu)化器與學習率設(shè)置詳解
點擊上方“程序員大白”,選擇“星標”公眾號
重磅干貨,第一時間送達
來源 |?https://a.3durl.cn/YrcZbc
導讀
?在很多學習過程中,都會采用動態(tài)調(diào)整學習率的方法。剛開始訓練的時候,學習率設(shè)置大一點,以加快學習速度;之后逐漸減小學習率,來尋找最優(yōu)解。那么在Pytorch中,如在訓練過程中動態(tài)地調(diào)整學習率呢?
學習率設(shè)置對于學習過程來說相當重要。學習率過低會導致學習速度太慢,學習率過高又容易導致難以收斂。在很多學習過程中,都會采用動態(tài)調(diào)整學習率的方法。剛開始訓練的時候,學習率設(shè)置大一點,以加快學習速度;之后逐漸減小學習率,來尋找最優(yōu)解。
那么在Pytorch中,如在訓練過程中動態(tài)地調(diào)整學習率呢?
目錄
優(yōu)化器Optimizer 只訓練模型的一部分參數(shù) 不同部分的參數(shù)設(shè)置不同的學習率(以及其他屬性) Optimizer基本屬性 optimizer基本方法 動態(tài)更新learning rate torch.optim.lr_scheduler torch.optim.lr_scheduler.LambdaLr torch.optim.lr_scheduler.StepLR torch.optim.lr_scheduler.MultiStepLR torch.optim.lr_scheduler.ExponentialLR torch.optim.lr_scheduler.CosineAnnealingLR torch.optim.lr_scheduler.ReduceLROnPlateau 手動修改lr
優(yōu)化器Optimizer
在說學習率調(diào)整方法之前,先來了解一下Pytorch中的優(yōu)化器Optimizer機制。
用過Pytorch的都知道,模型訓練時的固定搭配。
loss.backward()
optimizer.step()
optimizer.zero_grad()
...
簡單來說,loss.backward()就是反向計算出各參數(shù)的梯度,然后optimizer.step()就是更新網(wǎng)絡(luò)中的參數(shù),optimizer.zero_grad()將這一輪的梯度清零,防止這一輪的梯度影響下一輪的更新。
常用優(yōu)化器都在torch.optim包中,因此需要先導入包:
import?torch.optim.Adam
import?torch.optim.SGD
這里以常用的Adam優(yōu)化器和SGD優(yōu)化器為例,介紹一下Pytorch中的優(yōu)化器使用方法。
假設(shè)我們有一個網(wǎng)絡(luò)如下,下面的例子都以此網(wǎng)絡(luò)作為例子:
class?Net(nn.Module):
????def?__init__(self):
????????super(Net,?self).__init__()
????????self.layer?=?nn.Linear(10,?2)
????????self.layer2?=?nn.Linear(2,?10)
????def?forward(self,?input):
????????return?self.layer(input)???????
# Optimizer基本屬性
所有Optimizer公有的一些基本屬性:
lr: learning rate,學習率 eps: 學習率最小值,在動態(tài)更新學習率時,學習率最小不會小于該值。 weight_decay: 權(quán)值衰減。相當于對參數(shù)進行L2正則化(使模型復雜度盡可能低,防止過擬合),該值可以理解為正則化項的系數(shù)。 betas: (待研究) amsgrad: (bool)(待研究)
每個Optimizer都維護一個param_groups的list。該list中維護需要優(yōu)化的參數(shù)以及對應(yīng)的屬性設(shè)置。
optimizer基本方法
add_param_group(param_group): 為optimizer的param_groups增加一個參數(shù)組。這在微調(diào)預先訓練的網(wǎng)絡(luò)時非常有用,因為凍結(jié)層可以訓練并隨著訓練的進行添加到優(yōu)化器中。 load_state_dict(state_dict): 加載optimizer state。參數(shù)必須是optimizer.state_dict()返回的對象。 state_dict(): 返回一個dict,包含optimizer的狀態(tài):state和param_groups。 step(closure): 執(zhí)行一次參數(shù)更新過程。 zero_grad(): 清除所有已經(jīng)更新的參數(shù)的梯度。
我們在構(gòu)造優(yōu)化器時,最簡單的方法通常如下:
model?=?Net()
optimizer_Adam?=?torch.optim.Adam(model.parameters(),?lr=0.1)
model.parameters()返回網(wǎng)絡(luò)model的全部參數(shù)。
將model的全部參數(shù)傳入Adam中構(gòu)造出一個Adam優(yōu)化器,并設(shè)置 learning rate=0.1。因此該 Adam 優(yōu)化器的 param_groups 維護的就是模型 model 的全部參數(shù),并且學習率為0.1。這樣在調(diào)用optimizer_Adam.step()時,就會對model的全部參數(shù)進行更新。
Optimizer的param_groups是一個list,其中的每個元素都是一組獨立的參數(shù),以dict的方式存儲。結(jié)構(gòu)如下:
-param_groups
????-0(dict)??#?第一組參數(shù)
????????params:??#?維護要更新的參數(shù)
????????lr:??#?該組參數(shù)的學習率
????????betas:
????????eps:??#?該組參數(shù)的學習率最小值
????????weight_decay:??#?該組參數(shù)的權(quán)重衰減系數(shù)
????????amsgrad:??
????-1(dict)??#?第二組參數(shù)
????-2(dict)??#?第三組參數(shù)
????...
????...
這樣可以實現(xiàn)很多靈活的操作,比如以下。
只訓練模型的一部分參數(shù)
例如,只想訓練上面的model中的layer參數(shù),而保持layer2的參數(shù)不動。可以如下設(shè)置Optimizer:
model?=?Net()
optimizer_Adam?=?torch.optim.Adam(model.layer.parameters(),?lr=0.1)??#?只傳入layer層的參數(shù),就可以只更新layer層的參數(shù)而不影響其他參數(shù)。
不同部分的參數(shù)設(shè)置不同的學習率(以及其他屬性)
例如,要想使model的layer參數(shù)學習率為0.1,layer2的參數(shù)學習率為0.2,可以如下設(shè)置Optimizer:
model?=?Net()
params_dict?=?[{'params':?model.layer.parameters(),?'lr':?0.1},
???????????????????{'params':?model.layer2.parameters(),?'lr':?0.2}]
optimizer_Adam?=?torch.optim.Adam(params_dict)
這種方法更為靈活,手動構(gòu)造一個params_dict列表來初始化Optimizer。注意,字典中的參數(shù)部分的 key 必須為 ‘params’。
這樣就可以靈活的設(shè)置Optimizer啦。
動態(tài)更新learning rate
了解了Optimizer的基本結(jié)構(gòu)和使用方法,接下來就可以看一下,如何在訓練過程中動態(tài)更新learning rate。
手動修改lr
上面我們了解到,Optimizer的每一組參數(shù)維護一個lr,因此,最直接的方法就是我們在訓練過程中手動修改Optimizer中對應(yīng)的lr的值:
model?=?Net()??#?生成網(wǎng)絡(luò)
optimizer_Adam?=?torch.optim.Adam(model.parameters(),?lr=0.1)??#?生成優(yōu)化器
lr_list?=?[]
for?epoch?in?range(100):??#?假設(shè)迭代100次
????if?epoch?%?5?==?0:??#?每迭代5次,更新一次學習率
????????for?params?in?optimizer_Adam.param_groups:??#?遍歷Optimizer中的每一組參數(shù)
????????????params['lr']?*=?0.9??#?將該組參數(shù)的學習率?*?0.9
????????????#?params['weight_decay']?=?0.5??#?當然也可以修改其他屬性
????lr_list.append(optimizer_Adam.state_dict()['param_groups'][0]['lr'])
plt.plot(range(100),?lr_list,?color='r')
plt.show()

torch.optim.lr_scheduler
torch.optim.lr_scheduler包中提供了一些類,用于動態(tài)修改lr。
torch.optim.lr_scheduler.LambdaLrtorch.optim.lr_scheduler.StepLRtorch.optim.lr_scheduler.MultiStepLRtorch.optim.lr_scheduler.ExponentialLRtorch.optim.lr_sheduler.CosineAnneaingLRtorch.optim.lr_scheduler.ReduceLROnPlateau
注意: pytorch 1.1.0版本之后,在創(chuàng)建了lr_scheduler對象之后,會自動執(zhí)行第一次lr更新(可以理解為執(zhí)行一次scheduler.step())。因此,在使用的時候,需要先調(diào)用optimizer.step(),再調(diào)用scheduler.step()。如果創(chuàng)建了lr_scheduler對象之后,先調(diào)用scheduler.step(),再調(diào)用optimizer.step(),則會跳過了第一個學習率的值。
#?調(diào)用順序
loss.backward()
optimizer.step()
scheduler.step()
...
注意: 創(chuàng)建scheduler時,所傳入的Optimizer的param_groups必須有一個initial_lr鍵作為初始學習率。如果last_epoch=-1,則用于初始化的Optimizer可以沒有initial_lr鍵,以 lr 鍵初始化為initial_lr。
torch.optim.lr_scheduler.LambdaLr
torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1)
Optimizer:優(yōu)化器實例 lr_lambda:是一個函數(shù)(常用lambda表達式)或函數(shù)列表,該函數(shù)接收一個int參數(shù)(epoch),然后計算出一個系數(shù),最后學習率更新為 。其中l(wèi)r_lambda如果傳入多個函數(shù)的list的話,則對應(yīng)每組param_groups的學習率調(diào)整策略。 last_epoch:(int)上一個epoch數(shù)。默認為-1,且當last_epoch=-1時,將lr設(shè)置為initial_lr。第一次更新lr時,就按照epoch = last_epoch + 1更新。(比如last_epoch = 1,則第一次lr更新時,就將epoch=2傳入上面的lr_lambda函數(shù),得出系數(shù)
需要注意的是,該scheduler每次lr更新,是用initial_lr 乘以系數(shù) ? ,而不是用上一次的lr 乘以系數(shù) ,即

torch.optim.lr_scheduler.StepLR
torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)
每迭代step_size次,學習率乘以gamma。
model?=?Net()
optimizer_Adam?=?torch.optim.Adam(model.parameters(),?lr=0.1)
#?創(chuàng)建scheduler,每迭代5次,學習率衰減一半
scheduler?=?torch.optim.lr_scheduler.StepLR(optimizer_Adam,?step_size=5,?gamma=0.5,?last_epoch=-1)
lr_list_1?=?[]
for?epoch?in?range(100):
????scheduler.step()
????lr_list_1.append(optimizer_Adam.state_dict()['param_groups'][0]['lr'])
plt.plot(range(100),?lr_list_1,?color='r',?label='lr')
plt.legend()
plt.show()

torch.optim.lr_scheduler.MultiStepLR
torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)
多段衰減法。milestones傳入一個list,指定多個epoch數(shù),每迭代到指定的epoch次數(shù)時,lr乘以gamma。
例子:
model?=?Net()
optimizer_Adam?=?torch.optim.Adam(model.parameters(),?lr=0.1)??#?初始?lr=0.1
#?lr?變化
#?0?-?20?epoch:?0.1
#?21?-?40?epoch:?0.05
#?41?-?60?epoch:?0.025
#?60?-?80?epoch:?0.0125
#?80?-?end?epoch:?0.01125
scheduler?=?torch.optim.lr_scheduler.MultiStepLR(optimizer_Adam,?milestones=[20,?40,?60,?80],?gamma=0.5,?last_epoch=-1)
lr_list_1?=?[]
for?epoch?in?range(100):
????scheduler.step()
????lr_list_1.append(optimizer_Adam.state_dict()['param_groups'][0]['lr'])
plt.plot(range(100),?lr_list_1,?color='r',?label='lr')
plt.legend()
plt.show()

torch.optim.lr_scheduler.ExponentialLR
torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1)
每個epoch按指數(shù)衰減 lr。
model?=?Net()
optimizer_Adam?=?torch.optim.Adam(model.parameters(),?lr=0.1)
#?指數(shù)衰減學習率,衰減率為?gamma=0.9
scheduler?=?torch.optim.lr_scheduler.ExponentialLR(optimizer_Adam,?gamma=0.9,?last_epoch=-1)
lr_list_1?=?[]
for?epoch?in?range(100):
????scheduler.step()?
????lr_list_1.append(optimizer_Adam.state_dict()['param_groups'][0]['lr'])
plt.plot(range(100),?lr_list_1,?color='r',?label='lr')
plt.legend()
plt.show()

torch.optim.lr_scheduler.CosineAnnealingLR
torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max, eta_min=0, last_epoch=-1)
按照三角函數(shù)規(guī)則來更新學習率。
表示最小學習率, 即正弦函數(shù)最低點 表示最大學習率, 設(shè)置為initial_Ir (在last_epoch=-1 時, 即為Ir)。 表示當前epoch數(shù) 表示 個 周期所對應(yīng)的epoch數(shù)值
model?=?Net()
optimizer_Adam?=?torch.optim.Adam(model.parameters(),?lr=0.1)
#?周期為50epoch,lr最小值為0(默認)
scheduler?=?torch.optim.lr_scheduler.CosineAnnealingLR(optimizer_Adam,?eta_min=0,?T_max=25,?last_epoch=-1)
lr_list_1?=?[]
for?epoch?in?range(100):
????scheduler.step()
????lr_list_1.append(optimizer_Adam.state_dict()['param_groups'][0]['lr'])
plt.plot(range(100),?lr_list_1,?color='r',?label='lr')
plt.legend()
plt.show()

torch.optim.lr_scheduler.ReduceLROnPlateau
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)
根據(jù)指定的指標量來調(diào)整學習率。如果指標量停止變化時,就減小學習率。
參數(shù):
mode:(str),從(min, max)中選擇。 min:如果指定量不再下降,就減小lr max:如果指定量不再上升,就減小lr factor:(float),衰減因子,每次更新lr = lr * factor patience:(int),容忍度,如果經(jīng)過patience次迭代后,指標沒有變化(上升或下降),就更新lr。 verbose:(bool),每次更新lr,是否向std輸出。 threshold:(float),閾值,對于制定的指標只有超過閾值才算有變化 threshold_mode:(str),從(rel,abs)總選擇。性能衡量方式。 max模式下:dynamic_threshold = best + threshold min模式下:dynamic_threshold = best - threshold max模式下:dynamic_threshold = best * ( 1 + threshold ) min模式下:dynamic_threshold = best * ( 1 - threshold ) rel: abs: cooldown:(int),每次調(diào)整lr之后,冷卻cooldown個epoch,避免lr下降過快 min_lr:(float or list),學習率最小值。如果給定一個標量值,就param_groups中所有組都設(shè)置該最小值;也可以用一個list為每組指定一個最小值。 eps:(float),lr變化最小值,如果lr的兩次變化差距小于eps,則忽略這次變化。
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)推薦閱讀
關(guān)于程序員大白
程序員大白是一群哈工大,東北大學,西湖大學和上海交通大學的碩士博士運營維護的號,大家樂于分享高質(zhì)量文章,喜歡總結(jié)知識,歡迎關(guān)注[程序員大白],大家一起學習進步!


