3.11 实现一个神经网络
目前,我们已经对TensorFlow的架构体系和作用域机制有了大体的认知,接下来,我们将实现一个稍微复杂的模型,那就是完全连接的神经网络模型。这里使用的数据集是在深度学习过程中都经常使用的MNIST数据集(详见http://yann.lecun.com/exdb/mnist/),利用神经网络模型能够实现对数字进行分类。
由于这是我们的第一个神经网络示例,因此,我们将逐步介绍其中的关键部分。如果要了解程序从头到尾的运行情况,读者可以在ch3文件夹中的3_tensorflow_introduction.ipynb文件“MNIST数字识别分类”部分自行查验。
3.11.1 数据准备
首先,我们需要调用maybe_download(...)函数来下载数据集,并调用read_mnist(...)函数对其进行预处理。这里,read_mnist(...)函数执行以下两个主要步骤:
- 读取数据集的字节流并将其形成适当的NUPYP.NDARRY对象。将图像标准化为零均值和单位方差。
3.11.2 定义TensorFlow计算图
为了定义TensorFlow计算图,我们首先为输入图像(tf_input)和其对应标签(tf_lab)定义占位符:
# 定义输入和输出 tf_inputs = tf.placeholder(shape=[batch_size, input_size], dtype=tf.float32, name = 'inputs') tf_labels = tf.placeholder(shape=[batch_size, num_labels], dtype=tf.float32, name = 'labels')
接下来,编写一个Python函数,它将首次创建变量。需要说明的是,我们使用作用域来确保变量的可重用性,并确保变量被正确命名:
下面,我们将定义神经网络的推理过程。这里需要说明一点,就是与没有使用作用域的变量相比,作用域为函数中的代码提供了非常直观的流程。这里的神经网络有三层,具体如下:
- 具有ReLU激活的完全连接层(第1层)。
- 具有ReLU激活的完全连接层(第2层)。
- 完全连接的softmax层(输出)。
通过作用域,我们将每个层的变量(权重值和偏差)命名为layer1 / weight、layer1 / bias、layer2/ weight、layer2 / bias、output / weights和output / bias。在下面的代码中,它们都具有相同的名称,但作用域是不同的:
现在,我们将定义一个损失函数并将损失进行最小化操作。损失最小化操作是通过在最小化损失方向上对神经网络参数进行微移来开展的。TensorFlow中提供了多种优化器,我们在这里将使用MomentumOptimizer,它提供了比GradientDescentOptimizer更好的最终精度和收敛性:
最后,我们将定义一个操作来检索给定批量输入所预测的softmax概率。这个预测值将反过来用于计算神经网络的准确性:
# 定义预测 tf_predictions = tf.nn.softmax(inference(tf_inputs))
3.11.3 运行神经网络
现在,我们实现了运行神经网络所需要的所有基本操作,可以对它进行检查,看它是否有能力学习对图像中的数字进行正确分类:
在这段代码中,accuracy(test_prediction, test_tags)是一个函数,它接受一些预测和标签作为输入,并提供准确性(有多少预测与实际标签匹配)。最终,我们会得到类似于图3-17所示的示意图,从运行的结果来看,该模型表现良好,读者也可以自行运行代码进行查验。
图3-17 MNIST数字分类任务的训练损失和测试准确性示意图