PyTorch-訓練

簡介

在之前專欄的兩篇文章中我主要介紹了數據的準備以及模型的構建,模型構建完成的下一步就是模型的訓練優化,訓練完成的模型用於實際應用中。

損失函數

損失函數用於衡量預測值與目標值之間的誤差,通過最小化損失函數達到模型的優化目標。不同的損失函數其衡量效果不同,未必都是出於最好的精度而設計的。PyTorch對於很多常用的損失函數進行了封裝,均在torch.nn模塊下,它們的使用方法類似,實例化損失計算對象,然後用實例化的對象對預測值和目標值進行損失計算即可。

  • L1損失
    • nn.L1Loss(reduction)
    • 計算L1損失即絕對值誤差。
    • reduce參數表示是否返回標量,默認返回標量,否則返回同維張量。
    • size_average參數表示是否返回的標量爲均值,默認爲均值,否則爲求和結果。
    • reduction參數取代了上述兩個參數,meansumNone的取值對應上面的結果。
    • 下面代碼可以演示損失的計算流程。
      import torch
      from torch import nn
      pred = torch.ones(100, 1) * 0.5
      label = torch.ones(100, 1)
      
      l1_mean = nn.L1Loss()
      l1_sum = nn.L1Loss(reduction='sum')
      
      print(l1_mean(pred, label))
      print(l1_sum(pred, label))
      
  • MSE損失
    • nn.MSELoss(reduction='mean')
    • 計算均方誤差,常用於迴歸問題。
    • 參數同上。
  • CE損失
    • nn.CrossEntropyLoss(weight=None, ignore_index=-100, reduction='mean')
    • 計算交叉熵損失,常用於分類問題。並非標準的交叉熵,而是結合了Softmax的結果,也就是說會將結果先進行softmax計算爲概率分佈。
    • weight參數是每個類別的權重,用於解決樣本不均衡問題。
    • reduction參數類似上面的損失函數。
    • ignore_index參數表示忽略某個類別,不計算其損失。
  • KL散度
    • nn.KLDivLoss(reduction='mean')
    • 計算KL散度。
    • 參數同上。
  • 二分交叉熵
    • nn.BCELoss(reduction='mean')
    • 計算二分交叉熵損失,一般用於二分類問題。
  • 邏輯二分交叉熵
    • nn.BCEWithLogitsLoss()
    • 輸入先經過sigmoid變換再計算損失,類似CE損失。

上述只是提到了幾個常用的簡單損失函數,更加複雜的可以查看官方文檔,一共封裝了近20個損失,當然,也可以自定義損失函數,返回一個張量或者標量即可(事實上這些損失函數就是這麼幹的)。

優化器

數據、模型、損失函數都確定了,那這個深度模型任務其實已經完成了大半,接下來就是選擇合適的優化器對模型進行優化訓練。

首先,要了解PyTorch中優化器的機制,其所有優化器都是繼承自Optimizer類,該類封裝了一套基礎的方法如state_dict()load_state_dict()等。

參數組(param_groups)

任何優化器都有一個屬性爲param_groups,這是因爲優化器對參數的管理是基於組進行的,爲每一組參數配置特定的學習率、動量比例、衰減率等等,該屬性爲一個列表,裏面多個字典,對應不同的參數及其配置。

例如下面的代碼中只有一個組。

import torch
import torch.optim as optim


w1 = torch.randn(2, 2)
w2 = torch.randn(2, 2)

optimizer = optim.SGD([w1, w2], lr=0.1)
print(optimizer.param_groups)

在這裏插入圖片描述

梯度清零

事實上,PyTorch不會在一次優化完成後清零之前計算得到的梯度,所以需要每次優化完成後手動清零,即調用優化器的zero_grad()方法。

參數組添加

通過調用優化器的add_param_group()方法可以添加一組定製的參數。

常用優化器

PyTorch將這些優化算法均封裝於torch.optim模塊下,其實現時對原論文有所改動,具體參見源碼。

  • 隨機梯度下降
    • optim.SGD(params, lr, momentum, weight_decay)
    • 隨機梯度下降優化器。
    • params參數表示需要管理的參數組。
    • lr參數表示初始學習率,可以按需調整學習率。
    • momentum參數表示動量SGD中的動量值,一般爲0.9。
    • weight_decay參數表示權重衰減係數,也是L2正則係數。
  • 隨機平均梯度下降
    • optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-8, weight_decay=0, amsgrad=False)
    • Adam優化算法的實現。
    • 參數類似上面。

下圖演示了各種算法相同情境下的收斂效果。

在這裏插入圖片描述

學習率調整策略

合適的學習率可以使得模型迅速收斂,這也是Adam等算法的初衷,一般我們訓練時會在開始給一個較大的學習率,隨着訓練的進行逐漸下調這個學習率。那麼何時下調、下調多少,相關的問題就是學習率調整策略,PyTorch提供了6中策略以供使用,它們都在torch.optim.lr_scheduler中,分爲有序調整(較爲死板)、自適應調整(較爲靈活)和自定義調整(適合各種情況)。

下面介紹最常用的自動學習率調整機制。它封裝爲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-8)

當指標不再變化時即調整學習率,這是一種非常實用的學習率調整策略。例如,當驗證集的損失不再下降即即將陷入過擬合,進行學習率調整。

  • mode參數由兩種爲minmax,當指標不再變低或者變高時調整。
  • factor參數表示學習率調整比例。
  • patience參數表示等待耐心,當patience個step指標不變即調整學習率。
  • verbose參數表示調整學習率是否可見。
  • cooldown參數表示冷卻時間,調整後冷卻時間內不再調整。
  • min_lr參數表示學習率下限。
  • eps參數表示學習率衰減最小值,學習率變化小於該值不調整。

訓練流程實戰

下面的代碼演示了數據的導入、模型構建、損失函數使用以及優化器的優化整個流程,大部分時候我們使用PyTorch進行模型訓練都是這個思路

import torch
from torch import nn
import torch.nn.functional as F
from torch import optim


class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=(3, 3))
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3)
        self.pool2 = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(64*54*54, 256)
        self.fc2 = nn.Linear(256, 128)
        self.fc3 = nn.Linear(128, 101)

    def forward(self, x):
        x = self.pool1(F.relu(self.conv1(x)))
        x = self.pool2(F.relu(self.conv2(x)))
        x = x.view(-1, 64*54*54)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()
x = torch.randn((32, 3, 224, 224))
y = torch.ones(32, ).long()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9, dampening=0.1)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.1)

epochs = 10
losses = []
for epoch in range(epochs):

    correct = 0.0
    total = 0.0

    optimizer.zero_grad()
    outputs = net(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()
    scheduler.step()
    _, predicted = torch.max(outputs.data, 1)
    total += y.size(0)
    correct += (predicted == y).squeeze().sum().numpy()
    losses.append(loss.item())
    print("loss", loss.item(), "acc", correct / total)

import matplotlib.pyplot as plt
plt.plot(list(range(len(losses))), losses)
plt.savefig('his.png')
plt.show()

其訓練損失變化圖如下,由於只是給出的demo數據,訓練很快收斂,準確率一輪達到100%。

在這裏插入圖片描述

補充說明

本文介紹了PyTorch中損失函數的使用以及優化器的優化流程,這也是深度模型訓練的最後步驟,比較重要。本文的所有代碼均開源於我的Github,歡迎star或者fork。

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