TensorFlow入门之二:tensorflow手写数字识别

一、基础知识

基础知识可以跳过,可以直接看后面的代码实现

MNIST数据集

MNIST数据集的官网是Yann LeCun’s website。可以使用下面的python代码自动下载数据集。

#已经下载input_data.py
#import input_data
#没有下载input_data.py
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

下载下来的数据集被分成两部分:60000行的训练数据集(mnist.train)和10000行的测试数据集(mnist.test)。这样的切分很重要,在机器学习模型设计时必须有一个单独的测试数据集不用于训练而是用来评估这个模型的性能,从而更加容易把设计的模型推广到其他数据集上(泛化)。
每一个MNIST数据单元有两部分组成:一张包含手写数字的图片和一个对应的标签。我们把这些图片设为“xs”,把这些标签设为“ys”。训练数据集和测试数据集都包含xs和ys,比如训练数据集的图片是 mnist.train.images ,训练数据集的标签是 mnist.train.labels。

每一张图片包含28像素X28像素。我们可以用一个数字数组来表示这张图片:

我们把这个数组展开成一个向量,长度是 28x28 = 784。如何展开这个数组(数字间的顺序)不重要,只要保持各个图片采用相同的方式展开。从这个角度来看,MNIST数据集的图片就是在784维向量空间里面的点, 并且拥有比较复杂的结构 (提醒: 此类数据的可视化是计算密集型的)。

展平图片的数字数组会丢失图片的二维结构信息。这显然是不理想的,最优秀的计算机视觉方法会挖掘并利用这些结构信息,我们会在后续教程中介绍。但是在这个教程中我们忽略这些结构,所介绍的简单数学模型,softmax回归(softmax regression),不会利用这些结构信息。

一般的计算机视觉会先把图像转为图像矩阵,然后再转化为一维向量,处理图片过程如下:

from PIL import Image
#打开图像
image = Image.open("smallpi.jpg")
#转化成灰度图像,这一步不是必须的,但为了效率一般都会处理
image_gray = image.convert("L") 
#Image对象转化成图像矩阵
image_array = np.array(image_gray )
#flatten可以将矩阵转化成一维序列
image_vectors = image_array.flatten()
#归一化处理到0-1范围内
image_vectors_float = image_vectors/255

因此,在MNIST训练数据集中,mnist.train.images 是一个形状为 [60000, 784] 的张量,第一个维度数字用来索引图片,第二个维度数字用来索引每张图片中的像素点。在此张量里的每一个元素,都表示某张图片里的某个像素的强度值,值介于0和1之间。

相对应的MNIST数据集的标签是介于0到9的数字,用来描述给定图片里表示的数字。为了用于这个教程,我们使标签数据是"one-hot vectors"。 一个one-hot向量除了某一位的数字是1以外其余各维度数字都是0。所以在此教程中,数字n将表示成一个只有在第n维度(从0开始)数字为1的10维向量。比如,标签0将表示成([1,0,0,0,0,0,0,0,0,0,0])。因此, mnist.train.labels 是一个 [60000, 10] 的数字矩阵。

我们可以通过下面的python代码查看mnist这个数据集的情况:

print(mnist.train.images.shape, mnist.train.labels.shape)
print(mnist.test.images.shape, mnist.test.labels.shape)
print(mnist.validation.images.shape, mnist.validation.labels.shape)

其中mnist.train.images返回训练集的图片信息,minist.test返回测试集的图片信息,mnist.validation.images返回测试集的图片信息。而labels就是图片标签:0-9shape就是矩阵数据的维度,比如 [[2,3],[3,5],[1,5]]通过shape运算就返回(3,2)。
以上运行结果为:

(55000, 784) (55000, 10)
(10000, 784) (10000, 10)
(5000, 784) (5000, 10)

Softmax回归

softmax用于多分类过程中,它将多个神经元的输出,映射到(0,1)区间内,可以看成概率来理解,从而来进行多分类!
假设我们有一个数组,V,Vi表示V中的第i个元素,那么这个元素的softmax值就是
s i = e i j e j s_i = \dfrac{e^i}{\sum_j e^j}
更形象的如下图表示:

计算过程如下(用了四舍五入,保留2位有效数字):
y 1 = e z 1 j = 1 3 e z j = e z 1 e z 1 + e z 2 + e z 3 y_1=\dfrac{e^{z_1}}{\sum_{j=1}^3e^{z_j}}=\dfrac{e^{z_1}}{e^{z_1}+e^{z_2}+e^{z_3}} = e 3 e 3 + e 1 + e 3 = 20 20 + 2.7 + 0.05 = 0.88 =\dfrac{e^3}{e^3+e^1+e^{-3}} =\dfrac{20}{20+2.7+0.05}=0.88
y 2 = e z 2 j = 1 3 e z j = e z 2 e z 1 + e z 2 + e z 3 y_2=\dfrac{e^{z_2}}{\sum_{j=1}^3e^{z_j}}=\dfrac{e^{z_2}}{e^{z_1}+e^{z_2}+e^{z_3}} = e 1 e 3 + e 1 + e 3 = 2.7 20 + 2.7 + 0.05 = 0.12 =\dfrac{e^1}{e^3+e^1+e^{-3}} =\dfrac{2.7}{20+2.7+0.05}=0.12
y 3 = e z 3 j = 1 3 e z j = e z 3 e z 1 + e z 2 + e z 3 y_3=\dfrac{e^{z_3}}{\sum_{j=1}^3e^{z_j}}=\dfrac{e^{z_3}}{e^{z_1}+e^{z_2}+e^{z_3}} = e 3 e 3 + e 1 + e 3 = 0.05 20 + 2.7 + 0.05 = 0 =\dfrac{e^{-3}}{e^3+e^1+e^{-3}} =\dfrac{0.05}{20+2.7+0.05}=0
其中:
z 1 = w 11 × x 1 + w 21 × x 2 + w 31 × x 3 z_1=w_{11} \times x_1+w_{21} \times x_2+w_{31} \times x_3
z 2 = w 12 × x 1 + w 22 × x 2 + w 32 × x 3 z_2=w_{12} \times x_1+w_{22} \times x_2+w_{32} \times x_3
z 3 = w 13 × x 1 + w 23 × x 2 + w 33 × x 3 z_3=w_{13} \times x_1+w_{23} \times x_2+w_{33} \times x_3
softmax直白来说就是将原来输出是3,1,-3通过softmax函数一作用,就映射成为(0,1)的值,而这些值的累和为1(满足概率的性质),那么我们就可以将它理解成概率,在最后选取输出结点的时候,我们就可以选取概率最大(也就是值对应最大的)结点,作为我们的预测目标!

对于softmax回归模型可以用下面的图解释,对于输入的xs加权求和,再分别加上一个偏置量,最后再输入到softmax函数中:

如果把它写成一个等式,我们可以得到:

我们也可以用向量表示这个计算过程:用矩阵乘法和向量相加。这有助于提高计算效率。(也是一种更有效的思考方式)

更进一步,可以写成更加紧凑的方式:
y = s o f t m a x ( W x + b ) y=softmax(Wx+b)

二、使用 TensorFlow 实现Softmax回归模型

1、定义x,y两个Placeholder,所谓Placehoder,初学者可以理解为一种容器,后面我们将会把上面的训练集数据mnist.train.images及minst.train.labels输入到x,y中。
我们希望能够输入任意数量的MNIST图像,每一张图展平成784维的向量。我们用2维的浮点数张量来表示这些图,这个张量的形状是[None,784 ]。(这里的None表示此张量的第一个维度可以是任何长度的。)

import tensorflow as tf
 
#定义两个placeholder
x = tf.placeholder(tf.float32,[None,784])
y = tf.placeholder(tf.float32,[None,10])

这里的参数x将会是输入的图片数据即为[None,784],None就是图片的数量,因为我们训练的时候并不是一张一张图片输入的,因为一张一张输入的效率低,而且会因为一张噪音图片的对整个网络造成巨大影响,正确的训练方法应该是将100张图片组成的数据集[100,784]输入神经网络进行训练(当然,这里的100也可以是其他数值)。然后得到神经网络的输出prediction,这里的prediction就是标签y的预测值,也是一个10维的向量,例如[0.12,0.22,0.21,0.86,0.33,0.01,0.81,0.15,0.54,0.13]

2、接下来我们定义权重值和偏置量。

W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))

在这里,我们都用全为零的张量来初始化W和b。因为我们要学习W和b的值,它们的初值可以随意设置。一般的情况下我们都是采用随机设置。

3、创建模型。

prediction = tf.nn.softmax(tf.matmul(x,W)+b)

用tf.matmul(​​X,W)表示矩阵 x x 乘以矩阵 W W ,对应之前等式里面的 W x Wx ,这里x是一个2维张量拥有多个输入。然后再加上矩阵 b b ,把和输入到tf.nn.softmax函数里面。

4、定义损失函数(也叫成本函数)和优化算法。
这里定义二次损失函数和梯度优化算法,我们的目的就是通过更新神经网络参数W,b来让loss的最小,而更新 W W , b b 的操作就是train_step.

#二次代价函数
loss = tf.reduce_mean(tf.square(y-prediction))
#使用梯度下降法
train_step = tf.train.GradientDescentOptimizer(0.2).minimize(loss)

其中二次代价函数(也称为方差)为
l o s s = 1 2 N n = 1 N ( y p r e d i c t i o n ) 2 loss = \dfrac{1}{2N}\sum_{n=1}^N(y - prediction)^2
从sigmoid函数的图像就会发现,它的两侧几乎就是平的,导致它的方差在大部分情况下很小,这样在训练参数的时候收敛地就会很慢,交叉熵就是用来解决这个问题的。它的定义如下:
H y ( y ) = i y i l o g ( y i ) H_{y'}(y)=-\sum_iy'_ilog(y_i)
其中,y 是我们预测的概率分布, y’ 是实际的分布。
所以损失函数如下:
l o s s = y l o g ( p r e d i c t i o n ) loss = -\sum ylog(prediction )
代码如下:

loss = -tf.reduce_sum(y*tf.log(prediction))

5、计算准确率
这里的tf.argmax(y,1),tf.argmax(prediction,1)就是计算10维向量y及prediction中元素的最大值,也就是说预测的标签和实际标签相等时tf.equal返回真,否则返回假。最终预测正确的信息被保存到correct_prediction布尔型列表中,在通过tf.cast转化为float型,然后通过tf.reduce_mean求平均求出准确率。

#结果存放在一个布尔型列表中
correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(prediction,1))#argmax返回一维张量中最大的值所在的位置
#求准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

6、接着就进入正式训练了
首先通过init初始化变量,接着for循环训练40个回合。batch_size为每个批次的大小,n_batch为批次数。在每次训练一个回合后计算出测试集的准确率acc,并输出。

# 每个批次的大小
batch_size = 100
# 计算一共有多少个批次
n_batch = mnist.train.num_examples // batch_size
 
with tf.Session() as sess:
    init = tf.global_variables_initializer()
    sess.run(init)
    for epoch in range(40):
        for batch in range(n_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            sess.run(train_step, feed_dict={x: batch_xs, y: batch_ys})
 
        acc = sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels})
        print("Iter " + str(epoch) + ",Testing Accuracy " + str(acc))
Iter 0,Testing Accuracy 0.8335
Iter 1,Testing Accuracy 0.8706
Iter 2,Testing Accuracy 0.8815
Iter 3,Testing Accuracy 0.889
Iter 4,Testing Accuracy 0.8936
Iter 5,Testing Accuracy 0.8968
Iter 6,Testing Accuracy 0.8994
Iter 7,Testing Accuracy 0.9019
Iter 8,Testing Accuracy 0.9022
Iter 9,Testing Accuracy 0.9052
Iter 10,Testing Accuracy 0.9066

以上是程序的运行的结果,可以看到最终准确率为91.94%,这并不是很高的准确率,但却是一个入门的经典例子,后面我们将会讲到如何提高准确率.

全部代码为:

#coding: utf-8
#载入数据集
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data", one_hot=True)
 
 
print(mnist.train.images.shape, mnist.train.labels.shape)
print(mnist.test.images.shape, mnist.test.labels.shape)
print(mnist.validation.images.shape, mnist.validation.labels.shape)
 
import tensorflow as tf
 
#定义两个placeholder
x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])
 
#创建一个简单的神经网络
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
prediction = tf.nn.softmax(tf.matmul(x, W) + b)
 
#二次代价函数
loss = tf.reduce_mean(tf.square(y - prediction))
#使用梯度下降法
train_step = tf.train.GradientDescentOptimizer(0.2).minimize(loss)
 
#结果存放在一个布尔型列表中
correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(prediction,1))#argmax返回一维张量中最大的值所在的位置
#求准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
#每个批次的大小
batch_size = 100
#计算一共有多少个批次
n_batch = mnist.train.num_examples // batch_size
 
with tf.Session() as sess:
    init = tf.global_variables_initializer()
    sess.run(init)
    for epoch in range(40):
        for batch in range(n_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            sess.run(train_step, feed_dict={x: batch_xs, y: batch_ys})
 
        acc = sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels})
        print("Iter " + str(epoch) + ",Testing Accuracy " + str(acc))

参考:
http://www.tensorfly.cn/tfdoc/tutorials/mnist_beginners.html
https://blog.csdn.net/qq_36389843/article/details/78166622

猜你喜欢

转载自blog.csdn.net/mergerly/article/details/83591036