卷积神经网络(Convolutional Neural Networks, CNNs)的基本知识 附部分代码 (二)

写在前面

本篇文章算是作者在学习深度学习知识中的学习笔记,算是对于作者学习的相关论文、教材、视频的归纳总结,以后也会持续更新这个板块(如果上学了则会更新的慢一点)

本篇文章的代码源于用PyTorch实现MNIST手写数字识别(最新,非常详细)_mnist pytorch-CSDN博客

实战:MNIST手写数字识别神经网络搭建

1、下载MNIST数据集

MNIST(Modified National Institute of Standards and Technology)数据集是一个非常著名且广泛使用的手写数字数据集,用于训练各种图像处理系统。它包含了大量的手写数字图片,通常被用作计算机视觉和机器学习领域的入门级教材或基准测试。

通过百度网盘分享的文件:MNIST
链接:https://pan.baidu.com/s/1-153zuBFsUlAj5sy6-z0Jw?pwd=la12 
提取码:la12

2、数据集预处理

由于MNIST数据集中图像是由28*28像素的单通道图像组成的,每个像素的值都是0~255

,这个范围不方便用于计算,所以需要对其进行归一化处理,将其值转为0~1。

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

其中:transform.Compose是pytorch中的一个类,可以组合多个图像变换操作,他接受变换操作的列表,并按顺序应用这些操作;

          transform.ToTensor是一个图像变换操作,用于将PIL 图像或 NumPy 数组转换为 PyTorch 张量(Tensor)。转换过程中,图像的像素值会被缩放到 [0, 1] 范围内。是实现归一化处理的核心 操作。

          transforms.Normalize((0.1307,), (0.3081,))是一个图像变换操作,用于对图像张量进行标准化处理。它接受两个参数:均值(mean)和标准差(std)。其中具体的数值是针对MNIST数据集像素值统计得出的,其标准化处理公式为output = (input - mean) / std。

3、构建网络

定义一个网络结构的类,使其继承自torch.nn.Module,这是PyTorch中定义神经网络模型的标准方式

class Net(torch.nn.Module):

接着定义卷积层1,它的作用通常是用来提取图像的低级特征,如边缘、纹理等等,这些都是图像的基本构建块。其中输入通道数为1,输出通道数为10,卷积核大小为5x5;使用ReLU激活函数;最大池化窗孔大小为2x2;nn.Sequential的主要作用是简化模型定义,提高代码的可读性

 self.conv1 = nn.Sequential(
            nn.Conv2d(1, 10, kernel_size=5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )

同样定义卷积层2,它的作用是提取图像更高级别的特征。

 self.conv1 = nn.Sequential(
            nn.Conv2d(1, 10, kernel_size=5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )

接下来定义向前传递的函数

 def forward(self, x):
        x = self.conv1(x)    # 卷积层1
        x = self.conv2(x)    # 卷积层2
        x = x.view(x.size(0), -1)   # 展平
        x = F.relu(self.fc1(x))    # 全连接层1
        x = self.fc2(x)    # 全连接层2
        return x            # 返回输出

先将x传入第一个卷积层中,此时结果为二维的,再将它传入第二个卷积层中,此时得到的是四维的结果,所以使用x.view(x.size(0), -1)将其展平,随后通过两个全连接层来处理特征,返回最终的分类结果。

4、训练模型

在训练模型以前我们需要先将模型实例化,使用以下代码:

# 实例化模型
model = Net()

接着定义损失函数与优化器:

# 定义损失函数和优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

其中:torch.nn.CrossEntropyLoss():这是一个用于分类任务的损失函数。它结合了LogSoftmaxNLLLoss(负对数似然损失)。用于计算预测值(通常是模型的输出)和真实标签之间的差异。在分类任务中,它是一个常用的损失函数。

          torch.optim.SGD:这是随机梯度下降(Stochastic Gradient Descent)优化器的一个实现。model.parameters():这是一个生成器,返回模型中所有需要更新的参数。lr=0.01:学习率(learning rate),控制参数更新的步长。momentum=0.9:动量(momentum),用于加速SGD在相关方向上的收敛,并抑制震荡。

下面开始训练模型:

def train(epoch):
    model.train()  # 设置模型为训练模式
    running_loss = 0.0
    running_correct = 0#初始化两个变量 `running_loss` 和 `running_correct`,分别用于累积损失值和正确预测的数量。
    for batch_idx, (inputs, target) in enumerate(train_loader):#使用 enumerate 遍历 train_loader,其中 batch_idx 是批次索引,inputs 是输入数据,target 是目标标签。
        optimizer.zero_grad()#在每次迭代开始时,调用 optimizer.zero_grad() 将所有参数的梯度缓存清零,以避免梯度累积。
        outputs = model(inputs)#向前传递
        loss = criterion(outputs, target)#计算损失函数
        loss.backward()#反向传播
        optimizer.step()#更新参数
        #累积损失和正确预测数
        running_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)#找到输出中概率最大的类别作为预测结果。
        running_correct += (predicted == target).sum().item()

        if batch_idx % 300 == 299:#定期打印训练进度
            print('[%d, %5d]: loss: %.3f , acc: %.2f %%'
                  % (epoch + 1, batch_idx + 1, running_loss / 300, 100 * running_correct / (300 * train_loader.batch_size)))
            running_loss = 0.0
            running_correct = 0

5、测试模型

def test():
    model.eval()  # 设置模型为评估模式
    correct = 0#初始化计数器
    total = 0
    with torch.no_grad():#禁止梯度计算,以提高效率。
        for data in test_loader:#遍历测试集
            images, labels = data
            outputs = model(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    acc = correct / total
    print('[%d / %d]: Accuracy on test set: %.1f %% ' % (epoch + 1, EPOCH, 100 * acc))#打印准确率
    return acc

6、主函数

if __name__ == '__main__':
    acc_list_test = []
    # 训练与测试模型循环
    for epoch in range(EPOCH):
        train(epoch)
        acc_test = test()
        acc_list_test.append(acc_test)
    #绘制准确率曲线    
    plt.plot(acc_list_test)
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy On TestSet')
    plt.show()

完整代码

# 导入必要的库
import torch
import numpy as np
from matplotlib import pyplot as plt
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets
import torch.nn.functional as F

# 数据集预处理
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

# 确保路径存在并且有写权限
root_path = r'E:\MNIST'

# 加载数据集
train_dataset = datasets.MNIST(root=root_path, train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root=root_path, train=False, download=True, transform=transform)

# 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)

# 搭建模型
class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(1, 10, kernel_size=5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(10, 20, kernel_size=5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)

    def forward(self, x):
        x = self.conv1(x)    # 卷积层1
        x = self.conv2(x)    # 卷积层2
        x = x.view(x.size(0), -1)   # 展平
        x = F.relu(self.fc1(x))    # 全连接层1
        x = self.fc2(x)    # 全连接层2
        return x            # 返回输出

# 实例化模型
model = Net()
# 定义损失函数和优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

# 训练模型
def train(epoch):
    model.train()  # 设置模型为训练模式
    running_loss = 0.0
    running_correct = 0#初始化两个变量 `running_loss` 和 `running_correct`,分别用于累积损失值和正确预测的数量。
    for batch_idx, (inputs, target) in enumerate(train_loader):#使用 enumerate 遍历 train_loader,其中 batch_idx 是批次索引,inputs 是输入数据,target 是目标标签。
        optimizer.zero_grad()#在每次迭代开始时,调用 optimizer.zero_grad() 将所有参数的梯度缓存清零,以避免梯度累积。
        outputs = model(inputs)#向前传递
        loss = criterion(outputs, target)#计算损失函数
        loss.backward()#反向传播
        optimizer.step()#更新参数
        #累积损失和正确预测数
        running_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)#找到输出中概率最大的类别作为预测结果。
        running_correct += (predicted == target).sum().item()

        if batch_idx % 300 == 299:#定期打印训练进度
            print('[%d, %5d]: loss: %.3f , acc: %.2f %%'
                  % (epoch + 1, batch_idx + 1, running_loss / 300, 100 * running_correct / (300 * train_loader.batch_size)))
            running_loss = 0.0
            running_correct = 0

# 测试模型
def test():
    model.eval()  # 设置模型为评估模式
    correct = 0#初始化计数器
    total = 0
    with torch.no_grad():#禁止梯度计算,以提高效率。
        for data in test_loader:#遍历测试集
            images, labels = data
            outputs = model(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    acc = correct / total
    print('[%d / %d]: Accuracy on test set: %.1f %% ' % (epoch + 1, EPOCH, 100 * acc))#打印准确率
    return acc

# 超参数,表示训练过程的总迭代次数
EPOCH = 10

if __name__ == '__main__':
    acc_list_test = []
    # 训练与测试模型循环
    for epoch in range(EPOCH):
        train(epoch)
        acc_test = test()
        acc_list_test.append(acc_test)
    #绘制准确率曲线
    plt.plot(acc_list_test)
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy On TestSet')
    plt.show()

测试结果

[1,   300]: loss: 0.522 , acc: 83.49 %
[1,   600]: loss: 0.116 , acc: 96.30 %
[1,   900]: loss: 0.081 , acc: 97.45 %
[1 / 10]: Accuracy on test set: 97.7 % 
[2,   300]: loss: 0.069 , acc: 97.81 %
[2,   600]: loss: 0.062 , acc: 98.07 %
[2,   900]: loss: 0.053 , acc: 98.43 %
[2 / 10]: Accuracy on test set: 98.4 % 
[3,   300]: loss: 0.044 , acc: 98.63 %
[3,   600]: loss: 0.045 , acc: 98.62 %
[3,   900]: loss: 0.043 , acc: 98.64 %
[3 / 10]: Accuracy on test set: 98.6 % 
[4,   300]: loss: 0.036 , acc: 98.86 %
[4,   600]: loss: 0.035 , acc: 98.92 %
[4,   900]: loss: 0.036 , acc: 98.95 %
[4 / 10]: Accuracy on test set: 98.9 % 
[5,   300]: loss: 0.027 , acc: 99.13 %
[5,   600]: loss: 0.033 , acc: 98.93 %
[5,   900]: loss: 0.029 , acc: 99.14 %
[5 / 10]: Accuracy on test set: 99.0 % 
[6,   300]: loss: 0.024 , acc: 99.27 %
[6,   600]: loss: 0.023 , acc: 99.24 %
[6,   900]: loss: 0.026 , acc: 99.18 %
[6 / 10]: Accuracy on test set: 98.9 % 
[7,   300]: loss: 0.018 , acc: 99.38 %
[7,   600]: loss: 0.023 , acc: 99.21 %
[7,   900]: loss: 0.020 , acc: 99.36 %
[7 / 10]: Accuracy on test set: 99.0 % 
[8,   300]: loss: 0.016 , acc: 99.52 %
[8,   600]: loss: 0.020 , acc: 99.33 %
[8,   900]: loss: 0.023 , acc: 99.23 %
[8 / 10]: Accuracy on test set: 99.0 % 
[9,   300]: loss: 0.013 , acc: 99.60 %
[9,   600]: loss: 0.017 , acc: 99.44 %
[9,   900]: loss: 0.019 , acc: 99.31 %
[9 / 10]: Accuracy on test set: 98.9 % 
[10,   300]: loss: 0.011 , acc: 99.64 %
[10,   600]: loss: 0.013 , acc: 99.63 %
[10,   900]: loss: 0.015 , acc: 99.53 %
[10 / 10]: Accuracy on test set: 98.9 % 

图1运行结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值