深度学习实践5:Lenet图片分类实践(含BN操作)

思路:

        这段代码是使用PyTorch实现的LeNet-5卷积神经网络,用于处理Fashion-MNIST数据集。

代码流程:

import torch
import torchvision
from torchvision import transforms
from torch.utils import data
from torch import nn
import matplotlib.pyplot as plt
from batch_normalization import BatchNorm, batch_norm

这些是导入所需的库。torch是PyTorch的主要库,torchvision提供了处理视觉数据集的功能,transforms提供了数据预处理的工具,data提供了数据加载和处理的类,nn包含了神经网络的模块和函数,matplotlib.pyplot用于绘图,BatchNormbatch_norm是自定义的批归一化类。批归一化函数具体可查看:batch-norm slides (d2l.ai)

class Accumulator:
    """在`n`个变量上累加。"""

    def __init__(self, n):
        self.data = [0.0] * n

    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

这是一个累加器类,用于在n个变量上进行累加操作。它有add方法用于将输入的参数累加到相应的变量上,reset方法用于将变量重置为0,__getitem__方法用于获取指定索引的变量值。

def load_data_fashion_mnist(batch_size, resize=None):
    """下载Fashion-MNIST数据集,然后将其加载到内存中。"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))  # 对图片进行扩充
    trans = transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root="../data",  # 下载数据集中的训练集
                                                    train=True,
                                                    transform=trans,
                                                    download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="../data",  # 下载数据集中的测试集
                                                   train=False,
                                                   transform=trans,
                                                   download=True)
    return (data.DataLoader(mnist_train, batch_size, shuffle=True, ),
            data.DataLoader(mnist_test, batch_size, shuffle=False, ))

这个函数用于下载和加载Fashion-MNIST数据集。它接受batch_size参数用于指定每个批次的样本数量,resize参数用于指定是否对图像进行大小调整。通过使用transforms模块中的函数,可以将数据集转换为张量形式,并进行可选的大小调整。最后,函数返回两个数据加载器,分别用于训练集和测试集。

def accuracy(y_hat, y):
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum())

这个函数用于计算模型的预测准确率。它接受模型的预测值y_hat和真实标签y作为输入。如果y_hat的形状大于1维且第二维的大小大于1,则使用argmax方法获取预测标签。然后,将预测标签与真实标签进行比较,计算准确预测的数量,并返回准确率。

def evaluate_accuracy_gpu(net, data_iter, device=None):
    """使用GPU计算模型在数据集上的精度。"""
    if isinstance(net, torch.nn.Module):
        net.eval()
        if not device:
            device = next(iter(net.parameters())).device
    metric = Accumulator(2)
    for X, y in data_iter:
        if isinstance(X, list):
            X = [x.to(device) for x in X]
        else:
            X = X.to(device)
        y = y.to(device)
        metric.add(accuracy(net(X), y), y.numel())
    return metric[0] / metric[1]

这个函数用于在GPU上计算模型在给定数据集上的准确率。它接受模型(net)、数据迭代器(data_iter)和设备(device)作为输入。首先,将模型设置为评估模式,并将其移动到指定的设备上。然后,使用累加器(metric)来累加准确预测的数量和总样本数量。最后,返回准确率。

class Reshape(torch.nn.Module):
    def forward(self, x):
        return x.view(-1, 1, 28, 28)

这是一个自定义的PyTorch模块,用于将输入数据的形状调整为指定的形状。它将输入数据的形状调整为(-1, 1, 28, 28),其中-1表示自动计算该维度的大小。

def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
    def init_weights(m):
        if type(m) == nn.Linear or type(m) == nn.Conv2d:
            nn.init.xavier_uniform_(m.weight)

    net.apply(init_weights)
    print('training on', device)
    net.to(device)

    # 创建一个空的图形对象和子图对象
    fig, ax = plt.subplots()

    # 设置 x 轴标签和范围
    ax.set_xlabel('epoch')
    ax.set_xlim([1, num_epochs])

    # 设置图例
    ax.legend(['train loss', 'train acc', 'test acc'])

    # 创建空列表来保存训练损失、训练准确率和测试准确率
    train_loss_list = []
    train_acc_list = []
    test_acc_list = []

    optimizer = torch.optim.SGD(net.parameters(), lr=lr)
    loss = nn.CrossEntropyLoss()
    # 在每个 epoch 结束后更新图形
    for epoch in range(num_epochs):
        metric = Accumulator(3)
        net.train()
        for i, (X, y) in enumerate(train_iter):
            optimizer.zero_grad()
            X, y = X.to(device), y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            l.backward()
            optimizer.step()
            with torch.no_grad():
                metric.add(l * X.shape[0], accuracy(y_hat, y), X.shape[0])
            train_l = metric[0] / metric[2]
            train_acc = metric[1] / metric[2]

        test_acc = evaluate_accuracy_gpu(net, test_iter)

        # 将训练损失、训练准确率和测试准确率添加到列表中
        train_loss_list.append(train_l)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)

        # 清除当前图形
        ax.clear()

        # 绘制训练损失、训练准确率和测试准确率曲线
        ax.plot(range(1, epoch + 2), train_loss_list, label='train loss')
        ax.plot(range(1, epoch + 2), train_acc_list, label='train acc')
        ax.plot(range(1, epoch + 2), test_acc_list, label='test acc')

        # 设置 x 轴标签和范围
        ax.set_xlabel('epoch')
        ax.set_xlim([1, num_epochs])

        # 设置图例
        ax.legend(['train loss', 'train acc', 'test acc'])

        # 更新图形显示
        plt.pause(0.1)

    # 显示最终的图形
    plt.show()

这是一个用于训练模型的函数。它接受模型(`net`)、训练数据迭代器(`train_iter`)、测试数据迭代器(`test_iter`)、训练的轮数(`num_epochs`)、学习率(`lr`)和设备(`device`)作为输入。

首先,使用`init_weights`函数初始化模型的参数,并将模型移动到指定的设备上。然后,创建一个图形对象和子图对象,并设置图形的标签和范围。

接下来,创建空列表来保存训练损失、训练准确率和测试准确率。使用`torch.optim.SGD`定义优化器和`nn.CrossEntropyLoss`定义损失函数。

在每个epoch中,使用累加器(`metric`)来累加训练损失、训练准确率和训练样本数量。然后,计算并更新梯度,使用优化器进行参数更新。

在每个epoch结束后,使用`evaluate_accuracy_gpu`函数计算测试准确率,并将训练损失、训练准确率和测试准确率添加到相应的列表中。

最后,绘制训练损失、训练准确率和测试准确率的曲线,并返回训练过程中的损失和准确率列表。 

Lenet = nn.Sequential(Reshape(), nn.Conv2d(1, 6, kernel_size=5), BatchNorm(6, num_dims=4),
                      nn.Sigmoid(), nn.MaxPool2d(kernel_size=2, stride=2),
                      nn.Conv2d(6, 16,kernel_size=5), BatchNorm(16, num_dims=4),
                      nn.Sigmoid(), nn.MaxPool2d(kernel_size=2, stride=2),
                      nn.Flatten(), nn.Linear(16 * 4 * 4, 120),
                      BatchNorm(120, num_dims=2), nn.Sigmoid(),
                      nn.Linear(120, 84), BatchNorm(84, num_dims=2),
                      nn.Sigmoid(), nn.Linear(84, 10))

然后,我们构建Lenet 卷积神经网络,并加入了批归一化操作。

batch_size = 256
train_iter, test_iter = load_data_fashion_mnist(batch_size)

lr, num_epochs = 1.0, 10
device = torch.device('cuda')
train_ch6(Lenet, train_iter, test_iter, num_epochs, lr, device)

输入超参数,并运行模型进行训练。

代码结果:

        1.批归一化后的结果

        2.原始结果

        

可以看出,加入批归一化后,训练集和验证集的准确率均有部分提升,但也引发了过拟合的问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值