小土堆pytorch学习—P27👉P29-完整的模型训练套路

CIFAR数据集原始数据为PIL数据类型,在使用该数据集时,需要对其转换。

案例👇train.py

#引入模型
from model import *
#准备训练数据集
train_data = torchvision.datasets.CIFAR10(root = "../data", train = True ,transform = torchvision.transforms.ToTensor(), download = True )

#准备测试数据集
test_data = torchvision.datasets.CIFAR10(root = "../data" , train = False , transform = torchvision.transforms.ToTensor() , download = True  )

#如果想要训练数据集有多少张图片,测试数据集有多少张图片,下面代码可以做到👇
train_data_length = len(train_data)
test_data_length = len(test_data)
#该写法于python中较为常见
print("训练数据集的长度为:{}".format(train_data_length))
print("测试数据集的长度为:{}".format(test_data_length))

#利用DataLoader加载数据集
train_dataloader = DataLoader(train_data , batch_size = 64)
test_dataloader = DataLoader(test_data , batch_size = 64)

#创建网络模型
tudui = Tudui()

#创建损失函数
#因为这个是分类问题,所以可以用交叉熵,参数暂时用不到
loss_fn = nn.CrossEntropyLoss()

#定义优化器,用随机梯度下降,parameters一定要填,学习速率也要填。
learning_rate = 1e-2#方便提取
optimizer = torch.optim.SGD(tudui.parameters() , lr =learning_rate)

#1e-2 = 0.01
#设置训练网络的一些参数
#记录训练的次数
total_train_step = 0
#记录测试的次数
total_test_step = 0
#训练的轮数 
epoch = 10

for i in range(epoch):
    print("--------第{}轮训练开始--------".format(i+1))
    
    
    #训练步骤开始
    for data in train_dataloader:
        imgs , targets = data
        #分别取得训练数据,并放入网络当中
        outputs = tudui(imgs)
        #这个输出结果是每一张图片对应的实际标签。而outputs是训练之后10分类对应的标签得分
        #得到输出之后,要放入损失函数中,看与targets相差多少
        loss = loss_fn(outputs , targets)
        
        #优化器优化模型
        #进行优化,要记住利用优化器对梯度清0
        optimizer.zero_grad()
        #清零之后,可以用损失所得值进行反向传播,得到每个参数之间的梯度
        loss.backward()
        #调用该方法,就可以对其中参数进行优化
        optimizer.step()
        #因为已经做了一次优化,所以可以对优化次数加1
        total_train_step = total_train_step + 1
        #这里的训练次数说的是对于数据集中所有的数据进行一次训练,所以每个损失都不一样。
        print("训练次数:{} , loss:{}".format(total_train_step , loss.item()))

targets👇

image-20230707110759471

一个batch_size =64,所以会有64个targets。

outputs👇

image-20230707110732415

一张图片有可能被分为10类别中的一种,所以对应得分是10个。

为了规范,常把神经网络模型放入单独文件夹中,此时案例文件为model.py,代码如下👇

#搭建神经网络,因为CIFAR是10分类的数据集,所以该网络应该是10分类网络
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.model = nn.Sequential(
        	nn.Conv2d(3, 32 , 5, 1, 2),
            nn.MaxPool2d(2 ),
            nn.Conv2d(32 , 32 , 5 ,1 2),
            nn.MaxPool2d(2),
            nn.Conv2d(32,64,5 , 1, 2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(64*4*4,64),
            nn.linear(64, 10)
        )
    
    def forward(self , x ):
        x  = self.model(x)
        return x 
    
    #测试网络的正确性,一般是给定一个输入尺寸,输出尺寸是不是想要的,此时创建为input
if __name__ == '__main__':
    tudui = Tudui()
    input = torch.ones((batch_size = 64, channel = 3 , 32 , 32))
    output = input(tudui)
    print(output.shape)

但一定要注意,这两个文件要在同一文件夹下。

import torch
a = torch.tensor(5)
print(a)
print(a.item())

#如果只是打印a,那就会出现tensor(5)
#加上item(),则会转换为真是的数字5

训练步骤写完,但此时出现一个问题,怎么知道自己的模型有没有训练好?达到期望的需求?所以每一轮训练之后,会进行一个测试。让它再在测试数据集上跑一遍,以测试数据集的损失/正确率来评估模型有没有训练好。注意,在测试过程中,不需要对模型进行调优,因为想要利用现有的模型进行测试。

测试步骤代码及注释如下👇

#准备训练数据集
train_data = torchvision.datasets.CIFAR10(root = "../data", train = True ,transform = torchvision.transforms.ToTensor(), download = True )

#准备测试数据集
test_data = torchvision.datasets.CIFAR10(root = "../data" , train = False , transform = torchvision.transforms.ToTensor() , download = True )

#如果想要训练数据集有多少张图片,测试数据集有多少张图片,下面代码可以做到👇
train_data_length = len(train_data)
test_data_length = len(test_data)
#该写法于python中较为常见
print("训练数据集的长度为:{}".format(train_data_length))
print("测试数据集的长度为:{}".format(test_data_length))

#利用DataLoader加载数据集
train_dataloader = DataLoader(train_data , batch_size = 64)
test_dataloader = DataLoader(test_data , batch_size = 64)

#创建网络模型
tudui = Tudui()

#创建损失函数
#因为这个是分类问题,所以可以用交叉商,参数暂时用不到
loss_fn = nn.CrossEntropyLoss()

#定义优化器,用随机梯度下降,parameters一定要填,学习速率也要填。
learning_rate = 1e-2#方便提取
optimizer = torch.optim.SGD(tudui.parameters() , lr =learning_rate)

#1e-2 = 0.01
#设置训练网络的一些参数
#记录训练的次数
total_train_step = 0
#记录测试的次数
total_test_step = 0
#训练的轮数 
epoch = 10

#添加tensorboard
writer = SummaryWriter("tb_logs")

for i in range(epoch):
    print("--------第{}轮训练开始--------".format(i+1))
    
    
    #训练步骤开始
    for data in train_dataloader:
        imgs , targets = data
        #分别取得训练数据,并放入网络当中
        outputs = tudui(imgs)
        #得到输出之后,要放入损失函数中,看与targets相差多少
        loss = loss_fn(outputs , targets)
        
        #优化器优化模型
        #进行优化,要记住利用优化器对梯度清0
        optimizer.zero_grad()
        #清零之后,可以用损失所得值进行反向传播,得到每个参数之间的梯度
        loss.backward()
        #调用该方法,就可以对其中参数进行优化
        optimizer.step()
        #因为已经做了一次优化,所以可以对优化次数加1
        total_train_step + = 1
        if total_train_step % 100 = 0 :
        	print("训练次数:{} , loss:{}".format(total_train_step , loss.item()))
            #更方便查看训练次数
            #可以把每一次的训练记录下来,逢百记录
            writer.add_scalar("train_loss" , loss.item() , total_train_step )
        
        
        #
    total_test_loss  = 0
        #从名字看出来with里面的代码,没有梯度,这样就不能对其进行调优。
        #测试步骤开始,for以及with属同一级。
    total_accuracy = 0 #整体正确的个数
    with torch.no_grad():
        #从测试的dataloader中取测试的数据集
        for data in test_dataloader:
			#
            imgs , targets = data 
            #把测试数据集中的数据放入网络中,得到一个输出
            outputs = tudui(imgs)
            #比较输出与真实的targets之间的误差
            loss = loss_fn(outputs , targets)
            #一般是在测试过程中看网络模型在整体数据集上的误差/正确率
            #现在的loss只是一部分数据在网络模型上的损失,但目的是求整个数据集上的loss
            total_test_loss = total_test_loss + loss 
            #这部分运行结束,就相当于把所有的损失加到一块
            #添加每次一小部分数据,正确的个数有多少。就等于outputs最大的有多少,方向是横向的,所以是1,
            #又要与真实的targets进行相比,得出true,false等于多少,接下来再求和。
            accuracy = (outputs.argmax(1) == targets).sum()
            #把每次求出来的正确的个数加到总的上面
            total_accuracy = accuracy + total_accuracy
    print("整体测试集上的Loss:{}".format(total_test_loss))
    #正确率=总共预测对的个数,除以总共数据集上的个数
    print("整体测试集上的正确率total_accuracy:{}".format(total_accuracy/test_data_length))
    writer.add_scalar(tag = "test_loss"  , scalar_value = total_test_loss ,global_step =  total_test_step )
    writer.add_scalar("test_accuracy" , scalar_value = total_accuracy/test_data_length , total_test_step)
    total_test_step  = total_test_step +1 
        #global_step 就是之前记录测试的次数,测试次数要+1       
    torch.save(tudui  , "tudui_{}.pth".format(i))
    print("模型已保存")

所以在此的一个问题是为什么它训练的数量是782个一轮?

训练集大小为50000,batch_size = 64,则50000/64 = 781.25,总共有781个含64个数据的集合,以及一个含16个数据的集合,合成782次训练。


如果想要保存每一轮训练的数据,那么应该加上一点东西。在最后的几行。

解决二分类问题

outputs = [0.1 , 0.2]

​ [0.3 , 0.4]

按照下标来标注,每个概率大的都是[1]。输出为:preds = [1]\n[1],找到的都是其位置(下标)。

inputs targets = [0] [1]

接着对照👉preds == inputs targets ,这里对照的都是它们的位置。inputs targets的0位置,并不是概率最大的,所以为0,对应的后者为1。

[false , true].sum() = 1 相加过程中会把前者为0,后者为1。通过“ = = ”知道两者是否相等,接着对比preds 和inputs targets 相应的元素相等有多少,再除以总个数,可算出正确率。一横零纵。

print('='*100)
print(F"outputs = {outputs}")
print(F"outputs.argmax(1) = {outputs.argmax(1)}")
#outputs是一个矩阵,每一行代表一个数据,每一列代表一个数据对应10个类别的得分
#argmax,参数为1时,计算每一行/每一个数据得分最高的数值在该行的索引值
#argmax,参数为0时,计算每一列/每个类别得分最高的数值的索引
print(F"outputs.argmax(0) = {outputs.argmax(0)}")
print("="*100)

image-20230710114249050

细节

大多数训练模型在训练步骤开始时,会把网络模型设置为train,或者是在测试网络之前设置网络模型为eval(),这并不是说把网络设置为训练模式才能够开始训练,把网络设置为eval状态才开始测试。而是有一些层必须这样做。(神经网络都是继承Module)网站:https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module

可以看到它只对一部分的网络层有作用,比如说Dropout, BatchNorm。

同样,eval()层也只对特定层有作用。

捋一遍思路👇

首先准备数据集,准备对应的DataLoader,接着创建网络模型、损失函数、优化器,设置训练中的一些参数,接着设置训练中的轮数,以便能够进行多次训练。接着到55行进入训练的状态,不断从train_dataloader 中取数据,放入网络模型中,计算损失函数,放入优化器中,采用特定方式,展示输出。

特定步数之后,做测试,可以设置eval(),但是一定要设置with torch.no_grad(),让网络模型中的梯度都没有,不需要对梯度测试,也不需要优化。计算误差,构建特殊指标显示出来。最后可以在特定的步数保存模型。

posted @ 2023-07-10 16:02  西红柿爆炒鸡蛋  阅读(85)  评论(0)    收藏  举报