微信公众号:NLP从入门到放弃
本文大概需要阅读 4.1 分钟
聊一下对 Decoder 的个人总结和理解,我保证里面大部分内容你没在别人那看过,绝对原创。
我先说一个很小的细节点,当时花了点时间才琢磨出来,其实不难,就是当时没转过弯来。
我们都知道,Decoder 的交互层,Q 矩阵来自本身,K/V 矩阵来自整个 Encoder 层输出。
但是不知道大家当时有没有这样一个疑惑点,就是 Encoder 层是怎么生成这个 K/V 矩阵的?
对于每个单词的输入,Encoder 都会对应一个输出。想一下 Bert 是不是每个单词都会对应一个自己本身的 Token 输出。
那么 K/V 矩阵在计算的时候是用的其中哪个单词的输出?第一个单词?最后一个单词?还是其他情况?
OK,我这个问题问法肯定是错误的。
后来仔细的取看了看了一下代码,恍然大悟,这才明白自己这个思维究竟错在哪里?我先说结论:
K/V 矩阵的计算不是来自于某一个单词的输出,而是对所有单词的输出汇总然后计算 K/V 矩阵,记住这里的 K/V 矩阵也是多头的。
这个过程和在 Encoder 中计算 K/V 矩阵是一样的,只不过放在了交互层,一时没想明白。大家如果有点疑惑的话,可以在这里停一下思考一下。
如果还没明白的同学,可以这样去理解。这个过程就是非常的类似Seq2Seq 的 attention 阶段。
Seq2Seq 初始的时候,只是使用最后一个时刻的隐层输出作为 context vector。加入attention之后,增加了一个对输入 LSTM每一个时刻计算权重的过程。
所以类比回来,在交互层,当然需要对每一个时刻(当然Transformer不存在时刻这个概念)的输出做一个交互的attention。
接下来,谈一下 Decoder 的整体架构。与Encoder很类似,Decoder同样由完全相同的N个大模块堆叠而成,原论文中N为6。
每个大的模块分为三部分:多头注意力层,交互层,前馈神经层;每个层内部尾端都含有 Add&Norm。
和Encoder重复的内容我就跳过了,之前讲过,没看过的同学可以点击这里去看一下那个文章。
对于多头自注意力层,我个人认为这里需要注意的细节点是:需要对当前单词和之后的单词做mask。
为什么需要mask?最常规的解释就是在预测阶段,你的模型看不见当前时刻的输出以及未来时刻单词。
从代码角度讲,你只需要把当前时刻之后所有单词 mask 掉就好了,也就是在计算注意力的时候,忽略这些内容。
我自己的理解是我们需要确保模型在训练和测试的时候没有 GAP 。
我举个简单的例子来解释我这句话,如果做机器翻译,你需要翻译出来的句子是 "我/爱/吃/苹果"。
当前时刻是'爱'这个单词作为输入的一部分,另一部分是上一个时刻'我'作为输入的时候的输出值。
当然在机器翻译中,我们一般使用 Teacher forcing加速收敛,所以这里就使用”我“作为当前时刻输入的另一个部分。
所以这个时候,输入就是”我“的编码信息和”爱“的编码信息(当然还有位置编码)。我要预测的是”吃“这个单词。
如果我们没有mask,模型也是可以运行的,也就说此时”吃“和”苹果“两个词对”爱“这个时刻的输出是有贡献的。
那么问题来了,测试数据中你根本没有 ground truth,你怎么办?
也就说,训练的时候,你的模型是基于知道这个时刻后面的单词进行的训练,但是测试的时候,做机器翻译,你不知道自己应该翻译出来什么东西。
这就是问题的核心。你训练模型的时候,一部分精力花在了'吃'和'苹果'两个词上,这不就是无用功吗?
而且,训练的时候知道后面的单词,相当于你这个模型在'作弊',在测试集这个真正的考场,效果好才是有鬼啊。
所以,确保模型在训练和测试的时候没有GAP,我们需要mask掉”吃“和”苹果“两个词。
对于交互模块,这一块需要注意的细节点就是之前文章提到的,Q矩阵来自本身,K/V 矩阵来自 Encoder 的输出。
还有一个细节点是,K/V 矩阵对应的是来自整个encoder的输出。这一点一定要注意哦,是整个,而不是每一层。
如果看 Transformer 那个经典图的话,初期很容易理解为encoder和decode对应的每一层互相交互,这是不对的。
是整个输出与decoder做交互。
Decoder 其他部分就比较简单了,应用 Decoder 的一个重要模型就是 GPT,这个的讲解我会放在之后词向量的部分去讲。