2017年来自谷歌的Vaswani等人提出了Transformer模型,一种新颖的纯粹采用注意力机制实现的Seq2Seq架构,它具备并行化训练的能力,拥有非凡的性能表现,这些特点使它深受NLP研究人员们的喜欢,成为NLP领域的标志性模型之一。因此,当人们提到Transformer模型时,多头注意力机制(Multi-Head Attention)成为人们讨论最多的创新点,正如前面所说,这种机制让Transformer变得非常强大。
其实,原文使用到了一种非常有意思的技巧——Positional Encoding,中文翻译为位置编码。这个技巧,直观上非常容易理解,就是对序列中的词的位置进行编码。但是,原文并没有对这种新颖的位置编码方式作细致的讲解。
Positional Encoding(位置编码)是Transformer模型中很重要的一个概念。与“RNN”和“LSTM”等顺序算法不同,Transformer 没有内置机制来捕获句子中单词的相对位置。 这很重要,因为单词之间的距离提供了关键的上下文信息。 这是位置编码介入的地方。其实位置编码不是模型架构的一部分。 它实际上只是预处理的一部分。 生成的位置编码向量与每个词的嵌入向量大小维度相同。 经过计算,位置编码向量被添加到嵌入向量中。
一 为什么需要位置编码?
引入位置编码主要归因于两方面:
(1)对于任何一门语言,单词在句子中的位置以及排列顺序是非常重要的,它们不仅是一个句子的语法结构的组成部分,更是表达语义的重要概念。一个单词在句子的位置或排列顺序不同,可能整个句子的意思就发生了偏差。
句子1:我喜欢吃洋葱
句子2:洋葱喜欢吃我
上面两句话所使用的的字完全一样,但是所表达的句意却截然相反。那么,引入词序信息有助于区别这两句话的意思。
(2)Transformer模型抛弃了RNN、CNN作为序列学习的基本模型。我们知道,循环神经网络本身就是一种顺序结构,天生就包含了词在序列中的位置信息。当抛弃循环神经网络结构,完全采用Attention取而代之,这些词序信息就会丢失,模型就没有办法知道每个词在句子中的相对和绝对的位置信息。因此,有必要把词序信号加到词向量上帮助模型学习这些信息,位置编码就是用来解决这种问题的方法。
二 位置编码是什么?
位置编码(Positional Encoding)是一种用词的位置信息对序列中的每个词进行二次表示的方法。正如前文所述,Transformer模型本身不具备像RNN那样的学习词序信息的能力,需要主动将词序信息喂给模型。那么,模型原先的输入是不含词序信息的词向量,位置编码需要将词序信息和词向量结合起来形成一种新的表示输入给模型,这样模型就具备了学习词序信息的能力。
三 位置编码分类
总的来说,位置编码分为两个类型:函数型和表格型
函数型:通过输入token位置信息,得到相应的位置编码
表格型:建立一个长度为L的词表,按词表的长度来分配位置id
表格型
方法一:使用[0,1]范围分配
这个方法的分配方式是,将0-1这个范围的,将第一个token分配0,最后一个token分配去1,其余的token按照文章的长度平均分配。具体形式如下:
我喜欢吃洋葱 【0 0.16 0.32.....1】
我真的不喜欢吃洋葱【0 0.125 0.25.....1】
问题:我们可以看到,如果句子长度不同,那么位置编码是不一样,所以无法表示句子之间有什么相似性。你无法知道在一个特定区间范围内到底存在多少个单词。换句话说,不同句子之间的时间步差值没有任何的意义。
方法二:1-n正整数范围分配
这个方法比较直观,就是按照输入的顺序,一次分配给token所在的索引位置。具体形式如下:
我喜欢吃洋葱 【1,2,3,4,5,6】
我真的不喜欢吃洋葱【1,2,3,4,5,6,7】
问题:往往句子越长,后面的值越大,数字越大说明这个位置占的权重也越大,这样的方式无法凸显每个位置的真实的权重。这种方法带来的问题是,不仅这些数值会变得非常大,而且模型也会遇到一些比训练中的所有句子都要长的句子。此外,数据集中不一定在所有数值上都会包含相对应长度的句子,也就是模型很有可能没有看到过任何一个这样的长度的样本句子,这会严重影响模型的泛化能力。
因此,一种好的位置编码方案需要满足以下几条要求:
- 它能为每个时间步输出一个独一无二的编码;
- 不同长度的句子之间,任何两个时间步之间的距离应该保持一致;
- 模型应该能毫不费力地泛化到更长的句子。它的值应该是有界的;
- 它必须是确定性的。
相对位置的关系-函数型
相对位置编码的特点,关注一个token与另一个token距离的相对位置(距离差几个token)。位置1和位置2的距离比位置3和位置10的距离更近,位置1和位置2与位置3和位置4都只相差1。
还是按照上面"我喜欢吃洋葱"中的“我”为例,看看相对位置关系是什么样子的:
使用相对位置的方法,我们可以清晰的知道单词之间的距离远近的关系。
Transformer的作者们提出了一个简单但非常创新的位置编码方法,能够满足上述所有的要求。
首先,这种编码不是单一的一个数值,而是包含句子中特定位置信息的d维向量(非常像词向量)第二,这种编码没有整合进模型,而是用这个向量让每个词具有它在句子中的位置的信息。换句话说,通过注入词的顺序信息来增强模型输入。、
首先给一个定义:Transformer的位置信息是函数型的。在GPT-3论文中给出的公式如下:
首先需要注意的是,上个公式给出的每一个Token的位置信息编码不是一个数字,而是一个不同频率分割出来,和文本一样维度的向量。向量如下:
得到位置向量P之后,将和模型的embedding向量相加,得到进入Transformer模型的最终表示。
这里的 t 就是每个token的位置,比如说是位置1,位置2,以及位置n
为了保证这种相加操作正确,让位置向量(PE)的维度等于词向量(WE)的维度
为什么周期函数能够引入位置信息?
那么怎么在保存这个数的含义的情况下用更小的数表示,一种很简单的方式就是转换进制,这样就能够使用多维数据表示相同含义。用二进制举个例子, 我们将数字用二进制表示出来。可以发现,每个比特位的变化率是不一样的,越低位的变化越快,红色位置0和1每个数字会变化一次,而黄色位,每8个数字才会变化一次。
不同频率的sin和cosin组合其实也是同样的道理,通过调整三角函数的频率,我们可以实现这种低位到高位的变化,这样的话,位置信息就表示出来了。
上表中维度0,维度1,维度2,维度3拼成的数字就是该位置对应的二进制表示。可以看到每个维度(也就是每一列)其实都是有周期的,并且周期是不同的,这也就能够说明使用多个周期不同的周期函数组成的多维度编码和递增序列编码其实是可以等价的。这也就回答了为什么周期函数能够引入位置信息。
为什么选择三角函数引入位置信息?
理解了上面的内容就很容易想通这个问题了。
理想中的位置编码应该满足以下两个条件:
- 单个维度取值别太大(太大则又会有使用原始1,2,3...这样的编码方式遇到的问题)
- 可以轻易得到多个周期不同的函数(位置编码要求每个维度周期不同)
三角函数恰好就满足以上两点要求,取值范围[-1,1],并且能够轻易得到多个周期不同的函数,所以三角函数是一个不错的选择。
为什么这里使用向量相加而不是拼接的形式?
词向量 E 和位置向量 P 相加再经过后续的线性变换,可以理解为
而当两个向量拼接后经过后续的线性变换,可以理解为
当
二者的效果是等价的。我们可以由此发现,拼接总是不会比相加差的,但是由于参数量的增加,其学习难度也会进一步上升.
无论拼接还是相加,最终都要经过多头注意力的各个头入口处的线性变换,进行特征重新组合与降维,其实每一维都变成了之前所有维向量的线性组合。所以这个决定看上去是根据效果决定的,参数少效果好的相加自然成了模型的选择。
还按照前面的标记,在Transformer方案中,每一个注意力头都先将输入特征使用某个参数矩阵 W线性映射到一个更低维的向量,这时:无论使用向量相加
还是向量拼接
,在线性变换后得到的向量,都是
和
两个向量中所有元素的线性组合。也就是说,在任意一个注意力头中,输入的特征都已经被重组(求和)了。因此,使用拼接和相加应该差别不大,而拼接方法又使用了额外的参数量。