transformer-位置编码: Position Embedding

位置编码: Position Embedding

一: 作用

Position Embeddings(位置嵌入)用于在神经网络中为输入序列中的每个位置编码位置信息,以便模型能够识别输入元素的顺序,从而帮助模型更好地理解数据结构。

例如,在 Transformer 模型中,输入数据通常是一个无序的序列。即使一个句子被分割成一系列单词或词向量后,模型无法直接感知这些词的顺序关系。而序列中的位置顺序可能对任务(如翻译、文本生成或分类)至关重要。因此,需要通过 位置嵌入 将位置信息显式地融入模型的输入中。

位置编码能够提供一种不依赖序列长度的通用机制,使模型不仅能够理解绝对位置信息,还能捕获序列中元素的相对关系,从而提高序列处理能力。


二: Transformer中的位置编码

1. 直观思考

要想给输入的向量添加位置信息, 最先想到的无疑就是直接使用 1 , 2 , 3 , . . . , n 1, 2, 3,...,n 1,2,3,...,n这样的连续数字给输入向量赋予标号来表达向量的顺序。

但是这样会导致一些问题:

  1. 外推能力不足:如果模型没有见过比训练时更大的整数标记(如 101 、 102 101、102 101102 等),它很难推断这些数字与已知位置标记(如 $1 $到 100 100 100)之间的关系。
  2. 数值不稳定:当序列长度不断增大, 其位置标记也会越来越大, 可能导致数值不稳定,不利于训练。

因此,进一步可以想到将位置标记进行归一化,其中 0 0 0表示第一个单词, 1 1 1表示最后一个单词。这样可以解决上述两个问题,但这样又会导致新的问题:

  1. 难以捕捉相对位置关系:例如,标记值 0.5 0.5 0.5 在长度为 10 10 10 的序列中表示第 5 5 5 个位置,但在长度为 100 100 100 的序列中表示第 50 50 50 个位置。
  2. 相邻位置差异过小:如果序列长度较长, [ 0 , 1 ] [0,1] [0,1] 范围内的连续分布可能导致相邻位置之间的差异非常小。

因此,我们需要一种编码方式满足以下条件:

  1. 唯一性:每个位置的编码值应该唯一。
  2. 一致性:序列长度不同时,其中每个 token 间的相对位置保持一致。
  3. 有界性:编码范围有界,不会随序列长度增大而无限增大。

2. 正弦和余弦函数编码方案

Transformer 中提出了一种基于正弦和余弦函数的绝对位置编码方法,公式如下:
P E t ( i ) = { sin ⁡ ( w i t ) , i f i = 2 k cos ⁡ ( w i t ) , i f i = 2 k + 1 PE_t^{(i)}=\left\{ \begin{matrix} \sin(w_it),\quad if\quad i=2k\quad\quad \\ \cos(w_it),\quad if\quad i=2k+1 \end{matrix} \right. PEt(i)={sin(wit),ifi=2kcos(wit),ifi=2k+1
其中:

  • w k = 1 1000 0 2 k / d w_k = \frac{1}{10000^{2k/d}} wk=100002k/d1, k = 0 , 1 , . . . , d 2 − 1 k=0,1,...,\frac{d}{2}-1 k=0,1,...,2d1
  • d d d 是编码的维度,且是偶数。

对于上面的公式, 我们可以先单独看 sin ⁡ ( w k t ) \sin(w_kt) sin(wkt), 即输入向量中第 t 个 token 的位置编码为:
P E t = [ sin ⁡ ( 1 2 0 t ) , sin ⁡ ( 1 2 1 t ) , . . . , sin ⁡ ( 1 2 d − 1 t ) ] \begin{aligned} &PE_t = [\sin(\frac{1}{2^0}t), \sin(\frac{1}{2^1}t),...,\sin(\frac{1}{2^{d-1}}t)]\\ \end{aligned} PEt=[sin(201t),sin(211t),...,sin(2d11t)]

第 t+1 个 token 的位置编码为:
KaTeX parse error: Expected 'EOF', got '&' at position 2: &̲PE_{t+1}=[\sin(…

对于上面的式子, 进行分析:

  1. 随着位置编码维度增大, sin 函数的频率在减小, 波长变长. 即位置编码左部对 t 敏感, 右部对 t 不敏感
  2. 随着 t 从小到大, 位置编码左边低位变化大, 右边高位变化小. 与二进制编码类似.
    • 二进制编码左边变化大, 右边变化小
  3. 位置编码中低维度的位置会随着 t 的变化而快速变化,这种快速变化能够捕捉到序列中相邻位置的细微差异,即局部信息
  4. 位置编码中高维度的位置会随着 t 的变化而缓慢变化,这种缓慢变化能够捕捉到序列中远距离位置的全局关系。

在这里插入图片描述

由于 sin ⁡ \sin sin是周期函数,因此从纵向来看,如果函数的频率偏大,引起波长偏短,则不同 t t t下的位置向量可能出现几乎重合的情况。因此在Transformer的论文中, 将频率设置为一个非常小的值来确保不同 t t t下的位置向量不会出现重合: w k = 1 1000 0 2 k / d w_k = \frac{1}{10000^{2k/d}} wk=100002k/d1

sin ⁡ \sin sin函数解决了:

  1. 唯一性 t t t不同, 位置编码不同。
  2. 一致性:输入向量的长度不同时, 其分量的相对位置一致(由维度 i i i确定)。
  3. 有界性 sin ⁡ \sin sin函数取值为 [ − 1 , 1 ] [-1,1] [1,1],编码范围有界。

进一步, 我们想要要求: 不同的位置向量是可以通过线性转换得到的, 即:
P E t + Δ t = T Δ t ∗ P E t PE_{t+\Delta t} = T_{\Delta t} * PE_t PEt+Δt=TΔtPEt
其中 T T T表示一个线性变换矩阵。观察这个式子,联想到在向量空间中一种常用的线形变换——旋转。在这里,我们将 t t t想象为一个角度,那么 Δ t \Delta t Δt就是其旋转的角度,则上面的式子可以进一步写成:

在这里插入图片描述

然后我们就可以改写全是 sin ⁡ \sin sin函数的位置编码公式, 使得位置编码的维度两两一组, 用 sin ⁡ \sin sin cos ⁡ \cos cos表示:
P E t = [ sin ⁡ ( 1 2 0 t ) , cos ⁡ ( 1 2 1 t ) , . . . , cos ⁡ ( 1 2 d − 2 t ) ] , sin ⁡ ( 1 2 d − 1 t ) ] P E t + 1 = [ sin ⁡ ( 1 2 0 ( t + 1 ) ) , cos ⁡ ( 1 2 1 ( t + 1 ) ) , . . . , cos ⁡ ( 1 2 d − 2 ( t + 1 ) ) ] , sin ⁡ ( 1 2 d − 1 ( t + 1 ) ) ] \begin{aligned} &PE_t = [\sin(\frac{1}{2^0}t), \cos(\frac{1}{2^1}t),...,\cos(\frac{1}{2^{d-2}}t)],\sin(\frac{1}{2^{d-1}}t)]\\ &PE_{t+1}=[\sin(\frac{1}{2^0}(t+1)), \cos(\frac{1}{2^1}(t+1)),...,\cos(\frac{1}{2^{d-2}}(t+1))],\sin(\frac{1}{2^{d-1}}(t+1))]\\ \end{aligned} PEt=[sin(201t),cos(211t),...,cos(2d21t)],sin(2d11t)]PEt+1=[sin(201(t+1)),cos(211(t+1)),...,cos(2d21(t+1))],sin(2d11(t+1))]
所以也就可以得到:

P E t + △ t = T △ t ∗ P E t = ( [ c o s ( w 0 △ t ) s i n ( w 0 △ t ) − s i n ( w 0 △ t ) c o s ( w 0 △ t ) ] ⋯ 0 ⋯ ⋯ ⋯ 0 ⋯ [ c o s ( w d 2 − 1 △ t ) s i n ( w d 2 − 1 △ t ) − s i n ( w d 2 − 1 △ t ) c o s ( w d 2 − 1 △ t ) ] ) ( s i n ( w 0 t ) c o s ( w 0 t ) ⋯ s i n ( w d 2 − 1 t ) c o s ( w d 2 − 1 t ) ) = ( s i n ( w 0 ( t + △ t ) ) c o s ( w 0 ( t + △ t ) ) ⋯ s i n ( w d 2 − 1 ( t + △ t ) ) c o s ( w d 2 − 1 ( t + △ t ) ) ) \begin{aligned} PE_{t+\triangle t}&=T_{\triangle t}*PE_t \\ &= \begin{pmatrix} \begin{bmatrix} cos(w_0\bigtriangleup t) & sin(w_0\bigtriangleup t) \\ -sin(w_0\bigtriangleup t) & cos(w_0\bigtriangleup t) \end{bmatrix} & \cdots & 0 \\ \cdots & \cdots & \cdots \\ 0 & \cdots & \begin{bmatrix} cos(w_{\frac{d}{2}-1}\bigtriangleup t) & sin(w_{\frac{d}{2}-1}\bigtriangleup t) \\ -sin(w_{\frac{d}{2}-1}\bigtriangleup t) & cos(w_{\frac{d}{2}-1}\bigtriangleup t) \end{bmatrix} \end{pmatrix} \begin{pmatrix} sin(w_0t) \\ cos(w_0t) \\ \cdots \\ sin(w_{\frac{d}{2}-1}t) \\ cos(w_{\frac{d}{2}-1}t) \end{pmatrix} \\ &= \begin{pmatrix} sin(w_0(t+\triangle t)) \\ cos(w_0(t+\triangle t)) \\ \cdots \\ sin(w_{\frac{d}{2}-1}(t+\triangle t)) \\ cos(w_{\frac{d}{2}-1}(t+\triangle t)) \end{pmatrix} \end{aligned} PEt+t=TtPEt= [cos(w0t)sin(w0t)sin(w0t)cos(w0t)]00[cos(w2d1t)sin(w2d1t)sin(w2d1t)cos(w2d1t)] sin(w0t)cos(w0t)sin(w2d1t)cos(w2d1t) = sin(w0(t+t))cos(w0(t+t))sin(w2d1(t+t))cos(w2d1(t+t))

最终, 我们得到了Transformer中的位置编码公式:
P E t ( i ) = { sin ⁡ ( w i t ) , i f i = 2 k cos ⁡ ( w i t ) , i f i = 2 k + 1 PE_t^{(i)}=\left\{ \begin{matrix} \sin(w_it),\quad if\quad i=2k\quad\quad \\ \cos(w_it),\quad if\quad i=2k+1 \end{matrix} \right. PEt(i)={sin(wit),ifi=2kcos(wit),ifi=2k+1

3. 其他

在Transformer中, 将位置编码信息添加到词嵌入向量是以直接相加的方式:
ψ ′ ( w t ) = ψ ( w t ) + p t → \psi^{\prime}\left(\mathrm{w_t}\right)=\psi\left(\mathrm{w_t}\right)+\overrightarrow{\mathrm{p_t}} ψ(wt)=ψ(wt)+pt

  • p t → \overrightarrow{\mathrm{p_t}} pt 位置编码
  • ψ ( w t ) \psi\left(\mathrm{w_t}\right) ψ(wt) 词嵌入向量
  • 两个向量的维度需要保持一致

为什么位置编码是和词嵌入向量相加而不是拼接?

  1. 维度一致性:相加操作保持了向量的维度不变,而拼接会增加维度,导致后续计算复杂度增加。
  2. 信息融合:相加操作可以看作是将位置信息直接注入到词嵌入中,使得模型在计算注意力时能够同时考虑词义和位置信息。
  3. 简化模型:相加操作比拼接更简单,减少了模型的参数量和计算量。

在这里插入图片描述


三. 参考文章

  1. Transformer 结构详解:位置编码 | Transformer Architecture: The Positional Encoding-优快云博客
  2. Transformer学习笔记一:Positional Encoding(位置编码) - 知乎
### 什么是位置嵌入(Position Embeddings) 在机器学习和自然语言处理领域,位置嵌入是一种用于表示序列数据中每个词或标记的位置的技术。由于许多神经网络架构(如Transformer)不具有内置的时间/顺序感,因此需要显式地引入位置信息来帮助模型理解输入序列中的相对或绝对位置关系[^1]。 具体来说,位置嵌入通过向量的形式编码单词在其上下文中的位置信息,并将其与词嵌入相结合,形成最终的输入表征。这种技术对于捕捉长距离依赖性和提高模型性能至关重要。 --- ### 实现方法 #### 方法一:正弦余弦函数方式 一种常见的实现方法是采用固定的位置编码方案,其中位置嵌入由正弦和余弦函数定义: \[ PE_{(pos,2i)} = \sin\left(\frac{pos}{10000^{2i/d}}\right), \quad PE_{(pos,2i+1)} = \cos\left(\frac{pos}{10000^{2i/d}}\right) \] 这里 \( pos \) 表示位置索引,\( i \) 是维度索引,而 \( d \) 则代表嵌入向量的总维数。这种方法的优点在于其计算效率高且无需额外的学习参数。 ```python import numpy as np def get_position_encoding(seq_len, embed_dim): pe = np.zeros((seq_len, embed_dim)) position = np.arange(seq_len)[:, None] div_term = np.exp(np.arange(0, embed_dim, 2) * -(np.log(10000.0) / embed_dim)) pe[:, 0::2] = np.sin(position * div_term) pe[:, 1::2] = np.cos(position * div_term) return pe ``` 上述代码展示了如何基于正弦和余弦函数生成位置编码矩阵。 #### 方法二:可学习的位置嵌入 另一种常用的方法是将位置嵌入视为一组可学习的参数,在训练过程中与其他权重一起优化。这种方式更加灵活,能够适应特定任务的需求[^2]。 以下是使用 TensorFlow/Keras 定义可学习位置嵌入的一个简单例子: ```python import tensorflow as tf class PositionEmbedding(tf.keras.layers.Layer): def __init__(self, max_length, embed_dim): super(PositionEmbedding, self).__init__() self.pos_emb = tf.Variable( initial_value=tf.random.normal([max_length, embed_dim]), trainable=True, name="position_embedding" ) def call(self, inputs): batch_size, seq_len, _ = tf.shape(inputs) return self.pos_emb[:seq_len, :] ``` 在这个实现中,`pos_emb` 被初始化为随机值并作为模型的一部分参与反向传播过程。 --- ### 结合实际应用 当使用双向 RNN 或 Transformer 架构时,除了基本的词嵌入外还需要加入位置嵌入以保留原始序列结构的信息。例如,在给定的任务中如果要区分谓语和其他词语,则可以在相应位置上附加特殊的标志位或者调整该处的位置嵌入值[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值