深度学习架构师手册——理解递归神经网络

346 阅读15分钟

递归神经网络(RNN)是一种神经网络,专门用于处理序列数据,并能够识别数据的顺序。序列数据可以是时间序列数据,也可以是没有时间成分的数据,如文本数据。这类神经网络的应用建立在数据本身的性质上。对于时间序列数据,这可以是用于“现时预测”(利用过去和现在的数据进行当前时间的预测)或预测目标。对于文本数据,应用如语音识别和机器翻译可以利用这些神经网络。

随着能够捕捉序列数据同时完全去除递归连接并且取得更好性能的神经网络(如Transformer)的出现,递归神经网络的研究在过去几年有所放缓。然而,RNN仍然广泛应用于现实世界,作为一个很好的基准或替代模型,由于其较低的计算量和合理的指标性能,且具有低内存要求,适用于更快速的计算。

RNN中最突出的两个层是长短期记忆(LSTM)和门控递归单元(GRU)。在本书中,我们不会详细讨论原始的、普通的递归神经网络,而是将介绍LSTM和GRU作为复习。LSTM和GRU的主要操作提供了一种机制,能够保留仅与任务相关的记忆,并忽略不重要的数据,这是为时间序列或序列数据设计的关键归纳偏置。

在本章中,我们将更深入地探讨这两种RNN网络,具体内容包括:

  • 理解LSTM
  • 理解GRU
  • 理解GRU和LSTM层的进展

技术要求

本章内容简短精炼,但仍涵盖了在Python编程语言中实现RNN架构的实际应用。要完成本章内容,您需要一台安装了Pytorch库的计算机。

本章的代码文件可以在GitHub上找到,链接为:github.com/PacktPublis…

理解LSTM

LSTM(长短期记忆)于1997年发明,但仍然是广泛采用的神经网络。LSTM使用tanh激活函数,因为它提供了非线性特性,并且能够保留较长序列的二阶导数。tanh函数有助于防止梯度爆炸和梯度消失。LSTM层使用一系列顺序连接的LSTM单元。让我们深入了解LSTM单元的结构,如图4.1所示。

image.png

左侧的第一个LSTM单元描绘了LSTM单元的高层结构,左侧的第二个LSTM单元描绘了LSTM单元的中层操作、连接和结构,而右侧的第三个单元只是另一个LSTM单元,强调了LSTM层由多个顺序连接的LSTM单元构成。可以将LSTM单元视为包含四个门控机制,这些机制提供了忘记、学习、记忆和使用序列数据的方法。你可能会好奇的一个显著问题是,为什么sigmoid函数在输入、过去的隐藏状态以及忘记门、记忆门和使用门的三条路径中被显示为一个单独的过程。图4.1中显示的结构是LSTM单元的经典示意图,但没有包含连接权重的信息。这是因为输入通过加权求和过程,将前一个单元的隐藏状态与当前序列数据结合,并且每个连接的权重集是不同的。图4.2显示了考虑权重后的单个LSTM单元的最终低层结构。

image.png

在图4.2中,W和B分别表示权重和偏置。两个小写字母分别表示数据类型和门控机制。数据类型分为两种——由h表示的隐藏状态和由i表示的输入数据。涉及的门控机制有:表示忘记机制的F、表示学习机制的L,其中学习有两个与之相关的权重和偏置,以及表示使用机制的U。为了正确理解一个LSTM单元有多少参数,我们还需要解码隐藏状态和输入状态权重向量的维度。为了方便参考,假设输入向量的大小为n,隐藏状态的大小为m。

隐藏状态的权重维度为: nm

而输入状态的权重维度为: n²

另一方面,偏置的大小等于输入向量的大小。对于使用Tensorflow和Keras(基于Tensorflow)的情况,偏置每个机制只添加一次。对于PyTorch,偏置会为每个隐藏状态和输入状态权重分别添加。对于PyTorch,偏置的参数数量可以定义为: 2n

如图4.2所示,由于有四个机制,这意味着在PyTorch实现中,LSTM的参数数量可以根据以下公式计算: 参数数量 = 4(nm + n² + 2n)

现在我们已经理解了实际参数的位置,接下来让我们深入了解这些门控机制。

解码LSTM的忘记机制

忘记机制通过使用sigmoid激活函数并与前一个单元状态相乘来完成。该门控机制的名称暗示它基于当前输入序列和前一个单元输出的组合来决定移除哪些信息。可以这样理解,在0到1的尺度上,过去的信息有多相关?sigmoid机制将这个尺度限制在0到1之间。接近0的值会遗忘更多的前一个单元状态(长期记忆),而接近1的值则会遗忘较少的前一个单元状态记忆。

解码LSTM的学习机制

学习机制使用sigmoid激活前一个单元输出,并将tanh激活后的前一个单元输出加到忘记门的输出上,然后与使用门的输出相乘。这个机制也被称为输入门。这个机制允许从当前输入序列中学习信息。学到的信息随后会传递到记忆机制中。此外,学到的信息还会传递到用于下一个LSTM单元的信息使用机制中。接下来将依次介绍这两个机制。

解码LSTM的记忆机制

记忆机制通过简单地将忘记过程剩下的信息和学习到的信息加在一起实现,这就是学习门的输出。这个门的输出将被视为LSTM单元的当前单元状态。单元状态包含了LSTM序列的长期记忆。可以简单地把这个机制理解为一个操作,它允许网络选择性地决定保持和记住输入的哪一部分。

解码LSTM的“信息使用”机制

信息使用机制通过对当前单元状态应用tanh激活函数来实现非线性操作,并再次使用当前输入序列和前一个单元输出作为加权机制,确定过去和现在的信息中应使用多少相关信息。应用使用门后的输出将给出隐藏状态,这个隐藏状态也将作为下一个LSTM单元的前一个单元输出。

构建完整的LSTM网络

通常,为了创建一个完整的LSTM网络,会将多个LSTM层连接在一起,使用来自多个LSTM单元的隐藏状态序列作为后续的序列数据,并将其应用到下一个LSTM层。在几个LSTM层之后,前一层的隐藏状态序列通常会传递到一个全连接层,形成基于监督学习的简单LSTM架构的基础。图4.3展示了这一过程的视觉结构:

image.png

基于图4.3中描述的网络,PyTorch中的实现如下所示:

首先,我们导入PyTorch库中的方便的nn模块:

import torch.nn as nn

现在,我们将基于图4.3定义网络架构,这次使用顺序API,而不是类方法:

RNN = nn.Sequential(
  nn.LSTM(
      input_size=10, hidden_size=20,
      num_layers=2, dropout=0,
  ),
  nn.Linear(in_features=10, out_features=10),
  nn.Softmax(),
)

LSTM的输入大小、隐藏大小和层数,以及线性层的输出特征大小,可以根据输入数据集和需求进行配置。请注意,输入数据的每个时间步或序列步骤的大小可以大于1。这使我们可以轻松地将原始特征映射到更具代表性的特征嵌入,并利用它们的描述能力。此外,dropout正则化器可以通过将dropout参数设置为0到1之间的值来轻松添加,这将在每一层(除了最后一层)按指定的概率引入dropout层。在步骤2中定义的RNN现在可以像任何PyTorch类中定义的模型一样进行训练。

像往常一样,PyTorch使得构建RNN变得更加简单和快速。接下来,我们将进入下一种类型的RNN,称为门控递归单元(GRU)。

理解GRU

门控递归单元(GRU)于2014年发明,基于LSTM中实现的思想。GRU旨在简化LSTM,并提供一种更快速、更高效的方式,达到与LSTM相同的目标,即根据过去和现在的数据自适应地记忆和遗忘。在学习能力和可实现的指标性能方面,GRU和LSTM之间并没有明显的“银弹”式胜者,通常在工业界,两个RNN单元会相互进行基准测试,以找出哪种方法提供更好的性能水平。图4.4展示了GRU的结构。

image.png

图4.4采用了与图4.2中LSTM相同的权重和偏置符号表示。这里有三个不同的小写字母符号:R表示重置门,z表示更新门,h表示用于获得下一个隐藏状态的权重。这意味着GRU单元比LSTM单元具有更少的参数,只有三组权重和偏置,而不是四组。这使得GRU网络比LSTM网络稍微更快。

尽管被描绘为一个单元,但需要多个LSTM单元顺序连接的相同理论也适用于GRU:一个GRU网络层将有多个GRU单元顺序连接在一起。GRU只有两个机制,分别是重置门和更新门,并且只有来自前一个GRU单元的一个输入和传递到下一个GRU单元的一个输出。仅有一个输入和输出显然比LSTM更高效,因为我们需要执行的操作更少。现在,让我们深入了解这两个机制。

解码GRU的重置门

GRU的重置门作为遗忘前一个单元的长期信息(也叫隐藏状态)的一种机制。这个机制的目标与LSTM单元中的忘记门类似。类似地,这个机制将在0到1的尺度上,基于当前输入序列和前一个单元状态,决定我们应该减少和移除多少先前获得的长期信息。

然而,GRU的重置门与LSTM的忘记门在功能上有所不同。LSTM的忘记门决定从长期记忆中遗忘哪些信息,而GRU的重置门决定遗忘多少先前的隐藏状态。

解码GRU的更新门

GRU的更新门控制从长期记忆中传递到当前保持的记忆的信息量。这类似于LSTM中的记忆门,有助于网络记住长期信息。与前一个单元的隐藏单元相关的每个权重将学习捕捉短期依赖和长期依赖。短期依赖通常具有接近0的重置门输出值,更频繁地遗忘以前的信息,而长期依赖则具有权重和隐藏状态位置,学习记住长期依赖。

与LSTM的记忆门不同,LSTM的记忆门决定记住当前输入和前一个隐藏状态的哪些信息,而GRU的更新门决定记住多少前一个隐藏状态的信息。

GRU是一种简单的RNN,与LSTM相比具有更高效的操作。现在我们已经解码了LSTM和GRU,在不再重复类似LSTM的完整GRU网络的情况下,让我们探索使用这两种方法作为基础可以进行的改进。

理解标准GRU和LSTM层的进展

GRU和LSTM是目前最广泛使用的RNN方法,但人们可能会想知道如何突破标准GRU或标准LSTM能够达到的边界。构建这种直觉的一个好方法是理解这两种层类型都能接受序列数据,且构建网络时需要多个RNN层。这意味着完全可以将GRU和LSTM层组合在同一个网络中。然而,这不足以被认为是一个进展,因为一个完全的LSTM网络或完全的GRU网络随时可以超越组合LSTM和GRU网络的性能。接下来,让我们深入探讨另一个可以在这些标准RNN层之上进行的简单改进,叫做双向RNN。

解码双向RNN

GRU和LSTM都依赖数据的顺序性。这个序列的顺序可以是时间步增大的正向,也可以是时间步减小的反向。通常,选择哪个方向是一个试错过程,更多时候,使用的自然方向是正向时间顺序。

1997年,出现了一种改进方法,叫做双向RNN,它将正向顺序的RNN与反向顺序的RNN结合,以最大化RNN模型能够处理的输入数据。最初的想法是使用未来信息和历史信息来估计当前时间步的值,因为有两个RNN分别处理不同的数据集,因此同时能获得未来和历史信息。这自然允许在这种数据设置下实现更好的预测性能。今天,这一想法已经扩展为一种通用层,应用于相同的序列数据估计,并且已被证明能够提供预测性能的提升。图4.5展示了使用GRU的双向RNN示例,其中正向和反向GRU的隐藏状态被拼接在一起:

image.png

拼接后的隐藏状态可以传递到全连接层中,以进行标准的监督学习任务。PyTorch中双向GRU的实现示例如下:

RNN = nn.Sequential(
  nn.GRU(
      input_size=10, hidden_size=20,
      num_layers=2, bidirectional=True
  ),
  nn.Linear(in_features=10, out_features=10),
  nn.Softmax(),
)

接下来,让我们探讨基于LSTM的一个改进方法。

为LSTM添加窥视孔

在2000年引入的窥视孔使得单元状态(来自前一个和当前单元),它们承载LSTM的长期记忆,能够影响LSTM单元中的sigmoid门控机制。其直觉是,长期记忆包含了过去时间步的信息,而这些信息在前一个单元的隐藏状态中的短期记忆中是无法获取的。这使得与普通LSTM相比,预测性能得到了提升。图4.6展示了来自单元状态的额外窥视孔连接:

image.png

然而,这种方法的一个陷阱是,由于单元状态具有长期记忆的性质,单元状态可能会随着时间的推移增长为很大的值,因为它是无界的。这可能会导致门控机制总是处于打开状态,从而使得门控机制有时失效。这引出了我们将在下一个小节中讨论的最后一个改进方法。

为LSTM添加工作记忆以超越窥视孔连接的局限性

在2021年,基于LSTM的窥视孔连接进行了改进,通过使用tanh激活函数对单元状态进行约束,从而给单元状态加上了界限。这一简单的改进在实验基准测试中表现出比没有界限的带窥视孔的LSTM更好的性能,被称为LSTM的工作记忆连接。图4.7展示了LSTM的工作记忆连接结构:

image.png

RNN与标准的多层感知机(MLP)相似,因为没有一个单一的通用数据集可以作为不同研究项目的参考。即使使用相同的数据集,结果也可能不是一个决定性的真理来源,因为该数据集不够广泛,无法推广到其他数据集。换句话说,序列数据中没有类似于ImageNet的数据集。文本数据、视频数据和其他时间序列数据彼此之间差异巨大,但本质上都被视为序列数据,并可以输入到RNN中。对于RNN和MLP的基准结果,我们应保持一定的谨慎态度,因为结果可能因数据集而异。然而,图4.8展示了在两个不同的模型设置下,使用COCO数据集进行图像描述任务时,GRU、LSTM、LSTM窥视孔(LSTM-PH)和LSTM工作记忆(LSTM-WM)进行的基准测试版本,使用的度量标准考虑了生成文本的自然性——值越高越好。

image.png

图中显示,LSTM工作记忆(LSTM-WM)在单次实验中,在不同评估分数上都优于其他方法。再次提醒,对结果要持谨慎态度,因为COCO数据集绝不是序列数据或时间序列数据的代表性数据集。
自己尝试在你自己的数据集上测试,才能确定结果!至此,我们已经覆盖了RNN的重要概念,从基础到高级。接下来,让我们总结一下本章的内容。

总结

递归神经网络(RNN)是一种神经网络,明确地将序列数据的归纳偏置包含在其结构中。

RNN有几种变体,但它们都保持相同的高层概念。主要是,它们提供了不同的方式来决定从哪些数据中学习和记住,以及从记忆的记忆阶段中忘记哪些数据。
然而,需要注意的是,最近出现了一种名为transformers的架构(将在第6章《理解神经网络transformers》中介绍),它展示了在处理序列数据时,不一定需要递归就能取得良好的性能。

至此,我们已经完成了RNN的介绍,接下来我们将在下一章简要进入自编码器的世界。