卷积神经网络之LeNet

目录

 

一、LeNet的贡献

二、LeNet网络结构

1、各层参数详解

(1)INPUT层-输入层

(2)C1层-卷积层

(3)、S2层-池化层(下采样层)

(4)、C3层-卷积层

(5)、S4层-池化层(下采样层)

(6)、C5层-卷积层

(7)、F6层-全连接层

(8)、Output层-全连接层

三、LeNet论文

1、论文提出

2、但是将像素独自的输入到神经元中的问题

3、有了以上的问题,LeCun就提出了大名鼎鼎的卷积神经网络(CNN)

4、局部感受野

5、权值共享

6、下采样,池化

四、Keras实现

五、Pytorch实现


一、LeNet的贡献

LeNet-5可谓是第一个卷积神经网络,并且在手写数字识别上取得了很好的效果。对于图像像素作为神经网络的输入数据面临的一些问题

  1. 图像数据量较大,单独像素独立输入神经元中,需要很大的网络结构,训练参数过多
  2. 图像的形变,形变引起的图像特征位置变化的问题
  3. 图像的局部相关性。

其提出了卷积神经网络的概念,并应用

  • 局部感受野
  • 权值共享
  • 下采样(池化)

来解决上述问题。

二、LeNet网络结构

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

1、各层参数详解

(1)INPUT层-输入层

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

注意:本层不算LeNet-5的网络结构,传统上,不将输入层视为网络层次结构之一。

(2)C1层-卷积层

a、结构说明

  1. 输入图片:32*32

  2. 卷积核大小:5*5

  3. 卷积核种类:6

  4. 输出featuremap大小:在卷积的过程中不做边缘填充,步长为stride=1。单个核的卷积输出大小为(32−5+1)×(32−5+1)=28×28。由于有6个卷积核,所以整个卷积层输出得到为Feature Map为 28×28×6, 

  5. 神经元数量:28*28*6

  6. 可训练参数:(5*5+1) * 6(每个滤波器5*5=25个unit参数和一个bias参数,一共6个滤波器)

  7. 连接数:(5*5+1)*6*28*28=122304

b、详细说明

  1. 对输入图像进行第一次卷积运算(使用 6 个大小为 5*5 的卷积核),得到6个C1特征图(6个大小为28*28的 feature maps, 32-5+1=28)。

  2. 我们再来看看需要多少个参数,卷积核的大小为5*5,总共就有6*(5*5+1)=156个参数,其中+1是表示一个核有一个bias。

  3. 对于卷积层C1,C1内的每个像素都与输入图像中的5*5个像素和1个bias有连接,所以总共有156*28*28=122304个连接(connection)。有122304个连接,但是我们只需要学习156个参数,主要是通过权值共享实现的。

(3)、S2层-池化层(下采样层)

a、结构说明

  1. 输入:28*28

  2. 采样区域:2*2

  3. 采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid

  4. 采样种类:6

  5. 输出featureMap大小:14*14(28/2)

  6. 神经元数量:14*14*6

  7. 连接数:(2*2+1)*6*14*14

  8. S2中每个特征图的大小是C1中特征图大小的1/4。

b、详细说明

  1. 第一次卷积之后紧接着就是池化运算,使用 2*2核 进行池化,于是得到了S2,6个14*14的 特征图(28/2=14)。

  2. S2这个pooling层是对C1中的2*2区域内的像素求和乘以一个权值系数再加上一个偏置,然后将这个结果再做一次映射。同时有5x14x14x6=5

  3. 880个连接。

(4)、C3层-卷积层

a、结构说明

  1. 输入:S2中所有6个或者几个特征map组合

  2. 卷积核大小:5*5

  3. 卷积核种类:16

  4. 输出featureMap大小:10*10 (14-5+1)=10

  5. C3中的每个特征map是连接到S2中的所有6个或者几个特征map的,表示本层的特征map是上一层提取到的特征map的不同组合

  6. 存在的一个方式是:C3的前6个特征图以S2中3个相邻的特征图子集为输入。接下来6个特征图以S2中4个相邻特征图子集为输入。然后的3个以不相邻的4个特征图子集为输入。最后一个将S2中所有特征图为输入。

则:可训练参数:6*(3*5*5+1)+6*(4*5*5+1)+3*(4*5*5+1)+1*(6*5*5+1)=1516

连接数:10*10*1516=151600

b、详细说明:

第一次池化之后是第二次卷积,第二次卷积的输出是C3,16个10x10的特征图,卷积核大小是 5*5.

我们知道S2 有6个 14*14 的特征图,怎么从6 个特征图得到 16个特征图了? 这里是通过对 S2 的特征图特殊组合计算得到的16个特征图。具体如下:

  1. C3的前6个feature map(对应上图第一个红框的6列)与S2层相连的3个feature map相连接(上图第一个红框),

  2. 后面6个feature map与S2层相连的4个feature map相连接(上图第二个红框),

  3. 后面3个feature map与S2层部分不相连的4个feature map相连接,

  4. 最后一个与S2层的所有feature map相连。卷积核大小依然为5*5,

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

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

上图对应的参数为 3*5*5+1,一共进行6次卷积得到6个特征图,所以有6*(3*5*5+1)参数。 为什么采用上述这样的组合了?论文中说有两个原因:1)减少参数,2)这种不对称的组合连接的方式有利于提取多种组合特征。

(5)、S4层-池化层(下采样层)

a、结构说明

  1. 输入:10*10

  2. 采样区域:2*2

  3. 采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid

  4. 采样种类:16

  5. 输出featureMap大小:5*5(10/2)

  6. 神经元数量:5*5*16=400

  7. 连接数:16*(2*2+1)*5*5=2000

  8. S4中每个特征图的大小是C3中特征图大小的1/4

b、详细说明:

  1. S4是pooling层,窗口大小仍然是2*2,共计16个feature map,

  2. C3层的16个10x10的图分别进行以2x2为单位的池化得到16个5x5的特征图。有5x5x5x16=2000个连接。连接的方式与S2层类似。

(6)、C5层-卷积层

a、结构说明

  1. 输入:S4层的全部16个单元特征map(与s4全相连)

  2. 卷积核大小:5*5

  3. 卷积核种类:120

  4. 输出featureMap大小:1*1(5-5+1)

  5. 可训练参数/连接:120*(16*5*5+1)=48120

b、详细说明:

  1. C5层是一个卷积层。由于S4层的16个图的大小为5x5,与卷积核的大小相同,所以卷积后形成的图的大小为1x1。

  2. 这里形成120个卷积结果。每个都与上一层的16个图相连。所以共有(5x5x16+1)x120 = 48120个参数,同样有48120个连接。C5层的网络结构如下:

(7)、F6层-全连接层

a、结构说明

  1. 输入:c5 120维向量

  2. 计算方式:计算输入向量和权重向量之间的点积,再加上一个偏置,结果通过sigmoid函数输出。

  3. 可训练参数:84*(120+1)=10164

b、详细说明:

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

F6层的连接方式如下:

(8)、Output层-全连接层

Output层也是全连接层,共有10个节点,分别代表数字0到9,且如果节点 i 的值为0,则网络识别的结果是数字 i。采用的是径向基函数(RBF)的网络连接方式。假设x是上一层的输入,y是RBF的输出,则RBF输出的计算方式是:

网络解析(一):LeNet-5详解

上式w_ij 的值由i的比特图编码确定,i从0到9,j取值从0到7*12-1。RBF输出的值越接近于0,则越接近于i,即越接近于i的ASCII编码图,表示当前网络输入的识别结果是字符i。该层有84x10=840个参数和连接。

三、LeNet论文

LeNet是在1998年提出的,用于手写体数字的识别, 首次提出了卷积神经网络的基本组成:卷积层,池化层和全连接层以及权值共享,感受野等概念。 虽然时间比较久i,但是作为卷积神经网络的开山之作,还是值得入门者研读一番的。

1、论文提出

  • 传统的机器学习进行图像分类,需要手工的设计特征提取器从图像集中提取特征,然后输入到机器学习算法中进行学习。

  • 使用梯度下降的方法训练的多层神经网络,可以直接从大量的数据集中学习到复杂的,高维度以及非线性的特征,这些特征远比人工设计的特征要好很多。

  • 最后,可以在神经网络的全连接层直接利用前面网络层提取到的特征进行分类,不用像传统的机器学习分类那样,分成两个步,而是进行end-to-end的学习。

2、但是将像素独自的输入到神经元中的问题

  • 训练难。图像的数据由一个个的像素组成,如果将每个像素都输入到一个的单独的神经元中,那神经网络的尺寸,权值参数将是非常的大的。这么多的参数,需要消耗非常多的计算资源,以及需要非常大的数据集。

  • 无法应对图像内容位置的变化。对于手写体的数字图像,其每个字符的大小,倾斜度,书写的位置都会导致特征位置的变化。如果将每个像素输入到一个单独的神经元中,则需要大量的不同位置的训练数据输入到神经元中。

  • 忽略了图像的局部相关性。 图像的相邻像素有很强的相关性。单独一个像素输入到神经元中则丢失了这种相关性。

3、有了以上的问题,LeCun就提出了大名鼎鼎的卷积神经网络(CNN)

  • 利用卷积核对输入进行卷积后输入到神经元中,来保持图像局部相关性

  • 使用局部感受野,权值共享,池化(下采样)来实现图像的平移,缩放和形变的不变性。

4、局部感受野

在卷积神经网络的卷积层中,神经元的输入是上一层中一个像素邻域(也就是一个卷积核卷积后的结果,称为局部感受野)。 使用局部感受野,在浅层网络中神经元可以提取到图像的边缘,角点等视觉特征,这些特征在后面的网络中进行结合,组成更高层的特征。(在人工设计的特征提取器中,则很难提取图像的高层特征)。

感受野的定义: 感受野是卷积神经网络(CNN)每一层输出的特征图(feature map)上的像素点在原始输入图像上映射的区域大小。

一个局部感受野可以看作是一个卷积核进行一次卷积的结果,一个5×5的卷积核对输入图像的5×5邻域进行卷积得到一个输出P,输入到神经元中。 在当前的卷积层中,这个P就可以代表上一层的5×5邻域。 个人理解。

5、权值共享

引入权值共享的一个原因是为了解决图像的形变和平移导致的图像显著特征位置的变化。将同一个卷积核得到的结果设为相同的权值,可以有效的降低其位置不同带来的影响。

权值共享的另一个依据是,在一个位置能够提取到有效的特征,在另外的位置也能提取到(特别是基础的点线特征)。

另外,使用权值共享也大大的降低的网络的参数

一个卷积核就相当于一个特征提取器,每个卷积和和输入图像进行卷积得到输出称为Feature Map,一个FeatureMap中所有的像素点和上一层的连接,使用相同的权值参数,即为权值共享。

每一层中所有的神经元形成一个平面,这个平面中所有神经元共享权值。神经元(unit)的所有输出构成特征图,特征图中所有单元在图像的不同位置执行相同的操作(同一个特征图是使用同意给卷积核得到),这样他们可以在输入图像的不同位置检测到同样的特征,一个完整的卷积层由多个特征图组成(使用不同的权值向量),这样每个位置可以提取多种特征。

一个具体的示例就是 LeNet-5 中的第一层,第一层隐藏层中的所有单元形成6个平面,每个是一个特征图。一个特征图中的一个单元对应有25个输入,这25个输入连接到输入层的5x5区域,这个区域就是局部感受野。每个单元有25个输入,因此有25个可训练的参数加上一个偏置。

由于特征图中相邻单元以前一层中连续的单元为中心,所以相邻单元的局部感受野是重叠的。比如,LeNet-5中,水平方向连续的单元的感受野存在5行4列的重叠,之前提到过,一个特征图中所有单元共享25个权值和一个偏置,所以他们在输入图像的不同位置检测相同的特征,每一层的其他特征图使用不同的一组权值和偏置,提取不同类型的局部特征

LeNet中,每个输入位置会提取6个不同的特征。特征图的一种实现方式就是使用一个带有感受野的单元,扫面整个图像,并且将每个对应的位置的状态保持在特征图中,这种操作等价于卷积,后面加入一个偏置和一个函数,因此,取名为卷积网络,卷积核就是连接的权重。卷积层的核就是特征图中所有单元使用的一组连接权重。卷积层的一个重要特性是如果输入图像发生了位移,特征图会发生相应的位移,否则特征图保持不变。这个特性是CNN对位移和形变保持鲁棒的基础。

一个卷积核提取输入数据的某种特征输出一个Feature Map。 既然提取的是同一种特征,那么使用同一个权值也是应该的。

6、下采样,池化

在图像分类中,起主导作用的是图像特征的相对位置,如图像中的数字7从左上角移到右下角,仍然是数字7,重要的是直线-点-直线之间的相对位置。因为图像的平移,形变是很常见的,所以图像特征的精确的位置信息,在分类识别中甚至是有害的。 通过降低图像分辨率的方式来降低图像特征位置的精度,使用池化函数(均值或者最大)对图像进行下采样,降低网络的输出对图像形变和平移的敏感程度。如果对图像做平移,那么对应于高层特征的平移(因为权值共享);如果对图像做局部旋转,小范围旋转/扭曲会被局部感受野消除,大范围扭曲会因为降采样而模糊掉其影响。

四、Keras实现

并没有,精确的实现论文中描述的LeNet-5的网络结构,只是照着实现了一个简单的卷积神经网络,网络结构如下:

_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
conv2d_1 (Conv2D)            (None, 32, 32, 20)        1520
_________________________________________________________________
activation_1 (Activation)    (None, 32, 32, 20)        0
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 16, 16, 20)        0
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 16, 16, 50)        25050
_________________________________________________________________
activation_2 (Activation)    (None, 16, 16, 50)        0
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 8, 8, 50)          0
_________________________________________________________________
flatten_1 (Flatten)          (None, 3200)              0
_________________________________________________________________
dense_1 (Dense)              (None, 500)               1600500
_________________________________________________________________
activation_3 (Activation)    (None, 500)               0
_________________________________________________________________
dense_2 (Dense)              (None, 10)                5010
_________________________________________________________________
activation_4 (Activation)    (None, 10)                0
=================================================================
Total params: 1,632,080
Trainable params: 1,632,080
Non-trainable params: 0
_________________________________________________________________

调用Keras API可以很容易的实现上述的结构

# -*- coding:utf-8 -*-

from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dense
from keras import backend as K 

class LeNet:
    @staticmethod
    def build(width,height,depth,classes):
        model = Sequential()
        inputShape = (height,width,depth)

        if K.image_data_format() == "channels_first":
            inputShape = (depth,height,width)

        # first set of CONV => RELU => POOL
        model.add(Conv2D(20,(5,5),padding="same",input_shape=inputShape))
        model.add(Activation("relu"))
        model.add(MaxPooling2D(pool_size=(2,2),strides=(2,2)))

        # second set of CONV => RELU => POOL_layers
        model.add(Conv2D(50,(5,5),padding="same"))
        model.add(Activation("relu"))
        model.add(MaxPooling2D(pool_size=(2,2),strides=(2,2)))

        # set of FC => RELU layers
        model.add(Flatten())
        model.add(Dense(500))
        model.add(Activation("relu"))

        # softmax classifier
        model.add(Dense(classes))
        model.add(Activation("softmax"))

        return model

将上述网络应用于CIFAR10数据集进行分类

测试代码,首先使用scikit-learn加载CIFAR10数据,并进行归一化

print("[INFO] loading CIFAR-10 data...")
((trainX,trainY),(testX,testY)) = cifar10.load_data()
trainX = trainX.astype("float")  /  255.0
testX = testX.astype("float")  /  255.0

对CIFAR10的类别进行编码

lb =  LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.fit_transform(testY)

处理完数据后,调用上面的LeNet建立LeNet网络结构并使用trainX数据集进行训练

print("[INFO] compiling model...")
opt =  SGD(lr=0.05)
model = lenet.LeNet.build(width=width,height=height,depth=depth,classes=classes)
model.compile(loss="categorical_crossentropy",optimizer=opt,metrics=["accuracy"])

# train
print("[INFO] training network...")
H = model.fit(trainX,trainY,validation_data=(testX,testY),batch_size=32,epochs=epochs,verbose=1)

最后使用testX数据集进行评估

# evaluate the network
print("[INFO] evaluating networking...")
predictions = model.predict(testX,batch_size=32)
print(classification_report(testY.argmax(axis=1),predictions.argmax(axis=1),
    target_names=labelNames))

很明显的过拟合了,这里就不关注这个精度了,只是简单的测试下。

更详细的测试代码,可以Start/Fork GitHub上https://github.com/brookicv/machineLearningSample 以及 https://github.com/brookicv/imageClassification

五、Pytorch实现

# encoding: utf-8

"""
@author: sunxianpeng
@file: lenet.py
@time: 2019/11/18 11:58
"""
import torch
import torch.nn as nn
import torch.optim as optim
from torch.nn import functional as F
from torch.utils.data import DataLoader,TensorDataset


from torchvision import transforms
import math

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.features = nn.Sequential(
        nn.Conv2d(1,6,5),
        nn.ReLU(),
        nn.MaxPool2d(2),
        nn.Conv2d(6,16,5),
        nn.ReLU(),
        nn.MaxPool2d(2),
        )
        self.classifier = nn.Sequential(
        nn.Linear(16*5*5,120),
        nn.ReLU(),
        nn.Linear(120,84),
        nn.ReLU(),
        nn.Linear(84,10)
        )

    def forward(self,x):
        x = self.features(x)
        print(x.size())#torch.Size([1, 16, 5, 5])
        print(x.shape)#torch.Size([1, 16, 5, 5])
        x = x.view(x.size(0),-1)#torch.Size([1, 400])
        x = self.classifier(x)
        return x

if __name__ == '__main__':
    # 代入数据检验
    y = torch.randn(1, 1, 32, 32)
    model = LeNet()
    model(y)

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值