Transformer位置编码图解

在语言中,单词的顺序及其在句子中的位置非常重要。 如果重新排列单词,整个句子的意思可能会发生变化。 在实施 NLP 解决方案时,循环神经网络具有处理序列顺序的内置机制。 然而,transformer 模型不使用递归或卷积,而是将每个数据点视为独立于其他数据点。 因此,位置信息被明确地添加到模型中,以保留有关句子中单词顺序的信息。 位置编码是一种方案,通过它可以维护序列中对象顺序的知识。

在本教程中,我们将简化 Vaswani 等人在这篇非凡论文 Attention Is All You Need 中使用的符号。 完成本教程后,你将了解:

  • 什么是位置编码,为什么它很重要
  • Transformer中的位置编码
  • 使用 NumPy 在 Python 中编码和可视化位置编码矩阵

让我们开始吧。

1、什么是位置编码?

位置编码描述了序列中实体的位置或位置,以便为每个位置分配一个唯一的表示。 不使用单个数字(例如索引值)来表示项在转换器模型中的位置的原因有很多。 对于长序列,索引的幅度可能会变大。 如果将索引值规范化为介于 0 和 1 之间,则可能会为可变长度序列带来问题,因为它们的规范化方式不同。

Transformers 使用智能位置编码方案,其中每个位置/索引都映射到一个向量。 因此,位置编码层的输出是一个矩阵,其中矩阵的每一行代表序列中的一个编码对象与其位置信息相加。 下图显示了仅对位置信息进行编码的矩阵示例。

2、快速回顾三角函数

这是对正弦函数的快速回顾; 你可以等效地使用余弦函数。 函数的取值范围是 [-1,+1]。 该波形的频率是一秒内完成的周期数。 波长是波形重复自身的距离。 不同波形的波长和频率如下所示:

3、Transformer中的位置编码层

让我们直接进入这个主题。

假设你有一个长度为L的输入序列,要计算第K个元素的位置编码。位置编码由不同频率的正弦和余弦函数给出:

这里:

  • k:对象在输入序列中的位置,0<=k<L/2
  • d: 输出嵌入空间的维度
  • P(k,j): 位置函数,用于映射输入序列中k处的元素到位置矩阵的(k,j)处
  • n:用户定义的标量,由 Attention Is All You Need 的作者设置为 10,000。
  • i: 用于映射到列索引,0<=i<d/2,单个值i映射到正弦和余弦函数

在上面的表达式中,你可以看到偶数位置对应正弦函数,奇数位置对应余弦函数。

4、位置编码示例

为了理解上面的表达式,让我们以 n=100 和 d=4 的短语“I am a robot”为例。 下表显示了该短语的位置编码矩阵。 事实上,位置编码矩阵对于任何 n=100 和 d=4 的四字母短语都是相同的。

5、从头实现位置编码矩阵

这是使用 NumPy 实现位置编码的简短 Python 代码。 简化了代码,以便更容易理解位置编码。

import numpy as np
import matplotlib.pyplot as plt

def getPositionEncoding(seq_len, d, n=10000):
    P = np.zeros((seq_len, d))
    for k in range(seq_len):
        for i in np.arange(int(d/2)):
            denominator = np.power(n, 2*i/d)
            P[k, 2*i] = np.sin(k/denominator)
            P[k, 2*i+1] = np.cos(k/denominator)
    return P

P = getPositionEncoding(seq_len=4, d=4, n=100)
print(P)

输出如下:

[[ 0.          1.          0.          1.        ]
 [ 0.84147098  0.54030231  0.09983342  0.99500417]
 [ 0.90929743 -0.41614684  0.19866933  0.98006658]
 [ 0.14112001 -0.9899925   0.29552021  0.95533649]]

6、理解位置编码矩阵

要理解位置编码,让我们从查看 n=10,000 和 d=512 的不同位置的正弦波开始。

def plotSinusoid(k, d=512, n=10000):
    x = np.arange(0, 100, 1)
    denominator = np.power(n, 2*x/d)
    y = np.sin(k/denominator)
    plt.plot(x, y)
    plt.title('k = ' + str(k))

fig = plt.figure(figsize=(15, 4))    
for i in range(4):
    plt.subplot(141 + i)
    plotSinusoid(i*4)

下图是上面代码的输出:

可以看到每个位置对应于不同的正弦曲线,它将单个位置编码为向量。 如果仔细观察位置编码函数,你会发现固定i时对应的波长:

因此,正弦曲线的波长形成几何级数。 位置编码方案具有许多优点。

  • 正弦和余弦函数的值在 [-1, 1] 内,这使位置编码矩阵的值保持在归一化范围内。
  • 由于每个位置的正弦曲线都不同,因此你可以采用独特的方式对每个位置进行编码。
  • 有一种方法可以测量或量化不同位置之间的相似性,从而使你能够对单词的相对位置进行编码。

7、可视化位置矩阵

让我们可视化更大值的位置矩阵。 使用 matplotlib 库中的 matshow() 方法。 如原始论文中所做的那样设置 n=10,000,将得到以下结果:

P = getPositionEncoding(seq_len=100, d=512, n=10000)
cax = plt.matshow(P)
plt.gcf().colorbar(cax)

8、位置编码层的最终输出是什么?

位置编码层将位置向量与单词编码相加,并为后续层输出该矩阵。 整个过程如下图所示。


原文链接:A Gentle Introduction to Positional Encoding in Transformer Models, Part 1

BimAnt翻译整理,转载请标明出处