理解 Keras 中的简单循环神经网络

本教程旨在帮助任何希望了解循环神经网络(RNN)的工作原理以及如何通过Keras深度学习库使用它们的人。虽然Keras库提供了解决问题和构建应用程序所需的所有方法,但了解它们的工作原理也很重要。在本文中,将逐步展示RNN模型中发生的计算。接下来,将开发一个完整的时间序列预测端到端系统。

完成本教程后,您将了解:

  • RNN的结构
  • RNN在给定输入时如何计算输出
  • 如何为Keras中的SimpleRNN准备数据
  • 如何训练SimpleRNN模型

通过我的书 《Transformer模型与注意力机制构建》 开启您的项目。它提供了自学教程工作代码,指导您构建一个能够
将句子从一种语言翻译成另一种语言的完整 Transformer 模型...

让我们开始吧。

Umstead state park

理解Keras中的简单循环神经网络。图片来自Mehreen Saeed,部分权利保留。

教程概述

本教程分为两部分;它们是

  1. RNN的结构
    1. RNN不同层相关的不同权重和偏差
    2. 在给定输入时计算输出的计算方式
  2. 时间序列预测的完整应用程序

先决条件

假定您在开始实现RNN之前已经具备RNN的基础知识。《循环神经网络入门及驱动它们的数学原理》可以帮助您快速了解RNN。

现在我们开始实现部分。

导入部分

为了开始实现RNN,让我们添加导入部分。

想开始构建带有注意力的 Transformer 模型吗?

立即参加我的免费12天电子邮件速成课程(含示例代码)。

点击注册,同时获得该课程的免费PDF电子书版本。

Keras SimpleRNN

下面的函数返回一个包含SimpleRNN层和Dense层的模型,用于学习序列数据。input_shape指定参数(time_steps x features)。我们将简化一切,使用单变量数据,即只有一个特征;时间步长将在下面讨论。

对象demo_model返回,其中包含通过SimpleRNN层创建的两个隐藏单元和通过Dense层创建的一个密集单元。input_shape设置为3x1,并且为了简单起见,两个层都使用了线性激活函数。回想一下,线性激活函数 $f(x) = x$ 对输入不做任何改变。网络如下所示

如果我们有$m$个隐藏单元(上面情况,$m=2$),则

  • 输入:$x \in R$
  • 隐藏单元:$h \in R^m$
  • 输入单元的权重:$w_x \in R^m$
  • 隐藏单元的权重:$w_h \in R^{mxm}$
  • 隐藏单元的偏差:$b_h \in R^m$
  • 密集层的权重:$w_y \in R^m$
  • 密集层的偏差:$b_y \in R$

让我们看看上面的权重。注意:由于权重是随机初始化的,此处显示的权重结果将与您的不同。重要的是要了解正在使用的每个对象的结构是什么样的,以及它如何与其他对象交互以产生最终输出。

现在让我们做一个简单的实验,看看SimpleRNN和Dense层的输出是如何产生的。请牢记此图。

Layers Of A Recurrent Neural Network

循环神经网络的层

我们将输入x经过三个时间步,让网络生成一个输出。将计算时间步1、2和3的隐藏单元的值。$h_0$初始化为零向量。输出$o_3$由$h_3$和$w_y$计算得出。由于我们使用的是线性单元,因此不需要激活函数。

在太阳黑子数据集上运行RNN

现在我们已经了解了SimpleRNN和Dense层是如何组合在一起的。让我们在一个简单的时间序列数据集上运行一个完整的RNN。我们需要遵循以下步骤:

  1. 从给定的URL读取数据集
  2. 将数据分割成训练集和测试集
  3. 将输入准备成Keras所需的格式
  4. 创建一个RNN模型并进行训练
  5. 在训练集和测试集上进行预测,并打印两个集合的均方根误差
  6. 查看结果

步骤1、2:读取数据并分割成训练集和测试集

下面的函数从给定的URL读取训练和测试数据,并将其分割成指定百分比的训练和测试数据。它返回训练和测试数据的单维数组,同时使用scikit-learn中的MinMaxScaler将数据缩放到0到1之间。

步骤3:为Keras重塑数据

下一步是准备Keras模型训练的数据。输入数组的形状应为:total_samples x time_steps x features

有很多方法可以准备时间序列数据进行训练。我们将创建具有不重叠时间步的输入行。下面的图示为时间步=2的例子。这里,时间步表示用于预测时间序列数据下一个值的先前时间步的数量。

How Data Is Prepared For Sunspots Example

太阳黑子示例的数据准备方式

下面的get_XY()函数以一维数组作为输入,并将其转换为所需的输入X和目标Y数组。我们将为太阳黑子数据集使用12个time_steps,因为太阳黑子通常有12个月的周期。您可以尝试其他time_steps值。

步骤4:创建RNN模型并进行训练

在此步骤中,您可以重用上面定义的create_RNN()函数。

步骤5:计算并打印均方根误差

print_error()函数计算实际值和预测值之间的均方误差。

步骤6:查看结果

下面的函数绘制实际目标值和预测值。红线分隔了训练和测试数据点。

生成的图表如下:

合并代码

本教程的全部代码如下。请在您自己的环境中尝试,并试验不同的隐藏单元和时间步长。您可以向网络添加第二个SimpleRNN,看看它的行为如何。您还可以使用scaler对象将数据重新缩放到其正常范围。

进一步阅读

如果您想深入了解,本节提供了更多关于该主题的资源。

书籍

文章

总结

在本教程中,您了解了循环神经网络及其各种架构。

具体来说,你学到了:

  • RNN的结构
  • RNN如何从先前的输入计算输出
  • 如何使用RNN实现端到端的时序预测系统

Do you have any questions about RNNs discussed in this post? Ask your questions in the comments below, and I will do my best to answer.

学习 Transformer 和注意力!

Building Transformer Models with Attention

教您的深度学习模型阅读句子

...使用带有注意力的 Transformer 模型

在我的新电子书中探索如何实现
使用注意力机制构建 Transformer 模型

它提供了自学教程可运行代码,指导您构建一个可以
将句子从一种语言翻译成另一种语言的完整 Transformer 模型...

为理解人类语言提供神奇力量
您的项目


查看内容

, , , , , ,

18 条回复:理解 Keras 中的简单循环神经网络

  1. tony 2021年11月15日 晚上8:47 #

    当我使用tensorflow.keras时,在create_RNN()中抛出了NotImplementedError。我应该更新什么?谢谢。

    • Adrian Tam
      Adrian Tam 2021年11月16日 凌晨2:29 #

      create_RNN() 是这里定义的函数。哪一行触发了NotImplementedError?

      • tony 2021年11月16日 下午3:12 #

        在“model.add(SimpleRNN(hidden_units, input_shape=input_shape, activation=activation[0]))”中

        错误在
        ~/anaconda3/envs/py38/lib/python3.8/site-packages/tensorflow/python/framework/ops.py in __array__(self)
        850
        851 def __array__(self)
        –> 852 raise NotImplementedError(
        853 “Cannot convert a symbolic Tensor ({}) to a numpy array.”
        854 ” 此错误可能表明您尝试将 Tensor 传递给”

        NotImplementedError: 无法将符号张量 (simple_rnn/strided_slice:0) 转换为 numpy 数组。此错误可能表明您尝试将张量传递给 NumPy 调用,这是不支持的

        Python 版本是 3.8.12
        Tensorflow 版本是 2.4.1

        谢谢。

  2. Master56^^ 2021年12月21日 凌晨6:40 #

    错误仍然没有修复 NotImplementedError: 无法将符号张量 (simple_rnn_1/strided_slice:0) 转换为 numpy 数组。此错误可能表明您尝试将张量传递给 NumPy 调用,这是不支持的

    • James Carmichael 2021年12月24日 凌晨5:58 #

      你好 Master56^^…请提供完整的代码列表和你的 Python 编码环境的描述。

      此致,

  3. Tommy Guo 2022年8月9日 凌晨9:26 #

    你好 Mehran,

    感谢您提供如此有见地的帖子!我浏览了其他在线课程和实验室的学习材料,但直到我读了您的帖子,我才真正掌握 RNN 中到底发生了什么。它写得非常好,而且清晰明了。代码同样非常容易遵循,我希望在此示例的基础上进行扩展,为我的研究项目实现 RNN!

    祝好,

    Tommy

    • James Carmichael 2022年8月9日 凌晨9:53 #

      Tommy,感谢你的精彩反馈!我们感谢你的支持和反馈!

  4. Paolo 2023年3月3日 凌晨5:22 #

    非常棒的帖子!
    我可能发现了一个小小的拼写错误

    h1 = np.dot(x[0], wx) + h0 + bh

    应该是

    h1 = np.dot(x[0], wx) + np.dot(h0,wh) + bh

    无论如何,结果没有改变,因为 h0 是全零

    • James Carmichael 2023年3月3日 凌晨9:23 #

      谢谢 Paolo!我们感谢您的反馈。

  5. cesiy 2023年3月28日 下午5:30 #

    我们需要关于这个的更多信息,并且它也可以在 colab 或 jupyter 中复制粘贴执行

    • James Carmichael 2023年3月29日 凌晨7:16 #

      你好 cesiy…您是否成功执行了示例代码?如果没有,请告诉我们有什么不工作或您收到的任何错误消息。

  6. tasos 2023年7月10日 晚上8:57 #

    谢谢!那么一个序列具有多个特征(每个时间步)的情况呢?我尝试了以下方法,但 Keras 输出一个值,而手动计算输出两个值

    # hidden_units, dense_units, input_shape, activation
    m = 2
    demo_model = create_RNN(m, 1, (3,2), activation=[‘tanh’, ‘linear’])

    wx = demo_model.get_weights()[0]
    wh = demo_model.get_weights()[1]
    bh = demo_model.get_weights()[2]
    wy = demo_model.get_weights()[3]
    by = demo_model.get_weights()[4]

    print(‘wx = ‘, wx, ‘\nwh = ‘, wh, ‘\nbh = ‘, bh, ‘\nwy =’, wy, ‘\nby = ‘, by)

    x = np.array([1,0.1, 2,0.2, 3,0.3])
    # 将输入重塑为所需的 sample_size x time_steps x features
    x_input = np.reshape(x,(1, 3, 2))
    print(‘x =’, x)
    print(‘x_reshaped = ‘, x_input)
    y_pred_model = demo_model.predict(x_input)

    h0 = np.zeros(m)
    h1 = np.tanh(np.dot(x[0], wx) + np.dot(h0,wh) + bh)
    h2 = np.tanh(np.dot(x[1], wx) + np.dot(h1,wh) + bh)
    h3 = np.tanh(np.dot(x[2], wx) + np.dot(h2,wh) + bh)
    o3 = np.dot(h3, wy) + by

    print(‘h1 = ‘, h1, ‘h2 = ‘, h2, ‘h3 = ‘, h3)

    print(“来自网络的预测 “, y_pred_model)
    print(“来自我们计算的预测 “, o3)

    输出
    来自网络的预测 [[0.09162921]]
    来自我们计算的预测 [[ 0.14343597] [-0.27000149]]

    • James Carmichael 2023年7月10日 晚上11:12 #

      你好 Tasos…谢谢你的反馈!我们将审查此场景。

  7. Odise 2023年10月4日 凌晨7:05 #

    你好,我目前正在使用这个数据集,我需要使用一个分割(训练、测试、验证),其中训练:从第一行到 1965 行,验证:从 1966 行到 1975 行,测试:从 1976 行到最后一行。我目前不熟悉这种语言,因为我刚开始使用 Rstudio,而且我找不到解决方案,所以我想尽快得到答复。非常感谢。

  8. Zineb 2023年10月20日 晚上9:13 #

    你好,
    在第一个示例(Keras simpleRNN)中,您在没有训练模型的情况下获得了权重(编译模型后没有 fit 函数)

    这是怎么回事?

    提前感谢

  9. Zineb 2023年10月20日 晚上10:42 #

    还有一个问题,拜托

    在第一个示例(Keras Simple RNN)中;
    y_pred_model=demo_model.predict(x_input)
    print(“来自网络的预测 “, y_pred_model)
    我得到:[[-1.1634194]]!!

    我只是复制并粘贴了您提供的代码

  10. Kelvin 2024年4月30日 凌晨3:27 #

    我有点困惑,时间步是序列的长度还是序列的数量?例如,我有 (16230,1),我想使用 24 作为时间步,所以它变成 (680,24,1),其中 680 是样本数,24 是时间步的长度,1 是特征。请问我说的对吗?

发表回复

Machine Learning Mastery 是 Guiding Tech Media 的一部分,Guiding Tech Media 是一家领先的数字媒体出版商,专注于帮助人们了解技术。访问我们的公司网站以了解更多关于我们的使命和团队的信息。