不使用神经网络解释RNN

2020-07-11 04:15:01

Terence Parr Terence在旧金山大学的数据科学硕士项目任教,您可能知道他是ANTLR解析器生成器的创建者。

香草递归神经网络(RNNs)构成了更复杂模型的基础,如LSTM和GRU。有很多很棒的文章、书籍和视频描述了RNN的功能、数学和行为,所以,不要担心,这不是又一次重复。(有关资源列表,请参阅下面的内容。)。我的目标是给出一种解释,避免神经网络的比喻,把它剥离到它的本质-一系列导致可变长度输入向量嵌入的向量转换。

我的学习风格包括不断地钻研一些东西,直到我能够自己从基本的组成部分重新创造出来。这帮助我准确地理解模型在做什么以及为什么要这样做。如果您熟悉标准的神经网络层,并且对使用它们作为构建块的RNN解释感到满意,那么您可以忽略这篇文章。由于我仍在学习神经网络的细节,我想(1)通过这些层窥探下面的矩阵和向量,(2)调查训练过程的细节。我的出发点是与递归神经网络的不合理有效性相关的卡帕西的RNN代码片段,然后我吸收了杰里米·霍华德/西尔万·古格(Jeremy Howard/Sylvain Gugger)的书“使用FAST AI和PyTorch的程序员的深度学习”(Deep Learning For Coders With Fastai And PyTorch)和安德鲁·特拉斯克(Andrew Trask)的“摸索深度学习”(Groking Deep Learning)第12章的细节。

在本文中,我希望使用一个简单的数据集,将单词映射到相关的自然语言,为RNNs提供一个简单且直观的数据转换视角。右边的动画是从我为这篇文章制作的YouTube剪辑中截取(并加速)的。对于我实际的基于PyTorch的实现,我已经提供了使用自然语言数据集的重要姓氏的笔记本。这些链接打开了我在CoLab的完整实施笔记本:

当我试图学习RNN时,我的大脑一直在思考实现细节和关键概念,比如隐藏状态向量中到底包含了什么。我的大脑似乎是如此直截了当,直到它看到了整个画面的深度,才能理解任何东西。对于那些赶时间的人,让我总结一下我通过只使用矩阵和向量实现RNN所学到的一些关键内容。下面是全文的完整目录。表示RNN的递归关系中的h(有时称为s)到底是什么?(去掉非线性)?通常使用变量名h,因为它表示RNN的隐藏状态。RNN获取可变长度的符号输入记录(例如,股票价格序列、文档、句子或单词),并在高维空间中生成称为嵌入的定长向量,其以某种方式有意义地表示或编码输入记录。向量只与一条输入记录相关联,并且仅在分类或回归问题的上下文中有意义;RNN只是周围模型的一个组件。例如,h矢量通常通过最终的线性层V(多类逻辑回归)来获得模型预测。

h是否包含模型的学习参数?不是的。向量h是在我们处理单个记录的符号时保存部分结果的局部变量,但在RNN处理最终输入符号之后成为最终嵌入向量。此向量不作为梯度下降过程的一部分进行更新;它是使用上面给出的递归关系计算的。

h是RNN输出吗?我想这取决于你的观点。是的,嵌入的向量来自RNN,并成为后续层的输入,但它绝对不是整个模型的输出。比如说,模型输出来自另一个矩阵V到h的应用。

t是什么?它代表时间吗?如果您的可变长度输入记录是一个时间序列,如传感器或股票报价数据,那么yes t表示时间。变量t实际上只是RNN用来遍历单个输入记录的符号的迭代器变量。

什么是时间反向传播(BPTT)?BPTT是随机梯度下降(SGD),适用于经常处理时间序列数据的RNN的特定情况。反向传播本身意味着在较低损耗的方向上更新模型的参数。BPTT指的是我们在m层上执行BP的情况,这些层对输入记录中的m个符号重复使用相同的W和U。

那么截断的反向传播或截断的BPTT是什么呢?(首先,让我指出,对于相当短的输入记录,我们不需要截断的BPTT,就像我们对姓氏那样;我的示例不需要担心截断的BPTT。)。对于大的输入记录,例如文档,所有(展开的)RNN层上的梯度计算起来变得很昂贵,并且往往会消失或爆炸,这取决于我们的非线性激活函数。为了克服这个问题,我们可以简单地在计算图中进行一定数量的梯度计算之后停止BP过程。这意味着不能基于输入流中更早的输入符号更新模型参数。我有时在代码中看到用变量bptt表示的截断窗口的长度,这非常令人困惑。请注意,仍使用递归关系所描述的全计算来计算h。截断BP只是指我们使用来自BP的多少信息来更新W和U(通常是V)中的参数模型。矢量h使用W和U,但不会由BP更新。模型LMModel3和第12章的维护RNN状态一节详细解释了这一点。

每个变量h与单个输入记录相关联,并且在相关联的记录的起始处被初始化为零向量。

在为批次中的每个输入记录(如果使用纯SGD,则为单个单词)计算h嵌入向量之后,矩阵W、U、V作为SGD过程的一部分进行更新。当我们在时间上迭代符号时,W、U、V矩阵不会改变,除非我们对非常长的输入记录使用截断的BPTT。

微型批处理是在记录之间拆分的输入记录的一小部分,使所有输入记录保持不变。但是,在输入记录非常大的情况下,小批处理甚至可能涉及拆分单个记录,而不仅仅是在记录之间拆分。小批量中的每个记录都需要单独的h向量,这导致了我的示例中的矩阵H。

当组合一个热向量用于小批处理时,我们必须在左侧而不是右侧填充,以避免更改计算。请参阅本节:用0向量填充左侧的短词。

我已经把这篇文章分成两个主要部分。第一节尝试通过逐步重新发明机制来确定RNN如何将可变长度的输入记录编码为固定长度的向量。第二部分是关于细节的小批量处理和梯度下降训练循环的矢量化。首先,如果你是深度学习的新手,请查看杰里米·霍华德(Jeremy Howard)的完整课程(包括视频讲座),课程名为“程序员实用深度学习”。尤其是循环神经网络,这里有几个我觉得有用的资源:要开始学习RNN,我认为最好的第一站可能是麻省理工学院的RNN入门视频,以获得概述。卡帕西著名的RNN博客和相关的代码片段真的很鼓舞人心,我用它们作为基础来理解矢量是如何通过RNN递归关系流动的。杰里米·霍华德和西尔万·古格合著的“使用Fastai和PyTorch的程序员的深度学习”一书中的第12章充满了高级和低级细节。其他章节也很值得一读。安德鲁·特拉斯克(Andrew Trask)的“令人费解的深度学习”第11章和第12章有很多关于词向量和RNN的好东西。在François Chollet的“用Python进行深度学习”一书中,第6.2节使用神经网络层进行了非常好的讨论和实现;这本书写得非常清楚。然而,它确实关注角点,而我在本文中使用PyTorch作为张量。

我要感谢同时也是旧金山大学数据科学硕士项目教员的Yannet Interian,他们为我提供了资源,并为我提供了相关资料。安德鲁·肖(Andrew Shaw)和奥利弗·齐格曼(Oliver Zeigermann)也回答了我的许多问题,并填写了许多实施细节。