R语言Keras用RNN、双向RNNs递归神经网络、LSTM分析预测温度时间序列、 IMDB电影评分情感

在这篇文章中,我们将回顾三种提高循环神经网络的性能和泛化能力的高级方法。

由Kaizong Ye,Coin Ge撰写

我们将在一个温度预测问题上演示这三个概念,我们使用来自安装在建筑物屋顶的传感器的数据点的时间序列。


概述

安装在建筑物屋顶的传感器的数据点的时间序列,如温度、气压和湿度,你用这些数据点来预测最后一个数据点之后24小时的温度。这是一个相当具有挑战性的问题,它体现了在处理时间序列时遇到的许多常见困难。

我们将介绍以下方法。

  • 递归剔除–这是一种特定的、内置的方式,使用剔除来对抗递归层中的过度拟合。
  • 堆叠递归层–这增加了网络的表示能力(以较高的计算负荷为代价)。
  • 双向递归层–这些层以不同的方式向递归网络呈现相同的信息,提高了准确性并缓解了遗忘问题。
×

我们在学习某种神经网络模型时,一定要把如下几点理解透了,才算真正理解了这种神经网络。

  1. 网络的架构:包含那些层,每层的输入和输出,有那些模型参数是待优化的

  2. 前向传播算法

  3. 损失函数的定义

  4. 后向传播算法

  5. 什么情况下认为是发现了过拟合,怎么进行优化。

很多介绍深度学习的书籍,在介绍全连接神经网络时,对这几个方面都会介绍的比较清楚,但是在介绍CNN,RNN,LSTM等,都会只偏重介绍网络的架构,其他的方面介绍的少,没有讲解透彻。

问题:双向RNN中,有两个隐藏状态,一个是正序,如下图中的实线;一个是逆序,如下图中的虚线。那么在计算t时刻的状态变量时,对于正序,比较好理解,输入是上一个时刻的隐藏状态和当前时刻的系统输入;对于逆序,输入是下一个时刻的隐藏状态和当前时刻的系统输入。下一个时刻的隐藏状态属于未来的状态,那么,系统在计算时,怎么能够用未来的状态做输入呢?


 下面给出历程。

先理解下RNN的几点:

网络的架构:

这是一个标准的RNN结构图,图中每个箭头代表做一次变换,也就是说箭头连接带有权值。左侧是折叠起来的样子,右侧是展开的样子,左侧中h旁边的箭头代表此结构中的“循环“体现在隐层。

在展开结构中我们可以观察到,在标准的RNN结构中,隐层的神经元之间也是带有权值的。也就是说,随着序列的不断推进,前面的隐层将会影响后面的隐层。图中O代表输出,y代表样本给出的确定值,L代表损失函数,我们可以看到,“损失“也是随着序列的推荐而不断积累的。

除上述特点之外,标准RNN的还有以下特点:

1、权值共享,图中的W全是相同的,U和V也一样。

2、每一个输入值都只与它本身的那条路线建立权连接,不会和别的神经元连接。

前向传播算法:

x是输入,h是隐层单元,o为输出,L为损失函数,y为训练集的标签。这些元素右上角带的t代表t时刻的状态,其中需要注意的是,因策单元h在t时刻的表现不仅由此刻的输入决定,还受t时刻之前时刻的影响。V、W、U是权值,同一类型的权连接权值相同。

前向传播算法其实非常简单,对于t时刻:

h(t)=ϕ(U*x(t)+W*h(t−1)+b)

其中ϕ()为激活函数,一般来说会选择tanh函数,b为偏置。

t时刻的输出就更为简单:

o(t)=V*h(t)+c

最终模型的预测输出为:

yˆ(t)=σ(o(t))

其中σ为激活函数,通常RNN用于分类,故这里一般用softmax函数。

损失函数的定义:

对于分类问题,采用交叉熵。


后向传播算法:

BPTT(back-propagation through time)算法是常用的训练RNN的方法,其实本质还是BP算法,只不过RNN处理时间序列数据,所以要基于时间反向传播,故叫随时间反向传播。BPTT的中心思想和BP算法相同,沿着需要优化的参数的负梯度方向不断寻找更优的点直至收敛。综上所述,BPTT算法本质还是BP算法,BP算法本质还是梯度下降法,那么求各个参数的梯度便成了此算法的核心。

需要寻优的参数有三个,分别是U、V、W。与BP算法不同的是,其中W和U两个参数的寻优过程需要追溯之前的历史数据,参数V相对简单只需关注目前。

什么时候出现过拟合,怎么优化?

很多资料在介绍全连接神经网络时,都介绍过拟合的定义以及优化的方法,然后在介绍RNN时,却没有介绍这些。对于学习和实践的过程就很容易忽略这些。其中这些在实践中也是非常重要的。

出现过拟合的现象,就是安装过拟合的定义,当模型在训练数据的表现很好,而在测试数据集的表现不好时。

优化的方法:一是使用正则化,而是使用dropout。 dropout就是对权重参数进行随机丢弃的过程,采用超参数来控制丢失的比例。一般对同一个序列的内部的参数(即 U和V)使用dropout,而不对不同序列之间的连接的参数(即W)进行dropout。为什么要这样呢?这点很多书籍和资料都没有讲解透彻。

这其实是一个从实践的角度总结出来的,隐状态h是之前输入的信息的表示,在传递给下一个时刻,如果进行dropout,会有信息丢失。 而对同一个序列的内部的参数(即 U和V)使用dropout,只是讲信息表示的维数降低,比如隐层的维数是200维,使用dropout之后,变成180维,从而降低模型的复杂度。


现在理解双向RNN的几个方面:

  1. 网络的结构:在本文的开始有讲解,或者百度下,很多讲双向RNN的资料,基本上只讲解了这部分。要优化的参数包含两组:W,U,V。

  2. 前向传播算法:和RNN相比,这正是不太容易理解的地方。双向RNN中,有两个隐藏状态,一个是正序,如下图中的实线;一个是逆序,如下图中的虚线。那么在计算t时刻的状态变量时,对于正序,比较好理解,输入是上一个时刻的隐藏状态和当前时刻的系统输入;对于逆序,输入是下一个时刻的隐藏状态和当前时刻的系统输入。下一个时刻的隐藏状态属于未来的状态,那么,系统在计算时,怎么能够用未来的状态做输入呢?

  3. 损失函数的定义,是根据要解决的问题的类型来选择的,跟网络的类型一般关系不大。

  4. 后向传播算法,也是采用BPTT。不详细展开,根据函数定义和偏导数的定义,比如容易类推。

  5. 什么情况下认为是发现了过拟合,怎么进行优化。和RNN相比,比较容易类推。




温度预测的问题

到目前为止,我们所涉及的序列数据只有文本数据,如IMDB数据集和路透社数据集。但是,序列数据在许多问题中都能找到,而不仅仅是语言处理。在本节的所有例子中,你将分析一个天气时间序列数据集,该数据集来自生物地球化学研究所的气象站记录。

在这个数据集中,14个不同的量(如气温、大气压力、湿度、风向等)每10分钟被记录一次,历时数年。原始数据可以追溯到2003年,但这个例子仅限于2009-2016年的数据。这个数据集是学习处理数字时间序列的完美选择。你将用它来建立一个模型,把最近的一些数据(几天的数据点)作为输入,预测未来24小时的空气温度。

下载并解压数据如下。

unzip(
  "~/Downloads/climate.csv.zip",
)

视频

LSTM神经网络架构和原理及其在Python中的预测应用

探索见解

去bilibili观看

探索更多视频

我们来看看这些数据。

  

glimpse(data)

这里是温度(摄氏度)随时间变化的曲线图。在这个图上,你可以清楚地看到温度的年度周期性。

  

``````
mean(abs(preds - targets))

下面是评估循环。

   
  for (step in 1:val_steps) {
    c(samples, targets) %<-% val_gen()
    preds <- samples[,dim(samples)[[2]],2]
    mae <- mean(abs(preds - targets))
    batch_maes <- c(batch_maes, mae)
  }

这得出的MAE为0.29。因为温度数据已经被归一化,以0为中心,标准差为1。它转化为平均绝对误差为0.29 x temperature_std摄氏度:2.57˚C。

这是一个相当大的平均绝对误差。现在利用深度学习来做得更好。

基本的机器学习方法

在尝试机器学习方法之前,建立一个常识性的基线是很有用的,同样,在研究复杂和计算昂贵的模型(如RNN)之前,尝试简单、便宜的机器学习模型(如小型、密集连接的网络)也是很有用的。这是确保你在这个问题上投入的任何进一步的复杂性都是合法的,并带来真正的好处的最好方法。

下面的列表显示了一个全连接模型,它从扁平化的数据开始,然后通过两个密集层运行。注意最后一个密集层上没有激活函数,这对于回归问题来说是典型的。你使用MAE作为损失函数。因为你用完全相同的数据和完全相同的指标来评估你的常识性方法,所以结果将是直接可比的。


model %>% compile(   optimizer = optimizer_rmsprop(),   loss = "mae" )

验证和训练的损失曲线。

一些验证损失接近于无学习基线,但并不可靠。这就说明了首先要有这个基准的好处:事实证明,要超越它并不容易。你的常识包含了很多机器学习模型无法获得的宝贵信息。

你可能会想,如果存在一个简单的、表现良好的模型来从数据到目标(常识基准),为什么你正在训练的模型没有发现它并在此基础上进行改进?因为这个简单的解决方案并不是你的训练设置所要寻找的。你在其中寻找解决方案的模型空间–也就是你的假设空间–是所有可能的两层网络的空间,其配置由你定义。这些网络已经相当复杂了。当你用一个复杂的模型空间寻找解决方案时,简单的、表现良好的基线可能是不可学习的,即使它在技术上是假设空间的一部分。这是机器学习的一个相当重要的限制:除非学习算法被硬编码为寻找一种特定的简单模型,否则参数学习有时可能无法找到一个简单问题的简单解决方案。


HAR-RV-J与递归神经网络(RNN)混合模型预测和交易大型股票指数的高频波动率

阅读文章


第一个递归基准模型

第一个全连接方法做得不好,但这并不意味着机器学习不适用于这个问题。之前的方法首先对时间序列进行了扁平化处理,将时间的概念从输入数据中移除。相反,让我们把数据看成是:一个序列,其中因果关系和顺序很重要。你将尝试一个循环序列处理模型–它应该是最适合这种序列数据的,正是因为它利用了数据点的时间顺序,与第一种方法不同。

你将使用Chung等人在2014年开发的GRU层,而不是上一节中介绍的LSTM层。门控递归单元(GRU)层的工作原理与LSTM相同,但它们有些精简,因此运行成本更低(尽管它们可能没有LSTM那么多的表示能力)。在机器学习中,这种计算能力和表现能力之间的权衡随处可见。


随时关注您喜欢的主题


  layer\_gru(units = 32, input\_shape = list(NULL, dim(data)\[\[-1\]\])) %>% 
  layer_dense(units = 1)

结果绘制如下。好多了, 你可以大大超越常识性的基准模型,证明了机器学习的价值,以及在这种类型的任务上,递归网络比序列平坦的密集网络更有优势。

交互显示训练过程中的模型损失: 

新的验证MAE为~0.265(在你开始明显过拟合之前),转化为去标准化后的平均绝对误差为2.35˚C。与最初的2.57℃的误差相比,这是一个进步,但你可能仍有一点改进的余地。

使用递归丢弃dropout来对抗过拟合

从训练和验证曲线中可以看出,该模型正在过度拟合:训练和验证损失在几个 epochs之后开始出现明显的分歧。你已经熟悉了对抗这种现象的经典技术:dropout,它随机地将一个层的输入单元清零,以打破该层所接触的训练数据中的偶然相关性。

Yarin Gal使用Keras进行研究,并帮助将这一机制直接构建到Keras的递归层中。Keras中的每个递归层都有两个与dropout相关的参数:dropout,一个指定该层输入单元的dropout率的浮点数,以及recurrent\_dropout,指定递归单元的dropout率。让我们在layer\_gru中加入dropout和recurrent dropout,看看这样做对过拟合有什么影响。因为用dropout进行正则化的网络总是需要更长的时间来完全收敛,所以你将训练网络两倍的epochs。

  layer_gru(dropout = 0.2))

下面的图显示了结果。 你在前20个 epochs中不再过度拟合。但是,尽管你有更稳定的评估分数,你的最佳分数并没有比以前低很多。

因为你不再过度拟合,但似乎遇到了性能瓶颈,你应该考虑增加网络的容量。

堆叠递归层

回顾一下通用机器学习工作流程的描述:一般来说,增加网络的容量是个好主意,直到过拟合成为主要障碍(假设你已经采取了基本措施来减轻过拟合,比如使用dropout)。

增加网络容量通常是通过增加层中的单元数量或添加更多的层来实现的。递归层堆叠是建立更强大的递归网络的经典方法:例如,目前为谷歌翻译算法提供动力的是七个大型LSTM层的堆叠。

为了在Keras中把递归层堆叠起来,所有的中间层都应该返回它们的完整输出序列(一个三维张量),而不是它们在最后一个时间步的输出。这可以通过指定return_sequences = TRUE来实现。

  layer_gru(
            return_sequences = TRUE,
            input_shape = list(NULL, dim(data)\[\[-1\]\])) %>%

下图显示了结果。你可以看到,添加的层确实改善了一些结果,尽管并不明显。你可以得出两个结论。

  • 因为你的过拟合情况仍然不是很严重,所以你可以安全地增加层的大小以寻求验证损失的改善。不过,这有一个不可忽视的计算成本。
  • 增加一个层并没有明显的帮助,所以在这一点上,你可能会看到增加网络容量的回报越来越少。

使用双向的RNNs

本节介绍的最后一项技术被称为双向RNNs。双向RNN是一种常见的RNN变体,在某些任务上可以提供比普通RNN更大的性能。它经常被用于自然语言处理–你可以把它称为用于自然语言处理的深度学习的瑞士军刀。

值得注意的是,RNN是顺序依赖的,或者说是时间依赖的:它们按顺序处理其输入序列的时间段颠倒时间段可以完全改变RNN从序列中提取的表示。这正是它们在顺序有意义的问题上表现良好的原因,如温度预测问题。双向RNN利用了RNN的顺序敏感性:它包括使用两个常规的RNN,比如你已经熟悉的layer\_gru和layer\_lstm,每个都从一个方向(按时间顺序和按反时间顺序)处理输入序列,然后合并它们的表示。通过双向处理一个序列,双向RNN可以识别到可能被单向RNN忽略的模式。

值得注意的是,本节中的RNN层按时间顺序处理了序列(较早的时间段在前),这可能是一个随意的决定。如果RNN按反时序处理输入序列,例如(较新的时间段在前),它们的表现是否足够好?让我们在实践中尝试一下,看看会发生什么。训练你在本节第一个实验中使用的相同的单层GRU网络,你会得到如下的结果。

反序GRU的表现甚至低于常识性基线,表明在这种情况下,时间处理对你的方法的成功很重要。这是很有道理的:底层的GRU层通常会在记忆最近的过去方面比记忆遥远的过去方面做得更好,而对于这个问题来说,较近的天气数据点自然比较早的数据点更具预测性(这就是常识性基线相当强大的原因)。因此,按时间顺序排列的图层的表现必然会优于倒序的版本。重要的是,对于许多其他问题,包括自然语言,这并不正确:凭直觉,一个词在理解一个句子中的重要性通常并不取决于它在句子中的位置。

让我们在LSTM IMDB例子上尝试同样的技巧。

# 考虑作为特征的词的数量
max_features <- 10000  

# 剔除超过这个字数的文本
maxlen <- 500



# 反转序列
x\_train <- lapply(x\_train, rev)
x\_test <- lapply(x\_test, rev) 



  layer_lstm(units = 32) 

history <- model %>% fit(
  x\_train, y\_train,
  epochs = 10,
  batch_size = 128,
)

你得到的性能几乎与按时间顺序排列的LSTM相同。值得注意的是,在这样的文本数据集上,倒序处理与按时间顺序处理一样好,这证实了这样的假设:尽管词序在理解语言方面确实很重要,但你使用哪种顺序并不关键。重要的是,在颠倒的序列上训练的RNN将与在原始序列上训练的RNN学习不同的表征,就像在现实世界中,如果时间倒流,你会有不同的心理模型–如果你的生活中,你在第一天死亡,在最后一天出生。

在机器学习中,不同但有用的表征总是值得利用的,而且它们的差异越大越好:它们提供了一个新的角度来观察你的数据,识别到了其他方法所遗漏的数据方面,因此它们可以帮助提高任务的性能。

双向RNN利用这个想法来提高按时间顺序排列的RNN的性能。它从两个方面观察其输入序列,获得潜在的更丰富的表征,并识别到可能被单独的时间顺序版本所遗漏的模式。

要在Keras中实例化一个双向RNN,它需要一个递归层实例作为参数。创建这个递归层的第二个独立实例,并使用一个实例来处理按时间顺序排列的输入序列,另一个实例来处理按相反顺序排列的输入序列。我们在IMDB情感分析任务上试试。

bidirectional(
    lstm(units = 32)
  )

它的表现比之前尝试的普通LSTM略好,取得了超过89%的验证准确性。它似乎也会更快地过拟合,这并不奇怪,因为双向层的参数是按时间顺序排列的LSTM的两倍。通过一些正则化,双向方法很可能在这个任务中表现出色。

现在让我们在温度上尝试同样的方法

 bidirectional(
    layer_gru(units = 32),
  ) %>% 
  layer_dense(units = 1)

和普通的layer_gru的表现差不多。这很容易理解:所有的预测能力必须来自于网络中按时间顺序排列的那一半,因为已知反时间顺序排列的那一半在这个任务上表现严重不足。

进一步

为了提高温度预测问题的性能,你还可以尝试许多其他事情。

  • 调整堆叠设置中每个递归层的单元数量。目前的选择基本上是任意的,因此可能是次优的。
  • 调整RMSprop优化器使用的学习率。
  • 尝试使用layer\_lstm而不是layer\_gru。
  • 尝试在递归层之上使用更大的密集连接回归器:也就是说,一个更大的密集层,甚至是密集层的堆叠。
  • 最终在测试集上运行表现最好的模型(就验证MAE而言)。否则,你会开发出过度拟合的架构。

深度学习是一门艺术,而不是一门科学。我们可以提供指导方针,但最终,每个问题都是独一无二的;你必须根据经验来评估不同的策略。目前还没有任何理论能够事先准确地告诉你应该怎样做才能最有效地解决问题。你必须进行迭代。

总结

  • 在处理一个新问题时,最好首先为你选择的指标建立常识性的基准。如果你没有一个基准,你就无法判断你是否取得了真正的进展。
  • 先尝试简单的模型,以证明合理性。有时,一个简单的模型会成为你最好的选择。
  • 当你的数据的时间顺序很重要时,递归网络是一个很好的选择。
  • 为了在递归网络中使用dropout,你应该使用时间恒定的dropout mask和递归dropout mask。
  • 堆叠的RNN比单一的RNN层提供更多的表示能力。虽然它们在复杂的问题(如机器翻译)上提供了明显较好的效果,但它们可能在较简单的问题表现一般。
  • 双向的RNNs,从两个方面看一个序列,在自然语言处理问题上很有用。但它们在序列数据上的表现并不突出,因为在这些数据中,近期的信息量要比序列的初始数据大得多。

注意:股市和机器学习
一些读者一定会想把我们在这里介绍的技术,在预测股票市场上的证券(或货币汇率等)的未来价格问题上进行尝试。市场与自然现象(如天气模式)有着非常不同的统计特征,当涉及到股市时,过去的表现并不能很好地预测未来的回报–看后视镜是一种不好的驾驶方式。另一方面,机器学习适用于那些过去能很好地预测未来的数据集。


可下载资源

关于作者

Kaizong Ye拓端研究室(TRL)的研究员。在此对他对本文所作的贡献表示诚挚感谢,他在上海财经大学完成了统计学专业的硕士学位,专注人工智能领域。擅长Python.Matlab仿真、视觉处理、神经网络、数据分析。

本文借鉴了作者最近为《R语言数据分析挖掘必知必会 》课堂做的准备。

​非常感谢您阅读本文,如需帮助请联系我们!

 
QQ在线咨询
售前咨询热线
15121130882
售后咨询热线
0571-63341498

关注有关新文章的微信公众号


永远不要错过任何见解。当新文章发表时,我们会通过微信公众号向您推送。

技术干货

最新洞察

This will close in 0 seconds