使用 TensorFlow 和 Keras 编写神经网络和多层感知机

神经网络是一种受大脑结构和功能启发的建模技术。 正如大脑包含数百万个被称为神经元的微小互连单元一样,今天的神经网络由数百万个分层排列的微小互连计算单元组成。 由于神经网络的计算单元仅存在于数字世界中,与大脑的物理神经元相反,它们也被称为人工神经元。 类似地,神经网络(NN)也称为人工神经网络(ANN)

在本篇文章中,我们将进一步扩展以下主题:

  • 感知机(人工神经元)
  • 前馈神经网络
  • 用于图像分类的多层感知机(MLP)
    • 用于 MNIST 图像分类的基于 TensorFlow 的多层感知机
    • 用于 MNIST 分类的基于 Keras 的多层感知机
    • 用于 MNIST 分类的基于 TFLearn 的多层感知机
  • 用于时间序列回归多层感知机

感知机

让我们理解神经网络的最基本构建块,即感知机,也称为人工神经元。 感知机的概念起源于Frank Rosenblatt于1962年的作品。

您可能希望阅读以下工作来探索神经网络的起源:

Frank Rosenblatt, Principles of Neurodynamics: Perceptrons and the Theory of Brain Mechanisms. Spartan Books, 1962

以最简单的视角,感知机是来自于生物体神经元的抽象模型,该模型接收一个或多个输入,并将它们结合在一起产生输出。

如下图所示,感知器采用三个输入并将它们相加以生成输出 y

简单感知机

这种感知器太简单了,不具备任何实际用途。因此,通过添加权重,偏差和激活函数的概念来增强它。将权重添加到每个输入以获得加权和。如果加权和 \sum w_i x_i 小于阈值,则输出为0,否则输出为1:

y = \begin{cases} 0& \text{if} \sum w_i x_i < threshold, \\ 1& \text{if} \sum w_i x_i \ge threshold. \end{cases}

阈值称为偏置。 让我们将偏置移到等式的左边,用 b 表示它,并用 wx 的向量点积表示 \sum w_i x_i 。 感知器的等式现在变为如下:

y = \begin{cases} 0& \text{if} \sum w \cdot x + b < 0, \\ 1& \text{if} \sum w \cdot x + b \ge 0. \end{cases}

感知器现在看起来像下图:

带有权重和偏置的简单感知机

到目前为止,神经元是一个线性函数。为了使这个神经元产生非线性决策边界,需要通过称为激活函数或传递函数的非线性函数运行求和输出。有许多流行的激活功能可供使用:

  • ReLU:整流线性单元,使值平滑变迁到范围 (0, x)
    ReLU(x) = max(0, x)
  • sigmoid:S型函数使值平滑变迁到范围 (0, x)
    sigmoid(x) = \frac{1}{1+e^{-x}} = \frac{e^x}{1+e^x}
  • tanh:双曲正切使值平滑变迁到范围 (-1, 1)
    tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}

使用激活函数后,感知器的等式变为:

y = \phi(w \cdot x + b)

其中 \phi(\cdot) 是某个激活函数。

该神经元看上去像以下这幅图:

带有权重、偏置和激活函数的简单感知机

多层感知机

当我们将人工神经元连接在一起时,基于明确定义的结构,我们将其称为神经网络。以下是一个神经元最简单的神经网络:

只有一个神经元的神经网络

我们连接神经元,使得一层的输出成为下一层的输入,直到最后一层的输出成为最终输出。这种神经网络称为前馈神经网络(FFNN)。由于这些FFNN由连接在一起的神经元层组成,因此它们被称为多层感知器(MLP)深度神经网络(DNN)

作为示例,下图中描绘的MLP具有三个特征作为输入:两个隐藏层,每个神经元包含五个神经元,一个输出y。神经元完全连接到下一层的神经元。这些层也称为致密层(dense layer)或仿射层(affine layer),并且这种模型也称为顺序模型(sequential model)。

让我们重温一下我们之前探索过的一些示例数据集,并在TensorFlow中构建简单的神经网络(MLP或DNN)。

您可以按照Jupyter笔记本ch-05_MLP中的代码进行操作。

用于图像分割的多层感知机

让我们使用不同的库(例如TensorFlow,Keras和TFLearn)构建用于图像分类的MLP网络。我们将使用MNIST数据集作为本节中的示例。

MNIST数据集包含从0到9的手写数字的28×28像素图像,以及它们的标签,训练集为六万张,测试集为一万张。MNIST数据集是使用最广泛的数据集,包含在TensorFlow示例和教程中。

MNIST数据集和相关文档可从以下网址获得:http://yann.lecun.com/exdb/mnist/

让我们从纯TensorFlow方法开始。

用于MNIST分类的基于TensorFlow的多层感知机

首先,加载MNIST数据集,并使用以下代码定义训练和测试功能以及目标:

我们创建了三个辅助函数,它们将帮助我们创建一个只有一个隐藏层的简单多层感知机,然后是一个更大的多层感知机,每层有多个层和多个神经元。

mlp() 函数使用以下逻辑构建网络层:

  1. mlp() 函数有五个输入:
    • x 是输入特征变量
    • num_inputs 是输入特征的数量
    • num_outputs 是输出目标的数量
    • num_layers 是必须隐藏层的数量
    • num_neurons 是每层神经元数量列表
  2. 将权重和偏置列表设为空:
    w = []
    b = []
  3. 以隐藏层数量为准循环创建多个权重和偏移张量并将它们附加到各自的列表中:
    • 张量分别命名为 w_<layer_num> 和 b_<layer_num> 。命名张量有助于调试和查找代码问题。
    • 使用 tf.random_normal() 将张量初始化为正态分布。
    • 权重张量的第一个维度是来自前一层的输入数量。 对于第一个隐藏层,第一个维度是num_inputs。 权重张量的第二维是当前层中的神经元的数量。
    • 偏置都是一维张量,其中维度等于当前层中的神经元数量。
  1. 为最后一个隐藏层创建权重和偏置。在这种情况下,权重张量的维数等于最后隐藏层中的神经元数量和输出目标的数量。偏置将是具有输出特征数量大小的单个维度的张量:
  1. 现在开始定义图层。首先,将 x 视为第一个最明显的输入层:
  1. 循环添加隐藏的图层。每个隐藏层代表线性函数 tf.matmul(layer, w[i])+b[i] 由激活函数 tf.nn.relu() 变为非线性:
  1. 添加输出图层。输出层和隐藏层之间的一个区别是输出层中没有激活功能:
  1. 返回包含多层感知机网络的图层对象:

整个多层感知机函数的完整代码如下:

辅助函数 mnist_batch_func() 将 MNIST 数据集包装为 TensorFlow 的批处理函数,以提供下一批图像:

此功能不言自明。TensorFlow为MNIST数据集提供此功能;但是,对于其他数据集,我们可能必须编写自己的批处理函数。

辅助函数 tensorflow_classification() 训练和评估模型。

  1. tensorflow_classification() 函数需要几个输入:
    • n_epochs 是运行训练的循环次数
    • n_batches 是应该用于运行每个循环中的训练的随机样本批次的数量
    • batch_size 是每个批次中样本的数量
    • batch_func 是获取batch_size并返回X和Y的样本批处理的函数
    • model 是实际神经网络或带有神经元的层
    • optimizer 是使用 TensorFlow 定义的优化函数
    • loss 是成本函数给出的损失,即优化器优化参数的参考依据
    • accuracy_function 是计算精确分数的函数
    • X_test 和 Y_test 是用于测试的数据集
  1. 启动TensorFlow会话以运行训练循环:
  1. 运行训练 n_epochs 个周期:
  1. 在每个周期中,取n_batches数量的样本集并训练模型,计算每个批次的损失,计算每个时期的平均损失:
  1. 完成所有周期循环后,使用accuracy_function计算的精度分数并打印出来:

tensorflow_classification() 函数的完整代码如下:

现在让我们定义输入和输出占位符,x和y以及其他超参数:

参数如下所述:

  • num_layers 是隐藏层的数量。我们首先实现没有隐藏层,只有输入和输出层。
  • num_neurons 是空列表,因为没有隐藏层。
  • learning_rate 是 0.01,一个随机选取的较小的数。
  • num_epochs 代表 50 次迭代,以供学习连接输入和输出的唯一的神经元的参数。
  • batch_size 保持在 100,这也是一个选择问题。较大的批量大小不一定提供更多的好处。您可能需要探索不同的批量大小,以找到神经网络的最佳批量大小。
  • n_batches:批次数大致计算为示例数除以批处理中的样本数。

现在让我们将所有内容放在一起,并使用到目前为止定义的变量来定义网络,损失函数,优化器函数和精度函数。

在这段代码中,我们使用一个新的tensorflow函数来定义损失函数:

使用 softmax_cross_entropy_with_logits() 函数时,请确保输出未缩放且尚未通过 softmax 激活函数。 因为此函数内部使用 softmax 来缩放输出。

该函数计算模型(估计值y)和y的实际值之间的 softmax 熵。当输出属于且仅属于一个类,使用熵函数。在我们的示例中,图像只能属于其中一个数字。

更多有关交叉熵函数的内容可以在这里找到:

https://www.tensorflow.org/api_docs/python/tf/nn/softmax_cross_entropy_with_logits

定义好所有内容后,运行 tensorflow_classification() 函数来训练和评估模型:

运行分类函数后得到以下输出:

我们看到单个神经元网络在50次迭代中缓慢地将损失从8.3降低到0.66,最终得到几乎85%的准确度。对于这个具体的例子,这个准确性非常糟糕,因为这只是使用 TensorFlow 进行分类使用多层感知机的演示。

我们使用更多层和神经元运行相同的代码,并获得以下准确性:

层数每个隐藏层的神经元数量精确度
000.857
180.616
22560.936

因此,通过在每层添加两行和256个神经元,我们将精度提高到0.936。我们鼓励您尝试使用不同变量值的代码来观察它如何影响损失和准确性。

用于MNIST分类的基于 Keras 的多层感知机

现在让我们用 Keras 建立相同的多层感知机网络,Keras 是 TensorFlow 的高级库。此例子中的所有参数与本问中使用 TensorFlow 示例的参数相同,例如,隐藏层的激活函数保持为 ReLU 函数。

  1. 从Keras导入所需的模块:
  1. 定义超参数(我们假设数据集已经加载到X_train,Y_train,X_test和Y_test变量中):
  1. 创建顺序模型:
  1. 添加第一个隐藏图层。只有在第一个隐藏层中,我们必须指定输入张量的形状:
  1. 添加第二层:
  1. 使用softmax激活函数添加输出图层:
  1. 打印模型详细信息:

我们得到以下输出:

Layer (type)Output ShapeParam #
dense_1 (Dense)(None, 256)200960
dense_2 (Dense)(None, 256)65792
dense_3 (Dense)(None, 10)2570
  1. 使用SGD优化器编译模型:
  1. 训练模型:

在训练模型时,我们可以观察每次训练迭代的损失和准确性:

  1. 评估模型并打印损失和准确性:

我们得到以下输出:

使用 Keras 进行 MNIST 分类的多层感知机的完整代码在笔记本ch-05_MLP中提供。

用于MNIST分类的基于TFLearn的多层感知机

现在让我们看看如何使用 TFLearn 实现相同的多层感知机。TFLearn 是 TensorFlow 的另一个高级库:

  1. 导入 TFLearn 库:
  1. 定义超参数(我们假设数据集已经加载到X_train,Y_train,X_test和Y_test变量中):
  1. 构建输入层,两个隐藏层和输出层(与TensorFlow和Keras部分中的示例相同的架构):
  1. 使用最后一步中构建的DNN(在变量softmax中)定义优化器函数,神经网络和MLP模型(在TFLearn中称为DNN):
  1. 训练模型:

训练完毕后我们得到以下输出:

  1. 评估模型并打印精确度分数:

我们得到以下输出:

得到的精确度与使用TFLearn相比大致相当。

使用TFLearn进行MNIST分类的MLP完整代码在笔记本ch-05_MLP中提供。

用于时间序列回归的多层感知机

我们已经看到了图像数据分类的例子;现在让我们看一下时间序列数据的回归。我们将建立并使用多层感知机作为一个较小的单变量时间序列数据集,称为国际航空公司乘客数据集。该数据集包含多年来的乘客总数。该数据集可从以下链接获得:

让我们从准备数据集开始。

  1. 首先,使用以下代码加载数据集:
  1. 使用datasetslib中的实用程序功能,我们将数据集拆分为测试和训练集。对于时间序列数据集,我们有一个单独的函数,不会改变观察结果,因为对于时间序列回归,我们需要维持观察的顺序。我们使用67%的数据进行培训,33%的数据用于测试。您可能希望尝试使用不同比例的示例。
  1. 对于时间序列回归,我们转换数据集以构建监督数据集。在此示例中,我们采用滞后周期为两个时间步长。我们将n_x设置为2,mvts_to_xy() 函数返回输入和输出(X和Y)序列和测试集,也即X是两列时间{t-1, t}的值,且Y是时间{t+1}的值。 我们的学习算法假设通过找到时间{t-1,t,t + 1}的值之间的关系,可以学习时间t + 1的值。

有关将时间序列数据集转换为监督学习问题的更多信息,请访问以下链接:

http://machinelearningmastery.com/convert-time-series-supervised-learning-problem-python/

现在我们在我们的训练数据集上构建和训练模型:

  1. 导入必须的 Keras 模块:
  1. 设置构建模型所需的超参数:

请注意,我们使用批量大小为2,因为数据集非常小。我们使用两层MLP,每层只有八个神经元,因为我们的示例问题的规模很小。

  1. 构建、编译与训练模型:

请注意,我们使用Adam优化器而不是SGD。 您可能想要尝试TensorFlow和Keras中可用的不同优化器。

  1. 评估模型并打印均方误差(MSE)和均方根误差(RMSE):

我们得到以下输出:

  1. 使用我们的模型预测值并绘制它们,用于测试和训练数据集:

我们得到以下关于原始和预测时间序列值的图:

如你所见,这个估计效果非常好。然而,在现实生活中,数据本质上是多变量和复杂的。因此,我们将在后面的文章中看到时间序列数据的递归神经网络架构。

使用 TensorFlow、Keras 和 TFLearn 实现的多层感知机摘要

在前面的部分中,我们学习了如何使用 TensorFLow 及其高级库构建简单的多层感知机架构。纯 TensorFlow 的准确度约为0.93-0.94,Keras 的准确度为0.96-0.98,TFLearn的准确度为0.96-0.97。尽管我们的代码的所有示例都使用 TensorFlow,但相同体系结构和参数的准确性差异可归因于这样一个事实:尽管我们初始化了一些重要的超参数,但高级库和 TensorFlow 却抽象出许多其它我们没有修改过默认值的超参数。

我们观察到,与Keras和TFLearn相比,TensorFlow中的代码非常详细和冗长。高级库使我们更容易构建和训练神经网络模型。

总结

在本文中,我们了解了多层感知器。我们解释了如何为分类和回归问题构建和训练多层感知机模型。我们使用纯TensorFlow、Keras和TFLearn构建了多层感知机模型。对于分类,我们使用图像数据;对于回归,我们使用时间序列数据。

构建和训练多层感知机网络模型的技术对于任何其他类型的数据(例如数字或文本)是相同的。然而,对于图像数据集,卷积神经网络架构已被证明是最佳架构,对于序列数据集,例如时间序列和文本,循环神经网络模型已被证明是最佳架构。

虽然我们在本文中仅使用简单的数据集示例来演示多层感知机体系结构,但在后面的文章中,我们将介绍具有一些大型和高级数据集的卷积神经网络和循环神经网络体系结构。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据