自学内容网 自学内容网

pytorh学习笔记——手写数字识别mnist

pytorh学习第二站:手写数字识别

一、训练程序

1、创建脚本框架mnist_demo.py:

import torch
import torchvision.datasets as dataset

# data # 定义数据


# net # 定义网络


# loss # 损失


# optimizer  # 优化


# training  # 训练


# test  # 测试


# save# 保存

2、下载数据集

import torch
import torchvision.datasets as dataset
import torchvision.transforms as transforms

# data # 定义数据
train_data = dataset.MNIST(root='mnist_data',   # 创建数据集
                           train=True,          # 书否训练
                           transform=transforms.ToTensor,    # 转换成tensor
                           download=True)       # 是否下载

# net # 定义网络


# loss # 损失


# optimizer  # 优化


# training  # 训练


# test  # 测试


# save# 保存

        运行脚本,自动下载数据集。

        MNIST数据集是由60000个训练样本和10000个测试样本组成,每个样本都是一张28 * 28像素的灰度手写数字图片。 一共4个文件,训练集、训练集标签、测试集、测试集标签。

文件名称大小内容
train-images-idx3-ubyte.gz9,681 kb55000张训练集,5000张验证集
train-labels-idx1-ubyte.gz29 kb训练集图片对应的标签
t10k-images-idx3-ubyte.gz1,611 kb10000张测试集
t10k-labels-idx1-ubyte.gz5 kb测试集图片对应的标签

3、定义网络

# net # 定义网络
class CNN(torch.nn.Module):
    def __init__(self, channel=32):  # 通道数(channel)指的是卷积层中的滤波器数量。
        # 这个参数对于控制内存使用和训练速度非常重要。
        super(CNN, self).__init__()
        self.channel = channel
        self.conv = torch.nn.Sequential(  # 卷积
            # “torch.nn.Sequential” 是 PyTorch 中的一个容器类,它允许将多个模块按照顺序依次组合起来,形成一个新的模块。这样可以方便地构建神经网络模型,使得模型的定义更加简洁和清晰。
            # 例如,可以使用 “torch.nn.Sequential” 来快速构建一个包含多个卷积层和全连接层的神经网络。
            torch.nn.Conv2d(in_channels=1, out_channels=self.channel, kernel_size=5, stride=1, padding=2),  # 卷积层
            # torch.nn.Conv2d 是 PyTorch 深度学习框架中的一个类,它定义了一个二维卷积层。在神经网络中,卷积层是核心组成部分之一,主要用于执行卷积操作,
            # 这种操作可以通过一组可学习的滤波器(也称为卷积核)来提取输入数据的特征。
            # 具体来说,torch.nn.Conv2d 类接受几个关键参数:
            # in_channels:输入数据的通道数。例如,对于灰度图像,通道数为 1;对于 RGB 彩色图像,通道数为 3。
            # out_channels:输出数据的通道数,也就是卷积层将产生多少个特征图(feature map)。
            # kernel_size:卷积核的大小,通常表示为一个整数或者一个包含两个整数的元组(高度和宽度)。
            # stride:卷积操作的步长,即卷积核在输入数据上移动的间隔。
            # padding:填充大小,用于在输入数据的边缘周围添加额外的像素,以保持输出数据的大小与输入数据相同或按需调整。
            torch.nn.BatchNorm2d(self.channel),  # 归一化
            # torch.nn.BatchNorm2d() 是一个在 PyTorch 框架中定义的二维批量归一化(Batch Normalization)层。
            # 批量归一化是一种用于加速深度神经网络训练过程的技术。它通过对每一批数据进行归一化处理,使得数据的均值为0,方差为1,
            # 从而减少内部协变量偏移(Internal Covariate Shift)。这样做可以使得网络对于初始化权重的敏感度降低,并且允许使用更高的学习率。
            # 在这个上下文中,channel=32表示每个输入样本有32个特征图(feature maps)。
            # 二维批量归一化层通常用在卷积神经网络(CNN)中,用于处理图像数据。由于图像数据通常是二维的(高度和宽度),因此使用 BatchNorm2d
            # 而不是 BatchNorm1d,后者通常用于处理一维数据,如全连接层的输出。
            # 在实际应用中,BatchNorm2d 层通常紧跟在卷积层之后,激活函数之前。它有助于网络更快地收敛,并且可以起到一定的正则化效果,减少过拟合。
            # 总结一下,torch.nn.BatchNorm2d(32) 创建了一个适用于32通道输入数据的二维批量归一化层,这个层可以被添加到神经网络的模型定义中,以提高训练效率和模型性能。
            torch.nn.ReLU(),  # 激活函数
            # orch.nn.ReLU () ReLU(Rectified Linear Unit,修正线性单元)将输入值小于等于 0 的部分置为 0,输入值大于 0 的部分保持不变。
            # 它常用于神经网络中,能够引入非线性,帮助神经网络更好地拟合复杂的数据模式,提高模型的表达能力和性能。
            torch.nn.MaxPool2d(kernel_size=2))  # 池化层
            # torch.nn.MaxPool2d” 是 PyTorch 中的一个模块,用于对输入的二维信号(图像等)进行二维最大池化操作。最大池化通常是在卷积神经网络中用于降低特征图的空间尺寸,
            # 同时保留重要的特征信息,它在一定窗口范围内选取最大值作为输出。例如,对于一个图像特征图,通过 MaxPool2d 可以减少计算量,提高模型的效率和对一些局部特征变化的鲁棒性。

        self.fc = torch.nn.Linear(14 * 14 * 32, 10)  # 全连接
        # 这里的 torch.nn.Linear 是 PyTorch 框架中定义全连接层的类。这个类的构造函数接受两个参数:
        # 第一个参数 14*14*32 表示输入特征的数量。在这个例子中,它是由前面卷积层处理后的特征图大小决定的。
        # 假设经过一系列卷积和池化操作后,每个样本的特征被展平(flatten)成了一个 14x14x32 的三维数组,
        # 那么展平后的一维特征数量就是 14*14*32。
        # 第二个参数 10 表示输出特征的数量,也就是全连接层的神经元数量。在这个例子中,它对应于分类任务中的类别数,
        # 假设这是一个用于手写数字识别的任务,共有 10 个类别(0 到 9)。

    def forward(self, x):  # 前向传播
        x = self.conv(x)   # 之前定义的卷积操作
        x = x.view(x.size()[0], -1)  # reshape
        # view方法主要有以下应用:
        # 一、重塑张量(Tensor)形状
        # 1、基本概念
        # 张量是深度学习中数据的基本表示形式。view方法允许在不改变张量中数据的存储顺序和内容的情况下,改变其维度结构。
        # 例如,一个形状为(batch_size, channels, height, width)的 4D 图像张量可以通过view方法转换为(batch_size, channels * height * width)的 2D 张量,
        # 这在将图像数据展平以便输入到全连接层时非常有用。
        # 2、内存布局
        # view操作是基于张量的存储布局(contiguous memory layout)进行的。如果一个张量的存储不是连续的(例如,经过某些转置操作后),在使用view之前可能需要先调用
        # contiguous方法来确保正确的视图重塑。
        # 3、动态网络架构
        # 在构建动态神经网络结构时,view方法可以根据不同的输入或网络中间状态动态调整张量的形状。例如,在循环神经网络(RNN)的变长输入场景中,输入序列的长度可能是可变的。
        # 通过view方法,可以将输入张量调整为适合网络处理的形状,然后再将处理后的结果重新调整回合适的形状用于后续操作。
        # 4、模型输出调整
        # 在一些深度学习模型中,网络的输出可能需要调整形状以匹配特定的任务要求。例如,在生成对抗网络(GAN)中,生成器的输出可能需要通过view方法调整为与目标图像相同的形状,
        # 以便与真实图像进行比较和计算损失。
        output = self.fc(x)  # 全连接
        return output
cnn = CNN()  # 定义网络
cnn = cnn.cuda()  # 放入GPU

4、训练

# loss # 损失
loss_func = torch.nn.CrossEntropyLoss()  # 损失函数,使用了交叉熵损失函数。常用于分类问题中,衡量模型预测结果与真实标签之间的差异
    # 使模型的预测结果更接近真实标签。

# optimizer  # 优化
optimizer = torch.optim.Adam(cnn.parameters(), lr=0.01)  # 优化器,使用了Adam优化器。Adam是一种自适应学习率方法,可以自动调整学习率,使其适应训练过程。

# training  # 训练
for epoch in range(10):  # 训练会进行 10 次,每次代表一个 “epoch”。“epoch” 通常在机器学习中表示完整遍历一次训练数据集的过程。
    for i, (images, labels) in enumerate(train_loader):  # 遍历训练数据集
        images = images.cuda()  # 将数据放入GPU
        labels = labels.cuda()  # 将标签放入GPU

        optimizer.zero_grad()  # 梯度清零,这一行代码用于清零模型参数的梯度,因为在PyTorch中,
                            # 梯度是在每次反向传播时累加的,所以每次前向传播之前都需要清零梯度。
        outputs = cnn(images)  # 前向传播,将图像数据输入到CNN模型中,得到模型的输出outputs。
        loss = loss_func(outputs, labels)  # 计算损失
        loss.backward()  # 反向传播,计算损失函数对模型参数的梯度
        optimizer.step()  # 优化器更新参数

        if i % 100 == 0:  # 打印训练信息
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch + 1, i * len(images), len(train_loader.dataset),
                100. * i / len(train_loader), loss.item()))

 经过训练后输出如下:

Train Epoch: 10 [0/60000 (0%)]Loss: 0.011444
Train Epoch: 10 [6400/60000 (11%)]Loss: 0.014965
Train Epoch: 10 [12800/60000 (21%)]Loss: 0.032280
Train Epoch: 10 [19200/60000 (32%)]Loss: 0.055170
Train Epoch: 10 [25600/60000 (43%)]Loss: 0.019414
Train Epoch: 10 [32000/60000 (53%)]Loss: 0.010601
Train Epoch: 10 [38400/60000 (64%)]Loss: 0.016180
Train Epoch: 10 [44800/60000 (75%)]Loss: 0.005475
Train Epoch: 10 [51200/60000 (85%)]Loss: 0.046143
Train Epoch: 10 [57600/60000 (96%)]Loss: 0.067438

5、测试:

加入测试的代码:

# test  # 测试
    loss_test = 0
    correct = 0

    for i, (images, labels) in enumerate(test_loader):  # 遍历测试数据集
        images = images.cuda()  # 将数据放入GPU
        labels = labels.cuda()  # 将标签放入GPU

        outputs = cnn(images)  # 前向传播,将图像数据输入到CNN模型中,得到模型的输出outputs。
        loss_test += loss_func(outputs, labels)  # 计算损失
        _, predicted = outputs.max(1)  # 预测结果
        correct += predicted.eq(labels).sum().item()  # 计算正确率

    print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)'.format(
        loss_test / len(test_loader.dataset), correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

6、保存

# save# 保存
torch.save(cnn, 'model/mnist_cnn.pkl')  # 保存模型参数

完整代码:

import torch
import torchvision.datasets as dataset
import torchvision.transforms as transforms
import torch.utils.data as data_utils


# data # 定义数据
per_batch = 2000  # batch_size指的是在一次迭代中用于训练模型的样本数量
# 定义训练数据集
train_data = dataset.MNIST(root='mnist_data',  # 创建数据集
                           train=True,  # 是否训练
                           transform=transforms.ToTensor(),  # 转换成tensor
                           download=False)  # 是否下载

# 定义测试数据集
test_data = dataset.MNIST(root='mnist_data',  # 创建数据集
                          train=False,  # 是否训练
                          transform=transforms.ToTensor(),  # 转换成tensor
                          download=False)  # 是否下载
# 定义训练数据集的迭代器
train_loader = data_utils.DataLoader(dataset=train_data,  # 定义数据集
                                     batch_size=per_batch,  # 定义batch_size
                                     shuffle=True, )  # 是否打乱

# 定义测试数据集的迭代器
test_loader = data_utils.DataLoader(dataset=test_data,  # 定义数据集
                                    batch_size=per_batch,  # 定义batch_size
                                    shuffle=True, )  # 是否打乱


# net # 定义网络
class CNN(torch.nn.Module):
    def __init__(self, channel=32):  # 通道数(channel)指的是卷积层中的滤波器数量。
        # 这个参数对于控制内存使用和训练速度非常重要。
        super(CNN, self).__init__()
        self.channel = channel
        self.conv = torch.nn.Sequential(  # 卷积
            # “torch.nn.Sequential” 是 PyTorch 中的一个容器类,它允许将多个模块按照顺序依次组合起来,形成一个新的模块。这样可以方便地构建神经网络模型,使得模型的定义更加简洁和清晰。
            # 例如,可以使用 “torch.nn.Sequential” 来快速构建一个包含多个卷积层和全连接层的神经网络。
            torch.nn.Conv2d(in_channels=1, out_channels=self.channel, kernel_size=5, stride=1, padding=2),  # 卷积层
            # torch.nn.Conv2d 是 PyTorch 深度学习框架中的一个类,它定义了一个二维卷积层。在神经网络中,卷积层是核心组成部分之一,主要用于执行卷积操作,
            # 这种操作可以通过一组可学习的滤波器(也称为卷积核)来提取输入数据的特征。
            # 具体来说,torch.nn.Conv2d 类接受几个关键参数:
            # in_channels:输入数据的通道数。例如,对于灰度图像,通道数为 1;对于 RGB 彩色图像,通道数为 3。
            # out_channels:输出数据的通道数,也就是卷积层将产生多少个特征图(feature map)。
            # kernel_size:卷积核的大小,通常表示为一个整数或者一个包含两个整数的元组(高度和宽度)。
            # stride:卷积操作的步长,即卷积核在输入数据上移动的间隔。
            # padding:填充大小,用于在输入数据的边缘周围添加额外的像素,以保持输出数据的大小与输入数据相同或按需调整。
            torch.nn.BatchNorm2d(self.channel),  # 归一化
            # torch.nn.BatchNorm2d() 是一个在 PyTorch 框架中定义的二维批量归一化(Batch Normalization)层。
            # 批量归一化是一种用于加速深度神经网络训练过程的技术。它通过对每一批数据进行归一化处理,使得数据的均值为0,方差为1,
            # 从而减少内部协变量偏移(Internal Covariate Shift)。这样做可以使得网络对于初始化权重的敏感度降低,并且允许使用更高的学习率。
            # 在这个上下文中,channel=32表示每个输入样本有32个特征图(feature maps)。
            # 二维批量归一化层通常用在卷积神经网络(CNN)中,用于处理图像数据。由于图像数据通常是二维的(高度和宽度),因此使用 BatchNorm2d
            # 而不是 BatchNorm1d,后者通常用于处理一维数据,如全连接层的输出。
            # 在实际应用中,BatchNorm2d 层通常紧跟在卷积层之后,激活函数之前。它有助于网络更快地收敛,并且可以起到一定的正则化效果,减少过拟合。
            # 总结一下,torch.nn.BatchNorm2d(32) 创建了一个适用于32通道输入数据的二维批量归一化层,这个层可以被添加到神经网络的模型定义中,以提高训练效率和模型性能。
            torch.nn.ReLU(),  # 激活函数
            # orch.nn.ReLU () ReLU(Rectified Linear Unit,修正线性单元)将输入值小于等于 0 的部分置为 0,输入值大于 0 的部分保持不变。
            # 它常用于神经网络中,能够引入非线性,帮助神经网络更好地拟合复杂的数据模式,提高模型的表达能力和性能。
            torch.nn.MaxPool2d(kernel_size=2))  # 池化层
            # torch.nn.MaxPool2d” 是 PyTorch 中的一个模块,用于对输入的二维信号(图像等)进行二维最大池化操作。最大池化通常是在卷积神经网络中用于降低特征图的空间尺寸,
            # 同时保留重要的特征信息,它在一定窗口范围内选取最大值作为输出。例如,对于一个图像特征图,通过 MaxPool2d 可以减少计算量,提高模型的效率和对一些局部特征变化的鲁棒性。

        self.fc = torch.nn.Linear(14 * 14 * 32, 10)  # 全连接
        # 这里的 torch.nn.Linear 是 PyTorch 框架中定义全连接层的类。这个类的构造函数接受两个参数:
        # 第一个参数 14*14*32 表示输入特征的数量。在这个例子中,它是由前面卷积层处理后的特征图大小决定的。
        # 假设经过一系列卷积和池化操作后,每个样本的特征被展平(flatten)成了一个 14x14x32 的三维数组,
        # 那么展平后的一维特征数量就是 14*14*32。
        # 第二个参数 10 表示输出特征的数量,也就是全连接层的神经元数量。在这个例子中,它对应于分类任务中的类别数,
        # 假设这是一个用于手写数字识别的任务,共有 10 个类别(0 到 9)。

    def forward(self, x):  # 前向传播
        x = self.conv(x)   # 之前定义的卷积操作
        x = x.view(x.size()[0], -1)  # reshape
        # view方法主要有以下应用:
        # 一、重塑张量(Tensor)形状
        # 1、基本概念
        # 张量是深度学习中数据的基本表示形式。view方法允许在不改变张量中数据的存储顺序和内容的情况下,改变其维度结构。
        # 例如,一个形状为(batch_size, channels, height, width)的 4D 图像张量可以通过view方法转换为(batch_size, channels * height * width)的 2D 张量,
        # 这在将图像数据展平以便输入到全连接层时非常有用。
        # 2、内存布局
        # view操作是基于张量的存储布局(contiguous memory layout)进行的。如果一个张量的存储不是连续的(例如,经过某些转置操作后),在使用view之前可能需要先调用
        # contiguous方法来确保正确的视图重塑。
        # 3、动态网络架构
        # 在构建动态神经网络结构时,view方法可以根据不同的输入或网络中间状态动态调整张量的形状。例如,在循环神经网络(RNN)的变长输入场景中,输入序列的长度可能是可变的。
        # 通过view方法,可以将输入张量调整为适合网络处理的形状,然后再将处理后的结果重新调整回合适的形状用于后续操作。
        # 4、模型输出调整
        # 在一些深度学习模型中,网络的输出可能需要调整形状以匹配特定的任务要求。例如,在生成对抗网络(GAN)中,生成器的输出可能需要通过view方法调整为与目标图像相同的形状,
        # 以便与真实图像进行比较和计算损失。
        output = self.fc(x)  # 全连接
        return output
cnn = CNN()  # 定义网络
cnn = cnn.cuda()  # 放入GPU

# loss # 损失
loss_func = torch.nn.CrossEntropyLoss()  # 损失函数,使用了交叉熵损失函数。常用于分类问题中,衡量模型预测结果与真实标签之间的差异
    # 使模型的预测结果更接近真实标签。

# optimizer  # 优化
optimizer = torch.optim.Adam(cnn.parameters(), lr=0.01)  # 优化器,使用了Adam优化器。Adam是一种自适应学习率方法,可以自动调整学习率,使其适应训练过程。

# training  # 训练
for epoch in range(10):  # 训练会进行 10 次,每次代表一个 “epoch”。“epoch” 通常在机器学习中表示完整遍历一次训练数据集的过程。
    for i, (images, labels) in enumerate(train_loader):  # 遍历训练数据集
        images = images.cuda()  # 将数据放入GPU
        labels = labels.cuda()  # 将标签放入GPU

        optimizer.zero_grad()  # 梯度清零,这一行代码用于清零模型参数的梯度,因为在PyTorch中,
                            # 梯度是在每次反向传播时累加的,所以每次前向传播之前都需要清零梯度。
        outputs = cnn(images)  # 前向传播,将图像数据输入到CNN模型中,得到模型的输出outputs。
        loss = loss_func(outputs, labels)  # 计算损失
        loss.backward()  # 反向传播,计算损失函数对模型参数的梯度
        optimizer.step()  # 优化器更新参数

        if i % 100 == 0:  # 打印训练信息
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch + 1, i * len(images), len(train_loader.dataset),
                100. * i / len(train_loader), loss.item()))

# test  # 测试
    loss_test = 0
    correct = 0

    for i, (images, labels) in enumerate(test_loader):  # 遍历测试数据集
        images = images.cuda()  # 将数据放入GPU
        labels = labels.cuda()  # 将标签放入GPU

        outputs = cnn(images)  # 前向传播,将图像数据输入到CNN模型中,得到模型的输出outputs。
        loss_test += loss_func(outputs, labels)  # 计算损失
        _, predicted = outputs.max(1)  # 预测结果
        correct += predicted.eq(labels).sum().item()  # 计算正确率

    print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)'.format(
        loss_test / len(test_loader.dataset), correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))



# save# 保存
torch.save(cnn, 'model/mnist_cnn.pkl')  # 保存模型参数

 二、利用保存的模型推理

新建inference.py:

import torch
import torchvision.datasets as dataset
import torchvision.transforms as transforms
import torch.utils.data as data_utils


# net # 定义网络
class CNN(torch.nn.Module):
    def __init__(self, channel=32):  # 通道数(channel)指的是卷积层中的滤波器数量。
        # 这个参数对于控制内存使用和训练速度非常重要。
        super(CNN, self).__init__()
        self.channel = channel
        self.conv = torch.nn.Sequential(  # 卷积
            torch.nn.Conv2d(in_channels=1, out_channels=self.channel, kernel_size=5, stride=1, padding=2),  # 卷积层
            torch.nn.BatchNorm2d(self.channel),  # 归一化
            torch.nn.ReLU(),  # 激活函数
            torch.nn.MaxPool2d(kernel_size=2))  # 池化层

        self.fc = torch.nn.Linear(14 * 14 * 32, 10)  # 全连接

    def forward(self, x):  # 前向传播
        x = self.conv(x)   # 之前定义的卷积操作
        x = x.view(x.size()[0], -1)  # reshape
        output = self.fc(x)  # 全连接
        return output

# data # 定义数据
per_batch = 2000
# 定义训练数据集
train_data = dataset.MNIST(root='mnist_data',  # 创建数据集
                           train=True,  # 是否训练
                           transform=transforms.ToTensor(),  # 转换成tensor
                           download=False)  # 是否下载

# 定义测试数据集
test_data = dataset.MNIST(root='mnist_data',  # 创建数据集
                          train=False,  # 是否训练
                          transform=transforms.ToTensor(),  # 转换成tensor
                          download=False)  # 是否下载

# 定义测试数据集的迭代器
test_loader = data_utils.DataLoader(dataset=test_data,  # 定义数据集
                                    batch_size=per_batch,  # 定义batch_size
                                    shuffle=True, )  # 是否打乱


cnn = torch.load('E:\AI_tset\mnist_demo\model\mnist_cnn.pkl')  # 定义网络
cnn = cnn.cuda()  # 放入GPU




# inference  # 预测
loss_test = 0
correct = 0

for i, (images, labels) in enumerate(test_loader):  # 遍历测试数据集
    images = images.cuda()  # 将数据放入GPU
    labels = labels.cuda()  # 将标签放入GPU

    outputs = cnn(images)  # 前向传播,将图像数据输入到CNN模型中,得到模型的输出outputs。
    _, predicted = outputs.max(1)  # 预测结果
    correct += predicted.eq(labels).sum().item()  # 计算正确率

print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)'.format(
    loss_test / len(test_loader.dataset), correct, len(test_loader.dataset),
    100. * correct / len(test_loader.dataset)))

运行结果:

Test set: Average loss: 0.0000, Accuracy: 9813/10000 (98%)

三、将网络定义为单独调用的类

1、新建CNN.py ,将之前训练脚本中的网络定义拷贝:

import torch


# net # 定义网络
class CNN(torch.nn.Module):
    def __init__(self, channel=32):  # 通道数(channel)指的是卷积层中的滤波器数量。
        # 这个参数对于控制内存使用和训练速度非常重要。
        super(CNN, self).__init__()
        self.channel = channel
        self.conv = torch.nn.Sequential(  # 卷积
            # “torch.nn.Sequential” 是 PyTorch 中的一个容器类,它允许将多个模块按照顺序依次组合起来,形成一个新的模块。这样可以方便地构建神经网络模型,使得模型的定义更加简洁和清晰。
            # 例如,可以使用 “torch.nn.Sequential” 来快速构建一个包含多个卷积层和全连接层的神经网络。
            torch.nn.Conv2d(in_channels=1, out_channels=self.channel, kernel_size=5, stride=1, padding=2),  # 卷积层
            # torch.nn.Conv2d 是 PyTorch 深度学习框架中的一个类,它定义了一个二维卷积层。在神经网络中,卷积层是核心组成部分之一,主要用于执行卷积操作,
            # 这种操作可以通过一组可学习的滤波器(也称为卷积核)来提取输入数据的特征。
            # 具体来说,torch.nn.Conv2d 类接受几个关键参数:
            # in_channels:输入数据的通道数。例如,对于灰度图像,通道数为 1;对于 RGB 彩色图像,通道数为 3。
            # out_channels:输出数据的通道数,也就是卷积层将产生多少个特征图(feature map)。
            # kernel_size:卷积核的大小,通常表示为一个整数或者一个包含两个整数的元组(高度和宽度)。
            # stride:卷积操作的步长,即卷积核在输入数据上移动的间隔。
            # padding:填充大小,用于在输入数据的边缘周围添加额外的像素,以保持输出数据的大小与输入数据相同或按需调整。
            torch.nn.BatchNorm2d(self.channel),  # 归一化
            # torch.nn.BatchNorm2d() 是一个在 PyTorch 框架中定义的二维批量归一化(Batch Normalization)层。
            # 批量归一化是一种用于加速深度神经网络训练过程的技术。它通过对每一批数据进行归一化处理,使得数据的均值为0,方差为1,
            # 从而减少内部协变量偏移(Internal Covariate Shift)。这样做可以使得网络对于初始化权重的敏感度降低,并且允许使用更高的学习率。
            # 在这个上下文中,channel=32表示每个输入样本有32个特征图(feature maps)。
            # 二维批量归一化层通常用在卷积神经网络(CNN)中,用于处理图像数据。由于图像数据通常是二维的(高度和宽度),因此使用 BatchNorm2d
            # 而不是 BatchNorm1d,后者通常用于处理一维数据,如全连接层的输出。
            # 在实际应用中,BatchNorm2d 层通常紧跟在卷积层之后,激活函数之前。它有助于网络更快地收敛,并且可以起到一定的正则化效果,减少过拟合。
            # 总结一下,torch.nn.BatchNorm2d(32) 创建了一个适用于32通道输入数据的二维批量归一化层,这个层可以被添加到神经网络的模型定义中,以提高训练效率和模型性能。
            torch.nn.ReLU(),  # 激活函数
            # orch.nn.ReLU () ReLU(Rectified Linear Unit,修正线性单元)将输入值小于等于 0 的部分置为 0,输入值大于 0 的部分保持不变。
            # 它常用于神经网络中,能够引入非线性,帮助神经网络更好地拟合复杂的数据模式,提高模型的表达能力和性能。
            torch.nn.MaxPool2d(kernel_size=2))  # 池化层
        # torch.nn.MaxPool2d” 是 PyTorch 中的一个模块,用于对输入的二维信号(图像等)进行二维最大池化操作。最大池化通常是在卷积神经网络中用于降低特征图的空间尺寸,
        # 同时保留重要的特征信息,它在一定窗口范围内选取最大值作为输出。例如,对于一个图像特征图,通过 MaxPool2d 可以减少计算量,提高模型的效率和对一些局部特征变化的鲁棒性。

        self.fc = torch.nn.Linear(14 * 14 * 32, 10)  # 全连接
        # 这里的 torch.nn.Linear 是 PyTorch 框架中定义全连接层的类。这个类的构造函数接受两个参数:
        # 第一个参数 14*14*32 表示输入特征的数量。在这个例子中,它是由前面卷积层处理后的特征图大小决定的。
        # 假设经过一系列卷积和池化操作后,每个样本的特征被展平(flatten)成了一个 14x14x32 的三维数组,
        # 那么展平后的一维特征数量就是 14*14*32。
        # 第二个参数 10 表示输出特征的数量,也就是全连接层的神经元数量。在这个例子中,它对应于分类任务中的类别数,
        # 假设这是一个用于手写数字识别的任务,共有 10 个类别(0 到 9)。

    def forward(self, x):  # 前向传播
        x = self.conv(x)  # 之前定义的卷积操作
        x = x.view(x.size()[0], -1)  # reshape
        # view方法主要有以下应用: 一、重塑张量(Tensor)形状 1、基本概念 张量是深度学习中数据的基本表示形式。view方法允许在不改变张量中数据的存储顺序和内容的情况下,改变其维度结构。 例如,一个形状为(
        # batch_size, channels, height, width)的 4D 图像张量可以通过view方法转换为(batch_size, channels * height * width)的 2D 张量,
        # 这在将图像数据展平以便输入到全连接层时非常有用。 2、内存布局 view操作是基于张量的存储布局(contiguous memory
        # layout)进行的。如果一个张量的存储不是连续的(例如,经过某些转置操作后),在使用view之前可能需要先调用 contiguous方法来确保正确的视图重塑。 3、动态网络架构
        # 在构建动态神经网络结构时,view方法可以根据不同的输入或网络中间状态动态调整张量的形状。例如,在循环神经网络(RNN)的变长输入场景中,输入序列的长度可能是可变的。
        # 通过view方法,可以将输入张量调整为适合网络处理的形状,然后再将处理后的结果重新调整回合适的形状用于后续操作。 4、模型输出调整
        # 在一些深度学习模型中,网络的输出可能需要调整形状以匹配特定的任务要求。例如,在生成对抗网络(GAN)中,生成器的输出可能需要通过view方法调整为与目标图像相同的形状, 以便与真实图像进行比较和计算损失。
        output = self.fc(x)  # 全连接
        return output

2、在训练脚本中调用定义好的网络:

import torch
import torchvision.datasets as dataset
import torchvision.transforms as transforms
import torch.utils.data as data_utils
from CNN import CNN

#  “torch.utils.data”提供了一些用于数据处理和加载的实用工具,比如可以用来创建数据加载器(DataLoader)、对数据集进行分割等操作。
# “torch.utils.data” 中的 “data_utils” 子模块除了可以分割数据集外,还提供了以下一些实用工具:
# 一、数据加载器相关
# 创建数据加载器(DataLoader)时可以设置各种参数,如批次大小(batch_size)、是否随机打乱数据(shuffle)等,方便高效地加载数据进行训练和推理。
# 可以处理不同类型的数据,包括图像、文本、表格数据等,将其转换为适合模型输入的格式。
# 二、数据预处理
# 提供一些常见的数据预处理函数,例如归一化、标准化等操作,以便使数据更适合模型训练。
# 可以对数据进行随机裁剪、旋转等数据增强操作,增加数据的多样性,提高模型的泛化能力。
# 三、数据集管理
# 帮助管理数据集的迭代,确保在训练过程中能够正确地遍历数据集,避免重复或遗漏数据。
# 可以方便地与不同类型的数据集进行交互,包括自定义数据集和常见的公开数据集。

# data # 定义数据
one_batch = 2000  # batch_size指的是在一次迭代中用于训练模型的样本数量
# 定义训练数据集
train_data = dataset.MNIST(root='mnist_data',  # 创建数据集
                           train=True,  # 是否训练
                           transform=transforms.ToTensor(),  # 转换成tensor
                           download=False)  # 是否下载

# 定义测试数据集
test_data = dataset.MNIST(root='mnist_data',  # 创建数据集
                          train=False,  # 是否训练
                          transform=transforms.ToTensor(),  # 转换成tensor
                          download=False)  # 是否下载
# 定义训练数据集的迭代器
train_loader = data_utils.DataLoader(dataset=train_data,  # 定义数据集
                                     batch_size=one_batch,  # 定义batch_size
                                     shuffle=True, )  # 是否打乱

# 定义测试数据集的迭代器
test_loader = data_utils.DataLoader(dataset=test_data,  # 定义数据集
                                    batch_size=one_batch,  # 定义batch_size
                                    shuffle=True, )  # 是否打乱



cnn = CNN()  # 定义网络
cnn = cnn.cuda()  # 放入GPU

# loss # 损失
loss_func = torch.nn.CrossEntropyLoss()  # 损失函数,使用了交叉熵损失函数。常用于分类问题中,衡量模型预测结果与真实标签之间的差异
    # 使模型的预测结果更接近真实标签。

# optimizer  # 优化
optimizer = torch.optim.Adam(cnn.parameters(), lr=0.01)  # 优化器,使用了Adam优化器。Adam是一种自适应学习率方法,可以自动调整学习率,使其适应训练过程。

# training  # 训练
for epoch in range(10):  # 训练会进行 10 次,每次代表一个 “epoch”。“epoch” 通常在机器学习中表示完整遍历一次训练数据集的过程。
    for i, (images, labels) in enumerate(train_loader):  # 遍历训练数据集
        images = images.cuda()  # 将数据放入GPU
        labels = labels.cuda()  # 将标签放入GPU

        optimizer.zero_grad()  # 梯度清零,这一行代码用于清零模型参数的梯度,因为在PyTorch中,
                            # 梯度是在每次反向传播时累加的,所以每次前向传播之前都需要清零梯度。
        outputs = cnn(images)  # 前向传播,将图像数据输入到CNN模型中,得到模型的输出outputs。
        loss = loss_func(outputs, labels)  # 计算损失
        loss.backward()  # 反向传播,计算损失函数对模型参数的梯度
        optimizer.step()  # 优化器更新参数

        if i % 100 == 0:  # 打印训练信息
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch + 1, i * len(images), len(train_loader.dataset),
                100. * i / len(train_loader), loss.item()))

# test  # 测试
    loss_test = 0
    correct = 0

    for i, (images, labels) in enumerate(test_loader):  # 遍历测试数据集
        images = images.cuda()  # 将数据放入GPU
        labels = labels.cuda()  # 将标签放入GPU

        outputs = cnn(images)  # 前向传播,将图像数据输入到CNN模型中,得到模型的输出outputs。
        loss_test += loss_func(outputs, labels)  # 计算损失
        _, predicted = outputs.max(1)  # 预测结果
        correct += predicted.eq(labels).sum().item()  # 计算正确率

    print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)'.format(
        loss_test / len(test_loader.dataset), correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))



# save# 保存
torch.save(cnn, 'model/mnist_cnn.pkl')  # 保存模型参数

四、在推理脚本中加入图片显示的功能:

import torch
import torchvision.datasets as dataset
import torchvision.transforms as transforms
import torch.utils.data as data_utils
import cv2

# data # 定义数据
per_batch = 32
# 定义训练数据集
train_data = dataset.MNIST(root='mnist_data',  # 创建数据集
                           train=True,  # 是否训练
                           transform=transforms.ToTensor(),  # 转换成tensor
                           download=False)  # 是否下载

# 定义测试数据集
test_data = dataset.MNIST(root='mnist_data',  # 创建数据集
                          train=False,  # 是否训练
                          transform=transforms.ToTensor(),  # 转换成tensor
                          download=False)  # 是否下载

# 定义测试数据集的迭代器
test_loader = data_utils.DataLoader(dataset=test_data,  # 定义数据集
                                    batch_size=per_batch,  # 定义batch_size
                                    shuffle=True, )  # 是否打乱

cnn = torch.load('E:\AI_tset\mnist_demo\model\mnist_cnn.pkl')  # 定义网络
cnn = cnn.cuda()  # 放入GPU

# inference  # 预测
loss_test = 0
correct = 0

for i, (images, labels) in enumerate(test_loader):  # 遍历测试数据集
    images = images.cuda()  # 将数据放入GPU
    labels = labels.cuda()  # 将标签放入GPU

    outputs = cnn(images)  # 前向传播,将图像数据输入到CNN模型中,得到模型的输出outputs。
    _, predicted = outputs.max(1)  # 预测结果
    correct += predicted.eq(labels).sum().item()  # 计算正确率

    images = images.cpu().numpy()  # 将数据从GPU中取出
    labels = labels.cpu().numpy()  # 将标签从GPU中取出
    predicted = predicted.cpu().numpy()  # 将预测结果从GPU中取出

    for idx in range(per_batch):  # 遍历每一批数据
        image = images[idx]  # 取出图像
        label = labels[idx]  # 取出标签
        predict = predicted[idx]  # 取出预测结果

        print(image.shape)   # 打印图像的shape (1, 28, 28)
        # 由于image的维度是(1, 28, 28),所以需要将维度变成(28, 28, 1),下面两种方法都可以
        # image = image.reshape(28, 28)  # 将图像reshape成28*28的矩阵
        image = image.transpose(1, 2, 0)  # 将图像转置   # (1, 28, 28) -> (28, 28, 1)        
        cv2.imshow('image', image)  # 显示图像
        print('label:{},predict:{}'.format(label, predict))  # 打印标签和预测结果
        cv2.waitKey(0)  # 等待按键

运行结果:


原文地址:https://blog.csdn.net/xulibo5828/article/details/142856881

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!