一、LeNet的由来

近期,公众号将推出卷积神经网络结构系列专题文章,将深入浅出的为大家介绍从 1998 年到 2020 年的卷积神经网络结构,深刻体会每种网络的前世今身和进化历程。本文作为开篇,我们一起来探索一下由 CNN 之父 Yann LeCun 在 1998 提出来的第一个神经网络结构——LeNet-5,这个经典模型来自下面这图灵奖得主LeCun中提出来的:
image.png
CNN:LeNet模型原理及其实现 - 图2

LeNet 通过巧妙的设计,利用卷积、参数共享、下采样等操作提取特征,避免了大量的计算成本,最后再使用全连接神经网络进行分类识别,这个网络也是近 20 年来大量神经网络架构的起源。

LeNet-5 是 1998 年 YannLeCun 设计用于手写数字识别的模型,LeNet-5 也是 LeNet 系列最新的卷积神经网络,网络结构如下图所示:

lenet5.jpg

lenet5.1.jpg

网络基本架构为:Input -> conv1 (6) -> pool1 -> conv2 (16) -> pool2 -> fc3 (120) -> fc4 (84) -> fc5 (10) -> softmax。括号中的数字代表通道数,网络名称中有 5 表示它有 5 层 conv/fc 层。其中每一个卷积层中的卷积核大小均为 5x5,stride=1,无填充,LeNet-5 中使用的激活函数为 tanh。LeNet-5 之后被成功用于 ATM 以对支票中的手写数字进行识别。LeNet 取名源自其作者姓 LeCun。
CNN:LeNet模型原理及其实现 - 图5

二、网络架构具体介绍如下:

1、输出张量size的计算

image.png

对于卷积/池化层输出张量的计算

CNN:LeNet模型原理及其实现 - 图7

2、参数数量的计算

lenet5_para.jpg

  • 首先输入图像是单通道32 x 32小的图像,在pytorch 中维度表示就是[B,1,32,32]其中B代表batch_size

    卷积层 C1[Convolutional Layer]

  • 卷积核尺寸为 5x5,滑动步长为 1,卷积核数目为 6,那么经过这一层后图像的尺寸变成(32 - 5)/1 + 1 = 28,输出特征图的维度即为:[B,6,28,28]

  • 参数个数为 (5x5+1)x6=156 (其中 5x5 对应 kernel size,+1 为 bias,6 为 feature map 数目)
  • 连接数为 156x28x28=122304(156 为参数个数,其中 feature map 上每个像素点对应 156 个连接)

    池化层 S2[Pooling Layer]

  • 池化核尺寸为 2x2,步长为 2,这是没有重叠的 max pooling, 进行池化操作之后,图像尺寸减半,变为 14x14, 输出特征图维数为:[B,6,14,14]

  • 参数个数为 6x(1+1)=12。(因为 LeNet-5 采用的 sigmoid(a*average(x)+b) 作为池化函数)
  • 链接个数为 6x14x14x(2x2+1)=5880。六个 feature map,总共 6x14x14 个 feature,每个 feature 由 4 个 C2 特征 + 1 个 bias

    卷积层 C3[Convolutional Layer]

  • 卷积核尺寸为 5x5,滑动步长为 1,卷积核数目为 16,那么经过这一层后图像的尺寸变成(14 - 5)/1 + 1 = 10,输出特征图的维度即为:[B,16,10,10]

  • 参数个数为 (5x5x3+1)x6+(5x5x4+1)x9+(5x5x6+1)=1516 个。括号内部为 kernel_size x kernel_size x feature_map_num + bias_num,表示从 feature_map_size 卷积得到的 feature map 所需要的参数个数;括号外为相应得到 feature map 的数目。
  • 链接个数为 1516x10x10=151600 个。其中 1516 为参数个数,10 为新生成的 feature map 的 size。

    池化层 S4[Pooling Layer]

  • 池化核尺寸为 2x2,步长为 2,这是没有重叠的 max pooling, 进行池化操作之后,图像尺寸减半,变为 5x5, 输出特征图维数为:[B,16,5,5]

  • 参数个数为 16x(1+1)=32。(因为 LeNet-5 采用的 sigmoid(a*average(x)+b) 作为池化函数)
  • 链接个数为 5x5x16x(2x2+1)=2000。(5 为新生成 feature map 的 size,16 为 feature map 的数目,2 为 kernel size,1 为 bias。

    卷积层 C5[Convolutional Layer]

  • 卷积核尺寸为 5x5,滑动步长为 1,卷积核数目为 120,那么经过这一层后图像的尺寸变成(5- 5)/1 + 1 = 1,输出特征图的维度即为[B,120,1,1]

  • 参数个数为 120x(5x5x16+1)=48120。
  • 链接个数等于参数个数,因为新生成 feature map 的 size 为 1。

    全连接层 F6[Full Connection Layer]

  • 输入 120 个,输出 84 个(文章中是把图片对应的字符在 7x12 的 bitmap 上画出,白值为 - 1,黑值为 1,其中 84 个像素平铺之后的向量对应为相应字符的表述,作为真值与 F6 连接)

  • 链接个数 = 参数个数 =(120+1)x84,其中 + 1 为 bias

    全连接层 Out[Full Connection Layer]

  • 输出神经元个数为 10,最后得到一个 10 维的特征向量,用于 10 个数字的分类训练,再送入 softmaxt 分类,得到分类结果的概率 output。

三、PyTorch 代码实现如下

  1. class LeNet5(nn.Module):
  2. def __init__(self, num_classes, grayscale=False):
  3. super(LeNet5, self).__init__()
  4. self.grayscale = grayscale
  5. self.num_classes = num_classes
  6. if self.grayscale:
  7. in_channels = 1
  8. else:
  9. in_channels = 3
  10. self.features = nn.Sequential(
  11. nn.Conv2d(in_channels, 6, kernel_size=5),
  12. nn.Tanh(),
  13. nn.MaxPool2d(kernel_size=2),
  14. nn.Conv2d(6, 16, kernel_size=5),
  15. nn.Tanh(),
  16. nn.MaxPool2d(kernel_size=2) )
  17. self.classifier = nn.Sequential(
  18. nn.Linear(16*5*5, 120),
  19. nn.Tanh(),
  20. nn.Linear(120, 84),
  21. nn.Tanh(),
  22. nn.Linear(84, num_classes), )
  23. def forward(self, x):
  24. x = self.features(x)
  25. x = torch.flatten(x, 1)
  26. logits = self.classifier(x)
  27. probas = F.softmax(logits, dim=1)
  28. return logits, probas

推荐阅读