ResNet网络

ResNet网络诞生背景

在这里插入图片描述
来源:百度百科
孙剑(1976年10月—2022年6月14日),男,出生于西安 [6],人工智能领域科学家 [7],生前为旷视科技首席科学家、旷视研究院院长、西安交通大学人工智能学院首任院长 [6]。
孙剑于1997年在西安交通大学获工学学士学位,2000年在西安交通大学获工学硕士学位,2003年在西安交通大学获工学博士学位 [4];2003年在微软亚洲研究院担任首席研究员,2016年7月加入旷视科技任首席科学家和旷视研究院(Megvii Research)负责人;2022年6月14日,因突发疾病抢救无效逝世,年仅45岁。 [6]
孙剑主要研究方向是计算机视觉和计算摄影学、人脸识别和基于深度学习的图像理解。 [6]

网络加深带来的问题是什么呢

在这里插入图片描述
模型退化的原因在于:
1.不可能让w永远保持为1
2.每一层经过激活函数的处理,会带来特征的信息损失(有效特征或者冗余特征)

ResNet网络创新的地方-残差块

在这里插入图片描述

批量规范化层(归一化,是含有网络参数的),是需要通过反向传播来更新参数的。

之前的GoogLeNet网络中的局部归一化是不含网络参数的。
2种残差块结构(一种含有1*1卷积,保证与输入具有相同的宽高和通道数,进而方便和输入进行相加运算,一种是不含的)
在这里插入图片描述

1X1卷积(通过改变步长来和批量规范化层输出的特征大小保持一致卷积核的数量,来保证通道数是一致的,以便进行相加操作)

具体的搭建

在这里插入图片描述

import torch
from torch import nn
from torchsummary import summary


#借鉴GoogLeNet网络的Inception模块的搭建,因此可以先搭建一个残差块(模板式,后续直接调用即可)
class Residual(nn.Module):
    #定义初始化函数
     def __init__(self,input_channels,num_channels,use_1conv =False,strides=1):
         super(Residual).__init__()
         self.ReLu = nn.ReLU()
         self.conv1 = nn.Conv2d(in_channels=input_channels ,out_channels=num_channels,kernel_size=3,padding=1,stride=strides)
         self.conv2 = nn.Conv2d(in_channels=num_channels ,out_channels= num_channels,kernel_size=3,padding=1)
         self.bn1 = nn.BatchNorm2d(num_channels)
         self.bn2 = nn.BatchNorm2d(num_channels)
         ##两种残差块,有参数的是有1*1卷积的,use_1conv为真时,有1*1卷积块,为假时,为空,一般填充为0,会发现参数不够用
         if use_1conv:
             self.conv3 = nn.Conv2d(in_channels=input_channels ,out_channels= num_channels,kernel_size=1,stride=strides)
         else:
             self.conv3 = None
     #利用前向传播,来进行一个模型的构建
     def forward(self,x):
        y = self.ReLu(self.bn1(self.conv1(x)))
        y = self.bn2(self.conv2(y))
        #条件判断,是否有conv3,conv3 = None,条件为假,则没有
        if self.conv3:
            x = self.conv3(x)
        y =self.ReLu(y+x)

        return y

ResNet网络创新的地方-BN操作

在这里插入图片描述
在这里插入图片描述

归一化处理,消除了物理量纲对结果的影响(同一起跑线,那个特征对结果的影响较大,对应的w就多一些更新)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第四步的作用在于普遍性(而不是单纯的几个激活函数的效果比较好)
既要收敛速度快,还不要近似于线性函数使得弱化网络性能。
第四步得到的性能是最差的情况是和没有处理的性能是一样的。
在这里插入图片描述

ResNet网络参数详解

以ResNet-18为例

网络结构,巧妙,简单,这才是真正厉害的地方

3*3最大池化的作用是降低分辨率(尺寸变为原来的一半)

在这里插入图片描述
在这里插入图片描述

步幅为1,填充为13*3的卷积核,特征图大小是不变的
计算特征图大小的公式不要忘了哦!

在这里插入图片描述
在这里插入图片描述

上图中右括号处写的3个(表示带有1*1卷积+不带有的构成一个小结构,3个就是共有3个这样的小结构)

在这里插入图片描述
在这里插入图片描述
注意一下,这个1*1的卷积层,填充一般为0,步幅的大小,与刚进入残差块的第一个卷积核的步幅大小一致。即1*1卷积的卷积核个数和步长是根据实时的变换而变换,不是固定不变的步长。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
全局平均池化模块,在上一篇GoogLeNet中强调了,这里就不再提了。
在这里插入图片描述

ResNet网络模型搭建

import torch
from torch import nn
from torchsummary import summary


#借鉴GoogLeNet网络的Inception模块的搭建,因此可以先搭建一个残差块(模板式,后续直接调用即可)
class Residual(nn.Module):
    #定义初始化函数,use_1conv =False默认为假,是不存在1*1卷积函数的。
     def __init__(self, input_channels,num_channels,use_1conv =False,strides=1):
         super(Residual,self).__init__()
         self.ReLu = nn.ReLU()
         self.conv1 = nn.Conv2d(in_channels=input_channels ,out_channels=num_channels,kernel_size=3,padding=1,stride=strides)
         self.conv2 = nn.Conv2d(in_channels=num_channels ,out_channels= num_channels,kernel_size=3,padding=1)
         self.bn1 = nn.BatchNorm2d(num_channels)
         self.bn2 = nn.BatchNorm2d(num_channels)
         ##两种残差块,有参数的是有1*1卷积的,use_1conv为真时,有1*1卷积块,为假时,为空,一般填充为0,会发现参数不够用
         if use_1conv:
             self.conv3 = nn.Conv2d(in_channels=input_channels ,out_channels= num_channels,kernel_size=1,stride=strides)
         else:
             self.conv3 = None


     #利用前向传播,来进行一个模型的构建
     def forward(self,x):
        y = self.ReLu(self.bn1(self.conv1(x)))
        y = self.bn2(self.conv2(y))
        #条件判断,是否有conv3,conv3 = None,条件为假,则没有
        if self.conv3:
            x = self.conv3(x)
        y =self.ReLu(y+x)

        return y
##定义ResNet18的网络模型
class ResNet18(nn.Module):
    def __init__(self,Residual):
        super(ResNet18,self).__init__()
        #定义第一个代码块
        self.b1 = nn.Sequential(
            nn.Conv2d(in_channels=1,out_channels=64,kernel_size=7,stride=2,padding=3),
            nn.ReLU(),
            nn.BatchNorm2d(64),
            nn.MaxPool2d(kernel_size=3,stride=2,padding=1))

        #定义第二个代码块,use_1conv =False,strides=1默认可以不写,写上为了一目了然
        self.b2 = nn.Sequential(Residual(64,64,use_1conv =False,strides=1),
                                Residual(64,64,use_1conv =False,strides=1))
        # 定义第三到五个代码块(有1*1卷积和没有1*1卷积组成一个代码块)
        self.b3 = nn.Sequential(Residual(64, 128, use_1conv=True, strides=2),
                                Residual(128, 128, use_1conv=False, strides=1))
        self.b4 = nn.Sequential(Residual(128, 256, use_1conv=True, strides=2),
                                Residual(256, 256, use_1conv=False, strides=1))
        self.b5 = nn.Sequential(Residual(256, 512, use_1conv=True, strides=2),
                                Residual(512, 512, use_1conv=False, strides=1))

        self.b6 = nn.Sequential(nn.AdaptiveAvgPool2d((1,1)),
                                nn.Flatten(),
                                nn.Linear(512,10))



        #定义前向传播
    def forward(self,x):
        x = self.b1(x)
        x = self.b2(x)
        x = self.b3(x)
        x = self.b4(x)
        x = self.b5(x)
        x = self.b6(x)
        return x
#写一个主函数,测试一下模型的情况
if __name__ == "__main__":
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    #模型实例化放入到设备中
    model = ResNet18(Residual).to(device)
    #打印一下网络模型的各层情况
    print(summary(model,(1,224,224)))

super(Residual,self).init(),语法问题,self不要少
否则会出现下面的错误
在这里插入图片描述
在这里插入图片描述
两个地方可能出现的错误:
File “C:\Users\mcg\Desktop\ResNet18\model.py”, line 74, in
model = ResNet18(Residual).to(device)
File “C:\Users\mcg\Desktop\ResNet18\model.py”, line 55, in init
self.b6 = nn.Sequential(nn.AdaptiveAvgPool2d(1,1),

根据您提供的错误信息,问题出现在尝试使用 nn.Sequential 来添加一个 nn.AdaptiveAvgPool2d 模块时。错误提示表明 __init__() 函数接收了比预期更多的参数。

在 PyTorch 中,nn.Sequential 的构造函数接受以下参数:

python
class torch.nn.Sequential(*args)
*args 表示可变数量的位置参数,这些参数应该是 nn.Module 的实例。如果您在调用 nn.Sequential 时直接在括号内指定模块,而不是将它们作为参数列表传递,就会出现这个错误。

正确的使用方式是将模块作为参数列表传递给 nn.Sequential,如下所示:

python
self.b6 = nn.Sequential(
    nn.AdaptiveAvgPool2d(1),
    # 其他模块...
)
请注意,nn.AdaptiveAvgPool2d 只需要一个参数,即输出特征图的大小,这个大小应该是一个元组 (H, W)。如果两个参数都是 1,您应该将它们作为一个元组传递,而不是两个独立的参数。

如果您的意图是将输出特征图的大小调整为 1x1,正确的代码应该是:

python
self.b6 = nn.Sequential(
    nn.AdaptiveAvgPool2d((1, 1)),  # 注意这里的参数是一个元组
    # 其他模块...
)
如果您在 nn.Sequential 中还有其他模块要添加,确保它们也是以 nn.Module 的实例形式添加的。如果问题仍然存在,请检查 model.py 文件的第 55 行,确保所有模块都是正确添加的,并且没有其他语法错误。

在这里插入图片描述
因此需要将1,1用括号给括起来 在这里插入图片描述
在这里插入图片描述
"页面文件太小"这个提示通常出现在操作系统中,特别是在使用Windows系统时。页面文件,也被称为虚拟内存或交换文件,是操作系统用来扩展物理内存(RAM)的一种机制。当系统的物理内存不足时,它会使用硬盘空间作为临时的内存使用。
如果你收到“页面文件太小”的提示,这可能意味着:

  1. 内存不足:你的系统可能在运行大量内存密集型应用程序,导致物理内存不足。
  2. 性能问题:系统可能会变得缓慢或响应延迟,因为硬盘的速度远低于RAM。
  3. 系统设置:页面文件的大小可能被设置得太小,无法满足当前的系统需求。
    解决这个问题的一些方法包括:
    ● 增加物理内存:如果可能的话,增加更多的RAM可以减少对页面文件的依赖。
    ● 调整页面文件大小:在Windows系统中,你可以手动调整页面文件的大小。通常,页面文件的大小设置为物理内存的1.5倍是一个常见的建议。
    ● 关闭不必要的程序:关闭那些不需要的或内存占用高的程序,以减少对内存的需求。
    ● 磁盘清理:释放硬盘空间,确保有足够的空间用于页面文件。
    ● 检查磁盘健康:确保硬盘没有错误或坏道,这些问题可能会影响页面文件的性能。
    在Windows中调整页面文件大小的步骤大致如下:
  4. 右键点击“计算机”或“此电脑”,选择“属性”。
  5. 在左侧菜单中选择“高级系统设置”。
  6. 在“性能”区域,点击“设置”按钮。
  7. 切换到“高级”选项卡,然后在“虚拟内存”区域点击“更改”。
  8. 取消勾选“自动管理分页文件大小”。
  9. 选择“自定义大小”,然后输入初始大小和最大大小。
  10. 点击“设置”,然后“确定”保存更改。
  11. 重启计算机以使更改生效。
    根据上面出现的问题,炮导语录:
出现报错的地方,不要去怕,去解决问题,就在报错的附近看看
首先,第一点想的是不是和老师的代码是完全一样的
第二点,是不是哪里敲错了(拼写错误),或者缩进不对啦
第三点,考虑一下是不是环境的问题,但前面的网络都没有什么问题,应该不会是环境的问题。

ResNet网络模型训练

需要改的地方
from model import ResNet18,Residual
torch.save(best_model_wts, ‘C:/Users/mcg/Desktop/ResNet18/best_model.pth’)
ResNet18 = ResNet18(Residual)
train_process = train_model_process(ResNet18,train_dataloader,val_dataloader,20)
完整训练代码,以 FashionMNIST数据集为例进行训练

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导入我们的模型,Inception 也需要导入进来
from model import ResNet18,Residual

#训练集和验证集的定义,名称尽可能一目了然,用于处理训练集和验证集
def train_val_data_process():
    ##数据加载进来,transforms.Compose((transforms.Resize(size=28),大小设置按照模型的输入要求来,AlexNet网络的大小是227
    train_data = FashionMNIST(root='./data',
                              train=True,
                              transform=transforms.Compose((transforms.Resize(size=224), 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是否打乱数据,batch size看GPU情况,这个参数比较少应该可以的。
    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),这行代码是没有用的。下面的两种保存权重方法都是可以的,权重的路径改一下,别一下跑到LeNet的去了
    torch.save(best_model_wts, 'C:/Users/mcg/Desktop/ResNet18/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
##定义一个绘制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导入进去,然后在下面进行实例化。将模型放到训练代码就可以了。有个别的模型可能数据的预处理需要改一点。
     ResNet18 = ResNet18(Residual)
     #加载数据集,分为训练集和验证集
     train_dataloader,val_dataloader = train_val_data_process()
     #将训练集,验证集,模型,和训练次数放进训练代码中,
     train_process = train_model_process(ResNet18,train_dataloader,val_dataloader,20)    #自己进行设定训练的次数,假如这里的训练次数是20轮,并保留中间值
     ##绘制损失和准确率的图形
     matplot_acc_loss(train_process)
     

任务管理器运行情况
在这里插入图片描述
训练ing
12 Train Loss: 0.0334 Train Acc: 0.9886
12 Val Loss: 0.3286 Val Acc: 0.9231
训练和验证耗费的时间90m12s
训练结果:
在这里插入图片描述
训练结束:
19 Train Loss: 0.0189 Train Acc: 0.9936
19 Val Loss: 0.3646 Val Acc: 0.9277
训练和验证耗费的时间138m47s
结果略微有一些过拟合
尝试ResNet13等,或者减少神经元个数/Dropout(从而有可能减少过拟合的出现)
训练集结果的参考价值不那么的大,最好去看验证集的结果情况(测试的结果大概在0.92左右)

视频中的结果:
在这里插入图片描述

ResNet网络模型测试

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

##数据就处理好了。
##导入数据进行模型的测试,从模型训练的代码拿一部分,改改即可,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=224), 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 = ResNet18(Residual)
    #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])

视频中的结果:
在这里插入图片描述
我的:
在这里插入图片描述
至此,Pytorch框架与经典卷积神经网络学习告一段落。

学习参考来源于:B站导师:炮哥带你学
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值