使用PyTorch演示​​實現神經網路過程

banq發表於2024-03-09

藉助著名的開源PyTorch 框架,可以使用Python建立和訓練神經網路。本教程將教您如何使用 PyTorch 建立基本神經網路並對 MNIST 資料集中的手寫數字進行分類。

現代人工智慧依賴於神經網路,神經網路賦予機器類似於人類的學習和判斷能力。迴歸、分類和建立只是神經網路(作為計算機模型)在從輸入中學習後可能執行的一些任務。流行的開源 PyTorch 框架可用於用 Python 設計和訓練神經網路。在本教程中,您將學習如何使用 PyTorch 使用基本神經網路對 MNIST 資料集中的手寫數字進行分類。

如何在 PyTorch 中建立神經網路?
透過 nn.Module 類或 nn.Sequential 容器,PyTorch 提供了兩種構建神經網路的主要方法。如果您繼承 nn.Module 類並實現 __init__ 和轉發函式,您可以構建自己獨特的網路。前向函式指定如何透過級別傳輸輸入並作為輸出返回,而 __init__ 方法則建立網路的層和引數。

如果您提供層列表作為引數,則 nn.Sequential 容器可讓您建立網路。指定順序後,圖層會自動連線。

PyTorch 提供的幾個模組和方法使 Python 中的神經網路實現變得簡單:

  • 引入(匯入)所有必需的模組,包括 torch、torch.nn 和 torch.optim。
  • 描述資料,包括目標標籤和輸入特徵集。您可以構建自己的張量或利用 PyTorch 中的內建資料集。
  • 描述神經網路的架構,包括層數和型別、啟用函式和輸出大小。您可以子類 torch.nn.Module 來構造自己獨特的層,也可以利用 PyTorch 預設層,例如 torch.nn.Linear、torch.nn.Conv2d或 torch.nn.LSTM。
  • 指定損失函式(torch.nn.MSELoss、torch.nn.CrossEntropyLoss、torch.nn.BCELoss等)。網路輸出與目標的相似程度是透過損失函式來衡量的。
  • 指定最佳化器(torch.optim.SGD、torch.optim.Adam或 torch.optim.RMSprop)。利用梯度和學習率——最佳化器修改網路的權重。
  • 為了訓練網路,執行前向和後向傳遞,並對資料應用迴圈最佳化器。透過釋出損失或其他指標(例如準確性或精確度),您可以密切關注訓練的進展情況。
  • 使用新資料(例如驗證集或測試集)測試網路,以評估其效能。此外,torch.save 和 torch.load 允許您載入和儲存網路狀態。

為 MNIST 實現前饋神經網路
為了更好地理解,讓我們看看如何在 PyTorch 中建立神經網路。請注意,這些只是簡短的示例,您可以擴充套件和更改以滿足您的需求;它們不是全面的解決方案。在此示例中,使用簡單的前饋神經網路對 MNIST 資料集中的手寫數字進行分類。

  • 我們在本例中定義了一個具有兩個完全連線層的簡單前饋神經網路。當權重矩陣和偏置向量用於連結每個輸入和輸出單元時,該層被稱為完全連結。
  • 第一層在接收扁平化圖片(28×28畫素)作為輸入後產生512個特徵。第二層使用 512 個特徵作為輸入生成 10 個類別,即數字 0 到 9。
  • 為了生成完全連結的層並將它們提供為網路物件特徵,我們利用 nn.Linear 類。為了賦予網路一些非線性並幫助其學習複雜模式的能力。我們還使用 F.relu 函式將ReLU啟用函式應用於第一層。
  • 輸入影像僅在前向方法中被展平,然後應用第一層 ReLU 函式和第二層。每個類別的 10 個 logits 的張量是網路輸出。

第1步:匯入必要的庫

# Import the necessary libraries
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import numpy as np

第 2 步:定義超引數和變換
所提供的程式碼定義了超引數和轉換,以便在機器學習中應用於影像。超引數(包括 batch_size、num_epochs 和 learning_rate)被初始化,以控制訓練過程。此外,還定義了一個轉換管道 transform,用於預處理輸入影像。該管道採用了兩種連續變換:

  • transforms.ToTensor() 將影像轉換為 PyTorch 張量,這是神經網路計算所需的格式;
  • transforms.Normalize() 則透過減去平均值(0.1307)併除以標準偏差(0.3081)使畫素值標準化。

# Define the hyperparameters
batch_size = 64 # The number of samples per batch
num_epochs = 10 # The number of times to iterate over the whole dataset
learning_rate = 0.01 # The learning rate for the optimizer

# Define the transformation to apply to the images
transform = transforms.Compose([
    transforms.ToTensor(), # 將影像轉換為張量
    transforms.Normalize((0.1307,), (0.3081,)) # 用平均值和 std 對畫素值進行歸一化處理
])

步驟 3:載入並準備資料集
所提供的程式碼會從網上載入 MNIST 資料集,該資料集由手寫數字影像及其相應標籤組成。它初始化了兩個資料集:用於訓練資料的 train_dataset 和用於測試資料的 test_dataset。

這兩個資料集都配置了之前定義的轉換,實現了影像張量轉換和畫素值歸一化。

隨後,建立資料載入器 train_loader 和 test_loader,以便在訓練和測試階段分別對資料進行批處理和洗牌。

# Load the MNIST dataset from the web
train_dataset = datasets.MNIST(root='.', train=True, download=True, transform=transform) # The training set
test_dataset = datasets.MNIST(root='.', train=False, download=True, transform=transform) # The test set

# 建立資料載入器,用於批處理和洗牌資料
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True) # The training loader
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False) # The test loader


步驟 4:定義神經網路模型
我們使用 PyTorch 的 nn.Module 定義一個簡單的神經網路類 Net。該網路由兩個全連線層(fc1 和 fc2)組成。下面是程式碼的詳細說明:

__init__(self):這是定義網路架構的構造方法。它使用 nn.Linear 初始化兩個全連線層。第一層(fc1)接收大小為 28*28 的輸入(假設輸入影像為 28×28 畫素,並平鋪成一個向量),並輸出 512 個特徵。第二層(fc2)將第一層的 512 個特徵作為輸入,並輸出 10 個類別(假設這是一個有 10 個類別的分類任務)。

forward(self, x):該方法定義網路的前向傳遞。它接收輸入張量 x(代表一批影像)並執行以下操作:

  • 使用 x.view(-1, 28*28) 將輸入張量扁平化為一個向量。
  • 使用 F.relu(self.fc1(x))將扁平化後的輸入透過第一個全連線層 (fc1) 並應用 ReLU 啟用函式。
  • 將第一層的輸出透過第二層全連線層 (fc2) 得到最終輸出 logits。

# Define the neural network model
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # 網路有兩個完全連線的層
        self.fc1 = nn.Linear(28*28, 512) # 第一層將扁平化影像作為輸入,輸出 512 個特徵點
        self.fc2 = nn.Linear(512, 10) # 第二層將 512 個特徵作為輸入,輸出 10 個類別

    def forward(self, x):
        # The forward pass of the network
        x = x.view(-1, 28*28) # Flatten the image into a vector
        x = F.relu(self.fc1(x)) # Apply the ReLU activation function to the first layer
        x = self.fc2(x) # Apply the second layer
        return x # Return the output logits


第 5 步:定義損失函式、最佳化器和模型例項
提供的程式碼段會初始化神經網路模型,將其移動到可用裝置(CPU 或 GPU)上,並定義損失函式和最佳化器。

# 建立模型例項並將其移動到裝置(CPU 或 GPU)
device = torch.device(<font>"cuda" if torch.cuda.is_available() else "cpu") # Get the device
model = Net().to(device) # Move the model to the device
print(model) # Print the model summary

# Define the loss function and the optimizer
criterion = nn.CrossEntropyLoss() # The cross entropy loss for multi-class classification
optimizer = optim.SGD(model.parameters(), lr=learning_rate) # The stochastic gradient descent optimizer

# 定義一個計算模型精度的函式
def accuracy(outputs, labels):
    # The accuracy is the percentage of correct predictions
    _, preds = torch.max(outputs, 1) # 從輸出日誌中獲取預測類別
    return torch.sum(preds == labels).item() / len(labels) # 返回正確預測的比率

輸出:

Net(
  (fc1): Linear(in_features=784, out_features=512, bias=True)
  (fc2): Linear(in_features=512, out_features=10, bias=True)
)

第 6 步:定義訓練和測試迴圈

  • train(model,device,train_loader,criterion,optimizer,epoch):該函式使用訓練資料訓練模型。它將模型設定為訓練模式,迴圈處理來自 train_loader 的批次資料,將輸入和標籤移動到指定的裝置,對模型執行前向傳遞以獲得輸出對數,使用指定的準則計算損失,執行後向傳遞以計算梯度,並使用指定的最佳化器更新模型引數。它還會列印各批次的平均損失和準確率。
  • test(model, device, test_loader, criterion):該函式使用測試資料對模型進行評估。它將模型設定為評估模式,迴圈處理來自 test_loader 的批次資料,將輸入和標籤移動到指定的裝置,對模型執行前向傳遞以獲取輸出對數,使用指定的準則計算損耗,並列印批次的平均損耗和準確度。

# Define the training loop
def train(model, device, train_loader, criterion, optimizer, epoch):
    # 將模型設定為訓練模式
    model.train()
    # Initialize the running loss and accuracy
    running_loss = 0.0
    running_acc = 0.0
    # 迴圈瀏覽批次資料
    for i, (inputs, labels) in enumerate(train_loader):
        將輸入和標籤移到裝置上
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 引數梯度歸零
        optimizer.zero_grad()
        # Forward pass
        outputs = model(inputs) # 獲取模型的輸出對數
        loss = criterion(outputs, labels) # Calculate the loss
        # 後向傳遞和最佳化
        loss.backward() # 計算梯度
        optimizer.step() 更新引數
        # Print the statistics
        running_loss += loss.item() # Accumulate the loss累計損失
        running_acc += accuracy(outputs, labels) # Accumulate the accuracy
        if (i + 1) % 200 == 0: # Print every 200 batches
            print(f'Epoch {epoch}, Batch {i + 1}, Loss: {running_loss / 200:.4f}, Accuracy: {running_acc / 200:.4f}')
            running_loss = 0.0
            running_acc = 0.0

# Define the test loop
def test(model, device, test_loader, criterion):
    # 將模型設定為評估模式
    model.eval()
    # Initialize the loss and accuracy
    test_loss = 0.0
    test_acc = 0.0
    # Loop over the batches of data
    with torch.no_grad(): # No need to track the gradients
        for inputs, labels in test_loader:
            # Move the inputs and labels to the device
            inputs = inputs.to(device)
            labels = labels.to(device)
            # Forward pass
            outputs = model(inputs) # Get the output logits from the model
            loss = criterion(outputs, labels) # Calculate the loss
            # Print the statistics
            test_loss += loss.item() # Accumulate the loss
            test_acc += accuracy(outputs, labels) # Accumulate the accuracy
    # Print the average loss and accuracy
    print(f'Test Loss: {test_loss / len(test_loader):.4f}, Test Accuracy: {test_acc / len(test_loader):.4f}')

步驟 7:訓練和測試模型,同時視覺化一些樣本影像和預測結果
該程式碼段將對模型進行指定次數的訓練和測試,然後將一些樣本影像及其預測結果視覺化。

# Train and test the model for the specified number of epochs
for epoch in range(1, num_epochs + 1):
    train(model, device, train_loader, criterion, optimizer, epoch) # Train the model
    test(model, device, test_loader, criterion) # Test the model

視覺化一些示例影像和預測
samples, labels = next(iter(test_loader)) # Get a batch of test data
samples = samples.to(device) # Move the samples to the device
outputs = model(samples) 獲取模型的輸出對數
_, preds = torch.max(outputs, 1) # 從輸出日誌中獲取預測類別
samples = samples.cpu().numpy() # 將樣本移回 CPU 並轉換為 numpy 陣列
fig, axes = plt.subplots(3, 3, figsize=(8, 8)) # 建立 3x3 網格的子繪圖
for i, ax in enumerate(axes.ravel()):
    ax.imshow(samples[i].squeeze(), cmap='gray') # Plot the image
    ax.set_title(f'Label: {labels[i]}, Prediction: {preds[i]}') # Set the title
    ax.axis('off') # Hide the axes
plt.tight_layout() # Adjust the spacing調整間距
plt.show() # Show the plot


輸出:

Epoch 1, Batch 200, Loss: 1.1144, Accuracy: 0.7486
Epoch 1, Batch 400, Loss: 0.4952, Accuracy: 0.8739
Epoch 1, Batch 600, Loss: 0.3917, Accuracy: 0.8903
Epoch 1, Batch 800, Loss: 0.3515, Accuracy: 0.9042
Test Loss: 0.3018, Test Accuracy: 0.9155
Epoch 2, Batch 200, Loss: 0.3067, Accuracy: 0.9123
Epoch 2, Batch 400, Loss: 0.2929, Accuracy: 0.9168
Epoch 2, Batch 600, Loss: 0.2878, Accuracy: 0.9185
Epoch 2, Batch 800, Loss: 0.2735, Accuracy: 0.9210
Test Loss: 0.2471, Test Accuracy: 0.9314
Epoch 3, Batch 200, Loss: 0.2580, Accuracy: 0.9256
Epoch 3, Batch 400, Loss: 0.2442, Accuracy: 0.9301
Epoch 3, Batch 600, Loss: 0.2354, Accuracy: 0.9338
Epoch 3, Batch 800, Loss: 0.2281, Accuracy: 0.9359
Test Loss: 0.2130, Test Accuracy: 0.9403
Epoch 4, Batch 200, Loss: 0.2149, Accuracy: 0.9403
Epoch 4, Batch 400, Loss: 0.2055, Accuracy: 0.9441
Epoch 4, Batch 600, Loss: 0.2050, Accuracy: 0.9395
Epoch 4, Batch 800, Loss: 0.2018, Accuracy: 0.9425
Test Loss: 0.1860, Test Accuracy: 0.9465
Epoch 5, Batch 200, Loss: 0.1925, Accuracy: 0.9464
Epoch 5, Batch 400, Loss: 0.1850, Accuracy: 0.9473
Epoch 5, Batch 600, Loss: 0.1813, Accuracy: 0.9481
Epoch 5, Batch 800, Loss: 0.1753, Accuracy: 0.9503
Test Loss: 0.1691, Test Accuracy: 0.9517
Epoch 6, Batch 200, Loss: 0.1719, Accuracy: 0.9521
Epoch 6, Batch 400, Loss: 0.1599, Accuracy: 0.9557
Epoch 6, Batch 600, Loss: 0.1627, Accuracy: 0.9521
Epoch 6, Batch 800, Loss: 0.1567, Accuracy: 0.9562
Test Loss: 0.1549, Test Accuracy: 0.9547
Epoch 7, Batch 200, Loss: 0.1441, Accuracy: 0.9620
Epoch 7, Batch 400, Loss: 0.1474, Accuracy: 0.9587
Epoch 7, Batch 600, Loss: 0.1447, Accuracy: 0.9601
Epoch 7, Batch 800, Loss: 0.1426, Accuracy: 0.9580
Test Loss: 0.1404, Test Accuracy: 0.9602
Epoch 8, Batch 200, Loss: 0.1360, Accuracy: 0.9627
Epoch 8, Batch 400, Loss: 0.1359, Accuracy: 0.9620
Epoch 8, Batch 600, Loss: 0.1304, Accuracy: 0.9631
Epoch 8, Batch 800, Loss: 0.1322, Accuracy: 0.9634
Test Loss: 0.1308, Test Accuracy: 0.9624
Epoch 9, Batch 200, Loss: 0.1152, Accuracy: 0.9690
Epoch 9, Batch 400, Loss: 0.1188, Accuracy: 0.9674
Epoch 9, Batch 600, Loss: 0.1303, Accuracy: 0.9637
Epoch 9, Batch 800, Loss: 0.1236, Accuracy: 0.9645
Test Loss: 0.1234, Test Accuracy: 0.9633
Epoch 10, Batch 200, Loss: 0.1112, Accuracy: 0.9679
Epoch 10, Batch 400, Loss: 0.1120, Accuracy: 0.9707
Epoch 10, Batch 600, Loss: 0.1158, Accuracy: 0.9681
Epoch 10, Batch 800, Loss: 0.1138, Accuracy: 0.9688
Test Loss: 0.1145, Test Accuracy: 0.9665

結論
這篇文章教我們如何使用 PyTorch 構建的基本神經網路識別 MNIST 資料集中的手寫數字。我們還學習瞭如何使用 nn.Module 類、nn.Sequential 容器、損失函式、最佳化器和資料載入器在 PyTorch 中構建、訓練和測試神經網路。你可以使用 PyTorch 這個強大且適應性強的框架來建立和測試不同的神經網路模型。
 

相關文章