什么是位置编码
- 位置编码概述 位置编码的目的是为了补充序列的位置信息,这是因为自注意力机制本身不包含位置的概念(例如顺序信息)。位置编码的具体作用是,对于不同的输入序列成分,赋予其不同的位置标识,确保序列信息在不同的上下文中仍然,即使是相同的文本序列也因位置不同而有不同的含义。
Transformers 使用的位置编码方法,其中每个位置/词素都被分配一个编号。到此,位置编码的输出是一个矩阵,其中每行的每一行代表序列中的一个特定词素与其位置信息相结合。下图演示了一个较小的位置编码矩阵示例的构成方式。
位置编码矩阵
位置编码矩阵示例 - 序列 ‘I am a robot’
三角函数
- 三角函数 三角函数是数学中的基本概念,不仅可以表达周期性的现象,还能描述波动的基本特性。这些函数的图像通常在[-1, 1]的区间内。三角函数的一般形式包括正弦和余弦两种,它们的周期性是函数的重要特性。以下表格列出了几种常见的三角函数形式及其特性:
位置编码公式
位置编码的数学公式用于为每一个位置(即序列中的词素)分配一个独特的编码,以使其能够在不同的上下文中区别对待。位置编码使用下述公式来生成位置编码矩阵:
其中:
- 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 am a robot” 为例,设定 n = 100, d = 4。在这个例子中,我们计算了 n = 100 和 d = 4 的序列的位置编码,位置编码的计算结果如下表:
位置编码矩阵示例 - 序列 ‘I am a robot’
Python实现位置编码
使用 NumPy 库进行矩阵和数学运算以及 Matplotlib 库进行图形绘制,下面是 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)
单个序列的可视化
使用 Matplotlib 库绘制不同位置的三角函数图,下面是 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
def plotSinusoid(k, d, n):
x = np.arange(0, 100, 1)
denominator = np.power(n, 2*k/d)
y = np.sin(x/denominator)
plt.plot(x, y)
plt.title('k = ' + str(k))
# 使用长序列和较大维度的参数
seq_len = 100
d = 512
n = 10000
P = getPositionEncoding(seq_len, d, n)
# 正弦波绘图
fig = plt.figure(figsize=(15, 4))
for i in range(4):
plt.subplot(1, 4, i + 1)
plotSinusoid(i*4, d, n) # 确保传入函数的参数和生成P矩阵时的参数一致
plt.show()
下图是四个不同 k 值的正弦波形图。可以看到,随着 k 值的增大,波形周期发生了变化。
可视化结果:四个不同 k 值的正弦波形图,分别对应 k=0, k=4, k=8, 和 k=12 的情况。随着 k 值的增大,波形周期发生变化,显示了不同频率和波长的正弦波。
热图可视化编码矩阵
使用 Python 的 Matplotlib 库的 matshow()
函数,可以将位置编码矩阵以热图的形式可视化。热图可以直观地展示不同位置编码的值的大小。下面是 Python 代码示例以及热图的输出结果。
# 热图绘图
fig2 = plt.figure(figsize=(15, 4))
cax = plt.matshow(P, aspect='auto') # aspect='auto'保证热图在x轴和y轴方向拉伸以填满画布
plt.colorbar(cax)
plt.show()
热图结果:展示了一个长为 100,宽为 512 的位置编码矩阵的热图。热图中的颜色变化表示不同位置编码值的大小,可以看到随着位置的变化,颜色也呈现出周期性的变化模式。
位置编码的整体流程
Transformer模型的位置编码过程包括将词汇转换为向量,然后与位置编码相加,以保持位置信息。以下是该过程的详细描述:
- 输入序列(Input sequence): I, am, a, Robot
- 词向量嵌入(Word embedding): 将每个输入词转换为一个嵌入向量(embedding vector)。
- v0 是 "I" 的嵌入向量
- v1 是 "am" 的嵌入向量
- v2 是 "a" 的嵌入向量
- v3 是 "Robot" 的嵌入向量
- 位置编码矩阵(Positional Encoding Matrix): 计算序列中每个词的位置编码向量。
- P0 是 "I" 的位置编码向量
- P1 是 "am" 的位置编码向量
- P2 是 "a" 的位置编码向量
- P3 是 "Robot" 的位置编码向量
- 位置编码层的输出(Output of positional encoding layer): 词向量和位置编码向量相加,得到最终的编码向量。
- y0 是 "I" 的位置编码
- y1 是 "am" 的位置编码
- y2 是 "a" 的位置编码
- y3 是 "Robot" 的位置编码