在实施 NLP 解决方案时,循环神经网络(RNN)具有处理序列顺序的内置机制。Transformer则是引入来位置编码机制来保存文本中字符的位置信息。
-
位置编码定义
位置编码记录了文本中字符的位置信息,这里位置信息的记录不使用单个数字(例如索引值)来记录位置信息的原因有很多。 对于长序列,索引的大小可能会变大,不利于存储。 如果将索引值规范化为介于 0~1 之间,则可能会为可变长度序列带来问题,因为它们的标准化方式不同。 -
三角函数
这是对正弦函数的快速回顾;你可以等效地使用余弦函数。函数的取值范围是
[−1,+1]。该波形的频率是一秒内完成的周期数。波长是波形重复自身的距离。不同波形的波长和频率如下所示: -
位置编码公式
假设你有一个长度为 L 的输入序列,要计算第K个元素的位置编码。位置编码由不同频率的正弦和余弦函数给出。 -
可视化理解位置编码
Python实现位置编码,这是使用 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]]
单个字符可视化,要理解位置编码,让我们从查看 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)
下图是上面代码的输出:
可以看到每个位置对应于不同的正弦曲线,它将单个位置编码为向量。
- 整句话的位置编码可视化
可视化更大值的位置矩阵。 使用 matplotlib 库中的 matshow() 方法。 如原始论文中所做的那样设置 n=10,000 ,将得到以下结果:
P = getPositionEncoding(seq_len=100, d=512, n=10000)
cax = plt.matshow(P)
plt.gcf().colorbar(cax)
这里使用颜色来表示位置编码中的数值:
- 位置编码层的最终输出
Transformer中的位置编码层把位置向量与单词编码相加,并为后续层输出该矩阵。