在语言中,单词的顺序及其在句子中的位置非常重要。 如果重新排列单词,整个句子的意思可能会发生变化。 在实施 NLP 解决方案时,循环神经网络具有处理序列顺序的内置机制。 然而,transformer 模型不使用递归或卷积,而是将每个数据点视为独立于其他数据点。 因此,位置信息被明确地添加到模型中,以保留有关句子中单词顺序的信息。 位置编码是一种方案,通过它可以维护序列中对象顺序的知识。
在本教程中,我们将简化 Vaswani 等人在这篇非凡论文 Attention Is All You Need 中使用的符号。 完成本教程后,你将了解:
- 什么是位置编码,为什么它很重要
- Transformer中的位置编码
- 使用 NumPy 在 Python 中编码和可视化位置编码矩阵
让我们开始吧。
推荐:使用 NSDT场景设计器 快速搭建 3D场景。
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、位置编码层的最终输出是什么?
位置编码层将位置向量与单词编码相加,并为后续层输出该矩阵。 整个过程如下图所示。
原文链接:Transformer位置编码图解 — BimAnt