当前位置: 首页 > article >正文

LeNet网络搭建

LeNet网络搭建

项目准备

首先,在某个磁盘中准备一个文件夹,就叫LeNet吧,作为项目的工程目录,然后配置好python环境。

python环境版本

  • python==3.8
  • Pytorch==1.10.1
  • Cudatookit==11.3
  • Cudnn==8.2
  • torchsummary==1.5.1
  • numpy==1.23.2
  • pandas==1.3.4
  • matplotlib==3.5.0
  • sklearn==0.0

定义模型类

创建一个model.py模型类。

# --model.py--
# 导包
import torch
from torch import nn
from torchsummmary import summary # 展现神经网络的参数

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__() # 要与类名相同
        # 第一层:定义卷积,输入通道数1,输出通道数6,卷积核大小6*6,步长不写默认1,填充2
        self.c1 = nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5,padding=2)
        # 定义激活函数 sigmoid
        self.sig = nn.Sigmoid()
        # 第二层: 定义平均池化 感受野(核)是2*2,步长为2
        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: 接受输入的数据
        x = self.c1(x) # 第一层卷积
        x = self.sig(x) # sigmoid激活函数
        x = self.s2(x) # 第二层平均池化
        x = self.c3(x) # 第三层卷积
        x = self.sig(x) # sigmoid激活函数
        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)
    
    # 输出模型的数据
    # summary(模型类,(输入的特征大小))
    print(summary(model,(1,28,28)))

定义训练类

数据准备

在进行训练模型前,需要提前准备好数据,来给模型训练。

这里准备的是服装的FashionMNIST数据:

  • 数据总量:七万张数据集
  • 数据集大小:28*28*1
  • 分类:10分类
  • 训练集:六万
  • 测试集:一万

创建一个plot.py 文件,用来加载数据,并且绘制出来。

# --plot.py--

# torchvision.datasets库中包含了很多数据集
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, # 要训练集的数据
    # 将数据进行归一化操作
    tranform=transforms.Compose([
        transforms.Resize(size=224), # 将数据转成224大小
        transforms.ToTensor() # 将数据转成张量(Tensor)的形式,方面后续模型的应用
    ]),
    download=True # 开始下载数据
)

# 通过DataLoader()类来加载数据集
train_loader = Data.DataLoader(
    dataset=train_data,# 要加载的数据
    batch_size=64,# 每轮的训练的数据个数是64个
    shuffle=True, # 数据是否打乱
    num_workders=0 # 加载数据的进程
)

# ---- 数据展示部分 ----
# 获得一个Batch(批次)的数据
# b_x:图片数据 b_y:标签
for step,(b_x,b_y) in enumerate(train_loader):
    if step > 0:
        break
# 比如64*224*224*1 将1移除,(批次,长,宽,通道数)
batch_x = b_x.squeeze().numpy() # 将四维张量移除第1维(第一个类别),并转换成numpy数据
batch_y = b_y.numpy() # 将张量转换numpy数组,方便画图
class_label = train_data.classes # 训练集的标签
print(class_label)

# 绘制一个Batch可视化的图像
plt.figure(figsize=(12,5))
for ii ini 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()

上诉代码如果下载成功(只下载了train数据),那么在项目的根目录下就会创建以下文件目录:

LeNet
	-data
		-FashionMNIST
			-raw
				-train-images-idx3-ubyte
				-train-images-idx3-ubyte.gz

模型训练的代码模板

训练集模板

这里定义训练集模板,是为了未来的训练方便使用的,因为训练模型的时候,基本上就按这套模板来进行训练,也就说大部分代码结构是一致的。

创建model_train.py文件做模型训练

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

from model import LeNet  # 导入自定义模型


# 处理训练集(train) 和 验证集(val)
# 每训练一轮就验证一轮
def train_val_data_process():
    # 加载训练数据
    train_data = FashionMNIST(
        root='./data',  # 指定数据集要下载的路径
        train=True,  # 要训练集的数据
        # 将数据进行归一化操作
        tranform=transforms.Compose([
            transforms.Resize(size=28),  # 将数据转成28大小
            transforms.ToTensor()  # 将数据转成张量(Tensor)的形式,方面后续模型的应用
        ]),
        download=True  # 开始下载数据
    )

    # 划分训练集和验证集 将数据集中的数据随机划分为训练集和验证集
    train_data, val_data = Data.random_split(
        train_data,  # 要划分的数据集
        [
            round(0.8 * len(train_data)),  # 将80%的数据集用作训练集
            round(0.2 * len(train_data))  # 将20%的数据集用作验证集
        ]
    )

    # 加载数据
    train_dataloader = Data.DataLoader(
        dataset=train_data,
        batch_size=32,  # 每轮训练的批次
        shuffle=True,
        num_workders=2
    )

    val_dataloader = Data.DataLoader(
        dataset=train_data,
        batch_size=32,  # 每轮训练的批次
        shuffle=True,
        num_workders=2
    )

    return train_dataloader, val_dataloader


# 模型训练
def train_model_process(model, train_dataloader, val_dataloader, num_epochs):
    # model:训练的模型,train_dataloader:训练集,val_dataloader:验证集,num_epochs训练轮数

    # 指定使用的设备
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    # 定义优化器Adam(模型的参数,学习率)
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

    # 定义损失函数 交叉熵损失
    criterion = nn.CrossEntropyLoss()

    # 将模型放入到设备当中 进行训练
    model = model.to(device)

    # 复制当前模型的参数
    best_model_wts = copy.deepcopy(model.state_dict())

    # -- 初始化参数,来记录参数 --
    best_acc = 0.0  # 最高准确度

    train_loss_all = []  # 训练集损失列表
    val_loss_all = []  # 验证集损失列表

    train_acc_all = []  # 训练集准确度列表
    val_acc_all = []  # 验证集准确度列表

    since = time.time()  # 记录训练的时间

    # -- 开始训练模型 --
    for epoch in range(num_epochs):
        print("Epoch {}/{}".format(epoch, num_epochs - 1))  # 打印轮数
        print("-" * 10)

        train_loss = 0.0  # 初始化训练集损失值
        train_corrents = 0  # 初始化训练集精度

        val_loss = 0.0  # 初始化验证集损失值
        val_corrents = 0  # 初始化验证集精度

        train_num = 0  # 该轮次的训练集样本数量
        val_num = 0  # 该轮次的验证集样本数量

        # 取出每轮中的数据集 进行 训练和计算
        for step, (b_x, b_y) in enumerate(train_dataloader):
            # b_x:128*28*28*1(批次,长,宽,通道)
            # b_y:128*label
            b_x = b_x.to(device)  # 将训练数据放入设备
            b_y = b_y.to(device)  # 将标签数据放入设备

            model.train()  # 开启模型训练模型

            # 将每轮数据中的标签标签(batch)放入模型中进行前向传播,得到输出(不是结果)
            output = model(b_x)

            # 查找每一行中最大值对应的行标(索引),即概率最大
            pre_lab = torch.argmax(output, dim=1) # 里面有个softmax的过程

            # 计算每个batch的损失
            loss = criterion(output, b_y)  # 通过模型输出和标签来计算损失值

            # 将梯度初始化为0,每轮训练完后
            optimzer.zero_grad()

            # 反向传播计算
            loss.backward()

            # 根据反向传播的梯度信息来更新网络参数,降低loss函数计算值
            optimzer.step()

            # 对损失函数进行累加
            train_loss += loss.item() * b_x.size(0)

            # 如果预测正确,则准确度+1
            train_corrects += torch.sum(pre_lab == b_y.data)

            # 当前用于训练的样本数量
            train_num += b_x.size(0)

        # 模型验证
        for step, (b_x, b_y) in enumerate(val_dataloader):
            # 将特征放入到验证设备中
            b_x = b_x.to(device)
            # 将标签放入到验证设备中
            b_y = b_y.to(device)

            model.eval()  # 设置模型为评估(验证)模式

            # 前向传播过程,输入一个batch,输出该batch对应的预测
            out_put = model(b_x)

            # 查找每一行中最大值对应的行标,也就是正确的类别
            pre_lab = torch.argmax(output, dim=1)

            # 计算每一个batch的损失函数
            loss = criterion(output, b_y)

            # 对损失函数进行累加
            val_loss += loss.item() * b_x.size(0)

            # 如果预测正确,则准确度+1
            val_corrects += torch.sum(pre_lab == b_y.data)

            # 当前用于验证的样本数量
            val_num += b_x.size(0)

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

        val_loss_all.append(val_loss / val_num)  # 验证集本轮的平均loss值
        val_acc_all.append(val_corrects.double().item() / val_num)  # 验证集本轮的平均准确率

        print("{} train loss:{:.4f} train acc: {:.4f}".format(epoch, train_loss_all[-1], train_acc_all[-1]))
        print("{} val loss:{:.4f} val acc: {:.4f}".format(epoch, val_loss_all[-1], val_acc_all[-1]))
        
        # 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_wts保存
    torch.save(best_model_wts,'best_model.pth') # 保存模型参数 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值和准确度
def matplot_acc_loss(train_process):
    plt.figure(figsize=(12,4))
    plt.subplot(1,2,1) # 一行两列,第一张图
    plt.plot(train_process["epoch"],train_process.train_loss_all,'ro-',label="train loss")
    plt.plot(train_process["epoch"],train_process.val_loss_all,'bs-',label="val loss")
    plt.legend() # 图例
    plt.xlabel("epoch") # x轴标签
    plt.ylabel("loss") # y轴标签

    plt.subplot(1,2,2) # 一行两列,第二张图
    plt.plot(train_process["epoch"],train_process.train_acc_all,'ro-',label="train acc")
    plt.plot(train_process["epoch"],train_process.val_acc_all,'bs-',label="val acc")

    plt.xlabel("epoch") # x轴标签
    plt.ylabel("acc") # y轴标签
    plt.legend() # 图例

    plt.show() # 显示图像
        
        
        
        
        
if __name__ == "__main__":
    # 将自定义的模型 实例化
    LeNet = LeNet()
    
    # 加载数据集
    train_dataloader,val_dataloader = train_val_data_process()
    
    # 训练模型 (要训练的模型,训练集,测试集,训练轮数)
    train_process = train_model_process(LeNet,train_dataloader,val_dataloader,20)
    
    # 绘制图像
    matplot_acc_loss(train_process)
    

以上代码,可以根据具体情况(硬件设备),适当的更改 train_val_data_process()函数 中的 train_dataloaderval_dataloaderbatch_sizenum_workders

执行以上代码,本地会多出一个best_model.pth文件,里面保存的是模型的参数。

测试集模板

创建一个model_test.py文件

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

from model import LeNet # 导入自定义的模型


# 定义一个方法,用来加载测试数据
def test_data_process():
    # 加载测试数据
    test_data = FashionMNIST(
        root='./data',  # 指定数据集要下载的路径
        train=False,  # 不使用训练集
        # 将数据进行归一化操作
        tranform=transforms.Compose([
            transforms.Resize(size=28),  # 将数据转成28大小
            transforms.ToTensor()  # 将数据转成张量(Tensor)的形式,方面后续模型的应用
        ]),
        download=True  # 开始下载数据
    )


    # 通过DataLoader类来加载测试数据
    test_dataloader = Data.DataLoader(
        dataset=test_data,
        batch_size=1,  # 一张一张的测试
        shuffle=True,
        num_workders=0
    )
    return test_dataloader



# 测试模型
def test_model_process(model, test_dataloader):
    # 指定设备
    device = "cuda" if torch.cuda.is_available() else "cpu"
    
    # 将模型放入设备中 测试训练
    model = model.to(device)
    
    # -- 初始化参数 --
    test_corrects = 0.0 # 测试准确度
    test_num = 0 # 测试样本的数量
    
    # 只进行前向传播,不计算梯度,从而节省内存,加快运行速度
    with torch.no_grad(): # 将梯度置为0
        for test_data_x,test_data_y in test_dataloader: # 测试的数据 和 标签
            # 上面设置了批次为1
            test_data_x = test_data_x.to(device) # 将测试数据放入到设备
            test_data_y = test_data_y.to(device) # 将标签数据放入到设备
            
            model.eval() # 设置模型为评估模式
            
            # 前向传播过程,输入为测试数据,输出为每个样本的预测值
            output = model(test_data_x)
            
            # 查找每一行中最大值的行标(索引)
            pre_lab = torch.argmax(output,dim=1) # 里面有个softmax的过程
            
            # 模型预测的结果 与 标签 比较,也就是比对答案,对,就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()
    
    # 加载训练好的模型参数
    model.load_state_dict(torch.load('best_model.pth'))
    
    # 加载测试的数据集
    test_dataloader = test_data_process()
    
    # 加载模型测试的函数,传入要训练的模型,和 测试的数据集
   	test_model_process(model,test_dataloader)
    
            

查看测试过程中的具体数值

# ...以上代码与上面相同

if __name__ == "__main__":
    # 加载模型
    model = LeNet()
    # 加载训练好的模型参数文件
    model.load_state_dict(torch.load('best_model.pth'))
    # 加载测试集数据
    test_dataloader = test_data_process()
    
    # 指定测试所用的设备
    device = "cuda" if torch.cuda.is_available() else "cpu"
    
    # 将模型放入到设备当中
    model = model.to(device)
    
    # 衣服的类别类别,顺序不可以换
    classes = ['T-shirt/top','Trouser','Pullover','Dress','Coat','Sandal','Shirt','Sneaker','Bag','Ankle boot']
    
    # 将梯度设置为0
    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)
            
            # 获取最大值的下标,也就是预测结果,结果是个Tensor张量
            pre_lab = torch.argmax(output,dim=1) # 里面有softmax的过程
            
            # 取出张量中的下标
            result = pre_lab.item() # 预测值的索引
            label = b_y.item() # 标签的索引
            
            print("预测值: ",classes[result],"------","真实值: ",classes[label])


http://www.kler.cn/a/450169.html

相关文章:

  • 链表的详解
  • js事件机制详解
  • OpenCV相机标定与3D重建(26)计算两个二维点集之间的部分仿射变换矩阵(2x3)函数 estimateAffinePartial2D()的使用
  • 24.12.23 注解
  • 脑肿瘤检测数据集,对9900张原始图片进行YOLO,COCO,VOC格式的标注
  • GitCode 光引计划投稿|JavaVision:引领全能视觉智能识别新纪元
  • Linux SHELL脚本中的常用命令
  • 羊城杯2020 easycon
  • 红米Note 9 Pro5G刷小米官方系统
  • git报错:git SSL certificate problem: unable to get local issuer certificate
  • STM32内部flash分区
  • SQL SERVER日常运维巡检系列之-实例级参数
  • sqlalchemy连接dm8 get_columns BIGINT VARCHAR字段不显示
  • W25Q128存储器详解
  • 微服务篇-深入了解 MinIO 文件服务器(你还在使用阿里云 0SS 对象存储图片服务?教你使用 MinIO 文件服务器:实现从部署到具体使用)
  • 如何详细地遵循RustDesk的步骤来搭建远程访问和自定义服务器?
  • 如何使用nvm来管理node版本
  • 线程的安全、volatile、synchronized
  • 【运维笔记】向日葵远程:输入法大写无法切换至小写
  • 【React中最优雅的异步请求】
  • vue3+vite一个IP对站点名称的前端curd更新-会议系统优化
  • Spark-Streaming receiver模式源码解析
  • Redis实现延迟任务 + RedisUtil升级
  • 音频接口:PDM TDM128 TDM256
  • QT-简单视觉框架代码
  • Spring Security 自动踢掉前一个登录用户,一个配置搞定!,网易前端社招面经