深度学习入门(二)TensorFlow系统学习第二篇,手写数字识别

Nov 30, 2017


最近做一个索尼公司的商业项目,要用到深度学习的内容,于是这里记录一些东西,方便查阅..

环境:macOS-10.13.4, Python-3.6, Tensorflow-1.4.1

参考: 《TensorFlow实战》

下载数据并进行测试

说明:这个是TensorFlow自带的一个示例,可以通过这些示例去学习各个模型的使用

想做什么:比如下面的这个图片,我们想让计算机识别出来它是1,同理其他的数字也一样,识别手写数字

image

前提:安装好了TensorFlow并联网…电脑速度不要太慢就可以了

运行下面的python代码:

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

print(mnist.train.images.shape, mnist.train.labels.shape)
print(mnist.test.images.shape, mnist.test.labels.shape)
print(mnist.validation.images.shape, mnist.validation.labels.shape) 

服务器上等待了大约10分钟,得到下列终端输出:

Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
Extracting MNIST_data/train-images-idx3-ubyte.gz
Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
((55000, 784), (55000, 10))
((10000, 784), (10000, 10))
((5000, 784), (5000, 10))

前四个代表着在下载训练集和其他的必需的东西。

后面的三行,即上面代码里的三个print,查看mnist的情况。

可以发现,mnist的训练集有55000个样本,测试集10000个样本,验证集5000个样本,每一个样本都有对应的标注信息。

这个集合里面的元素,每一个都是一个28×28的图片,28×28的结果是784,即一个图片有784个像素点,所以上面的输出里带有784,是把每一张图片作为了一个784长度的向量来对待的。

而训练的标签数据是55000×10的Tensor,这个10呢,就是对10个数字0123456789进行了独热编码得到的10个种类。比如:

0的标签是[1,0,0,0,0,0,0,0,0,0]

1的标签是[0,1,0,0,0,0,0,0,0,0]

2的标签是[0,0,1,0,0,0,0,0,0,0]

3的标签是[0,0,0,1,0,0,0,0,0,0]

4的标签是[0,0,0,0,1,0,0,0,0,0]

5的标签是[0,0,0,0,0,1,0,0,0,0]

6的标签是[0,0,0,0,0,0,1,0,0,0]

7的标签是[0,0,0,0,0,0,0,1,0,0]

8的标签是[0,0,0,0,0,0,0,0,1,0]

9的标签是[1,0,0,0,0,0,0,0,0,1]

所以,最后我们准备的数据就是:

55000个784长度的向量,表示55000个样本图片,和配套的55000个标签,每个标签是长度10的向量,表示它的类别,这个作为训练集

10000个上述数据作为测试集

5000个上述数据作为验证集,在验证集上检验效果并决定何时完成训练

数据准备好了,接下来就是设计深度学习模型算法了

Softmax Regression简单概述

在这里我们用一个叫做Softmax Regression的模型来训练手写数字识别的分类模型。

工作原理:比如一张28*28的数字图片,模型会预测出它是10种类别的概率,然后输出概率最大的。比如一个模糊的写有数字5的图片,模型可能分析出来它是1的概率是10%, 2的概率是20%, …, 5的概率是50%, …等等,最后输出一个最大的概率所表示的类别,即5。

即使是卷积神经网络还是循环神经网络,如果是分类模型,最后一层同样是Softmax Regression,它的原理就是判断某类的特征相加,然后把这些特征转化为概率就可以了。

比如这里,我们可以对第i类(一共是十个类别嘛),构造一个特征公式:

\(feature_i = \sum_{j}^{}W_{i, j}*x_j + b_i\)

其中i表示第i类,j表示一张图片的第j个像素,bi表示bias表示偏差,要训练的。

先别管这个公式怎么来的,然后我们继续算一个softmax, 这个是根据所有的特征计算到的,很简单,就是标准化一下,让所有类别的概率相加为1:

\(softmax(x) = normalize(exp(x))\)

那么判断一个图片是几的话,就直接根据下面的公式算出来的就是概率了:

\(softmax(x)i = exp(x_i) / \sum{j}^{}exp(x_i)\)

所以一张图片属于谁的概率就能通过上面的式子来算了,我们只需要训练W和b, 让它得到的结果尽可能的准确,这个模型就能完成了。

所以简单的说,核心可以写成这样的一个公式:

\(y = softmax(Wx + b)\)

然后就是训练W和b这两个参数,让它们的结果的准确率更高。

于是我们可以写代码了。

实现

首先建立一个默认的Session,后面的运算都是建立在这个Session上,然后创建一个数据输入口placeholder:

import tensorflow as tf
sess = tf.InteractiveSession()
x = tf..placeholder(tf.float32, [None, 784])

sess就是默认的会话口,x就是数据输入口,后面的[None, 784]表示输入的数据可以无限,每个输入都是784维的向量

然后建立w和b,用Tensor对象去描述:

w = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

w中的784是特征维数,10是类别。b中需要一个10,全部初始化为0。

核心公式:

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

也就是上面提到过的\(y = softmax(Wx + b)\)

为了训练模型,我们有了训练集,自然还是需要一个东西去表示模型预测的结果和真实值的偏差,这个偏差越小自然就越准确,训练的目的,就是为了减少偏差。

定义cross-entropy减少偏差,这个具体概念以后再说:

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

这个y_表示的是真实的标签数据,主要就是和算出来的y进行比较,来算出下面的cross_entropy即两者的一个偏差。

然后我们就要想办法减少这个偏差呀,TensorFlow给我们一套优化器去减少偏差,我们这里用GradientDescentOptimizer来优化:

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

然后让全局参数初始化:

tf.global_variables_initializer().run()

最后开始真正的训练步骤:

for i in range(1000):
	batch_xs, batch_ys = mnist.train.next_batch(100)
	train_step.run({x: batch_xs, y_: batch_ys})

for就是训练的次数,第一行就是得到训练集里的数据,第二行就是输入数据进行训练,没错,这个train_step就是我们之前定义的优化器,里面两个参数x和y_, x表示输入的数据,y_表示输入的数据标签,两个进行对比,训练w和b让两者的偏差变小。

这样训练就完成了,我们验证一下准确率吧:

correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(accuracy.eval({x: mnist.text.images, y_: mnist.test.labels}))

就结束了。

总结

书上说这个事情啊,一共就四个步骤:

Step1: 定义算法公式,也就是上面的那个y = 什么什么w什么什么x什么什么b的那个工数

Step2: 定义loss,选定优化器,指定优化器优化这个loss,也就是上面的GradientDescentOptimizer,传入误差值和需要减少的量即loss

Step3: 迭代对数据进行训练,也就是上面的那个for循环,每一次输入数据和标签,优化器减少两者的误差

Step4: 在验证集上对准确率评测

在我看来所有的一切不过这些过程:

Step1: 收集数据,包括数据和标签,训练集和测试集。

Step2: 构建模型,包括模型的数据输入口,模型的算法

Step3: 根据模型的算法的结果,和标签进行比对(比如作差得到一个loss),然后弄一个优化器去调整模型的参数值(w, b)去减少这个误差

Step4: 迭代去训练几千次几万次,最终那验证集去测试一下准确率

所有代码

# 获取数据
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

print(mnist.train.images.shape, mnist.train.labels.shape)
print(mnist.test.images.shape, mnist.test.labels.shape)
print(mnist.validation.images.shape, mnist.validation.labels.shape) 


import tensorflow as tf

# 定义默认会话Session
sess = tf.InteractiveSession()

# 定义数据输入的入口
x = tf.placeholder(tf.float32, [None, 784])

# 定义weight和bias
w = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

# 定义算法公式
y = tf.nn.softmax(tf.matmul(x, w) + b)

# 定义标签输入的入口
y_ = tf.placeholder(tf.float32, [None, 10])

# 定义误差的计算方式
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))

# 定义优化器去减少误差
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

# 全局变量初始化
tf.global_variables_initializer().run()

# 训练一千次
for i in range(1000):
	# 输入数据,样本数据和标签
	batch_xs, batch_ys = mnist.train.next_batch(100)
	# 优化器进行训练,输入样本数据和标签
	train_step.run({x: batch_xs, y_: batch_ys})

# 定义预测正确率
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))

# 得到准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

# 打印结果
print(accuracy.eval({x: mnist.test.images, y_: mnist.test.labels}))

注意

其中全局变量初始化,即:

tf.global_variables_initializer().run()

在老版本中不适用,如果报错的话,请使用initialize_all_variables

这个带all的已经在2017年3月2日弃用。

然后如果报错是编码问题,请去掉上面所有的中文注释

最终输出结果是准确率。我的是0.91左右。