Bootstrap

【通俗易懂说模型】循环神经网络模型 · LSTM(呕心沥血版)

🌈 个人主页:十二月的猫-CSDN博客
🔥 系列专栏: 🏀深度学习_十二月的猫的博客-CSDN博客

💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 

目录

1. 前言

2. 循环神经网络模型

3. LSTM模型

3.1 对神经网络的思考

3.2 对连接的思考

3.3 对单层/多层网络结构的思考

3.4 对CNN的思考

3.5 对RNN的思考

3.6 梯度流过长导致的梯度消失/梯度爆炸问题

3.6.1 反向传播的总体观

3.6.2 反向传播的局部观

3.6.3 如何理解梯度流

3.7 LSTM 网络

4. LSTM 的核心思想

4.1 细胞状态(保持门)

4.2 遗忘机制(忘记门)

4.3 更新机制(输入门)

4.4 输出机制(输出门)

5. 总结


1. 前言

        进入这篇文章之前,想必大家已经阅读过前面的系列文章:

【通俗易懂说模型】线性回归(附深度学习、机器学习发展史)-CSDN博客

【通俗易懂说模型】非线性回归和逻辑回归(附神经网络图详细解释)-CSDN博客

【通俗易懂说模型】反向传播(附多元分类与Softmax函数)-CSDN博客

【通俗易懂说模型】卷积神经网络(呕心沥血版)-CSDN博客

【通俗易懂说模型】一篇弄懂几个经典CNN图像模型(AlexNet、VGGNet、ResNet)-CSDN博客

【通俗易懂说模型】循环神经网络模型 · RNN-CSDN博客

        通过前面的文章, 猫猫相信友友们对深度学习、机器学习一定有了一个较为全面且细致的理解,接下来的本篇文章,将基于前面提到的回归、反向传播、卷积神经网络等知识,从深度学习在图像识别领域发展的历史脉络出发,带你走入序列转序列模型。从图像识别中的卷积神经网络转入序列模型中的循环神经网络模型模型,学习人工智能中的奇思妙想,感悟前辈伟人的思想精华🥰

2. 循环神经网络模型

       前面我们学习过卷积神经网络模型(CNN),就是如下这个模型,CNN模型是基于卷积操作完成对图像得特征提取。(总结一下,前面我们已经学过两种模型:1、连接模型/全连接等,用于回归、分类操作;2、卷积神经网络模型,用于图像特征提取)

        序列转序列模型的就是记忆+理解。如果我们能把语言记忆下来(语言前后能够联系起来),同时理解语言的句子,那么我们就能够完成翻译或者其他操作。之前学习的卷积神经网络受到了生物视觉细胞的启发,相似地,循环神经网络受到了生物记忆能力的启发。循环神经网络是具有循环结构的一类神经网络,我们又称之为RNN,此外还有RNN的加强版LSTM和GRU,它们都拥有更强的“记忆力”。接下来,我们重点对LSTM进行讲解

循环神经网络(RNN):针对 序列转序列模型

语言理解的核心:记忆


        考虑到,LSTM有一篇在全世界广为流传的文章《理解LSTM网络》,因此本文主要是对该经典文章的翻译加上译者自己的理解。


3. LSTM模型

3.1 对神经网络的思考

        神经网络模型指的是模仿人类神经系统,一级级一层层提取更多信息,进行更多处理,最终做出判断的一系列模型。其特点是 多层次结构

        神经网络模型主要包括:基于视觉神经系统的卷积神经网络模型基于记忆神经系统的循环神经网络基于人脑思考系统的连接神经网络。见下图(图1是卷积神经网络、图2是连接神经网络、图3是循环神经网络):

        神经网络模型中的底层数学操作:卷积神经网络模型运用数学中的卷积操作提取图片特征从而进行图像处理;连接神经网络运用数学中的连接操作转化向量为最终输出结果;循环神经网络本质就是连接神经网络,只不过是无限长的连接神经网络

3.2 对连接的思考

        在学习LSTM之前,得先学习RNN,而在学习RNN之前,首先要了解一下最基本的单层网络,在了解单层网络结构之前,要了解连接是什么:连接本质就是变化 与 激活

         如下图所示,输入是x,经过变换Wx+b和激活函数f,得到输出y,这就是一个连接操作:

3.3 对单层/多层网络结构的思考

        每一层仅仅做一个操作(连接或者卷积)的就是单层网络结构,进行多次连接操作的就是多层网络结构。多层网络结构能够提取更多信息&&做更为复杂的信息处理。神经网络是指像神经元一样的网络结构,因此都是多层网络结构,因为需要复杂的网络结构去完成更复杂的信息提取和信息处理。单层网络结构就是我们平常说的单层感知机模型(神经网络模型的前身)见下图:

        多层网络结构就是单层感知机模型的多层叠加,见下图: 

3.4 对CNN的思考

        人类并不是每时每刻都从一片空白的大脑开始他们的思考。在你阅读这篇文章时候,你都是基于自己已经拥有的对先前所见词的理解来推断当前词的真实含义。我们不会将所有的东西都全部丢弃,然后用空白的大脑进行思考。我们的思想拥有持久性。

        传统的神经网络CNN并不能做到这点,看起来也像是一种巨大的弊端。例如,假设你希望对电影中的每个时间点的时间类型进行分类。传统的神经网络应该很难来处理这个问题——使用电影中先前的事件推断后续的事件。RNN 解决了这个问题。RNN 是包含循环的网络,允许信息的持久化。

        简单来说,CNN的问题是不存在记忆性。但是假如不存在记忆,一个输入就对应一个输出,那么我们就永远谈不上句子的理解。

        因为想要理解一个句子就需要对该句子有前后的所有单词有记忆,如此才能联合有一个理解,产生一个最终的输出结果

3.5 对RNN的思考

        通过下面的文章,我们能够对RNN有一个初步的理解,下面简单对文章进行概括和进一步拓展。【通俗易懂说模型】循环神经网络模型 · RNN-CSDN博客

        RNN循环神经网络是连接神经网络的拓展版本,可以认为是无限的连接神经网络,如下图。每一个方块中都是一个连接神经网络,然后多个连接神经网络构成一个循环神经网络。

        循环过程用数学公式表示如下。值得注意的是:在计算时,每一步使用的参数U、W、b都是一样的,也就是说每个步骤的参数都是共享的,这是RNN的重要特点

 h_{t}=f\left(W h_{t-1}+U x_{t}+b\right)

        RNN的功能可以分为两个部分:记忆+理解

        1、记忆:将记忆过程进行拆解可以得到下面两张图,每张图往后循环一轮:

        如此,反复直到所有输入都被记忆,这里我们假设输入序列长度为4,理论上这个长度是无限的:

        2、理解:通过上面“记忆”过程的层层循环,模型已经完全记住前面所有的输入,此时根据所有的输入去输出最后的结果,然后根据结果和事实的差距进行反向传播和梯度下降,这就是我们的“理解”。


        但是,RNN是存在问题的,一轮轮的循环会导致梯度不停的相乘(一般情况下梯度值小于1),因此当循环链比较长时,多个梯度相乘后梯度值会无限接近于0,这会使得反向传播时,更新梯度非常之小,前面的参数得不到更新,也就是RNN对前面输入的理解将会不到位。上面的解释有一个专业的名词叫做:长期依赖(Long-TermDependencies)问题

        RNN的关键点之一就是他们可以用来连接先前的信息到当前的任务上,例如使用过去的视频段来推测对当前段的理解。如果RNN可以做到这个,他们就变得非常有用。但是真的可以么?答案是,还有很多依赖因素。

        有时候,我们仅仅需要知道先前的信息来执行当前的任务。例如,我们有一个语言模型用来基于先前的词来预测下一个词。如果我们试着预测 “the clouds are in the sky” 最后的词,我们并不需要任何其他的上下文 —— 因此下一个词很显然就应该是 sky。在这样的场景中,相关的信息和预测的词位置之间的间隔是非常小的,RNN 可以学会使用先前的信息。

        但是同样会有一些更加复杂的场景。假设我们试着去预测“I grew up in France... I speak fluent French”最后的词。当前的信息建议下一个词可能是一种语言的名字,但是如果我们需要弄清楚是什么语言,我们是需要先前提到的离当前位置很远的 France 的上下文的。这说明相关信息和当前预测位置之间的间隔就肯定变得相当的大。不幸的是,在这个间隔不断增大时,RNN 会丧失学习到连接如此远的信息的能力。

        在理论上,RNN 绝对可以处理这样的 长期依赖 问题。人们可以仔细挑选参数来解决这类问题中的最初级形式,但在实践中,RNN 肯定不能够成功学习到这些知识。Bengio, et al. (1994)等人对该问题进行了深入的研究,他们发现一些使训练 RNN 变得非常困难的相当根本的原因。然而,幸运的是,LSTM 并没有这个问题!

既然问题是梯度消失,那么只要控制好每一个权重参数让整个训练过程中wt的值相对较大(可以通过调整输入数据或者b的值让wt的值变大,或者ReLU激活函数保证输出值不会过小,从而增加每次梯度下降的值,减缓“梯度消失”现象

3.6 梯度流过长导致的梯度消失/梯度爆炸问题

        想要理解为什么会有长期依赖问题,就要理解梯度消失/爆炸问题;而想要理解梯度消失/爆炸问题就要理解反向传播与梯度下降两个算法;想要理解反向传播就可以从总体观和局部观两个角度。

3.6.1 反向传播的总体观

         现在,我们先讨论一个最简单的例子:隐含层的层数为1的神经网络的前向传播及反向传播的过程。如下图所示,我们的前向传播过程为:输入\overrightarrow{z}^1,经过隐含层得到\overrightarrow{z}^2,再经过输出层得到\overrightarrow{z}^3,经过损失函数得到损失值{z}^4

        接着进行反向传播,为了方便计算和推导,我们定义\overrightarrow{\delta }变量,暂时不去考虑\overrightarrow{\delta }代表什么。我们可以用一种抽象的方式去审视反向传播的过程,如下图所示。这个过程首先将\delta ^4=\frac{dL}{dL}=1作为输入,然后由\delta ^4反向传播至第3层各节点得到\overrightarrow{\delta ^3}\overrightarrow{\delta ^3}反向传播经过第2层各节点得到\overrightarrow{\delta ^2},利用\overrightarrow{z^3}\overrightarrow{\delta ^3}及第2层到第3层之间的权重矩阵W^{(2,3)}求得梯度值\frac{\partial L}{\partial W^{(2,3)} }。接着,\overrightarrow{\delta ^2}继续反向传播得到\overrightarrow{\delta ^1},我们利用\overrightarrow{z^2}\overrightarrow{\delta ^2}及第1层到第2层之间的权重矩阵W^{(1,2)}可以求得梯度值\frac{\partial L}{\partial W^{(1,2)} }

反向传播的理解:

  • 前向传播中一个数据点会生成三个下一层中的中间值。那么在反向传播中,每一个下一层中的中间值都会反向来调整数据点的参数。
  • 反向传播本质上是求解参数相对于最终输出Loss的影响大小(梯度)的。
  • 上面有一个词用的非常模糊——反向传播。你可能会很疑惑,这个反向传播是怎么工作的,为什么就求出梯度大小了。下面的逐级观将给你答案。
  • 总体观只要关注到\overrightarrow{\delta }即可,不用考虑\overrightarrow{\delta }具体值是什么,这个值又是如何求解的

3.6.2 反向传播的局部观

        上面猫猫展示了反向传播的总体过程,如果你还没有完全理解也没关系,下面我们要探讨每
层的细节。如下图所示,我们将神经网络的第1层作为代表进行观察,第1层的输入是\overline{z}^l,设第I层到第I+1层之间的权重矩阵为W^{(l,l+1)},于是可以将数据经过该层的变换看作函数f^l的运算。

前向传播的数学表达式如下:

\vec{z}^{l+1}=f^{l}(\vec{z}^{l},W^{(l,l+1)})

为了方便描述反向传播的过程,我们定义{\delta ^l_j},数学表达如下:

\delta_{j}^{l}=\frac{\partial L}{\partial z_{j}^{l}}

根据微积分的链式法则,可以得到:

\delta_{j}^{l}=\frac{\partial L}{\partial z_{j}^{l}}=\sum_{k}\frac{\partial L}{\partial z_{k}^{l+1}}\cdot\frac{\partial z_{k}^{l+1}}{\partial z_{j}^{l}}=\sum_{k}\delta_{k}^{l+1}\frac{\partial z_{k}^{l+1}}{\partial z_{j}^{l}}

        上面公式表明想要求解出l层神经元相对于最终输出L的影响度,则需要求解出\frac{\partial z_k^{l+1}}{\partial z_j^l}的值,同时利用后一层也就是l+1层神经元相对于最终输出L的影响度。而\frac{\partial z_k^{l+1}}{\partial z_j^l}的值就是第l层和第l+1层之间的权重值W,因此核心就在于求出每一层之间的权重。到这里我们就求出了每一个神经元对于输出Loss的梯度。接下去就要思考,这个梯度如何用来更新权重。

        知道神经元对于Loss的梯度值后,我们就要根据梯度方向的相反方向去修正神经元的值。修正神经元的值本质就是修正神经元W权重的值。因此为了更新权重,需要计算出损失函数关于神经网络内每个权重的梯度,损失函数关于第l层到第I+1层之间权重矩阵W^{(l,l+1)}的梯度为:

\frac{\partial L}{\partial W^{(l,l+1)}}=\sum_{j}\frac{\partial L}{\partial z_{j}^{l+1}}\frac{\partial z_{j}^{l+1}}{\partial W^{(l,l+1)}}=\sum_{j}\delta_{j}^{l+1}\frac{\partial z_{j}^{l+1}}{\partial W^{(l,l+1)}}

        再来看下图,假设我们要修正W^{(2,3)},此时就需要知道W^{(2,3)}对于L的梯度。这个梯度的求解就要利用到z^3W^{(2,3)}\delta ^3,而这些我们都是已知的,因此这个梯度\frac{\partial L}{\partial W^{(2,3)}}就是已知的,那么就可以根据梯度下降法去梯度更新W^{(2,3)}的值了。​

3.6.3 如何理解梯度流

        通过上面的局部观,我们可以看到求解第l层权重w相对于loss的grad梯度\sigma^l值,需要用到后一层l+1层权重w相对于loss的grad梯度\sigma^{l+1}

        因此传递到最前面第一层就需要后面所有层的梯度值做一个乘法,此时假如每一层的梯度值小于1,则相乘后总值将无限接近于0,这也就会导致梯度更新值无限接近于0,也就是几乎没有更新,这也就是梯度消失。

        又假如每一层的梯度值大于1,则相乘后总值将无限接近于无穷大,这也就会导致梯度更新值无限接近于无穷大,也就是更新值非常大,这也就是梯度爆炸。

3.7 LSTM 网络

        Long Short Term 网络—— 一般就叫做 LSTM ——是一种 RNN 特殊的类型,可以学习长期依赖信息。LSTM 由Hochreiter & Schmidhuber (1997)提出,并在近期被Alex Graves进行了改良和推广。在很多问题,LSTM 都取得相当巨大的成功,并得到了广泛的使用。

        LSTM 通过刻意的设计来避免长期依赖问题。记住长期的信息在实践中是 LSTM 的默认行为,而非需要付出很大代价才能获得的能力

        所有 RNN 都具有一种重复神经网络模块的链式的形式。在标准的 RNN 中,这个重复的模块只有一个非常简单的结构,例如一个 tanh 层。那么我们有没有什么办法能够通过修改这个重复的模块让模型解决长期依赖问题呢?

        LSTM同样是这样的结构,但是重复的模块拥有一个不同的结构。具体来说,RNN是重复单一的神经网络层,LSTM中的重复模块则包含四个交互的层,三个Sigmoid 和一个tanh层,并以一种非常特殊的方式进行交互。

        不必担心这里的细节。我们会一步一步地剖析 LSTM 解析图。现在,我们先来熟悉一下图中使用的各种元素的图标。

        在上面的图例中,每一条黑线传输着一整个向量,从一个节点的输出到其他节点的输入。粉色的圈代表 pointwise 的操作,诸如向量的和,而黄色的矩阵就是学习到的神经网络层。合在一起的线表示向量的连接,分开的线表示内容被复制,然后分发到不同的位置。

4. LSTM 的核心思想

        LSTM的核心目标:克服长期依赖问题。后面的几个机制:

4.1 细胞状态(保持门)

        细胞状态就是水平线在图上方贯穿运行。细胞状态类似于传送带。直接在整个链上运行,只有一些少量的线性交互。信息在上面流传保持不变会很容易。

  1. 核心要点:细胞状态是LSTM的核心,它负责在时间步之间传递信息。细胞状态可以被看作是一个“记忆流”,它能够在长时间跨度中保留和传递信息,而不会受到梯度消失或梯度爆炸的影响
  2. 为什么细胞状态能够减缓梯度消失/爆炸问题:仅仅有少了线性交互,而没有非线性交互
  3. 为什么线性交互比非线性更稳定:常用的非线性激活函数(如Sigmoid和tanh)具有饱和性,即当输入值较大或较小时,函数的导数会趋近于零;非线性激活函数将数值限制在-1到1之间,会间接减少梯度值,从而导致梯度消失问题。

        假如下面的忘记门和输入门都完成了,那么值就会送到细胞状态机制中,具体见下图:

4.2 遗忘机制(忘记门)

        遗忘机制就是决定从细胞状态中丢弃什么信息。这个决定通过一个称为“忘记门”的结构完成。该忘记门会读取上一个输出h_{t-1}和当前输入x_{t},做一个Sigmoid 的非线性映射,然后输出一个向量f_{t}(该向量每一个维度的值都在0到1之间,1表示完全保留,0表示完全舍弃,相当于记住了重要的,忘记了无关紧要的),最后与细胞状态C_{t-1}相乘。

        让我们回到语言模型的例子中来基于已经看到的预测下一个词。在这个问题中,细胞状态可能包含当前主语的性别,因此正确的代词可以被选择出来。当我们看到新的主语,我们希望忘记旧的主语

  1. 对于上图右侧公式中的权值,是不一样的。即:f_{t}=\sigma\left(W_{f_{h}} h_{t-1}+W_{f_{x}} x_{t}+b_{f}\right)
  2. 至于右侧公式和左侧的图是怎样的一个一一对应关系呢?如果是用有方向的水流表示计算过程则将一目了然,上动图!红圈表示Sigmoid 激活函数,篮圈表示tanh 函数:

从感性角度思考为什么遗忘机制能够减缓长期依赖问题:

1. 避免信息过载

        在时间序列任务中,每个时间步都会产生新的信息。如果所有信息都被无差别地存储到细胞状态中,细胞状态很快就会变得过载,包含大量无关或冗余的信息。这会导致:

  • 重要的长期信息被淹没在大量无关信息中。
  • 模型难以从细胞状态中提取出真正有用的信息。

通过选择性保留机制,LSTM可以过滤掉无关信息,只保留对长期任务有用的信息,从而避免信息过载。

2. 减少信息干扰

        如果细胞状态中存储了过多的无关信息,这些信息可能会干扰模型对长期依赖关系的捕捉。例如:

  • 在自然语言处理中,某些词或短语可能只在局部上下文中重要,而在更长的文本中无关紧要。
  • 如果这些局部信息被无差别地保留,它们可能会干扰模型对全局语义的理解

        通过选择性保留机制,LSTM可以丢弃这些局部无关信息,减少对长期信息的干扰。

从数学角度思考为什么遗忘机制能够减缓长期依赖问题:

1. 减少梯度流长度

        通过遗忘机制将循环神经网络中的一些模块给忘记,从而减少梯度流的长度,最终让真正有用的信息能够得到更新,没用的信息舍弃则其梯度也就不在梯度流中,也就减缓了长期依赖问题。

4.3 更新机制(输入门)

        下一步是确定什么样的新信息被存放在细胞状态中。这里包含两个部分:
                第一:sigmoid层称“输入门层”决定什么值我们将要更新;
                第二:一个tanh层创建一个新的候选值向量\tilde{C}_{t},会被加入到状态中。
        在我们语言模型的例子中,我们希望增加新的主语的性别到细胞状态中,来替代旧的需要忘记的主语。

  1. 首先,为便于理解图中右侧的两个公式,我们展开下计算过程,即i_{t} = \sigma (W_{ih}h_{t-1} + W_{ix}x_{t} + b_{i})\tilde{C_{t}} = tanh(W_{Ch}h_{t-1} + W_{Cx}x_{t} + b_{C})
  2. 更新机制过程如下:

4.4 输出机制(输出门)

        最终,我们需要确定输出什么值。这个输出将会基于我们的细胞状态,但是也是一个过滤后的版本。首先,我们运行一个 sigmoid 层来确定细胞状态的哪个部分将输出出去。接着,我们把细胞状态通过 tanh 进行处理(得到一个在 -1 到 1 之间的值)并将它和 sigmoid 门的输出相乘,最终我们仅仅会输出我们确定输出的那部分。
        在语言模型的例子中,因为他就看到了一个 代词,可能需要输出与一个 动词 相关的信息。例如,可能输出是否代词是单数还是负数,这样如果是动词的话,我们也知道动词需要进行的词形变化。

依然分两个步骤来理解:

  1. 展开图中右侧第一个公式,o_{t} = \sigma (W_{oh}h_{t-1} + W_{ox}x_{t} + b_{o})
  2. 输出门的动图:

5. 总结

【如果想学习更多深度学习文章,可以订阅一下热门专栏:】

深度学习_十二月的猫的博客-CSDN博客

PyTorch实战深度学习80例_十二月的猫的博客-CSDN博客

零基础入门PyTorch框架_十二月的猫的博客-CSDN博客

如果想要学习更多pyTorch/python编程的知识,大家可以点个关注并订阅,持续学习、天天进步

你的点赞就是我更新的动力,如果觉得对你有帮助,辛苦友友点个赞,收个藏呀~~~

;