TensorFlow MNIST机器学习入门详解

发布于 / 机器学习 / Comments Off on TensorFlow MNIST机器学习入门详解

0x00、何为MNIST?

在学习编程语言的时候,我们第一课基本都是打印“Hello World”,MNIST也一样,是一个入门的必修课。 简单点说,MNIST就是一大堆手写数字图片的集合,包含了训练集(就是正确的集合,教会电脑什么样是正确的)和测试集(像试卷一样,考验电脑是否能成功识别)像下面这张图一样。

0x01、准备

首先下载文件。在这个链接:(http://yann.lecun.com/exdb/mnist/)中下载四个文件,放到MNIST_data文件夹内即可。这个数据集包含了4个文件,通俗点说,分别是

  • 训练所用图片(总共60000张)

  • 训练所用标签(解释了图片的内容)

  • 测试所用图片(总共10000张

  • 测试所用标签(测试图片的正确答案)

把测试数据与训练数据分开的目的是更客观的评价这个模型,从而更加容易把设计的模型推广到其他数据集上(泛化)如果不理解你可以想想,数学考试的时候老师并不喜欢用你做过的题目考你,而是用相同类型的其他题目考察你是否学习会了。 接着导入库文件: 

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import gzip
import os
import tempfile
import numpy
from six.moves import urllib
from six.moves import xrange  # pylint: disable=redefined-builtin
import tensorflow as tf
from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets

好了,下面就可以进行我们接下来的工作了。

 0x02、数据集

首先导入训练和测试数据 

mnist = read_data_sets("MNIST_data/", one_hot=True)

可以看到数据成功被解压读取。

 

  导入的数据(训练所用的60000份数据集,记做mnist.train,和测试所用的10000份,记做mnist.test。其中所有的图片记做xs,标签记做ys。)

 

这里的每个图片都是28*28像素的,我们可以把图片转化为一个矩阵,如下图

接着我们把这个矩阵转化成一个向量,这里可以一行一行的转换([a11, a12... a1n, a21, a22... a2n... ann]),也可以一列一列的转换([a11, a21... an1, a12, a22... an2... ann]),甚至可以斜着展开... 具体怎么转换无所谓,反正保证所有的数据都按照相同的方式展开就行。这个向量是28*28=784维的。

 

当然这样展开后图片的二维结构信息就丢失了,(原来方形的矩阵变成了“一条”),这样并不好,但是作为初级教程我们先这么展开。后续的教程中会弥补。

 

在训练集中,mnist.train.images(训练图片集)有60000个,每个图片是784维的向量,所以mnist.train.images是一个形状为 [60000, 784] 的张量。第一个维度数字为图片的索引(理解为下标,第几张图片),第二个维度是图片的内容,即像素点。值在0,1之间。如下图

  

 在标签集中,mnist.train.labels(训练标签集)是一个形状为[60000, 10]的张量,第一个依然是索引,第二个表示标签表示的第几个数字。例如 0表示为[1,0,0,0,0,0,0,0,0,0]

  

 好了,接下来我们可以构建模型了

 

0x03、Softmax回归

听着这个名字挺高大上,忽略了就好了。。。我们只要学会它的原理就好。 我们知道了MNIST里面是0-9的手写数字,有工整的,也有歪歪扭扭的。我们希望这个模型怎么工作才能识别它们呢?

我们希望它这么工作:首先传入一个图片,我们希望的得到10个数字,分别是0、1一直到9的概率值。打个比方

例如我们传入的手写数字为8,我可能会得到“是8的概率为80%,是9的概率为10%,是0的概率为1.5%.......”,是8的概率最高,所以我们得到了结果:这个数字很有可能是8。

  

这是一个使用softmax回归(softmax regression)模型的经典案例。softmax模型可以用来给不同的对象分配概率

 

上面我们判断手写图片写的数字是8,凭什么是8呢?

 

这里的这个“凭什么”,我们称之为证据(evidence)。要想知道证据是什么,我们要做的首先是对图片的每一个像素点的像素值(黑度)进行加权求和。

如果某个像素点能够说明图片写的是8,我们给它赋予一个正数的权重值。如果不能说明图片写的是8,我们给它一个负数的权重值。

 

如果没听懂上面的话,请认真读下面的例子。首先请仔细对比数字“3”和数字“8”的区别:

为了方便举例,这里我们假设“3”就是把“8”的左半部分削掉,保留右半部分

  3            8  

比如图片上写的是一个工整的宋体8,那对于8,图片上所有黑色的部分都可以说明这个数字是8,整个图片权重值是正的。

但是对于3,图片上只有右侧的部分可以说明这个数字是3,左侧部分并不像3,所以右侧的权重是正的,左边是负的,求和就成了0。小于8的权重。

 

这样就说明了数字是8。

 
 

下面的图片很好的显现的模型对图片处理时权重的大小。其中蓝色代表正,红色代表负。

但是我们不能保证图片是书写工整的,可能会有歪歪扭扭的,甚至可能会有不小心点的点,或者溅上去墨迹。

因此我们要定义一个偏置量(bias)。不懂什么是偏置量你可以理解为它能中和掉干扰量(不小心点上去的点)

 

这样我们的证据的值,也就是那个“凭什么是8”的计算方法就是对图片所有像素点加权求和,然后加上偏置量。

用公式表示就是

f26ef914245883c80f181c4aade2ed04.png

这里evidence表示证据值,Wi表示权重,bi是数字i类的偏置量,j代表给定图片x的像素索引用于像素求和,接着用

softmax函数即可将证据值转化为概率。

f26ef914245883c80f181c4aade2ed04.png

这里的softmax函数是如何实现的我们暂时不考虑。TensorFlow会自动帮我们完成它。 

 0x04、TensorFlow实现模型

这里我们为了进行高性能的数值计算,我们使用了numpy库。不懂的可以点击这里

我们先创建一个可交互的操作单元

x = tf.placeholder("float", [None, 784])

这里的x是一个占位符(placeholder),占位符没有数值上的意义。仅表示了向量的形状。

我们用2维的浮点数张量来表示这些图,这个张量的形状是[None,784 ]。(这里的None表示此张量的第一个维度可以是任何长度的。)

接着我们定义权重值和偏置量

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

这里的 Variable 是变量的意思,也就是TensorFlow中的变量。可以被修改。   

我们赋予 tf.Variable 不同的初值来创建不同的 Variable:在这里,我们都用全为零的张量(tf.zeros)来初始化W和b。

下面的操作中我们需要让电脑去学习 W 和 b 的值,所以这里的初始值可以任意定义。

 

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

 

下面搭建 softmax 模型:

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

 

tf.matmul(X,W) 表示 x 乘以 W,对应上面那个式子中的 Wx。

b是偏置量,对应式子中的bi。

 

好了,模型全部搭建好了,接下来是训练~~~

 

0x05、TensorFlow训练

我们定义一个指标,能评估模型的好坏,这个指标通常叫做“成本(cost)”或“损失(loss)”。

看着这个名字我们也知道,一定要降低成本或者损失,这样才符合常理嘛~

 

这里介绍一个非常常见的成本函数,叫做“交叉熵(这个自读作‘伤’)”(cross-entropy)

它的计算表达式为:

 

y 是我们预测的概率分布, y' 是实际的分布。

 

为了计算交叉熵,我们定义一个新的占位符。

y_ = tf.placeholder("float", [None,10])

接着计算交叉熵

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

这里 tf.log 可以计算对数,再把 _y(对应式子中的y')的每个元素与对数相乘。tf.reduce_sum用来求和。

是不是觉得这里的reduce_sum没有太理解?下面这个图会帮你更好的理解:

  

求和的原因在于交叉熵不是求某一个的交叉熵,而是求总和,来评估模型的整体性能。

 

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

这段代码利用了梯度下降算法,以0.01的学习速率最小化交叉熵。大致了解下就行。

 

设置好了,接着初始化创建的变量

init = tf.initialize_all_variables()

 

然后在一个session(会话)中启动我们的模型,初始化变量

sess = tf.Session()
sess.run(init)

 

好了,下面训练开始!

for i in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(100)
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

 

这里mnist.train.next_batch(100)表示返回随机100个训练数据,返回的格式为

array( array(表示图片像素的784维向量, 表示图片对应的数字的10维向量), array(像素的784维向量, 数字的10维向量) ...... )

表示像素的向量给了batch_xs,表示对应数字的向量给了batch_ys。

 

下面的这一行用来把这100个测试点作为参数替换掉之前的占位符来运行train_step

 

这样使用部分随机数据进行训练叫做随机训练。

 

0x06、评估

我们学习的怎么样了呢,让我们来测试一下吧! 首先让我们找出那些预测正确的标签。首先说一下 tf.argmax 函数,它能给出tensor对象在某一维上最大的数据所在的索引值。

例如[0, 0, 5, 0, 1, 0],argmax函数会返回 2,因为索引是 2 的数值为5,最大。

 

由于标签向量(就是那个表示这个图片上写的什么数字的向量)由0, 1组成,因此最大值1所在的索引位置就是类别标签。

 

接着还有一个函数,叫做 tf.equal,用来比较是否匹配相等

 

比如tf.argmax(y,1)返回的是模型对于任一输入x预测到的标签值,而 tf.argmax(y_,1) 代表正确的标签,我们可以用 tf.equal 来检测我们的预测是否真实标签匹配(索引位置一样表示匹配)。

 

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

 

这行代码会给我们一组布尔值。为了确定正确预测项的比例,我们可以把布尔值转换成浮点数,然后取平均值。例如,[True, False, True, True] 会变成 [1,0,1,1] ,取平均值后得到 0.75.

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

 

最后,我们计算所学习到的模型在测试数据集上面的正确率。

print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})

 0x07、总结

所有的代码整理一下大致是这样的:

# -*- coding:utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import gzip
import os
import tempfile
import numpy
from six.moves import urllib
from six.moves import xrange  # pylint: disable=redefined-builtin
import tensorflow as tf
from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets
mnist = read_data_sets("MNIST_data/", one_hot=True)#读取mnist数据
################# 搭建模型 #################
x = tf.placeholder("float", [None, 784])  #用来表示图像形状的占位符
W = tf.Variable(tf.zeros([784,10]))          #权重值
b = tf.Variable(tf.zeros([10]))      #偏置率
evidence = tf.matmul(x,W) + b      #证据=累加(W*x+b)
y = tf.nn.softmax(evidence)      #y表示概率。matmul表示相乘。x与W相乘,加上偏置率
################# 训练模型 #################
y_ = tf.placeholder("float", [None,10])    #用来表示交叉熵的占位符
cross_entropy = -tf.reduce_sum(y_*tf.log(y))#交叉熵=-累加(y_*log(y))
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
                      #0.01的学习速率最小化交叉熵
init = tf.initialize_all_variables()    #初始化所有变量
sess = tf.Session()
sess.run(init)          #启动会话
for i in range(1000):              #学习1000次
  batch_xs, batch_ys = mnist.train.next_batch(100)  
            #batch_xs为图像像素向量,batch_ys为标签向量
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
            #运行会话。按照train_step的方式,将像素和标签传入,进行学习
################# 评估好坏 #################
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
            #判断是否相等(是否正确)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
            #reduce_mean用来求平均值。cast用来布尔转浮点(true=>1)
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
            #用所有测试图片和标签进行测试。

   

 运行后准确率大致是91%。

 

转载原创文章请注明,转载自: 斐斐のBlog » TensorFlow MNIST机器学习入门详解
评论已关闭