1.视频网站:mooc慕课https://mooc.study.163.com/university/deeplearning_ai#/c
2.详细笔记网站(中文):http://www.ai-start.com/dl2017/
3.github课件+作业+答案:https://github.com/stormstone/deeplearning.ai
3.5 定向搜索的误差分析 Error analysis on beam search
在这五门课中的第三门课里,你了解了误差分析是如何能够帮助你集中时间做你的项目中最有用的工作,束搜索算法是一种近似搜索算法(an approximate search algorithm),也被称作启发式搜索算法(a heuristic search algorithm),它不总是输出可能性最大的句子,它仅记录着B为前3或者10或是100种可能。那么如果束搜索算法出现错误会怎样呢?
本节课中,你将会学习到误差分析和束搜索算法是如何相互起作用的,以及你怎样才能发现是束搜索算法出现了问题,需要花时间解决,还是你的RNN模型出了问题,要花时间解决。
我们先来看看如何对束搜索算法进行误差分析。
我们来用这个例子说明: “Jane visite l’Afrique en septembre”。假如说,在你的机器翻译的dev集中,也就是开发集(development set),人工是这样翻译的: Jane visits Africa in September,我会将这个标记为 y ∗ y^* y∗,这是一个十分不错的人工翻译结果。
不过假如说,当你在已经完成学习的RNN模型,也就是已完成学习的翻译模型中运行束搜索算法时,它输出了这个翻译结果:Jane visited Africa last September,我们将它标记为 y ^ \hat y y^。这是一个十分糟糕的翻译,它实际上改变了句子的原意,因此这不是个好翻译。
你的模型有两个主要部分,
- 一个是NN模型,或说是序列到序列模型(sequence to sequence model),我们将这个称作是RNN模型,它实际上是个编码器和解码器( an encoder and a decoder)。
- 另一部分是束搜索算法,以某个集束宽度B运行。
如果你能够找出造成这个错误,这个不太好的翻译的原因,是两个部分中的哪一个,不是很好吗? RNN (循环神经网络)是更可能是出错的原因呢,还是束搜索算法更可能是出错的原因呢?
你在第三门课中了解到了大家很容易想到去收集更多的训练数据,这总归没什么坏处。所以同样的,大家也会觉得不行就增大束宽,也是不会错的,或者说是很大可能是没有危害的。但是就像单纯获取更多训练数据,可能并不能得到预期的表现结果。相同的,单纯增大束宽也可能得不到你想要的结果,不过你怎样才能知道是不是值得花时间去改进搜索算法呢?
下面我们来分解这个问题弄清楚什么情况下该用什么解决办法。
RNN (循环神经网络)实际上是个编码器和解码器(the encoder and the decoder),它会计算 P ( y ∣ x ) P(y|x) P(y∣x)。
举个例子,对于这个句子:Jane visits Africa in September,你将Jane visits Africa填入这里(上图右下解码器),我现在忽略了字母的大小写,后面也是一样,然后这个就会计算 P ( y ∣ x ) P(y|x) P(y∣x)。结果表明,你此时能做的最有效的事就是用这个模型来计算 P ( y ∗ ∣ x ) P(y^*|x) P(y∗∣x),同时也用你的RNN模型来计算 P ( y ^ ∣ x ) P(\hat y|x) P(y^∣x),然后比较一下这两个值哪个更大。
有可能是
P
(
y
^
∣
x
)
P(\hat y|x)
P(y^∣x)大于
P
(
y
∗
∣
x
)
P(y^*|x)
P(y∗∣x),也有可能是
P
(
y
∗
∣
x
)
P(y^*|x)
P(y∗∣x)小于
P
(
y
^
∣
x
)
P(\hat y|x)
P(y^∣x),其实应该是小于或等于,对吧。取决于实际是哪种情况,你就能够更清楚地将这个特定的错误归咎于RNN或是束搜索算法,或说是哪个负有更大的责任。我们来探究一下其中的逻辑。
如上图,这是之前介绍的两个句子。记住,我们是要计算 P ( y ^ ∣ x ) P(\hat y|x) P(y^∣x)和 P ( y ∗ ∣ x ) P(y^*|x) P(y∗∣x),然后比较这两个哪个更大,所以就会有两种情况。
第一种情况,RNN模型的输出结果 P ( y ∗ ∣ x ) P(y^*|x) P(y∗∣x)大于 P ( y ^ ∣ x ) P(\hat y|x) P(y^∣x),这意味着什么呢? 束搜索算法选择了 y ^ \hat y y^ ,对吧? 你得到 y ^ \hat y y^的方式是,你用一个RNN模型来计算 P ( y ∣ x ) P(y|x) P(y∣x),然后束搜索算法做的就是尝试寻找使 P ( y ∣ x ) P(y|x) P(y∣x)最大的 y y y,即 a r g m a x P ( y ∣ x ) arg max P(y|x) argmaxP(y∣x)。不过在这种情况下,相比于 y ^ \hat y y^, y ∗ y^* y∗的值更大,因此你能够得出结论,束搜索算法实际上不能够给你一个能使 P ( y ∣ x ) P(y|x) P(y∣x)最大化的 y y y值,因为束搜索算法的任务就是寻找一个 y y y的值来使 P ( y ∣ x ) P(y|x) P(y∣x)更大,但是它却选择了 y ^ \hat y y^,而 y ∗ y^* y∗实际上能得到更大的值。因此这种情况下你能够得出是束搜索算法出错了。
那另一种情况是怎样的呢?
第二种情况是 P ( y ∗ ∣ x ) P(y^*|x) P(y∗∣x)小于或等于 P ( y ^ ∣ x ) P(\hat y|x) P(y^∣x)对吧?这两者之中总有一个是真的。情况1或是情况2总有一个为真。情况2你能够总结出什么呢? 在我们的例子中, y ∗ y^* y∗是比 y ^ \hat y y^更好的翻译结果,不过根据RNN模型的结果, P ( y ∗ ∣ x ) P(y^*|x) P(y∗∣x)是小于 P ( y ^ ∣ x ) P(\hat y|x) P(y^∣x)的,也就是说,相比于 y ^ \hat y y^, y ∗ y^* y∗成为输出的可能更小。因此在这种情况下,看来是RNN模型出了问题。同时可能值得在RNN模型上花更多时间。这里我少讲了一些有关长度归一化(length normalizations)的细节,如果你用了某种长度归一化,那么你要做的就不是比较 P ( y ∗ ∣ x ) P(y^*|x) P(y∗∣x)和 P ( y ^ ∣ x ) P(\hat y|x) P(y^∣x)这两种可能性大小,而是比较长度归一化后的最优化目标函数值。不过现在先忽略这种复杂的情况。第二种情况表明虽然 y ∗ y^* y∗是一个更好的翻译结果,RNN模型却赋予它( P ( y ∗ ∣ x ) P(y^*|x) P(y∗∣x))更低的可能性,是RNN模型出现了问题。
所以误差分析过程看起来就像下面这样。
你先遍历开发集,然后在其中找出算法产生的错误。
这个例子中,假如说 P ( y ∗ ∣ x ) P(y^*|x) P(y∗∣x)的值为2 x 10-10,而 P ( y ^ ∣ x ) P(\hat y|x) P(y^∣x)的值为 1 x10-10,根据上面介绍的逻辑关系,这种情况下我们得知束搜索算法实际上选择了比 y ∗ y^* y∗可能性更低的 y ^ \hat y y^,因此我会说束搜索算法出错了。我将它缩写为B。
接着你继续遍历第二个错误,再来看这些可能性。
也许对于第二个例子来说,你认为是RNN模型出现了问题,我会用缩写R来代表RNN。
再接着你遍历了更多的例子,有时是束搜索算法出现了问题,有时是模型出现了问题,等等。
通过这个过程,你就能够执行误差分析,得出束搜索算法和RNN模型出错的比例是多少。有了这样的误差分析过程,你就可以对开发集中每一个错误例子,即算法输出了比人工翻译更差的结果的情况,尝试确定这些错误,是搜索算法出了问题,还是生成目标函数(束搜索算法使之最大化)的RNN模型出了问题。
并且通过这个过程,你能够发现这两个部分中哪个是产生更多错误的原因,并且只有当你发现是束搜索算法造成了大部分错误时,才值得花费努力增大集束宽度。相反地,如果你发现是RNN模型出了更多错,那么你可以进行更深层次的分析,来决定是需要增加正则化还是获取更多的训练数据,抑或是尝试一个不同的网络结构,或是其他方案。你在第三门课中,了解到各种技巧都能够应用在这里。
这就是束搜索算法中的误差分析,我认为这个特定的误差分析过程是十分有用的,它可以用于分析近似最佳算法(如束搜索算法),这些算法被用来优化学习算法(例如序列到序列模型/RNN)输出的目标函数。也就是我们这些课中一直讨论的。学会了这个方法,我希望你能够在你的应用里更有效地运用好这些类型的模型。