1.从单层网络到经典的RNN结构
首先要了解一下最基本的单层网络,它的结构如下图所示:
输入是x,经过变换Wx+b和激活函数f,得到输出y。
在实际应用中,我们还会遇到很多序列形的数据:
如:
- 自然语言处理问题。x1可以看做是第一个单词,x2可以看做是第二个单词,依次类推
- 语音处理。此时,x1、x2、x3……是每帧的声音信号。
- 时间序列问题。例如每天的股票价格等等
而其中,序列形的数据 就不太好用原始的神经网络处理了。
解释:
- 时间序列数据是指在不同时间点上收集到的数据,这类数据反映了某一事物、现象等随时间的变化状态或程度。这是时间序列数据的定义,当然这里也可以不是时间,比如文字序列,但总归序列数据有一个特点——后面的数据跟前面的数据有关系。
为了建模序列问题,RNN引入了隐状态h(hidden state)的概念,隐状态h可以对序列形的数据提取特征,接着再转换为输出。
先从 h 1 h_{1} h1的计算开始看:
图示中记号的含义是:
a)圆圈或方块表示的是向量。
b)一个箭头就表示对该向量做一次变换。如上图中 h 0 h_{0} h0和 x 1 x_{1} x1分别有一个箭头连接,就表示对 h 0 h_{0} h0和 x 1 x_{1} x1各做了一次变换
- h 1 h_1 h1基于上一个隐藏层的状态 h 0 h_{0} h0和当前的输入 x 1 x_{1} x1计算得来,且提前说一嘴,泛化到任一时刻,便是
h t = f ( W h t − 1 + U x t ) h_{t}=f\left(W h_{t-1}+U x_{t}\right) ht=f(Wht−1+Uxt),而这里的f一般是tanh、sigmoid、ReLU等非线性的激活函数 - 且在实践中, h t h_t ht一般只包含前面若干步而非之前所有步的隐藏状态
h 2 h_{2} h2的计算和 h 1 h_{1} h1类似。但有两点需要注意下:
在计算时,每一步使用的参数U、W、b都是一样的,也就是说每个步骤的参数都是共享的,这是RNN的重要特点,一定要牢记;
前向传播时:存储的状态信息 h t h_{t} ht在每个时刻都被刷新,三个参数矩阵U,W,b自始至终都是固定不变的。
反向传播时:三个参数矩阵U,W,b被梯度下降法更新。
而LSTM中的权值则不共享,因为它是在两个不同的向量中。而RNN的权值为何共享呢?很简单,因为RNN的权值是在同一个向量中,只是不同时刻而已。
依次计算剩下来的(使用相同的参数U、W、b):
这里为了方便起见,只画出序列长度为4的情况,实际上,这个计算过程可以无限地持续下去。
我们目前的RNN还没有输出,得到输出值的方法就是直接通过h进行计算:
正如之前所说,一个箭头就表示对对应的向量做一次类似于f(Wx+b)的变换,这里的这个箭头就表示对h1进行一次变换,得到输出y1
剩下的输出类似进行(使用和y1同样的参数V和c):
这就是最经典的RNN结构,是x1, x2, …xn,输出为y1, y2, …yn,也就是说,输入和输出序列必须要是等长的。
总的来说,RNN(Recurrent Neural Network)是一类用于处理序列数据的神经网络。
2.循环神经网络
我们先来看一个NLP很常见的问题,命名实体识别,举个例子,现在有两句话:
第一句话:I like eating apple!(我喜欢吃苹果!)
第二句话:The Apple is a great company!(苹果真是一家很棒的公司!)
现在的任务是要给apple打Label,我们都知道第一个apple是一种水果,第二个apple是苹果公司,假设我们现在有大量的已经标记好的数据以供训练模型,当我们使用全连接的神经网络时,我们做法是把apple这个单词的特征向量输入到我们的模型中(如下图),在输出结果时,让我们的label里,正确的label概率最大,来训练模型,但我们的语料库中,有的apple的label是水果,有的label是公司,这将导致,模型在训练的过程中,预测的准确程度,取决于训练集中哪个label多一些,这样的模型对于我们来说完全没有作用。问题就出在了我们没有结合上下文去训练模型,而是单独的在训练apple这个单词的label,这也是全连接神经网络模型所不能做到的,于是就有了我们的循环神经网络。
通过上文第一节我们已经知道,RNN是包含循环的网络,在这个循环的结构中,每个神经网络的模块A,读取某个输入 x t x_{t} xt,并输出一个值 h t h_{t} ht(注:输出之前由y表示,从此处起,改为隐层输出h表示),然后不断循环。循环可以使得信息可以从当前步传递到下一步。
这些循环使得RNN看起来非常神秘。然而,如果你仔细想想,这样也不比一个正常的神经网络难于理解。RNN可以被看做是同一神经网络的多次复制,每个神经网络模块会把消息传递给下一个。所以,如果我们将这个循环展开:
链式的特征揭示了RNN本质上是与序列和列表相关的,它们是对于这类数据的最自然的神经网络架构。
总之,RNN在序列中的每个时间步需要两个输入,即时间步t的输入和前一个时间步t-1的隐藏状态 h t − 1 h_{t-1} ht−1,以生成t时的隐藏状态 h t h_{t} ht,最终预测输出 y t y_{t} yt
3.RNN局限性
第一个问题在于,虽然每个隐藏状态都是所有先前隐藏状态的聚合,然随着时间的推移,RNN 往往会忘记某一部分信息。
在这个间隔不断增大时,RNN会丧失学习到连接如此远的信息的能力。
在理论上,RNN绝对可以处理这样的长期依赖问题。人们可以仔细挑选参数来解决这类问题中的最初级形式,但在实践中,RNN则没法太好的学习到这些知识。Bengio,etal.(1994)等人对该问题进行了深入的研究,他们发现一些使训练RNN变得非常困难的相当根本的原因。
换句话说, RNN 会受到短时记忆的影响。如果一条序列足够长,那它们将很难将信息从较早的时间步传送到后面的时间步。
因此,如果你正在尝试处理一段文本进行预测,RNN 可能从一开始就会遗漏重要信息。在反向传播期间(反向传播是一个很重要的核心议题,本质是通过不断缩小误差去更新权值,从而不断去修正拟合的函数),RNN 会面临梯度消失的问题。
因为梯度是用于更新神经网络的权重值(新的权值 = 旧权值 - 学习率*梯度),梯度会随着时间的推移不断下降减少,而当梯度值变得非常小时,就不会继续学习。
在递归神经网络中,获得小梯度更新的层会停止学习—— 那些通常是较早的层。 由于这些层不学习,RNN会忘记它在较长序列中以前看到的内容,因此RNN只具有短时记忆。
而梯度爆炸则是因为计算的难度越来越复杂导致。
第二个问题在于,RNN没法并行训练,相当于推理快但训练慢
正在读本文的你,可曾想过为何RNN没法并行训练?可能很多读者立马反应出来了:在于其每个时间步的输出依赖于前一个时间步的输出
另,顺带说下,因为RNN这个结构,也导致其没法写成卷积形式(因为RNN多了一个非线性的转换函数,比如tanh)
参考文献:
1.如何从RNN起步,一步一步通俗理解LSTM
2.一文通透想颠覆Transformer的Mamba:从SSM、HiPPO、S4到Mamba