MNIST数据集
导入数据
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
- 60000行的训练数据集和10000行的测试数据集
每一个MNIST数据单元有两部分组成:一张包含手写数字的图片和一个对应的标签。我们把这些图片设为“xs”,把这些标签设为“ys”。
每一张图片包含28X28个像素点。
我们把这个数组展开成一个向量,长度是 28x28 = 784。
展平图片的数字数组会丢失图片的二维结构信息。这显然是不理想的,最优秀的计算机视觉方法会挖掘并利用这些结构信息,我们会在后续教程中介绍。
- 因此,在MNIST训练数据集中,**mnist.train.images **是一个形状为 [60000, 784] 的张量,第一个维度数字用来索引图片,第二个维度数字用来索引每张图片中的像素点。在此张量里的每一个元素,都表示某张图片里的某个像素的强度值,值介于0和1之间。
- 相对应的MNIST数据集的标签是介于0到9的数字,用来描述给定图片里表示的数字。我们使标签数据是"one-hot vectors"。 一个one-hot向量除了某一位的数字是1以外其余各维度数字都是0。标签0将表示成([1,0,0,0,0,0,0,0,0,0,0])。mnist.train.labels 是一个 [60000, 10] 的数字矩阵。
Softmax回归介绍
为了得到一张给定图片属于某个特定数字类的证据(evidence),我们对图片像素值进行加权求和。如果这个像素具有很强的证据说明这张图片不属于该类,那么相应的权值为负数,相反如果这个像素拥有有利的证据支持这张图片属于这个类,那么权值是正数。
下面的图片显示了一个模型学习到的图片上每个像素对于特定数字类的权值。红色代表负数权值,蓝色代表正数权值。
- 也需要加入一个额外的偏置量(bias),因为输入往往会带有一些无关的干扰量。因此对于给定的输入图片 x 它代表的是数字 i 的证据可以表示为
- 其中Wij代表权重,bi 代表数字 i 类的偏置量,xj 代表给定图片 x 的像素索引用于像素求和。然后用softmax函数可以把这些证据转换成概率 y
-
softmax可以看成是一个激活(activation)函数
- 假设模型里的权值不可以是0值或者负值。Softmax然后会正则化这些权重值,使它们的总和等于1,以此构造一个有效的概率分布。
- 可以写成更加紧凑的方式:
实现回归模型
- 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表示此张量的第一个维度可以是任何长度的。)
- 我们的模型也需要权重值和偏置量,也可以用占位符,但是有更好的表达方式:
W = tf.Variable(tf.zeros([784,10]))#W的维度是[784,10],因为我们想要用784维的图片向量乘以它以得到一个10维的证据值向量
b = tf.Variable(tf.zeros([10]))#b的形状是[10],所以我们可以直接把它加到输出上面。
一个Variable
代表一个可修改的张量,存在在TensorFlow的用于描述交互性操作的图中。它们可以用于计算输入值,也可以在计算中被修改。对于各种机器学习应用,一般都会有模型参数,可以用Variable
表示。
- 现在,我们可以实现我们的模型啦。只需要一行代码!
y = tf.nn.softmax(tf.matmul(x,W) + b)#输出的y跟x保持同样的第一维的个数
用tf.matmul(x,W)
表示x
乘以W
,这里x
是一个2维张量拥有多个输入。然后再加上b
,把和输入到tf.nn.softmax
函数里面。
训练模型
- 为了训练我们的模型,我们首先需要定义一个指标来评估这个模型是好的。指标称为成本(cost)或损失(loss),然后尽量最小化这个指标。
- 一个非常常见的,非常漂亮的成本函数是“交叉熵”(cross-entropy)
y 是我们预测的概率分布, y' 是实际的分布(我们输入的one-hot vector)。
比较粗糙的理解是,交叉熵是用来衡量我们的预测用于描述真相的低效性。
- 为了计算交叉熵,我们首先需要添加一个新的占位符用于输入正确值:
y_ = tf.placeholder("float", [None,10]) # y_就是上面的y',是一个输入的固定的值
- 计算交叉熵:
cross_entropy = -tf.reduce_sum(y_*tf.log(y))# 是一个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)#定义了一个训练方法,在后面作为session.run()的参数
这里TensorFlow用梯度下降算法(gradient descent algorithm)以0.01的学习速率最小化交叉熵。
当然TensorFlow也提供了其他许多优化算法:只要简单地调整一行代码就可以使用其他的算法。
TensorFlow在这里实际上所做的是,它会在后台给描述你的计算的那张图里面增加一系列新的计算操作单元用于实现反向传播算法和梯度下降算法。然后,它返回给你的只是一个单一的操作,当运行这个操作时,它用梯度下降算法训练你的模型,微调你的变量,不断减少成本。
- 现在,我们已经设置好了我们的模型。在运行计算之前,我们需要添加一个操作来初始化我们创建的变量:
init = tf.initialize_all_variables()#定义一个初始化的函数动作,将前面定义的W和b初始化为零
- 现在我们可以在一个Session里面启动我们的模型,并且初始化变量:
sess = tf.Session()#定义一个session
sess.run(init)#启动这个session并初始化变量
会话(session):
客户端通过创建 会话 (session)和 TensorFlow 系统进行交互。一个由会话接口提供的主要的操作就是 Run ,以需要计算的输出名称和替换某些输出节点的张量的操作集合作为其参数输入。通过控制 Run 的参数,TensorFlow 的实现可以计算所有节点的必须执行传递闭包来计算需要的输出,然后安排执行合适节点来保证他们的依赖关系(即如果要计算到参数那一步,前面必须的步骤会自动完成)。大多数 TensorFlow 的使用都是针对一个图启动一个会话,然后执行整个图或者通过 Run 调用来执行分离的子图数千或者数百万次。
- 然后开始训练模型,这里我们让模型循环训练1000次!
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)#随机抓取训练数据mnist中的100个批处理数据点
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})#用这些数据点作为参数替换之前的占位符来运行train_step
设计placeholder
节点的唯一的意图就是为了提供数据供给(feeding)的方法。placeholder
节点被声明的时候是未初始化的, 也不包含数据, 如果没有为它供给数据, 则TensorFlow运算的时候会产生错误, 所以千万不要忘了为placeholder
提供数据。
评估我们的模型
首先让我们找出那些预测正确的标签。
tf.argmax
是一个非常有用的函数,它能给出某个tensor
对象在某一维上的其数据最大值所在的索引值(就是对应的数字)。tf.argmax(y,1)
返回的是模型对于任一输入x
预测到的标签值,而tf.argmax(y_,1)
代表正确的标签,我们可以用tf.equal
来检测我们的预测是否真实标签匹配(索引位置一样表示匹配)。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))#比较两个有y和y_对应的标签序列,返回一个bool类型的序列
- 这行代码会给我们一组布尔值。为了确定正确预测项的比例,我们可以把布尔值转换成浮点数,然后取平均值。例如,
[True, False, True, True]
会变成[1,0,1,1]
,取平均值后得到0.75
.
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))#cast()转换类型,reduce_mean()获得平均值
- 最后,我们计算所学习到的模型在测试数据集上面的正确率。
print (sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
我再来解读一下最后的run
函数。
首先函数方法是accuracy
,往前看发现引用了一个correct_prediction
函数。查看correct_prediction
函数,里面有两个参数y
和y_
。往前看,y_
是一个placeholder
,y_
确定要传参数进来了。而y
是一个函数式,查看此函数式发现需要用到x
,再往前找到x
定义的地方发现x
是一个placeholder
,也需要传参数进来,就此终于形成闭包。最终是两个placeholder
,并且次序是先x
后y_
,因此用feed_dict={x: mnist.test.images, y_: mnist.test.labels}