Pytorch框架与经典卷积神经网络学习Day3|LeNet与AlexNet原理与实战

跟学视频

炮哥带你学_Pytorch框架与经典卷积神经网络与实战

1.原理

1.1LeNet-5

LetNet-5指的是带参数的神经网络层有5层,池化层不算一层。

LetNet-5有两个卷积层和3个全连接层。

使用的是平均池化,当时还未发明最大池化。

x——>[CNN——>sigmoid——>pool]

若干个这样的过程进行特征提取,最后经过全连接层进行分类。

上图输出改为(B,FN,OW,OH)

L=FNXOWXOH

全连接层输出为(B,FL)

当时使用平均池化,但是最大池化用得最多,效果比平均池化好,能提取最有效的特征。 

使用以下公式计算,在上篇博客上有具体介绍:

展平操作

卷积层和池化层进行特征提取,全连接层得到分类结果。

1.2AlexNet 

 LeNet-5在小数据集上效果好,在大数据集上受到硬件限制。 

现有的深度学习在大规模数据集上的确效果很好,而且可移植性也比较好,泛化性也比较强。

AlexNet和LeNet设计理念非常相似。

AlexNet有5层卷积层,3层全连接层,使用ReLU激活函数,会快很多。

sigmoid激活函数适合用在分类。

使用以下公式计算,在上篇博客上有具体介绍:

 池化层使我们特征图分辨率变低,通道数不变;卷积层希望我们通道数变大。

 展平(Flatten)操作,然后接全连接层:

过拟合(Overfitting) 是机器学习中常见的一种现象,指的是模型在训练数据上表现得非常好,但在新的、未见过的测试数据上表现较差的情况。简而言之,过拟合是指模型过于复杂,以至于它不仅学习到了数据中的真实规律,也学习到了训练数据中的噪声和细节,导致模型的泛化能力下降。

Dropout操作防止神经网络过拟合。 

Dropout操作可以设置一个比例,是这个比例的神经元失活。

加速训练

防止过拟合

Dropout是一种常用的正则化技术,广泛应用于训练深度神经网络,目的是防止过拟合。它通过在训练过程中随机地“丢弃”神经网络中的部分神经元,使得模型不依赖于某些特定的神经元,从而提高模型的泛化能力。

图像增强 

 

PCA(主成分分析,Principal Component Analysis)是一种常用的降维技术,主要用于数据预处理、特征提取、数据可视化等领域。PCA的目的是将数据从高维空间映射到低维空间,同时保留尽可能多的原始数据的方差和信息,减少数据的复杂度。

 LRN正则化

LRN(Local Response Normalization,局部响应归一化) 是一种正则化技术,最早由 AlexNet 中引入,用于增强神经网络在训练过程中的泛化能力,尤其是在卷积神经网络(CNN)中。LRN的目的是通过归一化神经元的响应来增加网络对特定输入的敏感度,帮助网络更好地学习到有效的特征。

在VGG使用此方法发现没有很大提升,故不再使用。

总结

重叠池化增加了特征的丰富性。

2.实战

2.1LeNet实战

新建LeNet目录

导入pytorch环境,具体环境配置可以看炮哥视频。

新建model.py文件 ,main.py文件没有用可以直接删除。

model.py

import torch
from torch import nn    # nn 提供了神经网络层、损失函数、优化器、激活函数等常用操作
from torchsummary import summary    # torchsummary是一个用于查看模型参数量的库


class LeNet(nn.Module):  # 通过继承 nn.Module 类,用户可以创建自己的神经网络模型。
    def __init__(self):  # 初始化(网络层、激活函数),网络前向传播
        super(LeNet, self).__init__()   # 模板,对应
        self.c1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2)  # 步幅为1,默认,可以不写
        self.sig = nn.Sigmoid()  # 激活函数
        self.s2 = nn.AvgPool2d(kernel_size=2, stride=2)  # 池化层
        self.c3 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
        self.s4 = nn.AvgPool2d(kernel_size=2,stride=2)

        self.flatten = nn.Flatten()
        self.f5 = nn.Linear(400,120)
        self.f6 = nn.Linear(120,84)
        self.f7 = nn.Linear(84,10)
    def forward(self, x):
        x = self.sig(self.c1(x))
        x = self.s2(x)
        x = self.sig(self.c3(x))
        x = self.s4(x)
        x = self.flatten(x)
        x = self.f5(x)
        x = self.f6(x)
        x = self.f7(x)
        return x

# 判断当前是否为主程序执行,只有当该脚本被直接运行时,才会执行下面的代码
if __name__ == "__main__" :
    # 判断是否有可用的GPU,如果有则使用GPU(cuda),否则使用CPU
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model = LeNet().to(device)  # 实例化LeNet模型,并将其移动到指定的设备(GPU或CPU)
    print(summary(model, (1, 28, 28)))  # 使用summary函数打印模型的摘要信息,输入的张量大小为 (1, 28, 28),即单通道28x28的图像

 plot.py

from torchvision.datasets import FashionMNIST   # torchvision.datasets提供了多种常用的数据集接口,特别是用于图像处理和计算机视觉任务的数据集。
from torchvision import transforms  # torchvision.transforms提供了一般的图像转换操作类。
import torch.utils.data as Data  # torch.utils.data模块提供了用于加载和处理数据的工具。
import numpy as np
import matplotlib.pyplot as plt # matplotlib.pyplot提供了类似MATLAB的绘图API。

# 使用 FashionMNIST 数据集类加载训练数据集,并进行图像预处理和转换
train_data = FashionMNIST(root='./data',  # 指定数据集下载或保存的路径,这里为当前目录下的 './data'
                          train=True,   # 加载训练集。如果设置为 False,则加载测试集
                          transform=transforms.Compose([    # 进行数据预处理和转换
                              transforms.Resize(size=224),  # 将图像的大小调整为 224x224 像素
                              transforms.ToTensor()]),  # 将图像转换为 PyTorch 张量
                          download=True)  # 如果指定的目录下没有数据集,下载数据集


train_loader = Data.DataLoader(dataset=train_data,  # 加载的数据集
                               batch_size=64,   # 指定每个小批次包含的样本数量
                               shuffle=True,  # 是否打乱样本的顺序
                               num_workers=0)


# 获取一个Batch的数据
for step, (b_x, b_y) in enumerate(train_loader):    # 每次加载一个小批次的数据
    if step > 0:
        break
batch_x = b_x.squeeze().numpy()  # 将四维张量移除第一维,并转换为 numpy 数组
batch_y = b_y.numpy()  # 将张量转换为 numpy 数组
class_label = train_data.classes  # 获取数据集的类别标签
# print(class_label)


# 可视化一个Batch的图像
plt.figure(figsize=(12, 5))  # 创建一个图像窗口
for ii in np.arange(len(batch_y)):  # 遍历每个样本
    plt.subplot(4, 16, ii + 1)  # 在图像窗口中创建一个子图
    plt.imshow(batch_x[ii, :, :], cmap=plt.cm.gray)  # 显示图像
    plt.title(class_label[batch_y[ii]], size=10)    # 设置子图标题
    plt.axis('off')   # 关闭坐标轴
    plt.subplots_adjust(wspace=0.05)    # 设置子图之间的水平间距
plt.show()  # 显示图像
    

 这段代码主要完成以下几项任务:

  1. FashionMNIST 数据集加载训练数据,并对每个图像进行预处理(如调整大小、转换为张量)。
  2. 使用 DataLoader 将数据按批次加载,每个批次包含 64 张图像。
  3. 可视化加载的第一个批次图像,通过 matplotlib 显示图像,并标注每个图像所属的类别。

这对于深度学习项目中的数据加载和可视化部分非常有帮助,帮助我们检查和理解数据的格式和预处理效果。

 model_train.py

  1. 导入必要的库

    • 导入了 PyTorch、Torchvision、Matplotlib 等常用库,用于模型训练、数据处理和结果可视化。
  2. train_val_data_process 函数

    • 下载并加载 FashionMNIST 数据集。
    • 对数据进行预处理,分割为训练集和验证集。
    • 返回训练和验证数据的 DataLoader,用于批量加载数据。
  3. train_model_process 函数

    • 设置设备(GPU 或 CPU)。
    • 使用 Adam 优化器和交叉熵损失函数。
    • 在多个 epoch 上进行模型训练,计算训练和验证集的损失及准确率。
    • 保存最优模型(准确率最高时的权重)。
  4. matplot_acc_loss 函数

    • 绘制训练过程中的损失(loss)和准确率(accuracy)变化图。
  5. __main__ 部分

    • 实例化 LeNet 模型。
    • 调用数据处理函数获取 DataLoader。
    • 训练模型并获取训练过程中的数据。
    • 可视化训练和验证的损失与准确率。
import copy
import time

import torch
from torchvision.datasets import FashionMNIST   # torchvision.datasets提供了多种常用的数据集接口,特别是用于图像处理和计算机视觉任务的数据集。
from torchvision import transforms  # torchvision.transforms提供了一般的图像转换操作类。
import torch.utils.data as Data  # torch.utils.data模块提供了用于加载和处理数据的工具。
import numpy as np
import matplotlib.pyplot as plt  # matplotlib.pyplot提供了类似MATLAB的绘图API。
from model import LeNet  # 从model.py中导入LeNet模型
import torch.nn as nn
import pandas as pd

def train_val_data_process():
    train_data = FashionMNIST(root='./data',  # 指定数据集下载或保存的路径,这里为当前目录下的 './data'
                              train=True,  # 加载训练集。如果设置为 False,则加载测试集
                              transform=transforms.Compose([  # 进行数据预处理和转换
                                  transforms.Resize(size=28),
                                  transforms.ToTensor()]),  # 将图像转换为 PyTorch 张量
                              download=True)  # 如果指定的目录下没有数据集,下载数据集
    train_data, val_data = Data.random_split(train_data, [round(0.8*len(train_data)), round(0.2*len(train_data))])

    train_dataloader = Data.DataLoader(dataset=train_data,
                                       batch_size=32,
                                       shuffle=True,
                                       num_workers=2)
    val_dataloader = Data.DataLoader(dataset=val_data,
                                       batch_size=32,
                                       shuffle=True,
                                       num_workers=2)

    return train_dataloader, val_dataloader


# 模型训练
def train_model_process(model, train_dataloader, val_dataloader, num_epochs):
    # 设定训练所用到的设备,有GPU则使用GPU,否则使用CPU
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  # 使用Adam优化器,学习率为0.001
    criterion = nn.CrossEntropyLoss()  # 使用交叉熵损失函数(回归一般使用均方误差损失函数,分类一般使用交叉熵损失函数)
    # 将模型放入到训练设备中
    model = model.to(device)
    # 复制当前模型的参数
    best_model_wts = copy.deepcopy(model.state_dict())

    # 初始化参数
    # 最高准确度
    best_acc = 0.0
    # 训练集损失列表
    train_loss_all = []
    # 验证集损失列表
    val_loss_all = []
    # 训练集准确度列表
    train_acc_all = []
    # 验证集准确度列表
    val_acc_all = []
    # 当前时间
    since = time.time()

    for epoch in range(num_epochs):
        print("Epoch {}/{}".format(epoch, num_epochs - 1))
        print("-"*10)

        # 初始化参数
        # 训练集损失函数
        train_loss = 0.0
        # 训练集准确度
        train_corrects = 0
        # 验证集损失函数
        val_loss = 0.0
        # 验证集准确度
        val_corrects = 0
        # 训练集样本数量
        train_num = 0
        # 验证集样本数量
        val_num = 0

        #对每一个mini-batch训练和计算
        for step, (b_x, b_y) in enumerate(train_dataloader):
            # 将特征放到训练设备中
            b_x = b_x.to(device)
            # 将标签放到训练设备中
            b_y = b_y.to(device)
            # 设置模型为训练模式
            model.train()

            # 前向传播过程,输入为一个batch,输出为一个batch中对应的预测
            output = model(b_x)
            # 查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output, dim=1)
            # 计算每一个batch的损失函数
            loss = criterion(output, b_y)

            # 将梯度初始化为0,每一轮都要初始化,防止梯度累积
            optimizer.zero_grad()
            # 反向传播,计算梯度
            loss.backward()
            # 根据网络反向传播的梯度信息更新网络的参数,以起到降低loss函数计算值的作用
            optimizer.step()
            # 对损失函数进行累加
            train_loss += loss.item() * b_x.size(0)
            # 如果预测正确,则准确值train_corrects加1
            train_corrects += torch.sum(pre_lab == b_y.data)
            # 当前用于训练的样本数量
            train_num += b_x.size(0)
        for step, (b_x, b_y) in enumerate(val_dataloader):
            # 将特征放到验证设备中
            b_x = b_x.to(device)
            # 将标签放到验证设备中
            b_y = b_y.to(device)
            # 设置模型为评估模式
            model.eval()
            # 前向传播过程,输入为一个batch,输出为一个batch中对应的预测
            output = model(b_x)
            # 查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output, dim=1)
            # 计算每一个batch的损失函数
            loss = criterion(output, b_y)

            # 对损失函数进行累加
            val_loss += loss.item() * b_x.size(0)
            # 如果预测正确,则准确值train_corrects加1
            val_corrects += torch.sum(pre_lab == b_y.data)
            # 当前用于验证的样本数量
            val_num += b_x.size(0)


        # 计算并保存每一次迭代的loss值和准确值
        # 计算并保存训练集的loss值
        train_loss_all.append(train_loss / train_num)
        # 计算并保存训练集的准确率
        train_acc_all.append(train_corrects.double().item() / train_num)

        # 计算并保存验证集的loss值
        val_loss_all.append(val_loss / val_num)
        # 计算并保存验证集的准确率
        val_acc_all.append(val_corrects.double().item() / val_num)
        print("{} train loss:{:.4f} train acc:{:.4f}".format(epoch, train_loss_all[-1], train_acc_all[-1]))
        print("{} val loss:{:.4f} val acc:{:.4f}".format(epoch, val_loss_all[-1], val_acc_all[-1]))

        # 寻找最高准确度的权重
        if val_acc_all[-1] > best_acc:
            # 保存当前的最高准确度
            best_acc = val_acc_all[-1]
            # 保存当前最高准确度对应的模型参数
            best_model_wts = copy.deepcopy(model.state_dict())
        # 训练耗费时间
        time_use = time.time() - since
        print("训练和验证耗费时间:{:.0f}m {:.0f}s".format(time_use // 60, time_use % 60))


    # 选择最优参数
    # 加载最高准确率下的模型参数
    model.load_state_dict(best_model_wts)
    torch.save(model.load_state_dict(best_model_wts), 'D:/code/pych/pytorch_learning/LeNet/best_model.pth')

    train_process = pd.DataFrame(data={"epoch": range(num_epochs),
                                        "train_loss_all": train_loss_all,
                                        "val_loss_all": val_loss_all,
                                        "train_acc_all": train_acc_all,
                                        "val_acc_all": val_acc_all})

    return train_process

def matplot_acc_loss(train_process):
    plt.figure(figsize=(12, 4))
    plt.subplot(1, 2, 1)
    plt.plot(train_process["epoch"], train_process.train_loss_all, 'ro-', label="train_loss")
    plt.plot(train_process["epoch"], train_process.val_loss_all, 'bs-', label="val_loss")
    plt.legend()
    plt.xlabel("epoch")
    plt.ylabel("loss")

    plt.figure(figsize=(12, 4))
    plt.subplot(1, 2, 2)
    plt.plot(train_process["epoch"], train_process.train_acc_all, 'ro-', label="train_acc")
    plt.plot(train_process["epoch"], train_process.val_acc_all, 'bs-', label="val_acc")
    plt.legend()
    plt.xlabel("epoch")
    plt.ylabel("acc")
    plt.show()


if __name__ == "__main__":
    # 实例化LeNet模型
    LeNet = LeNet()
    train_dataloader, val_dataloader = train_val_data_process()
    train_process = train_model_process(LeNet, train_dataloader, val_dataloader, 20)
    matplot_acc_loss(train_process)

运行结果

Epoch 0/19
----------
0 train loss:0.9587 train acc:0.6250
0 val loss:0.6695 val acc:0.7359
训练和验证耗费时间:0m 13s
Epoch 1/19
----------
1 train loss:0.5981 train acc:0.7736
1 val loss:0.5562 val acc:0.7827
训练和验证耗费时间:0m 22s
Epoch 2/19
----------
2 train loss:0.5226 train acc:0.8082
2 val loss:0.4927 val acc:0.8230
训练和验证耗费时间:0m 31s
Epoch 3/19
----------
3 train loss:0.4777 train acc:0.8265
3 val loss:0.4582 val acc:0.8347
训练和验证耗费时间:0m 40s
Epoch 4/19
----------
4 train loss:0.4452 train acc:0.8385
4 val loss:0.4375 val acc:0.8395
训练和验证耗费时间:0m 49s
Epoch 5/19
----------
5 train loss:0.4244 train acc:0.8455
5 val loss:0.4171 val acc:0.8518
训练和验证耗费时间:0m 59s
Epoch 6/19
----------
6 train loss:0.4062 train acc:0.8518
6 val loss:0.3985 val acc:0.8556
训练和验证耗费时间:1m 8s
Epoch 7/19
----------
7 train loss:0.3913 train acc:0.8564
7 val loss:0.3985 val acc:0.8546
训练和验证耗费时间:1m 17s
Epoch 8/19
----------
8 train loss:0.3784 train acc:0.8612
8 val loss:0.3931 val acc:0.8569
训练和验证耗费时间:1m 26s
Epoch 9/19
----------
9 train loss:0.3687 train acc:0.8652
9 val loss:0.4153 val acc:0.8497
训练和验证耗费时间:1m 35s
Epoch 10/19
----------
10 train loss:0.3602 train acc:0.8687
10 val loss:0.3735 val acc:0.8645
训练和验证耗费时间:1m 45s
Epoch 11/19
----------
11 train loss:0.3520 train acc:0.8712
11 val loss:0.3538 val acc:0.8736
训练和验证耗费时间:1m 54s
Epoch 12/19
----------
12 train loss:0.3466 train acc:0.8723
12 val loss:0.3571 val acc:0.8729
训练和验证耗费时间:2m 3s
Epoch 13/19
----------
13 train loss:0.3379 train acc:0.8773
13 val loss:0.3419 val acc:0.8752
训练和验证耗费时间:2m 12s
Epoch 14/19
----------
14 train loss:0.3327 train acc:0.8791
14 val loss:0.3648 val acc:0.8695
训练和验证耗费时间:2m 21s
Epoch 15/19
----------
15 train loss:0.3296 train acc:0.8790
15 val loss:0.3286 val acc:0.8809
训练和验证耗费时间:2m 31s
Epoch 16/19
----------
16 train loss:0.3252 train acc:0.8806
16 val loss:0.3345 val acc:0.8782
训练和验证耗费时间:2m 40s
Epoch 17/19
----------
17 train loss:0.3207 train acc:0.8829
17 val loss:0.3437 val acc:0.8751
训练和验证耗费时间:2m 49s
Epoch 18/19
----------
18 train loss:0.3168 train acc:0.8825
18 val loss:0.3414 val acc:0.8734
训练和验证耗费时间:2m 58s
Epoch 19/19
----------
19 train loss:0.3120 train acc:0.8848
19 val loss:0.3399 val acc:0.8781
训练和验证耗费时间:3m 8s

Process finished with exit code 0

 model_test.py

import torch
import torch.utils.data as Data
from torchvision import transforms
from torchvision.datasets import FashionMNIST
from model import LeNet


def test_data_process():
    test_data = FashionMNIST(root='./data',  # 指定数据集下载或保存的路径,这里为当前目录下的 './data'
                              train=False,  # 加载测试集。如果设置为 False,则加载测试集
                              transform=transforms.Compose([  # 进行数据预处理和转换
                                  transforms.Resize(size=28),
                                  transforms.ToTensor()]),  # 将图像转换为 PyTorch 张量
                              download=True)

    test_dataloader = Data.DataLoader(dataset=test_data,
                                       batch_size=1,
                                       shuffle=True,
                                       num_workers=0)

    return test_dataloader

def test_model_process(model, test_dataloader):
    # 设定训练所用到的设备,有GPU则使用GPU,否则使用CPU
    device = "cuda" if torch.cuda.is_available() else "cpu"
    # 将模型放入到测试设备中
    model = model.to(device)

    # 初始化参数
    test_correct = 0.0
    test_num = 0

    # 只进行前向传播,不进行梯度计算,从而节省内存,加快运行速度
    with torch.no_grad():
        for test_data_x, test_data_y in test_dataloader:
            # 将数据放入到测试设备中
            test_data_x = test_data_x.to(device)
            # 将标签放入到测试设备中
            test_data_y = test_data_y.to(device)
            # 将模型设置为评估模式
            model.eval()
            # 前向传播过程,输入为测试数据集,输出每个样本的预测值
            output = model(test_data_x)
            # 查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output, dim=1)
            # 如果预测正确,则精确度test_correct加1
            test_correct += torch.sum(pre_lab == test_data_y.data)
            # 将所有的测试样本累加
            test_num += test_data_x.size(0)

    # 计算测试集的准确度
    test_acc = test_correct.double().item() / test_num
    print("测试的准确率为:", test_acc)



if __name__ == "__main__":
    # 加载模型
    model = LeNet()
    model.load_state_dict(torch.load("best_model.pth"))
    # 加载测试数据
    test_dataloader = test_data_process()
    # 加载模型测试的函数
    #test_model_process(model, test_dataloader)

    # 设定测试所用到的设备,有GPU则使用GPU,否则使用CPU
    device = "cuda" if torch.cuda.is_available() else "cpu"
    model = model.to(device)


    classes = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
    with torch.no_grad():
        for b_x, b_y in test_dataloader:
            b_x = b_x.to(device)
            b_y = b_y.to(device)
            model.eval()
            output = model(b_x)
            pre_lab = torch.argmax(output, dim=1)
            result = pre_lab.item()
            label = b_y.item()
            print("预测值:", classes[result], "-------", "真实值:", classes[label])

 2.2AlexNet实战

model.py

import torch
from torch import nn
from torchsummary import summary
import torch.nn.functional as F


class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()
        self.ReLU = nn.ReLU()
        self.c1 = nn.Conv2d(in_channels=1, out_channels=96, kernel_size=11, stride=4)
        self.s2 = nn.MaxPool2d(kernel_size=3, stride=2)
        self.c3 = nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, padding=2)
        self.s4 = nn.MaxPool2d(kernel_size=3, stride=2)
        self.c5 = nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, padding=1)
        self.c6 = nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, padding=1)
        self.c7 = nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, padding=1)
        self.s8 = nn.MaxPool2d(kernel_size=3, stride=2)
        self.flatten = nn.Flatten()
        self.f1 = nn.Linear(6*6*256, 4096)
        self.f2 = nn.Linear(4096, 4096)
        self.f3 = nn.Linear(4096, 10)

    def forward(self, x):
        x = self.ReLU(self.c1(x))
        x = self.s2(x)
        x = self.ReLU(self.c3(x))
        x = self.s4(x)
        x = self.ReLU(self.c5(x))
        x = self.ReLU(self.c6(x))
        x = self.ReLU(self.c7(x))
        x = self.s8(x)
        x = self.flatten(x)
        x = self.ReLU(self.f1(x))
        x = F.dropout(x, p=0.5)
        x = self.ReLU(self.f2(x))
        x = F.dropout(x, p=0.5)
        x = self.f3(x)
        return x

if __name__ == '__main__':
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model = AlexNet().to(device)
    print(summary(model, (1, 227, 227)))

model_train.py

import copy
import time

import torch
from torchvision.datasets import FashionMNIST   # torchvision.datasets提供了多种常用的数据集接口,特别是用于图像处理和计算机视觉任务的数据集。
from torchvision import transforms  # torchvision.transforms提供了一般的图像转换操作类。
import torch.utils.data as Data  # torch.utils.data模块提供了用于加载和处理数据的工具。
import numpy as np
import matplotlib.pyplot as plt  # matplotlib.pyplot提供了类似MATLAB的绘图API。
from model import AlexNet  # 从model.py中导入AlexNet模型
import torch.nn as nn
import pandas as pd

def train_val_data_process():
    train_data = FashionMNIST(root='./data',  # 指定数据集下载或保存的路径,这里为当前目录下的 './data'
                              train=True,  # 加载训练集。如果设置为 False,则加载测试集
                              transform=transforms.Compose([  # 进行数据预处理和转换
                                  transforms.Resize(size=227),
                                  transforms.ToTensor()]),  # 将图像转换为 PyTorch 张量
                              download=True)  # 如果指定的目录下没有数据集,下载数据集
    train_data, val_data = Data.random_split(train_data, [round(0.8*len(train_data)), round(0.2*len(train_data))])

    train_dataloader = Data.DataLoader(dataset=train_data,
                                       batch_size=32,
                                       shuffle=True,
                                       num_workers=2)
    val_dataloader = Data.DataLoader(dataset=val_data,
                                       batch_size=32,
                                       shuffle=True,
                                       num_workers=2)

    return train_dataloader, val_dataloader


# 模型训练
def train_model_process(model, train_dataloader, val_dataloader, num_epochs):
    # 设定训练所用到的设备,有GPU则使用GPU,否则使用CPU
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  # 使用Adam优化器,学习率为0.001
    criterion = nn.CrossEntropyLoss()  # 使用交叉熵损失函数(回归一般使用均方误差损失函数,分类一般使用交叉熵损失函数)
    # 将模型放入到训练设备中
    model = model.to(device)
    # 复制当前模型的参数
    best_model_wts = copy.deepcopy(model.state_dict())

    # 初始化参数
    # 最高准确度
    best_acc = 0.0
    # 训练集损失列表
    train_loss_all = []
    # 验证集损失列表
    val_loss_all = []
    # 训练集准确度列表
    train_acc_all = []
    # 验证集准确度列表
    val_acc_all = []
    # 当前时间
    since = time.time()

    for epoch in range(num_epochs):
        print("Epoch {}/{}".format(epoch, num_epochs - 1))
        print("-"*10)

        # 初始化参数
        # 训练集损失函数
        train_loss = 0.0
        # 训练集准确度
        train_corrects = 0
        # 验证集损失函数
        val_loss = 0.0
        # 验证集准确度
        val_corrects = 0
        # 训练集样本数量
        train_num = 0
        # 验证集样本数量
        val_num = 0

        #对每一个mini-batch训练和计算
        for step, (b_x, b_y) in enumerate(train_dataloader):
            # 将特征放到训练设备中
            b_x = b_x.to(device)
            # 将标签放到训练设备中
            b_y = b_y.to(device)
            # 设置模型为训练模式
            model.train()

            # 前向传播过程,输入为一个batch,输出为一个batch中对应的预测
            output = model(b_x)
            # 查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output, dim=1)
            # 计算每一个batch的损失函数
            loss = criterion(output, b_y)

            # 将梯度初始化为0,每一轮都要初始化,防止梯度累积
            optimizer.zero_grad()
            # 反向传播,计算梯度
            loss.backward()
            # 根据网络反向传播的梯度信息更新网络的参数,以起到降低loss函数计算值的作用
            optimizer.step()
            # 对损失函数进行累加
            train_loss += loss.item() * b_x.size(0)
            # 如果预测正确,则准确值train_corrects加1
            train_corrects += torch.sum(pre_lab == b_y.data)
            # 当前用于训练的样本数量
            train_num += b_x.size(0)
        for step, (b_x, b_y) in enumerate(val_dataloader):
            # 将特征放到验证设备中
            b_x = b_x.to(device)
            # 将标签放到验证设备中
            b_y = b_y.to(device)
            # 设置模型为评估模式
            model.eval()
            # 前向传播过程,输入为一个batch,输出为一个batch中对应的预测
            output = model(b_x)
            # 查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output, dim=1)
            # 计算每一个batch的损失函数
            loss = criterion(output, b_y)

            # 对损失函数进行累加
            val_loss += loss.item() * b_x.size(0)
            # 如果预测正确,则准确值train_corrects加1
            val_corrects += torch.sum(pre_lab == b_y.data)
            # 当前用于验证的样本数量
            val_num += b_x.size(0)


        # 计算并保存每一次迭代的loss值和准确值
        # 计算并保存训练集的loss值
        train_loss_all.append(train_loss / train_num)
        # 计算并保存训练集的准确率
        train_acc_all.append(train_corrects.double().item() / train_num)

        # 计算并保存验证集的loss值
        val_loss_all.append(val_loss / val_num)
        # 计算并保存验证集的准确率
        val_acc_all.append(val_corrects.double().item() / val_num)
        print("{} train loss:{:.4f} train acc:{:.4f}".format(epoch, train_loss_all[-1], train_acc_all[-1]))
        print("{} val loss:{:.4f} val acc:{:.4f}".format(epoch, val_loss_all[-1], val_acc_all[-1]))

        # 寻找最高准确度的权重
        if val_acc_all[-1] > best_acc:
            # 保存当前的最高准确度
            best_acc = val_acc_all[-1]
            # 保存当前最高准确度对应的模型参数
            best_model_wts = copy.deepcopy(model.state_dict())
        # 训练耗费时间
        time_use = time.time() - since
        print("训练和验证耗费时间:{:.0f}m {:.0f}s".format(time_use // 60, time_use % 60))


    # 选择最优参数
    # 加载最高准确率下的模型参数
    torch.save(best_model_wts, 'D:/code/pych/pytorch_learning/AlexNet/best_model.pth')

    train_process = pd.DataFrame(data={"epoch": range(num_epochs),
                                        "train_loss_all": train_loss_all,
                                        "val_loss_all": val_loss_all,
                                        "train_acc_all": train_acc_all,
                                        "val_acc_all": val_acc_all})

    return train_process

def matplot_acc_loss(train_process):
    plt.figure(figsize=(12, 4))
    plt.subplot(1, 2, 1)
    plt.plot(train_process["epoch"], train_process.train_loss_all, 'ro-', label="train_loss")
    plt.plot(train_process["epoch"], train_process.val_loss_all, 'bs-', label="val_loss")
    plt.legend()
    plt.xlabel("epoch")
    plt.ylabel("loss")

    plt.figure(figsize=(12, 4))
    plt.subplot(1, 2, 2)
    plt.plot(train_process["epoch"], train_process.train_acc_all, 'ro-', label="train_acc")
    plt.plot(train_process["epoch"], train_process.val_acc_all, 'bs-', label="val_acc")
    plt.legend()
    plt.xlabel("epoch")
    plt.ylabel("acc")
    plt.show()


if __name__ == "__main__":
    # 实例化LeNet模型
    AlexNet = AlexNet()
    train_dataloader, val_dataloader = train_val_data_process()
    train_process = train_model_process(AlexNet, train_dataloader, val_dataloader, 20)
    matplot_acc_loss(train_process)

model_test.py

import torch
import torch.utils.data as Data
from torchvision import transforms
from torchvision.datasets import FashionMNIST
from model import AlexNet


def test_data_process():
    test_data = FashionMNIST(root='./data',  # 指定数据集下载或保存的路径,这里为当前目录下的 './data'
                              train=False,  # 加载测试集。如果设置为 False,则加载测试集
                              transform=transforms.Compose([  # 进行数据预处理和转换
                                  transforms.Resize(size=227),
                                  transforms.ToTensor()]),  # 将图像转换为 PyTorch 张量
                              download=True)

    test_dataloader = Data.DataLoader(dataset=test_data,
                                       batch_size=1,
                                       shuffle=True,
                                       num_workers=0)

    return test_dataloader

def test_model_process(model, test_dataloader):
    # 设定训练所用到的设备,有GPU则使用GPU,否则使用CPU
    device = "cuda" if torch.cuda.is_available() else "cpu"
    # 将模型放入到测试设备中
    model = model.to(device)

    # 初始化参数
    test_correct = 0.0
    test_num = 0

    # 只进行前向传播,不进行梯度计算,从而节省内存,加快运行速度
    with torch.no_grad():
        for test_data_x, test_data_y in test_dataloader:
            # 将数据放入到测试设备中
            test_data_x = test_data_x.to(device)
            # 将标签放入到测试设备中
            test_data_y = test_data_y.to(device)
            # 将模型设置为评估模式
            model.eval()
            # 前向传播过程,输入为测试数据集,输出每个样本的预测值
            output = model(test_data_x)
            # 查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output, dim=1)
            # 如果预测正确,则精确度test_correct加1
            test_correct += torch.sum(pre_lab == test_data_y.data)
            # 将所有的测试样本累加
            test_num += test_data_x.size(0)

    # 计算测试集的准确度
    test_acc = test_correct.double().item() / test_num
    print("测试的准确率为:", test_acc)



if __name__ == "__main__":
    # 加载模型
    model = AlexNet()
    model.load_state_dict(torch.load("best_model.pth"))
    # 加载测试数据
    test_dataloader = test_data_process()
    # 加载模型测试的函数
    #test_model_process(model, test_dataloader)

    # 设定测试所用到的设备,有GPU则使用GPU,否则使用CPU
    device = "cuda" if torch.cuda.is_available() else "cpu"
    model = model.to(device)


    classes = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
    with torch.no_grad():
        for b_x, b_y in test_dataloader:
            b_x = b_x.to(device)
            b_y = b_y.to(device)
            model.eval()
            output = model(b_x)
            pre_lab = torch.argmax(output, dim=1)
            result = pre_lab.item()
            label = b_y.item()
            print("预测值:", classes[result], "-------", "真实值:", classes[label])

 model_train.py和model_test.py可以移植使用,只需更改部分,model.py需要我们每次去构建。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值