技术人生,  机器学习

简述RNN循环神经网络

循环神经网络(Recurrent Neural Network)是一种常用于自然语言处理领域的特别神经网络模型。得益于其环形“记忆”结构,该模型特别适用于处理具有序列特点的信息(Sequential Information)。本文将讲述循环神经网络的原理、结构、训练方法和伴随而来的梯度消失问题,力求从概念上理解RNN。

本文是MUDSS Workshop系列的文字改写版,有关本次workshop的内容材料,欢迎查阅GitHub Repo

文章目录

为什么需要RNN

设想我们在阅读并尝试理解一段文字。作为人类,在阅读的过程中,我们可以很自然的根据上下文和语境理解内容。每当遇到一个单词,我们并不会遗忘之前的语境信息而根据单词本身的含义来进行理解和判断。换言之,我们具有持续性的记忆。但这样一个简单而自然的过程对传统的人工神经网络而言是难以做到的。

我们知道,传统的人工神经网络是对生物神经网最简单而直观的模拟,理论上它有能力完成具有相当复杂程度的任务。但在实际应用中,这种粗线条的模拟在面对特定任务时具有极大的缺陷。其中一个问题是,它的各个输入信号或输出信号之间是彼此独立的。借由连接权重,任何一个输入信号都可以对输出信号产生不同程度的影响,但一个输入信号却无法影响其前后的其他输入信号。在之前的例子中,这意味着传统的神经网络无法有效的保存上下文信息来辅助理解文本。更进一步的,它实际上难以处理任何具备时间或空间顺序的信息。

循环神经网络则可以很好的应对和处理这类问题。

再来考虑一个更具体的例子,假设我们希望根据一段已知文本预测其后合适的新单词。对于传统的神经网络,所有的已知信息将作为输入“公平”的参与计算和决策,使其难以发现句中隐藏的关键信息。而循环神经网络则可以很好的保留上下文信息,并利用它们更准确的做出预测。

RNN模型

模型理解

让我们首先观察循环神经网络的结构表示。

不用怀疑,RNN本身就是如此简单和清晰,它是自连接的(self-connected)。最引人注意的就是其中的回路(Loop),正式因为它的存在令网络具有了保存前序信息的能力。

这一模型表示清晰的体现出了回路和节点的连接方式。\(x_t\)是输入序列中位于\(t\)时刻的输入信号。\(A\)是隐藏层节点、RNN中的神经元,或记忆节点。\(h_t\)是对应的\(t\)时刻的输出信号。

注意,RNN中的隐藏层节点\(A\)实际上有两个输入,一是由外部流入的当前输入信号\(x_t\),二是由回路输入的来自上一个时刻的节点的信号。接着同样产生两个不同的输出信号,一个由\(h_t\)输出,另一个传递至下个时刻。

让我们进一步观察RNN的回路展开形式,将整个网络表达成一个链式的(chain-like)线性序列,这可能更有助于理解刚才的描述。

回顾我们之前提到的,RNN的结构决定了其适合处理具有顺序的序列式的输入。倘若整体输入\(x\)是一句句子,则其中的\(x_t\)可以是句中的一个单词。

模型中,回路的存在使RNN的输出不仅依赖于当前的输入,也依赖于前序计算结果。信息在网络中循环流动,这也正是“循环”这一名称的由来。网络中的神经元节点其实只有一个,在序列信息流动的过程中,该节点每次都会执行相同的计算过程,不断的将更新后的信息循环传递给下一个时刻的自己。因此,该节点捕捉并保存了所有前序的已知信息。这是我们可以将它视为“记忆”的原因。

理论上,回路的特性决定这个链式的序列可以是任意长度的,即RNN可以处理任意长度的输入信息。但之后我们将会看到,实际情况可能没有想象中的那么美好,就如同我们发现传统神经网络时那样。

实际应用

RNN天然的链式结构决定了其非常容易处理顺序的信息,一个最典型的领域是自然语言处理(NLP)。下面列举一些最为寻常的例子。

  • 语音识别(Speech Recognition): 输入一段声波信号的序列,通过模型预测输出对应的语音文本片段及其概率。
  • 语言模型(Language Modelling): 基于一段已知文本,预测在已知前序文本的基础上,原文中每个单词出现的概率。语言模型可以告诉我们任意一句句子出现的可能性,从而推断出句子的正确性的大小。而这也是机器翻译依赖的前期重要基础。
  • 文本生成(Text Generation): 有了上述的语言模型,我们很容易对模型稍加改造形成一个文本的生成模型。给定一段文本作为输入,令模型输出新的后续文本,从而给予AI”写作“的能力。
  • 机器翻译(Machine Translation): 事实上十分类似于文本生成,只不过需要输出的是另一种目标语言的文本。且我们往往在已知完整输入后才开始生成目标文本,以保留完整的上下文信息。
  • 图像标注(Image Captioning): 结合卷积神经网络(Convolutional Neural Network)对像素区块的特征提取,RNN可以用于生成无标签图像的内容描述。该应用结合了两种模型的强大能力。
图像标注任务

符号化表示

下面我们回到RNN模型本身,使用更精确的数学符号阐述RNN的原理和具体的计算过程。

首先为之前的结构模型定义一组更严谨的参数符号,让我们一一分析。

  • \(x_t\): 位于时序\(t\)时刻的输入。
  • \(U,V,W\): 分别对应输入与隐藏层节点、隐藏层节点与输出,及隐藏层节点间的连接的权重矩阵。注意:不同时序的计算共享同样的参数矩阵,即意味着每次迭代除输入不同外,均执行完全相同的计算。这样的设计极大减少了模型需要学习的参数数量。
  • \(o_t\): 位于时序\(t\)时刻的输出,计算方法为权重矩阵\(V\)与当前时刻隐藏层节点输出\(s_t\)的乘积,相当于神经网络中前馈信号的计算。拿语言模型举例,通常我们需要输出字典中各个单词出现的概率,因此可以对计算结果额外采用\(softmax\)激活函数。
  • \(s_t\): 位于时序\(t\)时刻的隐藏层节点,也是RNN的“记忆”节点。计算方法:$$s_t = tanh(Ux_t + Ws_{t-1})$$类似的,基于前一隐藏层节点和当前时刻的输入执行前馈信号计算。并对结果采用\(tanh\)(Hyperbolic Tangent)非线性激活函数。相较于\(ReLU\)函数,\(tanh\)函数可以在一定程度上缓解RNN的梯度消失问题。另外,为统一计算,通常指定\(s_{-1}\)初始化为\(0\)矩阵。

RNN训练 – BPTT

提到神经网络的训练,就不得不说到两个关键点:误差计算和反向传播(Backpropagation)。RNN的训练也是如此,对于每个时刻的输出\(o_t\),我们可以和传统的人工神经网络一样选取合适的误差计算方法,如交叉熵损失(Cross-entropy Loss)

对于反向传播,我们仍然采用梯度下降法(Gradient Descent),即损失函数的导数的负方向,更新权重。从输出节点\(o_t\)开始,反向传播误差到对应的隐藏层节点,再到输入节点。而与传统的神经网络不同的是,由于时序的引入,神经网络的权重矩阵由所有时刻共享。因此,误差梯度不仅取决于当前时刻的输出,还受前一时刻的计算结果的影响。基于这一想法,我们在原始的反向传播的基础上,令隐藏层和输入层的误差信号同样沿时序方向向后传播。这样从前往后依次计算误差,使每一层的误差由来自输出层和上一时刻的误差合并得到。

这种改进后的方法称为随时间反向传播(Backpropagation Through Time)

下面进一步给出训练过程的数学描述。这里我们假设采用的损失函数\(g\)为常用于多分类任务的交叉熵损失,隐藏层与输出层节点的计算遵循上文的描述。$$\begin{align} s_t &= tanh(Ux_t + Ws_{t-1}) \\ o_t &= softmax(Vs_t) \end{align}$$

定义位于\(t\)时刻的误差为$$L_t = g(o_t)$$

则总体误差为$$L = \sum_{t=0}^T L_t$$

最后分别对参数\(V,W,U\)求偏导,以进行梯度下降更新,得到以下结果。

$$\begin{align} \frac{\partial L}{\partial V} &= \sum_{t=0}^T \frac{\partial L_t}{\partial V} \\ \frac{\partial L}{\partial W} &= \sum_{t=0}^T \sum_{k=0}^t \frac{\partial L_t}{\partial s_t} (\prod_{j=k+1}^t \frac{\partial s_j}{\partial s_{j-1}}) \frac{\partial s_k}{\partial W} \\ \frac{\partial L}{\partial U} &= \sum_{t=0}^T \sum_{k=0}^t \frac{\partial L_t}{\partial s_t} (\prod_{j=k+1}^t \frac{\partial s_j}{\partial s_{j-1}}) \frac{\partial s_k}{\partial U} \end{align}$$

进一步观察这些结果,我们可以发现,在对\(W\)和\(U\)求偏导时,其中的内项求和将从时刻\(0\)到当前时刻\(t\)的损失值相加,这正对应了BPTT的核心思想。

梯度消失问题与LSTM

继续观察刚才的公式,另一个值得注意的地方是,\(W\)和\(U\)的偏导的计算结果中都包含了一个连乘项。稍加分析后不难发现,若连乘的项目数量不断增加,即\(t\)极大时,若\(\frac{\partial s_j}{\partial s_{j-1}}\)的介于0~1之间,则连乘结果将无限接近于0,反之若大于1,则连乘结果将趋近正无穷。这就是RNN不可避免的梯度消失(Gradient Vanishing)和梯度爆炸(Gradient Explosion)问题。

梯度消失/爆炸现象导致在梯度下降的计算中会出现极小/极大的梯度值,从而致使模型参数在训练中难以被继续更新。

因此,一般的RNN模型难以应对较长序列的输入,隐藏层节点的记忆能力有限,无法持续存储过去长远的信息。随着序列长度的增长,RNN不可避免的逐渐失去了学习能力。针对这一问题,改进后的长短期记忆网络(Long Short Term Memory Network)横空出世。它通过增加一个并行的记忆通道,专门用于存储长期信息,称作Cell State。该通道通过累加运算消除了连乘带来的影响。也因此,LSTM在NLP任务中比RNN更为常用。

本文不对LSTM做详细的描述,想了解更多请前往Understanding LSTMs,以及RNN/LSTM BPTT详细推导以及梯度消失问题分析

缺陷与改进

RNN只能按照时间顺序从前往后传递信号,然而,很多时候期望的输出不仅依赖于前序信息,更依赖于未来的序列。如在文本预测中,我们往往需要了解整个上下文后才能做出判断。基于此,我们可以对RNN稍加改进,在每个时刻处,增加一个并列的隐藏层节点用于接收未来节点的信号,再将两个节点的信息合并传递到输出层。该方法得到的新模型称作双向RNN(Bidirectional RNN)

Bidirectional RNN

由于RNN在不同时刻共享参数,导致模型的可训练参数数量较少,面对更为复杂的问题时无法很好满足需求。我们可以借鉴深度神经网络的经验,为RNN添加多个隐藏层,使信号经过多层传递后输出。这样便提高了模型的学习能力,但同时意味着更多的训练参数和更大的数据样本。

Deep Bidirectional RNN

PyTorch代码示例

有关使用PyTorch实现RNN执行语言模型分类任务的代码示例,请继续前往GitHub

A WindRunner. VoyagingOne

留言

您的邮箱地址不会被公开。 必填项已用 * 标注