5-1 Coursera吴恩达《序列模型》 第一周课程笔记-循环序列网络(RNN)

72 篇文章 29 订阅

记录吴恩达Andrew Ng深度学习专项课程笔记,方便之后回顾,共5门课。第一门课《神经网络与深度学习》(Neural Networks and Deep Learning)的4周课程笔记请见如下链接:

1-1 Coursera吴恩达《神经网络与深度学习》第一周课程笔记-深度学习概论

1-2 Coursera吴恩达《神经网络与深度学习》第二周课程笔记-神经网络基础

1-3 Coursera吴恩达《神经网络与深度学习》第三周课程笔记-浅层神经网络

1-4 Coursera吴恩达《神经网络与深度学习》第四周课程笔记-深层神经网络

第二门课《改善深度神经网络》(Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization)的3周课程笔记请见如下链接:

2-1 Coursera吴恩达《改善深度神经网络》 第一周课程笔记-深度学习的实践方面

2-2 Coursera吴恩达《改善深度神经网络》第二周课程笔记-优化算法

2-3 Coursera吴恩达《改善深度神经网络》第三周课程笔记-超参数调试、Batch正则化和编程框架

第三门课《构建机器学习项目》(Structuring Machine Learning Projects)的2周课程笔记请见如下链接:

3-1 Coursera吴恩达《构建机器学习项目》 第一周课程笔记-机器学习策略(1)

3-2 Coursera吴恩达《构建机器学习项目》 第二周课程笔记-机器学习策略(2)

第四门课《卷积神经网络》(Convolutional Neural Networks)的4周课程笔记请见如下链接:

4-1 Coursera吴恩达《卷积神经网络》 第一周课程笔记-卷积神经网络基础

4-2 Coursera吴恩达《卷积神经网络》 第二周课程笔记-深度卷积模型:实例探究

4-3 Coursera吴恩达《卷积神经网络》 第三周课程笔记-目标检测

4-4 Coursera吴恩达《卷积神经网络》 第四周课程笔记-特殊应用:人脸识别和神经风格转换

今天开始介绍第五门课《序列模型》(Sequence Models)第一周卷积神经网络基础的课程笔记,这门课主要介绍循环神经网络(RNN)的基本概念、模型和具体应用;自然语言处理和词嵌入;序列模型和注意力机制。能够让我们更深入的了解神经网络,解决其他类型的问题,该门课共三周,那我们开始吧。

目录

《1.1 为什么选择序列模型?》Why Sequence Models?

《1.2 数学符号》Notation

《1.3 循环神经网络》Recurrent Neural Network Model

《1.4 通过时间的方向传播》Backpropagation through time

《1.5 不同类型的循环神经网络》Different types of RNNs

《1.6 语言模型和序列生成》Language model and sequence generation

《1.7 对新序列采样》Sampling novel sequences

《1.8 带有神经网络的梯度消失》Vanishing gradients with RNNs

《1.9 GRU单元》Gated Recurrent Unit(GRU)

《1.10 长短期记忆(LSTM)》LSTM(long short term memory)unit

《1.11 双向神经网络》Bidirectional RNN

《1.12深层循环神经网络》Deep RNNs

《1.1 为什么选择序列模型?》Why Sequence Models?

在本课程中将学习序列模型(sequence models),它是深度学习中最令人激动的内容之一。循环神经网络(RNN)之类的模型在语音识别(speech recognition)、自然语言处理(natural language processing)和其他领域中引起变革。我们先看一些例子,这些例子都有效使用了序列模型。

(1)在语音识别(Speech recognition)时,给定了一个输入音频片段(audio clip)x,并要求输出对应的文字记录(text transcript)y。这个例子里输入和输出数据都是序列模型,因为x是一个按时播放的音频片段,输出y是一系列单词。所以之后将要学到的一些序列模型在语音识别方面是非常有用的。

(2)音乐生成问题(Music generation)是使用序列数据的另一个例子,在这个例子中,只有输出数据y是序列,而输入数据可以是空集(empty set),也可以是个单一的整数,这个数可能指代你想要生成的音乐风格,也可能是你想要生成的那首曲子的头几个音符。

(3)在处理情感分类(Sentiment classification)时,输入数据x是序列,你会得到类似这样的输入:“There is nothing to like in this movie.”,你认为这句评论对应几星?

(4)在DNA序列分析(DNA sequence analysis)时,你的DNA可以用A、C、G、T四个字母来表示。所以给定一段DNA序列,你能够标记出哪部分是匹配某种蛋白质的吗?

(5)在机器翻译(Machine translation)过程中,你会得到这样的输入句:“Voulez-vou chante avecmoi?”(法语:要和我一起唱么?),然后要求你输出另一种语言的翻译结果。

(6)在视频行为识别(Video activity recognition)时,你可能会得到一系列视频帧,然后要求你识别其中的行为。

(7)在命名实体识别(Name entity recognition)时,可能会给定一个句子要你识别出句中的人名。

总结一下,序列模型可以应用在语音识别、音乐生成器、情感分类、DNA序列分析、机器翻译、视频动作识别和命名实体识别等领域。这些问题都可以被称作使用标签数据(x,y)作为训练集的监督学习。但从这一系列例子中你可以看出序列问题有很多不同类型。有些问题里,输入数据x和输出数据y都是序列(x和y有时也会不一样长)。在另一些问题里,也可能只有x或者只有y是序列。

所以在本节我们将学到适用于不同情况的序列模型。

《1.2 数学符号》Notation

本节视频先从定义符号(notation)开始一步步构建序列模型。

如上图,你想要建立一个序列模型,它的输入语句是这样的:“Harry Potter and Herminoe Granger invented a new spell.”,(这些人名都是出自于J.K.Rowling笔下的系列小说Harry Potter)。假如你想要建立一个能够自动识别句中人名位置的序列模型,那么这就是一个命名实体识别问题(Name entity recognition),这常用于搜索引擎(search engines)。

现在给定这样的输入数据x,假如你想要一个序列模型输出y,使得输入的每个单词都对应一个输出值,同时这个y能够表明输入的单词是否是人名的一部分。技术上来说这也许不是最好的输出形式,还有更加复杂的输出形式,它不仅能够表明输入词是否是人名的一部分,它还能够告诉你这个人名在这个句子里从哪里开始到哪里结束。比如Harry Potter、Hermione Granger。

更简单的那种输出形式:这个输入数据是9个单词组成的序列,最终用9个特征集合来表示这9个单词:

用x^来索引这个序列的中间位置。t意味着它们是时序序列(temporal sequences),但不论是否是时序序列,我们都将用t来索引序列中的位置。输出数据表示为:

输入序列的长度表示为T_x,在这里T_x=9,输出序列的长度表示为T_y,在这里T_y=T_x=9(输入序列长度和输出序列长度也可以不相等)。

还有一些符号,第i个训练样本的序列中第t个元素表示为:

第i个训练样本的输出序列的长度表示为:

第i个训练样本的输出序列中第t个元素表示为:

第i个训练样本的输出序列的长度表示为:

接下来我们讨论一下怎样表示一个句子里单个的词

如上图,想要表示一个句子里的单词,第一件事是做一张词表(Vocabulary),有时也称词典(Dictionary),意思是列一列你的表示方法中用到的单词。这个词表(下图所示)中的第一个词是a,也就是说词典中的第一个单词是a,第二个单词是Aaron,然后更下面一些是单词and,再后面你会找到Harry,然后找到Potter,这样一直到最后,词典里最后一个单词可能是Zulu。

因此a是第一个单词,Aaron是第二个单词,在这个词典里,and出现在367这个位置上,Harry是在4075这个位置,Potter在6830,词典里的最后一个单词Zulu可能是第10,000个单词。所以在这个例子中Andrew用了10,000个单词大小的词典,这对现代自然语言处理应用来说太小了。对于商业应用来说,或者对于一般规模的商业应用来说30,000到50,000词大小的词典比较常见,也有100,000词,而且有些大型互联网公司会用百万词,甚至更大的词典。

如果你选定了10,000词的词典,构建这个词典的一个方法是遍历你的训练集,并且找到前10,000个常用词,你也可以去浏览一些网络词典,它能告诉你英语里最常用的10,000个单词,接下来你可以用one-hot表示法(representations)来表示词典里的每个单词。

举个例子,(1)在这里x^表示Harry这个单词,它就是一个第4075行是1,其余值都是0的向量(上图编号1所示),因为那是Harry在这个词典里的位置。(2)同样x^是个第6830行是1,其余位置都是0的向量(上图编号2所示)。(3)第三个单词and在词典里排第367,所以x^就是第367行是1,其余值都是0的向量(上图编号3所示)。(4)因为a是字典第一个单词,x^对应a,那么这个向量的第一个位置为1,其余位置都是0的向量(上图编号4所示)。

所以这种表示方法中,x^指代句子里的任意词,它就是个one-hot向量,因为它只有一个值是1,其余值都是0,所以会有9个one-hot向量来表示这个句中的9个单词,如果你的词典大小是10,000的话,那么这里的每个向量都是10,000维的。目的是用这样的表示方式表示X,用序列模型在X和目标输出Y之间学习建立一个映射。Andrew把它当作监督学习的问题(supervised learning problem),而且确信会给定带有(x,y)标签的数据。

注意一下,如果你遇到了一个不在你词表中的单词,答案就是创建一个新的标记,也就是一个叫做Unknow Word的伪单词,用作为标记,来表示不在词表中的单词,我们之后会讨论更多有关这个的内容。

《1.3 循环神经网络》Recurrent Neural Network Model

上个视频介绍了RNN常见的数学符号,这个视频我们讨论一下怎样建立一个神经网络来学习X到Y的映射。

如上图所示。可以尝试的方法之一是使用标准神经网络,还是这个例子,“Harry Potter and Herminoe Granger invented a new spell.”,把这9个单词(可能是9个one-hot向量)输入到一个标准神经网络中,经过一些隐藏层,最终会输出9个取值为0或1的项(表明每个输入单词是否是人名的一部分)。

但结果表明该方法主要有两个问题(problems)

(1)是在不同例子中输入和输出数据可以有不同的长度,不是所有的例子都有着同样输入长度T_x或是同样输出长度T_y。即使每个句子都有最大长度,也许你能够填充(pad)或零填充(zero pad)使每个输入语句都达到最大长度,但仍然不是一个好的表达方式。

(2)单纯的神经网络结构并不共享从文本的不同位置上学到的特征。我们希望对序列数据而言,有类似将部分图片里学到的内容快速推广到图片的其他部分的效果。

而且对于一个10,000的词表,单词都是10,000维的one-hot向量,这会是十分庞大的输入层。当总的输入大小是最大单词数乘以10,000时,那么第一层的权重矩阵就会有着巨量的参数。但是循环神经网络就没有这两个问题。

那么什么是循环神经网络呢?

如上图所示,从左到右的顺序读这个句子,我们将第一个单词x^<1>输入一个神经网络层,第一个神经网络的隐藏层,可以让神经网络尝试预测输出y^<1>,判断这是否是人名的一部分。循环神经网络做的是,当它读到第二个单词x^<2>时,它不是仅用x^<2>就预测出y^<2>,他也会输入一些来自时间步1的信息。具体而言,时间步1的激活值就会传递到时间步2。然后,在下一个时间步,循环神经网络同时也输入了x^<3>,然后预测输出了预测结果y^<3>,等等,一直到最后一个时间步。注意在这个例子中,T_x = T_y,当不相等时这个结构会需要作出一些改变。因此在每一个时间步(time step)中,循环神经网络传递一个激活值到下一个时间步中用于计算

要开始整个流程,在零时刻构造一个激活值a^<0>,通常是零向量。使用零向量作为零时刻的伪激活值是最常见的选择,因此我们把它输入神经网络。

在一些研究论文中或是一些书中这类神经网络也用这样的图形来表示(上图最右侧图像所示),表示循环连接(recurrent connection)时会画个圈,表示输回网络层;一个黑色方块(shaded box)来表示在这个黑色方块处会延迟一个时间步。在本次课程中更倾向于使用左边这种分布画法。

循环神经网络是从左向右扫描数据,同时每个时间步的参数也是共享的,如上图的红色标记,我们用W_ax来表示从x^<1>到隐藏层的连接的一系列参数,每个时间步使用的都是相同的参数。而激活值也就是水平联系是由参数W_aa决定的,同样每一个时间步都使用相同的参数,同样的输出结果由参数W_ya决定。下图详细讲述这些参数是如何起作用。

注意到该循环神经网络的一个缺点只使用了这个序列之前的信息来做出预测,为了判断Teddy是否是人名的一部分,仅仅知道句中前两个词是完全不够的,还需要知道句子后部分的信息,比如这个句子,“Teddy bears are on sale!”。因此如果只给定前三个单词,很难确切地知道Teddy是否是人名的一部分,第一个例子是人名,第二个例子就不是。这样特定的神经网络结构的一个限制是它在某一时刻的预测仅使用了从序列之前的输入信息并没有使用序列中后部分的信息,在之后的双向循环神经网络(bidirectional recurrent neural networks)的视频中解决这个问题。现在这个更简单的单向神经网络结构就够我们来解释关键概念(explain the key concepts)。

接下来我们具体地写出这个神经网络计算了些什么。

上图是一张清理后的神经网络示意图(the picture of the neural network),先输入零向量a^<0>,接着是前向传播过程(forward propagation),先计算激活值a^<1>,再计算y^<1>。

对矩阵下标的解释:W_ax的第一个下标a表示用来计算某个a类型的变量,第二个下标意味着要乘以某个x类型的量。

循环神经网络常用的激活函数是tanh,有时候也会用ReLU。选用哪个激活函数是取决于你的输出y,如果它是一个二分类问题,那么可以会用sigmoid函数作为激活函数;如果是k类别分类问题的话,那么可以选用softmax作为激活函数。对于更一般的情况,在t时刻有:

上述等式定义了神经网络的前向传播。下面具体看看这两个等式

将上述的第一个等式,以更简单的形式写出来,写为:

我们定义W_a的方式是将矩阵W_aa和W_ax水平并列放置,如:

例如之前的例子,如果a是100维的,x是10,000维的,那么W_aa就是个(100,100)维的矩阵W_ax就是个(100,10000)维的矩阵,因此如果将这两个矩阵堆起来,W_a就会是个(100,10100)维的矩阵。这种记法的好处是我们可以不使用两个参数矩阵,而是将其压缩成一个参数矩阵W_a,所以当我们建立更复杂模型时这就能够简化我们要用到的符号。

同样对于y也有更简化的例子:

现在我们看到了基本的循环神经网络,接下来我们会一起来讨论反向传播以及如何能够用RNN进行学习。

《1.4 通过时间的方向传播》Backpropagation through time

目前为止我们已经学过了循环神经网络的基础结构,在本节视频中我们将来了解反向传播(back propagation)是怎样在循环神经网络中运行的,它还有个很别致的名字:叫做“通过(穿越)时间反向传播(backpropagation through time)”。。和之前一样,当你在编程框架中实现循环神经网络时,编程框架通常会自动处理反向传播。Andrew认为,在循环神经网络中,对反向传播的运行有一个粗略的认识还是非常有用的,让我们来一探究竟。

对于前向传播(上图蓝色箭头所指方向)怎样在神经网络中从左到右地计算这些激活项,直到输出所有的预测结果。而对于反向传播(上图红色箭头所指方向),计算方向与前向传播基本上是相反的。

如上图,我们分析一下前向传播(forward propagation)的计算,现在有一个输入(input)序列,x^<1>,x^<2>,...,x^,为了计算出a^<1>,还需要参数W_a和b_a,所有的这些激活项都要取决于参数W_a和b_a,接着使用参数W_y和b_y可以计算出第一个预测值y帽^<1>。(参数如上图绿色标记)

为了计算反向传播,还需要一个损失函数。我们先定义一个元素的损失函数

我们将它定义为标准逻辑回归损失函数,也叫交叉熵损失函数(Cross Entropy Loss)。这是关于单个位置上或者说某个时间步t上某个单词的预测值的损失函数。

现在我们来定义整个序列的损失函数,通过将每一个单独时间步的顺势函数加起来,将L定义为

上图就是完整的计算图(computation graph),反向传播算法需要在相反的方向上进行计算和传递信息(红色标记)。最终把前向传播的箭头都反过来,就可以计算出所有合适的量,然后通过导数相关的参数,用梯度下降法(gradient descent)来更新参数。

在这个反向传播的过程中,最重要的信息传递或者说最重要的递归运算就是这个从右到左的运算(红色圆圈标记),这个算法有一个很别致的名字,叫做“通过(穿越)时间反向传播(backpropagation through time)”。取这个名字的原因是对于前向传播,你需要从左到右进行计算,在这个过程中,时间t不断增加。而对于反向传播,你需要从右到左进行计算,就像时间倒流。

《1.5 不同类型的循环神经网络》Different types of RNNs

到目前为止我们已经了解了一种RNN结构,它的输入量T_x等于输出数量T_y。事实上,对于其他一些应用,和l两者并不一定相等。接下来,我们会看到更多的RNN的结构

如上图,比如音乐生成(music generation),T_x可以是长度为1甚至为空集。再比如电影情感分类(sentiment classification),输出y可以是1到5的整数,而输入是一个序列。在命名实体识别(name entity recognition)中,这个例子中输入长度和输出长度是一样的。

还有一些情况,输入长度和输出长度不同,他们都是序列但长度不同,比如机器翻译(machine translation),一个法语句子和一个英语句子不同数量的单词却能表达同一个意思。

所以我们应该修改基本的RNN结构来处理这些问题,内容参考了Andrej Karpathy的博客,一篇叫做《循环神经网络的非理性效果》(“The Unreasonable Effectiveness of Recurrent Neural Networks”)的文章,我们看一些例子。

(备注:上图中小圆圈表示神经元。)如上图左侧所示,叫“多对多”(many-to-many)的结构,因为输入序列有很多的输入,而输出序列也有很多输出。再看一个例子(上图中间),处理情感分类问题时,x就是一个序列(可能是一段文本),而y可能是从1到5的一个数字,或者是0或1,这代表正面评价和负面评价,而数字1到5代表电影是1星,2星,3星,4星还是5星。这个神经网络叫做“多对一”(many-to-one)结构,因为它有很多输入,很多的单词,然后输出一个数字。接着“一对一”(one-to-one)的结构(上图最右侧所示),这个可能没有那么重要,这就是一个小型的标准的神经网络,输入x然后得到输出y。

除了“多对一”的结构,也可以有“一对多”(one-to-many)的结构。

如上图,对于一个“一对多”神经网络结构的例子就是音乐生成(music generation),x可以是空的输入,可设为0向量。首先是你的输入x,然后得到RNN的输出,第一个值,然后就没有输入了,再得到第二个输出,接着输出第三个值等等,一直到合成这个音乐作品的最后一个音符。这里用到了一个技术细节:在生成序列时通常会把第一个合成的输出也喂给下一层(上图红色标记)。

对于“多对多”的结构还有一个有趣的例子,就是输入和输出长度不同的情况。比如机器翻译(machine translation),输入句子的单词的数量(比如说一个法语的句子)和输出句子的单词数量(比如翻译成英语),这两个句子的长度可能不同,所以还需要一个新的网络结构(如上图右侧)。这个网络的结构有两个不同的部分,第一部分是一个编码器(encoder),获取输入,比如法语句子,然后是解码器(decoder),它会读取整个句子,然后输出翻译成其他语言的结果。

严格来说,还有“注意力”结构(attention based architectures),但是根据我们现在画的这些图不好理解这个模型。

接下来总结一下这些各种各样的RNN结构:

①“一对一”的结构,当去掉a^时是一种标准类型的神经网络。②“一对多”的结构,比如音乐生成或者序列生成。③“多对一”,如情感分类,首先读取输入,然后判断他们是否喜欢电影还是不喜欢。还有“多对多”的结构,④命名实体识别就是“多对多”的例子,其中T_x = T_y。⑤还有一种“多对多”结构例如机器翻译,T_x和T_y可以不同。

这个视频我们知道用这些RNN的基本模块,把它们组合在一起就可以构建各种各样的模型。下篇文章深入探讨序列生成。

《1.6 语言模型和序列生成》Language model and sequence generation

在自然语言处理中,构建语言模型(language model)是最基础的也是最重要的工作之一,并且能用RNN很好地实现。在这篇文章中,我们将学习用RNN构建一个语言模型。所以什么是语言模型呢?

如上图,比如一个语音识别系统,你听到一个句子,“the apple and pear(pair) salad was delicious.”,所以究竟说了什么?是 “the apple and pair salad”,还是“the apple and pear salad”?(pear和pair是近音词)。你可能觉得应该更像第二种,事实上,这就是一个好的语音识别系统要帮助输出的东西,即使这两句话听起来是如此相似。而让语音识别系统去选择第二个句子的方法就是使用一个语言模型,他能计算出这两句话各自的可能性。语言模型会告诉你某个特定的句子它出现的概率是多少它是两种系统的基本组成部分:语音识别系统(speech recognition systems)和机器翻译系统(machine translation systems)

对于语言模型来说,用y来表示这些序列比用x来表示要更好,然后语言模型会估计某个句子序列中各个单词出现的可能性。那么如何建立一个语言模型呢?

如上图,为了使用RNN模型,首先需要一个训练集(training set),包含一个很大的英文文本语料库(word corpus)或者其它的语言的语料库。语料库是自然语言处理的一个专有名词,意思就是很长的或者说数量众多的英文句子组成的文本。

例如,在训练集中得到这么一句话,“Cats average 15 hours of sleep a day.”(猫一天睡15小时),接下来需要(1)将这个句子标记化(tokenization),建立一个字典,然后将每个单词都转换成对应的one-hot向量,也就是字典中的索引。可能要定义句子的结尾,一般的做法就是增加一个额外的标记,叫做EOS(End Of Sentence),它表示句子的结尾,这样能够帮助你搞清楚一个句子什么时候结束。EOS标记可以被附加到训练集中每一个句子的结尾。在本例中我们,这句话就会有9个输入(如果你加了EOS标记)。同时,我们忽略了标点符号(punctuation),当然想加入字典也可以。

现在还有一个问题如果你的训练集中有一些词并不在你的字典里,比如“The Egyptian Mau is a bread of cat.”的词Mau,可以把Mau替换成一个叫做UNK(unknown words)的代表未知词的标志,我们只针对UNK建立概率模型,而不是针对这个具体的词Mau。完成标识化的过程后,这意味着输入的句子都映射到了各个标志上,或者说字典中的各个词上。(2)下一步我们要构建一个RNN来构建这些序列的概率模型

如上图,现在来建立RNN模型。还是这个句子“Cats average 15 hours of sleep a day.”。

①在第0个时间步,你要计算激活项a^<1>,它是以x^<1>(全为0的集合,即0向量)作为输入的函数。于是a^<1>会通过softmax进行一些预测来计算出第一个词可能会是什么,这一步其实就是通过一个softmax层来预测字典中的任意单词会是第一个词的概率,比如说第一个词是a的概率有多少,第一个词是Aaron的概率有多少,第一个词是cats的概率又有多少,就这样一直到Zulu是第一个词的概率是多少,还有第一个词是UNK(未知词)的概率有多少,还有第一个词是句子结尾标志的概率有多少,表示不必阅读。它只是预测第一个词的概率,而不去管结果是什么。在这个例子中,最终会得到单词Cats。所以softmax层输出10,000种结果,因为你的字典中有10,000个词,或者会有10,002个结果,因为可能加上了未知词和句子结尾这两个额外的标志。

②然后RNN进入下个时间步,在下一时间步中,仍然使用激活项a^<1>,这一步计算出第二个词会是什么。我们会告诉它第一个词就是Cats,这就是为什么y^<1> = x^<2>。在第二个时间步中,输出结果同样经过softmax层进行预测,RNN的职责就是预测这些词的概率),而不会去管结果是什么。所以在这种情况下,Andrew猜正确答案会是average,因为句子确实就是Cats average开头的。

③然后再进行RNN的下个时间步,现在要计算a^<3>。为了预测第三个词,也就是15,我们现在给它之前两个词,告诉它Cats average是句子的前两个词,所以这是下一个输入, x^<3> = y^<2> ,计算出字典中每一个词的概率,通过之前得到的Cats和average,在这种情况下,正确结果会是15,以此类推。

一直到最后,你会停在第9个时间步,最后的得到结果会是EOS标志,在这一步中,通过前面这些得到的单词,不管它们是什么,我们希望能预测出EOS句子结尾标志的概率会很高。

所以RNN中的每一步都会考虑前面得到的单词,这就是RNN如何学习从左往右地每次预测一个词。

接下来为了训练这个网络,我们要定义代价函数(cost function)。如下式:

而总体损失函数,也就是把所有单个预测的损失函数都相加起来。表达式如下:

如果你用很大的训练集来训练这个RNN,你就可以通过开头一系列单词像是Cars average 15或者Cars average 15 hours of来预测之后单词的概率。

这就是用RNN训练一个语言模型的基础结构。下一节课用语言模型做的一件最有趣的事就是从模型中进行采样

《1.7 对新序列采样》Sampling novel sequences

当训练完一个序列模型之后,我们要想了解到这个模型学到了什么,一种非正式的方法就是进行一次新序列采样(have a sample novel sequences),来看看到底应该怎么做。

注意序列模型模拟了任意特定单词序列的概率,我们需要对这些概率分布进行采样(sample)来生成一个新的单词序列。如上图所示:

第一步:对你想要模型生成的一个词进行采样,输入(input)x^<1>=0和a^<0>=0,现在第一个时间步得到的输出是经过softmax层后得到的概率,然后根据这个softmax的分布进行随机采样。Softmax分布给你的信息就是第一个词a的概率是多少,第一个词是aaron的概率是多少,第一个词是zulu的概率是多少,还有第一个词是UNK(未知标识)的概率是多少,这个标识可能代表句子的结尾,然后对这个向量使用numpy命令为:

np.random.choice,来根据向量中这些概率的分布(according to distribution)进行采样,这样就能对第一个词进行采样了。

然后继续下一个时间步,把采样得到的y帽^<1>作为输入(input),现在x^<2> = y帽^<1>,放入到a^<2>中,同样softmax层也会预测y帽^<2>。注意到不管第一个时间步得到的是什么词,都要传递到下一个位置作为输入(input)。

然后再到下一个时间步,无论你得到什么样的用one-hot码表示的选择结果,都把它传递到下一个时间步,然后对第三个词进行采样。不管得到什么都把它传递下去,一直这样直到最后一个时间步。

那么我们要怎样知道一个句子结束了呢?(1)方法之一是,如果你的字典中有代表句子结尾的标识,当采样得到EOS标识时,这代表着已经抵达结尾,可以停止采样了。(2)另一种方法是,如果你的字典中没有EOS标识,你可以决定从20个或100个或其他个单词进行采样,然后一直将采样进行下去直到达到所设定的时间步。不过这种方法可能会产生一些未知标识,如果你要确保你的算法不会输出这种标识,可以拒绝采样过程中产生任何未知的标识,一旦出现就继续在剩下的词中进行重采样,直到得到一个不是未知标识的词。如果你不介意有未知标识产生的话,你也可以完全不管它们。

这就是你如何从你的RNN语言模型中生成一个随机选择的句子。直到现在我们所建立的是基于词汇的RNN模型,意思就是字典中的词都是英语单词。

在实际应用中,我们还可以构建一个基于字符的RNN结构(character level RNN),字典不仅包含从a到z的字母,可能还会有空格符,需要的话,还可以有数字0到9,如果你想区分字母大小写,可以再加上大写的字母,还可以实际地看一看训练集中可能会出现的字符,然后用这些字符组成你的字典。

使用基于字符的语言模型有优点也有缺点。(1)优点(pros)就是不必担心会出现未知的标识,例如基于字符的语言模型会将Mau这样的序列也视为可能性非零的序列。而对于基于词汇的语言模型,如果Mau不在字典中,你只能把它当作未知标识UNK。(2)主要缺点(cons)最后会得到太多太长的序列,大多数英语句子只有10到20个的单词,但却可能包含很多字符。所以基于字符的语言模型在捕捉句子中的依赖关系(也就是句子较前部分如何影响较后部分)不如基于词汇的语言模型那样可以捕捉长范围的关系,并且基于字符的语言模型训练起来计算成本比较高昂。所以Andrew见到的自然语言处理的趋势就是,绝大多数都是使用基于词汇的语言模型。但随着计算机性能越来越高,在一些特殊情况下,会开始使用基于字符的模型。但是这需要更昂贵的计算力来训练,所以现在并没有得到广泛地使用。

总之,在现有的方法下,我们可以构建一个RNN结构,看一看英文文本的语料库,然后建立一个基于词汇的或者基于字符的语言模型,然后从训练的语言模型中进行采样。

如上图,有一些样本(examples),它们是从一个语言模型中采样得到的,准确来说是基于字符的语言模型。如果模型是用新闻文章训练的,它就会生成左边这样的文本;用莎士比亚的文章训练后生成了右边这篇东西。

这些就是基础的RNN结构和如何去建立一个语言模型并使用它,对于训练出的语言模型进行采样。在之后的学习中,Andrew想探讨在训练RNN时一些更加深入的挑战以及如何适应这些挑战,特别是梯度消失问题来建立更加强大的RNN模型。下节课,我们将谈到梯度消失并且会开始谈到GRU(门控循环单元)LSTM(长期记忆网络模型)

《1.8 带有神经网络的梯度消失》Vanishing gradients with RNNs

基本的RNN算法还有一个很大的问题,就是梯度消失(vanishing gradients)的问题。

如上图,这是个语言模型的例子。有两个句子:“The cat, which already ate ……, was full.”“The cats, which ate ……, were full.”它们都有长期的依赖(have very long-term dependencies),前面的单词对句子后面的单词有影响。但是基本的RNN模型(上图编号1所示的网络模型),不擅长捕获这种长期依赖效应。

原因如下:回忆之前的知识,一个很深很深的网络(上图编号2所示),100层,甚至更深,对这个网络从左到右做前向传播然后再反向传播。从输出y帽得到的梯度很难传播回去,很难影响靠前层的权重,很难影响前面层(红色标记)的计算。

对于RNN而言,首先从左向右前向传播,然后反向传播,同样存在梯度消失问题,后面层的输出误差很难影响到前面层的计算,实际上神经网络很难意识到它要记住的是单数还是复数单词,然后在序列后面生成依赖单复数形式的was或者were。所以基本的RNN模型会有很多局部影响,这是基本的RNN算法的一个缺点(weakness)不擅长处理长期依赖的问题

尽管我们一直在讨论梯度消失问题,但是,之前在讲很深的神经网络时也提到了梯度爆炸。在反向传播时,随着层数的增多,梯度不仅可能指数型的下降,也可能指数型的上升。事实上梯度消失在训练RNN时是首要的问题(bigger problem),尽管梯度爆炸也是会出现,但是梯度爆炸很明显,因为指数级大的梯度会让你的参数变得极其大,以至于你的网络参数崩溃。你会看到很多NaN,或者不是数字的情况,这意味着网络计算出现了数值溢出(numerical overflow)。如果你发现了梯度爆炸的问题,一个解决方法就是用梯度修剪(gradient clipping)。梯度修剪的意思就是观察你的梯度向量,如果它大于某个阈值(threshold),缩放梯度向量,保证它不会太大,这就是通过一些最大值(maximum value)来修剪的方法。因此,如果你遇到了梯度爆炸,如果导数值很大,出现了NaN或者不是数字的情况,就用梯度修剪,这是相对比较鲁棒的,这是梯度爆炸的解决方法。

总结一下(summarize),我们了解到训练很深的神经网络时,随着层数的增加,导数有可能指数型的下降或者指数型的增加,我们可能会遇到梯度消失或者梯度爆炸的问题。加入一个RNN处理1,000个时间序列的数据集或者10,000个时间序列的数据集,这就是一个1,000层或者10,000层的神经网络,这样的网络就会遇到上述类型的问题。梯度爆炸基本上用梯度修剪就可以应对,但梯度消失比较棘手。我们接下来会介绍GRU(门控循环单元网络),这个网络可以有效地解决梯度消失的问题,并且能够使你的神经网络捕获更长的长期依赖。

《1.9 GRU单元》Gated Recurrent Unit(GRU)

接下来我们将会学习门控循环单元(Gated Recurrent Unit),它改变了RNN的隐藏层,使其可以更好地捕捉深层连接,并改善了梯度消失问题,让我们看一看。

如上图的公式,在RNN的时间t处,计算激活值,把这个RNN的单元用图形表示(上图左)

备注:不知道为什么右上角的符号就是显示不出来,这篇文章都会()代替,有时间会解决这个bug,望谅解。

输入(input)上一个时间步激活值a^(t-1)和x^(t),并起来乘上权重项,在这个线性计算之后,经过激活函数g(这里是tanh激活函数)后,得到激活值a^(t),然后将激活值a^(t)传入softmax单元,用于输出y^(t)。这张图就是RNN隐藏层的单元的可视化呈现。展示这张图是为了使用相似的图来讲解门控循环单元。

注:许多GRU的想法都来分别自于Yu Young Chang, Kagawa,Gaza Hera, Chang Hung Chu和 Jose Banjo的两篇论文

①Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling

②On the Properties of Neural Machine Translation: Encoder-Decoder Approaches

如上图,还是之前的句子“The cat, which already ate……, was full.”,当我们从左到右读这个句子,GRU单元将会有个新的变量称为c,代表细胞(cell),即记忆细胞(memory cell)。记忆细胞的作用是提供了记忆的能力,比如说一只猫是单数(singular)还是复数(plural),所以当它看到之后的句子的时候,它仍能够判断句子的主语是单数还是复数。在时间t处,有记忆细胞c^(t),GRU实际输出了激活值a^(t),于是有:

于是我们想要使用不同的符号ca来表示记忆细胞的值输出的激活值,即使它们是一样的。Andrew 现在使用这个标记是因为等会说到LSTM的时候,这两个会是不同的值。

所以这些等式表示了GRU单元的计算,在每个时间步,我们将用一个候选值(candidate)重写记忆细胞,它替代了c^(t)的值,我们用tanh激活函数来计算,公式如下:

重点(key)来了,在GRU中真正重要的思想是我们有一个门,把这个门叫做gamma u,这是一个下标为u(字母u代表update)的大写希腊字母Γ,是一个0到1之间的值。实际上这个值是把这个式子带入sigmoid函数得到的,公式如下

对于大多数可能的输入,sigmoid函数的输出总是非常接近0或者非常接近1。

注意GRU的关键部分(key part)就是之前候选值的计算

首先我们得到候选值,然后门决定是否要更新它。①于是记忆细胞c^(t)被设定为0或者1,这取决于你考虑的单词在句子中是单数还是复数,这里单数情况我们先假定它被设为了1,如果是复数的情况我们就把它设为0。②GRU单元会一直记住记忆细胞c^(t)的值。③门Γ_u的作用就是决定什么时候会更新这个值,特别是当你看到词组the cat,即句子的主语猫,这就是一个好时机去更新这个值。然后当你使用完它的时候,“The cat, which already ate…, was full.”,然后你就可以忘记它了。

我们接下来要给GRU用的式子就是:

注意到,①当更新值Γ_u = 1时,表示更新它,有:

②当更新值Γ_u = 0时,意味着不更新它,则有:

下面再画个图来(下图左侧所示)解释一下GRU单元。Andrew个人感觉式子在图片中比较容易理解,那么即使看不懂图片也没关系

这就是GRU单元或者说是一个简化过的GRU单元,它的优点就是通过门决定,当从左到右扫描一个句子的时候,这个时机决定是否要更新某个记忆细胞,不更新(中间一直为0,表示一直不更新)直到你真的需要使用记忆细胞的时候再更新。

因为sigmoid的值,现在因为门很容易取到0值,只要这个值是一个很大的负数,再由于数值上的四舍五入,上面这些门大体上就是0,或者说非常非常非常接近0。所以在这样的情况下,这个更新式子就会变成:

这非常有利于维持记忆细胞的值因为Γ_u很接近0,这样就不会有梯度消失的问题。这就是缓解梯度消失问题的关键,因此允许神经网络运行在非常庞大的依赖词上,比如说cat和was单词即使被中间的很多单词分割开。

如上图,我们关注一些细节。记忆细胞c^(t)可以是一个向量(vector),假设有一个100维的隐藏的激活值,那么记忆细胞、替代值和更新门(上图红色标记)的维度也是100维。

在实际应用中Γ_u不会真的等于0或者1,有时候它是0到1的一个中间值,元素对应的乘积做的就是告诉GRU单元哪个记忆细胞的向量维度在每个时间步要做更新,所以你可以选择保存一些比特不变,而去更新其他的比特。比如说你可能需要一个比特来记忆猫是单数还是复数,其他比特来理解你正在谈论食物,因为你在谈论吃饭或者食物,然后你稍后可能就会谈论“The cat was full.”,你可以每个时间点只改变一些比特。

我们已经理解GRU最重要的思想了注意下图的三个式子

目前为止幻灯片中展示的实际上只是简化过的GRU单元,现在来描述一下完整的GRU单元,如下图:

对于完整的GRU单元,在我们计算的第一个式子中给记忆细胞的新候选值加上一个新的门Γ_r,r代表相关性(relevance),表示下一个记忆细胞c^的候选值跟上一个记忆细胞c^有多大相关性。这里引入一个新的参数矩阵W_r,计算公式如下:

为什么要引入一个新的门呢?这是因为多年来研究者们试验过很多不同可能的方法来设计这些单元,去尝试让神经网络有更深层的连接,去尝试产生更大范围的影响,还有解决梯度消失的问题,GRU就是其中一个研究者们最常使用的版本,也被发现在很多不同的问题上也是非常健壮和实用的。另一个常用的版本被称为LSTM(长短时记忆网络),这个我们会之后讲到,但是GRU和LSTM是在神经网络结构中最常用的两个具体实例。

还有在符号上的一点,Andrew尝试去定义固定的符号让这些概念容易理解。总的来说,这就是GRU,即门控循环单元,这是RNN的其中之一。这个结构可以更好捕捉非常长范围的依赖,让RNN更加有效。

还有其他常用的神经网络,比较经典的是这个叫做LSTM,即长短时记忆网络,我们在下个视频中了解。

《1.10 长短期记忆(LSTM)》LSTM(long short term memory)unit

上一个视频中我们学习了门控循环单元-GRU单元(Gated Recurrent Unit)它能够实现在序列中学习非常深的连接。其他类型的单元也可以做到,比如LSTM即长短时记忆网络,甚至比GRU更加有效,让我们看看。

如上图左侧是GRU的公式,有两个门(gates):更新门Γ_u(the update gate)相关门Γ_r(the relevance gate)

LSTM是一个比GRU更加强大和通用的版本,这多亏了 Sepp Hochreiter和 Jurgen Schmidhuber的论文”Long short-term memory”,它在序列模型上有着巨大影响。Andrew感觉这篇论文是挺难读懂的,虽然这篇论文在深度学习社群有着重大的影响,它深入讨论了梯度消失的理论,但是似乎大部分的人学到LSTM的细节是在其他的地方,而不是这篇论文。

上图右侧为LSTM的主要式子。同样考虑记忆细胞c的候选值,计算公式为:

(备注:还是这个问题,不知道为什么右上角的符号就是显示不出来,这篇文章都会()代替,有时间会解决这个bug,望谅解。)

注意这里有一些改变,专门使用a^(t)或者a^(t-1),而不是用c^(t-1),所以不再有a^(t) = c^(t)的情况。

同样也有更新门Γ_u更新参数W_u,表达式如下:

LSTM的新特性之一是不只有一个更新门控制,还有遗忘门Γ_f(the forget gate),表达式如下:

以及有一个新的输出门Γ_o(output gate),表达式如下:

于是记忆细胞的更新值公式为:

最后的式子为:

以上就是LSTM主要的式子,这里有三个门而不是两个,这有点复杂,它把门放到了和GRU有点不同的地方。

如上图,这里也用图片解释一下。Andrew认为式子比图片好理解,画图只是因为它比较直观。右上角的图的灵感来自于Chris Ola的一篇博客,标题是《理解LSTM网络》(Understanding LSTM Network),这里的这张图跟他博客上的图是很相似的。下面具体解释一下。

这里使用a^(t-1)和x^(t)一起来计算遗忘门、更新门和输出门的值。注意一下下方的三张图,把它们按照时间次序连接起来,这里输入x^(1)、x^(2)和x^(3),有个很有意思的事情(红色直线),这条线显示了只要你正确地设置了遗忘门和更新门,LSTM是相当容易把a^的值一直往下传递到右边,比如

这就是为什么LSTM和GRU非常擅长于长时间记忆某个值,对于存在记忆细胞中的某个值,即使经过很长很长的时间步。

这里和一般使用的版本会有些不同,最常用的版本可能是门值不仅取决于a^(t-1)和x^(t),也关注c^(t-1)的值。这叫做“窥视孔连接”(peephole connection)。如下图绿色标记:

以上就是LSTM。那么我们什么时候应该用GRU?什么时候用LSTM?这里没有统一的准则。在深度学习的历史上,LSTM也是更早出现的,而GRU是最近才发明出来的,它可能源于Pavia在更加复杂的LSTM模型中做出的简化。研究者们在很多不同问题上尝试了这两种模型,看看在不同的问题不同的算法中哪个模型更好。两者的优点如下

GRU的优点(advantage)是这是个更加简单的模型,所以更容易创建一个更大的网络,而且它只有两个门,在计算性上也运行得更快,然后它可以扩大模型的规模。

LSTM的优点(advantage)为更加强大和灵活,因为它有三个门而不是两个。

Andrew建议:LSTM在历史进程上是个更优先的选择,今天大部分的人还是会把LSTM作为默认的选择来尝试。虽然最近几年GRU获得了很多支持,而且越来越多的团队也正在使用GRU,因为它更加简单,而且还效果还不错,它更容易适应规模更加大的问题。

总的来说,无论是GRU还是LSTM,你都可以用它们来构建捕获更加深层连接的神经网络。

《1.11 双向神经网络》Bidirectional RNN

目前为止,我们已经了解了大部分RNN模型的关键的构件(key building blocks),还有两个方法(two more ideas)可以让你构建更好的模型,①双向RNN模型(bidirectional RNN),它可以让你在序列的某点处不仅可以获取之前的信息,还可以获取未来的信息。②第二个就是深层的RNN(deep RNN)。我们现在先从双向RNN开始吧。

回顾命名实体识别的例子,在判断第三个单词Teddy是不是人名的一部分时,我们只看了前三个单词,除了这些信息我们还需要更多的信息。因为根据前3个单词无法判断他们说的是Teddy熊,还是前美国总统Teddy Roosevelt,所以这是一个非双向的或者说只有前向的RNN。不管这些单元(上图方形区域)是标准的RNN块,还是GRU单元或者是LSTM单元,只要这些构件都是只有前向的

那么一个双向的RNN是如何解决这个问题的?下面解释双向RNN的工作原理

(备注:还是这个问题,不知道为什么右上角的符号就是显示不出来,这篇文章都会()代替,有时间会解决这个bug,望谅解。)

如上图,为了简化,用一个只有4个单词(4个输入,x^(1)到x^(4))的句子,这个网络会有一个前向的循环单元(紫色标记),在这上面加个向右的箭头(right arrow)来表示前向的循环单元(forward recurrent component),然后连接起来。这四个循环单元都有一个当前输入x输入进去,得到预测的y帽。

如上图,我们增加一个反向循环层(绿色标记),左箭头代表反向连接。这样,这个网络就构成了一个无环图(acyclic graph)。给定一个输入序列x^(1)到x^(4),序列先计算前向a^(1)(右箭头),然后计算a^(2)(右箭头)、a^(3)(右箭头)和a^(4)(右箭头)。然后反向序列从a^(4)(左箭头)开始计算,反向进行,计算反向是a^(3)(左箭头)。你计算的是网络激活值,这不是反向而是前向的传播,而图中这个前向传播一部分计算是从左到右,一部分计算是从右到左。可以用这些激活值计算反向的a^(2)(左箭头),然后是反向的a^(1)(左箭头),把所有这些激活值都计算完了就可以计算预测结果了。

为了预测结果,应用激活函数有公式:

具体看一个例子:

如上图黄色标记,比如你要观察时间3这里的预测结果,信息从x^(1)过来,流经前向的a^(1)(右箭头)和a^(2)(右箭头),到前向的a^(3)(右箭头)再到y^(3),这条路径把输入信息x^(1)到x^(3)都考虑在内。而x^(4)的信息会流到反向的a^(4)(左箭头),到反向a^(3)(左箭头),再到y^(3),这条路径使用了输入信息x^(4)。这样使得时间3的预测结果不仅输入了过去的信息,还有现在的信息,同时涉及了前向和反向的传播信息以及未来的信息。给定一个句子"He said Teddy Roosevelt..."来预测Teddy是不是人名的一部分,你需要同时考虑过去和未来的信息。

这就是双向循环神经网络(bidirectional recurrent neural network),并且这些基本单元不仅仅是标准RNN单元,也可以是GRU单元或者LSTM单元。事实上,很多的NLP问题,对于大量有自然语言处理问题的文本,有LSTM单元的双向RNN模型是用的最多的。所以如果有NLP问题,并且文本句子都是完整的,首先需要标定这些句子,一个有LSTM单元的双向RNN模型,有前向和反向过程是一个不错的首选。

以上就是双向RNN的内容,这个改进的方法不仅能用于基本的RNN结构,也能用于GRU和LSTM。

通过这些改变,可以用一个用RNN或GRU或LSTM构建的模型,并且能够预测任意位置,即使在句子的中间,因为模型能够考虑整个句子的信息。这个双向RNN网络模型的缺点(disadvantage)就是你需要完整的数据的序列(the entire sequence of data)才能预测任意位置。比如构建一个语音识别系统,那么双向RNN模型需要考虑整个语音表达,你需要等待这个人说完,然后获取整个语音表达才能处理这段语音,并进一步做语音识别。对于实际的语音识别的应用通常会有更加复杂的模块(somewhat more complex modules),而不是仅仅用我们见过的标准的双向RNN模型。但是对于很多自然语言处理的应用,如果你总是可以获取整个句子,这个标准的双向RNN算法实际上很高效(very effective)。

这就是双向RNN,下一个视频我们会讨论如何用这些概念,标准的RNN,LSTM单元,GRU单元,还有双向的版本,构建更深的网络。

《1.12深层循环神经网络》Deep RNNs

目前我们学习了不同RNN的版本,每一个都可以独当一面(already work quite well by themselves)。但是要学习非常复杂的函数,通常我们会把RNN的多个层堆叠在一起构建更深的模型,来构建一些更深的RNN。我们开始吧。

如上图左侧,一个标准的神经网络,首先是输入x,然后堆叠上隐含层,所以这里应该有激活值,比如说第一层是a^[1],接着堆叠上下一层,激活值a^[2],可以再加一层a^[3],然后得到预测值y帽。深层的RNN网络如上图右侧,还是画这样的网络,然后按时间展开。注意这里的符号,a^[1](0)表示第1层0时刻的激活值,更一般的用a^[l](t)表示第l层第t个时间点的激活值。上图是一个有三个隐层的新的网络。

(备注:还是这个问题,不知道为什么右上角的符号就是显示不出来,这篇文章都会()代替,有时间会解决这个bug,望谅解。)

看一个具体例子,如上图紫色标记,看这个激活值a^[2](3)是怎么计算的,一共有两个输入:一个是下面过来的输入a^[1](3),一个是左边过来的输入a^[2](2),于是计算公式为:

上述参数(W_a)^[2]和(b_a)^[2]在这一层的计算里都一样。相应的第一层也有自己的参数(绿色标记)。

对于左侧这样标准的神经网络,有很深的网络,甚至于100层深,而对于RNN来说,有三层就已经不少了。由于时间的维度,RNN网络会变得相当大,即使只有很少的几层,很少会看到这种网络堆叠到100层。

但有一种会容易见到,如下图:

在每一个上面堆叠循环层(recurrent layers),把上述第3层的输出y^(1)去掉,换成一些深的层,这些层并不水平连接,只是一个深层的网络,然后用来预测y^(1)。其他几个类似。这种类型的网络结构用的会稍微多一点,这种结构有三个循环单元,在时间上连接,接着一个网络在后面接一个网络。通常这些单元(上图方框)没必要非是标准的RNN,也可以是GRU单元或者LSTM单元,或者构建深层的双向RNN网络。由于深层的RNN训练需要很多计算资源,需要很长的时间,尽管看起来没有多少循环层,这个也就是在时间上连接了三个深层的循环层(蓝色圆圈),你看不到多少深层的循环层,不像卷积神经网络一样有大量的隐含层。

这就是深层RNN的内容,从基本的RNN网络,基本的循环单元到GRU,LSTM,再到双向RNN,还有深层版的模型。现在工具箱中已经有了很多工具(have a rich toolbox)

说明:记录学习笔记,如果错误欢迎指正!转载请联系我。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值