文章目录
文章目录
1. LeNet-5 基础介绍
2. 利用 TensorFlow + LeNet-5 识别 mnist 手写数字
1. LeNet-5 基础介绍
通过详解卷积神经网络 CNN[1]一文,我们对卷积神经网络的有了很多认识,接下来我们将通过几个经典的卷积神经网络,加深对卷积神经网络的理解和认识。
年, 等人在论文 《GradientBased Learning Applied to Document Recognition》 中提出并详细介绍了 LeNet-5 神经网络结构,并用于手写数字识别问题,能达到很高的识别率。LeNET-5 包含卷积层,池化层,全连接层,成为了卷积神经网络的经典结构,被誉为是卷积神经网络的"Hello Word"。
该网络现在来看十分简单,十分适合入门,其结构如下

该网络共包含 层(不含输入层),我们根据以下公式计算卷积或者池化之后的特征图大小
层
该层是一个卷积层,使用 个 的卷积核,步长为 。
每个卷积核有 个参数,共有 个参数。
层
该层是一个 Pooling 层,Pooling 大小为 ,步长为 .
该层没有要学习的参数
层
该层是一个卷积层,使用 个 的卷积核,步长为 。
共有 个参数。
层
该层是一个 Pooling 层,Pooling 大小为 ,步长为 .
该层没有要学习的参数
层
论文描述的是一个卷积层,使用 个 的卷积核,步长为 .
共有 个参数
其实这层也可以看做是一个全连接层的一个隐藏层,神经元个数为 个,输入的数据来自 层特征数据拉直。
层
该层是一个全连接层。
共有 个参数。
层
该层是一个全连接层。
共有 个参数。
我们用以下表格更加直观的看下每一层都做了什么
这里重点介绍其网络结构,部分细节可能和论文不一致,需要原论文的可联系我。
2. 利用 TesorFlow + LeNet-5 识别 mnist 手写数字
#!/usr/bin/python3
# @Time : 2021/2/23 9:56
# @Author :
# @File : Lenet5
# @Software: PyCharm
# @Description : Python==3.7.3、tensorflow==2.4.1
import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.keras.layers import Conv2D,BatchNormalization,Activation,MaxPool2D,Dropout,Flatten,Dense,AveragePooling2D
from tensorflow.keras import Model
import os
# 设置超过多长省略显示,这里设置np.inf表示无限长
np.set_printoptions(threshold=np.inf)
# 准备数据集合
# cifar10 = tf.keras.datasets.cifar10
cifar10 = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train,x_test = x_train/255.0,x_test/255.0
# 给数据增加一个维度,使数据和网络结构匹配
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1)
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1)
# 自定义网络模型
class Letnet5Model(Model,):
# 定义网络结构
def __init__(self):
super(Letnet5Model,self).__init__()
# 定义6个 5*5的卷积核
self.c1 = Conv2D(filters=6,kernel_size=(5,5),activation='sigmoid',padding='same')
# 平均池化,池化大小 2*2,步长2
self.s2 = AveragePooling2D(pool_size=(2,2),strides=2)
# 定义16个5*5的卷积核
self.c3 = Conv2D(filters=16,kernel_size=(5,5),activation='sigmoid')
self.s4 = AveragePooling2D(pool_size=(2,2),strides=2)
# 将卷积得到的特征数据拉直
self.f = Flatten()
# 定个一个120个神经元的全连接隐藏层
self.c5 = Dense(units=120, activation='sigmoid')
# 定个一个80个神经元的全连接隐藏层
self.f6 = Dense(units=84,activation='sigmoid')
# 定个一个10个神经元的全连接隐藏层
self.o7 = Dense(units=10,activation='softmax')
# 调用网络结构,实现前向传播
def call(self, inputs, training=None, mask=None):
inputs = self.c1(inputs)
inputs = self.s2(inputs)
inputs = self.c3(inputs)
inputs = self.s4(inputs)
inputs = self.f(inputs)
inputs = self.c5(inputs)
inputs = self.f6(inputs)
y = self.o7(inputs)
return y
def acc_loss_analyse(robot,):
# 绘制训练集和验证集的acc和loss曲线
acc = robot.history['sparse_categorical_accuracy']
val_acc = robot.history['val_sparse_categorical_accuracy']
loss = robot.history['loss']
val_loss = robot.history['val_loss']
plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title("Training and Validation Accuracy")
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()
def main():
model = Letnet5Model()
# 配置训练项,设定optimizer(优化器),loss(损失函数),metrics(网络评判标准)
model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])
breakpoint_sava_path = './checkpoint/LeNet5.ckpt'
if os.path.exists(breakpoint_sava_path+'.index'):
print('-------------load the model-----------------')
# 读取模型
model.load_weights(breakpoint_sava_path)
# 保存模型
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=breakpoint_sava_path, save_weights_only=True,save_best_only=True)
# 开始训练
robot = model.fit(x_train,y_train,batch_size=32,epochs=5,validation_data=(x_test,y_test),validation_freq=1,callbacks=[cp_callback])
# 查看网络结构基本信息
model.summary()
acc_loss_analyse(robot, )
if __name__ == '__main__':
main()复制
运行代码,可以看到 打印的每层参数个数和之前的分析保持一致
训练了两百轮,最好验证集准确率为
Reference
详解卷积神经网络 CNN: https://blog.csdn.net/SpiritedAway1106/article/details/113931976