首先给出LeNet的结构图:
然后是论文连接:http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf
这里我就不讲CNN的基础知识了,比如感受野,局部连接,权值共享等,我直接讲LeNet的7层网络是如何实现的。
⋆ 特征图的尺寸
如果足够细心的话,你应该会发现元素图像在经过卷积之后得到的特征图的尺寸会有所不同。确实是这样,一个特征图的尺寸与以下几个因素有关:
输入的图片的大小(input size)
卷积核的大小(filter size)
作卷积时的步长(stride)
填充大大小(padding )
为了便于说明,这里设图片的大小为i,卷积核的大小为k,步长为s,填充的大小为p,特征图的尺寸为o。 那么可以得到
o=(i+2p−k+1)/s
C1层(卷积层)
C1层有6个28x28的特征图组成,每个特征图中的任一个元素与该层的输入中一个5x5的区域相连接。如图所示:
输入图片:32*32
卷积核大小:5*5
卷积核种类:6
输出featuremap大小:28*28 (32-5+1)
神经元数量:28286
可训练参数:(5*5+1)6(每个滤波器55=25个unit参数和一个bias参数,一共6个滤波器)
连接数:(55+1)62828
S2层(池化层)
S2层是一个池化层,由6个14x14的特征图组成,每一个特征图中元素都与C1层中对应的特征图中一个2x2的相邻区域相连。这里的池化操作为:将4个输入相加,乘上一个可训练的系数,加上一个可训练的偏置,最后经过一个Sigmoid函数。
输入:28*28
采样区域:2*2
采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid
采样种类:6
输出featureMap大小:14*14(28/2)
神经元数量:14146
可训练参数:2*6(和的权+偏置)
连接数:(22+1)61414
S2中每个特征图的大小是C1中特征图大小的1/4
C3层(卷积层)
C3层由16个10x10的特征图组成,与C1的最大区别是,这里每个特征图中的元素会与S2层中若干个特征图中处于相同位置的5x5的区域相连,如图所示:
输入:S2中所有6个或者几个特征map组合
卷积核大小:5*5
卷积核种类:16
输出featureMap大小:10*10
C3中的每个特征map是连接到S2中的所有6个或者几个特征map的,表示本层的特征map是上一层提取到的特征map的不同组合
存在的一个方式是:C3的前6个特征图以S2中3个相邻的特征图子集为输入。接下来6个特征图以S2中4个相邻特征图子集为输入。然后的3个以不相邻的4个特征图子集为输入。最后一个将S2中所有特征图为输入。
则:可训练参数:6*(325+1)+6(425+1)+3(425+1)+(256+1)=1516
连接数:10101516=151600
S4层(池化层)
S4层是一个池化层,他由16个5x5的特征图构成,其操作和S2层相同。其共有32和可训练的参数和2000个连接。
#005
C5层(卷积层)
C5是一个类似C3的卷积层,由120个1x1的特征图组成。但是,与C3层不同的是,这里的连接是一种“全”连接,即每个特征图中的元素与S4层中每个特征图都连接。
S2层一共有120×(5×5×16+1)=48,120个可训练的参数。
#006
F6层(全连接层)
F6就是一个简单的全连接层,它由84个神经元构成。和传统的全连接一样每个神经元将C5层中的特征图的值乘上相应的权重并相加,再加上对应的偏置再经过tanh激活函数。
F6层共有84×(120+1)=10,164个可训练的参数。
代码实现
这里我们来实现LeNet-5
Keras实现
这里使用的是一个简化版的LeNet-5,经过两次卷积和Max Pooling之后,将得到的结果展开为一维,再经过一个Softmax。
import numpy as np
import keras
from keras.datasets import mnist
from keras.utils import np_utils
from keras.models import Sequential
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(-1, 28, 28, 1)
X_test = X_test.reshape(-1, 28, 28, 1)
X_train = X_train / 255
X_test = X_test / 255
y_train = np_utils.to_categorical(y_train, num_classes=10)
y_test = np_utils.to_categorical(y_test, num_classes=10)
model = Sequential()
model.add(keras.layers.Conv2D(input_shape=(28, 28, 1), kernel_size=(5,5), filters=20, activation='relu'))
model.add(keras.layers.MaxPool2D(pool_size=(2,2), strides=2, padding='same'))
model.add(keras.layers.Conv2D(kernel_size=(5,5), filters=50, activation='relu', padding='same'))
model.add(keras.layers.MaxPool2D(pool_size=(2,2), strides=2, padding='same'))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(500, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
print('Training')
model.fit(X_train, y_train, epochs=2, batch_size=32)
print('Testing')
loss, accuracy = model.evaluate(X_test, y_test)
print('loss', loss)
print('accuracy', accuracy)
参考:https://www.charleychai.com/blogs/2018/ai/NN/lenet.html