本文分享自華為雲社群《使用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
接下來我們一起討論一下Softmax Layer層
首先我們計算指數計算e的zi次冪,原因很簡單e的指數函式恆大於0;分母就是e的z1次冪+e的z2次冪+e的z3次冪…求和,這樣所有的機率和就為1了。
下圖形象的展示了Softmax,Exponent這裡指指數,和上面我們說的一樣,先求指數,這樣有了分子,再將所有指數求和,最後一一divide,得到了每一個機率。
接下來我們一起來看看損失函式
如果使用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)
執行結果如下
注意:神經網路的最後一層不需要啟用
在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)
執行結果如下
下面根據一個例子進行演示
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)
執行結果如下
根據上面的程式碼可以看出第一個損失比第二個損失要小。原因很簡單,想對於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)
接下來我們構建網路
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()
之後定義損失和最佳化器
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。