使用PyTorch演示​​实现神经网络过程

借助著名的开源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 <strong>init</strong>(self):
        super(Net, self).<strong>init</strong>()
        # 网络有两个完全连接的层
        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("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 这个强大且适应性强的框架来创建和测试不同的神经网络模型。