最近一直在学习李宏毅老师的机器学习视频教程,学到和神经网络那一块知识的时候,我觉得单纯的学习理论知识过于枯燥,就想着自己动手实现一些简单的Demo,毕竟实践是检验真理的唯一标准!!!但是网上很多的与tensorflow或者神经网络相关的Demo教程都只是在验证官方程序的过程,而如何把这些程序变成自己可以真正利用的程序这一块的资料就比较少,就好比被“玩烂的"MNIST数据集(ML界的”hello world"),网上是有很多手写数字识别的教程,但那些利用的都是官方提供的数据集,这样就算验证成功了带来的满足感还是远远不够!废话不多说,接下来就让我来介绍一下如何使用Tensorflow和MNIST识别自己写的数字(比如下图这个我写的数字5~~)

本文也参考了某些大神博客的内容。希望能帮助和我一样刚刚起步的同学,大家多多指教。

相应的代码和官方以及自己的数据集:https://github.com/tgpcai/digit_recognition

目录:

(1)MNIST数据集简介

(2)利用MNIST数据集训练模型

(3)自己手写数字,并用matlab进行预处理

(4)将图片输入网络进行识别

(5)实践过程遇到的坑与总结


(1)MNIST数据集简介

既然我们要构建自己的数据集,那我们就必须要了解官方提供的数据集的格式,大小等一些特征。MNIST是一个巨大的手写数字数据集,被广泛应用于机器学习识别领域。MNIST有60000张训练集数据和10000张测试集数据,每一个训练元素都是28*28像素的手写数字图片,而且都是黑白色构成(这里的黑色是一个0-1的浮点数,黑色越深表示数值越靠近1)。在网上搜索一下MNIST,你可以发现图片长这样:

上图就是4张MNIST图片。这些图片并不是传统意义上的png或者jpg格式的图片,因为png或者jpg的图片格式,会带有很多干扰信息,所以我们在创建自己的数据集的时候就必须进行预处理。

 划重点:28*28像素,灰度图

(2)利用MNIST数据集训练模型,并保存模型

该Demo使用的模型主要是CNN卷积神经网络,该模型广泛应用于图片识别、自然语言处理等方向。有关CNN卷积神经网络的知识在我的其他博客中有详细介绍,欢迎大家一起交流!上代码:

 import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data #定义初始化权重的函数
def weight_variavles(shape):
w = tf.Variable(tf.truncated_normal(shape, stddev=0.1))
return w #定义一个初始化偏置的函数
def bias_variavles(shape):
b = tf.Variable(tf.constant(0.1, shape=shape))
return b def model(): #1.建立数据的占位符 x [None, 784] y_true [None, 10]
with tf.variable_scope("date"):
x = tf.placeholder(tf.float32, [None, 784]) y_true = tf.placeholder(tf.float32, [None, 10]) #2.卷积层1 卷积:5*5*1,32个filter,strides= 1-激活-池化
with tf.variable_scope("conv1"):
#随机初始化权重
w_conv1 = weight_variavles([5, 5, 1, 32])
b_conv1 = bias_variavles([32]) #对x进行形状的改变[None, 784] ----- [None,28,28,1]
x_reshape = tf.reshape(x,[-1, 28, 28, 1]) #不能填None,不知道就填-1 # [None,28, 28, 1] -------- [None, 28, 28, 32]
x_relu1 = tf.nn.relu(tf.nn.conv2d(x_reshape, w_conv1, strides=[1, 1, 1, 1], padding = "SAME") + b_conv1) #池化 2*2,步长为2,【None, 28,28, 32]--------[None,14, 14, 32]
x_pool1 = tf.nn.max_pool(x_relu1, ksize=[1, 2, 2, 1],strides = [1,2,2,1],padding = "SAME") #3.卷积层2 卷积:5*5*32,64个filter,strides= 1-激活-池化
with tf.variable_scope("conv2"):
#随机初始化权重和偏置
w_conv2 = weight_variavles([5, 5, 32, 64])
b_conv2 = bias_variavles([64]) #卷积、激活、池化
#[None,14, 14, 32]----------【NOne, 14, 14, 64]
x_relu2 = tf.nn.relu(tf.nn.conv2d(x_pool1, w_conv2,strides=[1, 1, 1, 1], padding = "SAME") + b_conv2) #池化 2*2,步长为2 【None, 14,14,64]--------[None,7, 7, 64]
x_pool2 = tf.nn.max_pool(x_relu2, ksize=[1, 2, 2, 1],strides = [1,2,2,1],padding = "SAME") #4.全连接层 [None,7, 7, 64] --------- [None, 7*7*64] * [7*7*64, 10]+[10] = [none, 10]
with tf.variable_scope("fc"):
#随机初始化权重和偏置:
w_fc = weight_variavles([7 * 7 * 64, 1024])
b_fc = bias_variavles([1024]) #修改形状 [none, 7, 7, 64] ----------[None, 7*7*64]
x_fc_reshape = tf.reshape(x_pool2,[-1,7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(x_fc_reshape, w_fc) + b_fc) # 在输出之前加入dropout以减少过拟合
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) w_fc1 = weight_variavles([1024, 10])
b_fc1 = bias_variavles([10]) #进行矩阵运算得出每个样本的10个结果[NONE, 10],输出
y_predict = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc1) + b_fc1) return x, y_true, y_predict,keep_prob def conv_fc():
#获取数据,MNIST_data是楼主用来存放官方的数据集,如果你要这样表示的话,那MNIST_data这个文件夹应该和这个python文件在同一目录
mnist = input_data.read_data_sets('MNIST_data', one_hot=True) #定义模型,得出输出
x,y_true,y_predict,keep_prob = model() #进行交叉熵损失计算
#3.计算交叉熵损失
with tf.variable_scope("soft_cross"):
#求平均交叉熵损失,tf.reduce_mean对列表求平均值
loss = -tf.reduce_sum(y_true*tf.log(y_predict)) #4.梯度下降求出最小损失,注意在深度学习中,或者网络层次比较复杂的情况下,学习率通常不能太高
with tf.variable_scope("optimizer"): train_op = tf.train.AdamOptimizer(1e-4).minimize(loss) #5.计算准确率
with tf.variable_scope("acc"): equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))
#equal_list None个样本 类型为列表1为预测正确,0为预测错误[1, 0, 1, 0......] accuray = tf.reduce_mean(tf.cast(equal_list, tf.float32)) init_op = tf.global_variables_initializer() saver = tf.train.Saver() #开启会话运行
with tf.Session() as sess:
sess.run(init_op)
for i in range(3000):
mnist_x, mnist_y = mnist.train.next_batch(50)
if i%100 == 0:
# 评估模型准确度,此阶段不使用Dropout
train_accuracy = accuray.eval(feed_dict={x:mnist_x, y_true: mnist_y, keep_prob: 1.0})
print("step %d, training accuracy %g"%(i, train_accuracy)) # 训练模型,此阶段使用50%的Dropout
train_op.run(feed_dict={x:mnist_x, y_true: mnist_y, keep_prob: 0.5})
# 将模型保存在你自己想保存的位置
saver.save(sess, "D:/Dict/model/fc_model.ckpt") return None if __name__ == "__main__":
conv_fc()

然后在你保存模型的目录下会产生4个文件

.data文件是用来记录权重,偏置等参数信息;.meta是用来记录tensorflow图的结构。以下是我的电脑的结果图:

我只运行了6000次,按照tensorflow官方文档,运行9000次左右可以达到0.992左右的正确率

(3)自己手写数字,并用matlab进行预处理

首先让我们看一下预处理的结果:->->

具体过程也就是分为3个步骤:缩小它的大小为28*28像素,并转变为灰度图,最后进行二值化处理。具体matlab的代码如下:

clear all; close all; clc;
% 改图片像素为28*28 I=imread('5.jpg'); %你自己手写的数字的图片
J=imresize(I,[28,28]);
imshow(I);
figure;
imshow(J);
imwrite(J,'new5.jpp');%生成28*28手写数字图片

接下来进行灰度与二值化处理

clear all;close all;clc;
% Read an input image
A = imread('new5.jpg'); % Convert the image to single-channel grayscale image
A_gray = rgb2gray(A); figure,imhist(A_gray),title('hist of A_grey'); % Convert image to double i.e., [0,1]
A_gray = im2double(A_gray); % Generate threhold value using Otsu's algorithm
otsu_level = graythresh(A_gray); % Threshold image using Otsu's threshold and manually defined
% threshold values
B_otsu_thresh = im2bw(A_gray, otsu_level);
B_thresh_50 = im2bw(A_gray, 50/255);
B_thresh_100 = im2bw(A_gray, 100/255);
B_thresh_150 = im2bw(A_gray, 150/255);
B_thresh_200 = im2bw(A_gray, 200/255); % Display original and thresholded binary images side-by-side
figure, subplot(2, 3, 1), imshow(A_gray), title('Original image');
subplot(2, 3, 2), imshow(B_otsu_thresh), title('Binary image using Otsu threshold value');
subplot(2, 3, 3), imshow(B_thresh_50), title('Binary image using threshold value = 50');
subplot(2, 3, 4), imshow(B_thresh_100), title('Binary image using threshold value = 100');
subplot(2, 3, 5), imshow(B_thresh_150), title('Binary image using threshold value = 150');
subplot(2, 3, 6), imshow(B_thresh_200), title('Binary image using threshold value = 200');
imwrite(B_otsu_thresh,'newnew5.jpg');%填写你希望最终生成的数据集的名字和路径

到此就完成了对自己手写图片的预处理过程!

预处理的方法有很多,在这我在介绍一种利用OPENCV进行预处理:

import cv2

global img
global point1, point2
def on_mouse(event, x, y, flags, param):
global img, point1, point2
img2 = img.copy()
if event == cv2.EVENT_LBUTTONDOWN: #左键点击
point1 = (x,y)
cv2.circle(img2, point1, 10, (0,255,0), 5)
cv2.imshow('image', img2)
elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON): #按住左键拖曳
cv2.rectangle(img2, point1, (x,y), (255,0,0), 5) # 图像,矩形顶点,相对顶点,颜色,粗细
cv2.imshow('image', img2)
elif event == cv2.EVENT_LBUTTONUP: #左键释放
point2 = (x,y)
cv2.rectangle(img2, point1, point2, (0,0,255), 5)
cv2.imshow('image', img2)
min_x = min(point1[0], point2[0])
min_y = min(point1[1], point2[1])
width = abs(point1[0] - point2[0])
height = abs(point1[1] -point2[1])
cut_img = img[min_y:min_y+height, min_x:min_x+width]
resize_img = cv2.resize(cut_img, (28,28)) # 调整图像尺寸为28*28
ret, thresh_img = cv2.threshold(resize_img,127,255,cv2.THRESH_BINARY) # 二值化
cv2.imshow('result', thresh_img)
cv2.imwrite('new5.jpg', thresh_img) # 预处理后图像保存位置 def main():
global img
img = cv2.imread('5.jpg') # 手写数字图像所在位置
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换图像为单通道(灰度图)
cv2.namedWindow('image')
cv2.setMouseCallback('image', on_mouse) # 调用回调函数
cv2.imshow('image', img)
cv2.waitKey(0) if __name__ == '__main__':
main()

以上两种方法都可以,甚至还有大神利用PS自己生成数据集,感兴趣的同学可以自己去搜索一下~

(4)将图片输入网络进行识别

完成图像预处理后,即可将图片输入到网络中进行识别

 from PIL import Image, ImageFilter
import tensorflow as tf
import matplotlib.pyplot as plt def imageprepare():
im = Image.open('C:/Users/tgp/Desktop/newnew5.jpg')
plt.imshow(im)
data = list(im.getdata())
result = [(255-x)*1.0/255.0 for x in data]
return result #定义初始化权重的函数
def weight_variavles(shape):
w = tf.Variable(tf.truncated_normal(shape, stddev=0.1))
return w #定义一个初始化偏置的函数
def bias_variavles(shape):
b = tf.Variable(tf.constant(0.0, shape=shape))
return b def model():
tf.reset_default_graph()
#1.建立数据的占位符 x [None, 784] y_true [None, 10]
with tf.variable_scope("date"):
x = tf.placeholder(tf.float32, [None, 784]) #y_true = tf.placeholder(tf.float32, [None, 10]) #2.卷积层1 卷积:5*5*1,32个filter,strides= 1-激活-池化
with tf.variable_scope("conv1"):
#随机初始化权重
w_conv1 = weight_variavles([5, 5, 1, 32])
b_conv1 = bias_variavles([32]) #对x进行形状的改变[None, 784] ----- [None,28,28,1]
x_reshape = tf.reshape(x,[-1, 28, 28, 1]) #不能填None,不知道就填-1 # [None,28, 28, 1] -------- [None, 28, 28, 32]
x_relu1 = tf.nn.relu(tf.nn.conv2d(x_reshape, w_conv1, strides=[1, 1, 1, 1], padding = "SAME") + b_conv1) #池化 2*2,步长为2,【None, 28,28, 32]--------[None,14, 14, 32]
x_pool1 = tf.nn.max_pool(x_relu1, ksize=[1, 2, 2, 1],strides = [1,2,2,1],padding = "SAME") #3.卷积层2 卷积:5*5*32,64个filter,strides= 1-激活-池化
with tf.variable_scope("conv2"):
#随机初始化权重和偏置
w_conv2 = weight_variavles([5, 5, 32, 64])
b_conv2 = bias_variavles([64]) #卷积、激活、池化
#[None,14, 14, 32]----------【NOne, 14, 14, 64]
x_relu2 = tf.nn.relu(tf.nn.conv2d(x_pool1, w_conv2,strides=[1, 1, 1, 1], padding = "SAME") + b_conv2) #池化 2*2,步长为2 【None, 14,14,64]--------[None,7, 7, 64]
x_pool2 = tf.nn.max_pool(x_relu2, ksize=[1, 2, 2, 1],strides = [1,2,2,1],padding = "SAME") #4.全连接层 [None,7, 7, 64] --------- [None, 7*7*64] * [7*7*64, 10]+[10] = [none, 10]
with tf.variable_scope("fc"):
#随机初始化权重和偏置:
w_fc = weight_variavles([7 * 7 * 64, 1024])
b_fc = bias_variavles([1024]) #修改形状 [none, 7, 7, 64] ----------[None, 7*7*64]
x_fc_reshape = tf.reshape(x_pool2,[-1,7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(x_fc_reshape, w_fc) + b_fc) keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) w_fc1 = weight_variavles([1024, 10])
b_fc1 = bias_variavles([10]) #进行矩阵运算得出每个样本的10个结果[NONE, 10]
#y_predict = tf.matmul(h_fc1_drop, w_fc1) + b_fc1
y_predict = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc1) + b_fc1)
return x, y_predict,keep_prob def conv_fc():
#获取数据
result = imageprepare() #定义模型,得出输出
x,y_predict,keep_prob = model() init_op = tf.global_variables_initializer() saver = tf.train.Saver() #开启会话运行
#tf.reset_default_graph()
with tf.Session() as sess:
sess.run(init_op)
print(result)
saver.restore(sess, "D:/Dict/model/fc_model.ckpt")
prediction = tf.argmax(y_predict,1)
predint = prediction.eval(feed_dict={x: [result],keep_prob: 1.0}, session=sess)
print(predint)
print("recognize result: %d" %predint[0]) return None if __name__ == "__main__":
conv_fc()

运行结果如下:

(5)实践过程遇到的坑与总结

  • 刚开始写训练模型的代码的时候,我认为不需要防止过拟合这个处理过程,所以在我的模型里面没有防止过拟合这一操作,直接导致的结果是:在训练模型的时候效果非常不错,但是当真正拿自己手写数字去识别的时候,经常把‘4’和‘9’搞错。随便我在输出层和全连接层中间添加了一些代码用于防止过拟合,这样训练出的模型表现结果尚佳!由此可见,在训练模型的时候防止过拟合的操作还是非常有必要的。
  • 有关随机初始化权重和偏置的函数的选择:利用tf.truncated_normal()这个函数随机初始化权重训练出的模型的表现效果比利用tf.random_nomal()这个函数训练出的模型表现的更好,上网查询了一下,发现这两个函数有一下的区别:tf.truncated_normal的输出如字面意思是截断的,而截断的标准是2倍的stddev。使用tf.truncated_normal的输出是不可能出现[-2,2]以外的点的,而如果shape够大的话,tf.random_normal却会产生2.2或者2.4之类的输出。也就是说使用tf.random_normal产生的初始权重的值比tf.truncated_normal产生的大,这对于神经网络而言是致命的,因为这样非常容易产生梯度消失的问题。
  • 在随机初始化权重和偏置的时候,方差不能设置的过大,若方差过大,则在训练的时候准确率一直维持在很低的位置,容易产生梯度消失的问题。

  • 保存模型尽量以.ckpt结果,反正楼主一开始没有以.ckpt结尾,带来了很多麻烦,然后加上这个后缀,啥问题都消失了~(可能是玄学,不加可能也行的通,但是加了一定不会错~~)

以上就是本次实践的全部过程,欢迎大家交流讨论。

参考:https://www.cnblogs.com/lizheng114/p/7498328.html

TensorFlow下利用MNIST训练模型并识别自己手写的数字的更多相关文章

  1. 利用mnist训练集生成的caffemodel对mnist测试集与自己手写的数字进行测试

    从一到二:利用mnist训练集生成的caffemodel对mnist测试集与自己手写的数字进行测试 通过从零到一的教程,我们已经得到了通过mnist训练集生成的caffemodel,主要包含下面四个文 ...

  2. TensorFlow下利用MNIST训练模型识别手写数字

    本文将参考TensorFlow中文社区官方文档使用mnist数据集训练一个多层卷积神经网络(LeNet5网络),并利用所训练的模型识别自己手写数字. 训练MNIST数据集,并保存训练模型 # Pyth ...

  3. 从一到二:利用mnist训练集生成的caffemodel对mnist测试集与自己手写的数字进行测试

    通过从零到一的教程,我们已经得到了通过mnist训练集生成的caffemodel,主要包含下面四个文件: 接下来就可以利用模型进行测试了.关于测试方法按照上篇教程还是选择bat文件,当然python. ...

  4. 使用Tensorflow和MNIST识别自己手写的数字

    #!/usr/bin/env python3 from tensorflow.examples.tutorials.mnist import input_data mnist = input_data ...

  5. 利用神经网络算法的C#手写数字识别(一)

    利用神经网络算法的C#手写数字识别 转发来自云加社区,用于学习机器学习与神经网络 欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 下载Demo - 2.77 MB (原始地址):handwri ...

  6. 利用c++编写bp神经网络实现手写数字识别详解

    利用c++编写bp神经网络实现手写数字识别 写在前面 从大一入学开始,本菜菜就一直想学习一下神经网络算法,但由于时间和资源所限,一直未展开比较透彻的学习.大二下人工智能课的修习,给了我一个学习的契机. ...

  7. 利用神经网络算法的C#手写数字识别(二)

    利用神经网络算法的C#手写数字识别(二)   本篇主要内容: 让项目编译通过,并能打开图片进行识别.   1. 从上一篇<利用神经网络算法的C#手写数字识别>中的源码地址下载源码与资源, ...

  8. 利用TensorFlow识别手写的数字---基于Softmax回归

    1 MNIST数据集 MNIST数据集主要由一些手写数字的图片和相应的标签组成,图片一共有10类,分别对应从0-9,共10个阿拉伯数字.原始的MNIST数据库一共包含下面4个文件,见下表. 训练图像一 ...

  9. 利用TensorFlow识别手写的数字---基于两层卷积网络

    1 为什么使用卷积神经网络 Softmax回归是一个比较简单的模型,预测的准确率在91%左右,而使用卷积神经网络将预测的准确率提高到99%. 2 卷积网络的流程 3 代码展示 # -*- coding ...

随机推荐

  1. js new一个函数和直接调用函数的区别

    用new和调用一个函数的区别:如果函数返回值是一个值类型(Number.String.Boolen)时,new函数将会返回这个函数的实例对象,而如果这个函数的返回值是一个引用类型(Object.Arr ...

  2. JS实现的ajax和同源策略

    一.回顾jQuery实现的ajax 首先说一下ajax的优缺点 优点: AJAX使用Javascript技术向服务器发送异步请求: AJAX无须刷新整个页面: 因为服务器响应内容不再是整个页面,而是页 ...

  3. 动态获取后台传过来的值作为select选项

    问题描述:点击左侧菜单项,进入对应的具体页面a.html,页面上方有个select框,点击框后,会浮现选择项. 解决思路:对左侧菜单项添加一个onclick事件,进入后台做具体的查询,将查询到的lis ...

  4. ERROR 1044 (42000): Access denied for user 'root'@'%' to database 'mysql'

    原因:修改数据库账号时删除了默认的localhost root,  新建了% root 但没有赋予全部权限; 解决方法: 1.关闭数据库# mysqld stop 2.在my.cnf里加入skip-g ...

  5. cf1073c 二分+二维前缀和

    #include<bits/stdc++.h> using namespace std; #define maxn 200005 char s[maxn]; ][maxn]; map< ...

  6. python is和==的区别

    # ==和is # ==用来判断值是否相等# is是用看来判断是不是指定了同一个东西,判断是不是指向了同一个地址等 a = [11,22,33]b = [11,22,33] a == b # True ...

  7. python--使用递归的方式建立二叉树

    树和图的数据结构,就很有意思啦. # coding = utf-8 class BinaryTree: def __init__(self, root_obj): self.key = root_ob ...

  8. JavaScript学习:取数组中最大值和最小值

    在实际业务中有的时候要取出数组中的最大值或最小值.但在数组中并没有提供arr.max()和arr.min()这样的方法.那么是不是可以通过别的方式实现类似这样的方法呢?那么今天我们就来整理取出数组中最 ...

  9. SqlBulkCopy批量插入数据神器

    1.简单例子 class Program { static void Main(string[] args) { Stopwatch sw = new Stopwatch(); DataTable d ...

  10. CCF-CSP 第三题字符串整理(模拟大法好)

    URL映射 规则的相邻两项之间用‘/’分开,所以我们先把所有项分开,然后依次把两个字符串的对应项匹配即可. 分离字符串这里用字符串流(stringstream)处理,先把所有的‘/’变为空格,然后一个 ...