本文共 6762 字,大约阅读时间需要 22 分钟。
本教程面向刚学习机器学习和TensorFlow的读者。 如果您已经知道MNIST是什么,以及softmax(multinomial logistic)回归是什么,那么您可能更喜欢这个速度更快的教程。 确保在开始任何教程之前安装TensorFlow。
当学习如何编程时,首先要做的就是打印“Hello World”。 类似于编程中的Hello World,机器学习对应的是MNIST。
MNIST是一个简单的计算机视觉数据集。 它由这样的手写数字的图像组成:
它还包括每个图像的标签,告诉我们它是哪个数字。 例如,上述图像的标签是5,0,4和1。
在本教程中,我们将训练一个模型来查看图像并预测它们是什么数字。 我们的目标不是训练一个真正精细的模型来实现最先进的性能 - 尽管我们稍后会给您提供代码! - 而是倾向于使用TensorFlow去实现它的。 因此,我们将从一个非常简单的模型开始,称之为Softmax回归。
本教程的实际代码非常短,所有有趣的内容都只发生在三行中。 然而,了解其背后的理念非常重要:TensorFlow的工作原理和核心机器学习概念。 正因为如此,我们要非常仔细的了解代码内部的细节。
关于本教程
本教程是逐行解释代码中做了什么的事情。
您可以通过几种不同的方式使用本教程,其中包括:
- 阅读每行的解释时,将每个代码段逐行复制并粘贴到Python环境中。
- 在阅读解释之前或之后运行整个
mnist_softmax.py
Python文件,并使用本教程来理解不清楚的代码行。
我们将在本教程中完成的任务:
- 了解MNIST数据和softmax回归
- 基于查看图像中的每个像素,创建一个识别数字的模型
- 使用TensorFlow来训练模型,通过让数字“看”数千个示例来识别数字(并运行我们的第一个TensorFlow会话来完成)
- 用我们的测试数据检查模型的准确性
MNIST数据
MNIST数据在的网站上托管。如果您正在复制和粘贴本教程中的代码,请从这两行代码开始,它们将自动下载并读取数据:
from tensorflow.examples.tutorials.mnist import input_datamnist = input_data.read_data_sets(“MNIST_data /”,one_hot = True)
MNIST数据分为三个部分:55,000个训练数据(mnist.train
)的数据点,10,000个测试数据(mnist.test
),以及5,000个验证数据(mnist.validation
)。这种分组是非常重要的:机器学习过程中必须要有不能学习到的独立数据,这样我们才能确保我们学到的东西实际上是泛化的!
如前所述,每个MNIST数据点有两部分:一个手写数字的图像和一个相应的标签。我们将调用图像“x”和标签“y”。训练集和测试集都包含图像及其相应的标签;例如训练图像是mnist.train.images
,训练标签是mnist.train.labels
。
每个图像是28像素×28像素。我们可以把它解释为一大堆数字:
我们可以把这个数组变成一个28x28 = 784的数字。 只要我们在图像之间保持一致,那么我们如何展开阵列并不重要。 从这个角度来看,MNIST图像只是784维矢量空间中的一束点,具有非常丰富的结构(警告:计算密集的可视化)。
展平数据会丢弃有关图像二维结构的信息。这样不是不好吗? 确实,最好的计算机视觉方法就是利用这个原有结构,我们将在后面的教程中这样去做。 但是这里我们将使用简单方法,softmax回归(下面定义)。
mnist.train.images
的结果是一个形状为[55000,784]的张量(一个n维数组)。 第一维是图像列表的索引,第二维是每个图像中每个像素的索引。 张量中的每个条目是针对特定图像中的特定像素的介于0和1之间的像素强度。
MNIST中的每个图像都有相应的标签,0到9之间的数字代表图像中绘制的数字。
为了本教程的目的,我们将要我们的标签作为“one-hot vectors”。one-hot矢量是一个在多维度上为0,在单维度上为1的矢量。 在这种情况下,第n位将被表示为在第n维中为1的向量。 例如,3将是[0,0,0,1,0,0,0,0,0,0]。 因此,mnist.train.labels
是一个[55000,10]的浮点数组。
我们现在开始制作我们的模型!
Softmax回归
我们知道MNIST中的每个图像都是一个0到9之间的手写数字。所以一个给定的图像可能只有十种可能。我们希望能够看到一个图像,并给出它是每个数字的概率。例如,我们的模型可能会查看一张数字9的图片,并且有80%确定这是一张数字9的图片,但是给出一个5%的概率是因为它是一个8(因为上面弧形),并且有一些概率是其他数字,这不是100%确定。
这是一个softmax回归简单模型的一个经典案例。如果你想把一个对象的概率分配给几个不同的东西之一,softmax就是要做的事情,因为softmax给了我们一个0到1之间的值的列表,加起来就是1。在学习的后期,当我们训练更复杂的网络,最后一步将是softmax的一层。
softmax回归有两个步骤:首先我们将输入的特征加在某些类别中,然后将特征转换成概率。
为了收集给定图像在特定类别中的特征,我们进行像素强度的加权总和。如果具有高强度的像素是针对在该类别中的图像的特征,则权重是负的;如果特征是有利的,则权重是正的。
下图显示了一个模型在每个类中学习的权重。红色代表负面权重,蓝色代表正面权重。
我们还添加了一些额外的参数,称为bias。 基本上,我们希望有些东西能够更好的独立于输入。 结果是,热议一个输入x的类,得到的结果是:
- evidencei=∑jWi,jxj+bi
其中Wi是权重,bi是类别i的偏差,j是对输入图像x中的像素求和的指数。 然后,我们使用“softmax”函数将特征数据转换成我们的预测概率y:
- y=softmax(evidence)
这里softmax是作为一个“激活”或“链接”功能,塑造我们的线性函数的输出到我们想要的形式 - 在这种情况下,概率分布超过10个案例。 你可以把它看作是将特征转化为我们在每个类型中输入的概率。 它被定义为:
- softmax(evidence)=normalize(exp(evidence))
如果你展开这个等式,你会得到:
- softmax(evidence)i=exp(evidencei)∑jexp(evidencej)
但是,首先考虑softmax是更有帮助的:指数化输入,然后使其规格化。 指数意味着每多一个单位的特征将增加任何前向乘加的权重。 而相反,每少一个特征单位意味着每一个前向获得了上一层权重的一小部分。 没有前向的层为零或负权重。 Softmax然后归一化这些权重,使它们加起来为1,形成有效的概率分布。 (为了获得有关softmax函数的更多直觉,请查看Michael Nielsen书中的部分,并附带交互式可视化。)
你可以把我们的softmax回归看成如下所示,尽管有更多的xs。 对于每个输出,我们计算xs的加权总和,添加偏差,然后应用softmax。
如果我们把它写成等式,我们得到:
我们可以“矢量化”这个过程,把它变成矩阵乘法和矢量加法。 这对于计算效率是有帮助的。 (这也是一个有用的思考方式。)
更简洁,我们可以写:
- y=softmax(Wx+b)
现在让我们把它转换成TensorFlow可以使用的东西。
实现回归
为了在Python中进行高效的数值计算,我们通常使用像NumPy这样的库,这些库会执行复杂的操作,例如Python中的矩阵乘法,可以使用另一种语言实现的高效代码来完成。 不幸的是,每一次操作都会返回到Python,这仍然会有很多开销。 如果要在GPU上运行计算或以分布式方式运行计算,则传输数据的成本很高,所以此开销尤其糟糕。
TensorFlow也在Python之外进行繁重的工作,但是为了避免这种开销,还需要进一步的工作。 TensorFlow不是独立于Python运行的一个复杂操作,而是让我们描述一个完全在Python之外运行的交互操作图。 (像这样的方法可以在几个机器学习库中看到)
要使用TensorFlow,首先我们需要导入它。
import tensorflow as tf
我们通过操纵符号变量来描述这些交互操作。 我们来创建一个:
x = tf.placeholder(tf.float32, [None, 784])
x
不是一个具体的值。 这是一个占位符,当我们要求TensorFlow运行一个计算时,我们会输入一个值。 我们希望能够输入任意数量的MNIST图像,每个图像被平面化成784维向量。 我们将其表示为浮点数的二维张量,其形状为[None,784]
。 (这里None
表示维度可以是任何长度。)
我们也需要我们的模型的权重和biases。 我们可以想象把这些看作是额外的输入,但是TensorFlow有一个更好的方法来处理它:Variable(变量)。 变量是一个可修改的张量,它存在于TensorFlow的交互操作图中。 它可以被使用,甚至被计算修改。 对于机器学习应用程序,通常将模型参数设置为变量。
W = tf.Variable(tf.zeros([784, 10]))b = tf.Variable(tf.zeros([10]))
我们通过给定变量的初始值来创建这些变量:在这种情况下,我们将W和b初始化为全0的张量。 既然我们要学习W和b,它们最初是什么都没有关系。
注意到W的形状是[784,10],因为我们想用它乘以784维的图像向量来产生差分类的特征的10维向量。 b的形状是[10],所以我们可以将它添加到输出中。
我们现在可以实现我们的模型。 只需要一行来定义它!
y = tf.nn.softmax(tf.matmul(x, W) + b)
首先,我们乘以W
乘以表达式tf.matmul(x,W)
。这是从我们的方程中乘以它们的时候翻转过来的,这里我们把Wx作为一个小技巧来处理x是一个具有多个输入的2D张量。然后我们添加b,最后应用tf.nn.softmax
。
而已。经过几行简单的设置之后,我们只用一条线来定义我们的模型。这并不是因为TensorFlow被设计成使得softmax回归特别容易:从机器学习模型到物理模拟,这只是描述多种数值计算的一种非常灵活的方式。一旦定义,我们的模型可以在不同的设备上运行:计算机的CPU,GPU,甚至是手机!
训练
为了训练我们的模型,我们需要定义模型的好坏。那么实际上,在机器学习中,我们通常定义一个模型的坏的意义。我们称之为成本或损失,它表示我们的模型离我们期望的结果有多远。我们尽量减少这个误差,误差越小,我们的模型就越好。
非常常见的,确定模型的损失函数被称为“交叉熵”。交叉熵来源于信息论中信息压缩编码的思想,但在赌博,机器学习等许多领域,都是一个重要的思想。它被定义为:
- Hy′=−∑y′ilog(yi)
其中y
是我们预测的概率分布,y'
是真实分布(带有数字标签的one-hot矢量)。 在一些粗略的意义上,交叉熵是衡量我们的预测是多差的描述。 关于交叉熵的更多细节超出了本教程的范围,但是非常值得理解。
为了实现交叉熵,我们需要首先添加一个新的占位符来输入正确的答案:
y_ = tf.placeholder(tf.float32, [None, 10])
那么我们可以实现交叉熵函数,
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
首先,tf.log
计算y
的每个元素的对数。接下来,我们将y_
的每个元素与tf.log(y)
的相应元素相乘。然后,由于reduction_indices = [1]
参数,tf.reduce_sum
将元素添加到y
的第二维中。最后,tf.reduce_mean
计算批处理中所有示例的平均值。
请注意,在源代码中,我们不使用这个公式,因为它在数值上是不稳定的。相反,我们将tf.nn.softmax_cross_entropy_with_logits
应用于非标准化的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的梯度下降算法来最小化cross_entropy。 梯度下降是一个简单的过程,其中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})
循环中的每一步,我们从我们的训练集中获得一百个随机数据点的“batch”。 我们运行train_step并且传回一个字典数据来替换占位符。
使用小批量的随机数据称为随机训练 - 在这里是随机梯度下降。 理想情况下,我们希望将所有数据用于每一步训练,因为这样可以让我们更好地了解我们应该做什么,但是这样做很复杂。 所以相反,我们每次都使用不同的子集。 这样做很简单,而且有很多相同的好处。
评估我们的模型
怎么评价我们的模型有多好?
那么,首先让我们弄清楚我们预测了多少正确的标签。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%的准确性! (有关更多信息,请查看结果列表。)
转载地址:https://coordinate.blog.csdn.net/article/details/78703015 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!