TensorFlow实现 Softmax Regression 识别手写数字

这里使用 MNIST 数据集,它是由几万张 28像素 * 28像素 的手写数字组成。

流程:

  1. 参数初始化: w,b
  2. 定义算法公式:y = Softmax(Wx + b)
  3. 定义 loss function: cross-entropy
  4. 选定优化器,并指定优化器优化 loss:随机梯度下降SGD
  5. 随机选取样本,迭代训练
  6. 在测试集上对准确率进行评测

1. 加载数据集

TensorFlow 已经为我们提供了一个封装,可以直接加载。

1
2
3
from tensorflow.examples.tutorials.mnist import input_data
dataSet = input_data.read_data_sets("MNIST_data/", one_hot=True)

查看数据集的情况:

1
2
3
print(dataSet.train.images.shape,dataSet.train.labels.shape)
print(dataSet.test.images.shape,dataSet.test.labels.shape)
print(dataSet.validation.images.shape,dataSet.validation.labels.shape)

打印结果: 训练集有55000个样本,测试集有10000个样本,验证集有5000个样本。

1
2
3
4
5
6
7
8
➜ handswrite_mnist git:(master) ✗ python mnist.py
Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
(55000, 784) (55000, 10)
(10000, 784) (10000, 10)
(5000, 784) (5000, 10)

我们将在训练集上训练模型,在验证集上检验效果,最后在测试集上评测模型的效果。

2. 准备数据

首先需要将 28*28 的图片转为一维向量。

我们训练的特征是一个55000*784的 Tensor,第一个维度是图片的编号,第二个维度是图片中像素点的编号。

我们训练的label是一个55000*10的 Tensor,先对 10 种标签进行 one-hot 编码,label 是一个十维向量,数字 0 代表的是 [1,0,0,0,0,0,0,0,0,0],数字 n 代表对应位置的值为1。

3. 设计算法

这里使用 Softmax Regression 算法来训练分类模型:

当我们的模型对一张图片进行预测时,Softmax Regression 会对每一种类别估算一个概率,最后取概率最大的那个数字作为模型的输出结果。

原理:将可以判定为某类的特征相加,然后将这些特征转化为判定时这一类的概率。

我们可以将这些特征写成以下公式:

feature = Wx + b

接下来对特征计算 Softmax: 都计算一个 exp 函数,然后再进行标准化(让所有类别输出的概率值和为1)

Softmax(x) = normalize(exp(x))

y = Softmax(Wx + b)

4. 初始化

首先注册 session, 并创建一个 placeholder。 第二个参数代表 tensor 的 shape。

1
2
sess = tf.InteractiveSession()
x = tf.placeholder(tf.float32, [None, 784])

接下来给 weights 和 bias 创建 Variable 对象。其中 W 的 shape 是[784,10]。

1
2
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))

5. 实现算法

tf.nn 包含了大量神经网络的组件, softmax 是 tf.nn下的一个函数。tf.matmul 为矩阵乘法。

1
y = tf.nn.softmax(tf.matmul(x, W) + b)

6. 定义 loss 函数

我们需要定义一个 loss function 来描述模型的分类精度。

对于多分类问题,通常使用 cross-entropy 来作为 loss function。

H(y) = - y' * log(y) 

其中, y' 是真实结果,y是预测结果

reduce_sum 为求和, reduce_mean 对每个 batch 数据求均值。

1
2
3
yLabel = tf.placeholder(tf.float32, [None, 10])
cross_entropy = tf.reduce_mean(-tf.reduce_sum(yLabel * tf.log(y),
reduction_indices=[1]))

7. 优化算法

我们采用常见的随机梯度下降,tensorflow 根据我们定义的整个计算图自动求导,并根据反向传播算法进行训练,在每一轮迭代时更新参数来减少 loss 。

1
2
# 设置学习速率为0.5,优化目标设定为cross_entropy
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

8. 开始训练

随机取 100 个样本进行迭代训练。

1
2
3
for i in range(1000):
batch_xs, batch_ys = dataSet.train.next_batch(100)
train_step.run({x: batch_xs, yLabel: batch_ys})

9. 查看准确率

tf.cast 将 bool 型转换为 float32。tf.argmax(y, 1)是求各个预测的数字中概率最大的那一个。

1
2
correctPred = tf.equal(tf.argmax(y, 1), tf.argmax(yLabel, 1))
accuracy = tf.reduce_mean(tf.cast(correctPred, tf.float32))

10. 测试数据

1
print(accuracy.eval({x: dataSet.test.images, yLabel: dataSet.test.labels}))

11. 运行结果

准确率0.9162

即有 92% 的准确率

12. 其他

我们定义的各个公式其实只是 Computation Graph,在执行这行代码时,计算还没有实际发生,只有等调用 run 方法,并 feed 数据时计算才真正执行。 比如 cross_entropytrain_stepaccuracy 等都是计算图中的节点,而并不是数据结果,我们可以通过调用 run 方法执行这些节点或者说运算操作来调取结果。

13. 完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# -*- coding: utf-8 -*-
# !/usr/bin/env python
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
dataSet = input_data.read_data_sets("MNIST_data/", one_hot=True)
# print(dataSet.train.images.shape,dataSet.train.labels.shape)
# print(dataSet.test.images.shape,dataSet.test.labels.shape)
# print(dataSet.validation.images.shape,dataSet.validation.labels.shape)
sess = tf.InteractiveSession()
x = tf.placeholder(tf.float32, [None, 784])
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(x, W) + b)
yLabel = tf.placeholder(tf.float32, [None, 10])
cross_entropy = tf.reduce_mean(-tf.reduce_sum(yLabel * tf.log(y),
reduction_indices=[1]))
# 设置学习速率为0.5,优化目标设定为cross_entropy
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
tf.global_variables_initializer().run()
for i in range(1000):
batch_xs, batch_ys = dataSet.train.next_batch(100)
train_step.run({x: batch_xs, yLabel: batch_ys})
correctPred = tf.equal(tf.argmax(y, 1), tf.argmax(yLabel, 1))
accuracy = tf.reduce_mean(tf.cast(correctPred, tf.float32))
# 将测试数据输入到评测流程
print(accuracy.eval({x: dataSet.test.images, yLabel: dataSet.test.labels}))