深度学习之序列模型

序列模型 —吴恩达深度学习第五课

Week 1循环序列模型

1.1 为什么选择序列模型?

在本次课程中会学习自行创建这些序列模型,先从一些使用了序列模型的例子来进行了解。

  1. 语音识别
    给出一段输入音频片段X,并要求输出片段对应的文字记录Y,在这个例子中输入数据和输出数据都是序列数据。因为X是一个按时序播放的音频片段,输出Y时一系列单词。所以之后将要学习到的一些序列模型,如循环神经网络等对于语音识别方面都是比较有用。
  2. 音乐生成问题
    也是序列模型的一个例子。而且只有输出数据是序列数据,它的输入数据可以是空集,也可以是个单一的整数,这个数可能指代你想要生成的音乐风格也可能是你想要生成的曲子的头几个音符.
  3. 情感分类
    输入数据是序列,例如“There is nothing to like in this movie.”,这句评论应为几星?—> *
  4. DNA序列分析
    DNA序列可以用A G C T来表示,当给出一段DNA序列:AGCCCCTGTGAGGAACTAG,你能够标记出(AGCCCCTGTGAGGAACTAG)哪部分是匹配哪种蛋白质。
  5. 机器翻译
    输入:Voulez-vous chanter avecmoi? 输出:Do you want to sing with me?
  6. 视频行为识别
    输入:一系列的视频帧 输出:识别视频帧中的行为
  7. 命名实体识别
    给定一个句子,要求你识别出句子中的人名。
    所有这些问题都可以可以称为:使用标签数据(X,Y)作为训练集的监督学习。但是从这些例子中也可以看出序列问题有很多不同类型,有些问题里输入数据和输出数据都是序列,即使是在这种情况下X和Y有时也会不一样长,但是在例子4和例子7中是一样长的。在另一些问题里,只有X或是只有Y是序列。通过本课程会学到一些使用不同情况的序列模型。

1.2 数学符号 (Notation)

定义序列学习符号,以便于一步步构建序列模型。
Motivating Example
当想要一个能够自动识别句中人名位置的序列模型时(显然是一个命名实体识别问题),这常用于搜索引擎,比如想要搜取过去24小时内所有新闻报道提及的人名,用这个方法就能够恰当的进行索引,命名实体识别系统可以用来查找不同类型文件中的人名、公司名、时间、地点、国家名等信息。
给定了一个输入语句x,假如想要一个序列模型输出y,是的输入的每个单词都对应一个输出值,同时y又能够表明单词是否是人名的一部分,如果是人名则输出1,否则输出0。

输入语句x: Harry Potter and Hermione Granger invented a new spell .
x1 x2 x3 x4 x5 x6 x7 x8 x9 (上标尖括号)
输出y: 1 1 0 1 1 0 0 0 0
y1 y2 y3 y4 y5 y6 y7 y8 y9 (上标尖括号)

上面这种输出方式也许并不是最好的,还有其它的复杂输出方式,不仅能够知道这个输入词是否是人名的一部分,还能知道这个人名在这个句子里从哪里开始到哪里结束。但是在上述这个例子中会讨论这种简单的输出方式。上述例子中的输入数据是有9个单词组成的序列,所以最后会有9个特征集合来表示这9个单词,并按序列中的位置(x^1)进行索引。用x^t来表示这个序列的中间位置,t表示这是一个时序序列,但不论是否是时许序列在这里都用t来表示索引序列中的位置,输出数据也一样。

  • y^t:表示输出数据
  • T_x:表示输入序列的长度
  • T_y:表示输出序列的长度,在本例中T_x = T_y = 9,但是在有些问题中两者的值也会不一样。
  • x^(i)尖括号t:表示第i个训练样本的第t个元素
  • T_x^(i):表示的第i个训练样本的输入序列长度

一件需要我们事先决定的是:应该怎样表示句子中的一个单词?例如Harry这样的单词。而x^1究竟是什么。
想要表示一个句子里的单词第一件事是做一张词表,[a,aaron,…,and,…,Harry,…Potter,…zulu],词表以列的的形式存在。
构建这么一个词表的方法是:遍历你的训练集,然后查找到前10000个常用的单词,可以去浏览一些网络词典获得英语中常用的前10000个单词。
接下来可以用one-hot表示法来表示词典里的每个单词,例如x^1表示Harry(在词典中位于第4075个位置)这个单词,那么就是一个第4075行是1其余位置都是0的列向量。
用这种方式表示X的目的是,用序列模型在X和目标输出Y之间,学习建立一个映射。
还剩最后一件事:如果遇到的单词不在词表中时,创建一个新的标记,也就是一个叫做Unknown Word的伪造单词,用“UNK”作为标记来表示不在词表中的单词。

1.3 循环神经网络模型

如何建立一个模型/神经网络来学习X到Y的映射。
可以尝试的方法之一是:使用标准的神经网络。

把上述例子中9个单词输入到一个标准的神经网络中,经过一些隐藏层后会输出9个值为0或1的项,它表明9个输入单词是否是人名的一部分。但是结果表明这种方法并不好,主要有两个问题:

  1. 输入输出数据在不同的样本中的长度不一样
  2. 在文本的不同位置上学到的特征并不共享(单词和单词之间的特性无法被捕捉到)。

比如神经网络在x^1的位置上发现harry是人名的一部分,在其他位置发现harry时你也希望也能识别出是人名出来。在卷积网络中我们是这样学习的,最大单词数乘以10000维的维度,那么在第一层的权重矩阵中会有巨量的参数。假设我们有10000个常用词,为其构建一个10000*1 的矩阵(column matrix),假如第一个词是苹果(apple), 那么对应的第一个位置为1,其他都为0,所以称之为one-hot。这样每个单词都有对应的矩阵进行表示,如果这个词没有出现在我们的字典中,那么我们可以给一个特殊的符号代替,常用的是 [UNK] (unknown)

但是在下面要讲的循环神经网络却没有上述两种问题,那么什么是循环神经网络?

如果从左到右读句子第一个单词假如说是x[^1],将第一个单词输入到神经网络中的隐藏层,尝试预测输出y(判断是否是人名的一部分),那么循环神经网络做了些什么?当我们输入第二个单词时,同样通过隐层输出y,它不仅仅是利用了第二个单词的信息,它还利用了时间步1的信息,具体而言就是时间步1的激活值就会传递到时间步子2.在下一个时间步,在第三个单词输出y,直到最后一个时间布输入了x[^Tx],输出y[^Ty]。在这个例子中Tx = Ty,但是如果Tx和Ty不同上面的神经网络结构也会有变化。所以在每一个时间步,循环神经网络传递一个激活值a到下一个时间布中用于计算。为了开始整个流程,在时间步0处设置一个激活值a^0,通常是一个零向量(伪激活值0最常见),也可以随即用其他方法初始化a^0。
还有一种循环神经网络结构,为了表示循环画一个圈表示输回网络层,在这个圈上有一个黑色方块表示会延迟一个时间步。

用W_ax表示管理着从x^1到隐藏层的连接的一系列参数,每个时间步使用的都是相同的参数W_ax,而激活值是水平联系,是由参数W_aa决定的,同时每一个时间步使用的都是相同的参数W_aa,每一个时间步的输出由W_ya决定。

循环神经网络是从左向右扫描数据,同时每个时间步是共享参数的,共享参数,但有一个缺点是它只使用了当前位置在序列中之前的输入信息来预测并没有使用序列中后部分的信息。而双向循环神经网络解决了这个问题,它同时使用前面和后面的信息。

那么这些参数是如何起作用的?

上图是一张清理后的神经网络图示,一开始先输入a^0(是个零向量),接下来就是前向传播过程,为了算出a(1):
a(1)=g(W_aaa^(0)+W_axx^(1)+B_a) –>循环神经网络常用激活函数:tanh/Relu
y(1)=g(W_ya*a^(1)+B_y) –>根据输出选择sigmooid/softmax

(矩阵下标的符号说明:W_ax,第二个下标意味着W_ax要乘以某个x类型的量,a是表明要计算的某个a类型的量)

更常用:
a(t)=g(W_aaa^(t-1)+W_ax(x^t)+B_a)
y(t)=g(W_yaa^(t)+b_y)
简化上面两个等式:
a(t)=g(W_a[a^(t-1),x^(t)]+B_a)
定义W_a = [W_aa|W_ax],假设W_aa是一个(100,100)维的矩阵,那么W_ax是(100,10000),W_a是(100,10100)
[a^(t-1),x^(t)]的意思是将这两个向量行堆在一起,形成了一个10100维度的向量。这样做的好处是把参数矩阵压缩成一个W_a,当建立更加复杂的模型时可以简化符号。
同样地,y^t = g(W_y
a^(t) + B_y),W_y和B_y仅有一个下标表明输出什么类型的量。

1.4 通过时间的反向传播

损失函数,定义为标准的logistic回归损失函数,也称为交叉熵损失函数。

在这个反向传播过程中,最重要的信息传递或者说最重要的递归运算就是从右到左的运算,这种运算有一个别致的名字:通过时间(“穿越时间”)反向传播。

1.5不同类型的循环神经网络

在前面几节已经了解了T_x和T_y相等情况下的循环神经网络。但是T_x和T_y并不总是相等的。

Examples

  • Music generation:T_x长度可以是0(空集)或1,one-to-many形式,就是一个序列生成问题。

  • 文本情感分类:输出y可以是1到5的,而输入是一个序列我们在RNN结构中读入所有单词后再输出y’(预测值,一个数字),many-to-one

  • 机器翻译:many-to-many,输入输出的长度不同。


对于Many to many 结构有两种:

  1. T_x和T_y相等。
  2. T_x和T_y不相等。

1.6语言模型与序列生成(Language model and sequence generation)

什么是语言模型呢?

语音识别系统
the apple and pair salad.
the apple and pear salad.

  • p(the apple and pair salad)=3.2*10^(-13)
  • p(the apple and pear salad)=3.2*10^(-10)
    语言模型所做的就是:一个句子出现的概率是多少?p(sentences)= ?
    语言模型所的基础工作:输入一个句子(准确的说是一个文本序列y^1,…,y^(T_y)),语言模型会估计某个句子中各个单词出现的概率。
    So,如何来用RNN来建语言模型呢?

    首先需要一个训练集,包含一个很大的英文文本语料库或者其他的语料库。
    假如有一个句子:
    Cats average 15 hours of sleep a day.
    首先把这个句子标记化(tokenize),即像之前那样创建一个词典,把每个单词转换成对应的one-hot向量,也就是字典中的索引,另外增加一个’EOS’为句子的结束标记。EOS也可以做为训练集中每一个句子的结尾。如果训练集中有一些词不在词典中,unk标记这个单词。我们只针对UNK这个词来进行概率模型构建而不针对这个具体的词。完成标记化的过程后表示将这个句子都映射到了各个标志上或者说字典中的各个词上。接下来我们会构建一个RNN模型来构建这些序列的概率模型。

构建RNN模型

(把x^t设为y^(t-1))
Cats average 15 hours of sleep a day.《EOS》
在第0个时间不,计算激活项a^1,它是以x^1作为输入的函数,在这里x^1是被设为零向量的,在之前的a^0,按照惯例也是被设为零向量的,于是a^1要做的是它会通过softmax进行一些预测来计算出第一个词,其结果就是y’^1。这一步其实是通过一个soft max层来预测字典中的任意一个单词会是第一个词的概率。 所以y’^1只是来预测第一个词的概率,而不必管结果是什么。所有当字典中有10000个词时通过softmax就会有10000种结果(或包括UNK和EOS两种在内的10002中)。然后RNN进入下个时间步,仍然使用激活项a^1,然后在这一步要做出的计算是第二个词会是什么,现在我们依然传给它正确的第一个词y^1(cats),其实也就是x^2,使用激活项a^2,通过softmax层得到y’^2,也就是p
(average/cats)为多少,然后依照之前传入下一时间步。然后最后会传递到如上图所示第九个时间步,然后把x^9也就是y^8=”day”传递给它,得到y’^9(“EOS”)。看图理解!

接下来为了用RNN来训练这个网络,我们要定义代价函数:

或者也可以理解为:预测每个词的概率是多少,y是一个10000+2维(字典大小+unk+结束标记)。
现在有一个新的序列 y1,y2,y3.现在要计算出整个句子各个单词的出现概率P(y^1,y^2,y^3) = p(y^1)p(y^2|y^1)p(y^3|y^1,y^2),第一个softmax层告诉你p(y1)第二个softmax,p(y2|y1),第三个p(y3|y1,y2)。

接下来讨论如何用语言模型从模型中采样。

1.7 对新序列采样

当你训练了一个模型后,要想了解这个模型学到了什么,一个非正式的方法是进行一次新序列采样。
记住,一个序列模型,模拟了任意特定单词序列的概率,我们要做的是对这个概率分布进行采样,来生成一个新的单词序列。 这个网络已经被下图中的所示结构训练过了, 而为了进行采样要做一些截然不同的事情。

比如说现在有一个训练好的模型,为了进行采样,第一步要做的事就是,对你想要模型生成的第一个词进行采样,于是输入x^1=0,a^1=0,而第一个时间步得到的是所有可能的输出,是经过softmax层后得到的概率,然后 根据这个softmax的分布进行随机取样
softmax给你的就是第一个单词是a的概率是多少,是aaron的概率是多少,…是zulu或是unk的概率是多少, 然后对这个向量使用例如np.random.choice来根据向量中这些概率的分布进行采样,这样就能对第一个词进行采样了。
然后继续下一个时间步,记住,第二个时间步需要y^1作为输入,而现在要做的是把刚刚进行采样的词y’^1当作下一时间步的输入x^2,无论在上一个时间步得到的是什么词都要把他传递到下一个位置作为输入,(x2=y’^1),然后softmax就会预测得到y’^2。
比如,你在第一个时间步得到的是the, 然后把它作为x^2,
同样依次进行。不管得到什么都把它传递下去知道最后一个时间步。 怎么判断这个句子结束?方法就是,如果代表句子结尾的EOS标记在你的字典中,你可以一直进行采样直到得到EOS标记,这代表着你已经抵达结尾,可以停止采样了。
另外一种情况是,如果字典中没有这个词,你可以决定从20个或100个或其他个词中进行采样 ,然后一直将抽样进行下去,直到达到所设定的时间步。不过这种过程会产生一些未知标记,如果你要确保你的算法不会输出这种标识,就要拒绝采样过程中产生任何未知的标记,一旦出现就继续在剩下的词中重新进行采样,直到得到一个不是未知标识的词,不介意未知标识产生的话你也可以不管。以上就是如何从一个RNN语言模型生成一个随机选择的句子。
直到现在我们所建立的是基于词汇的RNN模型,意思就是字典中的词都是英语单词。根据实际应用也可以构建一个基于字符的RNN结构,字典中仅包含从a到z的字母,也可以有空格符、数字0到9,大写字幕A到Z,[a,b,c,..z, ,。,,,;,0,…,9,A,…,Z],可以看一看实际的训练集中可能会出现的字符,一起来构建一个字典。相较于基于词汇的模型,基于字符模型的y^1,y^2等都是单独的字符而不是单独的词汇。基于字符的语言模型,不会产生unk标志,一得到很多、很长的序列,基于字符的语言模型,在捕捉句子中也只是捕捉句子较前部分如何影响较后部分,不如基于词汇的模型能够捕捉长范围的信息。

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


以一个语言模型为例子,有一句话如下:

  • The cat which already ate and maybe already ate a bunch of food that was delicious … was full.
    其中cat是单数所以是“was full”。
  • The cats which already ate …,were full. cats对应的是”were full”。

这个句子中的信息有长期的依赖,最前面的单词对后面的单词有影响。但是目前我们所见到的RNN语言模型结构,不擅长捕捉这种长期依赖效应。
之前讨论过训练很深的网络会有梯度消失的问题。比如说有一个很深很深的网络,对这个网络从左到右做前向传播然后再反向传播, 我们知道如果是个很深的网络难么得到的y’很难传播回去,很难影响到靠前面的权重,很难影响前面层的计算。对于有同样问题的RNN,很难让一个很深的网络层能够意识到要记住前面的信息然后告诉后面的序列信息生成依赖效应从而选择是用was 还是were。
基本的RNN模型有很多限制,很难调整序列前面的计算。如果不解决的话RNN会不擅长这种长期依赖问题。在反向传播时,随着层数的增多,梯度不仅可能指数型的下降,还会出现指数级的上升(梯度爆炸问题)。事实上,梯度下降在训练RNN时是首要的问题,尽管梯度爆炸也会出现,但是梯度爆炸很明显,因为指数级大的梯度会让参数变得很大,以至于网络参数崩溃,所以梯度爆炸很容易发现,会看到很多NaN或者不是数字的情况,这意味着你的网络计算出现了数值溢出。如果发现了梯度爆炸的问题,一个解决方法就是用梯度修剪,即观察梯度向量如果它大于某个阈值,缩放梯度向量保证它不会太大, 这就是通过一些最大值来修剪的方法。
梯度消失更难解决。

1.9 GRU(Gated Recurrent Unit,门控循环单元)

GRU改变了RNN的隐藏层,使其更好的捕捉深层连接(捕捉长范围的依赖)并改善了梯度消失的问题。

上图(是RNN隐藏层的单元的可视化呈现)中的公式是指:在一个RNN结构的时间序列t处计算激活值, 把RNN的单元画个图,输入a^(t-1),即上一个时间步的激活值,然后在输入一个x^t,把它们两个合并起来,然后加上权重项,经过计算后,假设g是tanh函数,经过tanh得到激活值a^t,然后这个激活值会传递给一个softmax单元或者其它用于产生输出y’^t的东西。


许多GRU的想法来自于两篇paper。上图中给出的句子,如下:

the cat which already ate ...,was full.  

当我们从左到右读这个句子,GRU会有一个新的变量c代表记忆细胞(memory cell),当它看到一只猫是单数还是复数之再看到后面的句子后仍能够判断句子的主语究竟是单数还是复数。于是在时间t处,记忆细胞有关于t的值c。然而实际上GRU输出了激活值a^t,c^t = a^t。于是我们使用不同的符号c和a来表示记忆细胞的值和输出的激活值,即使他们是一样的。(注意:在LSTM中这两个值是不一样的。
在每一个时间步,我们将用一个候选值(即c~^t的值,就是个替代值)重写记忆细胞,候选值替代了c^t的值,然后用tanh函数来计算这个候选值。公式如下

c~^t=tanh(W_c[c^(t-1),x^t]+b_c) --用c~更新c的等式 

然而GRU中最重要的思想是: 我们有一个门(gate),把这个门叫做Γ_u ,其中Γ是大写的希腊字母读作gamma,u是下标,代表更新门(update gate),这是一个0到1之间的值。

Γ_u=sigmoid(W_u[c^(t-1),x^t]+b_u)  --用F_u更新门决定是否要真的更新  

于是我们可以这么看待,记忆细胞c将被设定为0或1,这取决于你考虑的单词在句子中是单数还是复数,然后GRU单元会一直记住c^t的值(假设为1),一直到(”was”)处还是1,所以用was。于是gate的作用就是决定什么时候你会更新这个值,特别是当你看到词组 the cat(句子的主语)时,你会知道在说一个新的概念,这就是一个好时机去更新,然后当你使用它的时候,就不需要记住它了(这个地方我有疑问)。
So,接下来要给GRU用的式子是:

c^t = Γ_u * c~^t + (1-Γ_u)*c^(t-1)     

注意到,如果更新值Γ_u是1,也就是说把这个新值c^t设为候选值,然后相同的将门值设为1,然后往前,再更新这个值,对于所有在这直接按的值应该把门的值设为0(不更新用旧的值)。

因为gate很容易取到0,只要(W_u[c^(t-1),x^t]+b_u)这个值是一个很大的负数,就基本为0,这样有利于维持细胞的值,因为gamma很接近0,可能是0.0000001或者更小,这样就不会有梯度消失的问题,这就是说c^t基本等于c^(t-1),而c^t的值也很好的被维持了,即使经过很多的时间步还是可以很好的维持下来,这就是缓解梯度消失的关键。
一些实现细节:c^t可以是向量,假如有一个100维的隐藏的激活值,那么c^t也是100维的,c~^t、Γ_u也是相同的维度。


完整的GRU
对于full GRU要做的一个改变就是:在第一个式子中给记忆细胞的新候选值,加上一个新的项Γ_r,您可以认为r代表相关性,Γ_r(相关门)用来计算出下一个c^t的候选值和c^(t-1)的相关性程度。

Γ_r=sigmoid(W_r[c^(t-1),x^t]+b_r)  

1.10 长短期记忆(Long Short Term Memory,LSTM)

比GRU更有效、更广泛应用。

注意到,在LSTM中不再有a^t=c^t的情况,一个LSTM的新特性是不只有一个更新门Γ_u控制,还有一个遗忘门Γ_f,一个新的输出门Γ_o, 于是更新值

c^t = Γ_u*c~^t + Γ_f*c^(t-1)

所以这给了记忆细胞选择权去维持旧的值c^(t-1)或者就加上新的值c~^t,这时 a^t = Γ_o*c^t 。下图中就是控制LSTM的几个公式:

选择使用GRU或LSTM并没有统一的准则。GRU的结构更简单,更容易去构建一个更大的网络,它只有两个门在计算性上也运行的更快,扩大模型的规模。但是LSTM有三个门,更加强大和灵活。

1.11 双向神经网络(Bidirectional RNN,BRNN)

以上已经了解过RNN模型的关键的结构,还有两种方法能构建更好的模型,其中之一就是双向RNN模型,这个模型不仅可以让你在序列的某点处获取之前的信息还可以获取未来的信息;第二个就是深层的RNN。

为了了解双向RNN的动机,我们先看下命名实体识别那个例子上图所示。
Teddy判断是不是人名有个问题,只看前几个单词是无法准确判断的,
双向RNN可以解决这个问题。下图解释双向RNN的工作原理。

给定一个输入序列,这个网络的前向先计算a^1,a^2,…,a^(T_x),然后反向的序列计算a^(T_x),…,a^2,a^1,这两个方向的计算都是前向传播,一个是从左到右的前向计算,一个是从右到左的反向计算。把这些反向的网络激活值都算完了就可以计算预测结果了。

事实上,很多的NLP问题对于大量有自然语言处理问题的文本,有LSTM单元的双向RNN模型是用的最多的,所以如果有NLP问题,并且文本句子都是完整的,首先需要标定这些句子,一个有LSTM单元的双向RNN模型有前向和反向传播过程是个不错的首选。

1.12 深度RNN

有的时候为了得到更好的模型,需要把很多个RNN堆叠加在一起,这种网络我们称之为深层RNN。
用a^([1]0)表示原来的激活值a^0。
a^([l]t):l表示第l层,t表示第t个时间点。
比如我们要计算 a^([2]3),它有两个输入。

a^([2]3)=g((W_a)^[2] [a^([2]2),a^([1]3]+(b_a)^[3])

-------------本文结束感谢您的阅读-------------
0%