使用 PyTorch 构建和训练一个卷积神经网络进行图像分类任务

下面是一个稍微复杂一些的神经网络示例代码。这个例子实现了一个卷积神经网络(CNN),用于处理图像分类任务(例如MNIST手写数字识别)。该网络包含卷积层、池化层、全连接层以及使用了ReLU激活函数和批量归一化层。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

# 定义卷积神经网络
class ConvNet(nn.Module):
    def __init__(self):
        super(ConvNet, self).__init__()
        # 第一卷积层:输入通道数为1(灰度图像),输出通道数为32,卷积核大小为3x3,步长为1,填充为1
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)
        # 第一批量归一化层:归一化32个特征图
        self.bn1 = nn.BatchNorm2d(32)
        # 第二卷积层:输入通道数为32,输出通道数为64,卷积核大小为3x3,步长为1,填充为1
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
        # 第二批量归一化层:归一化64个特征图
        self.bn2 = nn.BatchNorm2d(64)
        # 最大池化层:池化窗口大小为2x2,步长为2
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
        # 全连接层:输入大小为64*7*7,输出大小为128
        self.fc1 = nn.Linear(64 * 7 * 7, 128)
        # 全连接层:输入大小为128,输出大小为10(对应10个分类)
        self.fc2 = nn.Linear(128, 10)
        # Dropout层:在训练过程中随机断开50%的神经元连接,防止过拟合
        self.dropout = nn.Dropout(p=0.5)

    def forward(self, x):
        # 第一个卷积层,激活函数为ReLU,然后进行最大池化
        x = self.pool(F.relu(self.bn1(self.conv1(x))))
        # 第二个卷积层,激活函数为ReLU,然后进行最大池化
        x = self.pool(F.relu(self.bn2(self.conv2(x))))
        # 将特征图展平成一维向量
        x = x.view(-1, 64 * 7 * 7)
        # 第一个全连接层,激活函数为ReLU
        x = F.relu(self.fc1(x))
        # Dropout层
        x = self.dropout(x)
        # 第二个全连接层,输出未经过激活函数
        x = self.fc2(x)
        return x

# 数据预处理和加载
# 使用Compose将多个变换组合在一起:ToTensor()将图像转换为张量,Normalize()进行标准化
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

# 下载并加载训练集
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
# 下载并加载测试集
test_dataset = datasets.MNIST('./data', train=False, transform=transform)

# 使用DataLoader加载数据集
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)

# 初始化网络、损失函数和优化器
model = ConvNet()
criterion = nn.CrossEntropyLoss()  # 交叉熵损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001)  # Adam优化器,学习率为0.001

# 训练网络的函数
def train(model, device, train_loader, optimizer, criterion, epoch):
    model.train()  # 设置模型为训练模式
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)  # 将数据和标签移动到设备上(GPU或CPU)
        optimizer.zero_grad()  # 清空梯度
        output = model(data)  # 前向传播
        loss = criterion(output, target)  # 计算损失
        loss.backward()  # 反向传播
        optimizer.step()  # 更新参数
        if batch_idx % 100 == 0:  # 每100个批次打印一次训练信息
            print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} '
                  f'({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}')

# 测试网络的函数
def test(model, device, test_loader, criterion):
    model.eval()  # 设置模型为评估模式
    test_loss = 0
    correct = 0
    with torch.no_grad():  # 不计算梯度
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)  # 将数据和标签移动到设备上
            output = model(data)  # 前向传播
            test_loss += criterion(output, target).item()  # 累加批量损失
            pred = output.argmax(dim=1, keepdim=True)  # 获取最大概率的索引
            correct += pred.eq(target.view_as(pred)).sum().item()  # 统计正确预测的数量

    test_loss /= len(test_loader.dataset)  # 计算平均损失
    print(f'\nTest set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} '
          f'({100. * correct / len(test_loader.dataset):.0f}%)\n')


# 定义一个函数来显示图像及其预测标签和真实标签
def show_images(images, labels, preds):
    plt.figure(figsize=(10, 5))
    for i in range(10):
        plt.subplot(2, 5, i + 1)
        plt.imshow(images[i].numpy().squeeze(), cmap='gray')
        plt.title(f"True: {labels[i].item()}\nPred: {preds[i].item()}")
        plt.axis('off')
    plt.show()


# 获取一些测试样本并显示
def show_test_samples(model, device, test_loader):
    model.eval()
    data, target = next(iter(test_loader))
    data, target = data.to(device), target.to(device)
    output = model(data)
    preds = output.argmax(dim=1, keepdim=True)

    # 将图像从GPU移回CPU并转换为numpy格式
    images = data.cpu()
    labels = target.cpu()
    preds = preds.cpu()

    # 显示图像及其标签
    show_images(images, labels, preds)

# 训练和测试循环
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 选择设备
model.to(device)  # 将模型移动到设备上
for epoch in range(1, 11):  # 进行10个训练周期
    train(model, device, train_loader, optimizer, criterion, epoch)  # 训练模型
    test(model, device, test_loader, criterion)  # 测试模型

# 在训练和测试循环后调用这个函数来显示一些测试图像
show_test_samples(model, device, test_loader)
View Code

解释

  • 卷积神经网络(ConvNet):定义了一个包含两个卷积层、两个批量归一化层、两个池化层、两个全连接层和一个Dropout层的卷积神经网络。
  • 数据预处理和加载:使用 transforms 对数据进行标准化,并加载 MNIST 数据集。
  • 训练和测试函数:定义了训练和测试模型的函数,其中包含前向传播、反向传播和参数更新的过程。
  • 训练和测试循环:选择设备(GPU 或 CPU),将模型移动到设备,进行10个训练周期,每个周期结束后进行一次测试。

运行结果如下:

 

posted @ 2024-06-27 13:46  阿坦  阅读(375)  评论(0编辑  收藏  举报