两层全连接神经网络实现手写数字识别
一,思路:
分为两步,第一步是训练模型然后把模型保存到磁盘,第二步是复现模型结构然后读取模型权重系数,输入手写数字图片验证模型识别能力。
二,模型训练
在这部分使用mnist数据集,该数据集含有七万张28*28的灰度化的手写数字图片,其中六万张用于训练,一万张用于训练阶段的测试。
过程分为六步:(1.)import 相关模块 (2.)读入训练集与测试集 (3.)用Sequential模型(或者有的人叫它神经网络容器)搭建神经网络 (4.)定义神经网络训练时的优化器(或者叫参数更新的方法,例如随机梯度下降法SGD),损失函数(MSE或者交叉熵),测评指标(用来计算损失函数)(5.)训练模型 ,把最优模型保存到磁盘(6.)打印训练总览
代码如下:
import tensorflow as tf
import os
checkpoint_save_path = "./mnist.ckpt" # 指定模型路径
mnist = tf.keras.datasets.mnist
(train_data, train_label), (test_data, test_label) = mnist.load_data()
train_data = train_data / 255.0
test_data = test_data / 255.0 # 归一化,一是神经网络对0附近敏感,二是不归一化可能无法收敛
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(), # 拉直层参数可以省略,拉直层的作用是把输入神经网络的多维数组拉直为一维数组,拉直层长度是784
tf.keras.layers.Dense(128, activation='relu'), # 隐藏层
tf.keras.layers.Dense(10, activation='softmax') # 输出层,软判决输出,输出为概率值
])
model.compile(
optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy']
)
if os.path.exists(checkpoint_save_path + '.index'):
print('-------------load the model-----------------')
model.load_weights(checkpoint_save_path) # 从保存的模型中读取权重系数
# 如果之前并没有存储训练过的模型就忽略这个if语句块,如果有的话就从保存的权重系数开始训练,而不是随机选取初始权重
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)
history = model.fit(train_data, train_label, batch_size=32, epochs=5, validation_data=(test_data, test_label), validation_freq=1,
callbacks=[cp_callback]) # 在训练模型时,根据callbacks=[cp_callback]的配置选择保存模型最优时的权重系数
# history保存了训练阶段的loss,acc以及测试阶段时的loss,acc
model.summary()
对于该程序的分析,还是要抓住数据结构的变化:
刚刚读入数据集后train_dat是三维数组,shape:(60000,28,28)。第一个维度的每个元素是一个二维数组即一张图片的数据。train_label是一个shape为(60000)的一维数组,每个元素是一个数值即标签。test_data的shape(10000,28,28),test_label的shape为:(10000)。然后数据输入神经网络。神经网络拉直层无运算能力,不算层数,仅仅是把多维数组拉伸为一维数组。拉伸层之后为隐藏层,具有128个神经元,使用relu**函数。最后是输出层,具有十个神经元,使用sotmax**函数使得神经网络输出为概率形式。
神经网络结构示意图如下:
现在分析数据结构的变换:
输入数据按32个分成一个batch,即shape:(32,28,28)。经过拉伸层变为shape:(32,784)
拉直层到隐藏层的全连接权重系数矩阵shape:(784,128)。shape:(32,784)Xshape:(784,128)
得到的矩阵shape为(32,128),这就是隐藏层输出的数据结构。隐藏层到输出层的全连接结系数矩阵shape:(128,10)。shape(32,128)Xshape:(128,10)=shape:(32,10),这就是输出层数据数据的结构,一共有32行,每行是一个特征数据(输入的待识别图片)对于十分类的概率值,取其中概率值最大的分类对应的标签作为手写数字图片的识别结果。
三,复现模型结构,读取训练后得到的最优模型,然后进行实际测试
from PIL import Image
import numpy as np
import tensorflow as tf
model_save_path = './mnist.ckpt' # 指定模型的路径
# 复现模型结构
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')])
# 读入已经经过训练的权重系数
model.load_weights(model_save_path)
while True:
image_path = input("输入图片名")
img = Image.open(image_path)
# 缩放输入图片,使其大小固定
img = img.resize((28, 28), Image.ANTIALIAS) # Image.ANTIALIAS抗锯齿,抗图像折叠失真
img_arr = np.array(img.convert('L')) # 转为8位宽的np.array数据类型
# 遍历每一个像素,进行阈值二值化处理,把输入图片转换为黑底白字图片,因为训练时的图片 全是黑的白字的
for i in range(28):
for j in range(28):
if img_arr[i][j] < 200:
img_arr[i][j] = 255
else:
img_arr[i][j] = 0
img_arr = img_arr / 255.0 # 不归一化可能无法收敛
x_predict = img_arr[tf.newaxis, ...] # 添加一个维度,变为shape:(1,28,28),为输入predict函数做准备;
# predict函数默认参数batch=32,要求输入数据是三维的。
result = model.predict(x_predict) # model.predict执行神经网络前向传播过程,得到神经网络预测结果
print(result) # 打印十分类概率值
pred = tf.argmax(result, axis=1) # 取概率最大的分类的标签值,标签值与0~9数字一一对应
print('\n')
tf.print("识别结果为:", pred)
四,实际测试
手写了几张数字图片(就比如下面那几张),大小可以任意,因为输入验证的时候会调整大小。图片名例如为2.png,3.png等
注:数据增强(随机旋转,随机偏移等增大数据量)之后的识别结果会更好一点。
如果不想使用keras而只用原生的tensorflow的话可以参考我的一篇博客:
最简单的单层神经网络实现鸢尾花分类
推荐阅读
-
(sklearn:Logistic回归)和(keras:全连接神经网络)完成mnist手写数字分类
-
利用tensorflow实现MNIST手写数字识别(单层神经网络)
-
pytorch 深度学习入门代码 (四)多层全连接神经网络实现 MNIST 手写数字分类
-
tensorflow实战之全连接神经网络实现mnist手写字体识别
-
python实现卷积神经网络实现手写数字识别
-
(sklearn:Logistic回归)和(keras:全连接神经网络)完成mnist手写数字分类
-
神经网络实现手写数字识别(MNIST)
-
两层全连接神经网络实现手写数字识别
-
Numpy实现神经网络-手写数字识别
-
python手写神经网络实现识别手写数字