LeNet实现手写数据集的识别

376 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

在了解LeNet之前不妨先来看一组图片,这是由Yann LeCun等人于1998年提出的LeNet5模型,主要用于英文字母与手写数字的识别。这张图来源于yann.lecun.com/exdb/publis… 这篇论文,正是由它的提出者所著作,篇幅有点长,有兴趣的可以学习一下。

1.PNG

从图中的结构来看,LeNet5一共有7层

第一层是卷积层,输入为32x32大小的图像,用了6个5x5大小的卷积核进行卷积,padding为0,stride为1.输出为28x28x6的特征图,计算方式为(图像大小-卷积核大小+2xpadding)/stride+1

第二层是池化层,也就是所谓的下采样,输入是上一层的大小28x28x6,filter大小为2,stride=2,所以输出大小为(28-2+0)/2+1=14,即池化后输出大小为14x14x6

第三层卷积层, 这次的输入为14x14x6,此时的滤波器个数相比于之前增加到了16个,仍然是5x5大小,padding=0,stride=1,所以计算完成后得到的特征图为10x10x16

第四层池化层, 输入为10x10x16,与上一个池化层一样,还是2倍降采样,得到(10-2+0)/2+1=5,所以特征图为5x5x16

第五层全连接层,输出节点个数为120,图中已经标明

第六层全连接层,输出节点个数为84,图中标明

第七层输出节点个数为10,图中已经标明

分析完上面的网络结构,然后实现自己的网络实现手写数字的识别应该相对较为简单了。在写代码之前,先说一下代码中需要用到的优化器,其函数的详细形式已经在下面给出。

tf.keras.optimizers.Adam(
    learning_rate=0.001,  # 学习率 默认0.001
    beta_1=0.9,  # 一阶矩估计的指数衰减率  默认 0.9
    beta_2=0.999,  # 二阶矩估计的指数衰减率  默认 0.999
    epsilon=1e-07,  # 模糊因子。如果None,默认为K.epsilon()。该参数是非常小的数,其为了防止在实现中除以零。默认 1e-07
    amsgrad=False,  # 布尔。是否应用该算法的AMSGrad变体。默认 False
    name='Adam',  # 应用渐变时创建的操作的可选名称。
    **kwargs)

下面直接上代码

import tensorflow as tf
from tensorflow.keras import datasets, layers, models, optimizers

# 定义网络的参数
EPOCH = 30
BATCH_SIZE = 128
# 输出带进度条的日志信息
VERBOSE = 1
# 优化器
OPTIMIZER = optimizers.Adam()
# 划分比例
VALIDATION_SPLIT = 0.9
# 输入图片大小
img_w, img_h = 28, 28
input_shape = (img_w, img_h, 1)
num_class = 10


# 导入数据集,前几篇博客已经导入过非常多次了
(train_x, train_y), (test_x, test_y) = datasets.mnist.load_data()
train_x = train_x.reshape(60000, 28, 28, 1)
test_x = test_x.reshape(10000, 28, 28, 1)
train_y = train_y.astype("float32")
test_y = test_y.astype("float32")
# 归一化
train_x = train_x / 255.0
test_x = test_x / 255.0
# 转化为独热编码
train_y = tf.keras.utils.to_categorical(train_y, num_class)
test_y = tf.keras.utils.to_categorical(test_y, num_class)


# 定义网络结构
def LeNetStruct(in_shape, in_class):
    # 创建一个容器,在容器中放入卷积、池化、全连接层等
    model = models.Sequential()
    # 卷积层,20个卷积核,每个卷积核为5x5大小,如图中所示
    model.add(layers.Convolution2D(20, (5, 5), activation='relu', input_shape=in_shape))
    # 最大池化
    model.add(layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))

    # 卷积层增加到50个卷积核,每个卷积核为5x5大小,如图中所示
    model.add(layers.Convolution2D(50, (5, 5), activation='relu'))
    # 最大池化
    model.add(layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
    # 全连接层
    model.add(layers.Flatten())
    model.add(layers.Dense(500, activation='relu'))
    # 使用softmax分类器
    model.add(layers.Dense(in_class, activation='softmax'))
    return model


# 初始化网络结构、优化器
model = LeNetStruct(in_shape=input_shape, in_class=num_class)
# 配置训练方法时,告知训练时用的优化器、损失函数和准确率评测标准
model.compile(loss="categorical_crossentropy", optimizer=OPTIMIZER,metrics=["accuracy"])
# 输出参数计算过程
model.summary()
callback = [tf.keras.callbacks.TensorBoard(log_dir="./bolg06/")]
# 喂数据
parames = model.fit(train_x, train_y, batch_size=BATCH_SIZE, epochs=EPOCH, verbose=VERBOSE, validation_split=VALIDATION_SPLIT, callbacks=callback)
# 计算准确率
score = model.evaluate(test_x, test_y, verbose=VERBOSE)
print("accuracy",score[1])

上面代码中我设置了30个epoch,实际上不需要这么大,设置10应该就差不多了。

网络结构如下所示

2.PNG

训练过程中的loss与accuracy如下

3.PNG