初探神经网络---手写数据集识别

325 阅读4分钟

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

机器学习,学习的到底是什么呢?不要想的太复杂,机器学习模型对应的就是一些计算函数而已,现在先不去考虑这些函数有多复杂,只是先了解它就是函数就可以了,通过这些函数的作用,将一些特定的输入通过某种映射,产生输出的一种作用方式。在函数的内部也仅仅执行了加法和乘法而已,这么一想是不是突然就感觉没那么神秘,没那么复杂了!

在本篇博客开始,我们将开始学习代码了,从最基础的mnist手写数据集的识别开始,由简单慢慢转向复杂。 首先,我们需要得到手写数据集,网络上下载数据集的链接非常多,本人在这里给出其中一种,如果之前自己有保存,就无需重新下载。Mnist链接:yann.lecun.com/exdb/mnist/

手写数据集由60000个训练集和10000个测试集组成,数据集中每张图片都被严格的处理过,均为28x28像素大小。

在处理之前,先介绍一种编码方式,在后面的学习中经常需要用到此类的思想,在分类时,往往我们期望得到的是一些直观的非数值特征,例如车、人、飞机等等,但是在实际的处理过程中,数值特征相对于直观特征更加方便操作,因此将一些非数值特征转换为数值特征,是机器学习中常用的一种手段。本文,主要使用一种“独热编码”方式来进行处理。由于手写数字中一共包含了[0,9]十个数字,因此我们可以建立一个包含10位的二进制向量进行表示,例如表示数字7,则用one-hot编码可以写为[0,0,0,0,0,0,0,1,0,0]。

下面我将介绍使用tensorflow2.0如何搭建一个自己的手写数据集识别网络。 在tensorflow中提供了库函数来完成数据集的解析,库函数会方便的让我们把数据集加载进来,并拆分为训练集和测试集。当训练神经网络并归一化到[0,1]时,数据转换为flost32类型。另外,数据集加载函数会自动的帮我们将正确的标签对应带加载进来的数据集中,并对它们进行one-hot编码。

对于代码中的一些定义,在此处先说明一下,其含义在后面的代码学习中也同样适用。

epoch,定义了训练的持续时间。

bach_size,定义了一次输入到网络中的样本数。

validation,是为了检查或证明训练过程的有效性而保留的数据量。手写数据集的训练代码如下,代码中已经写了详细的注释。

# 导入用到的包
import tensorflow as tf
import numpy as np
from tensorflow import keras
import matplotlib.pyplot as plt

# 将字体改为中文
plt.rcParams["font.family"] = 'SimHei'
# 设置了中文字体默认后,坐标的"-"号无法显示,设置这个参数就可以避免
plt.rcParams['axes.unicode_minus'] = False

# 一些参数信息
epoch = 50
bach_size = 128
# 日志显示,0不显示,1显示,2每个epoch显示一次
verbose = 1
# 表示分为10类
nb_classes = 10
# 隐藏层数
n_hidden = 128
# 检查训练过程
validation_split = 0.2
# 加载在线的数据集,此处也可以替换成离线数据集
mnist = keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 28x28个神经元
reshape = 784
# 显示数据集中的图片与对应的标签
# plt.imshow((x_train[1]))
# plt.show()
# print(y_train[1])

x_train = x_train.reshape(60000, reshape)
x_test = x_test.reshape(10000, reshape)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
# print(x_train.shape[0])
# print(x_test.shape[0])
# 归一化处理
x_train /= 255.0
x_test /= 255.0
# 将标签改写为独热编码形式
y_train = keras.utils.to_categorical(y_train, nb_classes)
y_test = keras.utils.to_categorical(y_test, nb_classes)

# 定义容器
model = keras.models.Sequential()
# 手写数据集没有卷积和池化层,在此定义的为全连接层
model.add(keras.layers.Dense(n_hidden, input_shape=(reshape, ), name='dense_layer', activation='relu'))
model.add(keras.layers.Dense(n_hidden, input_shape=(reshape, ), name='dense_layer1', activation='relu'))
model.add(keras.layers.Dense(nb_classes, input_shape=(reshape, ), name='dense_layer2', activation='softmax'))
# 输出模型各层的参数状况
model.summary()
# 使用优化器,随机梯度下降
model.compile(optimizer='sgd',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
# 训练模型
model.fit(x_train, y_train, batch_size=bach_size, epochs=epoch, verbose=verbose, validation_split=validation_split)

# 评价模型
test_loss, test_acc = model.evaluate(x_test, y_test)
# 将模型进行保存
predictios = model.save('mnist.h5')
print("Test Acc:", test_acc)

上面的代码执行结果如下

捕获.PNG

从上面的执行结果中,我们并没有直观的看到我们训练模型的效果如何,因此呢,在上面代码的最后部分,我将模型保存了下来,在下面的代码中将模型再加载上,然后自己手写几张数字测试一下,这样就非常只管了。测试代码如下

import numpy as np
from PIL import Image
from sklearn.preprocessing import StandardScaler
from tensorflow import keras
# 加载保存的手写数据集识别模型
predictios=keras.models.load_model('mnist.h5')
scaler = StandardScaler()
# 打开自己手写的图片
im = Image.open('./6.jpg')
im=im.resize((28, 28), Image.ANTIALIAS)
L = im.convert('L')
im2 = np.array(L)
# 手写白色背景是255(白色),与训练集中不一致,需要自己处理一下
im2=np.array([255 - im2])
im3=scaler.fit_transform(im2.astype(np.float32).reshape(-1, 1)).reshape(-1, 784)
# 预测结果
y=predictios.predict(im3)
print(y[0])
print('预测的图片数字为:%s'%np.argmax(y[0]))

下面可以自己尝试写代码测试结果了,有兴趣的还可以借助PyQT5,实现一个简单的界面。