你的第一个神经网络!经典网络LeNet复现

软件环境:Jupyter notebook

硬件环境:GPU:RTX4060

实验数据集:Fashion-MNIST

本文主要实现了经典入门神经网络LeNet结构

Lenet是一个 7 层的神经网络,包含 3 个卷积层,2 个池化层,1 个全连接层。其中所有卷积层的所有卷积核都为 5x5,步长 strid=1,池化方法都为平均pooling,激活函数为 Sigmoid,网络结构如下:

在此不对LeNet的结构做详细解读 直接开始复现

1.导入必要的包

import torch
from torch import nn
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.utils.data as Data
import copy
import time

2.定义LeNet结构

首先用Class定义一个LeNet类,继承自pytorch的nn.moudle模块。

直接调用Conv2d函数定义卷积层,采用Sigmod函数做激活函数,采用AvgPool2d做平均池化。然后定义三个线性卷积层,完成400个输入到10个输出的连接

然后在类中定义forward前向传播函数,将所有的卷积层池化层激活函数和线性全连接层做一个连接 完全前向传播。

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2)
        self.sig = nn.Sigmoid()
        self.pool1 = nn.AvgPool2d(stride=2, kernel_size=2)
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
        self.pool2 = nn.AvgPool2d(stride=2, kernel_size=2)
        self.flatten = nn.Flatten()
        self.Lin1 = nn.Linear(400, 120)
        self.Lin2 = nn.Linear(120, 84)
        self.Lin3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = self.sig(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = self.sig(x)
        x = self.pool2(x)
        x = self.flatten(x)
        x = self.Lin1(x)
        x = self.Lin2(x)
        x = self.Lin3(x)
        return x

3.相关函数定义

train_val_data_process

transform调用了transforms里的剪裁函数 对图像进行裁剪成28尺寸 并转换成张量。

train_data的直接调用datrasets数据集中的FashionMNIST数据集,root=指定下载路径,train=ture代表下载的是训练集,download表示确定下载。

定义train_size=0.8*int(len(train_data))完成数据集的划分,80%用于训练,20%用于测试,使用Data.random_split完成随即划分。继续定义train_dataloader和val_dataloader用于加载数据,指定批次大小为128,8线程并行处理,shuffle=True代表打乱。

def train_val_data_process():
    transform = transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()])

    train_data = FashionMNIST(root="D:/jupyter/sklearn", train=True, transform=transform, download=True)
    train_size = int(0.8 * len(train_data))
    val_size = len(train_data) - train_size
    train_data, val_data = Data.random_split(train_data, [train_size, val_size])

    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,训练轮次,dataloader以及保存路径

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")此句表示定义训练设备为gpu(如果gpu可用),反之使用cpu完成训练。定义优化器为Adam优化器,学习率为1e-3。使用交叉熵函数作为损失函数。model=model.to(device)表示将模型移到gpu上。

使用循环开始迭代 ,每次迭代时先输入迭代轮次和当前的时间。初始化训练损失和准确率都为0

使用model.train()将模型转换为训练模式 内层循环遍历数据集所有数据 optimizer.zero_grad()完成梯度清零,防止梯度累计最终导致梯度爆炸。输出output并用loss记录交叉熵损失。loss.backward()根据loss值完成反向传播,optimizer.step()表示根据反向传播的结果进行参数的更新。如此完成训练。

使用model.eval()转换为验证模式。验证模式只需要计算损失和准确率即可,不需要进行反向传播和参数更新。其中准确率的计算如下:

  • torch.sum(preds == b_y.data):比较模型预测的类别与实际标签是否相等,返回一个布尔值的张量。torch.sum 统计 True 的数量,即正确预测的数量。
  • train_corrects 记录训练集上的正确预测总数。
  • train_acc 计算训练集上的准确率,即正确预测的数量除以训练集样本总数。
def train_model_process(model, epochs, train_dataloader, val_dataloader, save_path='LeNet/best_model.pth'):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
    loss_fn = nn.CrossEntropyLoss()

    model = model.to(device)

    best_model_wts = copy.deepcopy(model.state_dict())
    best_acc = 0.0

    for epoch in range(epochs):
        start_time = time.time()
        print("第 {}/{} 次迭代".format(epoch, epochs - 1))
        print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time)))

        train_loss = 0.0
        train_corrects = 0

        val_loss = 0.0
        val_corrects = 0

        # 训练阶段
        model.train()
        for step, (b_x, b_y) in enumerate(train_dataloader):
            b_x = b_x.to(device)
            b_y = b_y.to(device)

            optimizer.zero_grad()
            output = model(b_x)
            loss = loss_fn(output, b_y)
            loss.backward()
            optimizer.step()

            train_loss += loss.item() * b_x.size(0)
            _, preds = torch.max(output, 1)
            train_corrects += torch.sum(preds == b_y.data)

        # 验证阶段
        model.eval()
        with torch.no_grad():
            for step, (b_x, b_y) in enumerate(val_dataloader):
                b_x = b_x.to(device)
                b_y = b_y.to(device)

                output = model(b_x)
                loss = loss_fn(output, b_y)

                val_loss += loss.item() * b_x.size(0)
                _, preds = torch.max(output, 1)
                val_corrects += torch.sum(preds == b_y.data)

        # 计算准确率
        train_acc = train_corrects.double() / len(train_dataloader.dataset)
        val_acc = val_corrects.double() / len(val_dataloader.dataset)

        print("训练 Loss: {:.4f}, 准确率: {:.4f}".format(train_loss, train_acc))
        print("验证 Loss: {:.4f}, 准确率: {:.4f}".format(val_loss, val_acc))

        # 输出本次epoch的时间
        end_time = time.time()
        elapsed_time = end_time - start_time
        print("耗时: {:.0f}m {:.0f}s".format(elapsed_time // 60, elapsed_time % 60))

        # 保存最佳模型
        if val_acc > best_acc:
            best_acc = val_acc
            best_model_wts = copy.deepcopy(model.state_dict())

    # 加载并保存最佳模型
    model.load_state_dict(best_model_wts)
    torch.save(model.state_dict(), save_path)

5.实例化LeNet类并运行

实例化一个LeNet_model,完成数据集的划分和调用

这里我只训练了二十轮,大家可以自行修改epochs的值训练到模型收敛,收敛后准确率应该在0.86。

LeNet_model = LeNet()
train_dataloader, val_dataloader = train_val_data_process()
train_model_process(LeNet_model, epochs=10, train_dataloader=train_dataloader, val_dataloader=val_dataloader)

6.结果展示

共完成了20次训练 可以看到模型收敛 准确率基本在0.86 没有出现过拟合与欠拟合的问题

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值