经典卷积神经网络算法(1):LeNet-5

 

LeNet-5科学家Yann LeCun在1998年发表论文《Gradient based learning applied to document-recognition》上提出的一个神经网络模型,是最早期的卷积神经网络,论文中,作者将LeNet-5应用于于灰度图像的数字识别中获得了不错的效果。关于LeNet-5卷积神经网络原理,在上一篇介绍卷积神经网络入门博客中已经阐述清楚,本篇中,我们主要对LeNet-5使用TensorFlow进行实现。
LeNet-5网络结构如下所示:

 

 

接下来,本文就上图所示LeNet-5结构进行实现。

In [25]:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow import keras
from tensorflow.keras import datasets, layers, optimizers, Sequential ,metrics
 

TensorFlow中自带手写数字识别图像数据集,使用datasets模块进行加载即可。

In [26]:
(x, y), (x_test, y_test) = datasets.mnist.load_data()
 

查看数据的数量和图像size:

In [27]:
print(x.shape, y.shape)
print(x_test.shape, y_test.shape)
 
(60000, 28, 28) (60000,)
(10000, 28, 28) (10000,)
 

可见,训练集中包含60000张图像,测试集中包含10000章图像,图像大小为2828。图像size与上图LeNet-5卷积网络中所用3232数据集有所不同,不过没关系,我们在第一层卷积层中对图像进行padding即可。

 

使用matplotlib对数据集进行展示,如下所示,图片上方数字为图像对应的数字。

In [28]:
index = 1
fig, axes = plt.subplots(4, 3, figsize=(8, 4), tight_layout=True)
for row in range(4):
    for col in range(3):
        axes[row, col].imshow(x[index])
        axes[row, col].axis('off')
        axes[row, col].set_title(y[index])
        index += 1
plt.show()
 
 

刚加载好的图像是numpy数组形式,元素值在0~255之间,需要进行类型转换和归一化。这里我们定义一个preprocess作为预处理函数,在将数据集打包成TensorFlow的dataset形式后,使用map函数调用preprocess对数据进行预处理更加方便。

In [29]:
def preprocess(x, y):
    x = tf.cast(x, dtype=tf.float32) / 255.
    x = tf.reshape(x,[28,28,1])
    y = tf.cast(y, dtype=tf.int32)
    return x, y
In [30]:
batchs = 32  # 每个簇的大小,批量梯度下降法时每一批的包含图像的数量
 

打包成TensorFlow到的dataset对象,并随机打乱数据:

In [31]:
db = tf.data.Dataset.from_tensor_slices((x, y))
db = db.map(preprocess).shuffle(10000).batch(batchs)
 

对测试集同样打包成dataset,不过测试集可以不随机打乱:

In [32]:
db_test = tf.data.Dataset.from_tensor_slices((x_test, y_test))
db_test = db_test.map(preprocess).batch(batchs)
In [40]:
net_layers = [ # 卷积部分网络
    # 第一个卷积层:5*5*6
    # 这个padding在最初的LeNet-5网络中是没有的,那时候还没有padding的概念,为了使这一层输出与元素LeNet-5网络保持一致,所以这里添加padding操作
    layers.Conv2D(6, kernel_size=[5,5],padding='same', activation='relu'),  # 6个5*5的卷积核,进行padding
    # 池化层
    layers.MaxPool2D(pool_size=[2, 2], strides=2),  # 池化层大小2*2,步长2
#     layers.ReLU()
    # 第二个池化层:5*5*16
    layers.Conv2D(16, kernel_size=[5,5],padding='valid', activation='relu'),
    # 池化层
    layers.MaxPool2D(pool_size=[2, 2], strides=2),
#     layers.ReLU()
    layers.Flatten(),  # 展平成一维数组
    # 全连接层
    layers.Dense(120,activation='relu'),
    layers.Dense(84,activation='relu'),
    layers.Dense(10,activation='softmax')
]
In [41]:
model = tf.keras.models.Sequential(net_layers) # 将上面创建的层合并打包成模型
model.build(input_shape=(None, 28, 28, 1))     # 指定输入数据形状
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)  # 创建优化器
model.compile(optimizer=optimizer,         # 配置模型
           loss='sparse_categorical_crossentropy',  # 指定损失函数
           metrics=['accuracy'])
model.fit(db,epochs=5, validation_data=db_test)  # 训练模型
 
Epoch 1/5
1875/1875 [==============================] - 46s 25ms/step - loss: 0.5028 - accuracy: 0.8513 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00
Epoch 2/5
1875/1875 [==============================] - 42s 22ms/step - loss: 0.1257 - accuracy: 0.9619 - val_loss: 0.1051 - val_accuracy: 0.9671
Epoch 3/5
1875/1875 [==============================] - 42s 23ms/step - loss: 0.0928 - accuracy: 0.9716 - val_loss: 0.0798 - val_accuracy: 0.9742
Epoch 4/5
1875/1875 [==============================] - 43s 23ms/step - loss: 0.0746 - accuracy: 0.9766 - val_loss: 0.0666 - val_accuracy: 0.9785
Epoch 5/5
1875/1875 [==============================] - 41s 22ms/step - loss: 0.0638 - accuracy: 0.9801 - val_loss: 0.0580 - val_accuracy: 0.9800
Out[41]:
<tensorflow.python.keras.callbacks.History at 0x7fba79d4b610>
 

可以看到,经过5轮迭代之后,模型的准确率达到98.01%,这在当时已经是相当不错的成绩。
上述代码对LeNet-5卷积神经网络进行实现,网络一共包含7层(激活函数不计算在内),注意,上文中实现的是现代版的LeNet卷积网络,与最初Yann LeCun论文中描述的LeNet-5在结构上是一致的,不过,现代版的LeNet-5网络更多使用ReLU激活函数作为中间层激活函数和Softmax在输出层转化为概率输出,另外在池化层现代更多用最大池化,而不是当初的平均池化。

posted @ 2020-05-07 07:18  奥辰  阅读(395)  评论(0编辑  收藏