机器学习笔记-MNIST数据库

初学者介绍MLIST

本教程适用于初学机器学习和TensorFlow的读者。如果你已经知道MNIST是什么,以及什么softmax(多项式逻辑)回归,那么你可能更喜欢这个更快节奏的教程。在开始任一教程之前,请务必安装TensorFlow。

当学习如何编程时,有一个传统,你所做的第一件事是打印“Hello World”。就像编程有Hello World,机器学习有MNIST。
MNIST是一个简单的计算机视觉数据集。它由以下手写数字的图像组成:
这里写图片描述

它还包括每个图像的标签,告诉我们哪个数字。例如,上述图像的标签是5,0,4和1。

在本教程中,我们将训练一个模型来查看图像并预测它们的数字。我们的目标不是训练一个真正精致的模型,而是实现最先进的性能 - 尽管我们稍后会给你代码! - 而是将趾部浸入使用TensorFlow。因此,我们将从一个非常简单的模型开始,称为Softmax回归。

本教程的实际代码很短,所有有趣的东西都发生在三行。然而,了解其背后的想法是非常重要的:TensorFlow如何运作和核心机器学习概念。因此,我们将非常仔细地编写代码。

关于本教程

本教程将逐行解释mnist_softmax.py代码中发生的情况。

您可以通过以下几种不同的方式使用本教程,其中包括:

当您阅读每行的说明时,将每个代码片段逐行复制并粘贴到Python环境中。
在阅读说明之前或之后运行整个mnist_softmax.py Python文件,并使用本教程来了解不清楚的代码行。
我们将在本教程中完成什么:

了解MNIST数据和softmax回归
基于查看图像中的每个像素,创建一个用于识别数字的模型的函数
使用TensorFlow来训练模型来识别数字,方法是将其“查看”成千上万个示例(并运行我们的第一个TensorFlow会话)
使用我们的测试数据检查型号的精度
MNIST数据

MNIST数据托管在Yann LeCun的网站上。如果您在本教程的代码中复制和粘贴,请从这两个代码开始,这两行代码将自动下载和读取数据:

来自tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets(“MNIST_data /”,one_hot = True)
MNIST数据分为三部分:训练数据(mnist.train)55,000个数据点,10,000点测试数据(mnist.test)和5,000点验证数据(mnist.validation)。这种分裂是非常重要的:在机器学习中,我们有独立的数据,我们不学习,因此我们可以确保我们所学到的知识实际上被概括!

如前所述,每个MNIST数据点都有两部分:手写数字的图像和相应的标签。我们将调用图像“x”和标签“y”。训练集和测试集都包含图像及其相应的标签;例如,训练图像是mnist.train.images,训练标签是mnist.train.labels。

每个图像是28像素×28像素。我们可以把它解释为一大批数字:
这里写图片描述

我们可以把这个数组变成一个28×28 = 784数字的向量。只要我们在图像之间保持一致,那么我们如何平坦化数组并不重要。从这个角度来看,MNIST图像只是一个784维向量空间中的一个点,结构非常丰富(警告:计算密集的可视化)。

平铺数据会丢弃有关图像2D结构的信息。不是那么糟糕吗那么,最好的计算机视觉方法会利用这个结构,我们将在后面的教程中。但是我们将在这里使用的简单方法,一个softmax回归(下面定义)不会。

结果是mnist.train.images是一个形状为[55000,784]的张量(n维数组)。第一个维度是图像列表中的索引,第二个维度是每个图像中每个像素的索引。对于特定图像中的特定像素,张量中的每个条目是0和1之间的像素强度。
这里写图片描述

MNIST中的每个图像都具有相应的标签,0到9之间的数字表示图像中绘制的数字。

为了本教程的目的,我们将要将我们的标签称为“一个热向量”。一个热矢量是一个向量,在大多数维度上为0,在一个维度上为1。在这种情况下,[数学处理错误]第n个数字将被表示为[数学处理错误]第n维中为1的向量。例如,3将是[数学处理错误] [0,0,0,1,0,0,0,0,0,0]。因此,mnist.train.labels是一个[55000,10]的浮点数组。
这里写图片描述
我们现在准备实际制作我们的模特儿了!

Softmax回归

我们知道MNIST中的每个图像都是零到九之间的手写数字。所以给定的图像只有十个可能的东西可以。我们希望能够看到一个图像,并给出它是每个数字的概率。例如,我们的模型可能会看到一个九分之一的图片,80%的人肯定它是一个九,但是给它一个5%的几率是八分之一(因为顶级循环),并有一点概率所有其他,因为它不是100%确定。

这是一个经典的情况,其中softmax回归是一种自然简单的模型。如果要将概率分配给几个不同的东西之一的对象,softmax是要做的事情,因为softmax给出了一个0到1之间的值加起来为1的列表。稍后,当我们训练更复杂型号,最后一步将是一层softmax。

softmax回归有两个步骤:首先我们将我们的输入的证据加在某些类中,然后将该证据转换成概率。

为了统计给定图像在特定类中的证据,我们进行像素强度的加权和。如果具有高强度的像素是针对该类中的图像的证据,那么权重是负的,如果它是有利的证据则为正。

下图显示了为每个类学习的一个模型的权重。红色代表负重,而蓝色代表负重。
这里写图片描述

我们还添加了一些称为偏见的额外证据。基本上,我们希望能够说一些事情更可能独立于输入。结果是我给出了一个输入[数学处理错误] x的类[Math Processing Error]的证据是:

[数学处理错误]
证据=ΣjWi,jxj + bi
其中[数学处理误差] Wi是权重,[数学处理误差] bi是类[数学处理误差] i的偏差,[数学处理误差] j是对输入图像中的像素求和的索引[数学处理误差]处理错误] x。然后,使用“softmax”函数将证据数据转换为我们的预测概率[数学处理误差] y:

[数学处理错误]
y = softmax(证据)
这里softmax用作“激活”或“链接”功能,将我们的线性函数的输出整形成我们想要的形式 - 在这种情况下,概率分布超过10个。您可以将其视为将证据的一切转化为我们在每个类中的输入概率。它定义为:

[数学处理错误]
softmax(x)= normalize(exp⁡(x))
如果你扩展这个方程式,你得到:
[数学处理错误]
softmax(x)i =exp⁡(xi)Σjexp⁡(xj)
但是通过第一种方式来考虑softmax通常更有帮助:对其输入进行指数化,然后对它们进行归一化。取幂意味着一个单位的证据增加了任何假设乘以的权重。相反,拥有一个较少的证据单位意味着一个假设得到其早期重量的一小部分。没有假设有零或负重。 Softmax然后对这些权重进行归一化,使它们加起来一个,形成有效的概率分布。 (要获得更多关于softmax功能的直觉,请参阅Michael Nielsen的书中的部分内容,并附有交互式可视化文件。)

您可以将我们的softmax回归图像看成如下,尽管还有更多的[数学处理错误] xs。对于每个输出,我们计算[数学处理误差] xs的加权和,添加偏差,然后应用softmax。
这里写图片描述

如果我们把它写成方程式,我们得到:
这里写图片描述

我们可以“矢量化”这个过程,把它变成矩阵乘法和向量加法。这有助于计算效率。 (这也是一个有用的思路。)
这里写图片描述

更紧凑,我们可以写:

[数学处理错误]
y = softmax(Wx + b)
现在我们来看看TensorFlow可以使用的东西。

实现回归

为了在Python中进行有效的数值计算,我们通常使用像NumPy这样的数据库,它们可以使用高效的代码来实现另一种语言,而使用昂贵的操作,比如Python之外的矩阵乘法。不幸的是,每次操作都需要重新切换到Python的开销很大。如果要在GPU上运行计算或以分布式方式运行计算,那么这种开销尤其糟糕,传输数据的成本很高。

TensorFlow也在Python之外做了很大的工作,但它需要进一步的工作来避免这种开销。 TensorFlow不是独立于Python运行单独的昂贵操作,而是可以描述完全在Python之外运行的交互操作的图形。 (这样的方法可以在几台机器学习库中看到。)

要使用TensorFlow,首先我们需要导入它。

导入张量流作为tf
我们通过操纵符号变量来描述这些交互操作。我们创建一个:

x = tf.placeholder(tf.float32,[None,784])
x不是一个特定的值。这是一个占位符,当我们要求TensorFlow运行计算时,我们将输入一个值。我们希望能够输入任何数量的MNIST图像,每个图像被平铺成784维的向量。我们将其表示为2-D张量的浮点数,形状为[无,784]。 (这里无意味着尺寸可以是任何长度。)

我们还需要我们的模型的权重和偏差。我们可以想象,像其他输入一样处理这些信息,但是TensorFlow有一个更好的处理方式:变量。变量是一个可修改的张量,它存在于TensorFlow的交互操作图中。它可以被计算使用甚至修改。对于机器学习应用程序,一般通常将模型参数设为变量。
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
通过给变量tf.Variable创建变量的初始值来创建这些变量:在这种情况下,我们初始化W和b作为满量程的张量。由于我们要学习W和B,所以它们最初并不重要。

请注意,W具有[784,10]的形状,因为我们要将784维图像向量乘以它,以产生差分类别的10维证据向量。 b具有[10]的形状,所以我们可以将其添加到输出。

我们现在可以实施我们的模型。它只需要一行来定义它!

y = tf.nn.softmax(tf.matmul(x,W)+ b)
首先,我们将x乘以W,表达式为tf.matmul(x,W)。当我们在我们的方程中乘以它们时,它被翻转,我们有[Math Processing Error] Wx,作为处理x是具有多个输入的2D张量的小技巧。然后我们添加b,最后应用tf.nn.softmax。

而已。经过几个短暂的设置,我们只需要一行来定义我们的模型。这并不是因为TensorFlow的设计使得softmax的回归特别容易:它只是描述从机器学习模型到物理模拟的多种数值计算的非常灵活的方式。一旦定义,我们的型号可以在不同的设备上运行:您的计算机的CPU,GPU,甚至手机!

训练

为了训练我们的模型,我们需要定义模型是什么意思。那么实际上,在机器学习中,我们通常定义一个模型对于坏的意义。我们称之为成本或损失,它代表了我们的模型与我们所期望的结果有多远。我们尝试最小化这个错误,而且误差范围越小,我们的模型就越好。

确定模型损失的一个非常常见的非常好的功能称为“交叉熵”。交叉熵来源于对信息理论中的信息压缩代码的思考,但是从赌博到机器学习在很多领域都是一个重要的思想。它定义为:

[数学处理错误]
Hy’(y)= - Σiyi’log⁡(yi)
其中[数学处理误差] y是我们的预测概率分布,[数学处理误差] y’是真实分布(带有数字标签的单热矢量)。在粗略的意义上,交叉熵正在衡量我们的预言是如何无效地描述真相。有关交叉熵的更多细节超出了本教程的范围,但它是非常值得理解的。

为了实现交叉熵,我们需要先添加一个新的占位符来输入正确答案:

y_ = tf.placeholder(tf.float32,[None,10])
然后我们可以实现交叉熵函数,[数学处理误差]-Σy’log⁡(y):

cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y),reduction_indices = [1]))
首先,tf.log计算y的每个元素的对数。接下来,我们将y_的每个元素乘以tf.log(y)的相应元素。然后,由于reduce_indices = [1]参数,tf.reduce_sum会在y的第二维中添加元素。最后,tf.reduce_mean计算批次中所有示例的平均值。

请注意,在源代码中,我们不使用此公式,因为它在数值上是不稳定的。相反,我们对非规范化逻辑应用tf.nn.softmax_cross_entropy_with_logits(例如,我们在tf.matmul(x,W)+ b)上调用softmax_cross_entropy_with_logits,因为这个更数值稳定的函数在内部计算softmax激活。在您的代码中,请考虑使用tf.nn.softmax_cross_entropy_with_logits。

现在我们知道我们想要我们的模型做什么,很容易让TensorFlow训练它来做到这一点。因为TensorFlow知道您的计算的整个图形,它可以自动使用反向传播算法来有效地确定变量如何影响您要求最小化的损失。那么它可以应用您选择的优化算法来修改变量并减少损失。

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
在这种情况下,我们要求TensorFlow使用具有0.5学习速率的梯度下降算法来最小化交叉熵。梯度下降是一个简单的过程,其中TensorFlow简单地将每个变量在减少成本的方向上稍微移动一点。但是TensorFlow还提供了许多其他优化算法:使用一个就像调整一行一样简单。

什么TensorFlow在这里,在幕后,是添加新的操作到您的图形,实现反向传播和梯度下降。然后,它返回一个单一的操作,当运行时,进行梯度下降训练的步骤,稍微调整您的变量以减少损失。

我们现在可以在InteractiveSession中启动该模型:
sess = tf.InteractiveSession()
我们首先必须创建一个操作来初始化我们创建的变量:

tf.global_variables_initializer()。run()
我们来训练 - 我们将运行1000次训练步骤!

for _ in range(1000):
batch_xs,batch_ys = mnist.train.next_batch(100)
sess.run(train_step,feed_dict = {x:batch_xs,y_:batch_ys})
循环的每一步,我们从训练集中得到一百个随机数据点的“批次”。我们在批次数据中运行train_step feed来替换占位符。

使用小批量的随机数据称为随机训练 - 在这种情况下,随机梯度下降。理想情况下,我们希望将所有数据用于培训的每个步骤,因为这样可以让我们更好地了解我们应该做什么,但这很贵。因此,我们每次使用不同的子集。这样做是便宜的,并且有很多同样的好处。

评估我们的模型

我们的模型做得如何?

那么首先我们来弄清楚我们预测正确的标签。 tf.argmax是一个非常有用的功能,它可以给出沿某个轴的张量中最高条目的索引。例如,tf.argmax(y,1)是我们的模型认为对每个输入最有可能的标签,而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,tf.float32))
最后,我们要求我们对测试数据的准确性。

print(sess.run(accuracy,feed_dict = {x:mnist.test.images,y_:mnist.test.labels}))
这应该是92%左右。

那好吗嗯,不是真的其实这很糟糕这是因为我们使用的是非常简单的模型。有了一些小的变化,我们可以达到97%。最好的型号可以达到99.7%的精度!