TensorFlow实现Softmax Regression识别手写数字
MNIST数据集
MNIST(Mixed National Institute of Standards and Technology database)是一个非常简单的机器视觉数据集,它由几万张28像素×28像素的手写数字组成,这些图片只包含灰度值信息。我们的任务是对这些手写数字的图片进行分类,转换为0-9一共10类。
数据集包含:
- train-images-idx3-ubyte 训练数据图像 (60,000)
- train-labels-idx1-ubyte 训练数据label
- t10k-images-idx3-ubyte 测试数据图像 (10,000)
- t10k-labels-idx1-ubyte 测试数据label
对MNIST数据集加载时,TensorFlow为我们提供了一个方便的封装,可以直接加载MNIST数据成为我们期望的格式。
对MNIST数据集加载时,TensorFlow为我们提供了一个方便的封装,可以直接加载MNIST数据成为我们期望的格式。
import input_data |
查看MNIST数据集,其图像是28×28像素大小的灰度图片。空白部分全为0,有笔迹的地方根据颜色深浅有0-1之间的取值。相当于每个样本有784(28×28=784)维特征,因此丢弃图片的二维结构信息,把一张图片变为一个很长的1维向量。
训练数据共有55000张灰度图,则训练集的训练数据为60000×784的Tensor,训练数据的Label是一个60000×10的Tensor。对10个类进行one-hot编码,Label是一个10维的向量,只有对应数字正确的位置为1,其他都为0.如数字0,对应Label为[1,0,0,0,0,0,0,0,0,0];数字8对应Label为[0,0,0,0,0,0,0,0,1,0]。
Softmax Regression算法
输入和输出
我们的分类结果共有10个,当模型对一张图片进行预测时,Softmax Regression会对每一种类别估算一个概率。如预测是数字3的概率为80%,是数字5的概率为5%,最后概率最大的数字作为模型的输出。
当处理多分类任务时,通常需要使用Softmax Regression 模型。其工作原理很简单,可以判定为某类的特征相加,然后将这些特征转化为判定是这一类的概率。特征可以通过一些简单的方法得到,如对所有像素求加权和,权重是通过数据训练出来的。如某个像素的灰度值大代表很可能是数字n时,这个像素的权重就很大,如果某像素的灰度值大代表不太可能是数字n时,这个像素的权重可能是负的。
将这些特征写成如下公式:
\(feature_i = \sum\limits_{j}W_{i,j}x_j+bb_j\)
i代表第i类,j代表一张图片的第j个像素。bi是bias,即数据本身的一些偏置项,比如大部分数字都是0,那么0的特征对应的bias就会很大。
接下来对所有特征计算softmax。都是计算一个exp函数,再进行标准化,其目的是让所有的类别输出概率值和为1。\[softmax(x)=normalize(exp(x))\]其中判定为第i类的概率就可以由下面的公式得到:
\[softmax(x)_i=\frac{exp(x_i)}{\sum\limits_{j}exp(x_j)}\] 先对各个特征求exp函数,然后将其归一化,特征值越大的类,最后输出的概率也越大,使用exp函数保证了所有的特征值概率不可能小于或等于0,将Softmax Regression的计算过程可视化:
将图变为公式,元素相乘变为矩阵乘法:
\[
\begin{bmatrix}y_1\\y_2\\y_3\end{bmatrix}=softmax\left\{\begin{bmatrix}W_{1,1}&W_{1,2}&W_{1,3}\\W_{2,1}&W_{2,2}&W_{2,3}\\W_{3,1}&W_{3,2}&W_{3,3}\end{bmatrix}\cdot\begin{bmatrix}x_1\\x_2\\x_3\end{bmatrix}+\begin{bmatrix}b_1\\b_2\\b_3\end{bmatrix}\right\}
\]
则上述矩阵可以表示为:
\[y=softmax(Wx+b)\] 使用TensorFlow实现Softmax Regression。在python中使用Numpy进行矩阵的操作运算,首先创建一个输入的变量:
import tensorflow as tf |
None表示样本的个数,这里不限个数的输入。
接下来创建weights和biases变量,并使用公式计算y的值
W = tf.Variable(tf.zeros([784,10])) |
损失函数
为了训练模型,需要定义一个loss function来描述模型对问题的分类精度。为了得到最好的训练效果,希望loss越小越好。对于多分类问题,通常使用交叉熵(cross-entropy)作为损失函数:\[H_{y'}(y)=-\sum\limits_{i}y'_i\log(y_i)\]其中y是预测的概率分布,y’是真实的概率分布。在TensorFlow中定义cross-entropy:
y_ = tf.placeholder("float", [None,10])
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
随机梯度下降算法
TensorFlow可以根据定义好的计算题自动求导,并根据反向传播算法进行训练,在每一轮迭代时,更新参数来最小化loss。TensorFlow已经封装好了随机梯度下降算法的优化器,我们只需要直接调用tf.train.GradientDescentOptimizer
,设置函数的学习率以及优化的目标损失函数。
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
在这里,我们要求TensorFlow用梯度下降算法(gradient descent algorithm)以0.01的学习速率最小化交叉熵。梯度下降算法(gradient descent algorithm)是一个简单的学习过程,TensorFlow只需将每个变量一点点地往使成本不断降低的方向移动。
训练模型
定义好所有参数以及优化方法后,在Session中启动模型,并且初始化变量。
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
然后开始训练模型,这里我们让模型循环训练1000次。在每次训练时,随机抓取训练数据中的100个样本批处理样本点。
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})
使用一小部分的随机数据来进行训练被称为随机训练(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))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
最后,计算训练的模型在测试数据集上的正确率:
print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})
最终的结果值大约为91%,其正确率并不是很高,原因在于我们使用的模型十分简单,它并不是一个神经网络模型,在现在的模型基础上增加一层隐藏层,就会变成一个神经网络,且正确率更高。
备注:
完整的代码保存在”/home/student/public/deep_learning/TensorFlow/class2“中。