【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)

目录

  • 一、MNIST数据集的特点
  • 二、LeNet模型原理
    • (一)卷积神经网络
    • (二)LeNet-5模型
  • 三、实验结果
    • (一)训练测试结果:
    • (二)匹配结果:
  • 四、相关代码


一、MNIST数据集的特点

部分数据:
【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第1张图片
MNIST数据集是一个手写体数据集,数据集中每一个样本都是一个0-9的手写数字。该数据集由四部分组成,训练图片集,训练标签集,测试图片集和测试标签集。其中,训练集中有60000个样本,测试集中有10000个样本。每张照片均为28*28的二值图片,为方便存储,官方已对图片集进行处理,将每一张图片变成了维度为(1,784)的向量。

MNIST数据集特点有:
1、每张照片均为28*28的二值图片
2、数字的笔画无间断
3、数字在图像中倾斜的角度不超过45°
4、所有数字在图像中所占的比例相同


二、LeNet模型原理

(一)卷积神经网络

卷积神经网络是一种特殊的多层神经网络,像其它的神经网络一样,卷积神经网络也使用一种反向传播算法来进行训练,不同之处在于网络的结构。卷积神经网络的网络连接具有局部连接、参数共享的特点。局部连接是相对于普通神经网络的全连接而言的,是指这一层的某个节点只与上一层的部分节点相连。参数共享是指一层中多个节点的连接共享相同的一组参数。

对于一个卷积神经网络,假如该网络的第k层有n个节点,k+1层为卷积层且有m个节点,则k+1层的每个节点只与k层的部分节点相连,此处假设只与k层的i个节点相连(局部连接);另外k+1层的每个节点的连接共享相同的参数、相同的bias(参数共享)。这样该卷积神经网络的第k、k+1层间共有m*i个连接、i+1个参数。由于i小于n且为常数,所以卷积层的连接数、参数数量的数量级约为O(n),远小于全连接的O(n2)的数量级。卷积神经网络的部分连接的结构如下图:
【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第2张图片
【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第3张图片

(二)LeNet-5模型

LeNet-5是Yann LeCun在1998年设计的用于手写数字识别的卷积神经网络,是早期卷积神经网络中最有代表性的实验系统之一。

它的网路结构如下图:
【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第4张图片

图一 LeNet-5的网络结构

LeNet-5共有7层(不包含输入),每层都包含可训练参数。

首先,输入图像大小为32×32的灰度图,比MNIST数据集的图片要大一些,此在训练整个网络之前,需要对28×28的图像加上paddings(即周围填充0)。

在LeNet-5的网络结构中,C1层是一个卷积层。该层使用6个大小为5×5的卷积核对输入层进行卷积运算,产生6个大小为28×28的特征图。卷积层的作用是对输入图像提取6个特征。C1层的连接结构如图二。
【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第5张图片

图二 C1层的连接结构

S2层是一个池化层(也称为下采样层)。这里采用max_pool(最大池化),池化的size定为2×2。对C1层的每一个特征图的长宽尺寸降到原来的1/2,得到6个14×14的特征通道,且数量不变。池化层作用是降低网络训练参数及模型的过拟合程度。S2层的网络连接结构如图三:
【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第6张图片

图三 S2层的网络连接结构

C3层仍为一个卷积层,选用大小为5×5的16种不同的卷积核。C3当中的每个特征图,都是S2层中的所有或其中几个特征图进行加权组合得到的。输出为16个10措10的特征图。该层的作用是提取深层特征。

S4层仍为一个池化层,size为2×2,仍采用max_pool。最后输出16个5×5的特征图,神经元个数也减少至16×5×5=400个。

C5层继续用5*5的卷积核对S4层的输出进行卷积,卷积核数量增加至120。这样C5层的输出图片大小为5-5+1=1。最终输出120个1×1的特征图。这里实际上是与S4全连接了,但仍将其标为卷积层,原因是如果LeNet-5的输入图片尺寸变大,其他保持不变,那该层特征图的维数也会大于1×1。C5层的连接方式如图四。
【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第7张图片

图四 C5层的连接方式

F6层是全连接层,F有84个节点,该层输出84张特征图。84个神经元与C5中的120个神经元全连接,再加上4个偏置项。连接方式如图五。
【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第8张图片

图五 F6层连接方式

三、实验结果

(一)训练测试结果:

1、训练次数为20时:
【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第9张图片

(二)匹配结果:

  • 开始页面展示
    【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第10张图片

  • 选择图片进行识别
    【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第11张图片

  • 当把图像进行旋转后
    在这里插入图片描述
    出现错误
    【计算机视觉】LeNet方法实现手写体识别(MNIST数据集)_第12张图片
    旋转后的图片不是维度为(1,784)的向量,所以程序无法对其进行识别。


四、相关代码

1、窗口界面代码:

import tensorflow as tf
import numpy as np
import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk
from tkinter import filedialog
import time


def creat_windows():
    win = tk.Tk() # 创建窗口
    sw = win.winfo_screenwidth()
    sh = win.winfo_screenheight()
    ww, wh = 400, 450
    x, y = (sw-ww)/2, (sh-wh)/2
    win.geometry("%dx%d+%d+%d"%(ww, wh, x, y-40)) # 居中放置窗口

    win.title('手写体识别') # 窗口命名

    bg1_open = Image.open("timg.jpg").resize((300, 300))
    bg1 = ImageTk.PhotoImage(bg1_open)
    canvas = tk.Label(win, image=bg1)
    canvas.pack()


    var = tk.StringVar() # 创建变量文字
    var.set('')
    tk.Label(win, textvariable=var, bg='#C1FFC1', font=('宋体', 21), width=20, height=2).pack()

    tk.Button(win, text='选择图片', width=20, height=2, bg='#FF8C00', command=lambda:main(var, canvas), font=('圆体', 10)).pack()
    
    win.mainloop()

def main(var, canvas):
    file_path = filedialog.askopenfilename()
    bg1_open = Image.open(file_path).resize((28, 28))
    pic = np.array(bg1_open).reshape(784,)
    bg1_resize = bg1_open.resize((300, 300))
    bg1 = ImageTk.PhotoImage(bg1_resize)
    canvas.configure(image=bg1)
    canvas.image = bg1

    init = tf.global_variables_initializer()

    with tf.Session() as sess:
            sess.run(init)
            saver = tf.train.import_meta_graph('save/model.meta')  # 载入模型结构
            saver.restore(sess, 'save/model')  # 载入模型参数
            graph = tf.get_default_graph()       # 加载计算图
            x = graph.get_tensor_by_name("x-input:0")  # 从模型中读取占位符变量
            keep_prob = graph.get_tensor_by_name("keep_prob:0")
            y_conv = graph.get_tensor_by_name("y-pred:0")  # 关键的一句  从模型中读取占位符变量
            prediction = tf.argmax(y_conv, 1)
            predint = prediction.eval(feed_dict={x: [pic], keep_prob: 1.0}, session=sess)  # feed_dict输入数据给placeholder占位符
            answer = str(predint[0])
    var.set("预测的结果是:" + answer)

if __name__ == "__main__":
    creat_windows()

2、训练测试代码:

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)

batch = mnist.train.next_batch(64)
print(len(batch))
print(len(batch[0]),len(batch[1]))
print(len(batch[0][0]),len(batch[1][0]))
print(batch[1][0])

import cv2
import numpy as np
img = cv2.imread("mnist_train/1/mnist_train_3.png",0)
array = np.array(img).flatten()
print(len(array))

import os 
import cv2 
import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
sess = tf.InteractiveSession()
#iterNum = 1000
iterNum = 20
batch_size=64


def getTrain():
    train=[[],[]] # 指定训练集的格式,一维为输入数据,一维为其标签
    # 读取所有训练图像,作为训练集
    train_root="mnist_train" 
    labels = os.listdir(train_root)
    for label in labels:
        imgpaths = os.listdir(os.path.join(train_root,label))
        for imgname in imgpaths:
            img = cv2.imread(os.path.join(train_root,label,imgname),0)
            array = np.array(img).flatten() # 将二维图像平铺为一维图像
            array=MaxMinNormalization(array)
            train[0].append(array)
            label_ = [0,0,0,0,0,0,0,0,0,0]
            label_[int(label)] = 1
            train[1].append(label_)
    train = shuff(train)
    return train

def getTest():
    test=[[],[]] # 指定训练集的格式,一维为输入数据,一维为其标签
    # 读取所有训练图像,作为训练集
    test_root="mnist_test" 
    labels = os.listdir(test_root)
    for label in labels:
        imgpaths = os.listdir(os.path.join(test_root,label))
        for imgname in imgpaths:
            img = cv2.imread(os.path.join(test_root,label,imgname),0)
            array = np.array(img).flatten() # 将二维图像平铺为一维图像
            array=MaxMinNormalization(array)
            test[0].append(array)
            label_ = [0,0,0,0,0,0,0,0,0,0]
            label_[int(label)] = 1
            test[1].append(label_)
    test = shuff(test)
    return test[0],test[1]

def shuff(data):
    temp=[]
    for i in range(len(data[0])):
        temp.append([data[0][i],data[1][i]])
    import random
    random.shuffle(temp)
    data=[[],[]]
    for tt in temp:
        data[0].append(tt[0])
        data[1].append(tt[1])
    return data

count = 0
def getBatchNum(batch_size,maxNum):
    global count
    if count ==0:
        count=count+batch_size
        return 0,min(batch_size,maxNum)
    else:
        temp = count
        count=count+batch_size
        if min(count,maxNum)==maxNum:
            count=0
            return getBatchNum(batch_size,maxNum)
        return temp,min(count,maxNum)
    
def MaxMinNormalization(x):
    x = (x - np.min(x)) / (np.max(x) - np.min(x))
    return x


def weight_variable(shape):
    initial = tf.truncated_normal(shape,stddev=0.1)#正太分布的标准差设为0.1
    return tf.Variable(initial)
def bias_variable(shape):
    initial = tf.constant(0.1,shape=shape)
    return tf.Variable(initial)


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')



train=getTrain()
test0,test1=getTest()


x = tf.placeholder(tf.float32, [None,784], name="x-input")
y_ = tf.placeholder(tf.float32,[None,10]) # 10列


w_conv1 = weight_variable([5,5,1,32])
b_conv1 = bias_variable([32]) # 每个输出通道都有一个对应的偏置量

x_image = tf.reshape(x,[-1,28,28,1])

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)

keep_prob = tf.placeholder(tf.float32, name="keep_prob")# placeholder是占位符
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# 8、输出层,最后添加一个softmax层
w_fc2 = weight_variable([1024,10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc2) + b_fc2, name="y-pred")


cross_entropy = tf.reduce_sum(-tf.reduce_sum(y_ * tf.log(y_conv),reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

sess.run(tf.initialize_all_variables())

# 保存最后一个模型
saver = tf.train.Saver(max_to_keep=1)


for i in range(iterNum):
    for j in range(int(len(train[1])/batch_size)):
        imagesNum=getBatchNum(batch_size,len(train[1]))
        batch = [train[0][imagesNum[0]:imagesNum[1]],train[1][imagesNum[0]:imagesNum[1]]]
        train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
    if i % 4 == 0:
        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))
print("test accuracy %f " % accuracy.eval(feed_dict={x: test0, y_:test1, keep_prob: 1.0})) 
# 保存模型于文件夹
saver.save(sess,"save/model")

你可能感兴趣的:(计算机视觉)