基于卷积神经网络的 Fashionminsit 数据集分类

实验目的

        1. 理解卷积神经网络(CNN)。

        2. 掌握数据预处理和增强技术:学习如何通过数据增强技术(如旋转、缩放、剪切等)来增加模型的泛化能力,减少过拟合。

        3. 应用正则化技术:通过实验,掌握 Dropout、L2 正则化等技术在卷积神经网络中的应用,以降低模型的过拟合风险。

        4. 优化模型性能:通过调整网络结构和超参数(如卷积层数量、卷积核大小、池化层、激活函数等),优化模型的准确率。

        5. 理解过拟合现象:通过实验,理解过拟合产生的原因,以及如何通过不同的方法来识别和避免过拟合。

        6. 模型调优:学习如何通过调整学习率、批大小、优化器等超参数来提高模型的训练效率和性能。

环境

        硬件:12th Gen Intel(R) Core(TM) i5-12500H   2.50 GHz

        软件:PyCharm Community Edition 2023.2.1

内容与要求

        1.数据加载与预处理: 使用 FashionMNIST 数据集进行分类任务,通过随机水平翻转、旋转、颜色抖动等增强技术,提升数据集的多样性。所有图片进行归一化处理,以加速训练和提升稳定性。

        2.模型构建与优化: 设计一个增强版卷积神经网络(CNN),包括四个卷积层和相应的池化、批量归一化操作,以确保模型能够学习到不同层次的特征。同时,增加全连接层并应用 Dropout 技术,提升模型的泛化能力。

        3.学习率调度与超参数调整: 使用余弦退火学习率调度器进行学习率的动态调整。通过不同的优化器(SGD 和 Adam)和不同的学习率组合,训练并测试模型的性能。

过程与分析

导入模块功能

1.PyTorch库:

        (1)用于构建和训练神经网络。

        (2)导入PyTorch的神经网络模块,用于定义神经网络层和模型。

        (3)导入PyTorch的函数式接口,提供了一些常用的函数,如激活函数。

        (4)导入PyTorch的优化器模块,用于定义优化算法,如SGD、Adam等,用于训练模型。

2.torchvision库:

        (1)常用于计算机视觉任务的库,提供了数据集、预处理工具和模型。

       (2)导入torchvision的transforms模块,用于对图像数据进行预处理,如缩放、裁剪、归一化等。

3.matplotlib库:

        导入matplotlib的pyplot模块,用于绘制图像和图表,常用于数据可视化。

预处理步骤

1.数据预处理和增强

        (1)随机旋转:transforms.RandomRotation(10)对训练数据集中的每张图片进行随机旋转,旋转角度在-10度到10度之间。这有助于模型学习到旋转不变的特征。

        (2)随机裁剪:transforms.RandomResizedCrop(28, scale=(0.8, 1.2))对图片进行随机裁剪并调整大小到28x28像素。scale参数控制裁剪大小相对于原图的比例,这里的比例在0.8到1.2之间,意味着裁剪后的图片大小会在原图的80%到120%之间。

        (3)转换为张量:transforms.ToTensor()将PIL图像或NumPy数组转换为PyTorch张量,并自动将像素值从[0, 255]缩放到[0.0, 1.0]。

        (4)归一化:transforms.Normalize((0.1307,), (0.3081,))对张量进行归一化处理,使用均值0.1307和标准差0.3081。这通常是基于数据集的统计信息,有助于模型训练的稳定性和收敛速度。

2.加载数据集

1)加载数据集

对于训练集

  1. 通过torchvision.datasets.FashionMNIST函数加载 FashionMNIST 数据集的训练部分。这个数据集是一个流行的用于图像分类的数据集,包含各种时尚物品的图像。

  2. root='./data'指定了数据集存储的路径,如果数据集不存在于该路径下,且download=True,那么会自动从网络上下载数据集并保存到指定路径。

  3. transform=transform_train应用了之前定义的针对训练数据的预处理和数据增强操作。这些操作包括随机旋转、随机裁剪并调整大小、转换为张量以及归一化等。这些操作可以增加数据的多样性,提高模型的泛化能力,使其能够更好地适应不同的输入变化。

对于测试集:

  1. 同样使用torchvision.datasets.FashionMNIST加载测试数据部分,train=False明确表示加载的是测试集。

  2. 应用transform_test预处理操作,该操作主要是将图像转换为张量并进行归一化,确保测试数据的处理方式与训练数据一致,以便模型在相同的条件下进行评估。

2)创建数据加载器

对于训练集:

  1. torch.utils.data.DataLoader创建了一个数据加载器对象trainloader。

  2. batch_size=64意味着每次从训练集中取出 64 个样本组成一个批次。这样做的好处是可以利用现代硬件(如 GPU)的并行计算能力,提高训练效率

  3. shuffle=True在每个训练周期(epoch)开始时随机打乱训练数据的顺序。这有助于模型更好地学习数据的分布,避免模型过度依赖特定的数据顺序,从而提高模型的泛化能力。

  4. num_workers=2指定使用两个子进程来加载数据。这可以加快数据加载的速度,特别是在处理大规模数据集时,可以减少数据加载对训练过程的瓶颈影响。

对于测试集:

  1. 类似地,创建了测试集的数据加载器testloader。

  2. batch_size=64保持与训练集一致,方便在测试时进行批量处理。

  3. shuffle=False因为在测试阶段不需要打乱数据顺序,通常是按照固定的顺序依次进行评估。

  4. num_workers=2同样用于提高数据加载效率。

构建和准备图像分类的卷积神经网络模型

1.定义卷积神经网络模型:

__init__方法

        (1)构建了CNN模型的网络结构。

        (2)定义了两个卷积层:conv1和conv2。conv1的输入通道数为1,适用于单通道的灰度图像,输出通道数为16,卷积核大小为3且设置了填充为1,以保持输入输出尺寸的一致性;conv2的输入通道数为16(conv1的输出通道数),输出通道数为32,卷积核大小和填充设置与conv1相同。

        (3)定义了一个最大池化层pool,池化核大小为2且步长为2,用于对卷积层输出的数据进行下采样,减少数据维度的同时提取重要特征。 4)定义了两个全连接层:fc1和fc2。fc1的输入维度是经过卷积和池化操作后得到的特征维度,输出维度为128;fc2的输入维度为128,输出维度为10,对应于分类任务中的类别数。

forward方法

        (1)定义了数据在CNN模型中的前向传播路径。

        (2)首先,输入数据x经过conv1卷积层进行卷积操作,然后通过relu激活函数增加非线性特性,接着经过pool池化层进行下采样。

        (3)之后,数据再经过conv2卷积层进行卷积操作,同样经过relu激活函数和pool池化层。

        (4)经过两次卷积和池化后,数据通过x.view(-1, 32 * 7 * 7)操作将其维度调整为适合全连接层fc1的输入形式。

        (5)然后,数据依次经过fc1全连接层和relu激活函数进行处理,再经过fc2全连接层进行进一步的特征映射。

        (6)最后,经过softmax函数对输出结果进行处理,将输出转换为各个类别上的概率分布,dim=1表示在维度1上进行softmax操作,即针对每个样本的不同类别计算概率,以便用于后续的分类决策。

2.定义损失函数和优化器,这里使用Adam优化器:

        (1)损失函数:使用交叉熵损失函数(nn.CrossEntropyLoss()),这是多分类问题中常用的损失函数。它衡量模型预测的概率分布与真实标签的概率分布之间的差异。

        (2)优化器:使用Adam优化器(optim.Adam()),这是一种自适应学习率的优化算法,它结合了动量和RMSProp的概念。优化器用于在训练过程中调整模型的参数,以最小化损失函数。

        (3)参数设置:优化器的lr=0.001参数设置了学习率,这是每次参数更新时步长的大小。

训练模型

        1.训练循环设置设置了训练的轮数为30轮(可调整),每一轮代表对整个训练数据集的一次完整遍历。

        2.每轮训练的初始化在每一轮开始时,将running_loss(累计损失)初始化为0.0,correct(正确分类的样本数)和total(总样本数)初始化为0,用于统计本轮训练的相关指标。

        3.数据加载和模型训练对于每一批次的数据(通过for i, data in enumerate(trainloader, 0)遍历数据加载器trainloader):

        (1)从数据中分离出输入inputs和标签labels。

        (2)首先将优化器的梯度清零(optimizer.zero_grad()),以避免上一批次的梯度影响当前批次的训练。

        (3)将输入数据传入模型得到输出outputs,然后根据模型输出和真实标签计算损失loss。

        (4)通过loss.backward()进行反向传播,计算模型参数的梯度。

        (5)最后使用optimizer.step()根据计算得到的梯度更新模型参数。

        (6)在每一批次训练过程中,累计损失running_loss加上当前批次的损失值loss.item(),同时统计正确分类的样本数correct和总样本数total。

        4.记录训练指标

        (1)每一轮训练结束后,将本轮的平均损失添加到train_losses列表中,用于记录训练过程中的损失变化情况。

        (2)将本轮在训练集上的准确率添加到train_accuracies列表中,用于评估模型在训练集上的性能提升情况。

结果可视化

1.实验结果:

基于卷积神经网络的 Fashionminsit 数据集分类_第1张图片

图1 在不同学习率和优化器下的loss-acc图像

基于卷积神经网络的 Fashionminsit 数据集分类_第2张图片

图2 混淆矩阵热力图

2.结果分析:

        (1)整体观察可以看到不同的预测标签和真实标签之间的对应关系。对角线上的数值表示正确预测的数量,非对角线元素表示错误预测的情况。

        (2)优化器和学习率的影响对于某些类别(如第一个类别有3个正确预测,类别5有2个错误预测到类别6等),不同的优化器和学习率组合可能会导致不同的预测结果。

        (3)损失曲线 

        SGD和Adam对比:

在学习率为0.001时,Adam优化器的训练损失和测试损失整体上比SGD优化器下降得更快且更平稳。例如,在大约20个epoch后,Adam的训练损失已经接近0.5,而SGD的训练损失还在1.0左右。

当学习率变为0.01时,SGD的训练损失和测试损失曲线波动较大,说明模型可能不稳定。而Adam的损失曲线虽然也有一定波动,但相对较小。

        学习率影响:

对于SGD优化器,学习率从0.001增加到0.01时,损失曲线上升且波动剧烈,说明学习率过大导致模型难以收敛。

对于Adam优化器,学习率从0.001增加到0.01时,损失曲线也有上升趋势,但波动不如SGD大,不过也表明学习率增大对模型性能有负面影响。

        (4)准确率曲线:

        SGD和Adam对比:

在学习率为0.001时,Adam优化器的训练准确率和测试准确率在前期上升速度比SGD快,且最终的测试准确率也较高。

当学习率变为 0.01 时,SGD 的准确率曲线波动剧烈且最终准确率较低,而 Adam 的准确率曲线波动相对较小且最终准确率仍高于 SGD

        学习率影响:

对于SGD优化器,学习率从0.001增加到0.01时,准确率明显下降,说明学习率过大不利于模型学习到正确的模式

对于Adam优化器,学习率从0.001增加到001时,准确率也有所下降,但下降幅度小于SGD,说明Adam对学习率的敏感度相对较低。

        (5)混淆矩阵:是T 恤/top、裤子/trouser、套头衫/pullover、连衣裙/dress、外套/coat、凉鞋/sandal、衬衫/shirt、运动鞋/Sneaker、包/Bag、短靴/Ankle boot这 10 种商品的 10x10 混淆矩阵图。图中不同位置的元素表示预测类别与实际类别的数量关系,颜色越深代表值越大。从图中可以看出,对衬衫/shirt 的识别正确率最高,为966;其次是包/Bag,为 983;再次是对T恤/top的识别,为 985。而其他几类的识别准确度较低,例如对短靴/Ankle boot的识别只有96。

总结

        在本次实验中,深入探讨了卷积神经网络(CNN)在图像分类任务中的应用,特别是在FashionMNIST数据集上的表现。通过实验,不仅理解了CNN的基本概念,还掌握了数据预处理和增强技术,这对于提高模型的泛化能力和减少过拟合至关重要。实验中,采用了随机旋转和裁剪等数据增强方法,以及归一化处理,以确保数据的一致性和模型训练的稳定性。

        实验过程中,注意到了过拟合现象,并探讨了其产生的原因及避免方法。通过调整学习率、批大小和优化器等超参数来进行模型调优,以提高模型的训练效率和性能。在训练过程中,使用了Adam优化器,并观察了不同学习率对模型性能的影响。

        最终,通过绘制损失曲线和准确率曲线,以及生成混淆矩阵热力图,对模型的性能进行了评估和可视化。实验结果表明,Adam优化器在大多数情况下比SGD优化器表现更好,尤其是在较高学习率下,Adam的稳定性和收敛速度都优于SGD。

        总的来说,这次实验不仅加深了对CNN在图像分类任务中应用的理解,还提高了在模型设计、训练和调优方面的实践能力。通过实验,学会了如何通过不同的技术和方法来提高模型的泛化能力和性能。

源代码

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt

# 数据预处理和增强
transform_train = transforms.Compose([
    transforms.RandomRotation(10),
    transforms.RandomResizedCrop(28, scale=(0.8, 1.2)),
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])
transform_test = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

# 加载数据集
trainset = torchvision.datasets.FashionMNIST(root='./data', train=True,
                                            download=True, transform=transform_train)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64,
                                          shuffle=True, num_workers=2)
testset = torchvision.datasets.FashionMNIST(root='./data', train=False,
                                            download=True, transform=transform_test)
testloader = torch.utils.data.DataLoader(testset, batch_size=64,
                                         shuffle=False, num_workers=2)

# 定义卷积神经网络模型
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, kernel_size=3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)
        self.fc1 = nn.Linear(32 * 7 * 7, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 32 * 7 * 7)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return F.softmax(x, dim=1)

# 实例化模型
net = CNN()

# 定义损失函数和优化器,这里使用Adam优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)

# 用于存储训练损失和准确率
train_losses = []
train_accuracies = []
test_accuracies = []

# 训练模型
for epoch in range(30):  # 可以根据需要调整训练轮数
    running_loss = 0.0
    correct = 0
    total = 0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data
        optimizer.zero_grad()
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
    train_losses.append(running_loss / len(trainloader))
    train_accuracies.append(100 * correct / total)

    # 在测试集上评估模型
    correct = 0
    total = 0
    with torch.no_grad():
        for data in testloader:
            images, labels = data
            outputs = Inet(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    test_accuracies.append(100 * correct / total)

    print('[%d] Train Loss: %.3f, Train Accuracy: %.2f%%, Test Accuracy: %.2f%%' % (
        epoch + 1, train_losses[-1], train_accuracies[-1], test_accuracies[-1]))

# 绘制损失曲线
plt.figure(figsize=(10, 5))
plt.plot(range(1, len(train_losses) + 1), train_losses, label='Train Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
    plt.legend()
plt.show()

# 绘制准确率曲线
plt.figure(figsize=(10, 5))
plt.plot(range(1, len(train_accuracies) + 1), train_accuracies, label='Train Accuracy')
plt.plot(range(1, len(test_accuracies) + 1), test_accuracies, label='Test Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
plt.title('Accuracy Curves')
plt.legend()
plt.show()

你可能感兴趣的:(cnn,分类,人工智能)