吴恩达《深度学习》-第五门课 序列模型(Sequence Models)-第三周 序列模型和注意力机制(Sequence models & Attention mechanism)-课程笔记
第三周 序列模型和注意力机制(Sequence models & Attention mechanism)
3.1 序列结构的各种序列(Various sequence to sequence architectures)
首先,我们先建立一个网络,这个网络叫做编码网络(encoder network)(上图编号 1 所示),它是一个 RNN 的结构, RNN 的单元可以是 GRU 也可以是 LSTM。每次只向该网络中输入一个法语单词,将输入序列接收完毕后,这个 RNN 网络会输出一个向量来代表这个输入序列。之后你可以建立一个解码网络,我把它画出来(上图编号 2 所示),它以编码网络的输出作为输入,编码网络是左边的黑色部分(上图编号 1 所示),之后它可以被训练为每次输出一个翻译后的单词,一直到它输出序列的结尾或者句子结尾标记,这个解码网络的工作就结束了。和往常一样我们把每次生成的标记都传递到下一个单元中来进行预测,就像之前用语言模型合成文本时一样。
在之前的卷积网络课程中,你已经知道了如何将图片输入到卷积神经网络中, 比如一个预训练的 AlexNet 结构(上图编号 2 方框所示),然后让其学习图片的编码,或者 学习图片的一系列特征。现在幻灯片所展示的就是 AlexNet 结构,我们去掉最后的 softmax 单元(上图编号 3 所示),这个预训练的 AlexNet 结构会给你一个 4096 维的特征向量,向 量表示的就是这只猫的图片,所以这个预训练网络可以是图像的编码网络。现在你得到了一个 4096 维的向量来表示这张图片,接着你可以把这个向量输入到 RNN 中(上图编号 4 方框 所示),RNN 要做的就是生成图像的描述,每次生成一个单词,这和我们在之前将法语译为 英语的机器翻译中看到的结构很像,现在你输入一个描述输入的特征向量,然后让网络生成 一个输出序列,或者说一个一个地输出单词序列。事实证明在图像描述领域,这种方法相当有效,特别是当你想生成的描述不是特别长时。
3.2 选择最可能的句子(Picking the most likely sentence)
在语言模型中上方是一个我们在第一周所建立的模型,这个模型可以让你能够估计句子的可能性,这就是语言模型所做的事情。 你也可以将它用于生成一个新的句子,如果你在图上的该处(下图编号 1 所示),有\(𝑥^{<1>}\)和 \(𝑥^{<2>}\),那么在该例中\(𝑥^{<2>} = 𝑦^{<1>}\),但是\(、𝑥^{<1>}、𝑥^{<2>}\)等在这里并不重要。为了让图片看起来 更简洁,我把它们先抹去,可以理解为\(𝑥^{<1>}\)是一个全为 0 的向量,然后\(、𝑥{<2>}、𝑥^{<3>}\)等都等于之前所生成的输出,这就是所说的语言模型。
机器翻译模型是下面这样的,我这里用两种不同的颜色来表示,即绿色和紫色,用绿色(上图编号 2 所示)表示 encoder 网络,用紫色(上图编号 3 所示)表示 decoder 网络。 你会发现 decoder 网络看起来和刚才所画的语言模型几乎一模一样,机器翻译模型其实和语言模型非常相似,不同在于语言模型总是以零向量(上图编号 4 所示)开始,而 encoder 网络会计算出一系列向量(上图编号 2 所示)来表示输入的句子。有了这个输入句子,decoder 网络就可以以这个句子开始,而不是以零向量开始,所以我把它叫做条件语言模型 (conditional language model)。相比语言模型,输出任意句子的概率,翻译模型会输出句子的英文翻译(上图编号 5 所示),这取决于输入的法语句子(上图编号 6 所示)。换句话说,你将估计一个英文翻译的概率,比如估计这句英语翻译的概率,"Jane is visiting Africa in September.",这句翻译是取决于法语句子,"Jane visite I'Afrique en septembre.",这就是英语句子相对于输入的法语句子的可能性,所以它是一个条件语言模型。
第一串(上图编号 1 所示)翻译明显比第二个(上图编号 2 所示)好,所以我们希望机 器翻译模型会说第一个句子的𝑃(𝑦|𝑥)比第二个句子要高,第一个句子对于法语原文来说更好 更简洁,虽然第二个也不错,但是有些啰嗦,里面有很多不重要的词。但如果贪心算法挑选 出了"Jane is"作为前两个词,因为在英语中 going 更加常见,于是对于法语句子来说"Jane is going"相比"Jane is visiting"会有更高的概率作为法语的翻译,所以很有可能如果你仅仅根据 前两个词来估计第三个词的可能性,得到的就是 going,最终你会得到一个欠佳的句子,在 𝑃(𝑦|𝑥)模型中这不是一个最好的选择。
当你想得到单词序列 \(、𝑦^{<1>}、𝑦^{<2>}\)一直到最后一个词总体的概率时,一次仅仅挑选一个词并不是最佳的选择。当 然,在英语中各种词汇的组合数量还有很多很多,如果你的字典中有 10,000 个单词,并且 你的翻译可能有 10 个词那么长,那么可能的组合就有 10,000 的 10 次方这么多,这仅仅是 10 个单词的句子,从这样大一个字典中来挑选单词,所以可能的句子数量非常巨大,不可 能去计算每一种组合的可能性。
这时最常用的办法就是用一个近似的搜索算法,这个近 似的搜索算法做的就是它会尽力地,尽管不一定总会成功,但它将挑选出句子𝑦使得条件概 率最大化,尽管它不能保证找到的𝑦值一定可以使概率最大化,但这已经足够了。
3.3 集束搜索(Beam Search)
贪婪算法只会挑出最可能的那一个单词,然后继续。而集束搜索则会考虑多个选择,集束搜索算法会有一个参数 B,叫做集束宽(beam width)。在这个例子中我把这个集束宽设成 3,这样就意味着集束搜索不会只考虑一个可能结果,而是一次会考虑 3 个,比如对第一 个单词有不同选择的可能性,最后找到 in、jane、september,是英语输出的第一个单词的 最可能的三个选项,然后集束搜索算法会把结果存到计算机内存里以便后面尝试用这三个词。
集束搜索算法的第二步,已经选出了 in、jane、september 作为第一个单词 三个最可能的选择,集束算法接下来会针对每个第一个单词考虑第二个单词是什么,单词 in 后面的第二个单词可能是 a 或者是 aaron,我就是从词汇表里把这些词列了出来,或者是列 表里某个位置,september,可能是列表里的 visit,一直到字母 z,最后一个单词是 zulu(上图编号 1 所示)。
为了评估第二个词的概率值,我们用这个神经网络的部分,绿色是编码部分(上图编号 2 所示),而对于解码部分,当决定单词 in 后面是什么,别忘了解码器的第一个输出\(𝑦^{<1>}\), 我把\(𝑦^{<1>}\)设为单词 in(上图编号 3 所示),然后把它喂回来,这里就是单词 in(上图编号 4 所示),因为它的目的是努力找出第一个单词是 in 的情况下,第二个单词是什么。这个输 出就是\(𝑦^{<2>}\)(上图编号 5 所示),有了这个连接(上图编号 6 所示),就是这里的第一个单 词 in(上图编号 4 所示)作为输入,这样这个网络就可以用来评估第二个单词的概率了,在 给定法语句子和翻译结果的第一个单词 in 的情况下。
注意,在第二步里我们更关心的是要找到最可能的第一个和第二个单词对,所以不仅仅 是第二个单词有最大的概率,而是第一个、第二个单词对有最大的概率(上图编号 7 所示)。 按照条件概率的准则,这个可以表示成第一个单词的概率(上图编号 8 所示)乘以第二个单 词的概率(上图编号 9 所示),这个可以从这个网络部分里得到(上图编号 10 所示),对 于已经选择的 in、jane、september 这三个单词,你可以先保存这个概率值(上图编号 8 所 示),然后再乘以第二个概率值(上图编号 9 所示)就得到了第一个和第二个单词对的概率 (上图编号 7 所示)。
针对第二个单词所有 10,000 个不同的选择,最后对于单词 september 也一样,从单词 a 到单词 zulu,用这个网络部分,我把它画在这里。来看看如果第一个单词是 september, 第二个单词最可能是什么。所以对于集束搜索的第二步,由于我们一直用的集束宽为 3,并 且词汇表里有 10,000 个单词,那么最终我们会有 3 乘以 10,000 也就是 30,000 个可能的结 果,因为这里(上图编号 1 所示)是 10,000,这里(上图编号 2 所示)是 10,000,这里(上图编号 3 所示)是 10,000,就是集束宽乘以词汇表大小,你要做的就是评估这 30,000 个选 择。按照第一个词和第二个词的概率,然后选出前三个,这样又减少了这 30,000 个可能性, 又变成了 3 个,减少到集束宽的大小。假如这 30,000 个选择里最可能的是“in September”(上 图编号 4 所示)和“jane is”(上图编号 5 所示),以及“jane visits”(上图编号 6 所示),画 的有点乱,但这就是这 30,000 个选择里最可能的三个结果,集束搜索算法会保存这些结果, 然后用于下一次集束搜索。
注意一件事情,如果集束搜索找到了第一个和第二个单词对最可能的三个选择是“in September”或者“jane is”或者“jane visits”,这就意味着我们去掉了 september 作为英语翻译 结果的第一个单词的选择,所以我们的第一个单词现在减少到了两个可能结果,但是我们的 集束宽是 3,所以还是有\(,𝑦^{<1>},𝑦^{<2>}\)对的三个选择。
然后继续,接着进行集束搜索的第三、四...步,再加一个单词继续,最终这个过程的输出一次 增加一个单词,集束搜索最终会找到“Jane visits africa in september”这个句子,终止在句尾 符号,用这种符号的系统非常常见,它们会发现这是最有可能输出的一 个英语句子。注意如果集束宽等于 1,只考虑 1 种 可能结果,这实际上就变成了贪婪搜索算法,上个视频里我们已经讨论过了。但是如果同时考虑多个,可能的结果比如 3 个,10 个或者其他的个数,集束搜索通常会找到比贪婪搜索 更好的输出结果。
3.4 改进集束搜索(Refinements to Beam Search)
前面讲到束搜索就是最大化这个概率,这个乘积就是\(𝑃(𝑦^{<1>} … 𝑦^{<𝑇𝑦>}|𝑋)\),可以表示 成:\(𝑃(𝑦^{<1>}|𝑋) 𝑃(𝑦^{<2>}|𝑋, 𝑦^{<1>}) 𝑃(𝑦^{<3>}|𝑋, 𝑦^{<1>}, 𝑦^{<2>})…𝑃(𝑦^{<𝑇𝑦>}|𝑋, 𝑦^{<1>}, 𝑦^{<2>} … 𝑦^{<𝑇𝑦−1>})\)
这就是我们之前见到的乘积概率(the product probabilities)。如果计算这些,其实这些概率值都是小于 1 的,通常远小于 1。很多小于 1 的数乘起来,会得到很小很小的数字,会造成数值下溢(numerical underflow)。数值下溢 就是数值太小了,导致电脑的浮点表示不能精确地储存,因此在实践中,我们不会最大化这 个乘积,而是取𝑙𝑜𝑔值。如果在这加上一个𝑙𝑜𝑔,最大化这个𝑙𝑜𝑔求和的概率值,在选择最可 能的句子𝑦时,你会得到同样的结果。所以通过取𝑙𝑜𝑔,我们会得到一个数值上更稳定的算法, 不容易出现四舍五入的误差,数值的舍入误差(rounding errors)或者说数值下溢(numerical underflow)。因为𝑙𝑜𝑔函数它是严格单调递增的函数,最大化𝑃(𝑦),因为对数函数,这就是 𝑙𝑜𝑔函数,是严格单调递增的函数,所以最大化𝑙𝑜𝑔𝑃(𝑦|𝑥)和最大化𝑃(𝑦|𝑥)结果一样。如果一 个𝑦值能够使前者最大,就肯定能使后者也取最大。所以实际工作中,我们总是记录概率的 对数和(the sum of logs of the probabilities),而不是概率的乘积(the production of probabilities)。
对于目标函数(this objective function),还可以做一些改变,可以使得机器翻译表现的更好。如果参照原来的目标函数(this original objective),如果有一个很长的句子,那么这 个句子的概率会很低,因为乘了很多项小于 1 的数字来估计句子的概率。所以这个目标函数有一个缺点,它可能 不自然地倾向于简短的翻译结果,它更偏向短的输出。
概率的𝑙𝑜𝑔值通常小于等于 1,实际上在𝑙𝑜𝑔的这个范围内,所以加起来的项越多,得到的结果越负,所以对这个算法另一个改变也可以使它表现的更好,也就是我们不再最大化这个目标 函数了,我们可以把它归一化,通过除以翻译结果的单词数量(normalize this by the number of words in your translation)。这样就是取每个单词的概率对数值的平均了,这样很明显地 减少了对输出长的结果的惩罚(this significantly reduces the penalty for outputting longer translations.)。
在实践中,有个探索性的方法,相比于直接除\(𝑇_𝑦\),也就是输出句子的单词总数,我们有 时会用一个更柔和的方法(a softer approach),在\(𝑇_𝑦\)上加上指数𝑎,𝑎可以等于 0.7。如果𝑎 等于 1,就相当于完全用长度来归一化,如果𝑎等于 0,\(𝑇_𝑦\)的 0 次幂就是 1,就相当于完全没 有归一化,这就是在完全归一化和没有归一化之间。𝑎就是算法另一个超参数(hyper parameter),需要调整大小来得到最好的结果。不得不承认,这样用𝑎实际上是试探性的, 它并没有理论验证。但是大家都发现效果很好,大家都发现实践中效果不错,所以很多人都 会这么做。你可以尝试不同的𝑎值,看看哪一个能够得到最好的结果。
最后还有一些实现的细节,如何选择束宽 B。B 越大,你考虑的选择越多,你找到的句子可能越好,但是 B 越大,你的算法的计算代价越大,因为你要把很多的可能选择保存起来。
如果束宽很大,你会考虑很多的可能,你会得到一个更好的结果,因为你要考虑 很多的选择,但是算法会运行的慢一些,内存占用也会增大,计算起来会慢一点。而如果你 用小的束宽,结果会没那么好,因为你在算法运行中,保存的选择更少,但是你的算法运行 的更快,内存占用也小。也经常看到大家用束宽为 1000 或者 3000,这也是取 决于特定的应用和特定的领域。
在你实现你的应用时,尝试不同的束宽的值,当 B 很大的时 候,性能提高会越来越少。对于很多应用来说,从束宽 1,也就是贪心算法,到束宽为 3、 到 10,你会看到一个很大的改善。但是当束宽从 1000 增加到 3000 时,效果就没那么明显 了。
事实上 在束搜索上做误差分析是我发现的最有用的工具之一。有时你想知道是否应该增大束宽,我 的束宽是否足够好,你可以计算一些简单的东西来指导你需要做什么,来改进你的搜索算法。
3.5 集束搜索的误差分析(Error analysis in beam search)
RNN (循环神经网络)实际上是个编码器和解码器(the encoder and the decoder),它会 计算𝑃(𝑦|𝑥)。所以举个例子,对于这个句子:Jane visits Africa in September,你将 Jane visits Africa 填入这里,同样,我现在忽略了字母的大小写,后面也是一样, 然后这个就会计算。𝑃(𝑦|𝑥)结果表明,你此时能做的最有效的事就是用这个模型来计算 𝑃(\(𝑦^∗\) |𝑥),同时也用你的 RNN 模型来计算𝑃(\(\hat{𝑦}\) |𝑥),然后比较一下这两个值哪个更大。有可能 是左边大于右边,也有可能是𝑃(\(𝑦^∗\) )小于𝑃(\(\hat{𝑦}\)),其实应该是小于或等于,对吧。取决于实际 是哪种情况,你就能够更清楚地将这个特定的错误归咎于 RNN 或是束搜索算法,或说是哪个负有更大的责任。
第一种情况,RNN 模型的输出结果𝑃(\(𝑦^∗\) |𝑥) 大于𝑃(\(\hat{𝑦}\) |𝑥):
这意味着束搜索算法选择了\(\hat{y}\) ,你得到 $ \hat{y}$的方式是,你用一个 RNN 模型来计算𝑃(𝑦|𝑥),然后束搜索算法做的就是尝试寻找使𝑃(𝑦|𝑥)最大的𝑦,不过在这种情况下,相比于 \(\hat{y}\),\(𝑦^∗\)的值更𝑃(𝑦|𝑥)大,因此 你能够得出束搜索算法实际上不能够给你一个能使𝑃(𝑦|𝑥)最大化的𝑦值。
因为束搜索算法的 任务就是寻找一个𝑦的值来使这项更大,但是它却选择了\(\hat{𝑦}\),而\(𝑦^∗\)实际上能得到更大的值。因此这种情况下你能够得出是束搜索算法出错了。
第二种情况是𝑃(\(𝑦^∗\) |𝑥)小于或等于𝑃(\(\hat{y}\) |𝑥):
在我们的例子中,\(𝑦^∗\) 是比 \(\hat{𝑦}\)更好的 翻译结果,不过根据 RNN 模型的结果,𝑃(\(𝑦^∗\) ) 是小于𝑃(\(\hat{𝑦}\))的,也就是说,相比于\(\hat{𝑦}\),\(𝑦^∗\)成为 输出的可能更小。因此在这种情况下,看来是 RNN 模型出了问题。同时可能值得在 RNN 模 型上花更多时间。这里我少讲了一些有关长度归一化(length normalizations)的细节。这里 我略过了有关长度归一化的细节,如果你用了某种长度归一化,那么你要做的就不是比较这 两种可能性大小,而是比较长度归一化后的最优化目标函数值。不过现在先忽略这种复杂的 情况。第二种情况表明虽然\(𝑦^∗\)是一个更好的翻译结果,RNN 模型却赋予它更低的可能性,是 RNN 模型出现了问题。
误差分析过程看起来就像下面这样:
先遍历开发集,然后在其中找出算法产生的 错误,这个例子中,假如说𝑃(\(𝑦^∗\) |𝑥)的值为 \(2 x 10^{-10}\),而𝑃(\(\hat{𝑦}\) |𝑥)的值为 \(1 x10^{-10}\),根据上页幻灯片中的逻辑关系,这种情况下我们得知束搜索算法实际上选择了比\(𝑦^∗\)可能性更低的\(\hat{𝑦}\), 因此我会说束搜索算法出错了。我将它缩写为 B。接着你继续遍历第二个错误,再来看这些 可能性。也许对于第二个例子来说,你认为是 RNN 模型出现了问题,我会用缩写 R 来代表 RNN。再接着你遍历了更多的例子,有时是束搜索算法出现了问题,有时是模型出现了问题, 等等。通过这个过程,你就能够执行误差分析,得出束搜索算法和 RNN 模型出错的比例是 多少。有了这样的误差分析过程,你就可以对开发集中每一个错误例子,即算法输出了比人 工翻译更差的结果的情况,尝试确定这些错误,是搜索算法出了问题,还是生成目标函数(束 搜索算法使之最大化)的 RNN 模型出了问题。并且只有当你发现是束搜索算法造成了大部分错误时,才值得 花费努力增大集束宽度。相反地,如果你发现是 RNN 模型出了更多错,那么你可以进行更 深层次的分析,来决定是需要增加正则化还是获取更多的训练数据,抑或是尝试一个不同的网络结构,或是其他方案。
3.6 Bleu 得分(选修)(Bleu Score (optional))
机器翻译(machine translation)的一大难题是一个法语句子可以有多种英文翻译而且 都同样好,所以当有多个同样好的答案时,怎样评估一个机器翻译系统呢?
假如给你一个法语句子:Le chat est sur le tapis,然后给你一个这个句子的人工翻译作 参考:The cat is on the mat。不过有多种相当不错的翻译。所以一个不同的人,也许会将其 翻译为:There is a cat on the mat,同时,实际上这两个都是很好的,都准确地翻译了这个 法语句子。BLEU 得分做的就是,给定一个机器生成的翻译,它能够自动地计算一个分数来 衡量机器翻译的好坏。直觉告诉我们,只要这个机器生成的翻译与任何一个人工翻译的结果 足够接近,那么它就会得到一个高的 BLEU 分数。顺便提一下 BLEU 代表 bilingual evaluation understudy (双语评估替补)。BLEU 的初衷是相对于请评估员(ask human evaluators),人工评估机器翻译系统(the machine translation system),BLEU 得分就相当 于一个侯补者,它可以代替人类来评估机器翻译的每一个输出结果。
我们来看一个极端的例子。我们假设机器翻译系统缩写为 MT。机器翻译 (MT)的 输出是:the the the the the the the。这显然是一个十分糟糕的翻译。衡量机器翻译输出质量 的方法之一是观察输出结果的每一个词看其是否出现在参考中,这被称做是机器翻译的精确度(a precision of the machine translation output)。
单词 the 在两个参考中都出现了, 所以看上去每个词都是很合理的。因此这个输出的精确度就是 7/7,看起来是一个极好的精 确度。这就是为什么把出现在参考中的词在 MT 输出的所有词中所占的比例作为精确度评估 标准并不是很有用的原因。
因 此取而代之的是我们要用的这个改良后的精确度评估方法,我们把每一个单词的记分上限定 为它在参考句子中出现的最多次数。在参考 1 中,单词 the 出现了两次,在参考 2 中,单词 the 只出现了一次。而 2 比 1 大,所以我们会说,单词 the 的得分上限为 2。有了这个改良 后的精确度,我们就说,这个输出句子的得分为 2/7,因为在 7 个词中,我们最多只能给它 2 分。所以这里分母就是 7 个词中单词 the 总共出现的次数,而分子就是单词 the 出现的计 数。我们在达到上限时截断计数,这就是改良后的精确度评估(the modified precision measure)。
在 BLEU 得分中,你不想仅仅考虑单个的单 词,你也许也想考虑成对的单词,我们定义一下二元词组(bigrams)的 BLEU 得分。bigram 的意思就是相邻的两个单词。
不过现在 我们假定机器翻译输出了稍微好一点的翻译:The cat the cat on the mat,仍然不是一个好的 翻译,不过也许比上一个好一些。这里,可能的二元词组有 the cat ,忽略大小写,接着是 cat the, 这是另一个二元词组,然后又是 the cat。不过我已经有了,所以我们跳过它,然 后下一个是 cat on,然后是 on the,再然后是 the mat。所以这些就是机器翻译中的二元词 组。好,我们来数一数每个二元词组出现了多少次。the cat 出现了两次 ,cat the 出现了一 次,剩下的都只出现了一次。
我们来定义一下截取计数(the clipped count)。也就 是 Count_clip。为了定义它,我们以这列的值为基础,但是给算法设置得分上限,上限值为 二元词组出现在参考 1 或 2 中的最大次数。the cat 在两个参考中最多出现一次,所以我将 截取它的计数为 1。cat the 它并没有出现在参考 1 和参考 2 中,所以我将它截取为 0。cat on ,好,它出现了一次,我们就记 1 分。on the 出现一次就记 1 分,the mat 出现了一次, 所以这些就是截取完的计数(the clipped counts)。我们把所有的这些计数都截取了一遍, 实际上就是将它们降低使之不大于二元词组出现在参考中的次数。最后,修改后的二元词组 的精确度就是 count_clip 之和。因此那就是 4 除以二元词组的总个数,也就是 6。因此是 4/6 也就是 2/3 为二元词组改良后的精确度。
我们将改良后的一元词组精 确度定义为𝑃1,𝑃代表的是精确度。这里的下标 1 的意思是一元词组。不过它定义为一元词 组之和,也就是对机器翻译结果中所有单词求和,MT 输出就是\(\hat{𝑦}\),Countclip (unigram)。除 以机器翻译输出中的一元词组出现次数之和。因此这个就是最终结果应该是两页幻灯片前得 到的 2/7。
这里的 1 指代的是一元词组,意思是我们在考虑单独的词,你也可以定义𝑃𝑛为𝑛 元词组精确度,用 n-gram 替代掉一元词组。所以这就是机器翻译输出中的𝑛元词组的 countclip 之和除以𝑛元词组的出现次数之和。因此这些精确度或说是这些改良后的精确度得 分评估的是一元词组或是二元词组。就是我们前页幻灯片中做的,或者是三元词组,也就是 由三个词组成的,甚至是𝑛取更大数值的𝑛元词组。
最后,我们将这些组合一下来构成最终的 BLEU 得分。\(𝑃_𝑛\)就是𝑛元词组这一项的 BLEU 得 分,也是计算出的𝑛元词组改良后的精确度,按照惯例,为了用一个值来表示你需要计算\(,,,𝑃_1, 𝑃_2, 𝑃_3,𝑃_4\)。然后将它们用这个公式组合在一起,就是取平均值。按照惯例 BLEU 得分被定 义为,\(𝑒𝑥𝑝( \frac{1}{4} \sum^4_{n=1} 𝑃_𝑛)\),对这个线性运算进行乘方运算,乘方是严格单调递增的运算。
我们实 际上会用额外的一个叫做 BP 的惩罚因子(the BP penalty)来调整这项。BP 的意思是“简短 惩罚”( brevity penalty)。
事 实表明,如果你输出了一个非常短的翻译,那么它会更容易得到一个高精确度。因为输出的 大部分词可能都出现在参考之中,不过我们并不想要特别短的翻译结果。因此简短惩罚(BP) 就是一个调整因子,它能够惩罚输出了太短翻译结果的翻译系统。BP 的公式如上图所示。 如果你的机器翻译系统实际上输出了比人工翻译结果更长的翻译,那么它就等于 1,其他情 况下就是像这样的公式,惩罚所有更短的翻译,细节部分你能够在论文中找到。
BLEU 得分对于机器翻译来说,具有革命性的原因是因为它有一个 相当不错的虽然不是完美的但是非常好的单一实数评估指标,因此它加快了整个机器翻译领 域的进程,实践中,很少人会从零 实现一个 BLEU 得分(implement a BLEU score from scratch),有很多开源的实现结果,你可 以下载下来然后直接用来评估你的系统。不过今天,BLEU 得分被用来评估许多生成文本的 系统(systems that generate text),比如说机器翻译系统(machine translation systems), 也有我之前简单提到的图像描述系统(image captioning systems)。也就是说你会用神经网 络来生成图像描述,然后使用 BLEU 得分来看一下,结果在多大程度上与参考描述或是多个 人工完成的参考描述内容相符。不过它并没有用于语 音识别(speech recognition)。因为在语音识别当中,通常只有一个答案,你可以用其他的 评估方法,来看一下你的语音识别结果,是否十分相近或是字字正确(pretty much, exactly word for word correct)。
3.7 注意力模型直观理解(Attention Model Intuition)
当你使用 RNN 读一个句子,于是另一个会输出一个句子。我们要对其做 一些改变,称为注意力模型(the Attention Model),并且这会使它工作得更好。注意力模 型或者说注意力这种思想(The attention algorithm, the attention idea)已经是深度学习中最重要的思想之一:
在上面这个编码解码结构中, 会看到它对于短句子效果非常好,于是它会有一个相对高的 Bleu 分(Bleu score),但是对 于长句子而言,比如说大于 30 或者 40 词的句子,它的表现就会变差。Bleu 评分看起来就 会像是这样,随着单词数量变化,短的句子会难以翻译,因为很难得到所有词。对于长的句子,效果也不好,因为在神经网络中,记忆非常长句子是非常困难的。
我们有一个很平常的句子:(法语)Jane visite l'Afrique en Septembre。假定我们使用 RNN,在这个情况中,我们将使用一个双向的 RNN(a bidirectional RNN),为了计算每个输入单词的的特征集(set of features),你必须要理解输 出\(\hat{𝑦}^{<1>}\)到\(\hat{𝑦}^{<3>}\)一直到\(\hat{𝑦}^{<5>}\)的双向 RNN。但是我们并不是只翻译一个单词,让我们先去掉上 面的𝑌,就用双向的 RNN。我们要对单词做的就是,对于句子里的每五个单词,计算一个句 子中单词的特征集,也有可能是周围的词,让我们试试,生成英文翻译。我们将使用另一个 RNN 生成英文翻译,这是我平时用的 RNN 记号。我不用𝐴来表示感知机(the activation), 这是为了避免和这里的感知机(the activations)混淆。我会用另一个不同的记号,我会用𝑆 来表示 RNN 的隐藏状态(the hidden state in this RNN),不用\(𝐴^{<1>}\),而是用\(𝑆^{<1>}\)。
当你尝试生成第一个词,即输出,那么我们应该看输入的法语句子的哪个部分? 似乎你应该先看第一个单词,或者它附近的词。但是你别看太远了,比如说看到句尾去了。
所以注意力模型就会计算注意力权重(a set of attention weights),我们将用\(𝑎^{<1,1>}\)来表示 当你生成第一个词时你应该放多少注意力在这个第一块信息处。然后我们算第二个,这个叫 注意力权重,\(𝑎^{<1,2>}\)它告诉我们当你尝试去计算第一个词 Jane 时,我们应该花多少注意力在 输入的第二个词上面。同理这里是\(𝑎^{<1,3>}\),接下去也同理。这些将会告诉我们,我们应该花 多少注意力在记号为𝐶的内容上。
对于 RNN 的第二步,我们将有一个新 的隐藏状态\(𝑆^{<2>}\),我们也会用一个新的注意力权值集(a new set of the attention weights),我 们将用\(𝑎^{<2,1>}\)来告诉我们什么时候生成第二个词, 那么 visits 就会是第二个标签了(the ground trip label)。我们应该花多少注意力在输入的第一个法语词上。然后同理\(𝑎^{<2,2>}\),接下 去也同理,我们应该花多少注意力在 visite 词上,我们应该花多少注意在词 l'Afique 上面。 当然我们第一个生成的词 Jane 也会输入到这里,于是我们就有了需要花注意力的上下文。
第二步,这也是个输入,然后会一起生成第二个词,这会让我们来到第三步\(𝑆^{<3>}\),这是输入, 我们再有上下文 C,它取决于在不同的时间集(time sets),上面的\(𝑎^{<3>}\)。这个告诉了我们我 们要花注意力在不同的法语的输入词上面。 然后同理。
注意力模型参考:
3.8 注意力模型(Attention Model)
我们先假定有一个输入句子,并使用双向的 RNN,或者双向的 GRU 或者双向的 LSTM,去计算每个词的特征。
为了 简化每个时间步的记号,即使你在双向 RNN 已经计算了前向的特征值和后向的特征值,我 就用\(𝑎^{<𝑡>}\)来一起表示这些联系。所以\(𝑎^{<𝑡>}\)就是时间步𝑡上的特征向量。但是为了保持记号的 一致性,我们用第二个,也就是𝑡′,实际上我将用𝑡′来索引法语句子里面的词。
接下来我们只 进行前向计算,就是说这是个单向的 RNN,用状态𝑆表示生成翻译。所以第一个时间步,它 应该生成\(𝑦^{<1>}\),当你输入上下文𝐶的时候就会这样,如果你想用时间来索引它,你可以写\(𝐶^{<1>}\), 但有时候我就写个𝐶,就是没有上标的𝐶,这个会取决于注意力参数,即\(,𝑎^{<1,1>},𝑎^{<1,2>}\)以此 类推,告诉我们应该花多少注意力。同样的,这个𝑎参数告诉我们上下文有多少取决于我们 得到的特征,或者我们从不同时间步中得到的激活值。
所以我们定义上下文的方式实际上来源于被注意力权重加权的不同时间步中的特征值。于是更公式化的注意力权重将会满足非负 的条件,所以这就是个 0 或正数,它们加起来等于 1。
这个权重的所有的𝑡′值,加上这些激活值。所以这里的这项(上图编号 1 所示)就是 注意力权重,这里的这项(上图编号 2)来自于这里(上图编号 3),于是\(𝑎^{<𝑡,𝑡′>}\)就是\(𝑦^{<𝑡>}\)应 该在𝑡′时花在𝑎上注意力的数量。换句话来说,当你在𝑡处生成输出词,你应该花多少注意力 在第𝑡′个输入词上面,这是生成输出的其中一步。然后下一个时间步,你会生成第二个输出。
于是相似的,你现在有了一个新的注意力权重集,再找到一个新的方式将它们相加,这就产 生了一个新的上下文,这个也是输入,且允许你生成第二个词。只有现在才用这种方式相加, 它会变成第二个时间步的上下文。即对𝑡′的\(𝑎^{<2,𝑡′>}\)进行求和,于是使用这些上下文向量,\(𝐶^{<1>}\) 写到这里,\(𝐶^{<2>}\)也同理。
回忆一下\(𝑎^{<𝑡,𝑡′>}\),是你应该花费在\(𝑎^{<𝑡′>}\)上的注意力的数量,当你尝试去生成第𝑡个输出 的翻译词,让我们先把式子写下来,再讨论它是怎么来的。这个式子你可以用来计算\(𝑎^{<𝑡,𝑡′>}\), 在此之前我们要先计算\(𝑒^{<𝑡,𝑡′>}\),关键要用 softmax,来确保这些权重加起来等于 1。如果你对 𝑡′求和,比如每一个固定的𝑡值,这些加起来等于 1。如果你对𝑡′求和,然后优先使用 softmax, 确保这些值加起来等于 1。
现在我们如何计算这些𝑒项,一种我们可以用的方式是用下面这样的小的神经网络,于是\(𝑠^{<𝑡−1>}\)就是神经网络在上个时间步的状态,于是这里我们有一个神经网络,如果你想要生成\(𝑦^{<𝑡>}\),那么\(𝑠^{<𝑡−1>}\)就是上一时间步的隐藏状态,即\(𝑠^{<𝑡>}\)。这是给小神经网络的其中一个输 入,也就是在神经网络中的一个隐藏层,因为你需要经常计算它们,然后\(𝑎^{<𝑡′>}\),即上个时间步的的特征是另一个输入。
直观来想就是,如果你想要决定要花多少注意力在𝑡′的激活值上。 于是,似乎它会很大程度上取决于你上一个时间步的的隐藏状态的激活值。你还没有当前状 态的激活值,因为上下文会输入到这里,所以你还没计算出来,但是看看你生成上一个翻译 的 RNN 的隐藏状态,然后对于每一个位置,每一个词都看向他们的特征值,这看起来很自 然,即\(𝑎^{<𝑡,𝑡′>}\)和\(𝑒^{<𝑡,𝑡′>}\)应该取决于这两个量。
但是我们不知道具体函数是什么,所以我们可以做的事情就是训练一个很小的神经网络,去学习这个函数到底是什么。相信反向传播算法, 相信梯度下降算法学到一个正确的函数。这表示,如果你应用这整个的模型,然后用梯度下 降来训练它,这是可行的。这个小型的神经网络做了一件相当棒的事情,告诉你\(𝑦^{<𝑡>}\)应该花 多少注意力在\(𝑎^{<𝑡>}\)上面,然后这个式子确保注意力权重加起来等于 1,于是当你持续地一次 生成一个词,这个神经网络实际上会花注意力在右边的这个输入句子上,它会完全自动的通 过梯度下降来学习。
这个算法的一个缺点就是它要花费三次方的时间,就是说这个算法的复杂是𝑂(𝑛3)的, 如果你有\(𝑇_𝑥\)个输入单词和\(𝑇_𝑦\)个输出单词,于是注意力参数的总数就会是\(𝑇_𝑥 × 𝑇_𝑦\),所以这个算 法有着三次方的消耗。但是在机器翻译的应用上,输入和输出的句子一般不会太长,可能三 次方的消耗是可以接受,但也有很多研究工作,尝试去减少这样的消耗。
3.9 语音识别(Speech recognition)
现在你有一个音频片段𝑥(an audio clip,x),你的任务是自动 地生成文本𝑦。现在有一个音频片段,画出来是这样,该图的横轴是时间。一个麦克风的作 用是测量出微小的气压变化,这时我们希望一个语音识别算法(a speech recognition algorithm),通过输入这段 音频,然后输出音频的文本内容。
语音识别方面,最令人振奋的趋势之一就是曾经有一段时间,语音识别系统是用音位 (phonemes)来构建的,也就是人工设计的基本单元(hand-engineered basic units of cells), 如果用音位来表示"the quick brown fox",我这里稍微简化一些,"the"含有"th"和"e"的音,而"quick"有"k" "w" "i" "k"的音,语音学家过去把这些音作为声音的基本单元写下来,把这些 语音分解成这些基本的声音单元,而"brown"不是一个很正式的音位,因为它的音写起来比 较复杂,不过语音学家(linguists)们认为用这些基本的音位单元(basic units of sound called phonemes)来表示音频(audio),是做语音识别最好的办法。
在 end-to-end 模型中, 我们发现这种音位表示法(phonemes representations)已经不再必要了,而是可以构建一个 系统,通过向系统中输入音频片段(audio clip),然后直接输出音频的文本(a transcript), 而不需要使用这种人工设计的表示方法。使这种方法成为可能的一件事就是用一个很大的数 据集,所以语音识别的研究数据集可能长达 300 个小时,在学术界,甚至 3000 小时的文本 音频数据集,都被认为是合理的大小。
还有一种效果也不错的方法,就是用 CTC 损失函数(CTC cost)来做语音识别。CTC 就 是 Connectionist Temporal Classification。
算法思想如下:
假设语音片段内容是某人说:"the quick brown fox",这时我们使用一个新的网络,结构 像这个样子,这里输入𝑥和输出𝑦的数量都是一样的,因为我在这里画的,只是一个简单的单 向 RNN 结构。然而在实际中,它有可能是双向的 LSTM 结构,或者双向的 GIU 结构,并且 通常是很深的模型。但注意一下这里时间步的数量,它非常地大。在语音识别中,通常输入 的时间步数量(the number of input time steps)要比输出的时间步的数量(the number of output time steps)多出很多。举个例子,比如你有一段 10 秒的音频,并且特征(features) 是 100 赫兹的,即每秒有 100 个样本,于是这段 10 秒的音频片段就会有 1000 个输入,就 是简单地用 100 赫兹乘上 10 秒。所以有 1000 个输入,但可能你的输出就没有 1000 个字母 了,或者说没有 1000 个字符。这时要怎么办呢?CTC 损失函数允许 RNN 生成这样的输出: ttt,这是一个特殊的字符,叫做空白符,我们这里用下划线表示,这句话开头的音可表示为 h_eee_ _ ,然后这里可能有个空格,我们用这个来表示空格,之后是 _ qqq_,这样的输 出也被看做是正确的输出。下面这段输出对应的是"the q"。CTC 损失函数的一个基本规则是 将空白符之间的重复的字符折叠起来,再说清楚一些,我这里用下划线来表示这个特殊的空白符(a special blank character),它和空格(the space character)是不一样的。所以 the 和 quick 之间有一个空格符,所以我要输出一个空格,通过把用空白符所分割的重复的字符折 叠起来,然后我们就可以把这段序列折叠成"the q"。这样一来你的神经网络因为有很多这种 重复的字符,和很多插入在其中的空白符(blank characters),所以最后我们得到的文本会 短上很多。于是这句"the quick brown fox"包括空格一共有 19 个字符,在这样的情况下,通 过允许神经网络有重复的字符和插入空白符使得它能强制输出 1000 个字符,甚至你可以输 出 1000 个𝑦值来表示这段 19 个字符长的输出。这篇论文来自于 Alex Grace 以及刚才提到的 那些人。
3.10 触发字检测(Trigger Word Detection)
对于触发字检测,最好的算法是什么, 目前还没有一个广泛的定论。现在有一个这样的 RNN 结构,我们要做的就是把一个音频片段(an audio clip)计算出它的声谱图特 征(spectrogram features)得到特征向量\(𝑥^{<1>}, 𝑥^{<2>}, 𝑥^{<3>}..\),然后把它放到 RNN 中,最后 要做的,就是定义我们的目标标签𝑦。假如音频片段中的这一点是某人刚刚说完一个触发字, 比如"Alexa",或者"小度你好" 或者"Okay Google",那么在这一点之前,你就可以在训练集 中把目标标签都设为 0,然后在这个点之后把目标标签设为 1。假如在一段时间之后,触发 字又被说了一次,比如是在这个点说的,那么就可以再次在这个点之后把目标标签设为 1。 这样的标签方案对于 RNN 来说是可行的,并且确实运行得非常不错。不过该算法一个明显 的缺点就是它构建了一个很不平衡的训练集(a very imbalanced training set),0 的数量比 1 多太多了。
这里还有一个解决方法,虽然听起来有点简单粗暴,但确实能使其变得更容易训练。比 起只在一个时间步上去输出 1,其实你可以在输出变回 0 之前,多次输出 1,或说在固定的 一段时间内输出多个 1。这样的话,就稍微提高了 1 与 0 的比例,这确实有些简单粗暴。在 音频片段中,触发字刚被说完之后,就把多个目标标签设为 1,这里触发字又被说了一次。 说完以后,又让 RNN 去输出 1。