Python(TensorFlow框架)实现手写数字识别系统的方法


Posted in Python onMay 29, 2018

手写数字识别算法的设计与实现

本文使用python基于TensorFlow设计手写数字识别算法,并编程实现GUI界面,构建手写数字识别系统。这是本人的本科毕业论文课题,当然,这个也是机器学习的基本问题。本博文不会以论文的形式展现,而是以编程实战完成机器学习项目的角度去描述。

项目要求:本文主要解决的问题是手写数字识别,最终要完成一个识别系统。

设计识别率高的算法,实现快速识别的系统。

1 LeNet-5模型的介绍

本文实现手写数字识别,使用的是卷积神经网络,建模思想来自LeNet-5,如下图所示:

Python(TensorFlow框架)实现手写数字识别系统的方法

这是原始的应用于手写数字识别的网络,我认为这也是最简单的深度网络。

LeNet-5不包括输入,一共7层,较低层由卷积层和最大池化层交替构成,更高层则是全连接和高斯连接。

LeNet-5的输入与BP神经网路的不一样。这里假设图像是黑白的,那么LeNet-5的输入是一个32*32的二维矩阵。同时,输入与下一层并不是全连接的,而是进行稀疏连接。本层每个神经元的输入来自于前一层神经元的局部区域(5×5),卷积核对原始图像卷积的结果加上相应的阈值,得出的结果再经过激活函数处理,输出即形成卷积层(C层)。卷积层中的每个特征映射都各自共享权重和阈值,这样能大大减少训练开销。降采样层(S层)为减少数据量同时保存有用信息,进行亚抽样。

第一个卷积层(C1层)由6个特征映射构成,每个特征映射是一个28×28的神经元阵列,其中每个神经元负责从5×5的区域通过卷积滤波器提取局部特征。一般情况下,滤波器数量越多,就会得出越多的特征映射,反映越多的原始图像的特征。本层训练参数共6×(5×5+1)=156个,每个像素点都是由上层5×5=25个像素点和1个阈值连接计算所得,共28×28×156=122304个连接。

S2层是对应上述6个特征映射的降采样层(pooling层)。pooling层的实现方法有两种,分别是max-pooling和mean-pooling,LeNet-5采用的是mean-pooling,即取n×n区域内像素的均值。C1通过2×2的窗口区域像素求均值再加上本层的阈值,然后经过激活函数的处理,得到S2层。pooling的实现,在保存图片信息的基础上,减少了权重参数,降低了计算成本,还能控制过拟合。本层学习参数共有1*6+6=12个,S2中的每个像素都与C1层中的2×2个像素和1个阈值相连,共6×(2×2+1)×14×14=5880个连接。

S2层和C3层的连接比较复杂。C3卷积层是由16个大小为10×10的特征映射组成的,当中的每个特征映射与S2层的若干个特征映射的局部感受野(大小为5×5)相连。其中,前6个特征映射与S2层连续3个特征映射相连,后面接着的6个映射与S2层的连续的4个特征映射相连,然后的3个特征映射与S2层不连续的4个特征映射相连,最后一个映射与S2层的所有特征映射相连。此处卷积核大小为5×5,所以学习参数共有6×(3×5×5+1)+9×(4×5×5+1)+1×(6×5×5+1)=1516个参数。而图像大小为28×28,因此共有151600个连接。

S4层是对C3层进行的降采样,与S2同理,学习参数有16×1+16=32个,同时共有16×(2×2+1)×5×5=2000个连接。

C5层是由120个大小为1×1的特征映射组成的卷积层,而且S4层与C5层是全连接的,因此学习参数总个数为120×(16×25+1)=48120个。

F6是与C5全连接的84个神经元,所以共有84×(120+1)=10164个学习参数。

卷积神经网络通过通过稀疏连接和共享权重和阈值,大大减少了计算的开销,同时,pooling的实现,一定程度上减少了过拟合问题的出现,非常适合用于图像的处理和识别。

2 手写数字识别算法模型的构建

2.1 各层设计

有了第一节的基础知识,在这基础上,进行完善和改进。

输入层设计

输入为28×28的矩阵,而不是向量。

激活函数的选取

Sigmoid函数具有光滑性、鲁棒性和其导数可用自身表示的优点,但其运算涉及指数运算,反向传播求误差梯度时,求导又涉及乘除运算,计算量相对较大。同时,针对本文构建的含有两层卷积层和降采样层,由于sgmoid函数自身的特性,在反向传播时,很容易出现梯度消失的情况,从而难以完成网络的训练。因此,本文设计的网络使用ReLU函数作为激活函数。

ReLU的表达式:Python(TensorFlow框架)实现手写数字识别系统的方法

卷积层设计

本文设计卷积神经网络采取的是离散卷积,卷积步长为1,即水平和垂直方向每次运算完,移动一个像素。卷积核大小为5×5。

降采样层

本文降采样层的pooling方式是max-pooling,大小为2×2。

输出层设计

输出层设置为10个神经网络节点。数字0~9的目标向量如下表所示:

Python(TensorFlow框架)实现手写数字识别系统的方法

2.2 网络模型的总体结构

Python(TensorFlow框架)实现手写数字识别系统的方法

其实,本文网络的构建,参考自TensorFlow的手写数字识别的官方教程的,读者有兴趣也可以详细阅读。

2.3 编程实现算法

本文使用Python,调用TensorFlow的api完成手写数字识别的算法。

注:本文程序运行环境是:Win10,python3.5.2。当然,也可以在Linux下运行,由于TensorFlow对py2和py3兼容得比较好,在Linux下可以在python2.7中运行。

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Fri Feb 17 19:50:49 2017

@author: Yonghao Huang
"""

#import modules
import numpy as np
import matplotlib.pyplot as plt
#from sklearn.metrics import confusion_matrix
import tensorflow as tf
import time
from datetime import timedelta
import math
from tensorflow.examples.tutorials.mnist import input_data


def new_weights(shape):
  return tf.Variable(tf.truncated_normal(shape,stddev=0.05))
def new_biases(length):
  return tf.Variable(tf.constant(0.1,shape=length))
def conv2d(x,W):
  return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')
def max_pool_2x2(inputx):
  return tf.nn.max_pool(inputx,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')

#import data
data = input_data.read_data_sets("./data", one_hot=True) # one_hot means [0 0 1 0 0 0 0 0 0 0] stands for 2

print("Size of:")
print("--Training-set:\t\t{}".format(len(data.train.labels)))
print("--Testing-set:\t\t{}".format(len(data.test.labels)))
print("--Validation-set:\t\t{}".format(len(data.validation.labels)))
data.test.cls = np.argmax(data.test.labels,axis=1)  # show the real test labels: [7 2 1 ..., 4 5 6], 10000values

x = tf.placeholder("float",shape=[None,784],name='x')
x_image = tf.reshape(x,[-1,28,28,1])

y_true = tf.placeholder("float",shape=[None,10],name='y_true')
y_true_cls = tf.argmax(y_true,dimension=1)
# Conv 1
layer_conv1 = {"weights":new_weights([5,5,1,32]),
        "biases":new_biases([32])}
h_conv1 = tf.nn.relu(conv2d(x_image,layer_conv1["weights"])+layer_conv1["biases"])
h_pool1 = max_pool_2x2(h_conv1)
# Conv 2
layer_conv2 = {"weights":new_weights([5,5,32,64]),
        "biases":new_biases([64])}
h_conv2 = tf.nn.relu(conv2d(h_pool1,layer_conv2["weights"])+layer_conv2["biases"])
h_pool2 = max_pool_2x2(h_conv2)
# Full-connected layer 1
fc1_layer = {"weights":new_weights([7*7*64,1024]),
      "biases":new_biases([1024])}
h_pool2_flat = tf.reshape(h_pool2,[-1,7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,fc1_layer["weights"])+fc1_layer["biases"])
# Droupout Layer
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)
# Full-connected layer 2
fc2_layer = {"weights":new_weights([1024,10]),
       "biases":new_weights([10])}
# Predicted class
y_pred = tf.nn.softmax(tf.matmul(h_fc1_drop,fc2_layer["weights"])+fc2_layer["biases"]) # The output is like [0 0 1 0 0 0 0 0 0 0]
y_pred_cls = tf.argmax(y_pred,dimension=1) # Show the real predict number like '2'
# cost function to be optimized
cross_entropy = -tf.reduce_mean(y_true*tf.log(y_pred))
optimizer = tf.train.AdamOptimizer(learning_rate=1e-4).minimize(cross_entropy)
# Performance Measures
correct_prediction = tf.equal(y_pred_cls,y_true_cls)
accuracy = tf.reduce_mean(tf.cast(correct_prediction,"float"))
with tf.Session() as sess:
  init = tf.global_variables_initializer()
  sess.run(init)
  train_batch_size = 50
  def optimize(num_iterations):
    total_iterations=0
    start_time = time.time()
    for i in range(total_iterations,total_iterations+num_iterations):
      x_batch,y_true_batch = data.train.next_batch(train_batch_size)
      feed_dict_train_op = {x:x_batch,y_true:y_true_batch,keep_prob:0.5}
      feed_dict_train = {x:x_batch,y_true:y_true_batch,keep_prob:1.0}
      sess.run(optimizer,feed_dict=feed_dict_train_op)
      # Print status every 100 iterations.
      if i%100==0:
        # Calculate the accuracy on the training-set.
        acc = sess.run(accuracy,feed_dict=feed_dict_train)
        # Message for printing.
        msg = "Optimization Iteration:{0:>6}, Training Accuracy: {1:>6.1%}"
        # Print it.
        print(msg.format(i+1,acc))
    # Update the total number of iterations performed
    total_iterations += num_iterations
    # Ending time
    end_time = time.time()
    # Difference between start and end_times.
    time_dif = end_time-start_time
    # Print the time-usage
    print("Time usage:"+str(timedelta(seconds=int(round(time_dif)))))
  test_batch_size = 256
  def print_test_accuracy():
    # Number of images in the test-set.
    num_test = len(data.test.images)
    cls_pred = np.zeros(shape=num_test,dtype=np.int)
    i = 0
    while i < num_test:
      # The ending index for the next batch is denoted j.
      j = min(i+test_batch_size,num_test)
      # Get the images from the test-set between index i and j
      images = data.test.images[i:j, :]
      # Get the associated labels
      labels = data.test.labels[i:j, :]
      # Create a feed-dict with these images and labels.
      feed_dict={x:images,y_true:labels,keep_prob:1.0}
      # Calculate the predicted class using Tensorflow.
      cls_pred[i:j] = sess.run(y_pred_cls,feed_dict=feed_dict)
      # Set the start-index for the next batch to the
      # end-index of the current batch
      i = j
    cls_true = data.test.cls
    correct = (cls_true==cls_pred)
    correct_sum = correct.sum()
    acc = float(correct_sum) / num_test
    # Print the accuracy
    msg = "Accuracy on Test-Set: {0:.1%} ({1}/{2})"
    print(msg.format(acc,correct_sum,num_test))
  # Performance after 10000 optimization iterations
  optimize(num_iterations=10000)
  print_test_accuracy()
  savew_hl1 = layer_conv1["weights"].eval()
  saveb_hl1 = layer_conv1["biases"].eval()
  savew_hl2 = layer_conv2["weights"].eval()
  saveb_hl2 = layer_conv2["biases"].eval()
  savew_fc1 = fc1_layer["weights"].eval()
  saveb_fc1 = fc1_layer["biases"].eval()
  savew_op = fc2_layer["weights"].eval()
  saveb_op = fc2_layer["biases"].eval()

  np.save("savew_hl1.npy", savew_hl1)
  np.save("saveb_hl1.npy", saveb_hl1)
  np.save("savew_hl2.npy", savew_hl2)
  np.save("saveb_hl2.npy", saveb_hl2)
  np.save("savew_hl3.npy", savew_fc1)
  np.save("saveb_hl3.npy", saveb_fc1)
  np.save("savew_op.npy", savew_op)
  np.save("saveb_op.npy", saveb_op)

运行结果显示:测试集中准确率大概为99.2%。

我还写了一些辅助函数,可以查看部分识别错误的图片,

Python(TensorFlow框架)实现手写数字识别系统的方法

还可以查看混淆矩阵,

Python(TensorFlow框架)实现手写数字识别系统的方法

2.3 实现手写识别系统

最后,将训练好的参数保存,封装进一个GUI界面中,形成一个手写识别系统。

Python(TensorFlow框架)实现手写数字识别系统的方法

系统中还添加了一点图像预处理的操作,比如灰度化,图像信息的归一化等,更贴近实际应用。
系统可进行快速识别,如下图

Python(TensorFlow框架)实现手写数字识别系统的方法

3 总结

本文实现的系统其实是基于卷积神经网络的手写数字识别系统。该系统能快速实现手写数字识别,成功识别率高。缺点:只能正确识别单个数字,图像预处理还不够,没有进行图像分割,读者也可以自行添加,进行完善。

4 收获

本人之前的本科期间,虽然努力学习高数、线性代数和概率论,但是没有认真学习过机器学习,本人是2017年才开始系统学习机器学习相关知识,而且本科毕业论文也选择了相关的课题,虽然比较基础,但是认真完成后,有一种学以致用的满足感,同时也激励着我进行更深入的理论学习和实践探讨,与所有读者共勉。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
在Python中使用HTML模版的教程
Apr 29 Python
python中pygame针对游戏窗口的显示方法实例分析(附源码)
Nov 11 Python
PyCharm使用教程之搭建Python开发环境
Jun 07 Python
使用numba对Python运算加速的方法
Oct 15 Python
CentOS7安装Python3的教程详解
Apr 10 Python
pyqt 实现为长内容添加滑轮 scrollArea
Jun 19 Python
Python操作excel的方法总结(xlrd、xlwt、openpyxl)
Sep 02 Python
基于Python3.7.1无法导入Numpy的解决方式
Mar 09 Python
学会python自动收发邮件 代替你问候女友
May 20 Python
解决Python paramiko 模块远程执行ssh 命令 nohup 不生效的问题
Jul 14 Python
Python3爬虫里关于代理的设置总结
Jul 30 Python
python 制作一个gui界面的翻译工具
May 14 Python
Python实现的多进程和多线程功能示例
May 29 #Python
Python实现的redis分布式锁功能示例
May 29 #Python
Python计算一个给定时间点前一个月和后一个月第一天的方法
May 29 #Python
PyTorch CNN实战之MNIST手写数字识别示例
May 29 #Python
Python根据指定日期计算后n天,前n天是哪一天的方法
May 29 #Python
python 将md5转为16字节的方法
May 29 #Python
python 利用栈和队列模拟递归的过程
May 29 #Python
You might like
PHP开发大型项目的一点经验
2006/10/09 PHP
win7 64位系统 配置php最新版开发环境(php+Apache+mysql)
2014/08/15 PHP
PHP入门教程之操作符与控制结构流程详解
2016/09/09 PHP
select组合框option的捕捉实例代码
2008/09/30 Javascript
JavaScript 事件参考手册
2008/12/24 Javascript
如何让easyui gridview 宽度自适应窗口改变及fitColumns应用
2013/01/25 Javascript
JavaScript自定义日期格式化函数详细解析
2014/01/14 Javascript
JS基于FileSystemObject创建一个指定路径的TXT文本文件
2015/08/05 Javascript
jquery如何获取元素的滚动条高度等实现代码
2015/10/19 Javascript
JavaScript希尔排序、快速排序、归并排序算法
2016/05/08 Javascript
javascript事件模型介绍
2016/05/31 Javascript
jquery 判断selection range 是否在容器中的简单实例
2016/08/02 Javascript
javascript数组遍历的方法实例分析
2016/09/13 Javascript
用javascript获取任意颜色的更亮或更暗颜色值示例代码
2017/07/21 Javascript
微信小程序url与token设置详解
2017/09/26 Javascript
JS运动特效之完美运动框架实例分析
2018/01/24 Javascript
p5.js实现简单货车运动动画
2019/10/23 Javascript
vue框架中props的typescript用法详解
2020/02/17 Javascript
微信公众号网页分享功能开发的示例代码
2020/05/27 Javascript
解决Vue-Router升级导致的Uncaught (in promise)问题
2020/08/07 Javascript
Vue的Options用法说明
2020/08/14 Javascript
python实现桌面托盘气泡提示
2019/07/29 Python
详解使用PyInstaller将Pygame库编写的小游戏程序打包为exe文件
2019/08/23 Python
python matplotlib折线图样式实现过程
2019/11/04 Python
pytorch 获取tensor维度信息示例
2020/01/03 Python
利用setuptools打包python程序的方法步骤
2020/01/18 Python
接口自动化多层嵌套json数据处理代码实例
2020/11/20 Python
利用HTML5+css3+jquery+weui实现仿微信聊天界面功能
2018/01/08 HTML / CSS
研发工程师的岗位职责
2013/11/18 职场文书
银行实习的自我鉴定
2013/12/10 职场文书
面包店的创业计划书范文
2014/01/16 职场文书
公务员更新知识培训实施方案
2014/03/31 职场文书
教师四风对照检查材料思想汇报
2014/09/17 职场文书
2015年民主评议党员工作总结
2015/05/19 职场文书
使用Canvas绘制一个游戏人物属性图
2022/03/25 Javascript
CDPR谈《巫师》新作用虚幻5原因 称不会为Epic独占
2022/04/06 其他游戏