當我們想指定每一層的學習率時:
1
2
3
4
|
optim.SGD([ { 'params' : model.base.parameters()}, { 'params' : model.classifier.parameters(), 'lr' : 1e - 3 } ], lr = 1e - 2 , momentum = 0.9 ) |
這意味著model.base的參數將會使用1e-2的學習率,model.classifier的參數將會使用1e-3的學習率,并且0.9的momentum將會被用于所有的參數。
進行單次優化
所有的optimizer都實現了step()方法,這個方法會更新所有的參數。它能按兩種方式來使用:
1
|
optimizer.step() |
這是大多數optimizer所支持的簡化版本。一旦梯度被如backward()之類的函數計算好后,我們就可以調用這個函數。
例子
1
2
3
4
5
6
7
|
for input , target in dataset: optimizer.zero_grad() output = model( input ) loss = loss_fn(output, target) loss.backward() optimizer.step() optimizer.step(closure) |
一些優化算法例如Conjugate Gradient和LBFGS需要重復多次計算函數,因此你需要傳入一個閉包去允許它們重新計算你的模型。這個閉包應當清空梯度,計算損失,然后返回。
例子:
1
2
3
4
5
6
7
8
|
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) |
補充:Pytorch optimizer.step() 和loss.backward()和scheduler.step()的關系與區別
首先需要明確optimzier優化器的作用, 形象地來說,優化器就是需要根據網絡反向傳播的梯度信息來更服務器之家絡的參數,以起到降低loss函數計算值的作用,這也是機器學習里面最一般的方法論。
從優化器的作用出發,要使得優化器能夠起作用,需要主要兩個東西:
1. 優化器需要知道當前的網絡或者別的什么模型的參數空間
這也就是為什么在訓練文件中,正式開始訓練之前需要將網絡的參數放到優化器里面,比如使用pytorch的話總會出現類似如下的代碼:
1
2
|
optimizer_G = Adam(model_G.parameters(), lr = train_c.lr_G) # lr 使用的是初始lr optimizer_D = Adam(model_D.parameters(), lr = train_c.lr_D) |
2. 需要知道反向傳播的梯度信息
我們還是從代碼入手,如下所示是Pytorch 中SGD優化算法的step()函數具體寫法,具體SGD的寫法放在參考部分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
def step( self , closure = None ): """Performs a single optimization step. Arguments: closure (callable, optional): A closure that reevaluates the model and returns the loss. """ loss = None if closure is not None : loss = closure() for group in self .param_groups: weight_decay = group[ 'weight_decay' ] momentum = group[ 'momentum' ] dampening = group[ 'dampening' ] nesterov = group[ 'nesterov' ] for p in group[ 'params' ]: if p.grad is None : continue d_p = p.grad.data if weight_decay ! = 0 : d_p.add_(weight_decay, p.data) if momentum ! = 0 : param_state = self .state[p] if 'momentum_buffer' not in param_state: buf = param_state[ 'momentum_buffer' ] = d_p.clone() else : buf = param_state[ 'momentum_buffer' ] buf.mul_(momentum).add_( 1 - dampening, d_p) if nesterov: d_p = d_p.add(momentum, buf) else : d_p = buf p.data.add_( - group[ 'lr' ], d_p) return loss |
從上面的代碼可以看到step這個函數使用的是參數空間(param_groups)中的grad,也就是當前參數空間對應的梯度,這也就解釋了為什么optimzier使用之前需要zero清零一下,因為如果不清零,那么使用的這個grad就得同上一個mini-batch有關,這不是我們需要的結果。
再回過頭來看,我們知道optimizer更新參數空間需要基于反向梯度,因此,當調用optimizer.step()的時候應當是loss.backward()的時候,這也就是經常會碰到,如下情況
1
2
|
total_loss.backward() optimizer_G.step() |
loss.backward()在前,然后跟一個step。
那么為什么optimizer.step()需要放在每一個batch訓練中,而不是epoch訓練中,這是因為現在的mini-batch訓練模式是假定每一個訓練集就只有mini-batch這樣大,因此實際上可以將每一次mini-batch看做是一次訓練,一次訓練更新一次參數空間,因而optimizer.step()放在這里。
scheduler.step()按照Pytorch的定義是用來更新優化器的學習率的,一般是按照epoch為單位進行更換,即多少個epoch后更換一次學習率,因而scheduler.step()放在epoch這個大循環下。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/qq_40178291/article/details/99963586