上一篇 | 下一篇 |
---|---|
注意力机制(2/4集) | 注意力机制(4/4集) |
位置编码
R N N RNN RNN 和 L S T M LSTM LSTM 这些网络都是串行执行的,在潜移默化中,就包含了顺序关系,也就是词序关系。而注意力机制是并行的,词与词之间不存在顺序关系,比如说输入 [ A , B , C ] [A, B, C] [A,B,C] 和 [ B , A , C ] [B, A, C] [B,A,C] 会被视为相似,那么这样就会丢失词序信息,所以需要在执行注意力机制之前,先进行位置编码,这样就包含了词序关系。
①位置编码向量添加方式
原本的自注意力机制的输入,就是词向量,但是对其位置编码后,会在原来的词向量上再加一个位置向量变成一个新的向量(对应元素相加),里面包含了当前词向量和其他的词向量之间的位置关系。并且位置向量的维度和词向量的维度是一致的。
②位置编码公式
编码方式其实有多种,不过 T r a n s f o r m e r Transformer Transformer 中用的就是下方这种(效果不是最好的,只是当时正好想到了这种方式)。
T r a n s f o r m e r Transformer Transformer 论文中使用的 正弦-余弦位置编码 是固定编码(不可训练),其公式如下:
P E ( p o s , 2 i ) = s i n ( p o s 1000 0 2 i d m o d e l ) P E ( p o s , 2 i + 1 ) = c o s ( p o s 1000 0 2 i d m o d e l ) \Large PE_{(pos,2i)}=sin(\frac{pos}{10000^{\frac{2i}{d_{model}}}})\\ \Large PE_{(pos,2i+1)}=cos(\frac{pos}{10000^{\frac{2i}{d_{model}}}}) PE(pos,2i)=sin(10000dmodel2ipos)PE(pos,2i+1)=cos(10000dmodel2ipos)
其中, p o s pos pos 表示元素在序列中的位置( 0 ≤ p o s ≤ 单词个数 0 ≤ pos ≤ 单词个数 0≤pos≤单词个数)。 i i i 表示编码向量的维度索引( 0 ≤ i < d m o d e l 2 0 ≤ i < \frac{d_{model}}{2} 0≤i<2dmodel)。 d m o d e l d_{model} dmodel 表示模型隐藏层维度(如 512 512 512 )。
上述公式表明:偶数位置的元素值使用 s i n sin sin 函数计算,奇数位置的元素值使用 c o s cos cos 函数计算(偶 s i n sin sin 奇 c o s cos cos )。
③举例帮助理解
举个例子帮助理解上述参数的含义:假设一句话中有 m m m 个单词,则有 0 ≤ p o s ≤ m 0 ≤ pos ≤ m 0≤pos≤m ,每个单词的位置编码向量的维度为 d m o d e l d_{model} dmodel (就是这个向量中的元素个数),比如说第 p o s pos pos 个单词的位置编码向量为 [ 0.841 , 0.540 , 0.860 , 0.509 , . . . ] [0.841, 0.540, 0.860, 0.509, ...] [0.841,0.540,0.860,0.509,...] 。
现在我们来更具体化这个例子:
令 m = 3 m=3 m=3 ,假设句子为 “ I l o v e y o u I~love~you I love you”,包含 3 3 3 个单词(对应于位置 p o s = 0 , 1 , 2 pos=0,1,2 pos=0,1,2 ),模型维度 d m o d e l = 512 d_{model}=512 dmodel=512。则第 p o s pos pos 个单词的位置编码向量中第 2 i 2i 2i 和第 2 i + 1 2i+1 2i+1 个元素的值应按照如下公式计算:
P E ( p o s , 2 i ) = s i n ( p o s 1000 0 2 i 512 ) P E ( p o s , 2 i + 1 ) = c o s ( p o s 1000 0 2 i 512 ) \Large PE_{(pos,2i)}=sin(\frac{pos}{10000^{\frac{2i}{512}}})\\ \Large PE_{(pos,2i+1)}=cos(\frac{pos}{10000^{\frac{2i}{512}}}) PE(pos,2i)=sin(100005122ipos)PE(pos,2i+1)=cos(100005122ipos)
则有:
单词位置(pos) | 维度0 | 维度1 | 维度2 | 维度3 | …(共512维) |
---|---|---|---|---|---|
pos=0 (“I”) | 0.000 | 1.000 | 0.000 | 1.000 | … |
pos=1 (“love”) | 0.841 | 0.540 | 0.860 | 0.509 | … |
pos=2 (“deep”) | 0.909 | -0.416 | 0.876 | -0.481 | … |
- 高频维度(如维度 512 的最后几维)的值接近微小波动,但对模型仍携带位置信息。
- 单词很多时,其中更远的位置(例如
pos=100
)会因为频率放大分母,使得大幅衰减为小值。
测试代码:
import math
import numpy as npdef get_div_term(d_model):div_term = np.exp(np.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))return div_term # 形状: [256]if __name__ == '__main__':np.set_printoptions(suppress=True)# 更改下面的 0 为其他的 posprint(np.sin(0 / get_div_term(512))) # 输出向量中偶数位置元素print(np.cos(0 / get_div_term(512))) # 输出向量中奇数位置元素
④为什么位置编码要这样编码
下面只做简单解释,详细的原因可以问 d e e p s e e k deepseek deepseek
按顺序推导,请耐心观看。
(里面为什么是除以 1000 0 2 i / 512 10000^{2i/512} 100002i/512 不用管,知道宏观公式的原因就行)
灵感来源:三角函数的和差化积公式:
s i n ( α + β ) = s i n α ⋅ c o s β + c o s α ⋅ s i n β c o s ( α + β ) = c o s α ⋅ c o s β − s i n α ⋅ s i n β sin(\alpha+\beta)=sin\alpha·cos\beta+cos\alpha·sin\beta\\ cos(\alpha+\beta)=cos\alpha·cos\beta-sin\alpha·sin\beta sin(α+β)=sinα⋅cosβ+cosα⋅sinβcos(α+β)=cosα⋅cosβ−sinα⋅sinβ
则第 p o s + k pos+k pos+k 个位置的单词的位置编码向量 P E ( p o s + k ) PE_{(pos+k)} PE(pos+k) 中的偶数和奇数位置的元素可被表示为:
偶数位: P E ( p o s + k , 2 i ) = sin ( p o s + k 1000 0 2 i 512 ) = sin ( p o s 1000 0 2 i 512 + k 1000 0 2 i 512 ) = sin ( p o s 1000 0 2 i 512 ) ⋅ cos ( k 1000 0 2 i 512 ) + cos ( p o s 1000 0 2 i 512 ) ⋅ sin ( k 1000 0 2 i 512 ) = P E ( p o s , 2 i ) ⋅ P E ( k , 2 i + 1 ) + P E ( p o s , 2 i + 1 ) ⋅ P E ( k , 2 i ) 奇数位: P E ( p o s + k , 2 i + 1 ) = cos ( p o s + k 1000 0 2 i 512 ) = cos ( p o s 1000 0 2 i 512 + k 1000 0 2 i 512 ) = cos ( p o s 1000 0 2 i 512 ) ⋅ cos ( k 1000 0 2 i 512 ) − sin ( p o s 1000 0 2 i 512 ) ⋅ sin ( k 1000 0 2 i 512 ) = P E ( p o s , 2 i + 1 ) ⋅ P E ( k , 2 i + 1 ) − P E ( p o s , 2 i ) ⋅ P E ( k , 2 i ) \begin{aligned} \text{偶数位:} & \\ PE_{(pos+k,2i)} &= \sin\left(\frac{pos+k}{10000^{\frac{2i}{512}}}\right) = \sin\left(\frac{pos}{10000^{\frac{2i}{512}}} + \frac{k}{10000^{\frac{2i}{512}}}\right) \\ &= \sin\left(\frac{pos}{10000^{\frac{2i}{512}}}\right) \cdot \cos\left(\frac{k}{10000^{\frac{2i}{512}}}\right) + \cos\left(\frac{pos}{10000^{\frac{2i}{512}}}\right) \cdot \sin\left(\frac{k}{10000^{\frac{2i}{512}}}\right) \\ &= PE_{(pos,2i)} \cdot PE_{(k,2i+1)} + PE_{(pos,2i+1)} \cdot PE_{(k,2i)} \\ \hline\\ \text{奇数位:} & \\ PE_{(pos+k,2i+1)} &= \cos\left(\frac{pos+k}{10000^{\frac{2i}{512}}}\right) = \cos\left(\frac{pos}{10000^{\frac{2i}{512}}} + \frac{k}{10000^{\frac{2i}{512}}}\right) \\ &= \cos\left(\frac{pos}{10000^{\frac{2i}{512}}}\right) \cdot \cos\left(\frac{k}{10000^{\frac{2i}{512}}}\right) - \sin\left(\frac{pos}{10000^{\frac{2i}{512}}}\right) \cdot \sin\left(\frac{k}{10000^{\frac{2i}{512}}}\right) \\ &= PE_{(pos,2i+1)} \cdot PE_{(k,2i+1)} - PE_{(pos,2i)} \cdot PE_{(k,2i)} \end{aligned} 偶数位:PE(pos+k,2i)奇数位:PE(pos+k,2i+1)=sin(100005122ipos+k)=sin(100005122ipos+100005122ik)=sin(100005122ipos)⋅cos(100005122ik)+cos(100005122ipos)⋅sin(100005122ik)=PE(pos,2i)⋅PE(k,2i+1)+PE(pos,2i+1)⋅PE(k,2i)=cos(100005122ipos+k)=cos(100005122ipos+100005122ik)=cos(100005122ipos)⋅cos(100005122ik)−sin(100005122ipos)⋅sin(100005122ik)=PE(pos,2i+1)⋅PE(k,2i+1)−PE(pos,2i)⋅PE(k,2i)
可以看出,对于 p o s + k pos+k pos+k 位置的位置向量中的 2 i 2i 2i 或 2 i + 1 2i+1 2i+1 维而言,可以被表示为: p o s pos pos 位置与 k k k 位置的位置向量中的 2 i 2i 2i 与 2 i + 1 2i+1 2i+1 维的线性组合,这样的线性组合意味着位置向量中蕴含了相对位置信息(也就是说:第 p o s + k pos+k pos+k 个单词和第 p o s pos pos 、第 k k k 个单词有关联)。
将上述推导过程中的 P E ( k , 2 i ) PE_{(k,2i)} PE(k,2i) 和 P E ( k , 2 i + 1 ) PE_{(k,2i+1)} PE(k,2i+1) 看成是 W k W_k Wk ,那么上述公式就可以理解成:模型可以通过权重 W k W_k Wk 来掌握第 p o s pos pos 个单词和距离它 k k k 个单位远的单词之间的关联。
⑤这种编码方式的优缺点
-
优点:
- 泛化性强:支持处理比训练时更长的序列(外推能力)。
- 计算高效:无需额外训练参数。
-
缺点:这是一种不可学习,即给出即固定的编码方式,灵活性低。
后续的 GPT 等模型大部分都采用了可学习的编码方式。