文章目录
- 概述
- 绝对位置编码
- 相对位置编码
- T5 Bias
- ALiBi
- RoPE
- 参考资料
概述
相对于RNN这样的序列模型来说,Transformer可并行是一个很大的优势,但可并行性带来一个问题,由于不是从前到后,所以模型对于位置信息是不敏感的。于是在Transformer最早提出时就定义了位置编码(Positional Encodings)的概念,本文章旨在介绍常见位置编码方式。
绝对位置编码
绝对位置编码(Absolute Positional Encodings)也在早期的Transformer架构中被广泛使用。
Transformer中的位置编码如下图所示:
最初Transformer中的位置编码采用三角式绝对位置编码,计算方式如下:
P E ( k , 2 i ) = s i n ( k 1000 0 2 i / d ) PE_{(k, 2i)}=sin(\frac{k}{10000^{2i/d}}) PE(k,2i)=sin(100002i/dk)
P E ( k , 2 i + 1 ) = c o s ( k 1000 0 2 i / d ) PE_{(k, 2i+1)}=cos(\frac{k}{10000^{2i/d}}) PE(k,2i+1)=cos(100002i/dk)
其中, 2 i 2i 2i和 2 i + 1 2i+1 2i+1代表向量的第几维, k k k代表token在序列中的位置, d d d是向量的维度。
实现代码:
import torch
import math
seq_len = 128 # 序列长度
d_model = 8 # 向量维度
pe = torch.zeros(seq_len, d_model)
position = torch.arange(0, seq_len, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0).transpose(0, 1) # shape:[128, 1, 8]
相对位置编码
绝对位置编码的缺点很明显:
- 可扩展性不足,只能处理训练数据中看过的长度,如基础BERT模型只能够处理512个token,这显然是不合理的。所以越来越多的模型使用相对位置编码的方法来增加模型对于长度的泛化性。
- 每一个位置编码都是独立的。换而言之,位置编码和距离没有关系,不管距离多远都是一样的。
在正式介绍相对位置编码之前回顾一下原始论文中 self-attention的计算方式:
q i = ( x i + p i ) W q q_i=(x_i+p_i)W_q qi=(xi+pi)Wq
k j = ( x j + p j ) W k k_j=(x_j+p_j)W_k kj=(xj+pj)Wk
v k = ( x k + p k ) W v v_k=(x_k+p_k)W_v vk=(xk+pk)Wv
α i , j = s o f t m a x ( q i k j T ) \alpha_{i,j}=softmax(q_ik_j^T) αi,j=softmax(qikjT)
o i = ∑ j α i , j v j o_{i}=\sum_j\alpha_{i,j}v_j oi=∑jαi,jvj
我们把 q i q_i qi和 k j k_j kj中与位置编码的项去掉,再引入一个相对位置编码项。 对 q i k j T q_ik_j^T qikjT进行展开,得到以下等式:
q i k j T = ( x i W q ) ( x j W k ) T ⊕ R i , j q_ik_j^T=(x_iW_q)(x_jW_k)^T\oplus R_{i,j} qikjT=(xiWq)(xjWk)T⊕Ri,j
其中 R i , j R_{i,j} Ri,j代表相对位置的编码方式, ⊕ \oplus ⊕代表融入相对位置编码的方式(如:相乘或相加)。
T5 Bias
T5模型直接使用一个可学习矩阵来进行相对位置编码的学习:
q i k j T = ( x i W q ) ( x j W k ) T + β i , j q_ik_j^T=(x_iW_q)(x_jW_k)^T+ \beta_{i,j} qikjT=(xiWq)(xjWk)T+βi,j
其中 β i , j \beta_{i,j} βi,j为一个可学习的、共享的bias值,这种方法的缺点很明显,需要训练矩阵的参数,训练会变慢。
ALiBi
基于T5 bias的缺点,Press[3]提出了ALiBi算法。该方法通过预定义相对位置编码,通过在 q i k j T q_ik_j^T qikjT后增加一个偏置项来达到相对位置编码的目的:
其中 m m m是和注意力头有关的斜率,对于 n n n个注意力头,其斜率集合从 2 − 8 n 2^{\frac{-8}{n}} 2n−8开始,计算示例如下图所示:
从原始论文可以看到,对比T5 Bias,输入长度越长优势明显。
RoPE
RoPE[5]提出的出发点是“通过绝对位置编码的方式实现相对位置编码”。如前所述,self-attention核心是计算是内积,所以问题就变成了内积前带有绝对位置编码信息,内积之后带有相对位置编码信息。假设存在这样的位置函数记为 f ( x , m ) f(\boldsymbol {x},m) f(x,m),则有:
⟨ f ( q , m ) , f ( k , m ) ⟩ = g ( q , k , m − n ) \langle{f(\boldsymbol{q},m), f(\boldsymbol {k},m)}\rangle=g(\boldsymbol {q},\boldsymbol {k}, m-n) ⟨f(q,m),f(k,m)⟩=g(q,k,m−n)
所以现在的目标变成了找到这样一个函数使得上述等式成立。论文就提出了一个可以满足上述等式的函数。为了简化推理,我们假设向量的维度为2,对于第 m m m个位置的token,对应的旋转矩阵为:
R θ , m = ( cos m θ − sin m θ sin m θ cos m θ ) R_{\theta,m}=\begin{pmatrix} \cos{m\theta} & -\sin{m\theta} \\ \sin{m\theta} & \cos{m\theta} \end {pmatrix} Rθ,m=(cosmθsinmθ−sinmθcosmθ)
其中 θ \theta θ是一个预设值,经过旋转之后的矩阵我们可以写成:
q ′ = R θ , m q m = ( cos m θ − sin m θ sin m θ cos m θ ) ( q m ( 1 ) q m ( 2 ) ) q' = R_{\theta,m}q_m=\begin{pmatrix} \cos{m\theta} & -\sin{m\theta} \\ \sin{m\theta} & \cos{m\theta} \end {pmatrix} \begin{pmatrix} q_m^{(1)} \\ q_m^{(2)} \end {pmatrix} q′=Rθ,mqm=(cosmθsinmθ−sinmθcosmθ)(qm(1)qm(2))
对于二维向量,原始论文给出的RoPE实现过程如下图所示:
RoPE一般性实现如下:
q m T k n = ( R θ , m q m ) T ( R θ , n k n ) = q T R θ , m T R θ , n k n ( R θ , m T R θ , n = R θ , n − m ) = q T R ( θ , n − m ) k n \begin{align} q_m^Tk_n &= (R_{\theta,m}q_m)^T(R_{\theta,n}k_n) \\ &=q^TR_{\theta,m}^TR_{\theta,n}k_n \quad\quad\quad(R_{\theta,m}^TR_{\theta,n}=R_{\theta, n-m})\\ &=q^TR_{(\theta,n-m)}k_n \end{align} qmTkn=(Rθ,mqm)T(Rθ,nkn)=qTRθ,mTRθ,nkn(Rθ,mTRθ,n=Rθ,n−m)=qTR(θ,n−m)kn
可以看出RoPE巧妙地结合了绝对位置编码和相对位置编码:
● θ \theta θ只与token当前的位置有关,且对于所有token都是一样的
● n − m n-m n−m和两个token之间的距离有关,token距离越小,旋转的角度越小,反之,旋转角度越大。
RoPE实现代码[8]如下:
import torchfrom torch import nndef get_rotary_matrix(context_len: int, embedding_dim: int) -> torch.Tensor:"""Generate the Rotary Matrix for ROPEArgs:context_len (int): context lenembedding_dim (int): embedding dimReturns:torch.Tensor: the rotary matrix of dimension context_len x embedding_dim x embedding_dim"""R = torch.zeros((context_len, embedding_dim, embedding_dim), requires_grad=False)positions = torch.arange(1, context_len+1).unsqueeze(1)# Create matrix theta (shape: context_len x embedding_dim // 2)slice_i = torch.arange(0, embedding_dim // 2)# 原始论文旋转角度计算方式theta = 10000. ** (-2.0 * (slice_i.float()) / embedding_dim) m_theta = positions * theta# Create sin and cos valuescos_values = torch.cos(m_theta)sin_values = torch.sin(m_theta)# 按照公式进行赋值R[:, 2*slice_i, 2*slice_i] = cos_valuesR[:, 2*slice_i, 2*slice_i+1] = -sin_valuesR[:, 2*slice_i+1, 2*slice_i] = sin_valuesR[:, 2*slice_i+1, 2*slice_i+1] = cos_valuesreturn Rbatch_size = 1
context_len = 8
embedding_dim = 2
ff_q = nn.Linear(embedding_dim, embedding_dim, bias=False)
ff_k = nn.Linear(embedding_dim, embedding_dim, bias=False)
x = torch.randn((batch_size, context_len, embedding_dim))# 初始化矩阵:Q、K
queries = ff_q(x)
keys = ff_k(x)# 获取旋转矩阵
R_matrix = get_rotary_matrix(context_len, embedding_dim)queries_rot = (queries.transpose(0,1) @ R_matrix).transpose(0,1)
keys_rot = (keys.transpose(0,1) @ R_matrix).transpose(0,1)# ... Compute the score in the attention mechanism using the rotated queries and keys
目前RoPE技术已经在LLAMA系列、GLM系列、Baichuan系列、Qwen系列等广泛使用。
参考资料
- Zhao et al. Length Extrapolation of Transformers: A Survey from the Perspective of Positional Encoding.
- Su etal. ROFORMER: ENHANCED TRANSFORMER WITH ROTARY POSITION EMBEDDING.
- 科学空间《让研究人员绞尽脑汁的Transformer位置编码》
- Press et al. Train short, test long: Attention with linear biases enables input length extrapolation.
- Transformer升级之路:2、博采众长的旋转式位置编码
- Su et al. RoFormer: Enhanced Transformer with Rotary Position Embedding
- https://github.com/meta-llama/llama3/blob/main/llama/model.py
- https://afterhoursresearch.hashnode.dev/rope-rotary-positional-embedding