基于Tensorflow的MNIST手写数字识别及Web验证的实现

最近一月在郫县大数据中心实训,实训的项目包括Python数据处理和Spark ML,我选的实训的结题项目为MNIST手写数字识别项目,一来是对学的Python知识进行总结,二来是对机器学习进行一个入门,特整理博客,梳理一下思路。

拿到这个题目,加上之前对于机器学习理论知识的学习,首先想到的是使用Tensorflow框架进行编写,因为Python实现较为多,也较为顺手,几乎不会遇到一些未知和无关的错误。

这里实现一个基于Tensorflow的MNIST手写数字识别及Web验证的实现,也就是包括数据处理、机器学习的训练、再到测试集的验证、再实现一个前端在线测试,就是手动画一个数字,利用API,使用训练好的模型进行测试,查看效果。

我跟的教程为官方教程。所用技术为Tensorflow + Numpy+Flask。采用的模型包括regression回归 和convolution卷积 两种。

此处不再说明如何配环境了,对于这些包的安装都是cmd中pip install命令,IDE采用PyCharm

pip install tensorflow
pip install numpy
pip install flask

关于本项目的架构为

---MyMnist  //项目总目录

------data //训练得到的模型文件的目录

------MNIST_data //MNIST数据集:训练集和测试集,一共四个文件

------input.py //用于在项目中读取数据集

------model.py //定义模型(卷积和回归)的函数

------convolutional.py //卷积的具体实现:训练和测试

------regression.py //回归的具体实现:训练和测试

------main.py//为前端提供一个接口,用于运行前端和数据传送

------其他网页前端文件夹(src、static、templates)

1.首先导入MNIST数据集。数据集可以手动导入,也可以利用Tf自带的datasets进行导入,前者可以自己去下载,建一个包装进去,然后就会自动解压识别。我这里使用的是后者,当时公司断网,就又手动导入了。

input.py

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import tensorflow
import gzip
import os
import tempfile

import numpy
from six.moves import urllib
from six.moves import xrange

from tensorflow.contrib.learn.python.learn.datasets.mnist import  read_data_sets

input.py负责数据集的导入,以及必要包的导入。

这里的read_data_sets会被后边经常用到,调用来读取数据集。mnist在tf中内置

在input写好以后,在新建的py文件中,先import input 再data=input.read_data_sets即可将数据集下载、解压、赋给data

接下来,构造网络模型model.py

import tensorflow as tf

#y=wx+b
def regression(x):
    W = tf.Variable(tf.zeros([784,10]),name="W")  #all is 0 定义权重w,784*10的图像
    b=tf.Variable(tf.zeros([10]),name="b") #定义偏移量b
    y=tf.nn.softmax(tf.matmul(x,W)+b) #定义y

    return y, [W, b]

def convolutional(x, keep_prob):
    def conv2d(x, W):  # 卷积层
        return tf.nn.conv2d(x,W,strides=[1, 1, 1, 1], padding='SAME')

    def max_pool_2x2(x):  # 池化层
        return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    def weight_variable(shape):  # 定义权重
        initial = tf.truncated_normal(shape, stddev=0.1)
        return tf.Variable(initial)

    def bias_variable(shape):  # 定义bias
        initial = tf.constant(0.1,shape=shape)
        return tf.Variable(initial)
# 第一层
    x_image = tf.reshape(x,[-1, 28, 28, 1])
    W_conv1 = weight_variable([5, 5, 1, 32])
    b_conv1 = bias_variable([32])
    h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1)+b_conv1)
    h_pool1 = max_pool_2x2(h_conv1)

# 第二层
    W_conv2 = weight_variable([5, 5, 32, 64])
    b_conv2 = bias_variable([64])
    h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
    h_pool2 = max_pool_2x2(h_conv2)

#全连接层
    W_fc1 = weight_variable([7*7*64, 1024])
    b_fc1 = bias_variable([1024])
    h_pool2_flat = tf.reshape(h_pool2,[-1, 7*7*64])
    h_fc1=tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1)+b_fc1)

    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

    W_fc2=weight_variable([1024,10])
    b_fc2=bias_variable([10])
    y = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2)

    return y, [W_conv1, b_conv1, W_conv2, b_conv2, W_fc1, b_fc1, W_fc2, b_fc2]

首先是对回归模型的函数的定义

#y=wx+b
def regression(x):
    W = tf.Variable(tf.zeros([784,10]),name="W")  #all is 0 定义权重w,784*10个0
    b=tf.Variable(tf.zeros([10]),name="b") #定义偏移量b
    y=tf.nn.softmax(tf.matmul(x,W)+b) #定义y

    return y, [W, b]

这一段主要就是实现 y=wx+b 

先定义权重w    偏移量b  然后定义y与w x和b的关系,其中Softmax函数,或称归一化指数函数,是逻辑函数的一种推广。

 W = tf.Variable(tf.zeros([784,10]),name="W")  #定义权重为 784*10个值,并全部赋值为0

为什么是784*10呢?因为这是回归模型,如图所示,输入为28*28的图像,也就是输入为784个变量,而输出为0-9这十个数。

或者根据y=wx+b对应的矩阵模型,输入x为784行1列的矩阵、w为784行10列的矩阵、b为10行1列的矩阵,y也是10行1列。

矩阵相乘是左行乘右列,w乘x就变成了10行一列,正好与b相匹配。

如图所示(盗的图)

2017-09-05-17-51-03

接着是卷积函数的定义

def convolutional(x, keep_prob):
    def conv2d(x, W):  # 卷积层
        return tf.nn.conv2d(x,W,strides=[1, 1, 1, 1], padding='SAME')

    def max_pool_2x2(x):  # 池化层
        return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    def weight_variable(shape):  # 定义权重
        initial = tf.truncated_normal(shape, stddev=0.1)
        return tf.Variable(initial)

    def bias_variable(shape):  # 定义bias
        initial = tf.constant(0.1,shape=shape)
        return tf.Variable(initial)
# 第一层
    x_image = tf.reshape(x,[-1, 28, 28, 1])
    W_conv1 = weight_variable([5, 5, 1, 32])
    b_conv1 = bias_variable([32])
    h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1)+b_conv1)
    h_pool1 = max_pool_2x2(h_conv1)

# 第二层
    W_conv2 = weight_variable([5, 5, 32, 64])
    b_conv2 = bias_variable([64])
    h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
    h_pool2 = max_pool_2x2(h_conv2)

#全连接层
    W_fc1 = weight_variable([7*7*64, 1024])
    b_fc1 = bias_variable([1024])
    h_pool2_flat = tf.reshape(h_pool2,[-1, 7*7*64])
    h_fc1=tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1)+b_fc1)

    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

    W_fc2=weight_variable([1024,10])
    b_fc2=bias_variable([10])
    y = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2)
return y, [W_conv1, b_conv1, W_conv2, b_conv2, W_fc1, b_fc1, W_fc2, b_fc2]

本项目的卷积神经网络包括三层

第一层内有 卷积层、池化层,先卷积再池化,激活函数是relu

第二层内有 卷积层、池化层,先卷积再池化,激活函数是relu

第三层为全连接层,将前两层的参数进行全连接。激活函数是softmax

这部分后面的内容主要是对所用到的参数进行声明和定义,最后将这部分参数作为返回值进行返回。

接下来是进行两种算法的实现

regression.py 这部分具体实现回归算法。

import input
import os
import model
import tensorflow as tf


#读取数据集
data=input.read_data_sets("MNIST_data",one_hot=True)

# 建立回归模型,定义此次要用到的x 将x传入上面实现的regression函数中对y等进行赋值。
with tf.variable_scope("regression"):
    x=tf.placeholder(tf.float32, [None, 784])  #占位符
    y, variables = model.regression(x)

# 设定训练的一系列参数
y_=tf.placeholder("float",[None,10])
# 定义交叉熵
cross_entropy=-tf.reduce_sum(y_ * tf.log(y))
# 定义训练的步长,采用优化器
train_step=tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

correct_prediction=tf.equal(tf.arg_max(y,1),tf.argmax(y_,1))
# 准确度
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

# 用来保存所有参数
saver =tf.train.Saver(variables)

#开始训练
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for _ in range(1000):
        batch_xs, batch_ys = data.train.next_batch(100)
        sess.run(train_step, feed_dict={x:batch_xs, y_:batch_ys})

#测试集、测试准确率
    print((sess.run(accuracy,feed_dict={x:data.test.images, y_:data.test.labels})))

#保存模型到ckpt
    path=saver.save(
        sess, os.path.join(os.path.dirname(__file__), 'data', 'regression.ckpt'), # 存到ckpt文件里
        write_meta_graph=False, write_state=False)  # 不存到图里

    print("Saved:", path)

convolutional.py 这部分具体实现卷积神经网络

import os
import model
import tensorflow as tf
import input

data = input.read_data_sets('MNIST_data',one_hot=True)

#model
with tf.variable_scope("convolutional"):
    x = tf.placeholder(tf.float32,[None,784], name='x')
    keep_prob = tf.placeholder(tf.float32)
    y, variables = model.convolutional(x, keep_prob)

#train
y_ = tf.placeholder(tf.float32, [None, 10], name='y')
cross_entropy = -tf.reduce_sum(y_ * tf.log(y))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

saver = tf.train.Saver(variables)

with tf.Session() as sess:
    merged_summary_op = tf.summary.merge_all()
    summary_writer = tf.summary.FileWriter('/tmp/mnist_log/1', sess.graph)
    summary_writer.add_graph(sess.graph)  # 将它的图保存起来
    sess.run(tf.global_variables_initializer())

    for i in range(20000): # 设置训练两万次
        batch = data.train.next_batch(50)
        if i % 100 == 0:  # 每100次对准确率进行打印
            train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0})
            print("Step %d, training accuracy %g" % (i, train_accuracy))
        sess.run(train_step,feed_dict={x: batch[0], y_: batch[1],keep_prob: 0.5 })

    # 测试
    print(sess.run(accuracy,feed_dict={x: data.test.images, y_: data.test.labels, keep_prob: 1.0}))

    path = saver.save(
        sess, os.path.join(os.path.dirname(__file__), 'data', 'convolutional.ckpt'),
        write_meta_graph = False, write_state=False)

    print("Saved:", path)

这部分主要是对两种回归函数进行训练、测试。

接下来,我们对regression.py和convolutional.py进行运行,进行训练,并且得到模型。

这里要注意的是,先建好data文件夹,不然会报错。

将前端代码编写好以后导入项目中,以完成前端逻辑并展示。

然后进行前端接口设计。其实也就是获取网页前端的输入(一幅图片),进行处理以后,分散到格子中,转化为坐标,然后传回服务器,利用API与已经训练好的模型进行比较,并返回结果。

main.py

import numpy as np
import tensorflow as tf
from flask import Flask,jsonify, render_template, request

import model

x=tf.placeholder("float",[None, 784])
sess= tf.Session()

with tf.variable_scope("regression"):
    y1, variables = model.regression(x)
saver = tf.train.Saver(variables)
saver.restore(sess,"data/regression.ckpt")

with tf.variable_scope("convolutional"):
    keep_prob = tf.placeholder("float")
    y2, variables = model.convolutional(x, keep_prob)
saver = tf.train.Saver(variables)
saver.restore(sess,"data/convolutional.ckpt")

def regression(input):
    return sess.run(y1, feed_dict={x: input}).flatten().tolist()

def convolutional(input):
    return sess.run(y2,feed_dict={x:input,keep_prob:1.0}).flatten().tolist()

app=Flask(__name__)
@app.route('/api/mnist',methods=['post'])
def mnist():
    input = ((255 - np.array(request.json, dtype=np.uint8))/255.0).reshape(1,784)
    output1 = regression(input)
    output2 = convolutional(input)

    return jsonify(results=[output1, output2])

@app.route('/')
def main():
    return render_template('index.html')

if __name__ =='__main__':
    app.debug = True
    app.run(host='127.0.0.1',port=8000)

接下来运行main.py即可开启服务器,使用chrome浏览器即可查看。

 

可以看出,卷积神经网络实现的更准确一点。

发布了52 篇原创文章 · 获赞 27 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_37457432/article/details/88417683