LeNet与AlexNet实战

搭建神经网络模型的过程可以总结为以下步骤:

  1. 初始化网络层与参数:在模型的初始化阶段,需要定义各种网络层(如卷积层、全连接层)以及所需的参数。这相当于为搭建网络准备基础的组件,如砖头、水泥等资源。

  2. 前向传播过程:定义前向传播函数,用于将输入数据通过各个网络层逐步进行计算,从而生成输出。每一层的输出作为下一层的输入,直到最终获得模型的输出。该过程通过调用已初始化的层来实现各层间的连接。

  3. 激活函数与其他操作:在前向传播的过程中,通常需要使用激活函数(如 ReLU)对中间结果进行非线性变换,提升模型的表达能力。此外,可能还会使用池化层等操作来缩小特征图的尺寸,从而减少计算量并提取更有意义的特征。

  4. 反向传播与训练:模型搭建完成后,配合训练过程的反向传播算法,通过计算梯度和更新参数来优化模型。训练过程通常在独立的训练代码中完成,而前向传播则是网络模型的一部分。

这个流程类似于搭建一座建筑物,先准备好各类材料和工具(即初始化网络层和参数),然后根据设计逐步构建,直到模型能够通过输入生成有效输出。

初始化

新建一个LeNet文件夹,其中model.py如下

import torch
from torch import nn
from torchsummary import summary

class LeNet(nn. Module):
    def __init__(self):
        super(LeNet, self).__init__()
        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)
        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)

在构建神经网络时,首先需要定义网络的各层及其参数。在初始化阶段,我们根据 LeNet 的结构定义卷积层、激活函数、池化层、展平层以及全连接层:

  • 卷积层 (Convolutional Layer):通过 nn.Conv2d 定义卷积层,指定输入通道、输出通道、卷积核的大小,以及填充(padding)等参数。LeNet 中的第一层卷积接收灰度图(通道数为 1),输出 6 个特征图,卷积核大小为 5x5,使用 padding 为 2。

  • 激活函数 (Activation Function):使用 nn.Sigmoid() 来增加网络的非线性表示能力。

  • 池化层 (Pooling Layer):通过 nn.AvgPool2d 定义池化层,用于下采样特征图,减少计算量。池化核大小为 2x2,步幅为 2。

  • 全连接层 (Fully Connected Layer):通过 nn.Linear 定义全连接层,用于将卷积层的输出映射到具体的类别标签。LeNet 中有三层全连接层,第一层输入 400 个特征,输出 120 个神经元,接着是 84 个神经元,最后输出为 10 类。

前向传播

 

import torch
from torch import nn
from torchsummary import summary

class LeNet(nn. Module):
    def __init__(self):
        super(LeNet, self).__init__()
        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)
        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))   # Pass input through the first convolution layer (c1), followed by a sigmoid activation function (sig).
    x = self.s2(x)             # Pass the result through the first pooling layer (s2).
    x = self.sig(self.c3(x))   # Pass the output through the second convolution layer (c3) and apply sigmoid activation again.
    x = self.s4(x)             # Pass the result through the second pooling layer (s4).
    x = self.flatten(x)        # Flatten the output to prepare it for fully connected layers.
    x = self.f5(x)             # Pass the flattened output through the first fully connected layer (f5).
    x = self.f6(x)             # Pass through the second fully connected layer (f6).
    x = self.f7(x)             # Pass through the final fully connected layer (f7) to produce the output.
    return x                   # Return the final result as the output of the forward pass.


最好是台词接下来我们模型已经定好了
对不对
就初始化的一些信息都已经有了
接下来我们干嘛
我们就是利用这些初始化好的一些信息啊
你干嘛搭建我们前向传播的一个过程
然后把我们的数据传输进去
这样的话我们模型整体一个结构就搭建完了
然后的话我们定一个什么
定一个forward函数
我们来看一下
我们定一个forward forward
就前向传播的一个哎对啊
forward这样的一个函数啊
这里的话你你写个括号之后
它自动会出现一个sf
这是写什么
在我们的定义
我们那个什么在我们那个class类里面
它自然而然会出那个约定俗成的一个算法啊
啊这那个什么语法语法啊
所以的话就是有就在学我们这个课程当中啊
你多少还是有一点有一点什么
有一点我们Python的基础的
当然你说你没有Python技术
你就照着我后面敲
你这个神经网络也能敲出来
相信我啊
肯定也能敲出来
而且每一步我会讲的很细啊
为什么
因为因为我的课其实是面向初学者的啊
所以希望大家能学到东西啊
所以可能有时候会显得啰嗦
然后的话这里有个X什么意思
就意思是什么意思
我们输入X还
我们再再再再再啰嗦一遍呗
就是此时我们所有的信息都已经有了
对不对
就神经网络层都已经有了
那么我们是不是要搭建我们的网络模型
对不对
那模型要输入X对不对
是不是要得出我们的Y对不对
然后你这里你肯定要定一个X2
X输入那个接口啊
对不对
所以你X要输入啊
然后的话然后我们X输入我们第一个就是什么
我们X等于什么呢
等于我们第一个层
第一层我们来看一下啊
我们看一下我的PPT是吧
第一个是什么
是我们的卷积层
所以我们先定一个卷积层
把这个卷积层拿过来
我们就直接第一个C1嘛
CCFCF点一是吧
然后你把这个X传入进去
是不是我们卷积层
把数据传入到我们卷积层里面去了
对不对
或者我这样讲
我们现在有一个
C对不对
你把X传入进来是吧
或者我们来我们C就你现在又给我卷积了
是不是卷积层
然后把X传进来
这个卷积之后呢
应该是一个在经过什么他的书经过激活函数
激活函数之后
再经过什么
经过我们的磁化
对不对
然后这池化呢再经过什么
再经过我们一个卷积
这里所有的什么
这里的X再输入到我们那个SINGA里面
在SYMD输出来的一个结果啊不对
这不对啊
这里应该等于AA输入到我们single mode里面
然后这个这个这个SNLMD激活出来
这个A1撇再输入到某个池化里面
明白我的意思吗
然后在池塘里面那个什么石化
应该输出的是什么
输入这个A撇一撇
这里应该是AA撇啊
A撇一撇
A撇一撇
出到什么
出到我们的卷积里面
明白我意思吗
然后卷积再输出一个嘛
A1撇一撇一撇
再输入到什么
再输入到我们的计划书里面
然后这里再输出A4吧
再输到什么
我们池化里面就不断的这样的过程
而事实上你这个值你你这里叫A
你这里也可以叫A啊
只是我们定义定义的一个名字而已嘛是吧
能明白我的意思吗
所以的话你看啊
我们在搭建什么
搭建网络模型的时候
你这里输出来输入到我们卷积里面是吧
然后是不是我们经过卷积了
那是我们再套一下嘛
点点什么呢
我们这里应该是一个我们那个什么
我们的一个旧函数
然后你再嵌套不就行了吗
这不就相当于我们数学里面嵌套函数吗是吧
你输入到我们卷积里面
卷积得出来的结果
再输入到我们single model里面函数里面
然后得出我们的X吗是吧
所以这里X是经过什么卷积和池
和我们奇偶函数得出来的一个X
对不对
然后呢到下一层我们X还等于什么呢
再等于什么
或者我们这里写X2或者X1是吧
这里C点什么呢
我们第二次应该是什么呢
是我们那个磁化了
对不对
所以就X2
然后再把什么再把X输入进来
由这里X输入到它里面
得出我们的X
在X在输入到我们池化里面得出X1
事实上我不想这样写
我就是我就想的是X好吧
可以吗
没有问题吧
然后呢再等于X等于什么呢
等于myself
那么这里我们学精简吗
跟前面一样吗
我们single mode里面再套一个什么self点点什么呢
点C2
这里是C2啊
这里有C3
不是C2
没有C没有C2这个参数吧是吧
X这里是什么意思呢
这里这里输出来磁化输出来X是吧
我们的数据放到了放置我们的卷积里面
卷积在在经过我们激活之后
激活得出我们这个这个X明白我的意思吧
然后呢我们在同样的X等于什么呢
等于我们cf是吧
这里应该是什么
我们第四这个有什么我们的第二层层外层是吧
那就是点什么S4
然后括号里面X这里什么意思呢
就这里经过卷积和经过经过什么
经过函数输出来的X再输入到我们池化层里面
得出我们的X
所以你会发现其实很流畅
对不对
很流畅对不对
然后我们这个池换完之后呢
最后就是我们的什么
我们平展层对不对
我们叫做平坦层
这里有吗
我就直接选呗
输入X
所以此时我们的X得出来一个值
是个平展层之后的一个值
对不对
然后的话平展成之后干嘛再输入到什么
输入到我们线性全连接层里面吧
或者我们看PPT里面PPT里面吧是吧
平展操作完之后与全连接层相连接
然后全连接层第一层
第二层
第三层嘛
我们都已经定好了
是不是我们都拿过来用呗
这就是我们石化之后
经过平展层得出来百X
那很显然这个X等于等于我们这个cf
那么就是F5嘛
点啊这你要点F5
然后我们得把X输入进来
然后得出我们这个xx
其实我们事实上到这里我们就可以直接什么
我复制一下
复制一下得出什么六七是不是
所以的话啊
你看啊这里所有定义的神经网络错
当然你说哎呀
我不服啊
我我我头铁
我把这里写到这里行不行
你看我们代码也没有什么任何问题吧
这里就告诉你我们定义了哪些模块吗
需要哪些模块吗
当然我们希望像像什么像我们这样的一个流程
从头到尾
从上到下这样的流程去定义嘛
你非要说我先上来定义一个线性
前面一层行不行啊行啊
但是你这不用反逻辑了吗
所以你就按照我们的逻辑去一层层定
我们神经网络层就OK了
定义完之后呢
我们再按照这个逻辑是一层一层往下去传
传入X得出X然后再传入X得出另一个X
每一层经过另一个操作嘛
你看这就是我们对应的神经网络层
的一个操作嘛
然后到这里还没有结束
什么意思呢
就按照这个逻辑
是不是我们最后假设我们这里是最后什么
我们要经过什么啊
我们全连接层
对不对
或者线性线性层
对不对
是不是得出一个什么我们的X这个X
或者说我们就是我们的Y
这个Y是不是让我们返回的
就是神经网络的一个结果吗
return个结果对不对
应该等于Y
所以的话你会发现我们模模型已经搭建好了
这个模型就是我们所有的流程嘛
对不对
然后你输入X应该得出我们的Y嘛
这个Y是我们的返回值啊
实际上反正结果所以前向传播它会有个结果嘛
所以我们这个return定义这个这个什么定义的函数
应该会有个什么对X的反馈
或者你说唉那我这这么去写行不行啊
明白我的意思吧
到这里返回就应该是Y嘛
当然这个这个这个也不直观嘛
我前面全部都用X
我们就用X呗
所以啊你这里就很清晰了
这里是我们所要利用的什么
所有的神经网络的一个层都已经初始化好了
这里再按照什么
按照前向传播的一个逻辑
把我们神经网络层都利用起来
传入我们X得出我们的X
然后一层一层的往下去传传到最后一层
然后得返回我们这一个什么神经网络
前向传播的结果
所以你会发现神经网络的定义和什么
和我们前面那个和我们前面什么原理
是是相扣的
对不对
所以前面我们就是花那么大力气去讲
神经网络的输入特征图和输出特征图
是有那么道理的
为了让你更清晰地去了解我们神经网络的一个
这个前向传播的一个过程哈
和和具体一个参数
然后把它搭建神经网络的时候
你看哎这就很好
很清晰搭建了
那你说我们大概什么net
和包括后面的一些神经网络
是不是都这样的流程
这里定义下面要前向传播就OK了嘛
啊你去看所有的什么
基本上现在所有用我们拍照时搭建
神经网络的一个模型啊
都是这样子啊
无非它会复杂一点
仅此而已

 前向传播

import torch
from torch import nn
from torchsummary import summary

class LeNet(nn. Module):
    def __init__(self):
        super(LeNet, self).__init__()
        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)
        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__":
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model = LeNet().to(device)
    print(summary(model, (1,28,28)))

数据集

由于手写数字识别已经做烂了,所以使用衣服分类的数据集,新建plot.py

from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt

train_data = FashionMNIST(root='./data',
                          train=True,
                          transform=transforms.Compose([transforms.Resize(size=224), transforms.ToTensor()]),
                          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()  # 将四维张量移除第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

plt.figure(figsize=(12, 5))
for ii in np.arange(len(batch_y)):
    plt.subplot(4, 16, ii + 1)

数据加载函数

model_train.py

from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt
from model import LeNet


def train_val_data_process():
    train_data = FashionMNIST(root='./data',
                              train=True,
                              transform=transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()]),
                              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=128,
                                       shuffle=True,
                                       num_workers=8)

    val_dataloader = Data.DataLoader(dataset=val_data,
                                     batch_size=128,
                                     shuffle=True,
                                     num_workers=8)

    return train_dataloader, val_dataloader

 

训练模板代码

model_train.py

import time

import torch.optim
from torch import nn
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt
from model import LeNet


def train_val_data_process():
    train_data = FashionMNIST(root='./data',
                              train=True,
                              transform=transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()]),
                              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=128,
                                       shuffle=True,
                                       num_workers=8)

    val_dataloader = Data.DataLoader(dataset=val_data,
                                     batch_size=128,
                                     shuffle=True,
                                     num_workers=8)

    return train_dataloader, val_dataloader


def train_model(model, train_dataloader, val_dataloader, num_epochs):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  # 优化器,梯度下降法进一步拓展是SGD,Adam和SGDM等等等等的

    criterion = nn.CrossEntropyLoss()
    # 均方损失,分类中我们一般用交叉熵损失来更新损失值,然后是w,b
    # 将模型放到训练设备中
    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()
  1. 参数初始化:我们将 train_loss, train_accuracy, val_loss, val_accuracy 初始化为 0,并在每个 epoch 中累积这些值。
  2. 训练数据加载:每次从 train_loader 中加载一批数据,并将输入 inputs 和标签 labels 放入设备(比如 GPU)。
  3. 梯度清零:为了防止梯度累积,每个 batch 都需要调用 optimizer.zero_grad() 来将梯度重置。
  4. 前向传播:通过模型的前向传播计算输出 outputs
  5. 损失计算:通过定义的损失函数 loss_fn 计算损失。
  6. 反向传播:通过 loss.backward() 计算梯度。
  7. 参数更新:使用优化器 optimizer.step() 进行参数更新。
  8. 计算训练准确度:通过比较预测结果 preds 与真实标签 labels,计算准确预测的数量,并更新累计准确率。

你可以在每个 epoch 后面添加验证的部分和相应的统计。

这样我们已经把一个完整的训练流程框架整理出来,接下来你可以根据需要继续完善验证部分或者添加细节。

1. 模型训练的初始化

我们已经定义了模型加载到合适的设备(如GPU或CPU)、设置了优化器(如Adam)以及损失函数(如交叉熵)。接下来我们进入实际的训练步骤。

2. 训练轮次(Epoch)

首先,我们在训练函数里会有一个 for 循环,循环的次数就是训练的轮次(epochs)。每轮训练的目的是通过反向传播更新模型的参数(WB),以便使损失函数的值逐渐下降,模型的性能不断提高。

3. 批次训练(Mini-batch Training)

对于每轮训练,我们需要将训练数据分成小批次(mini-batches),这样可以节省内存并提高训练速度。常用的方式是用 DataLoader 来迭代批次数据。在每个批次中,我们会:

  • 将数据和标签传入模型。
  • 前向传播:模型计算输出。
  • 计算损失值:利用损失函数计算模型输出和真实标签之间的差异。
  • 反向传播:通过损失值的梯度更新模型参数。
  • 优化器 step():更新模型的权重。

4. 验证集评估

在每个epoch结束时,我们通常会使用验证集来评估模型的性能。验证集不参与模型参数的更新,只用于衡量模型的泛化能力。这里需要关闭梯度计算(torch.no_grad()),以减少内存消耗和加速评估。

5. 保存最佳模型

训练过程中会保存当前最佳模型的参数(即使验证集上的损失最小),用于后续的模型测试或部署。保存的模型通常包括网络结构和权重。

6. 记录训练过程

为了可视化训练进度,我们会保存每个epoch的训练损失、验证损失、训练精度、验证精度等信息,供后续分析和绘图。

总结流程:

  1. 初始化
    • 确定设备、定义优化器和损失函数。
    • 将模型和数据加载到设备上。
  2. 训练循环
    • 对于每个epoch:
      • 对每个mini-batch进行前向传播、计算损失、反向传播、更新参数。
    • 使用验证集评估模型性能。
    • 保存最佳模型。
  3. 记录和输出
    • 保存每轮训练和验证的损失、精度,记录训练时间。

这样,整个训练函数的结构和思路就清晰了,你可以通过实现它来进行模型训练。

 训练反向传播

import time

import torch.optim
from torch import nn
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt
from model import LeNet


def train_val_data_process():
    train_data = FashionMNIST(root='./data',
                              train=True,
                              transform=transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()]),
                              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=128,
                                       shuffle=True,
                                       num_workers=8)

    val_dataloader = Data.DataLoader(dataset=val_data,
                                     batch_size=128,
                                     shuffle=True,
                                     num_workers=8)

    return train_dataloader, val_dataloader


def train_model(model, train_dataloader, val_dataloader, num_epochs):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  # 优化器,梯度下降法进一步拓展是SGD,Adam和SGDM等等等等的

    criterion = nn.CrossEntropyLoss()
    # 均方损失,分类中我们一般用交叉熵损失来更新损失值,然后是w,b
    # 将模型放到训练设备中
    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 + 1, num_epochs))
        print("-" * 10)

        train_loss = 0.0
        train_correct = 0.0
        val_loss = 0.0
        val_correct = 0.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)

首先train_dataloader是训练的所有数据, 加入60000*0.8的数据,其中每个批次有128个意味着,那可能有N个批次的数据一批次一批次就取嘛,直到把这第N个数据取完,假设这里是第一次循环,那么就取第一个批次的数据,如果第二次循环的话,是第二批次的数据,128个向量图,128乘以什么吧,28×28可能还有个通道嘛,这里的什么BX等于这个的,那么很显然BY是什么那BY就是128乘以它的标签嘛,就128个label,放到我们的设备当中这里进行一个训练,进行前向传播,得出这样的一个结果

# 对每一个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)
            #model已经输出来一个值了输出来的值要经过什么经过这个代码啊torch.arg
            #还记得前面我们说我们最后的输出是个神经元是10个值,把这个输出十个值输入到这个soft max里面
            #取概率最大的一个值作为标签,因为它的输出的值是十个值,所以查找每一行当中最大值对应的行标
            #最大对应下标的一个数值

            loss = criterion(output, b_y)#b_y是标签啊,看你的输出和标签利用交叉熵损失函数做对比,output就是你训练的模型训练出来的值,然后和标签值去算损失
            #这里不了解问什么用output和b_y做交叉熵损失函数的可以去看一下交叉熵损失函数的计算公式,计算过程需要用到每一个标签的预测概率

            #梯度值化为0
            optimizer.zero_grad()
            #反向传播计算
            loss.backward()
            #根据网络反向传播的梯度信息更新网路参数,起到降低loss函数计算值的作用
            optimizer.step()
            #对损失函数进行累加
            train_loss += loss.item()*b_x.size()
            #如果预测正确,准确度加1
            train_correct +=torch.sum(pre_lab==b_y.data)
            #当前用于训练的样本数量
            train_num += b_x.size

模型验证

train_loss += loss.item() * b_x.size(0)
            # 如果预测正确,准确度加1
            train_correct += torch.sum(pre_lab == b_y.data)
            # 当前用于训练的样本数量
            train_num += b_x.size(0)

还有我们的样本数量,对不对,我们来解释一下这里的loss值,它获取我们loss值,这个值是什么,是每个样本的,平均值,平均loss值,然后这里为什么要乘什么,乘以我们该样本的数量,乘以我们该样本的数量呢,因为你该你们一批轮次又一批,就假设是100个样本,它的一个平均值,假设平均平均loss乘以乘以100,是不是该批次的累累加,碾压loss对不对,然后你该批次的一个累加loss,是不是等于我们那个train loss,对不对,没有问题吧,因为应该是初始化它是零嘛,你现在把它累加在一起嘛,然后在第二个P4的时候,你再把什么,把把什么这个平均loss乘以什么乘以,我们对那个批次再累加到这个值上面,所以你不断的累加完之后,你会发现你该干嘛,这个train loss这样的一个什么一个值,它是我们我们假设5万个样本的loss值的累加,这里没有问题吧,你发现这里是什么,这里是我们当前训练样本的数量吗,就是每这是什么,这是每批次的数值吗,我们假设一批次是100,假设五个数就500人之后是吧,五五百人之后他就是什么,他就相当于就是我们所有的样本数量,不就已经获得了吗,这里肯定是没有问题,对不对,然后的话这里是什么,是我们所有样本的loss值的累加,对不对,我把那样的loss累加再除以什么,除以我们的level是不是就是什么,我们该轮次的根嘛,平均那个loss值能明白我的意思吗,所以所以我们那个什么,我们验证机不也是一样吗,所以你会发现就是我们loss值除以什么,除以我们的样本数量,就是该轮次的一个平均的一个loss值,加到我们这样的一个列表里面啊,我们所以该该列表里面就获取了什么,该列表一开始是空的嘛,我们定义这个是空的嘛,获取第一个什么第一次训练的一个什么,第一次训练轮次的一个什么loss值,能明白我的意思吗,这里再讲一下啊,这里有点晦涩难懂啊,这里的一个什么loss值,是我们该批次样本的一个什么,该批次比如说100个100个100个样本,它的平均loss,然后乘以对应的嘛,该批次的一个样本数量乘以对应P4,该样本的数量就是什么,就该轮次的就是不该就该批次样本的数量,就是train loss,是不是是吧,Train loss,然后我们一直在循环嘛是吧,把直到把所有的数据是吧,呃训练完之后,是不是到最后最后一个补的,就是我们就是最后一个批次,对不对,最后一个批次的时候啊,计算完之后,这里的值是不是包含了我们所有样本的,该样本的该轮该轮次,该样本的所有的loss值了,所有loss值,对不对啊,所有的loss值,然后这里获取的是什么呃,该轮次的所有的样本的数量,也就是我们训练样本的总数量嘛,都懂我意思吧,实际上你这里不这么写,你你在前面其实也能算出来吗,那我们前面这里已经已经已经算过了,对不对,这个已经算过了,只是为了方便嘛,跟大家统一嘛,你这里都初始化了,我这里初始化一下,对不对是吧,然然后的话,你你你这个这里面那个值,就是我们所有样本的一个loss值的总和,除以什么我们样本数量,那么就该轮次平均的一个loss值了吗是吧

import time

import torch.optim
from torch import nn
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt
from model import LeNet


def train_val_data_process():
    train_data = FashionMNIST(root='./data',
                              train=True,
                              transform=transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()]),
                              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=128,
                                       shuffle=True,
                                       num_workers=8)

    val_dataloader = Data.DataLoader(dataset=val_data,
                                     batch_size=128,
                                     shuffle=True,
                                     num_workers=8)

    return train_dataloader, val_dataloader


def train_model(model, train_dataloader, val_dataloader, num_epochs):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  # 优化器,梯度下降法进一步拓展是SGD,Adam和SGDM等等等等的

    criterion = nn.CrossEntropyLoss()
    # 均方损失,分类中我们一般用交叉熵损失来更新损失值,然后是w,b
    # 将模型放到训练设备中
    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 + 1, num_epochs))
        print("-" * 10)

        train_loss = 0.0
        train_correct = 0.0
        val_loss = 0.0
        val_correct = 0.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)
            # model已经输出来一个值了输出来的值要经过什么经过这个代码啊torch.arg
            # 还记得前面我们说我们最后的输出是个神经元是10个值,把这个输出十个值输入到这个soft max里面
            # 取概率最大的一个值作为标签,因为它的输出的值是十个值,所以查找每一行当中最大值对应的行标
            # 最大对应下标的一个数值

            loss = criterion(output, b_y)  # b_y是标签啊,看你的输出和标签利用交叉熵损失函数做对比,output就是你训练的模型训练出来的值,然后和标签值去算损失
            # 这里不了解问什么用output和b_y做交叉熵损失函数的可以去看一下交叉熵损失函数的计算公式,计算过程需要用到每一个标签的预测概率

            # 梯度值化为0
            optimizer.zero_grad()
            # 反向传播计算
            loss.backward()
            # 根据网络反向传播的梯度信息更新网路参数,起到降低loss函数计算值的作用
            optimizer.step()
            # 对损失函数进行累加
            train_loss += loss.item() * b_x.size(0)
            # 如果预测正确,准确度加1
            train_correct += 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()
                # 前向传播过程,输入为一个patch,输出为一个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_correct + 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_correct.double().item()/train_num)

        #
        val_loss_all.append(val_loss/val_num)
        val_acc_all.append(val_correct.double().item()/val_num)

        print('{} Train Loss:{:.4f} Train Accuracy:{:4f}'.format(epoch, train_loss, train_acc_all[-1]))
        print('{} Val Loss:{:.4f} Val Accuracy:{:4f}'.format(epoch, val_loss_all[-1], val_acc_all[-1]))




P50-P60跳了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值