TensorFlow实操之--MNIST手写体数字图像识别

问题描述

MNIST是一个手写体数字识别数据集,它是由图片组成,每一个图片上写的是0~9中的一个数字,我们的问题就是根据图片上的数字对图片进行10分类,也就是识别出这些图片上写的数字是几。

解决思路

MNIST数据集有三个部分组成,训练数据,测试数据,验证数据。一般而言,训练集用于训练模型参数,测试集用于估计模型对样本的泛化误差,验证集用于“训练”模型的超参数。

具体地,训练集是用来训练参数的,一般是用来梯度下降的。而验证集基本是在每个epoch完成后,用来测试一下当前模型的准确率。事实上,对于一个模型来说,其参数可以分为普通参数和超参数。在不引入强化学习的前提下,那么普通参数就是可以被梯度下降所更新的,也就是训练集所更新的参数。另外,还有超参数的概念,比如网络层数、网络节点数、迭代次数、学习率等等,这些参数不在梯度下降的更新范围内。多数情况下我们还是自己人工根据验证集来调。

经过训练集和验证集训练完模型之后,就可以用测试集来查看模型的好坏了,即泛化能力如何。
一般滴,识别思路是

  1. 获取数据,包括训练数据和测试数据。
  2. 模型建立
  3. 训练
  4. 模型验证

下面,我们一步步进行代码实操。

单层神经网络的TensorFlow代码实操

数据获取

MNIST数据很容易在官网上获取。代码如下:

import warnings
warnings.filterwarnings('ignore')
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist_data=input_data.read_data_sets('mnist_data/',one_hot=True)

我们看一下数据的大小

print('train data:',mnist_data.train.images.shape,mnist_data.train.labels.shape)
print('validation data:',mnist_data.validation.images.shape,mnist_data.validation.labels.shape)
print('test data:',mnist_data.test.images.shape,mnist_data.test.labels.shape)
train data: (55000, 784) (55000, 10)
validation data: (5000, 784) (5000, 10)
test data: (10000, 784) (10000, 10)

从结果得知,数据集被分成三个部分:55000行的训练数据集(mnist_data.train),5000个验证数据和10000行的测试数据集(mnist_data.test)。每一张图片均包含了28像素x28像素,用数组表示图像为长度为784的张量。
通过代码调试,我们可以看到mnist_data的具体内容。如下图所示:
在这里插入图片描述

模型建立

假设我们的网络只有输入层和输出层,那么针对输入数据x,输出y_可以通过 y − = f ( x ∗ w + b ) y_{-} = f(x*w+b) y=f(xw+b)来表示,其中函数 f f f 是激活函数。我们知道x的shape是(,784),y的shape是(,10), 那么w的shape和b的shape应该是(784,10)和(1,10)。

1. 首先我们来定义必要的参数。

batch_size=100     #每一轮数据量大小
learning_rate=0.01   #初始学习率
max_steps=1000  #最大训练步数
x=tf.placeholder('float',[None,784])   #训练数据集的输入,有784维个特征
y_ = tf.placeholder('float', [None, 10]) #定义一个新的占位符用于输入训练数据的标签,10分类
w=tf.Variable(tf.truncated_normal([784,10],stddev=0.1),name='weight')  #权重
b=tf.Variable(tf.truncated_normal([1,10],stddev=0.1),name='biases')     #偏值

由上面代码可以看到,用placeholder来对训练数据的数据和标签进行占位,并设计权重和偏值的维数。

2. 神经网络输出函数表示

我们采用单层神经网络,并用softmax来作为激活函数。则训练数据进入神经网络后的输出为

y=tf.nn.softmax(tf.matmul(x,w) + b)              #训练数据经过softmax计算之后的结果。

以上的参数设置实现的网络模型如下所示:
在这里插入图片描述

3. 定义损失函数,求得最佳参数配置
我们这里采用交叉熵函数来定义损失函数,目标是通过梯度下降来最小化该函数。

cross_entropy=-tf.reduce_sum(y_*tf.log(y)) #交叉熵函数来定义损失函数
train_step=tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy) #梯度下降来最小化该函数

4. 设置验证模型
对于输出结果,我们需要验证一下效果如何。其方法是通过测试集来测试准确率如何。我们把测试集的数据输入模型后产生的结果与其标签做对比,即可获得准确率

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.

模型训练

模型建立好之后,下一步是给模型喂数据,然后训练了。首先初始化所有参数,然后创建session,然后训练。

init=tf.initialize_all_variables()
with tf.Session() as sess:
    sess.run(init)
    for i in range(max_steps):  # 每batch_size数据作为一个批次进行训练
        batch_x,batch_y=mnist_data.train.next_batch(batch_size)
        sess.run(train_step,feed_dict={x:batch_x, y_:batch_y})

模型验证

我们用测试集对模型进行测试,即执行验证模型。每100不迭代查看一次结果。

if i%100==0:
   print(' accuracy is ',sess.run(accuracy, feed_dict=x:mnist_data.test.images,y_:mnist_data.test.labels}))  # 评估模型准确率

完整的代码如下

import warnings
warnings.filterwarnings('ignore')
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist_data=input_data.read_data_sets('mnist_data/',one_hot=True) #获取mnist数据

#设置超参数
batch_size=100     #每一轮数据量大小
learning_rate=0.01   #初始学习率
max_steps=1000  #最大训练步数

x=tf.placeholder('float',[None,784])   #训练数据集的输入,有784维个特征
y_ = tf.placeholder('float', [None, 10]) #定义一个新的占位符用于输入训练数据的标签,10分类
w=tf.Variable(tf.truncated_normal([784,10],stddev=0.1),name='weight')  #权重
b=tf.Variable(tf.truncated_normal([1,10],stddev=0.1),name='biases')     #偏值


y=tf.nn.softmax(tf.matmul(x,w) + b)                                   #训练数据经过计算之后的结果。

cross_entropy=-tf.reduce_sum(y_*tf.log(y))

train_step=tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy)

#评估模型
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.
init=tf.initialize_all_variables()
with tf.Session() as sess:
    sess.run(init)
  
    for i in range(max_steps):
        batch_x,batch_y=mnist_data.train.next_batch(batch_size)
        sess.run(train_step,feed_dict={x:batch_x, y_:batch_y})

        if i%100==0:
            print(' accuracy is ',sess.run(accuracy, feed_dict={x:mnist_data.test.images,y_:mnist_data.test.labels}))  # 评估模型准确率
 accuracy is  0.9224

从输出结果可以看到,准确率大概在92%左右,其实并不算高。另外的一个问题是,我们没有利用数据集里的验证数据集,在数据珍贵的时代,这太浪费了。同时我们的神经网络没有隐藏层。下面我们把验证集加入到训练中,同时设置多隐藏层,看看能不能提升准确率。

多隐藏层神经网络手写数字识别

同样的,多隐藏层神经网络的手写数字识别思路也是如下四步,只不过是在每一步里有所不同。 1. 获取数据,包括训练数据和测试数据。 2. 模型建立 3. 训练 4. 模型验证
通过设置一个隐藏层,在训练中更新参数,并通过正则化,我们得到较好的训练效果,准确率提升到97%。具体代码如下:

import warnings
warnings.filterwarnings('ignore')

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist_data=input_data.read_data_sets('mnist_data/',one_hot=True) #获取mnist数据

#设置超参数
batch_size=100     #每一轮数据量大小
learning_rate=0.8   #初始学习率
learning_rate_decay=0.999 #学习率的衰减
max_steps=1000  #最大训练步数
training_step=tf.Variable(0,trainable=False)

#定义隐藏层函数
def hidden_layer(input_tensor,weights1,biases1,weights2,biases2,layer_name):
    layer1=tf.nn.relu(tf.matmul(input_tensor,weights1)+biases1)  #第一层隐藏层采用了relu激活函数
    return tf.matmul(layer1,weights2)+biases2                    #第二层没有采用激活函数,即输出层

x=tf.placeholder(tf.float32,[None,784])   #训练数据集的输入,有784维个特征
y_=tf.placeholder(tf.float32,[None,10])  #训练数据的标签,10分类

weights1=tf.Variable(tf.truncated_normal([784,500],stddev=0.1),name='weight1')  #权重1
biases1=tf.Variable(tf.constant(0.1,shape=[500]) )    #偏值1
weights2=tf.Variable(tf.truncated_normal([500,10],stddev=0.1),name='weight2')  #权重2
biases2=tf.Variable(tf.constant(0.1,shape=[10]),name='biases2')     #偏值2

y=hidden_layer(x,weights1,biases1,weights2,biases2,'y')       #训练数据经过计算之后的结果。

#参数更新
averages_class=tf.train.ExponentialMovingAverage(0.99,training_step)
averages_op=averages_class.apply(tf.trainable_variables())

average_y=hidden_layer(x,averages_class.average(weights1),
                        averages_class.average(biases1),
                        averages_class.average(weights2),
                        averages_class.average(biases2),
                        'average_y')
#交叉熵
cross_entropy=tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,labels=tf.argmax(y_,1))
#正则化
regular=tf.contrib.layers.l2_regularizer(0.0001)
regulation=regular(weights1)+regular(weights2)
#总的损失函数
loss=tf.reduce_mean(cross_entropy)+regulation

#学习率更新
learning_rate=tf.train.exponential_decay(learning_rate,training_step,mnist_data.train.num_examples/batch_size,learning_rate_decay)
train_step=tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=training_step)

# train_op=tf.group(train_step)
with tf.control_dependencies([train_step,averages_op]):
    train_op=tf.no_op(name='train')

#评估模型
init=tf.global_variables_initializer()
correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))  # 这行代码的目的是对比预测值y与标签y_是否匹配
accuracy = tf.reduce_mean(tf.cast(correct_prediction,  tf.float32))
# 这行代码会给我们一组布尔值。为了确定正确预测项的比例,我们可以把布尔值转换成浮点数,然后取平均值。例如,[True, False, True, True] 会变成 [1,0,1,1] ,取平均值后得到 0.75.

with tf.Session() as sess:
    sess.run(init)
    validate_feed={x:mnist_data.validation.images,y_:mnist_data.validation.labels}
    test_feed={x:mnist_data.test.images,y_:mnist_data.test.labels}

    for i in range(max_steps):
        if i % 100 == 0:
            validate_accuracy=sess.run(accuracy, feed_dict=validate_feed)
            print('validate_accuracy',validate_accuracy)
        batch_x,batch_y=mnist_data.train.next_batch(batch_size)
        sess.run(train_op,feed_dict={x:batch_x, y_:batch_y})

    print(' accuracy is ',sess.run(accuracy, feed_dict=test_feed))  # 评估模型准确率

输出结果如下:

validate_accuracy 0.9634
validate_accuracy 0.9682
validate_accuracy 0.9702
validate_accuracy 0.972
validate_accuracy 0.9736
validate_accuracy 0.9762
validate_accuracy 0.9776
 accuracy is  0.9753

CNN 卷积神经网络的手写数字识别

我们的识别对象是图片,而CNN卷积神经网络是进行图像识别的一个非常好的算法。现在,我们尝试用CNN来对手写数字识别。CNN的核心结构如图所示:
在这里插入图片描述
在本示例中,根据CNN,在网络结构中,有卷积层和池化层,这里我们采用两层卷积层和池化层,卷积层采用5×5的滤波器对全0补齐的原数据进行卷积,卷积深度,即卷积核的个数初始为32,而池化层采用2×2的的最大池化。自己画的图太丑,先用文字来说明把,等我哪天会画漂亮图了在补上。
我们的数字图像数据是28×28维度的,用5*5大小的卷积核来卷积,在不使用全0填充时,卷积后的大小为28-5+1=24维。在使用全0填充时,卷积后的大小与原来图像矩阵一样大,即28×28.。采用2×2池化后,图像矩阵大小变为14×14。在进行一轮卷积池化,图像大小变为7×7.
模型结构为两层卷积层+池化层,一个全连接层,一个输出层。具体代码如下:

import warnings
warnings.filterwarnings('ignore')

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist_data=input_data.read_data_sets('mnist_data/',one_hot=True) #获取mnist数据

#卷积和池化处理函数
def conv2d(x, W): #定义卷积层
    return tf.nn.conv2d(x, W, strides = [1, 1, 1, 1], padding = 'SAME') #步长设为1,边距填充为0
#x:输入数据,4维变量,[a,b,c,d],a为数据index,b*c为图片大小,d为图像深度,灰色为1,RGB为3
#W:卷积核,4维变量,[a,b,c,d],a*b为卷积核大小,d为输入矩阵深度,接输入数据;d为通道数,本层过滤器的深度
#strides 为不同维度上过滤器移动的步长,[a,b,c,d]。a和d要求为1,只对长宽有效。b为长方向移动步长,c为宽方向步长
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = 'SAME')

x=tf.placeholder(tf.float32,[None,784])   #训练数据集的输入,有784维个特征
y_=tf.placeholder(tf.float32,[None,10])  #训练数据的标签,10分类
x_image = tf.reshape(x, [-1,28,28,1])#x的维度应该和W对应,其中第2、3维对应图片的宽和高,最后 颜色通道数
#第一层卷积
#第一层的结构包括一个卷积层加一个最大池化层。
W_conv1 = tf.Variable(tf.truncated_normal([5, 5, 1, 32],stddev=0.1),name='W_conv1') # 前两个维度代表patch大小,1代表通道数目,32是输出的通道数目
b_conv1 = tf.Variable(tf.truncated_normal([32],stddev=0.1),name='b_conv1')#对应上面每一个输出的通道

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)#把x和全职进行卷积,再加上偏置项,应用RELU激活函数防止线性化
h_pool1 = max_pool_2x2(h_conv1)#添加池化层

#第二层卷积层
W_conv2 = tf.Variable(tf.truncated_normal([5, 5, 32, 64],stddev=0.1),name='W_conv2')
b_conv2 = tf.Variable(tf.truncated_normal([64],stddev=0.1),name='b_conv2')
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

#密集连接层
W_fc1 = tf.Variable(tf.truncated_normal([7 * 7 * 64, 1024],stddev=0.1),name='W_fc1')#图片尺寸由28减少到了7,原因是经历了两次2x2的最大池化
b_fc1 = tf.Variable(tf.truncated_normal([1024],stddev=0.1),name='b_fc1')
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 = tf.Variable(tf.truncated_normal([1024, 10],stddev=0.1),name='W_fc2')
b_fc2 = tf.Variable(tf.truncated_normal([10],stddev=0.1),name='b_fc2')
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

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"))
init=tf.initialize_all_variables()
#训练和评估模型
sess = tf.InteractiveSession()
sess.run(init)
for i in range(2000):
    batch = mnist_data.train.next_batch(100)
    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_data.test.images, y_: mnist_data.test.labels, keep_prob: 1.0}))

通过2000次的训练,可以看到测试数据准确率提升到98%。

step 0, training accuracy 0.04
step 100, training accuracy 0.89
step 200, training accuracy 0.91
step 300, training accuracy 0.86
step 400, training accuracy 0.95
step 500, training accuracy 0.94
step 600, training accuracy 1
step 700, training accuracy 0.95
step 800, training accuracy 0.94
step 900, training accuracy 0.98
step 1000, training accuracy 0.96
step 1100, training accuracy 0.98
step 1200, training accuracy 0.98
step 1300, training accuracy 0.98
step 1400, training accuracy 1
step 1500, training accuracy 0.99
step 1600, training accuracy 0.96
step 1700, training accuracy 0.99
step 1800, training accuracy 0.97
step 1900, training accuracy 0.99
test accuracy 0.9812

总结

本文针对手写数字图像识别问题,采用了全连接、多隐藏层神经网络、CNN卷积神经网络等方法进行识别,准确率也逐步提升。通过该例子,我们熟悉了深度学习问题处理的一般思路 1. 获取数据,包括训练数据和测试数据。2. 模型建立 3. 训练 4. 模型验证,并熟悉TensorFlow的基本用法。同时,在练习中,我们也发现了很多值得去探讨的问题。包括激活参数的选择,超参数的调整,优化目标损失函数的选择,模型网络的结构搭建,泛化的避免等等,这些都是深度学习的基本并且核心的问题,未来将逐一探讨。

利用tensorflow实现的卷积神经网络来进行MNIST手写数字图像的分类。 #导入numpy模块 import numpy as np #导入tensorflow模块,程序使用tensorflow来实现卷积神经网络 import tensorflow as tf #下载mnist数据集,并从mnist_data目录中读取数据 from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('mnist_data',one_hot=True) #(1)这里的“mnist_data” 是和当前文件相同目录下的一个文件夹。自己先手工建立这个文件夹,然后从https://yann.lecun.com/exdb/mnist/ 下载所需的4个文件(即该网址中第三段“Four files are available on this site:”后面的四个文件),并放到目录MNIST_data下即可。 #(2)MNIST数据集是手写数字字符的数据集。每个样本都是一张28*28像素的灰度手写数字图片。 #(3)one_hot表示独热编码,其值被设为true。在分类问题的数据集标注时,如何不采用独热编码的方式, 类别通常就是一个符号而已,比如说是9。但如果采用独热编码的方式,则每个类表示为一个列表list,共计有10个数值,但只有一个为1,其余均为0。例如,“9”的独热编码可以为[00000 00001]. #定义输入数据x和输出y的形状。函数tf.placeholder的目的是定义输入,可以理解为采用占位符进行占位。 #None这个位置的参数在这里被用于表示样本的个数,而由于样本个数此时具体是多少还无法确定,所以这设为None。而每个输入样本的特征数目是确定的,即为28*28。 input_x = tf.placeholder(tf.float32,[None,28*28])/255 #因为每个像素的取值范围是 0~255 output_y = tf.placeholder(tf.int32,[None,10]) #10表示10个类别 #输入层的输入数据input_x被reshape成四维数据,其中第一维的数据代表了图片数量 input_x_images = tf.reshape(input_x,[-1,28,28,1]) test_x = mnist.test.images[:3000] #读取测试集图片的特征,读取3000个图片 test_y = mnist.test.labels[:3000] #读取测试集图片的标签。就是这3000个图片所对应的标签
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值