原文:
towardsdatascience.com/deep-learning-illustrated-part-4-recurrent-neural-networks-d0121f27bc74
欢迎来到我们图解深度学习之旅的第四部分!今天,我们将深入探讨递归神经网络。我们将讨论一些熟悉的概念,如输入、输出和激活函数,但会有所不同。如果你是这次旅程的第一站,请务必阅读之前的文章,特别是这一部分的1和2部分。
递归神经网络(RNN)是专门设计来处理基于序列的问题的独特模型,其中下一个位置依赖于前一个状态。
让我们用一个简单的例子来解释什么是基于序列的问题,这个例子来自这个MIT 课程。想象一下球在特定时间点 tn 的位置。
如果让我们预测球的方向,没有更多信息,那就是一个猜谜游戏——它可能向任何方向移动。
但如果我们提供了关于球之前位置的数据呢?
现在,我们可以自信地预测球将继续向右移动。
这种预测场景就是我们所说的序列问题——答案强烈受到先前数据的影响。这些序列问题无处不在,从根据过去温度数据预测明天的温度到一系列语言模型,包括情感分析、命名实体识别、机器翻译和语音识别。今天,我们将解决情感检测,这是一个基于序列问题的简单例子。
在情感检测中,我们取一段文本并确定它传达的是积极还是消极的情感。今天我们将构建一个 RNN,它以电影评论为输入并预测它是否积极。所以,给定这个电影评论…
…我们希望我们的神经网络能够预测这个具有积极的情绪。
这可能听起来像是一个简单的分类问题,但标准的神经网络在这里面临两个主要挑战。
首先,我们处理的是一个可变输入长度。标准的神经网络难以处理不同长度的输入。例如,如果我们用三个单词的电影评论来训练我们的神经网络,我们的输入大小将固定为三个。但如果我们想输入一个更长的评论怎么办?
它将感到困惑,无法处理包含十二个输入的上述评论。与之前的文章不同,其中我们有一个固定的输入数量(冰淇淋收入模型有两个输入——温度和星期几),在这种情况下,模型需要灵活,适应任何数量的单词。
第二,我们有顺序输入。典型的神经网络并不完全理解输入的方向性,这在这里是至关重要的。两个句子可能包含完全相同的单词,但顺序不同,它们可以传达完全相反的意义。
鉴于这些挑战,我们需要一种方法来顺序处理动态数量的输入。这正是 RNNs(递归神经网络)大放异彩的地方。
我们处理这个问题的方法是首先处理评论的第一个单词,“that”:
然后使用这些信息来处理第二个单词,“was”:
最后,使用所有上述信息来处理最后一个单词,“phenomenal”,并为评论的情感提供预测:
在我们开始构建我们的神经网络之前,我们需要讨论我们的输入。神经网络的输入必须是数值的。然而,我们这里的输入是单词,因此我们需要将这些单词转换成数字。有几种方法可以做到这一点,但今天,我们将使用一种基本的方法。
敬请期待即将推出的文章,我们将探讨更多复杂的方法来应对这一挑战。
目前,让我们假设我们有一个包含 10,000 个单词的大字典。我们将(天真地)假设任何出现在评论中的单词都可以在这个 10,000 个单词的字典中找到。每个单词都映射到一个相应的数字。
为了将单词"that"转换成一系列数字,我们需要确定"that"映射到的数字是…
…然后将其表示为一个包含 10,000 个 0 的矩阵,除了第 8600 个元素是 1:
同样,下一个两个单词的数值表示,“was”(字典中的第 9680 个单词)和*“phenomenal”*(字典中的第 4242 个单词),将是:
这就是我们将一个单词转换成神经网络友好的输入的方法。
现在,让我们将注意力转向神经网络的构建。为了简化,让我们假设我们的网络有 10,000 个输入(= 1 个单词),一个由一个神经元组成的单个隐藏层,以及一个输出神经元。
当然,如果这是一个完全训练好的神经网络,那么每个输入都将有关联的权重,神经元将有偏置项。
在这个网络中,输入权重被标记为 wᵢ,其中 i 表示输入。隐藏层神经元的偏置项是 bₕ。连接隐藏层到输出神经元的权重是 wₕᵧ。最后,输出神经元的偏置用 bᵧ 表示,因为 y 表示我们的输出。
我们将使用双曲正切函数 (tanh) 作为隐藏神经元的激活函数。
作为复习,从第一篇文章,tanh 接收一个输入并产生一个在 -1 到 1 范围内的输出。大的正输入趋向于 1,而大的负输入趋向于 -1。
为了确定文本的情感,我们可以在输出神经元中使用 sigmoid 激活函数。这个函数将隐藏层的输出转换为介于 0 和 1 之间的值,表示积极情感的概率。预测值接近 1 表示正面评论,而预测值接近 0 则表示不太可能是正面的。
虽然这个方法目前有效,但有一个更复杂的方法可以产生更好的结果!如果你看到文章的末尾,你会看到一个新且强大且普遍的激活函数——Softmax 激活函数——它能更好地解决这个问题。
使用这些激活函数,我们的神经网络看起来是这样的:
这个神经网络接收文本输入并预测其具有积极情感的概率。在上面的例子中,网络处理输入 “that” 并预测其积极的可能性。诚然,单词 “that” 单独并不能提供很多关于情感的信息。现在,我们需要弄清楚如何将下一个单词纳入网络。这就是循环神经网络中循环特性的作用所在,导致基本结构的修改。
我们通过创建上述神经网络的精确副本来输入评论的第二词 “was”。然而,我们不是使用 “that” 作为输入,而是使用 “was”:
神经网络输入为 “was” 的精确副本
记住,我们还想在这个神经网络中使用前一个单词的信息,“this”。因此,我们 从前一个神经网络的隐藏层取出输出并将其传递到当前网络的隐藏层:
将来自前一个神经网络的输入 “that” 与当前神经网络的输入 “was” 结合
这是一个关键步骤,所以让我们慢慢分解它。
从 [第一篇文章](https://medium.com/towards-data-science/neural-networks-illustrated-part-1-how-does-a-neural-network-work-c3f92ce3b462),我们了解到每个神经元的处理由两个步骤组成:求和和激活函数(如果您不确定这些术语的含义,请阅读第一篇文章)。让我们看看这在我们第一个神经网络中是什么样子。
在第一个神经网络的隐藏层神经元中,第一步是求和:
神经网络 1 – 隐藏神经元 – 第一部分 求和
在这里,我们将每个输入与其对应的权重相乘,并将偏置项加到所有乘积的总和中:
为了简化这个方程,让我们这样表示它,其中 wₓ 代表输入权重,x 代表输入:
神经网络 1 – 隐藏神经元 – 第一部分 求和
接下来,在第 2 步中,我们将这个求和通过激活函数 tanh:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/d3ac460e1d617e5faea467482fcb2bd5.pnghttps://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/d1dfd2e4ad49e431f1b7ad43ab4d10eb.png
神经网络 1 – 隐藏神经元 – 第二部分 激活函数 = h1
这产生了来自第一个神经网络隐藏层的输出 h₁。从这里,我们有两种选择 – 将 h1 传递给输出神经元或将其传递给下一个神经网络的隐藏层。
(选项 1) 如果我们只想对 “that” 进行情感预测,那么我们可以将 h₁ 传递给输出神经元:
选项 1 – 将 h1 传递给第一个神经网络的输出神经元
对于输出神经元,我们执行求和步骤…
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/090c300da40d66fb88ff0dab28068613.pnghttps://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2cfd6fbb357aa4e1007a1368ad704c43.png
神经网络 1 – 输出神经元 – 第一部分 求和
…然后将对这个和应用 sigmoid 函数…
神经网络 1 – 输出神经元 – 部分 2 激活 = y1_hat
…这给我们提供了预测的积极情感值:
神经网络 1 – 输出神经元 – 部分 2 激活 = y1_hat
因此,这个 _y₁hat 给我们提供了 “that” 具有积极情感的预测概率。
(选项 2) 但这并不是我们想要的。因此,我们不是将 h₁ 传递给输出神经元,而是像这样将此信息传递给下一个神经网络:
选项 2— 将 h1 传递给第二个神经网络的隐藏神经元
与其他具有输入权重的神经网络部分类似,我们也有一个从一层隐藏层到另一层的输入权重,wₕₕ。 隐藏层通过将 h₁ 与 wₕₕ 的乘积加到隐藏神经元的求和步骤中,来包含 h₁。因此,第二个神经网络神经元中更新的求和步骤将是:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/489349e2a6781e4e62149f8c12f01d7e.pnghttps://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/92deeb28b4f8ee4f2a2dc59df44ceb5c.png
神经网络 2— 隐藏神经元 – 部分 1 求和
需要注意的关键点 – 由于它们只是从上一个网络复制过来的,所以整个网络中的所有偏差和权重项保持不变。
这个和随后通过 tanh 函数…
神经网络 2 – 隐藏神经元 – 部分 2 激活函数
…产生 h₂,第二个神经网络隐藏层的输出:
神经网络 2 – 隐藏神经元 – 部分 2 激活函数
从这里,再次,我们可以通过将 h₂ 传递给输出神经元来获得情感预测:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/5d0499b6b76f8297b0c596e3e2093285.pnghttps://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/0993dac54dc3072bd9eff2e32c53f2d4.png
神经网络 2 – 输出神经元 – 部分 1 和 2 = y2_hat
这里,_y₂hat 得出 “that was” 具有积极情感的预测概率。
但我们知道这并不是评论的结束。因此,我们将复制此过程,再次克隆这个网络,但这次输入为 “phenomenal”,并将前一个隐藏层输出传递到当前隐藏层。
我们处理隐藏层神经元…
神经网络 3 – 隐藏神经元 – 部分 1 和 2 = h3
…到输出,h₃:
神经网络 3 – 隐藏神经元 – 部分 1 和 2 = h3
由于这是评论的最后一句话,因此是最终的输入,我们将这些数据传递给外层神经元…
神经网络 3 – 输出神经元 – 部分 1 和 2 = y3_hat
…以给出最终的预测情感:
神经网络 3 – 输出神经元 – 部分 1 和 2 = y3_hat
而 _y₃hat 就是我们要找的电影评论的情感,这是我们实现最初所绘制内容的方式!
正式表示
如果我们用细节充实上面的图,那么我们会得到类似这样的东西:
过程的每个阶段都涉及一个输入,x,它通过隐藏层生成一个输出,h。然后这个输出要么进入下一个神经网络的隐藏层,要么导致情感预测,表示为 _yhat。每个阶段都包含权重和偏差项(图中未显示偏差)。一个需要强调的关键点是,我们将 所有隐藏层合并成一个紧凑的单个框。虽然我们的模型只包含一个隐藏层中的一个神经元,但更复杂的模型可以包含多个隐藏层和多个神经元,所有这些都压缩在这个称为 隐藏状态 的框中。这个隐藏状态封装了隐藏层的抽象概念。
实质上,这是一个神经网络简化版本:
值得注意的是,我们可以用这个简化的图来表示所有这些内容:
这个过程的本质是将隐藏层的输出反复反馈回自身,这就是为什么它被称为循环神经网络。这通常是教科书中表示神经网络的方式。
从数学的角度来看,我们可以将其归结为两个基本方程:
表示整个过程的两个公式
第一个方程封装了隐藏状态中发生的完整线性变换。在我们的例子中,这种变换是单个神经元中的 tanh 激活函数。第二个方程表示输出层中发生的变换,即我们的例子中的 sigmoid 激活函数。
表示整个过程的两个公式
RNN 解决了哪些类型的问题?
多对一
我们刚刚讨论了这种情况,其中多个输入(在我们的例子中,是评论中的所有单词)被输入到 RNN 中。然后 RNN 生成一个单一输出,代表评论的情感。虽然每一步都可能有一个输出,但我们的主要兴趣在于最终输出,因为它封装了整个评论的情感。
另一个例子是文本补全。给定一串单词,我们希望 RNN 预测下一个单词。
一对多
一个一对一问题的经典例子是图像标题。在这里,单个输入是一张图片,输出是由多个单词组成的标题。
多对多
这种类型的 RNN 用于机器翻译等任务,例如将英语句子翻译成印地语。
缺点
既然我们已经了解了 RNN 的工作原理,那么探讨为什么它们没有被广泛使用(剧情转折!)就很有意义了。尽管 RNN 具有潜力,但在训练过程中,它们面临着重大的挑战,尤其是由于所谓的梯度消失问题。随着我们进一步展开 RNN,这个问题往往会加剧,这反过来又使得训练过程变得复杂。
在理想的世界里,我们希望 RNN 能够平等地考虑当前步骤的输入和之前步骤的输入:
然而,实际上它看起来更像这样:
每一步都会稍微忘记前一步,导致一个称为梯度消失问题的短期记忆问题。随着 RNN 处理更多的步骤,它往往难以保留之前的信息。
只有三个输入时,这个问题并不太明显。但如果我们有六个输入呢?
我们可以看到,前两个步骤的信息在最后一步几乎消失了,这是一个重大问题。
这里有一个例子,使用文本补全任务来说明这一点。给定这个句子进行补全,RNN 可能会成功。
然而,如果在中间添加更多单词,RNN 可能会难以准确预测下一个单词。这是因为 RNN 可能会因为单词之间的距离增加而忘记由初始单词提供的上下文。
这突出了这样一个事实,虽然 RNN 在理论上听起来很棒,但在实践中往往表现不佳。为了解决短期记忆问题,我们使用称为长短期记忆(LSTM)网络的专业 RNN 类型,这在系列的第五部分(Part 5)中有详细说明!
奖励:Softmax 激活函数
我们之前提到过一种处理情感预测的替代方法,效果要好得多。让我们退回到画板前,回到我们决定输出神经元激活函数的时候。
但这次我们的关注点略有不同。让我们聚焦于一个基本的神经网络,暂时放下循环方面。我们的目标现在?预测单个输入单词的情感,而不是整个电影评论。
之前,我们的预测模型旨在输出输入为正的概率。我们通过在输出神经元中使用 sigmoid 激活函数来实现这一点,该函数为正情感的可能性输出概率值。例如,如果我们输入单词 “terrible”,我们的模型理想情况下会输出一个低值,表示正情感的可能性很低。
然而,仔细思考后,这个输出并不那么好。正情感的低概率并不一定意味着消极——它也可能意味着输入是中性的。那么,我们如何改进这一点呢?
考虑一下:如果我们想知道电影评论是积极的、中性的还是消极的?
因此,我们不再只有一个输出神经元,它会输出输入为正的概率预测,而是可以使用三个输出神经元。每个神经元将分别预测评论为积极、中性和消极的可能性。
正如我们使用 sigmoid 函数为单输出神经元网络输出概率一样,我们也可以将同样的原则应用到我们当前网络中的每个神经元上,并在它们中应用 sigmoid 函数。
而每个神经元将输出其相应的概率值:
然而,这里有一个问题:概率值没有正确求和(0.1 + 0.2 + 0.85 != 1),所以这不是一个很好的解决方案。仅仅为所有输出神经元添加 sigmoid 函数并不能解决问题。我们需要找到一种方法来在三个输出之间归一化这些概率。
这里我们向我们的工具箱中引入一个强大的激活函数——softmax 激活函数。通过使用 softmax 激活函数,我们的神经网络呈现出新的形态:
虽然一开始可能觉得令人畏惧,但 softmax 函数实际上非常简单。它只是从我们的输出神经元中取出输出值 (_yhat) 并对其进行归一化。
然而,需要注意的是,对于这三个输出神经元,我们不会使用任何激活函数;输出 (_yhats) 将是我们在求和步骤后直接获得的结果。
如果你需要回顾神经元中的求和步骤和激活步骤,系列中的这篇文章 this article 详细介绍了神经元的内部工作原理!
我们通过使用 softmax 公式对这些 _yhat 输出进行归一化。这个公式提供了对积极情绪概率的预测:
类似地,我们也可以获得负面和中和结果的预测概率:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/b7e0af852d94793522cb1c4fc1af1a1a.pnghttps://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/928f7f653a9ae2954489e3a3ebf16839.png
让我们看看实际操作。例如,如果 “terrible” 是我们的输入,这些将是结果 _yhat 值:
然后,我们可以将这些值代入 softmax 公式,以计算单词 “terrible” 具有积极含义的预测概率。
这意味着,通过使用情感神经元的综合输出,“terrible” 表现出积极情绪的概率是 0.05。
如果我们要计算输入为中性的概率,我们会使用一个类似的公式,只需更改分子。因此,单词 “terrible” 被认为是中性的可能性是:
预测 “terrible” 是负面的概率是:
哇!现在概率加起来为 1,这使得我们的模型更具可解释性和逻辑性。
因此,当我们向神经网络提问——““terrible” 有负面情绪的概率是多少?”时,我们得到了一个相当直接的答案。它自信地表示,“terrible” 有负面情绪的概率是 85%。这就是 softmax 激活函数的美丽之处!
今天的内容就到这里!我们解决了两个大问题——循环神经网络和 Softmax 激活函数。这些是我们在以后深入探讨的许多高级概念的基石。所以,请慢慢来,让它深入人心,并且像往常一样,如果你有任何问题或评论,随时可以通过LinkedIn与我联系,或者发送电子邮件到 [email protected]。
注意:除非另有说明,所有插图均由作者绘制
3182

被折叠的 条评论
为什么被折叠?



