[tensorflow 入门] MNIST 入门
MNIST?
MNIST是一个入门级的计算机视觉数据集,它包含
- 各种手写数字图片。(images)
- 每一张图片对应的标签,告诉我们这个是数字几。(labels)
我们的任务便是:训练一个机器学习模型用于预测图片里面的数字。
HOW?
我们从官网下载数MNIST数据集,下载下来的数据集被分成两部分:
- 60000行的训练数据集(mnist.train)
- 10000行的测试数据集(mnist.test)。
其中:
- 训练数据集的图片是 mnist.train.images ,
- 训练数据集的标签是 mnist.train.labels。
images
每一张图片包含28X28个像素点。我们把这个数组展开成一个向量,长度是 28x28 = 784。因此,在MNIST训练数据集中,mnist.train.images 是一个形状为 [60000, 784] 的张量(tensor)
(60000张图 * 每张图784个像素点)
labels
使标签数据是”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] 的数字矩阵。
(60000个标签 ,每一个10维)
softmax回归
上面的图片显示了一个模型学习到的图片上每个像素对于特定数字类的权值;
其中对于数字0的权值,即[W
同理,对于数字1:[W
之所以维度为784,是因为images的数据集维度为784,W
所以最终W的维数为:[784, 10]
损失函数与梯度下降
一个非常常见的,非常漂亮的成本函数是“交叉熵”(cross-entropy)。
通过梯度下降的方式,使得损失函数下降最多的方向下降,以此来更新权值。
所谓梯度下降,便是以目标函数(此处为交叉熵)对多维的权值求导,求导出来的下降最大方向(是一个和权值同维数的向量)乘以步长,便是权值的更新向量。如下图:步长乘求导的梯度方向。
tensorflow代码实现
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
mnist是一个轻量级的类。它以Numpy数组的形式存储着训练、校验和测试数据集。同时提供了一个函数,用于在迭代中获得minibatch,后面我们将会用到。
# paras
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
一个变量(variable)代表着TensorFlow计算图中的一个值,能够在计算过程中使用,甚至进行修改。在机器学习的应用过程中,模型参数一般用Variable来表示。
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
这里的x和y并不是特定的值,相反,他们都只是一个占位符(placeholder)。
None表示其值大小不定,在这里作为第一个维度值,用以指代batch1的大小,意即x的数量不定。输出类别值y_也是一个2维张量,其中每一行为一个10维的one-hot向量,用于代表对应某一MNIST图片的类别。
y = tf.nn.softmax(tf.matmul(x, W) + b)
把向量化后的图片x和权重矩阵W矩阵相乘(matmul),加上偏置b,然后计算每个分类的softmax概率值。
# loss func
cross_entropy = -tf.reduce_sum(y_ * tf.log(y))
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
我们用梯度下降法让交叉熵下降,步长为0.01.
这一行代码实际上是用来往计算图上添加一个新操作,其中包括计算梯度,计算每个参数的步长变化,并且计算出新的参数值。
返回的train_step操作对象,在运行时会使用梯度下降来更新参数。因此,整个模型的训练可以通过反复地运行train_step来完成。
# init
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
变量需要通过seesion**初始化后,才能在session中使用**。这一初始化步骤为,为初始值指定具体值(本例当中是全为零),并将其分配给每个变量,可以一次性为所有变量完成此操作。
# train
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})
每一步迭代,加载50个训练样本,然后执行一次train_step,并通过feed_dict将x 和 y_张量占位符用训练训练数据替代。
correct_prediction = tf.equal(tf.arg_max(y, 1), tf.arg_max(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
求出y=y_ 的01矩阵,再求均值
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
之所以要feed_dict我猜是因为在循环批处理中
feed_dict={x: batch_xs, y_: batch_ys}
更改了x和y_的维度,所以要改回来。
maybe 所有的placeholder都要feed_dict才行?
- 在刚开始学习使用TF的过程中,我不是很理解什么是“batch”。也经常有人问,到底minibatch是干什么的?
然而这是一个在TF中,或者说很多DL的框架中很常见的词。
这个解释我觉得比较贴切也比较容易理解。引用如下:
深度学习的优化算法,说白了就是梯度下降。每次的参数更新有两种方式。
第一种,遍历全部数据集算一次损失函数,然后算函数对各个参数的梯度,更新梯度。这种方法每更新一次参数都要把数据集里的所有样本都看一遍,计算量开销大,计算速度慢,不支持在线学习,这称为Batch gradient descent,批梯度下降。
另一种,每看一个数据就算一下损失函数,然后求梯度更新参数,这个称为随机梯度下降,stochastic gradient descent。这个方法速度比较快,但是收敛性能不太好,可能在最优点附近晃来晃去,hit不到最优点。两次参数的更新也有可能互相抵消掉,造成目标函数震荡的比较剧烈。
为了克服两种方法的缺点,现在一般采用的是一种折中手段,mini-batch gradient decent,小批的梯度下降,这种方法把数据分为若干个批,按批来更新参数,这样,一个批中的一组数据共同决定了本次梯度的方向,下降起来就不容易跑偏,减少了随机性。另一方面因为批的样本数与整个数据集相比小了很多,计算量也不是很大。. ↩