深入研究MNIST
发布日期:2021-06-29 16:00:05 浏览次数:2 分类:技术文章

本文共 8122 字,大约阅读时间需要 27 分钟。

加载MNIST数据

如果您正在复制和粘贴本教程的代码,请从这里开始,下面这两行代码将自动下载并读取数据:

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

这里mnist是一个轻量级数据集,它将训练、验证和测试集存储为NumPy数组。 它还提供了一个函数来迭代数据minibatches,我们将在下面使用。

启动TensorFlow InteractiveSession

TensorFlow依靠高效的C ++后端来完成它的计算。 与此后端的连接称为会话。 TensorFlow程序的常见用法是首先创建一个图形,然后在会话中启动它。

在这里,我们使用便利的InteractiveSession类,这使得TensorFlow更加灵活地了解如何构建代码。 它允许您将构建计算图的操作与运行图的操作交错。 在IPython等交互式环境中工作时,这特别方便。 如果您没有使用InteractiveSession,则应在开始会话并启动图之前构建整个计算图。

import tensorflow as tfsess = tf.InteractiveSession()

计算图

为了在Python中进行有效的数值计算,我们通常使用像NumPy这样的库来执行复杂的操作,例如Python之外的矩阵乘法,使用另一种语言实现的高效代码来完成这些操作。不幸的是,每一次操作都会返回到Python,这仍然会有很多开销。如果要在GPU上运行计算或以分布式方式运行计算,则传输数据的成本很高,所以此开销尤其糟糕。

TensorFlow也在Python之外进行繁重的工作,但是为了避免这种开销,还需要进一步的工作。 TensorFlow不是独立于Python运行的一个复杂操作,而是让我们描述一个完全在Python之外运行的交互操作图。 (像这样的方法可以在几个机器学习库中看到)

因此,Python代码的作用是构建这个外部计算图,并指定运行图的每个部分。

建立一个Softmax回归模型

在本节中,我们将建立一个单线性层的softmax回归模型。在下一节中,我们将扩展到具有多层卷积网络的softmax回归的情况。

占位符

我们通过为输入图像和目标输出类创建节点来开始构建计算图。

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

这里xy_不是特定的值。相反,它们都是占位符 - 当我们要求TensorFlow运行一个计算时,我们会输入一个值。

输入图像x将由浮点数的二维张量组成。这里我们给它赋予一个[None,784]的形状,其中784是一个单一的28×28像素MNIST图像的维度,None表示与batch大小相对应的第一维度可以是任意大小。目标输出类别y_也将由二维张量组成,其中每一行是一个唯一的10维向量,指示对应的MNIST图像是哪个数字(0到9)。

占位符的形状参数是可选的,但它允许TensorFlow自动捕捉源自不一致张量形状的错误。

变量

我们现在定义权重W,并为我们的模型赋予偏差b。我们可以把这些看作是额外的输入,但是TensorFlow有一个更好的方法来处理它们:Variable(变量)。变量是存在于TensorFlow的计算图中的一个值。它可以被使用,甚至被计算修改。在机器学习应用中,通常将模型参数设置为变量。

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

我们将调用中的每个参数的初始值传递给tf.Variable。 在这种情况下,我们将Wb初始化为全0的张量。 W是784x10矩阵(因为我们有784个输入特征和10个输出),b是10维向量(因为我们有10个数字)。

在会话中使用变量之前,必须使用该会话进行初始化变量。 这一步将已经指定的初始值(在这里,张量全零),分配给每个变量。 这可以一次完成所有变量的赋值:

sess.run(tf.global_variables_initializer())

预测类和损失函数

我们现在可以实现我们的回归模型。 只需要一行! 我们将向量化的输入图像x乘以权重矩阵W,加上偏差b

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

我们可以很容易地指定一个损失函数。 损失函数描述模型的预测值在一个例子上有多差; 在训练所有的例子时,我们尽量减少损失函数的值。 在这里,我们的损失函数是目标和应用于模型预测的softmax激活函数之间的交叉熵。 正如在初学者教程中,我们使用同样的公式:

cross_entropy = tf.reduce_mean(    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

请注意,tf.nn.softmax_cross_entropy_with_logits在模型的非标准化模型预测中内部应用softmax,并在所有类中进行求和,tf.reduce_mean取这些和的平均值。

训练模型

现在我们已经定义了我们的模型和训练损失函数,接下来使用TensorFlow进行训练是很简单的。 由于TensorFlow知道整个计算图,因此可以使用自动微分来查找相对于每个变量的损失的梯度。 TensorFlow有多种内置的优化算法。 对于这个例子,我们将使用最陡的梯度下降,步长为0.5,降低交叉熵。

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

TensorFlow在这一行中实际上是在计算图中添加新的操作。 这些操作包括计算梯度、计算参数、更新步骤以及将更新步骤应用于参数。

运行train_step时返回的参数会用于更新梯度下降。 因此,训练模型可以通过重复运行train_step来完成。

for _ in range(1000):  batch = mnist.train.next_batch(100)  train_step.run(feed_dict={x: batch[0], y_: batch[1]})

我们在每次训练迭代中加载100个训练样例。 然后我们运行train_step操作,使用feed_dict将训练样例中的占位符张量xy_替换。 请注意,您可以使用feed_dict来替换计算图中的任何张量 - 它不仅限于占位符。

评估模型

我们的模型有多好?

首先我们要弄清楚我们在哪里预测了正确的标签。 tf.argmax是一个非常有用的函数,它可以为您提供某个轴上张量的最大输入索引。 例如,tf.argmax(y,1)是我们模型认为对每个输入最有可能的标签,而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, tf.float32))

最后,我们可以评估我们的测试数据的准确性。 这里应该是大约92%的准确率。

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

构建一个多层卷积网络

在MNIST上获得92%的准确性是不好的。 这几乎是令人尴尬的坏事。 在这一节中,我们将解决这个问题,从一个非常简单的模型跳到一些中等复杂的问题:一个小的卷积神经网络。 这将使我们达到约99.2%的准确性 - 不是最先进的,但是值得学习。

下面是一个用TensorBoard创建的关于我们将要构建的模型的图表:

权重初始化

要创建这个模型,我们需要创建很多权重和偏差。 一般应该用少量的噪声初始化权重,以防止对称性破坏,并防止0梯度。 由于我们使用的是ReLU神经元,为了避免“死神经元”,初始化这些神经元是一个很好的做法。 在我们构建模型的时候不要重复这样的操作,而是创建两个函数来为我们做这件事。

def weight_variable(shape):  initial = tf.truncated_normal(shape, stddev=0.1)  return tf.Variable(initial)def bias_variable(shape):  initial = tf.constant(0.1, shape=shape)  return tf.Variable(initial)

卷积和池化

TensorFlow也为卷积和池化操作提供了很大的灵活性。 我们如何处理边界? 我们的步幅是多少? 在这个例子中,我们选择vanilla版本。 我们使步幅大小为1,并在周围填充零,以便输出与输入大小相同。 我们的pooling是超过2x2的max pooling。 为了保证我们的代码更清晰,我们也将这些操作抽象为函数。

def conv2d(x, W):  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')def max_pool_2x2(x):  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],                        strides=[1, 2, 2, 1], padding='SAME')

tf.nn.conv2d

conv2d(    input,    filter,    strides,    padding,    use_cudnn_on_gpu=True,    data_format='NHWC',    name=None)
  • input:张量。必须是以下类型之一:half,float32。一个四维张量。维度顺序根据data_format的值来解释,详见下文。
  • filter:张量。必须具有与输入相同的类型。形状的四维张量[filter_height,filter_width,in_channels,out_channels]
  • strides:整数列表。一维长度的张量4.输入每个维度的滑动窗口的步幅。维度顺序由data_format的值决定,详见下文。
  • padding:来自“SAME”,“VALID”的字符串。要使用的填充算法的类型。
  • use_cudnn_on_gpu:一个可选的布尔。默认为True。
  • data_format:来自“NHWC”,“NCHW”的可选字符串。默认为“NHWC”。指定输入和输出数据的数据格式。使用默认格式“NHWC”,数据按照[batch,height,width,channels]的顺序存储。或者,格式可以是“NCHW”,数据存储顺序为:[batch,channels,height,width]。
  • name:操作的名称(可选)。

tf.nn.max_pool

max_pool(    value,    ksize,    strides,    padding,    data_format='NHWC',    name=None)
  • value:由data_format指定的格式的4维张量。
  • ksize:4元素的1维 int型张量。 输入张量表示窗口每个维度的大小。
  • strides:4元素的1维int型张量。 输入张量表示滑动窗口每个维度的步幅。
  • padding:一个字符串,可以是’VALID’ 或 ‘SAME’。
  • data_format:一个字符串。 支持“NHWC”,“NCHW”和“NCHW_VECT_C”。
  • name:操作的可选名称。

第一卷积层

我们现在可以实现我们的第一层。 它将由卷积组成,然后是max pooling。 卷积将为每个5x5 patch计算32个特征。 它的权重张量是[5,5,1,32]的形状。 前两个维度是patch大小,下一个是输入通道的数量,最后一个是输出通道的数量。 每个输出通道还会有带有一个偏差向量的分量。

W_conv1 = weight_variable([5, 5, 1, 32])b_conv1 = bias_variable([32])

为了应用该层,我们首先将x重塑为4维张量,第二维和第三维对应于图像的宽度和高度,并且最后一个维度对应于色彩通道的数量。

x_image = tf.reshape(x, [-1, 28, 28, 1])

然后,我们将x_image与权重张量进行卷积,加上偏差,应用ReLU函数,最后使用max pooling。 max_pool_2x2方法将图像大小减小到14x14。

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)h_pool1 = max_pool_2x2(h_conv1)

第二卷积层

为了建立一个深层网络,我们堆叠了这种类型的几个层。 第二层将为每个5x5 patch有64个特征。

W_conv2 = weight_variable([5, 5, 32, 64])b_conv2 = bias_variable([64])h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)h_pool2 = max_pool_2x2(h_conv2)

密集连接层

现在图像尺寸已经减小到7x7,我们添加了一个1024个神经元的全连接图层,以允许在整个图像上进行处理。 我们将pooling层中的张量重塑为一批向量,乘以权重矩阵,添加一个偏差,并应用一个ReLU。

W_fc1 = weight_variable([7 * 7 * 64, 1024])b_fc1 = bias_variable([1024])h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

Dropout

为了减少过拟合,我们将在读出层之前应用dropout。 我们创建一个占位符,用于在dropout期间保持神经元输出的概率。 这可以让我们在训练过程中关闭dropout,并在测试过程中将其关闭。 TensorFlow的tf.nn.dropout可以自动处理缩放神经元输出和掩蔽它们,所以dropout只是在没有任何附加缩放的情况下工作。(对于这个小卷积网络,性能实际上几乎是相同的,没有丢失。 dropout对于减少过拟合通常是非常有效的,但是是在训练非常大的神经网络。)

keep_prob = tf.placeholder(tf.float32)h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

tf.nn.dropout

dropout(    x,    keep_prob,    noise_shape=None,    seed=None,    name=None)
  • x:浮点张量。
  • keep_prob:与x相同类型的张量。 每个元素被保留的概率。
  • noise_shape:int32类型的一维张量,表示随机生成的保留/丢弃标志。
  • seed:一个Python整数。 用于创建随机种子。 有关行为,请参阅tf.set_random_seed。
  • name:此操作的名称(可选)。

对于概率keep_prob,输出按1 / keep_prob放大的输入元素,否则输出0。

读出层

最后,我们添加一个图层,就像上面的一层softmax回归一样。

W_fc2 = weight_variable([1024, 10])b_fc2 = bias_variable([10])y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2

训练和评估模型

这个模型有多好? 为了训练和评估,我们将使用与上述简单的一层SoftMax网络几乎相同的代码。

不同之处在于:

  • 我们将用更复杂的ADAM优化器替代最陡的梯度下降优化器。
  • 我们将在feed_dict中包含附加参数keep_prob来控制丢失率。
  • 我们将在训练过程中每100次迭代添加一次记录。

我们也将使用tf.Session而不是tf.InteractiveSession。 这更好地分离了创建图(模型说明)的过程和评估图(模型拟合)的过程。 它通常使更清晰的代码。tf.Session是在一个块内创建的,所以一旦块退出,它就会被自动销毁。

运行这个代码。 请注意,它会进行20,000次训练迭代,可能需要一段时间(可能长达半小时),具体取决于您的处理器。

cross_entropy = tf.reduce_mean(    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))with tf.Session() as sess:  sess.run(tf.global_variables_initializer())  for i in range(20000):    batch = mnist.train.next_batch(50)    if i % 100 == 0:      train_accuracy = accuracy.eval(feed_dict={          x: batch[0], y_: batch[1], keep_prob: 1.0})      print('step %d, training accuracy %g' % (i, train_accuracy))    train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})  print('test accuracy %g' % accuracy.eval(feed_dict={      x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

运行此代码后的最终测试集精度应该约为99.2%。

我们已经学会了如何使用TensorFlow快速,轻松地构建,训练和评估相当复杂的深度学习模型。

转载地址:https://coordinate.blog.csdn.net/article/details/78703026 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:TensorBoard可视化学习
下一篇:Tensorflow学习始于MNIST

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月12日 16时45分49秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

IDEA 怎么删除一个Module 2021-07-02
走进数据科学:最好是通过比网课更好的方法 2021-07-02
AI革命第一步:最容易被忽略但必不可少的物联网 2021-07-02
2020年开发运维工具清单:选择开发运维工具堆栈吧 2021-07-02
效率提升法则:高效人士不会去做的4件事 2021-07-02
8.PostgreSQL约束 2021-07-02
【技术分享】使用AES加密技术保障数据安全 2021-07-02
【应用实例】布线多?成本高?不可靠?泽耀方案没烦恼! 2021-07-02
数据可视化工具:Matplotlib绘图 2021-07-02
用Python写个超级小恐龙跑酷游戏,上班摸鱼我能玩一天 2021-07-02
闺蜜看我用Python画了一幅樱花图,吵着要我给他介绍程序员小哥哥 2021-07-02
【Python爬虫实战】知乎热榜数据采集,上班工作摸鱼两不误,知乎热门信息一网打尽 2021-07-02
自从我学会了数据挖掘Matplotlib、Numpy、Pandas、Ta-Lib等一系列库,我把领导开除了 2021-07-02
Python抓取哔哩哔哩up主信息:只要爬虫学的好,牢饭吃的早 2021-07-02
有个码龄5年的程序员跟我说:“他连wifi从来不用密码” 2021-07-02
领导让我整理上个季度的销售额,幸好我会Python数据分析,你猜我几点下班 2021-07-02
【Python爬虫实战】为何如此痴迷Python?还不是因为爱看小姐姐图 2021-07-02
零基础自学Python,你也可以实现经济独立! 2021-07-02
ElasticSearch与Mysql对比(ElasticSearch常用方法大全,持续更新) 2021-07-02
数字化转型的主干道上,华为云以“三大关键”成企业智能化推手 2021-07-02