拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Transformer位置编码:从正弦函数到RoPE,让模型理解序列顺序

1. 从“词袋”到“序列”位置编码的缘起与核心挑战如果你尝试过用早期的神经网络处理文本比如用全连接网络或简单的词袋模型去做情感分析可能会觉得效果还行。但当你把任务换成机器翻译、文本摘要或者代码生成时问题立刻就暴露了模型根本不知道词语的顺序。给它“猫追老鼠”和“老鼠追猫”它可能认为这两句话表达的意思是一样的因为模型看到的只是“猫”、“追”、“老鼠”这三个词的集合。这就是自然语言处理NLP早期面临的核心困境——模型缺乏对序列顺序的感知能力。循环神经网络RNN及其变体LSTM、GRU的出现一度被认为是解决序列建模的“银弹”。它们通过隐状态在时间步上的传递理论上能够捕捉序列的先后关系。然而在实际的大规模训练中RNN的串行计算特性导致了严重的效率瓶颈并且长距离依赖的捕捉能力依然有限梯度消失或爆炸的问题始终如影随形。卷积神经网络CNN通过滑动窗口也能捕捉局部顺序但其感受野受限于卷积核大小对于长序列的全局依赖建模能力不足。Transformer架构的横空出世彻底改变了游戏规则。它摒弃了RNN的循环结构完全依赖自注意力Self-Attention机制来建立序列中任意两个元素之间的关联。自注意力机制的计算是“并行”且“无序”的给定一个句子模型会同时计算所有词与所有词之间的注意力权重。这带来了前所未有的计算效率和强大的全局建模能力但也引入了一个根本性问题自注意力机制本身是置换等变的Permutation Equivariant。简单来说如果你把输入序列的词序完全打乱自注意力层输出的结果其内部顺序也会相应打乱但每个位置上的向量表示其“内容”只与打乱后的输入集合有关而与原始顺序无关。模型丢失了至关重要的位置信息。这就好比在一个会议上大家只根据彼此的身份词向量进行交流和形成共识但完全忘记了谁先发言、谁后发言。会议的最终结论模型输出可能只取决于参与者的身份集合而非他们发言的次序这显然无法准确理解一段有序的叙述或指令。因此我们必须人为地将“位置信息”注入到模型的输入中这就是位置编码Positional Encoding, PE最根本的动机在保持自注意力并行计算优势的同时让模型能够利用序列的顺序信息。那么如何注入位置信息呢最直观的想法可能是直接给每个位置分配一个独立的、可学习的向量即“可学习的位置编码”。这确实是一种常用且有效的方法尤其是在预训练数据充足、序列长度固定的场景下如BERT。然而Transformer原论文的作者们选择了一条更优雅、更具理论深度的路径使用正弦和余弦函数来构造位置编码。这个选择背后蕴含着对模型泛化能力、计算效率和多维度关系编码的深刻考量。2. 正弦与余弦为什么是它们而不是别的当我们决定要编码位置时首先要确定编码的形式。我们需要一个函数 $f: \mathbb{N} \to \mathbb{R}^d$将位置索引 $pos$一个整数映射到一个 $d$ 维的实数向量这个向量最终会与词嵌入向量相加作为Transformer的输入。2.1 核心设计目标与正弦函数的契合度作者的设计目标非常明确他们希望位置编码能满足以下几个关键性质唯一性每个位置必须有唯一的编码。相对位置关系的可表达性模型应能轻易地学习到位置之间的相对关系例如“位置5”和“位置6”的关联应该与“位置20”和“位置21”的关联具有某种相似的模式。这对于理解语言中的顺序至关重要。长度外推性模型在训练时可能只见过一定长度如512的序列但我们希望它能够处理更长的序列。因此位置编码应该能自然地泛化到训练时未见过的位置。有界性且平滑编码值应该有界避免在叠加到词嵌入后造成数值不稳定。同时相邻位置的编码变化应该是平滑的这有助于模型的优化。现在让我们看看正弦和余弦函数是如何完美契合这些目标的。唯一性与周期性正弦函数 $sin(x)$ 本身是周期性的单独使用确实会导致不同位置编码相同。但Transformer使用的是一组频率不同的正弦和余弦函数的组合。具体公式如下$$ PE_{(pos, 2i)} sin(pos / 10000^{2i/d_{model}}) $$ $$ PE_{(pos, 2i1)} cos(pos / 10000^{2i/d_{model}}) $$其中$pos$ 是位置索引0, 1, 2, ...。$i$ 是维度索引0, 1, ..., d_model/2 - 1它决定了该维度所使用的函数的频率。$d_{model}$ 是词嵌入的维度也是位置编码的维度。这里的关键在于对于每个维度 $i$我们使用了一个不同的频率由 $10000^{2i/d_{model}}$ 决定。频率随着维度索引 $i$ 的增加而指数级下降。这意味着在低维度$i$ 小正弦波变化非常快高频编码了细粒度的位置差异在高维度$i$ 大正弦波变化非常慢低频编码了粗粒度的、大范围的位置信息。这种从高频到低频的频谱覆盖确保了即使某个频率在某个区间内重复其他频率的波也处于不同相位从而在高维空间中每个位置的向量表示几乎是唯一的。相对位置关系的线性可表达性这是正弦位置编码最精妙之处。对于某个固定的偏移量 $k$位置 $pos k$ 的编码可以由位置 $pos$ 的编码通过一个线性变换得到。我们可以利用三角函数的和角公式 $$ sin(\alpha \beta) sin\alpha cos\beta cos\alpha sin\beta $$ $$ cos(\alpha \beta) cos\alpha cos\beta - sin\alpha sin\beta $$令 $\alpha pos \cdot \omega_i$, $\beta k \cdot \omega_i$其中 $\omega_i 1 / 10000^{2i/d_{model}}$。那么 $$ PE_{(posk, 2i)} sin((posk)\omega_i) sin(pos\omega_i)cos(k\omega_i) cos(pos\omega_i)sin(k\omega_i) $$ $$ PE_{(posk, 2i1)} cos((posk)\omega_i) cos(pos\omega_i)cos(k\omega_i) - sin(pos\omega_i)sin(k\omega_i) $$这可以写成一个矩阵乘法的形式 $$ \begin{bmatrix} PE_{(posk, 2i)} \ PE_{(posk, 2i1)} \end{bmatrix} \begin{bmatrix} cos(k\omega_i) sin(k\omega_i) \ -sin(k\omega_i) cos(k\omega_i) \end{bmatrix} \cdot \begin{bmatrix} PE_{(pos, 2i)} \ PE_{(pos, 2i1)} \end{bmatrix} $$这个变换矩阵是一个旋转矩阵它只依赖于相对距离 $k$ 和频率 $\omega_i$而与绝对位置 $pos$ 无关。这意味着模型的自注意力机制理论上可以很容易地学会通过这种固定的线性变换来关注“相对距离为k”的关系。例如要判断两个词是否是相邻关系模型可以学习一个权重模式该模式对应于 $k1$ 时的旋转矩阵所诱导的向量关系。这为模型理解“下一个词”、“前一个词”等相对位置概念提供了直接的数学基础。长度外推性由于正弦函数定义在整个实数域上我们可以为任意大的 $pos$ 计算其编码。虽然训练时模型只见过有限范围内的位置但正弦函数的连续性使得模型有机会将学到的位置关系模式泛化到更长的序列上尽管实践中直接外推效果可能并不完美但这提供了可能性。相比之下可学习的位置编码在遇到超出训练长度的位置时完全无法给出有意义的表示。有界性与平滑性正弦和余弦函数的值域天然就是 $[-1, 1]$这与经过归一化处理的词嵌入向量能够很好地相加不会引起数值爆炸。同时函数本身是无限可微的相邻位置的编码变化极其平滑这为基于梯度的优化算法提供了良好的条件。2.2 与“可学习位置编码”的对比理解了正弦编码的优势我们再回头对比“可学习位置编码”Learned Positional Embedding。后者将每个位置视为一个独立的可学习参数在训练中通过梯度下降来优化。优点极度灵活不引入任何先验假设让数据自己决定最好的位置表示。在数据充足且序列长度固定的任务上如BERT的512长度它通常能取得非常好的效果。缺点缺乏外推性模型无法处理比训练时更长的序列。除非在训练时就用更长的序列或者采用一些技巧如截断、分段。可能缺乏对相对位置的显式归纳偏置模型需要从零开始学习所有位置两两之间的关系这可能比利用正弦编码固有的相对位置线性性质需要更多的数据和计算。对训练数据中的位置分布敏感如果训练数据中某些位置出现得很少其编码可能学习不充分。在实际应用中两种方法都被广泛使用。Transformer原论文选择正弦编码很大程度上是出于其对机器翻译这种序列到序列任务中长度可变性和相对位置重要性的考量。而在像BERT这样的编码器模型中由于输入长度固定且数据量巨大可学习位置编码成为了更主流和简便的选择。3. 正弦位置编码的实践细节与可视化理解理论很优美但落到代码和实际理解上我们还需要拆解几个关键细节。3.1 维度交替与频率衰减编码空间的构建让我们仔细审视公式中的 $10000^{2i/d_{model}}$。这个项决定了频率。由于 $2i/d_{model}$ 在 $i$ 从0增长到 $d_{model}/2-1$ 时从0增长到接近1所以 $10000^{2i/d_{model}}$ 从 $10000^01$ 衰减到 $10000^{1}10000$ 的倒数即频率从 $1$ 衰减到 $1/10000$。为什么是10000这个数字是一个超参数论文中称为“波长”的调节因子。较大的基数如10000意味着频率衰减得更慢低频维度占据更多。这相当于为模型提供了更丰富的、变化缓慢的“全局位置”信号。你可以将其调小如1000或调大但10000是一个经过实验验证、在多种任务上表现良好的默认值。维度的交替公式中偶数维度$2i$用 $sin$奇数维度$2i1$用 $cos$。这种交替安排并非随意。回顾之前的线性变换推导正是这种 $sin$ 和 $cos$ 的配对使得同一频率 $\omega_i$ 下的两个维度可以组成一个二维空间中的点 $[sin(pos\cdot\omega_i), cos(pos\cdot\omega_i)]$。随着 $pos$ 增加这个点在该二维平面上做匀速圆周运动。不同频率 $\omega_i$ 对应不同转速的圆。最终的位置编码向量就是所有这些不同转速的圆周运动在 $d_{model}$ 维空间中的叠加。我们可以通过一个简单的Python可视化来感受一下import numpy as np import matplotlib.pyplot as plt def get_positional_encoding(max_len, d_model): pe np.zeros((max_len, d_model)) for pos in range(max_len): for i in range(0, d_model, 2): omega 1 / (10000 ** ((i) / d_model)) pe[pos, i] np.sin(pos * omega) pe[pos, i1] np.cos(pos * omega) return pe # 假设词向量维度是64看前128个位置 d_model 64 max_len 128 pe get_positional_encoding(max_len, d_model) # 可视化前几个维度的波形 plt.figure(figsize(12, 8)) for i in range(6): # 看前3对6个维度 plt.subplot(3, 2, i1) plt.plot(pe[:100, i]) plt.title(fDimension {i} ({sin if i%20 else cos})) plt.xlabel(Position) plt.ylabel(Encoding Value) plt.tight_layout() plt.show() # 热力图观察整个编码矩阵 plt.figure(figsize(10, 8)) plt.imshow(pe.T, aspectauto, cmapRdBu) plt.xlabel(Position Index) plt.ylabel(Encoding Dimension) plt.colorbar(labelEncoding Value) plt.title(Positional Encoding Matrix (d_model64)) plt.show()运行这段代码你会看到前几个维度高频的波形变化非常剧烈相邻位置的值差异很大。越往后的维度低频波形越来越平缓像一条缓慢波动的曲线。从热力图中你能清晰地看到一种“条纹”模式这是不同频率正弦波叠加的结果。每个位置每一列的向量模式都是独特的。3.2 与词嵌入的相加信息融合的方式得到位置编码 $PE$ 和词嵌入 $E$ 后Transformer的输入是 $X E PE$。为什么是相加Add而不是拼接Concatenate相加保持了维度不变$d_{model}$ 维的嵌入加上 $d_{model}$ 维的位置编码输入维度仍然是 $d_{model}$网络结构无需改变。相加迫使模型在同一个表示空间中同时处理语义和位置信息这可以看作是一种“调制”。模型后续的线性变换和注意力机制会同时作用于融合后的信息。从信息论角度看这要求模型学会在同一个向量通道中区分和利用两种不同来源的信号虽然增加了学习难度但也可能促使学习到更紧凑、高效的表示。实践有效性在Transformer及其后续变体中相加被证明是简单且有效的。拼接虽然能更清晰地区分两种信息但会加倍输入维度显著增加第一层线性变换的参数量和计算量而收益并不明显。注意相加操作的一个隐含假设是词嵌入和位置编码的数值范围大致在同一量级。通常词嵌入会进行缩放例如乘以 $\sqrt{d_{model}}$或标准化位置编码值域为[-1,1]这保证了相加的稳定性。4. 超越原始设计位置编码的演进与变体原始的Sinusoidal PE是开创性的但它并非没有缺点尤其是在长度外推和高效计算方面。近年来研究者们提出了许多改进和替代方案。4.1 相对位置编码Relative Positional Encoding原始Transformer的绝对位置编码有一个问题它只告诉模型“这是第几个词”但模型在计算注意力时更关心的是词与词之间的“相对距离”。虽然正弦编码本身支持相对位置的线性表示但需要模型在注意力计算中自己去发现和利用这种关系。相对位置编码将位置信息直接注入到注意力权重的计算过程中。典型代表如《Self-Attention with Relative Position Representations》和Transformer-XL中的方法。其核心思想是在计算注意力分数 $Attention(Q, K) softmax(\frac{QK^T}{\sqrt{d_k}})$ 时额外加入一个偏置项 $B$这个 $B$ 只与查询位置 $i$ 和键位置 $j$ 的相对距离 $(i-j)$ 有关。例如可以定义一组可学习的标量 $b_{i-j}$然后让注意力分数变为 $softmax(\frac{QK^T}{\sqrt{d_k}} B)$其中 $B_{ij} b_{i-j}$。这样模型在计算“猫”对“追”的注意力时如果“猫”在位置2“追”在位置3那么就会加上一个代表“距离为1”的偏置 $b_{1}$。这种方式更直接地建模了相对位置关系并且在处理超长文本时由于偏置只依赖于相对距离其参数数量是固定的与序列长度无关外推性更好。4.2 RoPERotary Position EmbeddingRoPE可以看作是正弦位置编码理论优雅性的现代升级版。它同样利用了旋转的思想但将其应用得更加彻底和统一。RoPE的核心洞察是不对词向量做加法而是对查询Q和键K向量做旋转变换。对于位置 $m$ 的词向量 $x_m$其对应的查询向量 $q_m$ 和键向量 $k_m$ 会经过一个依赖于位置 $m$ 的旋转矩阵 $R_{\Theta, m}$ 进行变换$\tilde{q}m R{\Theta, m} q_m$, $\tilde{k}n R{\Theta, n} k_n$。这个旋转矩阵 $R_{\Theta, m}$ 的设计非常巧妙它作用于词向量的每一对维度上类似于正弦编码中的配对维度使其旋转角度与位置 $m$ 和频率 $\theta_i$ 成正比。计算变换后的注意力分数时 $$ \tilde{q}m^T \tilde{k}n (R{\Theta, m} q_m)^T (R{\Theta, n} k_n) q_m^T R_{\Theta, n-m} k_n $$神奇的事情发生了最终的注意力分数 $q_m^T R_{\Theta, n-m} k_n$只依赖于词向量本身和它们的相对位置 $(n-m)$。这完美地将相对位置信息编码进了注意力机制且形式非常简洁。RoPE具有很好的外推性并且被广泛应用于LLaMA、GPT-NeoX等当前主流的大语言模型中。4.3 可学习位置编码的复兴与改进尽管正弦编码有诸多理论优点但在许多视觉TransformerViT和经过海量数据预训练的语言模型中简单的可学习位置编码一个nn.Embedding(max_len, d_model)因其极致的简单和灵活性依然是最主流的选择。为了克服其外推性差的缺点研究者们也提出了许多方法层次化位置编码将位置索引分解为不同粒度如段落、句子、词进行编码后再融合。相对位置偏置在可学习绝对位置编码的基础上再像相对位置编码那样在注意力分数中加入可学习的相对位置偏置。外推法在训练时采用某种策略让模型接触到更长的位置索引例如随机裁剪长序列、使用位置插值Position Interpolation等。Meta在发布Llama 2时就通过微调时将位置索引线性缩放成功将上下文长度从2k扩展到4k。4.4 无位置编码的探索更有趣的探索是我们是否真的需要显式的位置编码一些研究发现在视觉任务中当图像被分割成 patches 后由于每个 patch 本身就带有空间结构信息通过卷积或特殊初始化模型有时能在没有显式位置编码的情况下学到令人满意的性能。但在语言任务中由于文本的强顺序性完全移除位置编码通常会导致性能显著下降。5. 实战中的选择、调试与避坑指南了解了这么多理论在实际项目中该如何选择和运用位置编码呢5.1 如何根据任务选择位置编码自然语言处理尤其是生成式任务、长度可变首选RoPE对于自回归语言模型如GPT系列、机器翻译解码器RoPE是目前事实上的标准。它的外推性和理论性质都非常优秀。许多开源模型LLaMA, ChatGLM都采用了RoPE。次选正弦编码如果你在复现原始Transformer或进行相关研究正弦编码仍然是理解原理的绝佳起点。对于长度相对固定的编码任务如文本分类它也能工作得很好。慎用可学习编码除非你能确定你的训练和推理序列长度完全一致且不会变化。计算机视觉ViT及变体可学习位置编码这是最普遍、最稳定的选择。因为图像通常被分割成固定数量的 patches如14x14196序列长度是固定的。可学习编码简单有效。相对位置偏置在Swin Transformer等模型中由于使用了窗口注意力相对位置偏置在一个局部窗口内定义能更好地捕捉局部空间关系效果通常优于绝对位置编码。条件位置编码CPE一种更灵活的方式根据输入图像内容动态生成位置编码能更好地处理可变分辨率的输入。多模态任务需要统一处理文本和图像的位置信息。通常文本端使用RoPE或可学习编码图像端使用可学习编码或相对偏置。关键在于将两种模态的位置编码映射到同一语义空间或者设计跨模态的相对位置计算方式。5.2 实现正弦位置编码的常见陷阱即使你决定使用正弦编码在实现时也容易踩坑陷阱一频率计算错误错误的实现在一些早期教程中常见# 错误分母的指数计算不对 for pos in range(max_len): for i in range(d_model): pe[pos, i] np.sin(pos / (10000 ** (i / d_model))) # i 应该按奇偶区分处理正确的实现必须严格按照公式对奇偶维度分别用sin和cos并且频率项的计算要准确。使用前面提供的get_positional_encoding函数是安全的。陷阱二忘记停止梯度在PyTorch中如果你像计算词嵌入那样将位置编码定义为一个nn.Parameter并赋值那么它会被视为可学习参数。但原始的正弦编码是不可学习的确定性函数。你应该在forward函数中实时计算或者预先计算好一个缓冲区register_buffer并设置requires_gradFalse。# 正确做法注册为buffer class TransformerModel(nn.Module): def __init__(self, d_model, max_len5000): super().__init__() self.d_model d_model # 预先计算位置编码 pe torch.zeros(max_len, d_model) position torch.arange(0, max_len).unsqueeze(1) div_term torch.exp(torch.arange(0, d_model, 2) * -(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) # (1, max_len, d_model) self.register_buffer(pe, pe) # 不参与梯度更新 def forward(self, x): # x: (batch, seq_len, d_model) x x self.pe[:, :x.size(1)] # 自动广播相加 return x陷阱三与词嵌入的缩放不匹配如前所述如果词嵌入做了缩放例如乘以 $\sqrt{d_{model}}$那么位置编码的幅值[-1,1]可能显得过小。一种经验性做法是对位置编码也进行适当的缩放或者确保词嵌入初始化在一个合理的范围内如使用Xavier或Kaiming初始化使得两者量级相近。5.3 长度外推当模型遇到更长的序列这是所有位置编码方案面临的共同挑战。即使理论上正弦编码可以外推但模型在短序列上训练出的注意力模式可能无法直接迁移到长序列。策略一位置插值Position Interpolation这是目前最流行且有效的后处理方法。基本思想是将超出训练长度的位置索引“压缩”回模型见过的范围。例如模型在长度为L的序列上训练现在要处理长度为L’ L的序列。我们不是直接使用位置索引pos而是使用缩放后的索引pos * (L / L’)。这相当于将更长的位置序列线性地“挤”进训练过的位置区间。Meta的Code Llama和许多开源模型扩展上下文窗口都用了类似技巧。策略二随机化训练长度在训练时不是固定使用最大长度而是每次随机从某个范围内如[256, 1024]抽取序列长度进行训练。这能强迫模型适应多种长度提升鲁棒性。但这种方法可能牺牲在固定长度上的最优性能。策略三使用本身具有更好外推性的编码如RoPE其相对位置的性质使其在外推上具有天然优势。ALiBiAttention with Linear Biases也是一种为外推而设计的相对位置编码它直接给注意力分数加上一个与相对距离成负比例的线性偏置被证明具有极强的外推能力。位置编码这个看似简单的组件实则是Transformer模型理解有序世界的基石。从正弦波的优雅旋转到RoPE的巧妙设计再到各种工程上的权衡与技巧它的演进史也反映了深度学习对先验知识、归纳偏置和计算效率的持续探索。理解它不仅是为了用好Transformer更是为了洞察如何将人类对世界的结构性认知如顺序、距离有效地赋予给强大的数据驱动模型。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门