基于CNN的手写体识别与GUI系统设计(新手快进来!)

目录

  • 1. 写在前面
  • 2. 环境搭建
  • 3. 卷积神经网络
  • 4. 数字手写体识别实现

1. 写在前面

    这是我的本科毕设,今天终于差不多降完重了哈哈,总共耗时一个半月,写完赶紧趁热打铁来记录一下整个学习过程。本文是基于你了解了lenet-5模型结构进行的手写体识别系统设计,包括手写体数字识别与手写体汉字识别(准确率分别达到了99.52%和93.64%左右)在本文里不会去详细介绍有关lenet-5模型的各层原理(这个信息来源特别多),但是会对编写代码的每一步进行详细解释鉴于我的学习历程,发现很多博文代码没有解释),因此我觉得是特别适合了解理论但不会编代码的小白学习的。

2. 环境搭建

    先说一下我的实验环境:Anaconda2019.10+Pycharm+Tensorflow2. 1。电脑配置是系统:windows10;CPU:i5-7200u ,GPU:GeForce 920MX(计算能力应该是5)。原以为环境搭建应该是挺简单的,但是在遇坑的时候才发现这里面还是有很多的学问的,如果没有了解这方面的知识后面很容易出现很多意想不到的“惊喜”。这里要着重推荐一下北大的tensorflow笔记,第一课便是环境的搭建,后续的视频也是非常值得去学习。如果觉得看视频比较麻烦,也可以直接看一下我的这一篇文章。
搭建成功后,下面让我们开始迷人的深度学习之旅吧!

3. 卷积神经网络

    卷积神经网络的开篇之作LeNet-5产生于1998年,从根本来说CNN就是一个多层感知机,基本思想是局部连接、权值共享与采样。卷积其实就是卷积计算,在图像分类问题的卷积运算中,每个像素(pixel)代表一个神经元(neural cell),同一个卷积核(Convolution kernel)内,每个像素所在神经元连接上一层部分神经元,这便是局部连接;第 m层特征图(Feature Map)与第 m-1层特征图建立连接时,第 m-1层的不同神经元连接相同的神经元会共享权重,将部分像素映射为新的像素值,输出为第 m+1层特征图。
    采样是在时间或者空间上对输入特征图进行降低分辨率的处理,保持了网络的空间不变性。这三种特点使得在处理二维图像的问题上,图像可以直接作为网络的输入,有效解决了传统识别算法中特征提取难的问题,尤其是在输入数据的扭曲上具有很强的鲁棒性和网络泛化能力等。
基于CNN的手写体识别与GUI系统设计(新手快进来!)_第1张图片

卷积计算

基于CNN的手写体识别与GUI系统设计(新手快进来!)_第2张图片

    本篇论文主要是以实践为主,因此在这只做简单的介绍。有关LeNet-5模型的结构及卷积网络训练原理需要大家自行了解,下面咱们直接奔入正题!

4. 数字手写体识别实现

    我采用的是MNIST数据集,该数据集是封装在keras库的datasets里面,可以直接调用。本文我是用keras库直接堆叠网络结构,适合新手,下面直接看代码。
卷积神经网络训练六步法
第一步:import

import tensorflow as tf
from matplotlib import pyplot as plt
from net import net1, net2, net3  # 这里是我自己搭建的三个网络
from tensorflow.keras.optimizers import Adam  # 优化器
from tensorflow.keras.preprocessing.image import ImageDataGenerator  # 数据增强函数
from tensorflow.keras.callbacks import ReduceLROnPlateau  # 回调学习率函数

第二步:加载数据集

mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# x_train.shape:(60000,28,28),y_train.shape:(10000,28,28)
# 说明mnist数据集训练集与测试集样本分别为60000个、10000个,图像数据为28*28的像素矩阵,整体训练集与测试集为三维张量
# y_train.shape:(60000,)
# 标签是数值,一维张量

# 将训练集转变为四维张量并归一化
# 这里转为四维张量是因为keras库的卷积层要求输入为四维张量,归一化是方便网络训练
x_train = x_train.reshape(-1, 28, 28, 1) / 255.0
x_test = x_test.reshape(-1, 28, 28, 1) / 255.0

# 把标签转成独热码(一维的概率向量),这里跟后面选择的损失函数与评价指标有联系
# 因为加载的数据集标签都是数值形式,如果选用交叉熵损失函数Categorical_Crossentropy为损失函数就需要转变独热码形式
# y_train = utils.to_categorical(y_train, num_classes=10)
# y_test = utils.to_categorical(y_test, num_classes=10)

第三步:kears.Sequential

# 搭建卷积神经网络
model = tf.keras.Sequential([
        Conv2D(input_shape=(28, 28, 1), filters=32, kernel_size=(5, 5), activation="relu", padding='same'),
        MaxPool2D(pool_size=(2, 2)),
        Conv2D(filters=32, kernel_size=(5, 5), activation="relu"),
        MaxPool2D(pool_size=(2, 2), strides=2),
        Conv2D(filters=64, kernel_size=(3, 3), activation="relu"),
        Conv2D(filters=64, kernel_size=(3, 3), activation="relu"),
        Flatten(),
        # 输出层
        Dense(10, activation="softmax")
    ])
# 打印网络结构
model.summary()

第四步: model.compile 模型设定(优化器、损失函数、评价指标的选用)

optimizer = Adam()
# 一般都选 adam优化器,默认学习率为1e-3,这里我们选用另外一种交叉熵函数Sparse_Categorical_Crossentropy和准确率评价指标sparse_categorical_accuracy

model.compile(optimizer=optimizer, loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

# 回调学习率,这个目的是为了提高准确率,如果准确率迭代3次还没有增长就降低学习率(为原来的0.5倍),最低只能降到1e-5
# 经过我的一些实验结果,最后发现acc与loss曲线会收敛的更加平滑
learning_rate_reduction = ReduceLROnPlateau(monitor='val_sparse_categorical_accuracy', patience=3, mode='auto',
                                            verbose=1, factor=0.5, min_lr=1e-5)

# 数据增强,增强模型的泛化能力,是防止过拟合的一种方法
data_augment = ImageDataGenerator(rotation_range=10, zoom_range=0.1, width_shift_range=0.1, height_shift_range=0.1,
                                  horizontal_flip=False, vertical_flip=False)

第五步:model.fit或者model.fit_generator 模型训练

# 训练模型,每喂入32组数据打乱样本,迭代100次,verbose是日志输出形式有1,2,3,默认为1表示每个banch都输出,2表示一行一行的输出,3表示不输出
history = model.fit_generator(data_augment.flow(x_train, y_train, batch_size=32), epochs=100, verbose=2,
                              validation_data=(x_test, y_test), callbacks=[learning_rate_reduction], shuffle=True)

# 保存模型,因为本模型是我优化之后的所以这里直接保存下来,运行结果是输出一个h5文件,保存了模型的结构与参数,后面的GUI设计会用到这个文件
model.save('jun_model1.h5')

第六步:model.evaluate

# 评估模型,打印测试集loss和acc
loss, acc = model.evaluate(x_test, y_test, verbose=0)
print('训练完成\n')
print('Test loss is {:5f}'.format(loss))
print('Test accuracy is {:5.2f}% '.format(100 * acc))

    在这里我还加了一个可视化代码,几乎适用于每一个网络,注意如果你的损失函数和准确率不是Sparse_Categorical_Crossentropy和sparse_categorical_accuracy,那么对应的acc与val_acc就要改写代码。

# 训练集和验证集的acc和loss曲线可视化
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.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()

    训练结束,loss曲线与acc曲线如下图所示:
基于CNN的手写体识别与GUI系统设计(新手快进来!)_第3张图片
发现误差与准确率都收敛,说明网络训练良好,再看结果发现识别准确率达到了99.52%,网络训练非常成功!
基于CNN的手写体识别与GUI系统设计(新手快进来!)_第4张图片
基于CNN的手写体识别与GUI系统设计(新手快进来!)_第5张图片

基于CNN的手写体识别与GUI系统设计(新手快进来!)_第6张图片
创作中·······

你可能感兴趣的:(笔记)