You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
li.chengmeng 32b55dbbba
更新第二版readme
3 years ago
.idea test 3 years ago
myModel 显示预测错误对应的图片 3 years ago
.gitignore Initial commit 3 years ago
LICENSE Initial commit 3 years ago
README.md 更新第二版readme 3 years ago
predict.py 更新第二版readme 3 years ago
train.py 训练与预测分离,加入断点训练 3 years ago

README.md

num_mnist 手写数字识别

设计与实现

概要设计

本项目基于TensorFlow库包进行机器学习神经网络的构建、训练与评估,以实现对手写数字的识别。训练过程中使用了TensorFlow内置num_mnist训练集,保证了数据集的真实可靠。网络模型方面选择使用卷积神经网络与全连接神经网络结合,并搭配反向传播算法进行训练。选择ReLu线性整流函数作为激活函数,保证训练效率的同时简化了计算过程。

同时考虑到人机交互方面,于是加入了网络模型本地保存与读取的功能,使得网络模型的使用更加方便。另外考虑到在训练过程中可能会发生意外状况导致训练中止,为此加入了训练检查点并支持网络模型读取检查点来获取权值数据从而继续训练。

详细设计与实现

网络模型

神经网络模型本质是一种仿生的设计,我们人类大脑的神经元通过突触与其他神经元相连接,接收其他神经元送来的信号,经过汇总处理之后产生输出。于是机器学习科学家们将大脑神经元进行抽象,并在计算机上进行实现,便有了网络模型的概念。神经网络一般有多个层,其中第一层为输入层,对应输入向量。第一层网络中的神经元的数量等于输入向量的维数,这个层不对数据进行处理,只是将输入向量送入下一层中进行计算。在第一层与最后一层之间的所有层为隐藏层,负责主要的处理工作。最后是输出层,该层神经元的数量根据实际需求而定,每个神经元对应着一个输出。

神经网络的训练方式为监督学习,通俗来讲就像我们刷题的过程,不断刷题,做完一题就看答案,从而学会如何考满分。通过输入值X(题目)与真实值Y(答案),来不断进行训练。输入值X传入网络并经过一系列运算之后可以得到预测值Y^{'},通过预测值Y^{'}与真实值Y我们可以得到损失值loss,并以损失值来评估预测结果的好坏,并通过反向传播算法将预测好坏程度反馈给网络,网络以此来修改自己使自己的预测更准确。

网络模型选取

在部署好TensorFlow之后的第一件事便是为项目选择合适的神经网络模型。由于缺乏项目经验,于是我们决定将常用的网络先试一下看看效果,并实验得出卷积神经网络比较适合该项目。

全连接神经网络(MLP):顾名思义就是每一层网络的每一个神经元都与相邻层网络的每个神经元连接(如下图)。

img

每个神经元可以视为对输入值x乘以一个系数w并加上一个偏置b,从而得到输出值f


f=wx+b

其中我们称w为该神经元的权值,称b为该神经元的偏置

为了保证输出值在一定范围之内,往往需要使用激活函数来实现归一化(输出值映射到[0,1]的区间之内)。

神经元之间相互连接,最初的输入值经过多个神经元之手多次处理,从而得到最后输出层中的输出值。因为每一级输出的值都将作为下一级的输入,只有将输入归一化了,才会避免某个输入无穷大,导致其他输入无效,最终网络训练效果非常不好。

输入值在经过许多个神经元传递之后便得到了输出值。为了描述输出值的预测效果,我们定义损失值Loss。这里使用最小二乘法来得到损失值。


Loss=\Sigma_{i}^{n}(y_i-(wx_i+b))^2

损失值可以用来评估预测结果的好坏,为了得到最小的损失值Loss,我们需要通过梯度下降算法来求得最佳的权值w与偏置b

从输出层到输入层,对每层的神经元使用梯度下降算法来从后向前更新权值与偏置,这便是反向传播算法

img

卷积神经网络CNN

一个经典的CNN网络结构一般包含输入层Input layer、卷积层convolutional layer、池化层pooling layer和输出层output layer。卷积神经网络把图片转换成二维矩阵格式的数据输入数据网络的各层都以二维矩阵的方式处理数据这样的数据处理方式适用于二维矩阵格式的数字图像相较于传统的人工神经网络它能更快更好地把特征值从图像数据中提取出来。

image-20211029174821041

图像拥有很多冗余的信息而且往往作为输入信息它的矩阵又非常大利用全连接神经网络去训练的话计算量非常大前人为此提出了CNN它的亮点之一就是拥有卷积层。卷积层通俗易懂来说就是压缩提纯。

image-20211029175907096

卷积核在滑动过程中做的卷积运算就是卷积核w 与其所覆盖的区域的数进行点积最后将结果映射到卷积层如下式。这样看来我们将9个点的信息量压缩成了1个点也就是有损压缩这个过程我们也可以认为是特征提取。公式中(w , b)和之前全连接神经网络并没有区别,就是权值w 和偏置b ,它们在初始化之后,随着整个训练过程一轮又一轮的迭代逐渐趋向于最优。在卷积核 之后一般会加一个ReLu的激励函数,从而让训练结果更优。


f(x)=wx+b \\
w =
\begin{bmatrix}
	-1 && -2 && -1 \\
	0 && 0 && 0 \\
	1 && 2 && 1 \\
\end{bmatrix}

同时通过局部连接权值共享的方式来减少训练参数,加快训练速度。

image-20211029180250518

image-20211029180257828

一般来说,卷积层后面都会加一个池化层,可以将它理解为对卷积层进一步特征抽样,类似降低视频清晰度的操作,从而使得运算量进一步减小。

image-20211029180522893

最后在输出层前加入Softmax该层我们可以理解为使每个节点输出一个概率所有节点的概率加和等于1这也是CNN选择softmax层进行分类的原因所在,可以将一张待分类的图片放进模型,Softmax输出的概率中,最大概率所对应的标签便是这张待分类图的标签。

image-20211029180903550

实现过程

构建网络模型

# 构建网络 开始
'''
卷积 池化 卷积 池化 全连接 全连接
'''
model = keras.Sequential()
model.add(keras.layers.Conv2D(8, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(keras.layers.MaxPooling2D(2, 2))
model.add(keras.layers.Conv2D(8, (3, 3), activation='relu'))
model.add(keras.layers.MaxPooling2D(2, 2))

model.add(keras.layers.Flatten())  # 扁平化处理,实现从卷积到全连接的过渡
model.add(keras.layers.Dense(128, activation=tf.nn.relu))
model.add(keras.layers.Dense(10, activation=tf.nn.softmax))

model.compile(optimizer='adam', loss=tf.losses.sparse_categorical_crossentropy, metrics=['accuracy'])
# 构建网络 结束

训练并评估

# 训练
history = model.fit(
    train_images_scaled.reshape(-1, 28, 28, 1),
    train_labels,
    epochs=8,
    validation_data=(test_images.reshape(-1, 28, 28, 1), test_labels),
    callbacks=[cp_callback]
)

# 评估
results = model.evaluate(test_images.reshape(-1, 28, 28, 1), test_labels)

将训练后的模型保存

# 保存模型
model.save(model_path)

加载模型

# 读取训练好的模型
model = load_model(model_path)

可视化预测效果

# 可视化预测效果
show_num = 200
testShow = test_labels[:show_num]

pred = model.predict(test_images.reshape(-1, 28, 28, 1))
predict = []
for item in pred:
    predict.append(np.argmax(item))

# 显示折线图
plt.figure()
plt.title('Conv Predict')
plt.ylabel('true number')
plt.xlabel('img num')
plt.plot(range(testShow.size), predict[:show_num], marker='^', color='coral', label='predict')
plt.plot(range(testShow.size), testShow,  marker='o', color='deepskyblue', label='result')
plt.legend()

# 挑选出预测错误的图片,并显示预测值
wrongImg = []
wrongNum = []
for i in range(testShow.size):
    if (predict[i] != testShow[i]):
        wrongImg.append(test_images[i])
        wrongNum.append(predict[i])

for i in range(len(wrongImg)):
    plt.figure()
    plt.title(str(wrongNum[i]))
    plt.imshow(wrongImg[i])

plt.show()

系统测试

训练过程

image-20211029161354173

预测过程

image-20211029161427323

可视化预测效果。左侧x轴为图片编号纵轴为图片中数字的真实值右侧是输出的预测错误的图片。

image-20211029161907128

保存在本地的网络模型

image-20211029163642203

总结

本项目实现了基于TensorFlow的卷积神经网络数字识别,集训练、预测于一体,具有模型保存、读取功能并支持断点训练。在开发中遇到的最大的困难其实更多是配置TensorFlow的环境以及API的调用。使用Anaconda来管理Python环境使得环境配置更简单,搭配PyCharm使用让TensorFlow的使用快捷方便。在构建网络模型与训练方面使用了TensorFlow下的Keras包,该包囊括了网络模型自定义、模型训练、预测效果评估、模型保存与读取等众多功能,大大降低了开发的复杂度,保证了开发效率。同时为了实现预测效果的可视化,我使用了Pythonmatplotlib包,利用内置函数非常便捷的绘制出了形象生动的图片,使得预测效果一目了然。

通过本次项目,我不仅学习到了机器学习的相关知识,而且熟悉了Python开发的各种工具以及API的使用方法。同时也更加熟悉了Python的语法,本次项目经验使得我能在今后的开发中更加得心应手。