前言
LeNet是卷积神经网络的开山之作,也是将深度学习推向繁荣的一座里程碑。
Yann LeCun于上世纪90年代提出了LeNet,他首次采用了卷积层、池化层这两个全新的神经网络组件;LeNet在手写字符识别任务上取得了瞩目的准确率。
LeNet网络有一系列的版本,其中以LeNet-5版本最为著名,也是LeNet系列中效果最佳的版本。
LeNet-5使用5个卷积层来学习图像特征;卷积层的权重共享特点使得它相较于全连接层,节省了相当多的计算量与内存空间;同时卷积层的局部连接特点可以保证图像的空间相关性。
网络架构
LeNet-5网络架构
INPUT(输入层)
输入层为单通道的灰度图像,图像尺寸为32*32。
网络层级
网络层\简介 | 输入 | 输出 | (卷积/池化)窗口 | 可训练参数 | C1(卷积层) | 32*32灰度图像 | 6个尺寸为28*28的FeatureMap | 6个5*5卷积核 | 156 | S2(池化层) | 6个尺寸为28*28的FeatureMap | 6个尺寸为14*14的FeatureMap | 2*2池化窗口 | 12 | C3(卷积层) | 6个尺寸为14*14的FeatureMap | 16个尺寸为10*10的FeatureMap | 16个5*5卷积核 | 1516 | S4(池化层) | 16个尺寸为10*10的FeatureMap | 16个尺寸为5*5的FeatureMap | 2*2池化窗口 | 32 | C5(卷积层) | 16个尺寸为5*5的FeatureMap | 120个尺寸为1*1的FeatureMap | 120个5*5卷积核 | 48120 | F6(全连接层) | 120个特征 | 84个特征 | None | 10164 | Output(输出层) | 84个特征 | 10维向量 | None | 840 | 卷积层
通过卷积层,可使得信号特征增强,降低噪声;故其主要作用是降低图像噪声,提取图像重要特征。此外,卷积层能够保证图像的平移不变性,卷积运算保留了图像的空间信息,卷积后的图像位置关系没有改变。
卷积层利用卷积核对图像进行卷积运算;每个卷积核都是可训练的,一般会在卷积核上添加一个偏置参数。卷积运算是卷积核以步长stride在原图上进行移动,将其与卷积核进行元素乘法,并求和乘法结果,最后加上偏置,得到卷积结果。
卷积运算
我们将卷积运算后的结果称为特征图(FeatureMap),特征图的尺寸计算公式如下:
池化层
池化层一般不同训练,它的主要作用是减少数据,在降低数据维度的同时保留特征图中重要的特征信息;同时也避免了网络参数太多而造成的过拟合问题。
最大池化层运算
全连接层
全连接层一般接在卷积神经网络的最后,用于提取卷积和池化之后的特征向量;并基于提取的特征向量进行图像分类。所以全连接层即充当了特征提取器,又充当了分类器的角色。
网络实现
import torch
import torchvision
import torch.nn as nn
import torchvision.transforms as transforms
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt2. 数据集导入
trans_to_tensor = transforms.Compose([transforms.ToTensor()])
data_trAIn = torchvision.datasets.MNIST('./data',train=True,transform=trans_to_tensor,download=True)
data_test = torchvision.datasets.MNIST('./data',train=False,transform=trans_to_tensor,download=True)3. 查看数据集
train_loader = torch.utils.data.DataLoader(data_train,batch_size=100,shuffle=True)
x,y = next(iter(train_loader))
plt.imshow(x[1].squeeze(0),cmap='gray')4. 网络模型构建
class LeNet5(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1,6,5,padding=2)
self.pool1 = nn.AvgPool2d(2)
self.conv2 = nn.Conv2d(6,16,5)
self.pool2 = nn.AvgPool2d(2)
self.fc1 = nn.Linear(16*5*5,120)
self.fc2 = nn.Linear(120,84)
self.fc3 = nn.Linear(84,10)
def forward(self,x):
x = torch.tanh(self.conv1(x))
x = self.pool1(x)
x = torch.tanh(self.conv2(x))
x = self.pool2(x)
x = x.view(-1,16*5*5)
x = self.fc1(x)
x = self.fc2(x)
x = self.fc3(x)
return x5. 模型训练
def test(net):
net.eval()
test_loader = torch.utils.data.DataLoader(data_train,batch_size=10000,shuffle=False)
test_data = next(iter(test_loader))
with torch.no_grad():
x,y = test_data[0],test_data[1]
outputs = net(x)
pred = torch.max(outputs,1)[1]
print(f'test acc:{sum(pred==y)/outputs.shape[0]}')
net.train()
def fit(net,epoch=1):
net.train()
run_loss = 0
for num_epoch in range(epoch):
print(f'epoch {num_epoch}')
for i,data in enumerate(train_loader):
x,y = data[0],data[1]
outputs = net(x)
loss = criterion(outputs,y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
run_loss += loss.item()
if i%100 == 99:
print(f'[{(i+1) * 100} / 60000] loss={run_loss / 100}')
run_loss = 0
test(net)
LeNet = LeNet5()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(LeNet.parameters())
fit(LeNet,epoch=20)LeNet由于网络结构小,应用场景受限;LeNet的池化层采用的是均值池化,目前的结果表明采用最大池化效果更好,且能够加速网络收敛;LeNet采用的激活函数为tanh,目前的结果表明采用ReLU激活函数往往能获得更高的准确率。 胡思乱想
最近看完了Yann LeCun著作的《科学之路》,这本书详细介绍了他在深度学习领域的整个科研历程。循着他的科研历程,我也慢慢理清了整个深度学习的发展脉络。深度学习是在机器学习的基础上发展起来的,彼时的深度学习还不是端到端的网络结构:特征工程与算法模型各自发展;特征工程以SIFT算法为主,发展出来各种衍生版本,利用SIFT算法提取图像特征,将提取的特征输入多层感知机,进行图像分类。
彼时还没有发明反向传播算法,当时的神经网络还不能够自主学习,网络的权重更新是通过人工调节电阻器来实现的,所以太大的神经网络根本没法“学习”。
随着反向传播算法的发明,神经网络能够自学习了,这为发展深度学习提供了理论可能性;随着算力的快速发展,深度学习具备了现实可能性。但当时单一的全连接神经网络模块完全不具备解决各个领域问题的能力;随着卷积神经网络的出现,深度学习终于迎来了大爆发,它首先推动了计算机视觉领域的快速发展,同时也潜移默化地推动着神经网络模块多元化发展。
因此LeNet是里程碑式的一个神经网络,虽然面对如今的BERT、GPT-3等超大网络,LeNet显得黯然失色,但BERT、GPT-3等仍无法撼动LeNet的历史地位。
一切复杂的问题,在你完全理解它之后,终将变得简单;一切简单的问题,在你完全理解它之前,永远不简单。
深度学习课程推荐
上一篇:说说焦虑症和植物神经紊乱 下一篇:媒体评《亲爱的小孩》《心居》贩卖焦虑,你认同吗?如何 ... |