15-循环神经网络

2025-01-13 09:00

🐯 循环神经网络 Recurrent Neural Networks


💡 循环神经网络 RNN 也称循环序列模型,在语音识别、自然语言处理和其他领域中引起了巨大的变革。

一、什么是序列模型 Sequence Models

序列模型能够应用在许多领域,例如:

  • 语音识别

  • 音乐生成器

  • 情感分类

  • DNA序列分析

  • 机器翻译

  • 视频动作识别

  • 命名实体识别

image.png

💬 比如说:在进行语音识别时,给定了一个输入音频片段 X,并要求输出对应的文字记录 Y。这个例子里输入 X 和输出数据 Y 都是序列模型,因为 X 是一个按时播放的音频片段,输出 Y 是一系列单词。

所有这些问题都可以被称作使用标签数据 $(X,Y)$ 作为训练集的监督学习。但从这一系列例子中你可以看出序列问题有很多不同类型。

二、序列模型的符号定义 Notation

下面以命名实体识别为例(命名实体识别:给定一个句子识别出句中的人名),介绍序列模型的命名规则。示例语句为:

Harry Potter and Hermione Granger invented a new spell.

该句话包含 9 个单词,输出 Y 即为 1 x 9 向量,每位表征对应单词是否为人名的一部分,1 表示是,0 表示否。很明显,该句话中 “Harry”,“Potter”,“Hermione”,“Granger” 均是人名成分,所以,对应的输入 Y 可表示为:

image.png

一般约定使用 $y^{}$ 表示序列对应位置的输出,使用 $T_y$ 表示输出序列长度,则 $1\leq t\leq T_y$:

💡 t 意味着它们是时序序列,但不论是否是时序序列,我们都将用来索引序列中的位置。

同样,$x^{}$ 表示序列对应位置的输入,$T_x$ 表示输入序列长度。注意,此例中,$T_x=T_y$ ,但是也存在 $T_x\neq T_y$ 的情况

image.png

❓ 如何来表示每个 $x^{}$ 呢?方法是首先建立一个词汇库 vocabulary,尽可能包含更多的词汇。例如一个包含 10000 个词汇的词汇库为:

image.png

该词汇库可看成是 10000 x 1 的向量。然后,使用 one-hot 编码,例句中的每个单词 $x^{}$ 都可以表示成 10000 x 1 的向量,词汇表中与 $x^{}$ 对应的位置为1,其它位置为0。该 $x^{}$ 为 one-hot 向量。值得一提的是如果出现词汇表之外的单词,可以使用 UNK 或其他字符串来表示。

对于多样本,以上序列模型对应的命名规则可表示为:$X^{(i)}$,$y^{(i)}$ ,$T_x^{(i)}$ ,$T_y^{(i)}$ 。其中,$i$ 表示第 $i$ 个样本。不同样本的 $T_x^{(i)}$ 或 $T_y^{(i)}$ 都有可能不同。

三、循环神经网络模型 Recurrent Neural Network Model

① 基础结构

对于序列模型,如果使用标准的神经网络,其模型结构如下:

image.png

但结果表明这个方法并不好,主要有两个问题:

  • 第一个问题,不同样本的输入序列长度或输出序列长度可能不同,即 $T_x^{(i)}\neq T_x^{(j)}$ ,造成模型难以统一。解决办法之一是设定一个最大序列长度,对每个输入和输出序列补零并统一到最大长度。但是这种做法实际效果并不理想。

  • 第二个问题,也是主要问题,这种标准神经网络结构无法共享序列不同 $x^{}$ 之间的特征。例如,如果某个 $x^{}$ 比如 “Harry”是人名成分,那么句子其它位置出现了 “Harry”,也很可能也是人名。这是共享特征的结果,如同 CNN网络特点一样。但是,上图所示的网络不具备共享特征的能力。值得一提的是,共享特征还有助于减少神经网络中的参数数量,一定程度上减小了模型的计算复杂度。例如上图所示的标准神经网络,假设每个 $x^{}$ 扩展到最大序列长度为 100,且词汇表长度为10000,则输入层就已经包含了 100 x 10000个 神经元了,权重参数很多,运算量将是庞大的。

    标准的神经网络不适合解决序列模型问题,而循环神经网络(RNN)是专门用来解决序列模型问题的。RNN 模型结构如下:

    image.png

    如果你以从左到右的顺序读这个句子,第一个单词是$x^{<1>}$,我们要做的就是将第一个词输入一个神经网络层,神经网络尝试预测输出,判断这是否是人名的一部分。当神经网络读到句中的第二个单词 $x^{<2>}$ 时 ,它不是仅用$x^{<2>}$就预测出${\hat{y}}^{<2>}$,它也会使用一些来自时间步 1 的信息。具体而言,时间步 1 的激活值会传递到时间步 2。也就是说在每一个时间步中,循环神经网络传递一个激活值到下一个时间步中用于计算。比如说在预测${\hat{y}}^{< 3 >}$时,不仅要使用$x^{<3>}$的信息,还要使用来自$x^{<1>}$和$x^{<2>}$的信息

    要开始整个流程,在零时刻需要构造一个激活值 $a^{<0>}$,这通常是零向量。有些研究人员会随机用其他方法初始化,不过使用零向量作为零时刻的伪激活值是最常见的选择。

    上述我们提到的 RNN 实际上是单向循环神经网络,这个网络的一个缺点就是它只使用了这个序列中之前的信息来做出预测,尤其当预测${\hat{y}}^{<3>}$时,它没有用到$x^{<4>}$,$x^{<5>}$,$x^{<6>}$等等的信息。所以这就有一个问题,因为如果给定了这个句子,“Teddy Roosevelt was a great President,为了判断Teddy是否是人名的一部分,仅仅知道句中前两个词是完全不够的,还需要知道句中后部分的信息,这也是十分有用的,因为句子也可能是这样的,“Teddy bears are on sale”。显然,这个句子中的 Teddy 并不是人名。之后我们将学习双向循环神经网络 BRNN 解决这个问题。

② 正向传播 Forward Propagation

单向循环神经网络是从左向右扫描数据,同时每个时间步的参数也是共享的,我们用$W_{\text{ax}}$来表示管理着从$x^{<1>}$到隐藏层的连接的一系列参数,每个时间步使用的都是相同的参数$W_{\text{ax}}$。而激活值也就是水平联系是由参数$W_{aa}$决定的,同时每一个时间步都使用相同的参数$W_{aa}$,同样的输出结果由$W_{\text{ya}}$决定。下图详细讲述这些参数是如何起作用:

💡 以 $W_{ax}$ 为例:第二个下标意味着 $W_{\text{ax}}$ 要乘以某个$x$类型的量,第一个下标$a$表示它是用来计算某个 $a$ 类型的变量。

image.png

一般开始先输入$a^{<0>}$,它是一个零向量。接着就是正向传播过程,先计算激活值$a^{<1>}$,然后再计算$y^{<1>}$,......

$a^{} = g_{1}(W_{{aa}}a^{< t-1 >} + W_{{ax}}x^{< t >} + b_{a})$,g 表示激活函数

$\hat y^{< t >} = g_{2}(W_{{ya}}a^{< t >} + b_{y})$

为了帮我们建立更复杂的神经网络,将这个符号简化一下

image.png

⭐ 则正向传播可表示为:

image.png

③ 通过时间的反向传播 Backpropagation through time

针对上面的例子,反向传播的过程是为了快速的求出前向传播过程中需要使用的参数 $W_a, W_y, b_a, b_y$

为了计算反向传播,我们先定义一个对于单个样本的损失函数 Loss Function:

image.png

所有样本的代价函数 Cost function 为:

image.png

然后,反向传播(Backpropagation)过程就是从右到左分别计算 $L(\hat y,y)$ 对参数 $W_{a}$ ,$W_{y}$ ,$b_a$ ,$b_y$ 的偏导数。思路与做法与标准的神经网络是一样的。一般可以通过成熟的深度学习框架自动求导,例如 PyTorch、Tensorflow等。

这种从右到左的求导过程被称为 Backpropagation through time。取这个名字的原因是:对于前向传播你需要从左到右进行计算,在这个过程中,时刻 t 不断增加。而对于反向传播,你需要从右到左进行计算,就像时间倒流。

四、不同类型的循环神经网络

以上介绍的例子中,$T_x=T_y$ 。但是在很多 RNN 模型中,$T_x$ 是不等于 $T_y$ 的。 根据 $T_x$ 与 $T_y$ 的关系,RNN 模型包含以下几个类型:

  • Many to many: $T_x=T_y$

  • Many to many: $T_x\neq T_y$

  • Many to one: $T_x>1,T_y=1$

  • One to many: $T_x=1,T_y>1$

  • One to one: $T_x=1,T_y=1$

不同类型相应的示例结构如下:

image.png

五、语言模型和序列生成 Language model and sequence generation

语言模型是自然语言处理(NLP)中最基本和最重要的任务之一。使用 RNN 能够很好地建立需要的不同语言风格的语言模型。

什么是语言模型呢?举个例子,在语音识别中,某句语音有两种翻译:

  • The apple and pair salad.

  • The apple and pear salad.

很明显,第二句话更有可能是正确的翻译。语言模型实际上会计算出这两句话各自的出现概率。比如第一句话概率为 ,第二句话概率为 。也就是说,利用语言模型得到各自语句的概率,选择概率最大的语句作为正确的翻译。概率计算的表达式为:

$P(y^{<1>},y^{< 2 >},...,y^{<T_y>})$

如何使用RNN构建语言模型?首先,我们需要一个足够大的训练集,训练集由大量的单词语句**语料库(corpus)**构成。然后,对 corpus 的每句话进行切分词(tokenize)。对每个单词进行one-hot编码。例如下面这句话:

The Egyptian Mau is a bread of cat.

One-hot 编码已经介绍过了,不再赘述。还需注意的是,每句话结束末尾,需要加上 < EOS > 作为语句结束符。另外,若语句中有词汇表中没有的单词,用 < UNK > 表示。假设单词 “Mau” 不在词汇表中,则上面这句话可表示为:

The Egyptian < UNK > is a bread of cat. < EOS >

准备好训练集并对语料库进行切分词等处理之后,接下来构建相应的RNN模型。

image.png

语言模型的RNN结构如上图所示,$x^{ <1 >}$ 和 $a^{<0>}$ 均为零向量。Softmax 输出层 $\hat y^{<1>}$ 表示出现该语句第一个单词的概率,softmax 输出层 $\hat y^{<2>}$ 表示在第一个单词基础上出现第二个单词的概率,即条件概率,以此类推,最后是出现 < EOS > 的条件概率。

单个元素的 softmax Loss Function 为:

image.png

该样本所有元素的 Cost Function 为:

image.png

对语料库的每条语句进行RNN模型训练,最终得到的模型可以根据给出语句的前几个单词预测其余部分,将语句补充完整。例如给出 “Cats average 15”,RNN模型可能预测完整的语句是 “Cats average 15 hours of sleep a day.”

最后补充一点,整个语句出现的概率等于语句中所有元素出现的条件概率乘积。例如某个语句包含$y^{<1>},y^{<2>},y^{<3>}$,则整个语句出现的概率为:

image.png

六、对新序列采样 Sampling novel sequences

利用训练好的RNN语言模型,可以进行新的序列采样,从而随机产生新的语句。与上一节介绍的一样,相应的RNN模型如下所示:

image.png

第一步要做的就是对你想要模型生成的第一个词进行采样,于是你输入$x^{<1>} =0$,$a^{<0>} =0$,现在你的第一个时间步得到的是所有可能的输出是经过softmax层后得到的概率,然后根据这个 softmax 的分布 $ \hat y^{<1>}$ 进行随机采样,即随机选取一个 word 作为新语句的首单词。

然后, $y^{<1>}$ 作为 $x^{<2>}$ ,得到 $ \hat y^{<1>}$ 的softmax分布。从中选取概率最大的word作为 $y^{<2>}$,继续将$y^{<2>}$作为$x^{<3>}$,以此类推。直到产生 < EOS > 结束符,则标志语句生成完毕。当然,也可以设定语句长度上限,达到长度上限即停止生成新的单词。最终,根据随机选择的首单词,RNN模型会生成一条新的语句。

如果不希望新的语句中包含 < UNK > 标志符,可以在每次产生 < UNK > 时重新采样,直到生成非 < UNK > 标志符为止。

七、循环神经网络的梯度消失/爆炸 Vanishing gradients with RNNs

① 梯度消失问题

语句中可能存在跨度很大的依赖关系,即某个word可能与它距离较远的某个word具有强依赖关系。例如下面这两条语句:

  • The cat, which already ate fish, was full.

  • The cats, which already ate fish, were full.

第一句话中,was 受 cat影响;第二句话中,were受cats影响。它们之间都跨越了很多单词。而一般的RNN模型每个元素受其周围附近的影响较大,难以建立跨度较大的依赖性。上面两句话的这种依赖关系,由于跨度很大,普通的RNN网络容易出现梯度消失,捕捉不到它们之间的依赖,造成语法错误。

💡 回顾一下梯度消失的原理:比如说一个很深很深的网络,100层,甚至更深,对这个网络从左到右做前向传播然后再反向传播。我们知道如果这是个很深的神经网络,从输出得到的梯度很难传播回去,很难影响到前层的权重。这就意味着,很难让一个很深的神经网络能够意识到它要记住看到的是单数名词还是复数名词,然后在序列后面生成依赖单复数形式的was或者were

② 梯度爆炸问题

另一方面,RNN也可能出现梯度爆炸的问题,即梯度过大。我们在反向传播的时候,随着层数的增多,梯度不仅可能指数型的下降,也可能指数型的上升。事实上梯度消失在训练 RNN 时是首要的问题,尽管梯度爆炸也是会出现,但是梯度爆炸很明显,因为指数级大的梯度会让你的参数变得极其大,以至于你的网络参数崩溃。所以梯度爆炸很容易发现,因为参数会大到崩溃,你会看到很多 NaN,或者不是数字的情况,这意味着你的网络计算出现了数值溢出

常用的解决梯度爆炸的办法是设定一个阈值,一旦梯度最大值达到这个阈值,就对整个梯度向量进行尺度缩小。这种做法被称为 梯度修剪 gradient clipping

③ 解决梯度消失问题

Ⅰ 门控循环单元 GRU

Gated Recurrent Unit(GRU) 门控循环单元,它改变了RNN的隐藏层,使其可以更好地捕捉深层连接,并改善了梯度消失问题。

RNN的隐藏层单元结构如下图所示:

image.png

$a^{}$ 的表达式为:

image.png

为了解决梯度消失问题,对上述单元进行修改,添加了记忆单元,构建 GRU,如下图所示:

image.png

相应的表达式为:

image.png

上面介绍的是简化的GRU模型,完整的GRU添加了另外一个 gate,即 $\Gamma_r$ ,表达式如下:

image.png

Ⅱ 长短期记忆 LSTM

LSTM是另一种更强大的解决梯度消失问题的方法。它对应的RNN隐藏层单元结构如下图所示:

image.png

相应的表达式为:

image.png

GRU 可以看成是简化的 LSTM,两种方法都具有各自的优势。

八、双向循环神经网络 Bidirectional RNN

前面我们已经提到过,BRNN 可以用于解决单向神经网络的缺点,让你在序列的某点处不仅可以获取之前的信息,还可以获取未来的信息,BRNN 的结构如下图所示:

image.png

这样,这个网络就构成了一个无环图。

给定一个输入序列$x^{<1>}$到$x^{<4>}$,这个序列首先计算前向的${\overrightarrow{a}}^{<1>}$,然后计算前向的${\overrightarrow{a}}^{<2>}$,接着${\overrightarrow{a}}^{<3>}$,${\overrightarrow{a}}^{<4>}$。而反向序列从计算${\overleftarrow{a}}^{<4>}$开始,反向进行,计算反向的${\overleftarrow{a}}^{<3>}$。注意你计算的是网络激活值,这不是反向传播而是前向的传播 !!!,而图中这个前向传播一部分计算是从左到右,一部分计算是从右到左。计算完了反向的${\overleftarrow{a}}^{<3>}$,可以用这些激活值计算反向的${\overleftarrow{a}}^{<2>}$,然后是反向的${\overleftarrow{a}}^{<1>}$,把所有这些激活值都计算完了就可以计算预测结果了。

BRNN对应的输出 $ y^{}$ 表达式为:

image.png

BRNN能够同时对序列进行双向处理,性能大大提高。但是计算量较大,且在处理实时语音时,需要等到完整的一句话结束时才能进行分析。

九、深层循环神经网络 Deep RNNs

Deep RNNs由多层RNN组成,其结构如下图所示:

image.png

与DNN一样,用上标 $[l]$ 表示层数。Deep RNNs 中 $a^{[l]}$ 的表达式为:

image.png

Deep RNNs 一般没有那么多层,3 层 RNNs 已经较复杂了。

另外一种 Deep RNNs 结构是每个输出层上还有一些垂直单元,如下图所示:

image.png

📚 Reference

相关文章
热点文章
精彩视频
Tags

在线访客: 今日访问量: 昨日访问量: 总访问量:

×
请扫码支付

扫码支付后自动跳转查看