Tensorflow实战(一)——MNIST

入门篇
Tensorflow入门(二)——基本操作
Tensorflow入门(三)——计算图、会话
Tensorflow入门(四)——激活函数
Tensorflow入门(五)——损失函数

  • 引用

    https://www.cnblogs.com/lizheng114/p/7439556.html

    https://www.cnblogs.com/xiaoyh/p/10813413.html

    https://zhuanlan.zhihu.com/p/30670463

本节以整理为主,使用 MNIST 数据集训练一个可以识别数据的深度学习模型来帮助识别手写数字。

1. MNIST

1.1 MNIST 简介

首先介绍一下MNIST数据集。

MNIST 是一个入门级计算机视觉数据集,由6万张训练图片和1万张测试图片构成的,每张图片都是28*28大小(如下图),而且都是黑白色构成(这里的黑色是一个0-1的浮点数,黑色越深表示数值越靠近1),这些图片是采集的不同的人手写从0到9的数字。

TensorFlow将这个数据集和相关操作封装到了库中。

img

上图就是4张MNIST图片。这些图片并不是传统意义上的png或者jpg格式的图片,因为png或者jpg的图片格式,会带有很多干扰信息(如:数据块,图片头,图片尾,长度等等),这些图片会被处理成很简易的二维数组,如图:

img

可以看到,矩阵中有值的地方构成的图形,跟左边的图形很相似。之所以这样做,是为了让模型更简单清晰。特征更明显。

原始的MNIST 数据库一共包含下面4 个文件:

文件名大小用途
train-images-idx3-ubyte.gz≈ 9.45 MB训练图像数据
train-labels-idx1-ubyte.gz≈ 0.03 MB训练图像标签
t10k-images-idx3-ubyte.gz≈ 1.57 MB测试图像数据
t10k-labels-idx1-ubyte.gz≈ 4.4 KB测试图像标签

图像数据是指很多张手写字符的图像,图像的标签是指每一张图像实际对应的数字是几,也就是说,在MNIST 数据集中的每一张图像都事先标明了对应的数字。

训练图像一共有60000 张,供研究人员训练出合适的模型。测试图像一共有10000 张,供研究人员测试训练的模型的性能。

1.2 MNIST 下载

在 TensorFlow 中提供了这个数据集,我们可以用如下方法进行导入:

# 从tensorflow.examples.tutorials.mnist引入模块。这是TensorFlow为了教学MNIST而提前编制的程序
from tensorflow.examples.tutorials.mnist import input_data

# 从MNIST_data/中读取MNIST数据。这条语句在数据不存在时,会自动执行下载
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)

执行完上述语句后,可在制定好的MNIST_data文件夹中看到以下4个文件:

1.3 MNIST 使用

MNIST对象中各个属性的含义和大小如下表所示:

属性名内容大小
mnist.train.images训练图像(55000, 784)
mnist.train.labels训练标签(55000, 10)
mnist.validation.images验证图像(5000, 784)
mnist.validation.labels验证标签(5000, 10)
mnist.test.images测试图像(10000, 784)
mnist.test.labels测试标签(10000, 10)

在TensorFlow 中,变量mnist.train.images 是训练样本, 它的形状为(55000, 784)。其中,55000 是训练图像的个数,而784 实际为单个样本的维数,即 784 个像素点,我们可以把它展开形成一个向量,即长度为 784 的向量。即每张图片都由一个784 维的向量表示( 784 正好等于28×28 ) 。

所以训练集我们可以转化为 [55000, 784] 的向量,第一维就是训练集中包含的图片个数,第二维是图片的像素点表示的向量。

可以使用以下代码打印出第0 张训练图片对应的向量表示:

# 打印出第0张图片的向量表示
print(mnist.train.images[0,:])
 
# 打印出第0幅图片的标签
print(mnist.train.labels[0, :])

1.4 图像标签的one-hot表示

变量mnist. train.labels 表示训练图像的标签,它的形状是(55000, 10)。原始的图像标签是数字0~9 ,我们完全可以用一个数字来存储图像标签,但为什么这里每个训练标签是一个10 维的向量呢?其实,这个10 维的向量是原先类别号的独热( one-hot )表示。所谓独热表示,就是“一位高效编码” 。我们用N维的向量来表示N 个类别,每个类别占据独立的一位,任何时候独热表示中只再一位是1 ,其他都为0 ,如下表所示:

原始表示
(0~9,共10个类别)
独热表示
(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)
…………
9(0, 0, 0, 0, 0, 0, 0, 0, 0, 1)

2. Softmax 回归

2.1 Softmax 简介

Softmax 可以看成是一个激励(activation)函数或者链接(link)函数,把我们定义的线性函数的输出转换成我们想要的格式,也就是关于10个数字类的概率分布。因此,给定一张图片,它对于每一个数字的吻合度可以被 Softmax 函数转换成为一个概率值。Softmax 函数可以定义为:
s o f t m a x ( x ) = n o r m a l i z e ( e x p ( x ) ) softmax(x)=normalize(exp(x)) softmax(x)=normalize(exp(x))
展开等式右边的子式,可以得到:
s o f t m a x ( x ) i = e x p ( x i ) ∑ j e x p ( x j ) softmax(x)_i=\frac{exp(x_i)}{\sum_{j}exp(x_j)} softmax(x)i=jexp(xj)exp(xi)
判断一张图片中的动物是什么,可能的结果有三种,猫、狗、鸡,我们可以经过计算得出它们分别的得分为 3.2、5.1、-1.7,Softmax 的过程首先会对各个值进行次幂计算,分别为 24.5、164.0、0.18,然后计算各个次幂结果占总次幂结果的比重,这样就可以得到 0.13、0.87、0.00 这三个数值,所以这样我们就可以实现差别的放缩,即好的更好、差的更差。

如果要进一步求损失值可以进一步求对数然后取负值,这样 Softmax 后的值如果值越接近 1,那么得到的值越小,即损失越小,如果越远离 1,那么得到的值越大。

2.2 Softmax 实现

首先导入 TensorFlow,命令如下:

import tensorflow as tf

接下来我们指定一个输入,在这里输入即为样本数据,如果是训练集那么则是 55000 x 784 的矩阵,如果是验证集则为 5000 x 784 的矩阵,如果是测试集则是 10000 x 784 的矩阵,所以它的行数是不确定的,但是列数是确定的。

所以可以先声明一个 placeholder 对象:

x = tf.placeholder(tf.float32, [None, 784])

这里第一个参数指定了矩阵中每个数据的类型,第二个参数指定了数据的维度。

接下来我们需要构建第一层网络,表达式如下:
e v i d e n c e i = ∑ j W i , j x j + b i evidence_i=\sum_jW_{i,j}x_j+b_i evidencei=jWi,jxj+bi
这里实际上是对输入的 x 乘以 w 权重,然后加上一个偏置项作为输出,而这两个变量实际是在训练的过程中动态调优的,所以我们需要指定它们的类型为 Variable,代码如下:

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

接下来需要实现的就是上图所述的公式了:

output = tf.matmul(x, W) + b

最后我们加入 softmax 函数对各个得分进行概率求值:

y = tf.nn.softmax(output)

通过上面几行代码我们就已经把模型构建完毕了,结构非常简单。

3. 损失函数

3.1 损失函数简介

为了训练我们的模型,我们首先需要定义一个指标来评估这个模型是好的。其实,在机器学习,我们通常定义指标来表示一个模型是坏的,这个指标称为成本(cost)或损失(loss),然后尽量最小化这个指标。但是这两种方式是相同的。

一个非常常见的,非常漂亮的成本函数是“交叉熵”(cross-entropy)。交叉熵产生于信息论里面的信息压缩编码技术,但是它后来演变成为从博弈论到机器学习等其他领域里的重要技术手段。

y 是我们预测的概率分布, y’ 是实际的分布,比较粗糙的理解是,交叉熵是用来衡量我们的预测用于描述真相的低效性。

3.2 损失函数实现

我们可以首先定义 y’,它的表达式是:

y_ = tf.placeholder(tf.float32, [None, 10])

接下来我们需要计算它们的交叉熵,代码如下:

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

首先用 reduce_sum() 方法针对每一个维度进行求和,reduction_indices 是指定沿哪些维度进行求和。

然后调用 reduce_mean() 则求平均值,将一个向量中的所有元素求算平均值。

这样我们最后只需要优化这个交叉熵就好了。

所以这样我们再定义一个优化方法:

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

这里使用了 GradientDescentOptimizer,在这里,我们要求 TensorFlow 用梯度下降算法(gradient descent algorithm)以 0.5 的学习速率最小化交叉熵。梯度下降算法(gradient descent algorithm)是一个简单的学习过程,TensorFlow只需将每个变量一点点地往使成本不断降低的方向移动即可。

4. 运行模型

定义好了以上内容之后,相当于我们已经构建好了一个计算图,即设置好了模型,我们把它放到 Session 里面运行即可:

with tf.Session() as sess:
    tf.global_variables_initializer().run()
    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})

该循环的每个步骤中,我们都会随机抓取训练数据中的100个批处理数据点,然后我们用这些数据点作为参数替换之前的占位符来运行train_step。

5. 测试模型

那么我们的模型性能如何呢?

首先让我们找出那些预测正确的标签。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))

这行代码会给我们一组布尔值。为了确定正确预测项的比例,我们可以把布尔值转换成浮点数,然后取平均值。

例如,[True, False, True, True] 会变成 [1,0,1,1] ,取平均值后得到 0.75

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

最后,我们计算所学习到的模型在测试数据集上面的正确率。

print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

这个最终结果值应该大约是92%。

6. 完整代码与解析

# 导入
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# 加载mnist
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)

# x是特征值,28*28=784
x = tf.placeholder(tf.float32, [None, 784])

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

output = tf.matmul(x, w) + b

y = tf.nn.softmax(output)

# y_是图片对应的真实值
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)

with tf.Session() as sess:
    tf.global_variables_initializer().run()

    # 训练数据
    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})

    # 取得y的最大概率对应的数组索引来和y_的数组索引对比,如果索引相同,则表示预测正确
    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}))

x(图片的特征值):这里使用了一个28×28=784列的数据来表示一个图片的构成,也就是说,每一个点都是这个图片的一个特征,这个其实比较好理解,因为每一个点都会对图片的样子和表达的含义有影响,只是影响的大小不同而已。

w(特征值对应的权重):这个值很重要,因为我们深度学习的过程,就是发现特征,经过一系列训练,从而得出每一个特征对结果影响的权重,我们训练,就是为了得到这个最佳权重值。

b(偏置量):去线性化

y(预测的结果):单个样本被预测出来是哪个数字的概率,比如:有可能结果是[ 1.07476616 -4.54194021 2.98073649 -7.42985344 3.29253793 1.96750617 8.59438515 -6.65950203 1.68721473 -0.9658531 ],则分别表示是0, 1, 2, 3, 4, 5, 6, 7, 8, 9的概率,然后会取一个最大值来作为本次预测的结果,对于这个数组来说,结果是6(8.59438515)

y_(真实结果):来自MNIST的训练集,每一个图片所对应的真实值,如果是6,则表示为:[0 0 0 0 0 0 1 0 0 0]

再下面两行代码是损失函数(交叉熵)和梯度下降算法,通过不断的调整权重和偏置量的值,来逐步减小根据计算的预测结果和提供的真实结果之间的差异,以达到训练模型的目的。

mnist.train.next_batch(100) 是从训练集里一次提取100张图片数据来训练,然后循环1000次,以达到训练的目的。

mnist.test.imagesmnist.test.labels 是测试集,用来测试。accuracy是预测准确率。

  • 7
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值