基于MNIST数据的手写数字识别

简易实现可通过网页进行手写数字输入,并使用基于MNIST数据训练的神经网络进行输入预测。先上图

软件版本:

Python:3.6

Tensorflow:1.9

代码地址:https://github.com/chfenix/learnTensorflow/tree/master/top/chfenix/tensorflow/mnist

神经网络训练

首先实现使用MNIST进行神经网络训练,创建文件nn_train.py

无法自动下载的可以通过官网下载:http://yann.lecun.com/exdb/mnist/

下载后的文件无需解压,直接放到代码所在目录下的MNIST_data目录中

   

在文件中导入MNIST数据

from tensorflow.examples.tutorials.mnist import input_data

# 读取mnist数据
mnist = input_data.read_data_sets("MNIST_data", one_hot=True)

MNIST数据每张图片为28*28像素的灰度图 ,即每张图片有784个数据点

mnist.train 为训练集,共55000条
mnist.test 为测试集,共10000条

可通过如下代码查看MNIST数据中具体的矩阵数值以及图像(文件:test_mnist2img.py)

im = mnist.train.images[index]
print("----------------------------------------")
print("label: %s" % mnist.train.labels[index])
for i in range(28):
    print(np.hsplit(im, 28)[i])
print("----------------------------------------")


im = im.reshape(-1,28)
pylab.imshow(im)
pylab.show()

考虑采用包含一个隐层的神经网络进行训练,由于每张图片有784个像素点组成,所以输入层节点数为784。预测结果为0-9,共10种可能,所以输出节点数位10。(训练集所提供的结果labels数据也是长度为10的数组)

首先声明占位符,用于之后接收训练或测试集的输入数据。由于简易页面输入无灰度值,所以此处对MNIST数据进行了四舍五入(tf.round)。此处也可以不做处理,训练后结果差异不大

with tf.name_scope("input"):
    # 输入数据,n*784矩阵
    inputData = tf.round(tf.placeholder(tf.float32, shape=(None, 784), name="inputData"))
    # 输入数据的结果(标签)n*10矩阵
    inputLabel = tf.placeholder(tf.float32, shape=(None, 10), name="inputLabel")

定义创建网络层方法,参数为输入数据,上层节点数,本层节点数,层名称(便于在Tensorboard查看),激活函数(默认使用relu)

# 创建神经网络层 @param:输入数据,上层节点数,本层节点数,层名称,激活函数
def create_nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu):
    # 设置命名空间
    with tf.name_scope(layer_name):
        # 初始化权重值
        with tf.name_scope("weights"):
            weights = init_weights([input_dim, output_dim])
            variable_summaries(weights)  # 记录权重信息

        # 初始化偏置量
        with tf.name_scope("biases"):
            biases = init_bias([output_dim])
            variable_summaries(biases)  # 记录偏置量信息

        # 执行Wx+b的线性计算,并且用直方图记录下来
        with tf.name_scope("linear_compute"):
            preactivate = tf.matmul(input_tensor, weights) + biases
            tf.summary.histogram("linear", preactivate)

        # 将线性输出经过激励函数,并将输出也用直方图记录下来
        activations = act(preactivate, name="activation")
        tf.summary.histogram("activations", activations)

    # 返回激励层的最终输出
    return activations

创建隐层,输入数据即MNIST的原始数据,隐层节点数通过常量定义,便于修改观察运行效果

# 创建隐层
layerHidden1 = create_nn_layer(inputData, 784, HIDDEN_LAYER_POTIN_NUM, "layerHidden1")

通过常量定义,可以选择使用或者不是用dropout层,dropout层作用为按照比例禁用一部分节点,防止网络过拟合(在实际测试过程中,此模型dropout对其影响不大)

# 创建dropout层
with tf.name_scope('dropout'):
    keep_prob = tf.placeholder(tf.float32, name="keep_prob")
    tf.summary.scalar('dropout_keep_probability', keep_prob)
    layerDropout = tf.nn.dropout(layerHidden1, keep_prob)

layerToOut = layerHidden1

# 判断是否使用dropout层
if BOL_DROPOUT:
    layerToOut = layerDropout

创建输出层,由于之后会使用tf的内置softmax交叉熵方法,此处只对结果层的输入做原样输出(tf.identity)

# 创建输出层,tf.identity为恒等操作
layerOutput = create_nn_layer(layerToOut, HIDDEN_LAYER_POTIN_NUM, 10, "layerOutput", act=tf.identity)

至此已经将网络结构定义完毕,整体网络分为输入层(MNIST原始数据,784节点),隐层layerHidden1,Dropout层(如果有),输出层(10节点)。接下来定义损失函数

# 定义损失函数
with tf.name_scope("loss"):
    # 计算交叉熵损失(每个样本都会有一个损失)
    diff = tf.nn.softmax_cross_entropy_with_logits_v2(labels=inputLabel, logits=layerOutput)
    with tf.name_scope("total"):
        # 计算所有样本交叉熵损失的均值
        cross_entropy = tf.reduce_mean(diff)

有了损失函数以后定义训练模型,使用梯度下降方式

# 定义训练函数
with tf.name_scope("train"):
    train_op = tf.train.GradientDescentOptimizer(TRAIN_LEARN_RATE).minimize(cross_entropy)

定义预测准确率

# 定义预测准确率
with tf.name_scope("accuracy"):
    # 预测op,layerOutput为n*10矩阵,argmax为获取矩阵中某个纬度的最大值所在索引,也就是预测出的具体数字,第二个参数1代表每行计算最大值,0代表按列计算
    with tf.name_scope("correct_prediction"):
        # 分别将预测和真实的标签中取出最大值的索引,弱相同则返回1(true),不同则返回0(false)
        correct_prediction = tf.equal(tf.argmax(layerOutput, 1), tf.argmax(inputLabel, 1))
    with tf.name_scope("accuracy"):
        # 求均值即为准确率
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32), name="rate")

至此已经将训练所需的内容定义完毕,可以使用tensorflow运行定义好的模型

sess = tf.InteractiveSession()

# 运行初始化所有变量
tf.global_variables_initializer().run()

# 输入数据(True:训练,False:测试)
def feed_dict(train):
    if train:
        # 训练
        image_data, result_label = mnist.train.next_batch(TRAIN_BATCH_NUM)
        dropout = DROPOUT_RATE
    else:
        # 测试
        image_data, result_label = mnist.test.images, mnist.test.labels
        dropout = 1.0
    return {inputData: image_data, inputLabel: result_label, keep_prob: dropout}

# 进行迭代训练
total_start_time = time.clock()
for i in range(TRAIN_STEP):
    start_time = time.clock()
    if i % 10 == 0:  # 记录测试集的summary与accuracy
        summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False))
        test_writer.add_summary(summary, i)
        end_time = time.clock()
        print("迭代[%s] 准确率:%.2f%% 耗时:%.0fms" % (i, acc * 100, (end_time - start_time) * 1000))
    else:  # 记录训练集的summary
        if i % 100 == 99:  # Record execution stats
            run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
            run_metadata = tf.RunMetadata()
            summary, _ = sess.run([merged, train_op],
                                  feed_dict=feed_dict(True),
                                  options=run_options,
                                  run_metadata=run_metadata)
            train_writer.add_run_metadata(run_metadata, "step%03d" % i)
            train_writer.add_summary(summary, i)
            # print("Adding run metadata for", i)
        else:  # Record a summary
            summary, _ = sess.run([merged, train_op], feed_dict=feed_dict(True))
            train_writer.add_summary(summary, i)
total_end_time = time.clock()
# 最终准确率
end_acc = sess.run(accuracy, feed_dict=feed_dict(False))
print("迭代次数[%s] 测试集准确率[%.2f%%] 学习率[%s] 隐层节点数[%s] Dropout[%s:%s] 总耗时[%.3fs]"
      % (i + 1, end_acc * 100, TRAIN_LEARN_RATE, HIDDEN_LAYER_POTIN_NUM, BOL_DROPOUT, DROPOUT_RATE,(total_end_time - total_start_time)))
print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
train_writer.close()
test_writer.close()

没10次迭代进行一次测试,并输入测试准确率。没100次迭代记录训练情况,用于Tensorboard显示

在训练结束后将参数及最终测试准确率,耗时输出,便于观察参数对模型变化的影响

最后将训练好的模型进行保存,用于后续的网页输入预测

if BOL_SAVE_CHECKPOINT:
    saver = tf.train.Saver()
    save_path = saver.save(sess, "./model/mnist")

在代码所在目录执行 tensorboard --logdir=./eventlog ,并在浏览器访问执行后显示的url,可以查看模型运行情况

网页手写输入

按照MNIST数据形式,将页面输入定义为28*28的网格结构,通过js保存canvas数据并提交到后台,数据为784长度的数组,选中位置为1,其他位置为0。转换为28*28矩阵后数据示例如下:

由于此部分不是重点,不贴代码了,有兴趣的可以直接查看源代码(ocr.htmocr.jsserver.py

输入预测

当通过server.py采集到网页输入内容后,将输入内容传入nn_predict.py进行输入预测。

首先读取已经训练好的神经网络模型及数据

        saver = tf.train.import_meta_graph('./model/mnist.meta')
        saver.restore(sess, tf.train.latest_checkpoint('./model'))

        graph = tf.get_default_graph()

        # 恢复输入占位符
        restore_input_data = graph.get_tensor_by_name("input/inputData:0")
        if BOL_DROPOUT:
            # 恢复dropout占位符
            restore_keep_prob = graph.get_tensor_by_name("dropout/keep_prob:0")

        # 恢复预测算法
        restore_predict = graph.get_tensor_by_name("layerOutput/activation:0")

关于tensor的名称,如果不确定的可以在训练时print tensor对象,日志会显示其名称

使用页面输入的手写数据进行预测。预测时不进行节点的Dropout,所以此处dropout参数为1

# 将界面的图片数据传入预测算法,预测结果
        result = sess.run(tf.argmax(restore_predict, 1),
                          feed_dict={restore_input_data: np.mat(image_data), restore_keep_prob: 1.0})

手写输入识别

1.在mnist目录中通过 python -m http.server启动web服务
2.运行server.pay
3.在浏览器访问 http://localhost:8000/ocr.html

参考文献&推荐阅读

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值