教你如何使用PyTorch解決多分類問題

華為雲開發者聯盟發表於2023-11-22

本文分享自華為雲社群《使用PyTorch解決多分類問題:構建、訓練和評估深度學習模型》,作者: 小饅頭學Python。

引言

當處理多分類問題時,PyTorch是一種非常有用的深度學習框架。在這篇部落格中,我們將討論如何使用PyTorch來解決多分類問題。我們將介紹多分類問題的基本概念,構建一個簡單的多分類神經網路模型,並演示如何準備資料、訓練模型和評估結果。

什麼是多分類問題?

多分類問題是一種機器學習任務,其中目標是將輸入資料分為多個不同的類別或標籤。與二分類問題不同,多分類問題涉及到三個或更多類別的分類任務。例如,影像分類問題可以將影像分為不同的類別,如貓、狗、鳥等。

處理步驟

  • 準備資料:

收集和準備資料集,確保每個樣本都有相應的標籤,以指明其所屬類別。

劃分資料集為訓練集、驗證集和測試集,以便進行模型訓練、調優和效能評估。

  • 資料預處理:

    對資料進行預處理,例如歸一化、標準化、缺失值處理或資料增強,以確保模型訓練的穩定性和效能。

  • 選擇模型架構:

    選擇適當的深度學習模型架構,通常包括卷積神經網路(CNN)、迴圈神經網路(RNN)、Transformer等,具體取決於問題的性質。

  • 定義損失函式:

    為多分類問題選擇適當的損失函式,通常是交叉熵損失(Cross-Entropy Loss)。

  • 選擇最佳化器:

    選擇合適的最佳化演算法,如隨機梯度下降(SGD)、Adam、RMSprop等,以訓練模型並調整權重。

  • 訓練模型:

    使用訓練資料集來訓練模型。在每個訓練迭代中,透過前向傳播和反向傳播來更新模型引數,以減小損失函式的值。

  • 評估模型:

    使用驗證集來評估模型效能。常見的效能指標包括準確性、精確度、召回率、F1分數等。

  • 調優模型:

    根據驗證集的效能,對模型進行調優,可以嘗試不同的超引數設定、模型架構變化或資料增強策略。

  • 測試模型:

    最終,在獨立的測試資料集上評估模型的效能,以獲得最終效能評估。

  • 部署模型:

    將訓練好的模型部署到實際應用中,用於實時或批處理多分類任務。

多分類問題

之前我們討論的問題都是二分類居多,對於二分類問題,我們若求得p(0),南無p(1)=1-p(0),還是比較容易的,但是本節我們將引入多分類,那麼我們所求得就轉化為p(i)(i=1,2,3,4…),同時我們需要滿足以上機率中每一個都大於0;且總和為1。

處理多分類問題,這裡我們新引入了一個稱為Softmax Layer

cke_138.png

接下來我們一起討論一下Softmax Layer層

cke_14911.png

首先我們計算指數計算e的zi次冪,原因很簡單e的指數函式恆大於0;分母就是e的z1次冪+e的z2次冪+e的z3次冪…求和,這樣所有的機率和就為1了。

下圖形象的展示了Softmax,Exponent這裡指指數,和上面我們說的一樣,先求指數,這樣有了分子,再將所有指數求和,最後一一divide,得到了每一個機率。

cke_140.png

接下來我們一起來看看損失函式

cke_141.png

如果使用numpy進行實現,根據劉二大人的程式碼,可以進行如下的實現

import numpy as np

y = np.array([1,0,0])

z = np.array([0.2,0.1,-0.1])

y_pred = np.exp(z)/np.exp(z).sum()

loss = (-y * np.log(y_pred)).sum()

print(loss)

執行結果如下

cke_142.png

注意:神經網路的最後一層不需要啟用

在pytorch中

import torch

y = torch.LongTensor([0]) # 長整型

z = torch.Tensor([[0.2, 0.1, -0.1]])

criterion = torch.nn.CrossEntropyLoss()

loss = criterion(z, y)

print(loss)

執行結果如下

cke_143.png

下面根據一個例子進行演示

criterion = torch.nn.CrossEntropyLoss()

Y = torch.LongTensor([2,0,1])

Y_pred1 = torch.Tensor([[0.1, 0.2, 0.9],

[1.1, 0.1, 0.2],

[0.2, 2.1, 0.1]])

Y_pred2 = torch.Tensor([[0.8, 0.2, 0.3],

[0.2, 0.3, 0.5],

[0.2, 0.2, 0.5]])

l1 = criterion(Y_pred1, Y)

l2 = criterion(Y_pred2, Y)

print("Batch Loss1 = ", l1.data, "\nBatch Loss2=", l2.data)

執行結果如下

cke_144.png

根據上面的程式碼可以看出第一個損失比第二個損失要小。原因很簡單,想對於Y_pred1每一個預測的分類與Y是一致的,而Y_pred2則相差了一下,所以損失自然就大了些

MNIST dataset的實現

首先第一步還是導包

import torch

from torchvision import transforms

from torchvision import datasets

from torch.utils.data import DataLoader

import torch.nn.functional as F

import torch.optim as optim

之後是資料的準備

batch_size = 64

# transform可以將其轉化為0-1,形狀的轉換從28×28轉換為,1×28×28

transform = transforms.Compose([

transforms.ToTensor(),

transforms.Normalize((0.1307, ), (0.3081, )) # 均值mean和標準差std

])

train_dataset = datasets.MNIST(root='../dataset/mnist/',

train=True,

download=True,

transform=transform)

train_loader = DataLoader(train_dataset,

shuffle=True,

batch_size=batch_size)

test_dataset = datasets.MNIST(root='../dataset/mnist/',

train=False,

download=True,

transform=transform)

test_loader = DataLoader(test_dataset,

shuffle=False,

batch_size=batch_size)

cke_145.png

cke_29794.png

接下來我們構建網路

class Net(torch.nn.Module):

def __init__(self):

super(Net, self).__init__()

self.l1 = torch.nn.Linear(784, 512)

self.l2 = torch.nn.Linear(512, 256)

self.l3 = torch.nn.Linear(256, 128)

self.l4 = torch.nn.Linear(128, 64)

self.l5 = torch.nn.Linear(64, 10)

def forward(self, x):

x = x.view(-1, 784)

x = F.relu(self.l1(x))

x = F.relu(self.l2(x))

x = F.relu(self.l3(x))

x = F.relu(self.l4(x))

return self.l5(x) # 注意最後一層不做啟用

model = Net()

cke_147.png

之後定義損失和最佳化器

criterion = torch.nn.CrossEntropyLoss()

optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)

接下來就進行訓練了

def train(epoch):

running_loss = 0.0

for batch_idx, data in enumerate(train_loader, 0):

inputs, target = data

optimizer.zero_grad()

# forward + backward + update

outputs = model(inputs)

loss = criterion(outputs, target)

loss.backward()

optimizer.step()

running_loss += loss.item()

if batch_idx % 300 == 299:

print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))

running_loss = 0.0

def test():

correct = 0

total = 0

with torch.no_grad(): # 這裡可以防止內嵌程式碼不會執行梯度

for data in test_loader:

images, labels = data

outputs = model(images)

_, predicted = torch.max(outputs.data, dim=1)

total += labels.size(0)

correct += (predicted == labels).sum().item()

print('Accuracy on test set: %d %%' % (100 * correct / total))

最後呼叫執行

if __name__ == '__main__':

for epoch in range(10):

train(epoch)

test()

NLLLoss 和 CrossEntropyLoss

NLLLoss 和 CrossEntropyLoss(也稱為交叉熵損失)是深度學習中常用的兩種損失函式,用於測量模型的輸出與真實標籤之間的差距,通常用於分類任務。它們有一些相似之處,但也有一些不同之處。

相同點:

用途:兩者都用於分類任務,評估模型的輸出和真實標籤之間的差異,以便進行模型的訓練和最佳化。

數學基礎:NLLLoss 和 CrossEntropyLoss 本質上都是交叉熵損失的不同變種,它們都以資訊理論的概念為基礎,衡量兩個機率分佈之間的相似度。

輸入格式:它們通常期望模型的輸出是一個機率分佈,表示各個類別的預測機率,以及真實的標籤。

不同點:

輸入格式:NLLLoss 通常期望輸入是對數機率(log probabilities),而 CrossEntropyLoss 通常期望輸入是未經對數化的機率。在實際應用中,CrossEntropyLoss 通常與softmax操作結合使用,將原始模型輸出轉化為機率分佈,而NLLLoss可以直接使用對數機率。

對數化:NLLLoss 要求將模型輸出的機率經過對數化(取對數)以獲得對數機率,然後與真實標籤的離散機率分佈進行比較。CrossEntropyLoss 通常在 softmax 操作之後直接使用未對數化的機率值與真實標籤比較。

輸出維度:NLLLoss 更通用,可以用於多種情況,包括多類別分類和序列生成等任務,因此需要更多的靈活性。CrossEntropyLoss 通常用於多類別分類任務。

總之,NLLLoss 和 CrossEntropyLoss 都用於分類任務,但它們在輸入格式和使用上存在一些差異。通常,選擇哪個損失函式取決於你的模型輸出的格式以及任務的性質。如果你的模型輸出已經是對數機率形式,通常使用NLLLoss,否則通常使用CrossEntropyLoss。

 

點選關注,第一時間瞭解華為雲新鮮技術~

 

相關文章