深度学习入坑笔记之二---手写体图像识别问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/LEEANG121/article/details/101841239

目录

前言

写在前面的话:

1本文是根据tensorflow中文社区的相关内容进行整理及适当的解释重新梳理而成,对于初学者更加友好,本文仅关注达成目标的方法,对于实现方法的具体原因本文不做深究,会在其他博客里面另行解释。

2本文数据来源为http://yann.lecun.com/exdb/mnist/数据集

3写这篇博文的日期是2019年10月1日,祝愿祖国繁荣。

MNIST 手写体图像识别是机器学习领域最基础也是最经典的案例,相当于语言学习中的‘HELLO WORLD’。他包含了各种手写体数字图片:
在这里插入图片描述
该数据集包含了0-9总共10个类别的数字图片(标签),及每个图片对应的标签(让计算机知道3是3,5是5之类)本文的上半部分主要介绍通过Softmax Regression这个简单的数学模型进行图像的预测;本文后半段则利用卷积神经网络对相同的数据集进行预测,以对比两种不同的建模思路对预测结果的影响。

通过softmax进行手写体图像建模及识别

通过softmax regresion做数学建模并识别图像的一般流程为:1,数据的输入;2,建立模型;3,训练模型;4,评估模型。

数据导入

这里数据用的是MNIST官网给出的数据,在导入数据前,我们先把tensorflow模块导入,具体代码如下:

import warnings
warnings.filterwarnings('ignore') #忽略掉运行过程中出现的警告提示
#导入相关模块
import tensorflow as tf 
from tensorflow.examples.tutorials.mnist import input_data

#下载数据集
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
print(tf.__version__)
Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
1.14.0         

接下来对下载的数据集进行检查,具体代码如下:

#检查数据集
print(mnist.train.images.shape, mnist.train.labels.shape)#打印训练数据集
print("------------------------------------------------------------------------------------------")
print(mnist.test.images.shape, mnist.test.labels.shape)#打印测试数据集
print("------------------------------------------------------------------------------------------")
print(mnist.validation.images.shape, mnist.validation.labels.shape)#打印验证数据集
(55000, 784) (55000, 10)
----------------------------------------------------------------
(10000, 784) (10000, 10)
 ----------------------------------------------------------------
(5000, 784) (5000, 10)

从结果得知,下载下来的数据集被分成两部分:60000行的训练数据集(mnist.train)和10000行的测试数据集(mnist.test)。其中训练数据又分为55000个训练数据和5000个验证数据。
每一张图片均包含了28像素x28像素,用数组表示图像为长度为784的张量,如下图所示:
在这里插入图片描述

softmax建模

导入数据之后,开始进行数学建模。第一次我们利用softmax回归建立一个简单模型具体代码如下:

#该数学模型的数学结构为y=Wx+b
x = tf.placeholder('float', [None, 784])#x不是一个特定的值,而是一个占位符placeholder,关于占位符,我们会在另外的文章中详谈,同时网上也有很多详细介绍
#上面的None表示此张量的第一个维度可以是任意长度
W = tf.Variable(tf.zeros([784, 10]))#W代表权重
b = tf.Variable(tf.zeros([10]))#b代表偏置项
y = tf.nn.softmax(tf.matmul(x,W) + b)

注意,W的维度是[784,10],因为我们想要用784维的图片向量乘以它以得到一个10维的证据值向量,每一位对应不同数字类。b的形状是[10],所以我们可以直接把它加到输出上面。

训练模型

训练模型之前,我们首先要定义一个指标,用这个指标来判断最终的模型输出结果是好是坏。在机器学习中比较常用的做法是我们定义一个损失函数(loss function)/代价函数(cost function),当这个函数的结果越小时,我们认为模型的模拟程度越好。
本示例中,我们的成本函数采用‘交叉熵’(cross-entropy),关于交叉熵的具体含义,本文不做赘述。具体的代码如下所示:

#定义损失函数,判断模型好坏
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) #选择梯度下降优化器,并将学习率设为0.01
init = tf.initialize_all_variables() #初始化变量,这句话也可以写为tf.global_variables_initializer替代
sess = tf.Session() #运行对话,开启模型
sess.run(init)
#开始训练模型,循环设为1000次
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100) #以100作为一个训练批次进行训练
sess.run(train_step, feed_dict = {x: batch_xs, y_:batch_ys}) #这里指将训练数据放进x的占位符,将标签放进y_的占位符, y是预测值,靠计算得出

我们对模型进行训练,循坏设为1000,分为10次完成。

模型评估

当我们对数据训练完毕之后,我们需要测试,我们训练好的模型是否准确,这时候就需要对模型进行一个评估。这里我们对测试数据进行评估。具体的代码如下所示:

#评估模型
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))#这行代码的目的是对比预测值y与标签y_是否匹配
accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float')) #这行代码会给我们一组布尔值。为了确定正确预测项的比例,我们可以把布尔值转换成浮点数,然后取平均值。例如,[True, False, True, True] 会变成 [1,0,1,1] ,取平均值后得到 0.75.
print (sess.run(accuracy, feed_dict = {x: mnist.test.images, y_: mnist.test.labels}))#评估模型准确率
0.9147

最终结果得到的模型精度大约为91%。接下来我们会对模型进行一个简单的优化,引入卷积神经网络,然后比较卷积神经网络得到的模型精度是多少。

通过卷积网络进行手写体图像建模及识别

卷积神经网络模型需要的权重和偏置项数目数量远大于softmax模型,同时为了避免权重出现0梯度,我们应该加入适量的噪声,防止权重出现对称情况。我们使用的是ReLU神经元,因此比较好的做法是用一个较小的正数来初始化偏置项,以避免神经元节点输出恒为0的问题(dead neurons)。具体的代码实现过程如下:

初始化权重

#重新构建一个卷积神经网络,预测同样的数据集并进行比较
# 定义权重和偏置项,该做法的具体意义我们以后另讲,这里不再赘述
def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev = 0.1)
    return tf.Variable(initial)
def bias_variable(shape):
    initial = tf.constant(0.1, shape = shape)
    return tf.Variable(initial)

定义卷积层及池化层

本示例中,我们设定卷积步长为1,边距填充为0,池化层采用最大池化,尺寸为2x2,具体代码实现如下:

#卷积和池化处理
def conv2d(x, W): #定义卷积层
    return tf.nn.conv2d(x, W, strides = [1, 1, 1, 1], padding = 'SAME') #步长设为1,边距填充为0

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

添加层

定义完毕卷积层及池化层之后,接下来就要将卷积层和池化层逐层添加进模型之中,具体代码及解释如下:

#第一层卷积
#第一层的结构包括一个卷积层加一个最大池化层。
W_conv1 = weight_variable([5, 5, 1, 32])# 前两个维度代表patch大小,1代表通道数目,32是输出的通道数目
b_conv1 = bias_variable([32])#对应上面每一个输出的通道
x_image = tf.reshape(x, [-1,28,28,1])#x的维度应该和W对应,其中第2、3维对应图片的宽和高,最后嗲表颜色通道数
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)#把x和全职进行卷积,再加上偏置项,应用RELU激活函数防止线性化
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])#图片尺寸由28减少到了7,原因是经历了两次2x2的最大池化
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)

#dropout
#这一层的目的是防止模型过拟合,过拟合的模型会影响泛化能力
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

#输出层
#卷积神经网络的最后输出层依然采取全连接的形式
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)

训练及评估模型

由于模型教之前复杂,数据量较大,我们采取了ADAM优化器进行梯度下降。

#训练和评估模型
sess = tf.InteractiveSession()#这个一定要添加,否则会话无法计算
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
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, "float"))
sess.run(tf.initialize_all_variables())
for i in range(20000):
  batch = mnist.train.next_batch(50)
    if i%100 == 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))
    train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
print("test accuracy %g"%accuracy.eval(feed_dict={
x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
step 0, training accuracy 0.1
step 100, training accuracy 0.76
step 200, training accuracy 0.92
step 300, training accuracy 0.92
step 400, training accuracy 0.9
......
step 19400, training accuracy 1
step 19500, training accuracy 1
step 19600, training accuracy 1
step 19700, training accuracy 1
step 19800, training accuracy 1
step 19900, training accuracy 1

test accuracy 0.9916

从最终的测试结果得出,使用了卷积神经网络的模型在手写体图像识别问题上的识别率可以提高到99.2%

猜你喜欢

转载自blog.csdn.net/LEEANG121/article/details/101841239
今日推荐