【深度学习】卷积神经网络(LeNet)【文章重新修改中】

2023-09-14 18:59:53

前言

全连接神经网络,也称多层感知机, M L P MLP MLP,是深度学习最基本的神经网络之一。它包含输入层,多个隐藏层和输出层,每一层都与前一层的每个神经元相连接。尽管全连接神经网络具有一定的表达能力,其并不是解决所有问题的最佳工具。

e . g . e.g. e.g. 假设我们有一张 1000 ∗ 1000 1000 * 1000 10001000 像素的彩色照片,假设全连接层输出个数为 256 256 256,那么该层权重参数的形状是 3000000 ∗ 256 3 000 000 * 256 3000000256,即会占用 3 G B 3GB 3GB 的内存或显存。会导致复杂的模型与过高的存储开销。

在这里插入图片描述

卷积层试图解决这个问题。卷积层通过滑动窗口将同一卷积核与不同位置的输入重复计算,从而避免参数尺寸过大。而卷积神经网络,就是包含卷积层的网络。

LeNet 作为早期用来识别手写数字图像的卷积神经网络,名称来源于 Yann LeCun。其展示了通过梯度下降训练卷积神经网络可以达到手写数字识别在当时最先进的结果。


LeNet 模型

在这里插入图片描述

LeNet 模型分为卷积层块和全连接层块两个部分;

卷积层块里的基本单位是卷积层后接最大池化层:卷积层用来识别图像里的空间模式,如线条和物体局部,之后的最大池化层则是用来降低卷积层对位置的敏感性。卷积层,由这两个基本单位重复堆叠构成。

在这里插入图片描述

具体来说,在每个卷积层块中,每个卷积层都使用 5 ∗ 5 5*5 55 的窗口,并在输出上使用 s i g m o i d sigmoid sigmoid 激活函数;第一个卷积层输出通道数为 6 6 6,第二个卷积层输出通道数增加到 16 16 16。卷积层块的两个最大池化层的窗口形状均为 2 ∗ 2 2*2 22,且步幅为 2 2 2


代码实现

MINST

在这里插入图片描述

代码分块解析

1 构建 LeNet 网络结构
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5)   # 1个输入通道,6个输出通道,卷积核大小为5x5
        self.pool = nn.MaxPool2d(2, 2)   # 最大池化层,2x2窗口
        self.conv2 = nn.Conv2d(6, 16, 5)  # 6个输入通道,16个输出通道,卷积核大小为5x5
        self.fc1 = nn.Linear(16*4*4, 120) # 全连接层1
        self.fc2 = nn.Linear(120, 84)     # 全连接层2
        self.fc3 = nn.Linear(84, 10)      # 输出层,10个类别

    def forward(self, x):
        x = self.pool(torch.relu(self.conv1(x)))
        x = self.pool(torch.relu(self.conv2(x)))
        x = x.view(-1, 16*4*4)
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        x = torch.softmax(x, dim=1)
        return x

- 代码解释
super(LeNet, self).__init__() 是 python 中用于调用父类(或超类)的构造函数的一种方式。在上述定义中,用于在子类 LeNet 的构造函数中调用父类 nn.model 的构造函数。

具体来说:

  1. super(LeNet, self) 使用了 super() 函数创建了一个与子类 LeNet 相关联的 super 对象。这个 super 对象可以用来访问父类的方法和属性。
  2. .__init__() 调用 super 对象的构造函数,即调用父类 nn.model 的构造函数。确保子类 super 继承父类 nn.model 的所有属性和方法。

总之,super(LeNet, self).__init__() 目的是在子类 LeNet 的构造函数中初始化父类 nn.model。这是面向对象编程中用于构建继承层次结构中的子类。

- 代码解释
forward(self, x) 是神经网络中定义前向传播的方法。这个方法定义了一个张量 x,按照 LeNet 网络的结构将其传递给不同层,最终计算出网络的输出。

具体来说:

  1. x = self.pool(torch.relu(self.conv1(x))) 首先,输入 x 经过第一个卷积层 self.conv1(x),然后使用 ReLU 激活函数进行激活,接着使用 self.pool 进行最大池化操作;
  2. x = self.pool(torch.relu(self.conv2(x))) 然后,将前一步的输出再次经过第二个卷积层 self.conv2,然后使用 ReLU 激活函数进行激活,接着使用 self.pool 进行最大池化操作;
  3. x = x.view(-1, 16*5*5) 在进入到全连接层前,需要将池化层的输出展平为一维向量,通过 view 函数实现方法:
    e . g . e.g. e.g. 如果 x 的形状是 [ 64 , 16 , 5 , 5 ] [64, 16, 5, 5] [64,16,5,5](其中 batch_size 是 64,num_channels 是16,height 和 width 都是 5 ),那么 x.view(-1, 16 * 5 * 5) 将会将 x 的形状调整为 [ 64 , 16 ∗ 5 ∗ 5 ] [64, 16 * 5 * 5] [64,1655],也就是 [ 64 , 400 ] [64, 400] [64,400],作为全连接层的输入。
  4. x = torch.relu(self.fc1(x)) 将展平后的数据传递给第一个全连接层 self.fc1,然后使用 ReLU 函数进行激活;
  5. x = torch.relu(self.fc2(x)) 将第一个全连接层的输出传递给第二个全连接层 self.fc2,然后使用 ReLU 函数进行激活;
  6. x = self.fc3(x) x = torch.softmax(x, dim=1) 最后将第二个全连接层的输出传递给输出层 self.fc3,使用 softmax 获得概率分布;Softmax 将网络的原始输出值转化为 0 到 1 之间的概率值,以表示每个类别的预测概率。

2 加载数据集
# 数据标准化处理
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
# 加载数据集
trainset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
# 创建数据加载器 Loader
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

- 代码解释
transforms.ToTensor() 将图像从 PIL 图像对象转换为 PyTorch 张量,深度学习模型使用张量作为输入;

- 代码解释
transforms.Normalize((0.5,), (0.5,)) 对图像进行归一化操作。
参数 (0.5,)(0.5,) 表示均值和标准差,将图像像素值从 0 到 255 缩放到 -1 到 1 之间,以加速模型的训练过程。对于 MINST 数据集来说,因为只有一个通道(灰度图像),因此只有一个均值和一个标准差。


3 初始化模型和优化器
# 实例化网络对象
net = LeNet()
# 损失函数使用交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 优化器,使用随机梯度下降
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

- 代码解释
CrossEntropyLoss() 交叉熵损失函数:通常用于多类别分类任务,例如图像分类。

- 代码解释
optim.SGD 即使用随机梯度下降作为优化器,SGD(Stochastic Gradient Descend)
net.parameters() 代表优化器会将神经网络中所有可学习参数不断更新权重;
lr 即 learning rate 学习率,控制优化器每次权重更新的步长;
momentum=0.9 动量,关于动量的概念将在后期单独出一期博文解析,读者在这里可以得知的是动量是一种加速优化过程的技巧,有助于跳出局部最小值。


4 训练模型
for epoch in range(10):  	# 遍历数据集 10 次
    running_loss = 0.0		# 损失值
    for i, data in enumerate(trainloader, 0):
        # 每个批次中,数据data包含了输入inputs和相应的标签labels
        inputs, labels = data
        # zero_grad 方法将优化器中的梯度清零,计算新的梯度
        optimizer.zero_grad()
        # 将输入数据传递给神经网络 net 进行前向传播,计算模型的输出 outputs
        outputs = net(inputs)
        # 计算模型的输出与真实标签之间的损失
        loss = criterion(outputs, labels)
        # 根据损失值计算梯度,使用反向传播算法
        loss.backward()
        # 使用优化器 optimizer 更新网络的参数,减小损失值
        optimizer.step()
		# 将损失值累积到 running_loss 中
        running_loss += loss.item()
        if i % 200 == 199:  # 每 200 批次打印一次损失
        	# {i + 1:5d} 是一种字符串格式化的语法,用于将整数 i + 1 格式化为宽度为5的右对齐整数。
            print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 200:.3f}')
            running_loss = 0.0

5 训练完成
print('Finished Training')

完整代码

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms

# 定义LeNet模型
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5)   # 1个输入通道,6个输出通道,卷积核大小为5x5
        self.pool = nn.MaxPool2d(2, 2)   # 最大池化层,2x2窗口
        self.conv2 = nn.Conv2d(6, 16, 5)  # 6个输入通道,16个输出通道,卷积核大小为5x5
        self.fc1 = nn.Linear(16*4*4, 120) # 全连接层1
        self.fc2 = nn.Linear(120, 84)     # 全连接层2
        self.fc3 = nn.Linear(84, 10)      # 输出层,10个类别

    def forward(self, x):
        x = self.pool(torch.relu(self.conv1(x)))
        x = self.pool(torch.relu(self.conv2(x)))
        x = x.view(-1, 16*4*4)
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        x = torch.softmax(x, dim=1)
        return x

# 加载数据集
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
trainset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

# 初始化模型和优化器
net = LeNet()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

# 训练模型
for epoch in range(10):  # 遍历数据集10次
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data
        optimizer.zero_grad()
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if i % 200 == 199:  # 每200批次打印一次损失
            print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 200:.3f}')
            running_loss = 0.0

print('Finished Training')

Fashion-MINST

在这里插入图片描述

代码分块解析

1 构建 LeNet 网络结构
net = nn.Sequential()
net.add(
    nn.Conv2D(channels=6, kernel_size=5, activation='sigmoid'),
    nn.MaxPool2D(pool_size=2, strides=2),
    nn.Conv2D(channels=16, kernel_size=5, activation='sigmoid'),
    nn.MaxPool2D(pool_size=2, strides=2),
    nn.Dense(120, activation='sigmoid'),
    nn.Dense(84, activation='sigmoid'),
    nn.Dense(10)
)

使用 nn.Sequential() 创建 LeNet 模型,包括卷积层、池化层和全连接层,其中激活函数为 sigmoid。


2 初始化模型参数
ctx = d2l.try_gpu()
# force_reinit在初始化之前强制重新初始化模型参数
# init.Xavier有助于避免梯度消失和梯度爆炸,提高模型的收敛速度
net.initialize(force_reinit=True, ctx=ctx, init=init.Xavier())

3 加载数据集
# 加载Fashion-MNIST数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size, resize=96)

4 定义损失函数和优化器
# 定义损失函数和优化器
loss = gloss.SoftmaxCrossEntropyLoss()
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.9})

使用交叉熵损失函数来计算损失,使用梯度随机下降优化器 sgd 进行模型参数的优化。


5 训练模型
# 训练模型
num_epochs = 10
d2l.train_ch5(net, train_iter, test_iter, batch_size, trainer, ctx, num_epochs)

完整代码

import d2lzh as d2l
from mxnet import autograd, gluon, init, nd
from mxnet.gluon import loss as gloss, nn

# 定义LeNet模型
net = nn.Sequential()
net.add(
    nn.Conv2D(channels=6, kernel_size=5, activation='sigmoid'),
    nn.MaxPool2D(pool_size=2, strides=2),
    nn.Conv2D(channels=16, kernel_size=5, activation='sigmoid'),
    nn.MaxPool2D(pool_size=2, strides=2),
    nn.Dense(120, activation='sigmoid'),
    nn.Dense(84, activation='sigmoid'),
    nn.Dense(10)
)

# 初始化模型参数
ctx = d2l.try_gpu()
net.initialize(force_reinit=True, ctx=ctx, init=init.Xavier())

# 加载Fashion-MNIST数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size, resize=96)

# 定义损失函数和优化器
loss = gloss.SoftmaxCrossEntropyLoss()
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.9})

# 训练模型
num_epochs = 10
d2l.train_ch5(net, train_iter, test_iter, batch_size, trainer, ctx, num_epochs)

参考与更多阅读材料

更多推荐

网络编程day04(网络属性函数、广播、组播、TCP并发)

今日任务对于newfd的话,最好是另存然后传入给分支线程,避免父子线程操作同一个文件描述符1.广播:接收端代码:#include<stdio.h>#include<string.h>#include<stdlib.h>#include<sys/types.h>/*SeeNOTES*/#include<sys/socke

如何通过一键导出导入数据实现批量重命名文件名称

在日常办公中,我们经常需要对大量的文件进行重命名,以便更好地管理和查找文件。而且,有时候我们还需要将文件名称翻译成其他语言,以适应不同的工作需求。如何高效地完成这项任务呢?接下来,我将介绍一种方法,让你事半功倍。首先,你需要下载并安装一个名为“固乔文件管家”的软件。这是一款功能强大的文件管理工具,可以帮助你批量改名文件

山洪灾害预警方案(山洪预警解决方案的组成)

​随着气候变化的不断加剧,山洪灾害在许多地区成为了极具威胁性的自然灾害之一。为了帮助地方政府和居民更好地预防和应对山洪灾害,我们设计了一套基于星创易联的SR600工业路由器和DTU200的山洪灾害预警方案,并成功在某地区进行了部署。案例背景:我们选择了某山区作为实际部署的案例。这个地区常年受山洪的威胁,由于地处偏远,传

使用 Verilator 进行 Verilog Lint

FPGA设计是无情的,所以我们需要利用能获得的任何软件进行检查。Verilator是一个Verilog仿真器,还支持linting:静态分析设计中的问题。Verilator不仅可以发现综合工具可能忽略的问题,而且运行速度也很快。Verilator也非常适合使用SDL进行图形仿真。安装VerilatorLinuxVeri

C++ Primer Plus 第七章笔记

目录函数基本知识没有返回值的函数:void函数有返回值的函数:函数原型1.为什么需要函数原型?2.函数原型的语法3.函数原型的功能按值传递函数参数形参和实参局部变量参数问题使用const指针参数调用自身的函数(递归)指向函数的指针1.获取函数的地址2.声明函数指针3.使用函数指针来调用函数函数基本知识要使用C++函数必

分布式协议与算法——Raft算法

目录Raft算法领导者选举有哪些成员身份?领导者选举流程选举细节节点之间如何通信什么是任期选举有哪写规则随机超时时间是什么小结日志复制如何理解日志如何复制日志如何实现日志的一致小结节点成员变更成员变更的问题如何通过单节点变更解决成员变更的问题小结Raft小结Raft算法Raft算法属于Multi-Paxos算法,它是在

2023年贵州省职业院校技能大赛高职组信息安全管理与评估竞赛试题

2023年贵州省职业院校技能大赛高职组信息安全管理与评估竞赛试题第一阶段竞赛项目试题根据信息安全管理与评估技术文件要求,第一阶段为网络平台搭建与网络安全设备配置与防护。本文件为信息安全管理与评估项目竞赛-第一阶段试题。介绍竞赛阶段任务阶段竞赛任务第一阶段平台搭建与安全设备配置防护任务1网络平台搭建任务2网络安全设备配置

【SQL】MySQL中的窗口函数(开窗函数)

窗口函数是MYSQL8.0新增的聚合函数:多行变一行,常见的sum,count,max,min窗口函数:行数不变,常见的row_number,rank语法格式:窗口函数(表达式)over(partitionby…orderby…frame_clause)partitionby是分区,类似于groupby,如去掉相当于对

MySQL数据库入门到精通1--基础篇(MySQL概述,SQL)

1.MySQL概述1.1数据库相关概念目前主流的关系型数据库管理系统:Oracle:大型的收费数据库,Oracle公司产品,价格昂贵。MySQL:开源免费的中小型数据库,后来Sun公司收购了MySQL,而Oracle又收购了Sun公司。目前Oracle推出了收费版本的MySQL,也提供了免费的社区版本。SQLServe

docker基础命令快速入门

docker快速入门Docker是一个虚拟环境容器,可以将你的开发环境、代码、配置文件等一并打包到这个容器中,并发布和应用到任意平台中。Docker的三个概念镜像Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的配置参数。镜像不包含任何动态数据,其内容

4k、VR与万兆光网

“全光万兆”对VR意义重大。pico4的分辨率PICO4的单眼分辨率是2160×2160,整体分辨率高达4320×2160。这是一款高性能的VR一体机,采用了2.56英寸的Fast-LCD屏幕,最高可实现90Hz刷新率,还有1200PPI和20.6PPD的超清晰画面表现。PICO4的视场角为105°,比上一代PICON

热文推荐