经典神经网络(1)LeNet及其在Fashion-MNIST数据集上的应用

news/2025/1/3 6:20:14/

经典神经网络(1)LeNet

1、卷积神经网络LeNet

之前对于Fashion-MNIST服装分类数据集,为了能够应⽤softmax回归和多层感知机,我们⾸先将每个大小为28 × 28的图像展平为⼀个784维的固定⻓度的⼀维向量,然后⽤全连接层对其进⾏处理,此时我们丢失了图像的空间结构。

pytorch基础操作(四)softmax回归手动实现以及pytorch的API实现

pytorch基础操作(五)多层感知机的实现

通过卷积层的处理⽅法,我们可以在图像中保留空间结构。同时,⽤卷积层代替全连接层的另⼀个好处是:模型更简洁、所需的参数更少

1.1 LeNet简述

1.1.1 LeNet概述

LeNet是最早发布的卷积神经⽹络之⼀,因其在计算机视觉任务中的⾼效性能⽽受到⼴泛关注。这个模型是由AT&T⻉尔实验室的研究员Yann LeCun在1989年提出的(并以其命名),⽬的是识别图像中的⼿写数字。

当时,LeNet取得了与⽀持向量机(support vector machines)性能相媲美的成果,成为监督学习的主流⽅法。LeNet被⼴泛⽤于⾃动取款机(ATM)机中,帮助识别处理⽀票的数字。

1.1.2 LeNet的原始的网络架构

在这里插入图片描述

注:feature map 的描述有两种:channel first,如256x3x3channel last,如3x3x256

LeNet 网络包含了卷积层、池化层、全连接层,这些都是现代CNN 网络的基本组件。

  • 输入层:二维图像,尺寸为32x32

  • C1、C3、C5 层:二维卷积层。

    其中C5 将输入的 feature map(尺寸 16@5x5 )转化为尺寸为120x1x1feature map,然后转换为长度为120 的一维向量。

    这是一种常见的、将卷积层的输出转换为全连接层的输入的一种方法。

  • S2、S4 层:池化层。使用sigmoid 函数作为激活函数。

    后续的 CNN 都使用ReLU 作为激活函数。

  • F6 层:全连接层。

  • 输出层:由欧式径向基函数单元组成。

    后续的CNN 使用softmax 输出单元。

网络层核/池大小核数量步长输入尺寸输出尺寸
INPUT----1@32x32
C15x5611@32x326@28x28
S22x2-26@28x286@14x14
C35x51616@14x1416@10x10
S42x2-216@10x1016@5x5
C55x5120116@5x5120@1x1
F6---12084
OUTPUT---8410

1.2 架构图解释

我们将对原始模型做了⼀点⼩改动,去掉了最后⼀层的⾼斯激活,简化为下图。

在这里插入图片描述

  • 每个卷积块中的基本单元是⼀个卷积层、⼀个sigmoid激活函数和平均汇聚层。请注意,虽然ReLU和最⼤汇聚层更有效,但它们在20世纪90年代还没有出现。

  • 每个卷积层使⽤5 × 5卷积核和⼀个sigmoid激活函数。这些层将输⼊映射到多个⼆维特征输出,通常同时增加通道的数量。第⼀卷积层有6个输出通道,⽽第⼆个卷积层有16个输出通道。每个2 × 2池操作(步幅2)通过空间下采样将维数减少4倍。卷积的输出形状由批量⼤大小、通道数、高度、宽度决定。

  • 为了将卷积块的输出传递给稠密块,我们必须在⼩批量中展平每个样本。我们将这个四维输⼊转换成全连接层所期望的⼆维输⼊。这⾥的⼆维表示的第⼀个维度索引小批量中的样本,第⼆个维度给出每个样本的平⾯向量表⽰。LeNet的稠密块有三个全连接层,分别有120、84和10个输出。因为我们在执⾏分类任务,所以输出层的10维对应于最后输出结果的数量。

1.3 LeNet在Fashion-MNIST数据集上的应用代码

1.3.1 定义LeNet模型

import torch.nn as nn
import torchclass LeNet5(nn.Module):def __init__(self):super().__init__()self.model = nn.Sequential(nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2, stride=1),nn.Sigmoid(),nn.AvgPool2d(kernel_size=2,stride=2),nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5),nn.Sigmoid(),nn.AvgPool2d(kernel_size=2,stride=2),nn.Flatten(),nn.Linear(in_features=16 * 5 * 5, out_features=120),nn.Sigmoid(),nn.Linear(in_features=120, out_features=84),nn.Sigmoid(),nn.Linear(in_features=84, out_features=10))def forward(self, X):X = self.model(X)return Xif __name__ == '__main__':net = LeNet5()# 测试神经网络是否可运行# inputs = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)# outputs = net(inputs)# print(outputs.shape)# 查看每一层输出的shapeX = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)for layer in net.model:X = layer(X)print(layer.__class__.__name__, 'output shape:', X.shape)
Conv2d output shape: torch.Size([1, 6, 28, 28])
Sigmoid output shape: torch.Size([1, 6, 28, 28])
AvgPool2d output shape: torch.Size([1, 6, 14, 14])
Conv2d output shape: torch.Size([1, 16, 10, 10])
Sigmoid output shape: torch.Size([1, 16, 10, 10])
AvgPool2d output shape: torch.Size([1, 16, 5, 5])
Flatten output shape: torch.Size([1, 400])
Linear output shape: torch.Size([1, 120])
Sigmoid output shape: torch.Size([1, 120])
Linear output shape: torch.Size([1, 84])
Sigmoid output shape: torch.Size([1, 84])
Linear output shape: torch.Size([1, 10])
  • 在整个卷积块中,与上⼀层相⽐,每⼀层特征的⾼度和宽度都减⼩了。

    • 第⼀个卷积层使⽤2个像素的填充,来补偿5 × 5卷积核导致的特征减少。

    • 第⼆个卷积层没有填充,因此⾼度和宽度都减少了4个像素。

  • 随着层叠的上升,通道的数量从输⼊时的1个,增加到第⼀个卷积层之后的6个,再到第⼆个卷积层之后的16个。同时,每个汇聚层的⾼度和宽度都减半。

  • 最后,每个全连接层减少维数,最终输出⼀个维数与结果分类数相匹配的输出。

1.3.2 读取Fashion-MNIST数据集

# 通过框架中的内置函数将Fashion-MNIST数据集下载并读取到内存中
# Fashion-MNIST是⼀个服装分类数据集,由10个类别的图像组成
# Fashion-MNIST由10个类别的图像组成,每个类别由训练数据集(train dataset)中的6000张图像和测试数据集(test dataset)中的1000张图像组成。
# 因此,训练集和测试集分别包含60000和10000张图像。'''读取服装分类数据集 Fashion-MNIST
'''
import torchvision
import torch
from torch.utils import data
from torchvision import transformsdef get_dataloader_workers():"""使⽤4个进程来读取数据"""return 4def get_mnist_data(batch_size, resize=None):trans = [transforms.ToTensor()]if resize:# 还接受⼀个可选参数resize,⽤来将图像⼤⼩调整为另⼀种形状trans.insert(0,transforms.Resize(resize))trans = transforms.Compose(trans)# 需要下载,可以设置为Truemnist_train = torchvision.datasets.FashionMNIST(root='./data',train=True,transform=trans,download=False)mnist_test = torchvision.datasets.FashionMNIST(root='./data',train=False,transform=trans,download=False)# 数据加载器每次都会读取⼀⼩批量数据,⼤⼩为batch_size。通过内置数据迭代器,我们可以随机打乱了所有样本,从⽽⽆偏⻅地读取⼩批量# 数据迭代器是获得更⾼性能的关键组件。依靠实现良好的数据迭代器,利⽤⾼性能计算来避免减慢训练过程。train_iter = data.DataLoader(mnist_train,batch_size=batch_size,shuffle=True,num_workers=get_dataloader_workers())test_iter = data.DataLoader(mnist_test,batch_size=batch_size,shuffle=True,num_workers=get_dataloader_workers())return (train_iter,test_iter)batch_size = 256train_iter,test_iter = get_mnist_data(batch_size)

1.3.3 定义通用的网络模型训练函数

1、先定义几个类,用来计算精确率,画图,计算训练时间等

累加类Accumulator

'''
定义⼀个实⽤程序类Accumulator,⽤于对多个变量进⾏累加
'''
class Accumulator():"""在n个变量上累加"""def __init__(self, n):self.data = [0.0] * ndef add(self, *args):self.data = [a + float(b) for a,b in zip(self.data, args)]def reset(self):self.data = [0.0] * len(self.data)def __getitem__(self,index):return self.data[index]

时间类Timer

import timeclass Timer:"""Record multiple running times."""def __init__(self):"""Defined in :numref:`subsec_linear_model`"""self.times = []self.start()def start(self):"""Start the timer."""self.tik = time.time()def stop(self):"""Stop the timer and record the time in a list."""self.times.append(time.time() - self.tik)return self.times[-1]def avg(self):"""Return the average time."""return sum(self.times) / len(self.times)def sum(self):"""Return the sum of time."""return sum(self.times)def cumsum(self):"""Return the accumulated time."""return np.array(self.times).cumsum().tolist()

绘图类Animator

from matplotlib import pyplot as plt
from IPython import displaydef set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):"""设置matplotlib的轴"""axes.set_xlabel(xlabel)axes.set_ylabel(ylabel)axes.set_xscale(xscale)axes.set_yscale(yscale)axes.set_xlim(xlim)axes.set_ylim(ylim)if legend:axes.legend(legend)axes.grid()class Animator():"""在动画中绘制数据"""def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,ylim=None, xscale='linear', yscale='linear',fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,figsize=(3.5, 2.5)):# 增量地绘制多条线if legend is None:legend = []self.fig, self.axes = plt.subplots(nrows, ncols, figsize=figsize)if nrows * ncols == 1:self.axes = [self.axes, ]# 使用lambda函数捕获参数self.config_axes = lambda: set_axes(self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)self.X, self.Y, self.fmts = None, None, fmtsdef add(self, x, y):# 向图表中添加多个数据点if not hasattr(y, "__len__"):y = [y]n = len(y)if not hasattr(x, "__len__"):x = [x] * nif not self.X:self.X = [[] for _ in range(n)]if not self.Y:self.Y = [[] for _ in range(n)]for i, (a, b) in enumerate(zip(x, y)):if a is not None and b is not None:self.X[i].append(a)self.Y[i].append(b)self.axes[0].cla()for x, y, fmt in zip(self.X, self.Y, self.fmts):self.axes[0].plot(x, y, fmt)self.config_axes()display.display(self.fig)display.clear_output(wait=True)

2、定义训练函数

import torch.nn as nn
from AccumulatorClass import Accumulatordef accuracy(y_hat, y):"""计算预测正确的数量"""if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:y_hat = y_hat.argmax(axis=1)cmp = y_hat.type(y.dtype) == yreturn float(cmp.type(y.dtype).sum())def evaluate_accuracy_gpu(net, data_iter, device=None):"""使⽤GPU计算模型在数据集上的精度"""if isinstance(net, nn.Module):net.eval() # 设置为评估模式if not device:device = next(iter(net.parameters())).device# 正确预测的数量,总预测的数量metric = Accumulator(2)with torch.no_grad():for X, y in data_iter:if isinstance(X, list):# BERT微调所需的X = [x.to(device) for x in X]else:X = X.to(device)y = y.to(device)metric.add(accuracy(net(X), y), y.numel())return metric[0] / metric[1]
from AnimatorClass import Animator
from TimerClass import Timerdef train_ch(net, train_iter, test_iter, num_epochs, lr, device):"""⽤GPU训练模型"""def init_weights(m):if type(m) == nn.Linear or type(m) == nn.Conv2d:nn.init.xavier_uniform_(m.weight)# 初始化权重net.apply(init_weights)print('training on', device)net.to(device)# 梯度下降optimizer = torch.optim.SGD(net.parameters(), lr=lr)# 交叉熵损失loss = nn.CrossEntropyLoss()animator = Animator(xlabel='epoch', xlim=[1, num_epochs],legend=['train loss', 'train acc', 'test acc'])timer, num_batches = Timer(), len(train_iter)num_batches = len(train_iter)for epoch in range(num_epochs):# 训练损失之和,训练准确率之和,样本数metric = Accumulator(3)net.train()for i, (X, y) in enumerate(train_iter):timer.start()optimizer.zero_grad()X, y = X.to(device), y.to(device)y_hat = net(X)l = loss(y_hat, y)l.backward()optimizer.step()with torch.no_grad():metric.add(l * X.shape[0], accuracy(y_hat, y), X.shape[0])timer.stop()train_l = metric[0] / metric[2]train_acc = metric[1] / metric[2]if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:animator.add(epoch + (i + 1) / num_batches,(train_l, train_acc, None))test_acc = evaluate_accuracy_gpu(net, test_iter)animator.add(epoch + 1, (None, None, test_acc))print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, test acc {test_acc:.3f}')print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec on {str(device)}')

1.3.4 利用LeNet进行训练

from _01_LeNet5 import LeNet5def try_gpu(i=0):if torch.cuda.device_count() >= i + 1:return torch.device(f'cuda:{i}')return torch.device('cpu')# 初始化模型
net = LeNet5()lr, num_epochs = 0.9, 10
train_ch(net, train_iter, test_iter, num_epochs, lr, try_gpu())

结果如下:

在这里插入图片描述

注:卷积神经网络通俗解释

大白话讲解卷积神经网络工作原理

从“卷积”、到“图像卷积操作”、再到“卷积神经网络”,“卷积”意义的3次改变


http://www.ppmy.cn/news/63737.html

相关文章

关于使用SSM框架搭建的项目的运行方法

目录 运行环境配置 1、安装 IDEA 开发工具 中文版设置 JDK直接下载 2、安装 MYSQL 数据库 2.1 下载安装 2.2 配置环境变量 2.4 安装 MySQL 2.4 进入 MySQL 2.5 常见问题 3、安装Tomcat 4、IDEA配置MYSQL 4.1、常见错误 5、IDEA配置TOMCAT 5.1、常见报错 一 运行环…

PCIe热插拔机制(详细)总结-PCIe专题知识(五)

目录 前言一、概述二、原理详解2.1 热插拔原理总结2.2 热插拔软硬件要求 三、其他相关知识链接1、PCIe物理层总结-PCIE专题知识(一)2、PCIe数据链路层图文总结-PCIe专题知识(二)3、PCIe物理层链路训练和初始化总结-PCIe专题知识&a…

OpenAI ChatGPT Unity接入

OpenAI ChatGPT Unity接入 OpenAI ChatGPT Unity接入OpenAi-API-Unity 方法OpenAi-API-Unity 下载本地配置Unity 模块URL接入gz 接入json 接入Open AIOpenAi-Api-Unity 插件文档 OpenAi 本地化接入 Unity 方法Unity 关键字识别语音合成 & 文字转语音音频记录 & 实时音频…

JavaScript 进阶知识点概述

目录 第一部分: 垃圾回收 1. 什么是垃圾回收? 2. JavaScript 的垃圾回收机制 3. 新生代和老生代的概念 4. 垃圾回收算法和策略 第二部分: 事件循环 5. 什么是事件循环? 6. 宏任务和微任务 7. Event Loop 的执行顺序 8. 避免阻塞 GUI 线程的 Jav…

系统移植——linux内核移植——分析内核编译过程

uImage镜像文件 1.进入linux内核源码目录 ubuntuubuntu:~$ cd FSMP1A/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61/ 打开Makefile文件 vi Makefile 搜索include 因为 $(SRCARCH)->arm 所以上述指令为 arch/arm/Makefile 2.进入linux内核源码目录下,arch/arm目录下…

Kafka的概念|架构|搭建|查看命令

Kafka的概念|架构|搭建|查看命令 一 Kafka 概述二 使用消息队列的好处三Kafka 定义3.1Kafka 简介3.2Kafka 的特性3.3 Kafka 系统架构3.4 Partation 数据路由规则 四 kafka的架构五 搭建kafka5.1环境准备5.2安装kafka5.3 修改配置文件5.4 编辑其他二台虚拟机的配置文件5.5 编辑三…

CA OpenSSL自签名证书(服务器/客户端)

参考文章 https://juejin.cn/post/7092789498823573518 https://blog.csdn.net/mengting2040/article/details/120001810 目录 使用 OpenSSL 生成证书创建根证书创建 Root Pair创建 Root Key创建 Root Crt 创建服务器端证书创建服务器端keyip需要换成自己服务器的外网ip地址&am…

Oracle限制单个用户的并发连接数

Oracle限制单个用户的并发连接数 开启RESOURCE_LIMIT参数查看对用户的资源限制限制用户的并发连接数 开启RESOURCE_LIMIT参数 检查资源限制是否开启: SQL> show parameter resource_limitNAME TYPE VALUE ---- ---- ----- resource_limit boolean TRUE这个参数…