1、引言
卷积神经网络是当前最热门的技术,我想深入地学习这门技术,从他的发展历史开始,了解神经网络算法的兴衰起伏;同时了解他在发展过程中的**“里程碑式算法”**,能更好的把握神经网络发展的未来趋势,了解神经网络的特征。
2、LeNet提出背景
LeNet由Yann Lecun 提出,是一种经典的卷积神经网络,是现代卷积神经网络的起源之一。Yann将该网络用于邮局的邮政的邮政编码识别,有着良好的学习和识别能力。LeNet又称LeNet-5,具有一个输入层,两个卷积层,两个池化层,3个全连接层(其中最后一个全连接层为输出层)。
从LeNet5中我们可以看到卷积层,池化层,反向传播训练方法等,这些操作在现在的卷积神经网络之中仍然保存,并持续采用,可以说,这个模型的结构是经得起时间的检验的。当然这个模型是98年提出的,相比于现在上百层的深度,以及动辄上完的训练参数,这个模型显得十分简单,这受限于当时有限的计算能力。
3、LeNet的模型详解
从上述结构图才逐层分析LeNet的网络结构
- INPUT(输入层)
输入图像的尺寸为32X32,是单通道的灰色图像。
- C1(卷积层)
使用了6个大小为5×5的卷积核,步长为1,卷积后得到6张28×28的特征图。
- S2(池化层)
使用了6个2×2 的平均池化,步长为2,池化后得到6张14×14的特征图。
- C3(卷积层)
使用了16个大小为5×5的卷积核,步长为1,得到 16 张10×10的特征图。
由于是多个卷积核对应多个输入,论文中采用了如下组合方式:
16个大小为5x5的卷积核,步长为1。但是,这一层16个卷积核中只有10个和前面的6层相连接。也就是说,这16个卷积核并不是扫描前一层所有的6个通道。如上图,0 1 2 3 4 5这 6个卷积核是扫描3个相邻,然后是6 7 8这3个卷积核是扫描4个相邻,9 10 11 12 13 14这6个是扫描4个非相邻,最后一个15扫描6个。实际上前面的6通道每个都只有10个卷积核扫描到。
这么做的原因是打破图像的对称性,并减少连接的数量。如果不这样做的话,每一个卷积核扫描一层之后是10x10,一个核大小是5x5,输入6个通道,输出16个,所以是10x10x5x5x6x16=240000个连接。但实际上只有156000连接。训练参数的数量从2400变成了1516个。
- S4(池化层)
使用16个2×2的平均池化,步长为2,池化后得到16张5×5 的特征图。
- C5(卷积层)
使用120个大小为5×5的卷积核,步长为1,卷积后得到120张1×1的特征图。
- F6(全连接层)
输入维度120,输出维度是84(对应7x12 的比特图)。
- OUTPUT(输出层)
使用高斯核函数,输入维度84,输出维度是10(对应数字 0 到 9)。
最终,LeNet-5的总结如下
4、LeNet的创新之处
1、首先,C3层中的单个卷积核不使用S2层产生的所有特征,这在今天的标准下是非常不寻常的。其中一个原因是使网络的计算要求降低。另一个原因是让卷积内核学习不同的模式。这是非常有意义的:如果不同的内核接收不同的输入,它们将学习不同的模式。
2、其次就是使用平均池化,现在一般主要使用最大池化,因为最大池化的效果更好。第三是在池化层之后引入了非线性,现在一般是在卷积层后通过激活函数获取非线性,在池化层后不再引入非线性;第四是LeNet-5最后一步是Gaussian Connections,目前已经被Softmax取代。
5、LeNet的代码实现
from keras.models import Sequential
from keras.layers import Input, Dense, Activation, Conv2D, MaxPooling2D, Flatten
from keras.datasets import mnist
# 加载和准备数据集
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# astype修改数据类型
x_train = x_train.reshape(-1, 28, 28, 1)
x_train = x_train.astype('float32')
print(x_train.shape)
y_train = y_train.astype('float32')
x_test = x_test.reshape(-1, 28, 28, 1)
x_test = x_test.astype('float32')
y_test = y_test.astype('float32')
print(y_train)
# 归一化
x_train /= 255
x_test /= 255
from keras.utils import np_utils
y_train_new = np_utils.to_categorical(num_classes=10, y=y_train)
print(y_train_new)
y_test_new = np_utils.to_categorical(num_classes=10, y=y_test)
# 数据预处理
def LeNet_5():
model = Sequential()
model.add(Conv2D(filters=6, kernel_size=(5, 5), padding='valid', activation='tanh', input_shape=[28, 28, 1]))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=16, kernel_size=(5, 5), padding='valid', activation='tanh'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(120, activation='tanh'))
model.add(Dense(84, activation='tanh'))
model.add(Dense(10, activation='softmax'))
return model
# 训练模型
def train_model():
model = LeNet_5()
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train_new, batch_size=64, epochs=20, validation_split=0.2, shuffle=True)
return model
model = train_model()
# 返回测试集损失函数值和准确率
loss, accuracy = model.evaluate(x_test, y_test_new)
print(loss, accuracy)
6、总结
LeNet是当时表现最优秀的算法,在识别灰度图像中表现不俗,为后来的卷积神经网络提供了结构上的参考,为后来的AlexNet的横空出世做出了铺垫。