深度學習(十四)——優化器

2023-09-15 12:00:43

反向傳播可以求出神經網路中每個需要調節引數的梯度(grad),優化器可以根據梯度進行調整,達到降低整體誤差的作用。下面我們對優化器進行介紹。

1. 如何使用優化器

官方檔案:torch.optim — PyTorch 2.0 documentation

(1)構造優化器

舉個栗子:

optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
optimizer = optim.Adam([var1, var2], lr=0.0001)
  • 首先選擇優化器的演演算法optim.SGD

  • 之後在優化器中放入模型引數model.parameters(),這一步是必備

  • 還可在函數中設定一些引數,如學習速率lr=0.01(這是每個優化器中幾乎都會有的引數)

(2)呼叫優化器中的step方法

step()方法就是利用我們之前獲得的梯度,對神經網路中的引數進行更新。

舉個栗子:

for input, target in dataset:
    optimizer.zero_grad()
    output = model(input)
    loss = loss_fn(output, target)
    loss.backward()
    optimizer.step()
  • 步驟optimizer.zero_grad()是必選的

  • 我們的輸入經過了模型,並得到了輸出output

  • 之後計算輸出和target之間的誤差loss

  • 呼叫誤差的反向傳播loss.backwrd更新每個引數對應的梯度

  • 呼叫optimizer.step()對摺積核中的引數進行優化調整

  • 之後繼續進入for迴圈,使用函數optimizer.zero_grad()對每個引數的梯度進行清零,防止上一輪迴圈中計算出來的梯度影響下一輪迴圈。

2. 優化器的使用

優化器中演演算法共有的引數(其他引數因優化器的演演算法而異):

  • params: 傳入優化器模型中的引數

  • lr: learning rate,即學習速率

關於學習速率

  • 一般來說,學習速率設定得太大,模型執行起來會不穩定

  • 學習速率設定得太小,模型訓練起來會過

  • 建議在最開始訓練模型的時候,選擇設定一個較大的學習速率;訓練到後面的時候,再選擇一個較小的學習速率

程式碼栗子:

import torch.optim
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader

dataset=torchvision.datasets.CIFAR10("./dataset",train=False,download=True,transform=torchvision.transforms.ToTensor())
dataloder=DataLoader(dataset,batch_size=1)

class Demo(nn.Module):
    def __init__(self):
        super(Demo,self).__init__()

        self.model1=Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self,x):
        x=self.model1(x)
        return x

demo=Demo()
loss=nn.CrossEntropyLoss()

#設定優化器
#選擇隨機梯度下降
optim=torch.optim.SGD(demo.parameters(),lr=0.01)   #一般來說,學習速率設定得太大,模型執行起來會不穩定;設定得太小,模型訓練起來會比較慢


#對資料進行20次迴圈
for epoch in range(20):
    running_loss=0.0  #初始化loss
    #該回圈只對資料進行了一次訓練
    for data in dataloder:
        imgs,targets=data
        output=demo(imgs)
        result_loss=loss(output,targets)

    #----------------優化器訓練過程---------------------
        optim.zero_grad()   #各個引數對應的梯度設定為0
        result_loss.backward()  #反向傳播,得到每個節點對應的梯度
        optim.step()   #根據每個引數的梯度,對引數進行調優
        running_loss=running_loss+result_loss  #累加該輪迴圈的loss,計算該輪迴圈整體誤差的總和

    print(running_loss)  #輸出該輪迴圈整體誤差的總和
  • [Run]

    tensor(18713.4336, grad_fn=)
    tensor(16178.3564, grad_fn=)
    tensor(15432.6172, grad_fn=)
    tensor(16043.1025, grad_fn=)
    tensor(18018.3359, grad_fn=)

    ......

總結使用優化器訓練的訓練套路):

  • 設定損失函數loss function

  • 定義優化器optim

  • 從使用迴圈dataloader中的資料:for data in dataloder

    • 取出圖片imgs,標籤targets:imgs,targets=data

    • 將圖片放入神經網路,並得到一個輸出:output=model(imgs)

    • 計算誤差:loss_result=loss(output,targets)

    • 使用優化器,初始化引數的梯度為0:optim.zero_grad()

    • 使用反向傳播求出梯度:loss_result.backward()

    • 根據梯度,對每一個引數進行更新:optim.step()

  • 進入下一個迴圈,直到完成訓練所需的迴圈次數

3. 如何調整學習速率

再複製貼上一次:

  • 一般來說,學習速率設定得太大,模型執行起來會不穩定

  • 學習速率設定得太小,模型訓練起來會過

  • 建議在最開始訓練模型的時候,選擇設定一個較大的學習速率;訓練到後面的時候,再選擇一個較小的學習速率

pytorch中提供了一些方法,可以動態地調整學習速率

官方檔案:StepLR — PyTorch 2.0 documentation

(1)StepLR引數簡介

引數介紹:

  • optimizer: 放入模型所使用的優化器名稱

  • step_size(int): 訓練的時候,每多少步進行一個更新

  • gamma(float): 預設為0.1。在迴圈中,每次訓練的時候,新的學習速率=原來學習速率×gamma

不同的優化器中有很多不同的引數,但是這些引數都是跟幾個特定的演演算法相關的,這些需要使用的時候再去了解。

如果只是單純地使用優化器,那麼只需設定optimizer學習速率,就可以滿足絕大部分的訓練需求。

(2)StepLR程式碼栗子

import torch.optim
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader

dataset=torchvision.datasets.CIFAR10("./dataset",train=False,download=True,transform=torchvision.transforms.ToTensor())
dataloder=DataLoader(dataset,batch_size=1)

class Demo(nn.Module):
    def __init__(self):
        super(Demo,self).__init__()

        self.model1=Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self,x):
        x=self.model1(x)
        return x

demo=Demo()
loss=nn.CrossEntropyLoss()

#設定優化器
#選擇隨機梯度下降
optim=torch.optim.SGD(demo.parameters(),lr=0.01)   #一般來說,學習速率設定得太大,模型執行起來會不穩定;設定得太小,模型訓練起來會比較慢

#加入學習速率更新
scheduler = torch.optim.lr_scheduler.StepLR(optim, step_size=5, gamma=0.1)

#對資料進行20次迴圈
for epoch in range(20):
    running_loss=0.0  #初始化loss
    #該回圈只對資料進行了一次訓練
    for data in dataloder:
        imgs,targets=data
        output=demo(imgs)
        result_loss=loss(output,targets)

    #----------------優化器訓練過程---------------------
        optim.zero_grad()   #各個引數對應的梯度設定為0;如果不寫這行程式碼,那麼每次迴圈中都會對這個梯度進行累加
        result_loss.backward()  #反向傳播,得到每個節點對應的梯度
        #optim.step()   #根據每個引數的梯度,對引數進行調優
        scheduler.step()  #對每個引數的學習速率進行調整;通過scheduler可以在每次迴圈中對學習速率進行下降
        running_loss=running_loss+result_loss  #累加該輪迴圈的loss,計算該輪迴圈整體誤差的總和

    print(running_loss)  #輸出該輪迴圈整體誤差的總和