「深度学习一遍过」必修8:搭建卷积神经网络 LeNet-5

本文详细介绍了LeNet-5的网络结构及其各层参数,并提供了基于PyTorch的实现代码。LeNet-5是一种经典的手写数字识别深度学习模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本专栏用于记录关于深度学习的笔记,不光方便自己复习与查阅,同时也希望能给您解决一些关于深度学习的相关问题,并提供一些微不足道的人工神经网络模型设计思路。
专栏地址:「深度学习一遍过」必修篇

目录

1 LeNet-5 的网络结构与特点

2 各层参数详解

2.1 INPUT 层——输入层

2.2 C1 层——卷积层

2.3 S2 层——池化层(下采样层)

2.4 C3 层——卷积层

2.5 S4 层——池化层(下采样层)

2.6 C5 层——卷积层

2.7 F6 层——全连接层

2.8 Output层——全连接层

3 代码实现


1 LeNet-5 的网络结构与特点

LeNet-5 是 LeNet 系列的最终稳定版,它被美国银行用于手写数字识别,该网络有以下特点:
  • 所有卷积核均为 5\times 5,步长为 1
  • 所有池化方法为平均池化
  • 所有计划函数采用 Sigmoid

2 各层参数详解

LeNet-5 共7,不包含输入,每层都包含可训练参数;每个层有多个 Feature Map,每个 FeatureMap 通过一种卷积滤波器提取输入的一种特征,然后每个 FeatureMap 有多个神经元。 

2.1 INPUT 层——输入层

首先是数据 INPUT 层,输入图像的尺寸统一归一化为 32\times 32

2.2 C1 层——卷积层

  • 输入图片:32\times 32
  • 卷积核大小:5\times 5
  • 卷积核种类:6
  • 输出特征图大小:28\times 28(32-5+1)=28
  • 神经元数量:28\times 28\times 6
  • 可训练参数:(5\times 5+1)\times 6(每个滤波器 5\times 5=25 个 unit 参数和一个 bias 参数,一共6 个滤波器)
  • 连接数:(5\times 5+1)\times6\times28\times28=122304

2.3 S2 层——池化层(下采样层)

  • 输入:28\times *28
  • 采样区域:2\times 2
  • 采样方式:4 个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid
  • 采样种类:6
  • 输出特征图大小:14\times 14(28\setminus 2)
  • 神经元数量:14\times 14\times 6
  • 连接数:(2\times2+1) \times6\times14\times14
  • S2 中每个特征图的大小是 C1 中特征图大小的 1\setminus 4

2.4 C3 层——卷积层

  • 输入:S2 中所有 6 个或者几个特征图组合
  • 卷积核大小:5\times 5
  • 卷积核种类:16
  • 输出特征图大小:10\times 10 (14-5+1)=10

C3 中的每个特征 map 是连接到 S2 中的所有 6 个或者几个特征 map 的,表示本层的特征 map 是上一层提取到的特征 map 的不同组合存在的一个方式是:C3 的前 6 个特征图以 S2 中 3 个相邻的特征图子集为输入。接下来 6 个特征图以 S2 中 4 个相邻特征图子集为输入。然后的 3 个以不相邻的 4 个特征图子集为输入。最后一个将 S2 中所有特征图为输入。

  • 可训练参数:6\times (3\times5\times5+1)+6\times(4\times5\times5+1)+3\times(4\times5\times5+1)+1\times(6\times5\times5+1)=1516
  • 连接数:10\times 10\times 1516=151600

第一次池化之后是第二次卷积,第二次卷积的输出是 C3,16 个 10\times 10 的特征图,卷积核大小是  5\times 5。我们知道 S2 有 6 个 14\times 14 的特征图,怎么从 6 个特征图得到 16 个特征图了? 这里是通过对 S2 的特征图特殊组合计算得到的 16 个特征图。具体如下:

C3 的前 6 个特征图(对应上图第一个红框的 6 列)与 S2 层相连的 3 个特征图相连接(上图第一个红框),后面 6 个特征图与 S2 层相连的 4 个特征图相连接(上图第二个红框),后面 3 个特征图与 S2 层部分不相连的 4 个特征图相连接,最后一个与 S2 层的所有特征图相连。卷积核大小依然为 5*5。

所以总共有 6\times (3\times5\times5+1)+6\times(4\times5\times5+1)+3\times(4\times5\times5+1)+1\times(6\times5\times5+1)=1516 个参数。而图像大小为 10\times 10,所以共有 151600 个连接。

C3 与 S2 中前 3 个图相连的卷积结构如下图所示:

2.5 S4 层——池化层(下采样层)

  • 输入:10\times 10
  • 采样区域:2\times 2
  • 采样方式:4 个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid
  • 采样种类:16
  • 输出特征图大小:5\times 5(10 / 2)
  • 神经元数量:5\times 5\times16=400
  • 连接数:16\times (2\times 2+1)\times 5\times 5=2000
  • S4 中每个特征图的大小是C3中特征图大小的 1/4

2.6 C5 层——卷积层

  • 输入:S4 层的全部 16 个单元特征map(与S4全相连)
  • 卷积核大小:5\times 5
  • 卷积核种类:120
  • 输出特征图大小:1\times 1(5-5+1)
  • 可训练参数/连接:120\times (16\times5\times5+1)=48120
C5 层是一个卷积层。由于 S4 层的 16 个图的大小为 5\times 5,与卷积核的大小相同,所以卷积后形成的图的大小为 1\times 1。这里形成 120 个卷积结果。每个都与上一层的 16 个图相连。所以共有  (5\times 5\times16+1)\times120 = 48120 个参数,同样有 48120 个连接。C5 层的网络结构如下:

 

2.7 F6 层——全连接层

  • 输入:C5   120维向量
  • 计算方式:计算输入向量和权重向量之间的点积,再加上一个偏置,结果通过 sigmoid 函数输出。
  • 可训练参数:84\times (120+1)=10164

F6 层是全连接层。F6 层有 84 个节点,对应于一个 7\times 12 的比特图,-1 表示白色,1 表示黑色,这样每个符号的比特图的黑白色就对应于一个编码。该层的训练参数和连接数是 (120 +1)\times 84=10164ASCII 编码图如下:

2.8 Output 层——全连接层

Output 层也是全连接层,共有 10 个节点,分别代表数字 09,且如果节点 i 的值为 0,则网络识别的结果是数字 i。采用的是径向基函数(RBF)的网络连接方式。假设 x 是上一层的输入,yRBF 的输出,则 RBF输出的计算方式是: 
y_{i}=\sum_{j}^{}(x_{j}-w_{ij})^{2}

下图是 LeNet-5 识别数字 3 的过程:

3 代码实现

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor

# 查看pytorch版本
print(torch.__version__)

# 下载数据集
training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor()
)

test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
)

batch_size = 32

train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)

print(train_dataloader)

for X, y in test_dataloader:
    print(X.shape)
    print(y.shape)
    break

device = "cuda" if torch.cuda.is_available() else "cpu"


class MyLeNet(nn.Module):
    def __init__(self):
        super(MyLeNet, self).__init__()
        self.c1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1, padding=2)
        self.relu = nn.ReLU()
        self.s2 = nn.AdaptiveAvgPool2d(14)
        self.c3 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5, stride=1)
        self.s4 = nn.AdaptiveAvgPool2d(5)
        self.flatten = nn.Flatten()
        self.c5 = nn.Conv2d(in_channels=16, out_channels=120, kernel_size=5)
        self.f6 = nn.Linear(120, 84)
        self.output = nn.Linear(84, 10)

    def forward(self, x):
        x = self.relu(self.c1(x))
        x = self.s2(x)
        x = self.relu(self.c3(x))
        x = self.s4(x)
        x = self.c5(x)
        x = self.flatten(x)
        x = self.f6(x)
        x = self.output(x)
        return x


model = MyLeNet().to(device)
print(model)

loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)

def train(dataloader, model, loss_fn, optimizer):
    for batch, (x, y) in enumerate(dataloader):
        x, y = x.to(device), y.to(device)

        pred = model(x)
        loss = loss_fn(pred, y)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(x)
            print("loss = ", loss)


def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()
    test_loss, correct = 0, 0
    with torch.no_grad():
        for x, y in dataloader:
            x, y = x.to(device), y.to(device)
            pred = model(x)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
        test_loss /= num_batches
        correct /= size
        print("acc: ", correct)


epochs = 5

for t in range(epochs):
    print("Epoce ", t)
    train(train_dataloader, model, loss_fn, optimizer)
    test(test_dataloader, model, loss_fn)
print("Done")

欢迎大家交流评论,一起学习

希望本文能帮助您解决您在这方面遇到的问题

感谢阅读
END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荣仔!最靓的仔!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值