【Transformer模型学习】第三篇:位置编码
文章目录
- 0. 前言
- 1. 为什么需要位置编码?
- 2. 如何进行位置编码?
- 3. 正弦和余弦位置编码
- 4. 举个例子
- 4.1 参数设置
- 4.2 计算分母项
- 4.3 计算位置编码
- 4.4 位置编码矩阵
- 5. 相对位置信息
- 6. 改进的位置编码方式——RoPE
- 6.1 RoPE的核心思想
- 6.2 RoPE的优势
- 7. 总结
0. 前言
按照国际惯例,首先声明:本文只是我自己学习的理解,虽然参考了他人的宝贵见解及成果,但是内容可能存在不准确的地方。如果发现文中错误,希望批评指正,共同进步。
本文是Transformer学习系列的第三篇文章:
- 第一篇:提出背景、模型架构及推理过程
- 第二篇:多头注意力机制
- 第三篇:位置编码(本篇)
本文将介绍Transformer的重要(难点)内容——位置编码。
1. 为什么需要位置编码?
在第一篇文章就说明过:Transformer的优点在于可以并行化处理序列,这是因为注意力机制的引入,使得输入或输出序列中的(各个token的)距离将不会影响模型的处理,即注意力机制是排列不变的。并行处理加上排列不变就会带来一个麻烦:模型无法直接感知输入序列中元素的位置信息。
与RNN相比,RNN可以通过其循环结构逐步处理序列中的每个元素,并将前一个时间步的隐藏状态传递到下一个时间步。这种机制使RNN能够自然地捕捉序列中的时间依赖关系和顺序信息。
所以,Transformer需要一种编码机制:让模型能够知道输入序列中的元素的位置信息,尤其是两个元素间的相对位置信息。
2. 如何进行位置编码?
“对一个序列中的元素进行位置编码”,这乍一看是个非常简单的任务,只要对其中所有元素进行编号[1, 2, 3…]不就行了吗?
但实际上,如此简单粗暴的位置编码方式是不可行的。主要是因为自然编号是离散的整数值,缺乏连续性和平滑性。自然编号的数值范围会随序列长度线性增长,导致训练不稳定,尤其是在长序列任务中。
因此,我们需要一种更聪明的位置编码,它要满足以下条件:
位置编码(Positional Encoding)在Transformer中起着至关重要的作用,它需要满足以下条件才能有效地为模型提供位置信息:
-
唯一性:每个位置的位置编码必须是唯一的,以便模型能够区分序列中不同位置的元素。
-
相对位置信息:位置编码应该能够捕捉到相对位置信息(如距离、方向等),而不仅仅是绝对位置。例如,模型应该能够知道“距离当前词3个位置的词”在哪里。
-
可扩展性:位置编码应该能够适应不同长度的序列,而不仅仅是训练时见过的序列长度。例如,模型在训练时可能只见过长度为100的序列,但在测试时可能需要处理长度为1000的序列。
3. 正弦和余弦位置编码
在Attention is All You Need 提出了一种位置编码方法——正弦和余弦位置编码。
位置编码是一个与词嵌入维度相同的向量,直接加到词嵌入上:假设词嵌入的维度为 d model d_{\text{model}} dmodel,那位置编码的维度也为 d model d_{\text{model}} dmodel。
对于位置
p
o
s
pos
pos和维度
i
i
i,位置编码的计算公式为:
P
p
o
s
,
2
i
=
sin
(
p
o
s
1000
0
2
i
d
model
)
P_{pos, 2i} = \sin\left(\frac{pos}{10000^{\frac{2i}{d_{\text{model}}}}}\right)
Ppos,2i=sin(10000dmodel2ipos)
P
p
o
s
,
2
i
+
1
=
cos
(
p
o
s
1000
0
2
i
d
model
)
P_{pos, 2i+1} = \cos\left(\frac{pos}{10000^{\frac{2i}{d_{\text{model}}}}}\right)
Ppos,2i+1=cos(10000dmodel2ipos)
其中:
- p o s pos pos:序列中的位置索引(从0开始)。
- i i i:维度索引(从0到 d model − 1 d_{\text{model}}-1 dmodel−1)。
- d model d_{\text{model}} dmodel:词嵌入的维度,例如设为512。
- 10000 10000 10000:一个超参数,用于控制波长。
4. 举个例子
我们通过一个更简单的例子来计算位置编码。假设我们有一个序列长度为2(即2个词),词嵌入维度为4(即每个词用一个4维向量表示)。我们将使用正弦和余弦位置编码公式来计算位置编码。
4.1 参数设置
- 序列长度 seq_len = 2 \text{seq\_len} = 2 seq_len=2。
- 词嵌入维度 d model = 4 d_{\text{model}} = 4 dmodel=4。
- 位置索引 p o s = 0 , 1 pos = 0, 1 pos=0,1。
- 维度索引 i = 0 , 1 i = 0, 1 i=0,1(因为 d model = 4 d_{\text{model}} = 4 dmodel=4,所以 i i i 的取值范围是0到1)。
4.2 计算分母项
首先计算分母项
div_term
\text{div\_term}
div_term:
div_term
=
1000
0
2
i
d
model
\text{div\_term} = 10000^{\frac{2i}{d_{\text{model}}}}
div_term=10000dmodel2i
对于
i
=
0
,
1
i = 0, 1
i=0,1,计算结果如下:
- 当
i
=
0
i = 0
i=0 时:
div_term = 1000 0 0 4 = 1 \text{div\_term} = 10000^{\frac{0}{4}} = 1 div_term=1000040=1 - 当
i
=
1
i = 1
i=1 时:
div_term = 1000 0 2 4 = 1000 0 0.5 = 100 \text{div\_term} = 10000^{\frac{2}{4}} = 10000^{0.5} = 100 div_term=1000042=100000.5=100
4.3 计算位置编码
对于每个位置 p o s pos pos和维度 i i i,计算正弦和余弦值。
位置 p o s = 0 pos = 0 pos=0
- 偶数维度(
2
i
2i
2i):
-
i
=
0
i = 0
i=0:
P 0 , 0 = sin ( 0 1 ) = sin ( 0 ) = 0 P_{0, 0} = \sin\left(\frac{0}{1}\right) = \sin(0) = 0 P0,0=sin(10)=sin(0)=0 -
i
=
1
i = 1
i=1:
P 0 , 2 = sin ( 0 100 ) = sin ( 0 ) = 0 P_{0, 2} = \sin\left(\frac{0}{100}\right) = \sin(0) = 0 P0,2=sin(1000)=sin(0)=0
-
i
=
0
i = 0
i=0:
- 奇数维度(
2
i
+
1
2i+1
2i+1):
-
i
=
0
i = 0
i=0:
P 0 , 1 = cos ( 0 1 ) = cos ( 0 ) = 1 P_{0, 1} = \cos\left(\frac{0}{1}\right) = \cos(0) = 1 P0,1=cos(10)=cos(0)=1 -
i
=
1
i = 1
i=1:
P 0 , 3 = cos ( 0 100 ) = cos ( 0 ) = 1 P_{0, 3} = \cos\left(\frac{0}{100}\right) = \cos(0) = 1 P0,3=cos(1000)=cos(0)=1
-
i
=
0
i = 0
i=0:
- 位置
p
o
s
=
0
pos = 0
pos=0 的位置编码:
P 0 = [ 0 , 1 , 0 , 1 ] P_0 = [0, 1, 0, 1] P0=[0,1,0,1]
位置 p o s = 1 pos = 1 pos=1
- 偶数维度(
2
i
2i
2i):
-
i
=
0
i = 0
i=0:
P 1 , 0 = sin ( 1 1 ) = sin ( 1 ) ≈ 0.8415 P_{1, 0} = \sin\left(\frac{1}{1}\right) = \sin(1) \approx 0.8415 P1,0=sin(11)=sin(1)≈0.8415 -
i
=
1
i = 1
i=1:
P 1 , 2 = sin ( 1 100 ) = sin ( 0.01 ) ≈ 0.0100 P_{1, 2} = \sin\left(\frac{1}{100}\right) = \sin(0.01) \approx 0.0100 P1,2=sin(1001)=sin(0.01)≈0.0100
-
i
=
0
i = 0
i=0:
- 奇数维度(
2
i
+
1
2i+1
2i+1):
-
i
=
0
i = 0
i=0:
P 1 , 1 = cos ( 1 1 ) = cos ( 1 ) ≈ 0.5403 P_{1, 1} = \cos\left(\frac{1}{1}\right) = \cos(1) \approx 0.5403 P1,1=cos(11)=cos(1)≈0.5403 -
i
=
1
i = 1
i=1:
P 1 , 3 = cos ( 1 100 ) = cos ( 0.01 ) ≈ 0.9999 P_{1, 3} = \cos\left(\frac{1}{100}\right) = \cos(0.01) \approx 0.9999 P1,3=cos(1001)=cos(0.01)≈0.9999
-
i
=
0
i = 0
i=0:
- 位置 ( pos = 1 ) 的位置编码:
P 1 = [ 0.8415 , 0.5403 , 0.0100 , 0.9999 ] P_1 = [0.8415, 0.5403, 0.0100, 0.9999] P1=[0.8415,0.5403,0.0100,0.9999]
4.4 位置编码矩阵
将每个位置的位置编码组合起来,得到位置编码矩阵
P
P
P:
P
=
[
0
1
0
1
0.8415
0.5403
0.0100
0.9999
]
P = \begin{bmatrix} 0 & 1 & 0 & 1 \\ 0.8415 & 0.5403 & 0.0100 & 0.9999 \\ \end{bmatrix}
P=[00.841510.540300.010010.9999]
最终,将会把位置编码矩阵 P P P直接加到生成的词向量矩阵上。
5. 相对位置信息
前文我们说过:位置编码的重要性质之一是能捕获输入的相对位置信息,那正弦和余弦编码是如何获得相对位置信息的呢?
假设我们有两个位置 p o s pos pos和 p o s + k pos + k pos+k,它们的位置编码分别为 P p o s P_{pos} Ppos 和 P p o s + k P_{pos + k} Ppos+k。我们需要分析它们之间的关系。
正弦和余弦函数是周期函数,具有以下性质:
sin
(
a
+
b
)
=
sin
a
cos
b
+
cos
a
sin
b
\sin(a + b) = \sin a \cos b + \cos a \sin b
sin(a+b)=sinacosb+cosasinb
cos
(
a
+
b
)
=
cos
a
cos
b
−
sin
a
sin
b
\cos(a + b) = \cos a \cos b - \sin a \sin b
cos(a+b)=cosacosb−sinasinb
根据以上三角函数和差化积关系,容易得出:
-
对于偶数维度( 2 i 2i 2i):
P p o s + k , 2 i = P p o s , 2 i P k , 2 i + 1 + P p o s , 2 i + 1 P k , 2 i P_{pos + k, 2i} = P_{pos, 2i} P_{k, 2i+1} + P_{pos, 2i+1} P_{k, 2i} Ppos+k,2i=Ppos,2iPk,2i+1+Ppos,2i+1Pk,2i
-
对于奇数维度( 2 i + 1 2i+1 2i+1):
P p o s + k , 2 i = P p o s , 2 i + 1 P k , 2 i + 1 − P p o s , 2 i P k , 2 i P_{pos + k, 2i} = P_{pos, 2i+1} P_{k, 2i+1} - P_{pos, 2i} P_{k, 2i} Ppos+k,2i=Ppos,2i+1Pk,2i+1−Ppos,2iPk,2i
6. 改进的位置编码方式——RoPE
RoPE(Rotary Positional Encoding)是一种创新的位置编码方法,旨在改进Transformer模型中位置信息的处理方式。与传统的绝对位置编码不同,RoPE通过旋转矩阵的方式将位置信息直接嵌入到词向量的计算过程中,特别是在自注意力机制中。
6.1 RoPE的核心思想
RoPE的基本思想是通过在计算Query和Key向量时动态地应用旋转矩阵来编码位置信息。这种方法允许模型不仅能够捕捉单词间的相对位置关系,还能保持对长距离依赖的有效建模能力。
具体来说,对于一个给定的位置 p o s pos pos和维度 i i i,RoPE会生成一个旋转角度 θ p o s , i \theta_{pos,i} θpos,i,然后使用这个角度对Query和Key向量进行旋转操作。旋转操作可以通过以下公式描述:
-
对于偶数维度 i i i:
Q p o s , i ′ = Q p o s , i ⋅ cos ( θ p o s , i ) − Q p o s , i + 1 ⋅ sin ( θ p o s , i ) Q'_{pos,i} = Q_{pos,i} \cdot \cos(\theta_{pos,i}) - Q_{pos,i+1} \cdot \sin(\theta_{pos,i}) Qpos,i′=Qpos,i⋅cos(θpos,i)−Qpos,i+1⋅sin(θpos,i)K p o s , i ′ = K p o s , i ⋅ cos ( θ p o s , i ) − K p o s , i + 1 ⋅ sin ( θ p o s , i ) K'_{pos,i} = K_{pos,i} \cdot \cos(\theta_{pos,i}) - K_{pos,i+1} \cdot \sin(\theta_{pos,i}) Kpos,i′=Kpos,i⋅cos(θpos,i)−Kpos,i+1⋅sin(θpos,i)
-
对于奇数维度(i):
Q p o s , i ′ = Q p o s , i ⋅ cos ( θ p o s , i − 1 ) + Q p o s , i − 1 ⋅ sin ( θ p o s , i − 1 ) Q'_{pos,i} = Q_{pos,i} \cdot \cos(\theta_{pos,i-1}) + Q_{pos,i-1} \cdot \sin(\theta_{pos,i-1}) Qpos,i′=Qpos,i⋅cos(θpos,i−1)+Qpos,i−1⋅sin(θpos,i−1)
K p o s , i ′ = K p o s , i ⋅ cos ( θ p o s , i − 1 ) + K p o s , i − 1 ⋅ sin ( θ p o s , i − 1 ) K'_{pos,i} = K_{pos,i} \cdot \cos(\theta_{pos,i-1}) + K_{pos,i-1} \cdot \sin(\theta_{pos,i-1}) Kpos,i′=Kpos,i⋅cos(θpos,i−1)+Kpos,i−1⋅sin(θpos,i−1)
这里的 θ p o s , i \theta_{pos,i} θpos,i通常基于一些预定义的规则来确定,例如 θ p o s , i = p o s / 1000 0 2 i / d m o d e l \theta_{pos,i} = pos / 10000^{2i/d_{model}} θpos,i=pos/100002i/dmodel。
6.2 RoPE的优势
-
相对位置感知:由于RoPE直接在计算Query和Key的过程中融入了位置信息,这使得它天然支持对相对位置的感知,这对于理解句子结构非常关键。
-
简化模型设计:RoPE不需要额外的位置编码向量加到输入嵌入上,简化了模型的设计和实现。
-
增强表达能力:通过旋转矩阵的方式,RoPE能够在不同的维度上灵活地表示位置信息,从而增强了模型的整体表达能力。
-
适应性更强:RoPE可以自然地扩展到更长的序列长度,因为它不依赖于固定的最大序列长度,而是根据实际位置动态调整旋转角度。
RoPE提供了一种新颖且有效的方法来编码位置信息,尤其适用于需要精确捕捉元素间相对位置关系的任务。它的灵活性和有效性使其成为Transformer架构中的一个重要改进方向。
7. 总结
位置编码为Transformer模型提供序列中元素的位置信息,弥补自注意力机制的排列不变性。它通过引入位置信息,使模型能够区分不同位置的词,并捕捉相对位置关系。位置编码的设计需满足唯一性、连续性、可扩展性和计算效率等条件,以确保模型能够有效处理序列数据并提升性能。
本文详细介绍了常用的位置编码方式——正弦和余弦位置编码,并通过实例说明其应用方式。最后也介绍了改进的位置编码方式——RoPE。