Pytorch optimizer.step() 和loss.backward()和scheduler.step()的關係與區別

首先需要明確optimzier優化器的作用, 形象地來說,優化器就是需要根據網絡反向傳播的梯度信息來更新網絡的參數,以起到降低loss函數計算值的作用,這也是機器學習裏面最一般的方法論。

從優化器的作用出發,要使得優化器能夠起作用,需要主要兩個東西:

1. 優化器需要知道當前的網絡或者別的什麼模型的參數空間,這也就是爲什麼在訓練文件中,正式開始訓練之前需要將網絡的參數放到優化器裏面,比如使用pytorch的話總會出現類似如下的代碼:

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的寫法放在參考部分。

    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()的時候,這也就是經常會碰到,如下情況

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這個大循環下。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章