吴恩达深度学习课程笔记Lesson14(下)

第五门课 序列模型(Sequence Models)(下)

第一周 循环序列模型(Recurrent Neural Networks)

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

在自然语言处理中,构建语言模型是最基础的也是最重要的工作之一,并且能用RNN很好地实现。在本视频中,你将学习用RNN构建一个语言模型,在本周结束的时候,还会有一个很有趣的编程练习,你能在练习中构建一个语言模型,并用它来生成莎士比亚文风的文本或其他类型文本。

在这里插入图片描述

所以什么是语言模型呢?比如你在做一个语音识别系统,你听到一个句子,“the apple and pear(pair) salad was delicious.”,所以我究竟说了什么?我说的是 “the apple and pair salad”,还是“the apple and pear salad”?(pearpair是近音词)。你可能觉得我说的应该更像第二种,事实上,这就是一个好的语音识别系统要帮助输出的东西,即使这两句话听起来是如此相似。而让语音识别系统去选择第二个句子的方法就是使用一个语言模型,他能计算出这两句话各自的可能性。

举个例子,一个语音识别模型可能算出第一句话的概率是 P ( The apple and pair salad ) = 3.2 × 1 0 − 13 P( \text{The apple and pair salad}) = 3.2 \times 10^{-13} P(The apple and pair salad)=3.2×1013,而第二句话的概率是 P ( The apple and pear salad ) = 5.7 × 1 0 − 10 P\left(\text{The apple and pear salad} \right) = 5.7 \times 10^{-10} P(The apple and pear salad)=5.7×1010,比较这两个概率值,显然我说的话更像是第二种,因为第二句话的概率比第一句高出1000倍以上,这就是为什么语音识别系统能够在这两句话中作出选择。

所以语言模型所做的就是,它会告诉你某个特定的句子它出现的概率是多少,根据我所说的这个概率,假设你随机拿起一张报纸,打开任意邮件,或者任意网页或者听某人说下一句话,并且这个人是你的朋友,这个你即将从世界上的某个地方得到的句子会是某个特定句子的概率是多少,例如“the apple and pear salad”。它是两种系统的基本组成部分,一个刚才所说的语音识别系统,还有机器翻译系统,它要能正确输出最接近的句子。而语言模型做的最基本工作就是输入一个句子,准确地说是一个文本序列, y < 1 > y^{<1>} y<1> y < 2 > y^{<2>} y<2>一直到 y < T y > y^{<T_{y}>} y<Ty>。对于语言模型来说,用 y y y来表示这些序列比用 x x x来表示要更好,然后语言模型会估计某个句子序列中各个单词出现的可能性。

那么如何建立一个语言模型呢?为了使用RNN建立出这样的模型,你首先需要一个训练集,包含一个很大的英文文本语料库(corpus)或者其它的语言,你想用于构建模型的语言的语料库。语料库是自然语言处理的一个专有名词,意思就是很长的或者说数量众多的英文句子组成的文本。

在这里插入图片描述

假如说,你在训练集中得到这么一句话,“Cats average 15 hours of sleep a day.”(猫一天睡15小时),你要做的第一件事就是将这个句子标记化,意思就是像之前视频中一样,建立一个字典,然后将每个单词都转换成对应的one-hot向量,也就是字典中的索引。可能还有一件事就是你要定义句子的结尾,一般的做法就是增加一个额外的标记,叫做EOS(上图编号1所示),它表示句子的结尾,这样能够帮助你搞清楚一个句子什么时候结束,我们之后会详细讨论这个。EOS标记可以被附加到训练集中每一个句子的结尾,如果你想要你的模型能够准确识别句子结尾的话。在本周的练习中我们不需要使用这个EOS标记,不过在某些应用中你可能会用到它,不过稍后就能见到它的用处。于是在本例中我们,如果你加了EOS标记,这句话就会有9个输入,有 y < 1 > y^{<1>} y<1> y < 2 > y^{<2>} y<2>一直到 y < 9 > y^{<9>} y<9>。在标记化的过程中,你可以自行决定要不要把标点符号看成标记,在本例中,我们忽略了标点符号,所以我们只把day看成标志,不包括后面的句号,如果你想把句号或者其他符号也当作标志,那么你可以将句号也加入你的字典中。

现在还有一个问题如果你的训练集中有一些词并不在你的字典里,比如说你的字典有10,000个词,10,000个最常用的英语单词。现在这个句,“The Egyptian Mau is a bread of cat.”其中有一个词Mau,它可能并不是预先的那10,000个最常用的单词,在这种情况下,你可以把Mau替换成一个叫做UNK的代表未知词的标志,我们只针对UNK建立概率模型,而不是针对这个具体的词Mau

完成标识化的过程后,这意味着输入的句子都映射到了各个标志上,或者说字典中的各个词上。下一步我们要构建一个RNN来构建这些序列的概率模型。在下一张幻灯片中会看到的一件事就是最后你会将 x < t > x^{<t>} x<t>设为 y < t − 1 > y^{<t-1>} y<t1>

在这里插入图片描述

现在我们来建立RNN模型,我们继续使用“Cats average 15 hours of sleep a day.”这个句子来作为我们的运行样例,我将会画出一个RNN结构。在第0个时间步,你要计算激活项 a < 1 > a^{<1>} a<1>,它是以 x < 1 > x^{<1 >} x<1>作为输入的函数,而 x < 1 > x^{<1>} x<1>会被设为全为0的集合,也就是0向量。在之前的 a < 0 > a^{<0>} a<0>按照惯例也设为0向量,于是 a < 1 > a^{<1>} a<1>要做的就是它会通过softmax进行一些预测来计算出第一个词可能会是什么,其结果就是 y ^ < 1 > \hat y^{<1>} y^<1>(上图编号1所示),这一步其实就是通过一个softmax层来预测字典中的任意单词会是第一个词的概率,比如说第一个词是 a a a的概率有多少,第一个词是Aaron的概率有多少,第一个词是cats的概率又有多少,就这样一直到Zulu是第一个词的概率是多少,还有第一个词是UNK(未知词)的概率有多少,还有第一个词是句子结尾标志的概率有多少,表示不必阅读。所以 y ^ < 1 > \hat y^{<1>} y^<1>的输出是softmax的计算结果,它只是预测第一个词的概率,而不去管结果是什么。在我们的例子中,最终会得到单词Cats。所以softmax层输出10,000种结果,因为你的字典中有10,000个词,或者会有10,002个结果,因为你可能加上了未知词,还有句子结尾这两个额外的标志。

然后RNN进入下个时间步,在下一时间步中,仍然使用激活项 a < 1 > a^{<1>} a<1>,在这步要做的是计算出第二个词会是什么。现在我们依然传给它正确的第一个词,我们会告诉它第一个词就是Cats,也就是 y ^ < 1 > \hat y^{<1>} y^<1>,告诉它第一个词就是Cats,这就是为什么 y < 1 > = x < 2 > y^{<1>} = x^{<2>} y<1>=x<2>(上图编号2所示)。然后在第二个时间步中,输出结果同样经过softmax层进行预测,RNN的职责就是预测这些词的概率(上图编号3所示),而不会去管结果是什么,可能是b或者arron,可能是Cats或者Zulu或者UNK(未知词)或者EOS或者其他词,它只会考虑之前得到的词。所以在这种情况下,我猜正确答案会是average,因为句子确实就是Cats average开头的。

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

一直到最后,没猜错的话,你会停在第9个时间步,然后把 x < 9 > x^{<9>} x<9>也就是 y < 8 > y^{<8>} y<8>传给它(上图编号5所示),也就是单词day,这里是 a < 9 > a^{<9>} a<9>,它会输出 y < 9 > y^{<9>} y<9>,最后的得到结果会是EOS标志,在这一步中,通过前面这些得到的单词,不管它们是什么,我们希望能预测出EOS句子结尾标志的概率会很高(上图编号6所示)。

所以RNN中的每一步都会考虑前面得到的单词,比如给它前3个单词(上图编号7所示),让它给出下个词的分布,这就是RNN如何学习从左往右地每次预测一个词。

接下来为了训练这个网络,我们要定义代价函数。于是,在某个时间步 t t t,如果真正的词是 y < t > y^{<t>} y<t>,而神经网络的softmax层预测结果值是 y < t > y^{<t>} y<t>,那么这(上图编号8所示)就是softmax损失函数, L ( y ^ < t > , y < t > > ) = − ∑ i y i < t > log ⁡ y ^ i < t > L\left( \hat y^{<t>},y^{<t>}>\right) = - \sum_{i}^{}{y_{i}^{<t>}\log\hat y_{i}^{<t>}} L(y^<t>,y<t>>)=iyi<t>logy^i<t>。而总体损失函数(上图编号9所示) L = ∑ t L < t > ( y ^ < t > , y < t > ) L = \sum_{t}^{}{L^{< t >}\left( \hat y^{<t>},y^{<t>} \right)} L=tL<t>(y^<t>,y<t>),也就是把所有单个预测的损失函数都相加起来。

在这里插入图片描述

如果你用很大的训练集来训练这个RNN,你就可以通过开头一系列单词像是Cars average 15或者Cars average 15 hours of来预测之后单词的概率。现在有一个新句子,它是 y < 1 > y^{<1>} y<1> y < 2 > y^{<2>} y<2> y < 3 > y^{<3>} y<3>,为了简单起见,它只包含3个词(如上图所示),现在要计算出整个句子中各个单词的概率,方法就是第一个softmax层会告诉你 y < 1 > y^{<1>} y<1>的概率(上图编号1所示),这也是第一个输出,然后第二个softmax层会告诉你在考虑 y < 1 > y^{<1>} y<1>的情况下 y < 2 > y^{<2>} y<2>的概率(上图编号2所示),然后第三个softmax层告诉你在考虑 y < 1 > y^{<1>} y<1> y < 2 > y^{<2>} y<2>的情况下 y < 3 > y^{<3>} y<3>的概率(上图编号3所示),把这三个概率相乘,最后得到这个含3个词的整个句子的概率。

这就是用RNN训练一个语言模型的基础结构,可能我说的这些东西听起来有些抽象,不过别担心,你可以在编程练习中亲自实现这些东西。下一节课用语言模型做的一件最有趣的事就是从模型中进行采样。

1.7 对新序列采样(Sampling novel sequences)

在你训练一个序列模型之后,要想了解到这个模型学到了什么,一种非正式的方法就是进行一次新序列采样,来看看到底应该怎么做。

记住一个序列模型模拟了任意特定单词序列的概率,我们要做的就是对这些概率分布进行采样来生成一个新的单词序列。下图编号1所示的网络已经被上方所展示的结构训练训练过了,而为了进行采样(下图编号2所示的网络),你要做一些截然不同的事情。

在这里插入图片描述

第一步要做的就是对你想要模型生成的第一个词进行采样,于是你输入 x < 1 > = 0 x^{<1>} =0 x<1>=0 a < 0 > = 0 a^{<0>} =0 a<0>=0,现在你的第一个时间步得到的是所有可能的输出是经过softmax层后得到的概率,然后根据这个softmax的分布进行随机采样。Softmax分布给你的信息就是第一个词a的概率是多少,第一个词是aaron的概率是多少,第一个词是zulu的概率是多少,还有第一个词是UNK(未知标识)的概率是多少,这个标识可能代表句子的结尾,然后对这个向量使用例如numpy命令,np.random.choice(上图编号3所示),来根据向量中这些概率的分布进行采样,这样就能对第一个词进行采样了。

然后继续下一个时间步,记住第二个时间步需要 y ^ < 1 > \hat y^{<1>} y^<1>作为输入,而现在要做的是把刚刚采样得到的 y ^ < 1 > \hat y^{<1>} y^<1>放到 a < 2 > a^{<2>} a<2>(上图编号4所示),作为下一个时间步的输入,所以不管你在第一个时间步得到的是什么词,都要把它传递到下一个位置作为输入,然后softmax层就会预测 y ^ < 2 > \hat y^{<2>} y^<2>是什么。举个例子,假如说对第一个词进行抽样后,得到的是TheThe作为第一个词的情况很常见,然后把The当成 x < 2 > x^{<2>} x<2>,现在 x < 2 > x^{<2>} x<2>就是 y ^ < 1 > \hat y^{<1>} y^<1>,现在你要计算出在第一词是The的情况下,第二个词应该是什么(上图编号5所示),然后得到的结果就是 y ^ < 2 > \hat y^{<2>} y^<2>,然后再次用这个采样函数来对 y ^ < 2 > \hat y^{<2>} y^<2>进行采样。

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

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

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

在这里插入图片描述

根据你实际的应用,你还可以构建一个基于字符的RNN结构,在这种情况下,你的字典仅包含从az的字母,可能还会有空格符,如果你需要的话,还可以有数字0到9,如果你想区分字母大小写,你可以再加上大写的字母,你还可以实际地看一看训练集中可能会出现的字符,然后用这些字符组成你的字典(上图编号2所示)。

如果你建立一个基于字符的语言模型,比起基于词汇的语言模型,你的序列 y ^ < 1 > \hat y^{<1>} y^<1> y ^ < 2 > \hat y^{<2>} y^<2> y ^ < 3 > \hat y^{<3>} y^<3>在你的训练数据中将会是单独的字符,而不是单独的词汇。所以对于前面的例子来说,那个句子(上图编号3所示),“Cats average 15 hours of sleep a day.”,在该例中C就是 y ^ < 1 > \hat y^{<1>} y^<1>a就是 y ^ < 2 > \hat y^{<2>} y^<2>t就是 y ^ < 3 > \hat y^{<3>} y^<3>,空格符就是 y ^ < 4 > \hat y^{<4>} y^<4>等等。

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

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

在这里插入图片描述

这里有一些样本,它们是从一个语言模型中采样得到的,准确来说是基于字符的语言模型,你可以在编程练习中自己实现这样的模型。如果模型是用新闻文章训练的,它就会生成左边这样的文本,这有点像一篇不太合乎语法的新闻文本,不过听起来,这句“Concussion epidemic”,to be examined,确实有点像新闻报道。用莎士比亚的文章训练后生成了右边这篇东西,听起来很像是莎士比亚写的东西:

The mortal moon hath her eclipse in love.

And subject of this thou art another this fold.

When besser be my love to me see sabl’s.

For whose are ruse of mine eyes heaves.

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

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

你已经了解了RNN时如何工作的了,并且知道如何应用到具体问题上,比如命名实体识别,比如语言模型,你也看到了怎么把反向传播用于RNN。其实,基本的RNN算法还有一个很大的问题,就是梯度消失的问题。这节课我们会讨论,在下几节课我们会讨论一些方法用来解决这个问题。

在这里插入图片描述

你已经知道了RNN的样子,现在我们举个语言模型的例子,假如看到这个句子(上图编号1所示),“The cat, which already ate ……, was full.”,前后应该保持一致,因为cat是单数,所以应该用was。“The cats, which ate ……, were full.”(上图编号2所示),cats是复数,所以用were。这个例子中的句子有长期的依赖,最前面的单词对句子后面的单词有影响。但是我们目前见到的基本的RNN模型(上图编号3所示的网络模型),不擅长捕获这种长期依赖效应,解释一下为什么。

你应该还记得之前讨论的训练很深的网络,我们讨论了梯度消失的问题。比如说一个很深很深的网络(上图编号4所示),100层,甚至更深,对这个网络从左到右做前向传播然后再反向传播。我们知道如果这是个很深的神经网络,从输出 y ^ \hat y y^得到的梯度很难传播回去,很难影响靠前层的权重,很难影响前面层(编号5所示的层)的计算。

对于有同样问题的RNN,首先从左到右前向传播,然后反向传播。但是反向传播会很困难,因为同样的梯度消失的问题,后面层的输出误差(上图编号6所示)很难影响前面层(上图编号7所示的层)的计算。这就意味着,实际上很难让一个神经网络能够意识到它要记住看到的是单数名词还是复数名词,然后在序列后面生成依赖单复数形式的was或者were。而且在英语里面,这中间的内容(上图编号8所示)可以任意长,对吧?所以你需要长时间记住单词是单数还是复数,这样后面的句子才能用到这些信息。也正是这个原因,所以基本的RNN模型会有很多局部影响,意味着这个输出 y ^ < 3 > \hat y^{<3>}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值