LeNet-5网络训练

Python写程序注意缩进!!!
视频学习的源代码:
学习B站up主:炮哥带你学

#加载数据,并进行可视化,loss,准确率等的展示
import copy
import time

import pandas as pd
import torch
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.utils.data as Data
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
#从model.py导入我们的模型
from model import LeNet

#训练集和验证集的定义,名称尽可能一目了然,用于处理训练集和验证集
def train_val_data_process():
    ##数据加载进来,transforms.Compose((transforms.Resize(size=28),大小设置按照模型的输入要求来
    train_data = FashionMNIST(root='./data',
                              train=True,
                              transform=transforms.Compose((transforms.Resize(size=28), transforms.ToTensor())),
                              download=True)
    #把数据集划分为训练集和验证集=8:2
    train_data,val_data = Data.random_split(train_data,[round(0.8*len(train_data)),round(0.2*len(train_data))])
    #先放入数据,一批次数量(数据量比较小,6G运存应该是够的),shuffle是否打乱数据,进程是8,电脑垃圾的话,就改小一点
    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,没有GPU用CPU
    #告诉设备情况,这一步用到了torch,就需要在前面进行导入,就是这么一个过程,一开始不知道都用那些库,写着需要了就进行导入
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    #使用Adam优化器,便于后续的参数进行更新的,lr表示为学习率,Adam可以理解是梯度下降法的一种优化改良
    optimizer = torch.optim.Adam(model.parameters(),lr=0.001)
    #定义损失函数,在分类问题中一般损失函数用交叉熵损失函数,回归一般用均方差损失函数,来利用损失值来更新参数w和b
    criterion = nn.CrossEntropyLoss()
    #将模型放入到训练设备中
    model = model.to(device)

    #复制当前模型的参数,w,b会有一个初始化的值,随着反向传播的进行,w,b在进行不断的更新。后续把 best_model_wts保存下来(也即是权重)后续测试时可以进行加载

    best_model_wts = copy.deepcopy(model.state_dict())
    ##初始化参数
    ##最高准确度,初始化为0,后续会覆盖掉
    best_acc = 0.0

    #训练集损失值列表
    train_loss_all=[]
    # 验证集损失值列表
    val_loss_all = []
    # 训练集精度值列表
    train_acc_all = []
    # 验证集精度值/准确度列表
    val_acc_all = []

    #保存当前时间,方便知道一轮会用时多久,time.time()不要自己全部手敲,会报错,就time.等弹出选择即可
    since = time.time()

    #利用循环进行训练,打印信息的阶段
    for epoch in range(num_epochs):
        print("Epoch{}/{}".format(epoch,num_epochs-1))   #减1的原因在于,是从0开始99结束,共100轮(如果以100为例情况下)
        print("-"*10)

        #初始化参数,方便每一轮的开始值都是0,从而可以计算每一轮的loss和准确率,需要计算平均loss值,就需要定义样本数量
        #训练集损失函数
        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不是最终的结果而是一个向量
            output = model(b_x)

            #使用softmax取概率最大的作为标签,查找每一行中最大值对应的行标(类别)
            pre_lab = torch.argmax(output,dim=1)
            #利用模型的输出和标签来计算损失,不是通过softmax之后的结果和标签计算哦
            loss = criterion(output,b_y)

            #每一轮开始前将梯度初始化为0,防止梯度累计从而对参数更新产生干扰,只有这样每一轮结束之后才能更新参数w和b
            optimizer.zero_grad()
            #反向传播计算
            loss.backward()
            #根据网络反向传播的梯度信息来更新网络参数,以起到降低loss值函数计算值的作用,利用Adam优化器进行更新参数
            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)

            #模型验证代码部分撰写,基本和训练过程是差不多的操作,是不参与模型的训练,单纯计算loss和准确度,没有反向传播过程
            # 对每一个mini-batch训练和计算,直到最后一批次结束,意味着一轮结束。
        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)
              # 使用softmax取概率最大的作为标签,查找每一行中最大值对应的行标(类别)
             pre_lab = torch.argmax(output, dim=1)
             # 利用模型的输出和标签来计算损失,不是通过softmax之后的结果和标签计算哦
            ##相比较于训练过程,是没有反向传播,梯度更新,参数更新的过程。
             loss = criterion(output, b_y)
              #对损失函数进行累加,批次的平均loss*批次数量
             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)

              #计算并保存每一次迭代(轮次epoch?)的平均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)

              ##将上述结果进行打印[-1]表示取当前轮次的数值,print缩进的话,把内部训练的每一次都会进行打印的。
        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}s".format(time_use//60,time_use%60))

    #选择最优参数
    #加载最高准确率下的模型参数,并进行保存到一个文件地址,项目文件夹下产生一个best_model.pth,.pth是权重的独特后缀
    model.load_state_dict(best_model_wts)
    torch.save(model.load_state_dict(best_model_wts),'C:/Users/mcg/Desktop/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
#2024年7月22日继续学习
##定义一个绘制loss和acc的绘制图的函数,将train_process作为输入传进去
def matplot_acc_loss(train_process):
    plt.figure(figsize=(12,4))     #设置图形的大小
    plt.subplot(1,2,1)        #子图可以按照matlab的画法进行理解
    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.subplot(1, 2, 2)  # 子图可以按照matlab的画法进行理解
    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.legend()  # 图例
    plt.show()    #绘制

#对数据开始训练
#主函数,开始运行所有的代码   注意点1:双等号。2.要有冒号:
if __name__ == "__main__":
     #将模型进行实例化,如果后续是别的模型呢?from model import LeNet,先进行搭建我们的model.py,进而将model导入进去,然后在下面进行实例化。将模型放到训练代码就可以了。有个别的模型可能数据的预处理需要改一点。
     LeNet = LeNet()
     #加载数据集,分为训练集和验证集
     train_dataloader,val_dataloader = train_val_data_process()
     #将训练集,验证集,模型,和训练次数放进训练代码中,
     train_process = train_model_process(LeNet,train_dataloader,val_dataloader,20)    #自己进行设定训练的次数,假如这里的训练次数是20轮,并保留中间值
     ##绘制损失和准确率的图形
     matplot_acc_loss(train_process)

结果显示:
出现报错:
在这里插入图片描述
改了之后的代码

#加载数据,并进行可视化,loss,准确率等的展示
import copy
import time

import pandas as pd
import torch
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.utils.data as Data
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
#从model.py导入我们的模型
from model import LeNet

#训练集和验证集的定义,名称尽可能一目了然,用于处理训练集和验证集
def train_val_data_process():
    ##数据加载进来,transforms.Compose((transforms.Resize(size=28),大小设置按照模型的输入要求来
    train_data = FashionMNIST(root='./data',
                              train=True,
                              transform=transforms.Compose((transforms.Resize(size=28), transforms.ToTensor())),
                              download=True)
    #把数据集划分为训练集和验证集=8:2
    train_data,val_data = Data.random_split(train_data,[round(0.8*len(train_data)),round(0.2*len(train_data))])
    #先放入数据,一批次数量(数据量比较小,6G运存应该是够的),shuffle是否打乱数据,进程是8,电脑垃圾的话,就改小一点
    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,没有GPU用CPU
    #告诉设备情况,这一步用到了torch,就需要在前面进行导入,就是这么一个过程,一开始不知道都用那些库,写着需要了就进行导入
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    #使用Adam优化器,便于后续的参数进行更新的,lr表示为学习率,Adam可以理解是梯度下降法的一种优化改良
    optimizer = torch.optim.Adam(model.parameters(),lr=0.001)
    #定义损失函数,在分类问题中一般损失函数用交叉熵损失函数,回归一般用均方差损失函数,来利用损失值来更新参数w和b
    criterion = nn.CrossEntropyLoss()
    #将模型放入到训练设备中
    model = model.to(device)

    #复制当前模型的参数,w,b会有一个初始化的值,随着反向传播的进行,w,b在进行不断的更新。后续把 best_model_wts保存下来(也即是权重)后续测试时可以进行加载

    best_model_wts = copy.deepcopy(model.state_dict())
    ##初始化参数
    ##最高准确度,初始化为0,后续会覆盖掉
    best_acc = 0.0

    #训练集损失值列表,改的地方
    train_loss_all=[0] * num_epochs
    # 验证集损失值列表
    val_loss_all = [0] * num_epochs
    # 训练集精度值列表
    train_acc_all = [0] * num_epochs
    # 验证集精度值/准确度列表
    val_acc_all = [0] * num_epochs
    #保存当前时间,方便知道一轮会用时多久,time.time()不要自己全部手敲,会报错,就time.等弹出选择即可
    since = time.time()

    #利用循环进行训练,打印信息的阶段
    for epoch in range(num_epochs):
        print("Epoch{}/{}".format(epoch,num_epochs-1))   #减1的原因在于,是从0开始99结束,共100轮(如果以100为例情况下)
        print("-"*10)

    #初始化参数,方便每一轮的开始值都是0,从而可以计算每一轮的loss和准确率,需要计算平均loss值,就需要定义样本数量
        #训练集损失函数
        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不是最终的结果而是一个向量
            output = model(b_x)

            #使用softmax取概率最大的作为标签,查找每一行中最大值对应的行标(类别)
            pre_lab = torch.argmax(output,dim=1)
            #利用模型的输出和标签来计算损失,不是通过softmax之后的结果和标签计算哦
            loss = criterion(output,b_y)

            #每一轮开始前将梯度初始化为0,防止梯度累计从而对参数更新产生干扰,只有这样每一轮结束之后才能更新参数w和b
            optimizer.zero_grad()
            #反向传播计算
            loss.backward()
            #根据网络反向传播的梯度信息来更新网络参数,以起到降低loss值函数计算值的作用,利用Adam优化器进行更新参数
            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)

            #模型验证代码部分撰写,基本和训练过程是差不多的操作,是不参与模型的训练,单纯计算loss和准确度,没有反向传播过程
            # 对每一个mini-batch训练和计算,直到最后一批次结束,意味着一轮结束。
        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)
              # 使用softmax取概率最大的作为标签,查找每一行中最大值对应的行标(类别)
             pre_lab = torch.argmax(output, dim=1)
             # 利用模型的输出和标签来计算损失,不是通过softmax之后的结果和标签计算哦
            ##相比较于训练过程,是没有反向传播,梯度更新,参数更新的过程。
             loss = criterion(output, b_y)
              #对损失函数进行累加,批次的平均loss*批次数量
             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)

              #计算并保存每一次迭代(轮次epoch?)的平均loss和准确度,后续需要进行打印或者绘图
             train_loss_all[epoch] = train_loss / max(1, train_num)
             val_loss_all[epoch] = val_loss / max(1, val_num)
             train_acc_all[epoch] = train_corrects.double().item() / max(1, train_num)
             val_acc_all[epoch] = val_corrects.double().item() / max(1, val_num)

              ##将上述结果进行打印[-1]表示取当前轮次的数值,print缩进的话,把内部训练的每一次都会进行打印的。
        print('() Train Loss: {:.4f} Train Acc: {:.4f}'.format(epoch+1,train_loss_all[-1],train_acc_all[-1]))
        print('() Val Loss: {:.4f} Val  Acc: {:.4f}'.format(epoch+1,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}s".format(time_use//60,time_use%60))

    #选择最优参数
    #加载最高准确率下的模型参数,并进行保存到一个文件地址,项目文件夹下产生一个best_model.pth,.pth是权重的独特后缀
    model.load_state_dict(best_model_wts)
    torch.save(model.load_state_dict(best_model_wts),'C:/Users/mcg/Desktop/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
#2024年7月22日继续学习
##定义一个绘制loss和acc的绘制图的函数,将train_process作为输入传进去
def matplot_acc_loss(train_process):
    plt.figure(figsize=(12,4))     #设置图形的大小
    plt.subplot(1,2,1)        #子图可以按照matlab的画法进行理解
    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.subplot(1, 2, 2)  # 子图可以按照matlab的画法进行理解
    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.legend()  # 图例
    plt.show()    #绘制

#对数据开始训练
#主函数,开始运行所有的代码   注意点1:双等号。2.要有冒号:
if __name__ == "__main__":
     #将模型进行实例化,如果后续是别的模型呢?from model import LeNet,先进行搭建我们的model.py,进而将model导入进去,然后在下面进行实例化。将模型放到训练代码就可以了。有个别的模型可能数据的预处理需要改一点。
     LeNet = LeNet()
     #加载数据集,分为训练集和验证集
     train_dataloader,val_dataloader = train_val_data_process()
     #将训练集,验证集,模型,和训练次数放进训练代码中,
     train_process = train_model_process(LeNet,train_dataloader,val_dataloader,20)    #自己进行设定训练的次数,假如这里的训练次数是20轮,并保留中间值
     ##绘制损失和准确率的图形
     matplot_acc_loss(train_process)

结果显示:
在这里插入图片描述
视频中的结果:
在这里插入图片描述
两个代码的区别在于

#训练集损失值列表
    train_loss_all=[]
    # 验证集损失值列表
    val_loss_all = []
    # 训练集精度值列表
    train_acc_all = []
    # 验证集精度值/准确度列表
    val_acc_all = []
    
             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)

              ##将上述结果进行打印[-1]表示取当前轮次的数值,print缩进的话,把内部训练的每一次都会进行打印的。
        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]))
**改之后的:**
train_loss_all = [0] * num_epochs
val_loss_all = [0] * num_epochs
train_acc_all = [0] * num_epochs
val_acc_all = [0] * num_epochs

             train_loss_all[epoch] = train_loss / max(1, train_num)
             val_loss_all[epoch] = val_loss / max(1, val_num)
             train_acc_all[epoch] = train_corrects.double().item() / max(1, train_num)
             val_acc_all[epoch] = val_corrects.double().item() / max(1, val_num)
              ##将上述结果进行打印[-1]表示取当前轮次的数值,print缩进的话,把内部训练的每一次都会进行打印的。
        print('() Train Loss: {:.4f} Train Acc: {:.4f}'.format(epoch+1,train_loss_all[-1],train_acc_all[-1]))
        print('() Val Loss: {:.4f} Val  Acc: {:.4f}'.format(epoch+1,val_loss_all[-1], val_acc_all[-1]))

实在是想不明白为什么?
网上搜索了一下,
这里的问题可能是 train_loss_all、val_loss_all、train_acc_all 和 val_acc_all 中至少有一个列表的长度与其他列表不同。这通常是因为在循环中某些地方没有正确地累积数据。
为了解决这个问题,您需要确保在循环中正确地累积数据,并且在创建 DataFrame 之前所有列表的长度都与 num_epochs 相等。以下是一些可能的解决方案:

  1. 初始化列表: 在循环开始之前,确保所有列表都被初始化为长度为 num_epochs 的列表,并填充适当的初始值(例如 0 或 None)。
  2. 检查循环逻辑: 确保在每次迭代中都正确地向这些列表中添加数据。
  3. 打印调试信息: 在循环中添加打印语句,检查每次迭代后这些列表的长度和内容。
    关于两个代码不同地方的对比
    这两种代码片段都是初始化用于存储训练和验证过程中损失值和准确度的列表。它们之间的主要区别在于初始化列表的方式:
  4. 空列表初始化:
python
train_loss_all = []
val_loss_all = []
train_acc_all = []
val_acc_all = []

这种方式创建了四个空列表。随着训练过程的进行,你需要在每个epoch结束后手动将计算得到的损失值和准确度追加到这些列表中。例如:
python
train_loss_all.append(train_loss / train_num)
train_acc_all.append(train_acc / train_num)
2. 用特定值填充列表初始化:

python
train_loss_all = [0] * num_epochs
val_loss_all = [0] * num_epochs
train_acc_all = [0] * num_epochs
val_acc_all = [0] * num_epochs

这种方式创建了四个列表,每个列表都预先填充了 num_epochs 个元素,所有元素的初始值都是 0。这意味着每个列表的长度已经确定为 num_epochs,并且在训练过程中,你可以直接通过索引访问和更新这些列表的元素,而不需要在列表末尾追加元素。例如:

python
train_loss_all[epoch] = train_loss / train_num
train_acc_all[epoch] = train_acc / train_num

两种方法各有优缺点:
● 空列表初始化:更灵活,可以动态地追加元素,但需要手动管理列表的长度,确保在训练结束时列表的长度与 num_epochs 一致。
● 用特定值填充列表初始化:在训练开始前就确定了列表的长度,简化了在训练过程中更新列表的逻辑,因为不需要担心列表长度问题。此外,它还可以提供一个明确的错误检查点,如果某个epoch的结果没有被正确更新,那么相应的位置将会保持初始值(这里是0),这可能会在调试时提供一些线索。
在实际使用中,选择哪种方式取决于个人偏好和具体的应用场景。如果你预计在训练过程中不会动态地改变 num_epochs 的值,并且希望简化代码逻辑,那么使用填充特定值的方式可能更合适。如果你希望保留更多的灵活性,或者可能需要根据某些条件动态地调整 num_epochs,那么使用空列表初始化可能更合适。
这两段代码都是在训练和验证循环中更新损失值和准确率的列表,但它们在更新数据的方式上有所不同:

  1. 使用 append 方法:
python
train_loss_all.append(train_loss / train_num)
train_acc_all.append(train_corrects.double().item() / train_num)
val_loss_all.append(val_loss / val_num)
val_acc_all.append(val_corrects.double().item() / val_num)

这种方式是将每个epoch计算得到的平均损失和准确率作为新的元素追加到列表的末尾。这是动态地构建列表的方法,适用于在不确定最终轮次数量或者在训练过程中可能更改轮次数量的情况。
2. 直接通过索引赋值:

python
train_loss_all[epoch] = train_loss / max(1, train_num)
val_loss_all[epoch] = val_loss / max(1, val_num)
train_acc_all[epoch] = train_corrects.double().item() / max(1, train_num)
val_acc_all[epoch] = val_corrects.double().item() / max(1, val_num)

这种方式是直接在列表的特定位置(由 epoch 索引确定)存储该epoch的平均损失和准确率。这种方法适用于列表已经预先初始化为固定长度(例如,使用 [0] * num_epochs),并且你希望在每个epoch结束后立即更新相应位置的值。
两种方法的主要区别包括:
● 灵活性: 使用 append 更灵活,因为你可以在任何时候添加更多的数据点,而不需要预先知道总数。
● 性能: 对于预先知道数据点数量的情况,直接通过索引赋值可能更高效,因为它避免了列表动态扩展的开销。
● 错误处理: 使用索引赋值时,如果 epoch 的值超出了列表的索引范围,将会引发 IndexError。而使用 append 不会有这个问题。
● 除零操作: 在两种方法中,都使用了 max(1, train_num) 和 max(1, val_num) 来避免除以零的情况。这是一个很好的实践,可以防止在没有数据的批次上计算损失和准确率时发生错误。
选择哪种方法取决于具体的应用场景和个人偏好。如果训练的轮次是固定的,并且希望在训练开始前就初始化好所有列表,那么使用索引赋值可能更合适。如果轮次可能会动态变化,或者想要保持代码的简洁性,使用 append 方法可能更好。
蹲个大佬能帮忙看看问题出在了那里?
后续,在循环,条件判断等语句中,一定记得语句的缩进(有一定的逻辑关系)
在视频代码的基础上修改这两个部分即可
解决啦,晚上可以安心睡觉喽。

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}s".format(time_use//60,time_use%60))
          #计算并保存每一次迭代(轮次epoch?)的平均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)

       ##将上述结果进行打印[-1]表示取当前轮次的数值,print缩进的话,把内部训练的每一次都会进行打印的。
    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]))
    
切忌切忌,缩进,缩进,缩进。重要的事情说三遍。

在这里插入图片描述
经过这一次的报错,明白了一件事情,出现ValueError: All arrays must be of the same length,首先考虑是不是条件,循环等语句的缩进逻辑有问题,再考虑其他的,这也从另一方面说明使用python写条件,循环语句要有缩进的意识。养成良好的书写代码的习惯。
但突然又有一个疑问,为啥在改之后的版本,也就是指定了训练的轮数之后,即使条件,循环语句没有缩进,结果依然是正确的,这是为什么呢,还是很好奇。期待能有大佬们能够指点迷津。

2024.7.26更新
##单纯只求测试的准确率部分的完整代码
import torch   #用不用都先导入进来
import torch.utils.data as Data    #导入数据处理的库
from torchvision import transforms
from torchvision.datasets import FashionMNIST    #导入我们所用的数据集
from model import LeNet   #导入需要测试的模型

##数据就处理好了。
##导入数据进行模型的测试,从模型训练的代码拿一部分,改改即可,batch_size=1,希望是一张一张的去进行测试,不需要什么进程,改为num_workers=0即可
def test_data_process():
    ##数据加载进来,transforms.Compose((transforms.Resize(size=28),大小设置按照模型的输入要求来
    test_data = FashionMNIST(root='./data',
                              train=False,
                              transform=transforms.Compose((transforms.Resize(size=28), transforms.ToTensor())),
                              download=True)

    #先放入数据,一批次数量(数据量比较小,6G运存应该是够的),shuffle是否打乱数据,进程是8,电脑垃圾的话,就改小一点
    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,没有GPU用CPU
    device = "cuda" if torch.cuda.is_available() else "cpu"
    #将模型放入到测试设备当中
    model = model.to(device)
    #初始化模型参数,准确度即可,虽然已经知道测试的数据集有10000张,但为了后续不知道张数的情况下,也能进行测试。
    test_corrects = 0.0
    test_num = 0

    ##开始模型的测试,不存在反向传播,因此将梯度置为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()
            #前向传播过程,输入为测试数据集,输出为对每个样本的预测值,它其实是一个10*1的矩阵,并不能看出结果来,需要经过一个softmax得到结果。最大概率对应的下标
            output = model(test_data_x)
             #预测的类别,查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output,dim=1)
            #预测的结果和标签一样+1,如果预测正确,则 test_corrects加1,直到循环结束
            test_corrects += torch.sum(pre_lab == test_data_y.data)
            #将所有的测试样本进行累加
            test_num += test_data_x.size(0)

    #计算测试准确率,不在循环里面哦,注意缩进
    test_acc = test_corrects.double().item() / test_num

    print("测试的准确率为:" ,  test_acc)


#主函数进行运行程序
if __name__ == "__main__" :
    #加载模型
    model = LeNet()
    #torch.load('best_model.pth')加载训练好的模型参数,权重以字符串的形式, model.load_state_dict模型训练化,将实例化之后的模型加载上权重,规定的做法

    model.load_state_dict(torch.load('best_model.pth'))
    #导入测试集的数据集,加载测试数据
    test_dataloader = test_data_process()
    #加载模型测试的函数
    test_model_process(model, test_dataloader)

但在运行测试代码的时候,出现了下面的错误:
在这里插入图片描述
通过报错的信息,找到了训练部分的代码问题

    #选择最优参数
    #加载最高准确率下的模型参数,并进行保存到一个文件地址,项目文件夹下产生一个best_model.pth,.pth是权重的独特后缀
    model.load_state_dict(best_model_wts)
    torch.save(model.load_state_dict(best_model_wts),'C:/Users/mcg/Desktop/LeNet/best_model.pth')

改成

#加载最高准确率下的模型参数,并进行保存到一个文件地址,项目文件夹下产生一个best_model.pth,.pth是权重的独特后缀
    #model.load_state_dict(best_model_wts),这行代码是没有用的。下面的两种保存权重方法都是可以的
    #方法一
    torch.save(best_model_wts, 'C:/Users/mcg/Desktop/LeNet/best_model.pth')
    #方法二
    #torch.save(model.state_dict(best_model_wts), 'C:/Users/mcg/Desktop/LeNet/best_model.pth')

注意,这里用的是绝对路径,就要求路径中不含有中文符号,否则会出现一些奇怪的报错。当然,这里也可以用相对路径,只不过我暂时还没太学会。注意路径是/ 不是\,需要注意以下,改过来哦。
改了错误之后,运行程序,得到如下的结果
在这里插入图片描述
但如果还想要得到更加一些细节性的东西,比如希望把每一个测试的结果打印出来,对比每张图片的类别是什么样的,识别的准确率是多少?应该怎么做呢?

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

    #测试部分,没有梯度,不存在反向传播
    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)
            ##10个通过神经元得到数值,经过softmax函数获得对应的概率,从而获得最大值的下标
            #ToTensor()将数据格式转换为张量的形式,只有张量的数据格式,才可以去做梯度运算,反向传播等
            pre_lab = torch.argmax(output, dim=1)
            # pre_lab.item()表示将张量中的数值取出来,以便做其他的操作
            result = pre_lab.item()
            label = b_y.item()

            print("预测值:", result, "------", "真实值:", label) 

需要注意的是,进行模型推理的时候(即每一种类别的识别的时候),需要将上面测试函数中加载模型的函数部分注释掉,即

#导入测试集的数据集,加载测试数据
    test_dataloader = test_data_process()
    #加载模型测试的函数
    **#test_model_process(model, test_dataloader)**

进行测试得到如下结果:
在这里插入图片描述
只有单纯的数字,显然我们不知道对应的种类是什么,为了更加直观的看,我们需要对数字打上对应的种类标签,因此需要用表来进行定义

#使用列表取值的形式设置类别
    classes= ['T-shirt/top','Trouser','Pullover','Dress','Coat','Sandal','Shirt','Sneaker','Bag','Ankel boot']
# pre_lab.item()表示将张量中的数值取出来,以便做其他的操作(比如通过列表设置的类别,取整型方便运算,张量形式是没法进行的。
#不仅打印数值,更应该打印出对应的类别,推理之前设置有列表,用下标指向类别
            print("预测值:", classes[result], "------", "真实值:",classes[label])

结果:
在这里插入图片描述
这样便可以更加直观地进行观察模型的预测性能如何?

上述代码所涉及到的知识点:
涉及到的知识点:
1.10个通过神经元得到数值,经过softmax函数获得对应的概率,从而获得最大值的下标,torch.argmax(output, dim=1)
2.ToTensor()将数据格式转换为张量的形式,只有张量的数据格式,才可以去做梯度运算,反向传播等
具体设置在数据处理的部分
test_data = FashionMNIST(root='./data',
                          train=False,
                          transform=transforms.Compose((transforms.Resize(size=28),      transforms.ToTensor())),
                          download=True)
3.pre_lab.item()表示将张量中的数值取出来,以便做其他的操作(比如通过列表设置的类别,取整型方便运算,张量形式是没法进行的。classes[result]
4.使用列表给出类别
#使用列表取值的形式设置类别
classes= ['T-shirt/top','Trouser','Pullover','Dress','Coat','Sandal','Shirt','Sneaker','Bag','Ankel boot']
更想直观的话,可以将英文转换为中文
这里面的顺序不要变,因为在数据集训练的时候,就已经将0对应T-shirt/top'
因此需要注意,在训练自己的数据集的时候,下标对应是那个类别
写列表是时候就按顺序去写,不要乱序写,会准确率很低

一定值得注意的是,训练的时候的种类顺序,测试写标签的时候顺序不要乱写,一定要一一对应。
模型训练,测试部分的代码一定要好好去认真体会,后续将模型搭建好之后,训练和测试的部分改动的地方较少,涉及到模型的导入,数据的预处理等。
下面给出关于学习LeNet-5模型的所有完整代码,希望能和大家进行沟通交流,共同进步。
在这里插入图片描述
在这里插入图片描述

LeNet-5模型代码
#导入库,约定俗称的一些库
import torch
from torch import nn  ##导入nn,表示神经网络中的一些层,比如卷积层,池化层,激活函数等,之所以深度学习火的原因,不用太过于关注内部结构,就当成搭积木即可
from torchsummary import summary #summary展示模型的一个参数用的,前向传播的输入,输出特征图大小是多少


##约定俗称的定义网络的代码(类似于原材料)
class LeNet(nn.Module):   #定义一个类,nn.Module这样就可以用nn里面所有的网络层了
    def __init__(self):      #先进行初始化,告诉模型我们需要具备的都有了,剩下的就是开始搭建模型了。
        super(LeNet,self).__init__()      ##7-9行是约定俗成的网络结构的命令行,值得注意class LeNet和super LeNet 保持一致。

        ##开始定义第一层,后面相当于按照规划使用所需的材料,输入层不算,尽可能按照网络结构的顺序来定义每一层,不要改池化和卷积层的顺序
        #卷积层的名称可以自己去定义,以下面的c1为例,卷积函数用Conv2d(所需要传入的参数)
        #in_channels=1,表示为输入数据的通道数,1表示输入的为灰度图,3表示输入的为彩色图,out_channels=6,表示输出的通道数,由卷积核数量决定
        # kernel_size=5,代表卷积核的大小,padding=2,代表填充的大小,这里的步长为1,默认即可,不用去写
        self.c1 = nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5,padding=2)
        #定义激活函数
        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)  #步长为1,默认就不写,默认填充表示为0
        self.s4 = nn.AvgPool2d(kernel_size=2,stride=2)
        #定义平展层
        self.flatten = nn.Flatten()
        #定义全连接层,在pytorch中全连接层表示为linear(需要传入的输入的数据大小和输出的大小,即输入为平展之后的400个,输出为120个神经元)
        self.f5 = nn.Linear(400,120)   #写具体的数字会自动弹出in_features:和out_features:)
        self.f6 = nn.Linear(120,84)
        self.f7 = nn.Linear(84,10)

        ##定义前向传播并进行调用工具,self是python的语法,会自动弹出,如果没有,就要考虑是否出现问题,x表示为输入

    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的情况下,使用pytorch这个库来判别cuda是否激活了,如果激活了,返回cuda,如果没有则返回cpu
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    #模型实例化之后放到设备中
    model = LeNet().to(device)
    #打印模型的详细内容
    print(summary(model,(1,28,28)))

模型训练和验证部分

#加载数据,并进行可视化,loss,准确率等的展示
import copy
import time

import pandas as pd
import torch
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.utils.data as Data
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
#从model.py导入我们的模型
from model import LeNet

#训练集和验证集的定义,名称尽可能一目了然,用于处理训练集和验证集
def train_val_data_process():
    ##数据加载进来,transforms.Compose((transforms.Resize(size=28),大小设置按照模型的输入要求来
    train_data = FashionMNIST(root='./data',
                              train=True,
                              transform=transforms.Compose((transforms.Resize(size=28), transforms.ToTensor())),
                              download=True)
    #把数据集划分为训练集和验证集=8:2
    train_data,val_data = Data.random_split(train_data,[round(0.8*len(train_data)),round(0.2*len(train_data))])
    #先放入数据,一批次数量(数据量比较小,6G运存应该是够的),shuffle是否打乱数据,进程是8,电脑垃圾的话,就改小一点
    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,没有GPU用CPU
    #告诉设备情况,这一步用到了torch,就需要在前面进行导入,就是这么一个过程,一开始不知道都用那些库,写着需要了就进行导入
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    #使用Adam优化器,便于后续的参数进行更新的,lr表示为学习率,Adam可以理解是梯度下降法的一种优化改良
    optimizer = torch.optim.Adam(model.parameters(),lr=0.001)
    #定义损失函数,在分类问题中一般损失函数用交叉熵损失函数,回归一般用均方差损失函数,来利用损失值来更新参数w和b
    criterion = nn.CrossEntropyLoss()
    #将模型放入到训练设备中
    model = model.to(device)

    #复制当前模型的参数,w,b会有一个初始化的值,随着反向传播的进行,w,b在进行不断的更新。后续把 best_model_wts保存下来(也即是权重)后续测试时可以进行加载

    best_model_wts = copy.deepcopy(model.state_dict())
    ##初始化参数
    ##最高准确度,初始化为0,后续会覆盖掉
    best_acc = 0.0

    #训练集损失值列表,改的地方
    # 训练集损失值列表
    train_loss_all = []
    # 验证集损失值列表
    val_loss_all = []
    # 训练集精度值列表
    train_acc_all = []
    # 验证集精度值/准确度列表
    val_acc_all = []
    #保存当前时间,方便知道一轮会用时多久,time.time()不要自己全部手敲,会报错,就time.等弹出选择即可
    since = time.time()

    #利用循环进行训练,打印信息的阶段
    for epoch in range(num_epochs):
        print("Epoch{}/{}".format(epoch,num_epochs-1))   #减1的原因在于,是从0开始99结束,共100轮(如果以100为例情况下)
        print("-"*10)

        #初始化参数,方便每一轮的开始值都是0,从而可以计算每一轮的loss和准确率,需要计算平均loss值,就需要定义样本数量
        #训练集损失函数
        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不是最终的结果而是一个向量
            output = model(b_x)

            #使用softmax取概率最大的作为标签,查找每一行中最大值对应的行标(类别)
            pre_lab = torch.argmax(output,dim=1)
            #利用模型的输出和标签来计算损失,不是通过softmax之后的结果和标签计算哦
            loss = criterion(output,b_y)

            #每一轮开始前将梯度初始化为0,防止梯度累计从而对参数更新产生干扰,只有这样每一轮结束之后才能更新参数w和b
            optimizer.zero_grad()
            #反向传播计算
            loss.backward()
            #根据网络反向传播的梯度信息来更新网络参数,以起到降低loss值函数计算值的作用,利用Adam优化器进行更新参数
            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)

            #模型验证代码部分撰写,基本和训练过程是差不多的操作,是不参与模型的训练,单纯计算loss和准确度,没有反向传播过程
            # 对每一个mini-batch训练和计算,直到最后一批次结束,意味着一轮结束。
        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)
              # 使用softmax取概率最大的作为标签,查找每一行中最大值对应的行标(类别)
             pre_lab = torch.argmax(output, dim=1)
             # 利用模型的输出和标签来计算损失,不是通过softmax之后的结果和标签计算哦
            ##相比较于训练过程,是没有反向传播,梯度更新,参数更新的过程。
             loss = criterion(output, b_y)
              #对损失函数进行累加,批次的平均loss*批次数量
             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)

              #计算并保存每一次迭代(轮次epoch?)的平均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)

              ##将上述结果进行打印[-1]表示取当前轮次的数值,print缩进的话,把内部训练的每一次都会进行打印的。
        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))

    #选择最优参数
    #加载最高准确率下的模型参数,并进行保存到一个文件地址,项目文件夹下产生一个best_model.pth,.pth是权重的独特后缀
    #model.load_state_dict(best_model_wts),这行代码是没有用的。下面的两种保存权重方法都是可以的
    torch.save(best_model_wts, 'C:/Users/mcg/Desktop/LeNet/best_model.pth')
    #torch.save(model.state_dict(best_model_wts), 'C:/Users/mcg/Desktop/LeNet/best_model.pth')
    #原先的,这样是不行的,torch.save(model.load_state_dict(best_model_wts),'C:/Users/mcg/Desktop/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
#2024年7月22日继续学习
##定义一个绘制loss和acc的绘制图的函数,将train_process作为输入传进去
def matplot_acc_loss(train_process):
    plt.figure(figsize=(12,4))     #设置图形的大小
    plt.subplot(1,2,1)        #子图可以按照matlab的画法进行理解
    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.subplot(1, 2, 2)  # 子图可以按照matlab的画法进行理解
    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.legend()  # 图例
    plt.show()    #绘制

#对数据开始训练
#主函数,开始运行所有的代码   注意点1:双等号。2.要有冒号:
if __name__ == "__main__":
     #将模型进行实例化,如果后续是别的模型呢?from model import LeNet,先进行搭建我们的model.py,进而将model导入进去,然后在下面进行实例化。将模型放到训练代码就可以了。有个别的模型可能数据的预处理需要改一点。
     LeNet = LeNet()
     #加载数据集,分为训练集和验证集
     train_dataloader,val_dataloader = train_val_data_process()
     #将训练集,验证集,模型,和训练次数放进训练代码中,
     train_process = train_model_process(LeNet,train_dataloader,val_dataloader,20)    #自己进行设定训练的次数,假如这里的训练次数是20轮,并保留中间值
     ##绘制损失和准确率的图形
     matplot_acc_loss(train_process)

模型测试部分

import torch   #用不用都先导入进来
import torch.utils.data as Data    #导入数据处理的库
from torchvision import transforms
from torchvision.datasets import FashionMNIST    #导入我们所用的数据集
from model import LeNet   #导入需要测试的模型

##数据就处理好了。
##导入数据进行模型的测试,从模型训练的代码拿一部分,改改即可,batch_size=1,希望是一张一张的去进行测试,不需要什么进程,改为num_workers=0即可
def test_data_process():
    ##数据加载进来,transforms.Compose((transforms.Resize(size=28),大小设置按照模型的输入要求来
    test_data = FashionMNIST(root='./data',
                              train=False,
                              transform=transforms.Compose((transforms.Resize(size=28), transforms.ToTensor())),
                              download=True)

    #先放入数据,一批次数量(数据量比较小,6G运存应该是够的),shuffle是否打乱数据,进程是8,电脑垃圾的话,就改小一点
    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,没有GPU用CPU
    device = "cuda" if torch.cuda.is_available() else "cpu"
    #将模型放入到测试设备当中
    model = model.to(device)
    #初始化模型参数,准确度即可,虽然已经知道测试的数据集有10000张,但为了后续不知道张数的情况下,也能进行测试。
    test_corrects = 0.0
    test_num = 0

    ##开始模型的测试,不存在反向传播,因此将梯度置为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()
            #前向传播过程,输入为测试数据集,输出为对每个样本的预测值,它其实是一个10*1的矩阵,并不能看出结果来,需要经过一个softmax得到结果。最大概率对应的下标
            output = model(test_data_x)
             #预测的类别,查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output,dim=1)
            #预测的结果和标签一样+1,如果预测正确,则 test_corrects加1,直到循环结束
            test_corrects += torch.sum(pre_lab == test_data_y.data)
            #将所有的测试样本进行累加
            test_num += test_data_x.size(0)

    #计算测试准确率,不在循环里面哦,注意缩进
    test_acc = test_corrects.double().item() / test_num

    print("测试的准确率为:" ,  test_acc)


#主函数进行运行程序
if __name__ == "__main__" :
    #加载模型
    model = LeNet()
    #torch.load('best_model.pth')加载训练好的模型参数,权重以字符串的形式, model.load_state_dict模型训练化,将实例化之后的模型加载上权重,规定的做法

    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','Ankel 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)
            ##10个通过神经元得到数值,经过softmax函数获得对应的概率,从而获得最大值的下标
            #ToTensor()将数据格式转换为张量的形式,只有张量的数据格式,才可以去做梯度运算,反向传播等
            pre_lab = torch.argmax(output, dim=1)
            # pre_lab.item()表示将张量中的数值取出来,以便做其他的操作(比如通过列表设置的类别,取整型方便运算,张量形式是没法进行的。
            result = pre_lab.item()
            label = b_y.item()
            #不仅打印数值,更应该打印出对应的类别,推理之前设置有列表,用下标指向类别
            print("预测值:", classes[result], "------", "真实值:",classes[label])

可视化操作的一些代码

from torchvision.datasets import FashionMNIST  #torchvision计算机视觉识别的一些数据库
from torchvision import transforms   #处理数据归一化,大小变化加载张量等
import torch.utils.data as Data #导入一个处理数据的库
import numpy as np
import matplotlib.pyplot as plt  ##可视化操作

#transforms.ToTensor()将数据转化为张量形式,方便后续模型使用,transforms.Compose((transforms.Resize(size=224)改变输入的大小尺寸
##这一块是核心的操作,方便与后续画图,训练无关
train_data = FashionMNIST(root='./data',
                          train=True,
                          transform=transforms.Compose((transforms.Resize(size=224),transforms.ToTensor())),
                          download=True)

#处理一下数据,打乱,尺寸,64,64一捆,并且进行打乱
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()    #将四维张量移除第1维,并转换为Numpy数组
batch_y = b_y.numpy()     #将张量转换成Numpy数组
class_label = train_data.classes #训练集的标签
#print(class_label)
print("The size of batch in train data:",batch_x.shape)   #每个mini-batch的维度是64*224*224

#可视化一个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()     #这句代码不要放在内循环中,需要放在循环外,进行绘图即可。
  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值