原文:https://www.tensorflow.org/tutorials/mnist/beginners/html
这个教程的目标读者是对机器学习和TensorFlow都不太了解的新手。若是你已经了解MNIST和softmax回归(softmax regression)的相关知识,你能够阅读这个快速上手教程。python
当咱们开始学习编程的时候,第一件事每每是学习打印"Hello World"。就比如编程入门有Hello World,机器学习入门有MNIST。git
MNIST是一个入门级的计算机视觉数据集,它包含各类手写数字图片:github
它也包含每一张图片对应的标签,告诉咱们这个是数字几。好比,上面这四张图片的标签分别是5,0,4,1。web
在此教程中,咱们将训练一个机器学习模型用于预测图片里面的数字。咱们的目的不是要设计一个世界一流的复杂模型 -- 尽管咱们会在以后给你源代码去实现一流的预测模型 -- 而是要介绍下如何使用TensorFlow。因此,咱们这里会从一个很简单的数学模型开始,它叫作Softmax Regression。算法
对应这个教程的实现代码很短,并且真正有意思的内容只包含在三行代码里面。可是,去理解包含在这些代码里面的设计思想是很是重要的:TensorFlow工做流程和机器学习的基本概念。所以,这个教程会很详细地介绍这些代码的实现原理。编程
MNIST数据集的官网是Yann LeCun's website。在这里,咱们提供了一份python源代码用于自动下载和安装这个数据集。你能够下载这份代码,而后用下面的代码导入到你的项目里面,也能够直接复制粘贴到你的代码文件里面。api
import tensorflow.examples.tutorials.mnist.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
。机器学习
每一张图片包含28X28个像素点。咱们能够用一个数字数组来表示这张图片:
咱们把这个数组展开成一个向量,长度是 28x28 = 784。如何展开这个数组(数字间的顺序)不重要,只要保持各个图片采用相同的方式展开。从这个角度来看,MNIST数据集的图片就是在784维向量空间里面的点, 而且拥有比较复杂的结构 (提醒: 此类数据的可视化是计算密集型的)。
展平图片的数字数组会丢失图片的二维结构信息。这显然是不理想的,最优秀的计算机视觉方法会挖掘并利用这些结构信息,咱们会在后续教程中介绍。可是在这个教程中咱们忽略这些结构,所介绍的简单数学模型,softmax回归(softmax regression),不会利用这些结构信息。
所以,在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]
的数字矩阵。
如今,咱们准备好能够开始构建咱们的模型啦!
咱们知道MNIST的每一张图片都表示一个数字,从0到9。咱们但愿获得给定图片表明每一个数字的几率。好比说,咱们的模型可能推测一张包含9的图片表明数字9的几率是80%可是判断它是8的几率是5%(由于8和9都有上半部分的小圆),而后给予它表明其余数字的几率更小的值。
这是一个使用softmax回归(softmax regression)模型的经典案例。softmax模型能够用来给不一样的对象分配几率。即便在以后,咱们训练更加精细的模型时,最后一步也须要用softmax来分配几率。
softmax回归(softmax regression)分两步:第一步
为了获得一张给定图片属于某个特定数字类的证据(evidence),咱们对图片像素值进行加权求和。若是这个像素具备很强的证听说明这张图片不属于该类,那么相应的权值为负数,相反若是这个像素拥有有利的证据支持这张图片属于这个类,那么权值是正数。
下面的图片显示了一个模型学习到的图片上每一个像素对于特定数字类的权值。红色表明负数权值,蓝色表明正数权值。
咱们也须要加入一个额外的偏置量(bias),由于输入每每会带有一些无关的干扰量。所以对于给定的输入图片 x 它表明的是数字 i 的证据能够表示为
其中 表明权重,
表明数字 i 类的偏置量,j 表明给定图片 x 的像素索引用于像素求和。而后用softmax函数能够把这些证据转换成几率 y:
这里的softmax能够当作是一个激励(activation)函数或者连接(link)函数,把咱们定义的线性函数的输出转换成咱们想要的格式,也就是关于10个数字类的几率分布。所以,给定一张图片,它对于每个数字的吻合度能够被softmax函数转换成为一个几率值。softmax函数能够定义为:
展开等式右边的子式,能够获得:
可是更多的时候把softmax模型函数定义为前一种形式:把输入值当成幂指数求值,再正则化这些结果值。这个幂运算表示,更大的证据对应更大的假设模型(hypothesis)里面的乘数权重值。反之,拥有更少的证据意味着在假设模型里面拥有更小的乘数系数。假设模型里的权值不能够是0值或者负值。Softmax而后会正则化这些权重值,使它们的总和等于1,以此构造一个有效的几率分布。(更多的关于Softmax函数的信息,能够参考Michael Nieslen的书里面的这个部分,其中有关于softmax的可交互式的可视化解释。)
对于softmax回归模型能够用下面的图解释,对于输入的xs
加权求和,再分别加上一个偏置量,最后再输入到softmax函数中:
若是把它写成一个等式,咱们能够获得:
咱们也能够用向量表示这个计算过程:用矩阵乘法和向量相加。这有助于提升计算效率。(也是一种更有效的思考方式)
更进一步,能够写成更加紧凑的方式:
为了用python实现高效的数值计算,咱们一般会使用函数库,好比NumPy,会把相似矩阵乘法这样的复杂运算使用其余外部语言实现。不幸的是,从外部计算切换回Python的每个操做,仍然是一个很大的开销。若是你用GPU来进行外部计算,这样的开销会更大。用分布式的计算方式,也会花费更多的资源用来传输数据。
TensorFlow也把复杂的计算放在python以外完成,可是为了不前面说的那些开销,它作了进一步完善。Tensorflow不单独地运行单一的复杂计算,而是让咱们能够先用图描述一系列可交互的计算操做,而后所有一块儿在Python以外运行。(这样相似的运行方式,能够在很多的机器学习库中看到。)
使用TensorFlow以前,首先导入它:
import tensorflow as tf
咱们经过操做符号变量来描述这些可交互的操做单元,能够用下面的方式建立一个:
x = tf.placeholder(tf.float32, [None, 784])
x
不是一个特定的值,而是一个占位符placeholder
,咱们在TensorFlow运行计算时输入这个值。咱们但愿可以输入任意数量的MNIST图像,每一张图展平成784维的向量。咱们用2维的浮点数张量来表示这些图,这个张量的形状是[None,784 ]
。(这里的None
表示此张量的第一个维度能够是任何长度的。)
咱们的模型也须要权重值和偏置量,固然咱们能够把它们当作是另外的输入(使用占位符),但TensorFlow有一个更好的方法来表示它们:Variable
。 一个Variable
表明一个可修改的张量,存在在TensorFlow的用于描述交互性操做的图中。它们能够用于计算输入值,也能够在计算中被修改。对于各类机器学习应用,通常都会有模型参数,能够用Variable
表示。
W = tf.Variable(tf.zeros([784,10])) b = tf.Variable(tf.zeros([10]))
咱们赋予tf.Variable
不一样的初值来建立不一样的Variable
:在这里,咱们都用全为零的张量来初始化W
和b
。由于咱们要学习W
和b
的值,它们的初值能够随意设置。
注意,W
的维度是[784,10],由于咱们想要用784维的图片向量乘以它以获得一个10维的证据值向量,每一位对应不一样数字类。b
的形状是[10],因此咱们能够直接把它加到输出上面。
如今,咱们能够实现咱们的模型啦。只须要一行代码!
y = tf.nn.softmax(tf.matmul(x,W) + b)
首先,咱们用tf.matmul(X,W)
表示x
乘以W
,对应以前等式里面的,这里
x
是一个2维张量拥有多个输入。而后再加上b
,把和输入到tf.nn.softmax
函数里面。
至此,咱们先用了几行简短的代码来设置变量,而后只用了一行代码来定义咱们的模型。TensorFlow不只仅可使softmax回归模型计算变得特别简单,它也用这种很是灵活的方式来描述其余各类数值计算,从机器学习模型对物理学模拟仿真模型。一旦被定义好以后,咱们的模型就能够在不一样的设备上运行:计算机的CPU,GPU,甚至是手机!
为了训练咱们的模型,咱们首先须要定义一个指标来评估这个模型是好的。其实,在机器学习,咱们一般定义指标来表示一个模型是坏的,这个指标称为成本(cost)或损失(loss),而后尽可能最小化这个指标。可是,这两种方式是相同的。
一个很是常见的,很是漂亮的成本函数是“交叉熵”(cross-entropy)。交叉熵产生于信息论里面的信息压缩编码技术,可是它后来演变成为从博弈论到机器学习等其余领域里的重要技术手段。它的定义以下:
y 是咱们预测的几率分布, y' 是实际的分布(咱们输入的one-hot vector)。比较粗糙的理解是,交叉熵是用来衡量咱们的预测用于描述真相的低效性。更详细的关于交叉熵的解释超出本教程的范畴,可是你颇有必要好好理解它。
为了计算交叉熵,咱们首先须要添加一个新的占位符用于输入正确值:
y_ = tf.placeholder("float", [None,10])
而后咱们能够用 计算交叉熵:
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
首先,用 tf.log
计算 y
的每一个元素的对数。接下来,咱们把 y_
的每个元素和 tf.log(y)
的对应元素相乘。最后,用 tf.reduce_sum
计算张量的全部元素的总和。(注意,这里的交叉熵不只仅用来衡量单一的一对预测和真实值,而是全部100幅图片的交叉熵的总和。对于100个数据点的预测表现比单一数据点的表现能更好地描述咱们的模型的性能。
如今咱们知道咱们须要咱们的模型作什么啦,用TensorFlow来训练它是很是容易的。由于TensorFlow拥有一张描述你各个计算单元的图,它能够自动地使用反向传播算法(backpropagation algorithm)来有效地肯定你的变量是如何影响你想要最小化的那个成本值的。而后,TensorFlow会用你选择的优化算法来不断地修改变量以下降成本。
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
在这里,咱们要求TensorFlow用梯度降低算法(gradient descent algorithm)以0.01的学习速率最小化交叉熵。梯度降低算法(gradient descent algorithm)是一个简单的学习过程,TensorFlow只需将每一个变量一点点地往使成本不断下降的方向移动。固然TensorFlow也提供了其余许多优化算法:只要简单地调整一行代码就可使用其余的算法。
TensorFlow在这里实际上所作的是,它会在后台给描述你的计算的那张图里面增长一系列新的计算操做单元用于实现反向传播算法和梯度降低算法。而后,它返回给你的只是一个单一的操做,当运行这个操做时,它用梯度降低算法训练你的模型,微调你的变量,不断减小成本。
如今,咱们已经设置好了咱们的模型。在运行计算以前,咱们须要添加一个操做来初始化咱们建立的变量:
init = tf.initialize_all_variables()
如今咱们能够在一个Session
里面启动咱们的模型,而且初始化变量:
sess = tf.Session() sess.run(init)
而后开始训练模型,这里咱们让模型循环训练1000次!
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})
该循环的每一个步骤中,咱们都会随机抓取训练数据中的100个批处理数据点,而后咱们用这些数据点做为参数替换以前的占位符来运行train_step
。
使用一小部分的随机数据来进行训练被称为随机训练(stochastic training)- 在这里更确切的说是随机梯度降低训练。在理想状况下,咱们但愿用咱们全部的数据来进行每一步的训练,由于这能给咱们更好的训练结果,但显然这须要很大的计算开销。因此,每一次训练咱们可使用不一样的数据子集,这样作既能够减小计算开销,又能够最大化地学习到数据集的整体特性。
那么咱们的模型性能如何呢?
首先让咱们找出那些预测正确的标签。tf.argmax
是一个很是有用的函数,它能给出某个tensor对象在某一维上的其数据最大值所在的索引值。因为标签向量是由0,1组成,所以最大值1所在的索引位置就是类别标签,好比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})
这个最终结果值应该大约是91%。
这个结果好吗?嗯,并不太好。事实上,这个结果是不好的。这是由于咱们仅仅使用了一个很是简单的模型。不过,作一些小小的改进,咱们就能够获得97%的正确率。最好的模型甚至能够得到超过99.7%的准确率!(想了解更多信息,能够看看这个关于各类模型的性能对比列表。)
比结果更重要的是,咱们从这个模型中学习到的设计思想。不过,若是你仍然对这里的结果有点失望,能够查看下一个教程,在那里你能够学习如何用TensorFlow构建更加复杂的模型以得到更好的性能!
原文地址:MNIST For ML Beginners 翻译:linbojin 校对: