欢迎光临
我们一直在努力

通俗理解Transformer的位置编码

大家好,我是AI领域的从业者,经常在CSDN上分享深度学习相关知识。今天我们来聊一个经典却又非常核心的话题——Transformer的位置编码(Positional Encoding,简称PE)。为什么说它核心?因为Transformer一出世就颠覆了序列建模,但它本身是“无序”的,没有位置信息,就相当于一堆散装单词拼不成句子。

这篇文章的目标是通俗、深入、实用,从零基础到高手都能看懂。我们会一步步拆解为什么需要位置编码、经典的正弦位置编码是怎么设计的、可学习的位置编码又有什么不同、相对位置编码(RoPE、ALiBi)的最新进展,最后还会附上大量PyTorch代码和可视化图表。

目录

  • 前言:Transformer为什么“失忆”?
  • Transformer模型快速回顾
  • 位置编码的必要性:从RNN到Transformer的痛点
  • 经典方案:正弦/余弦位置编码(Sinusoidal Positional Encoding)
    • 4.1 公式详解
    • 4.2 为什么用正弦/余弦函数?
    • 4.3 可扩展性与相对位置感知
  • 代码实战:PyTorch实现正弦位置编码
  • 可学习的位置编码(Learned Positional Embedding)
  • 相对位置编码的崛起
    • 7.1 RoPE(Rotary Position Embedding)
    • 7.2 ALiBi(Attention with Linear Biases)
    • 7.3 T5相对偏置
  • 不同位置编码的对比表格
  • 可视化与实验:一图胜千言
  • 常见问题与读者互动
  • 总结与展望
  • 参考文献
  • 1. 前言:Transformer为什么“失忆”?

    想象一下,你在读这句话:“我爱吃苹果”。你能瞬间知道“我”是主语,“苹果”是宾语,顺序不能乱。但如果把单词打乱成“苹果吃爱我”,你就懵了。

    RNN/LSTM 天生就有顺序信息,因为它们是逐个处理序列的,位置信息被隐含在时间步里。

    Transformer 却不一样。它用自注意力机制(Self-Attention)一次性处理整个序列,计算每个词与其他词的关联时,完全不考虑顺序——这就好比一锅乱炖,香味混在一起,却分不清谁先谁后。

    因此,位置编码 就成了Transformer的“救命稻草”,它给每个词贴上一个“身份证”,让模型知道“谁在前面,谁在后面”。

    你猜猜看:如果不加位置编码,Transformer还能做什么任务?(欢迎评论区告诉我你的答案)

    在这里插入图片描述

    在这里插入图片描述

    (图1-2:经典Transformer架构图,注意输入Embedding后直接加了Positional Encoding)

    2. Transformer模型快速回顾

    Transformer由Google在2017年《Attention is All You Need》提出,主要结构是Encoder-Decoder。

    • 输入:词向量(Word Embedding) + 位置编码(Positional Encoding)
    • 核心:Multi-Head Self-Attention + Feed-Forward Network
    • 位置编码 通常加在词向量上: X = Embedding + Positional_Encoding

    位置编码的维度必须和词向量相同(d_model),这样才能直接相加。

    3. 位置编码的必要性:从RNN到Transformer的痛点

    RNN的痛点大家都知道:梯度消失、并行度低。

    Transformer解决了并行问题,但引入了新问题:绝对位置丢失。

    举个例子:

    句子A:The cat sat on the mat. 句子B:The mat sat on the cat.(语义完全不同)

    如果没有位置编码,两个句子的表示几乎一样(词向量相同,只是顺序不同),模型就傻了。

    位置编码的作用:

  • 提供绝对位置信息(第几个词)
  • 提供相对位置信息(词i和词j相距多远)
  • 可扩展到更长序列
  • 4. 经典方案:正弦/余弦位置编码

    Transformer原论文用的就是这个,至今仍是许多模型的默认选择。

    公式如下:

    对于位置pos(从0开始),维度i(0到d_model/2-1):

    PE_{(pos,2i)} = \\sin\\left(\\frac{pos}{10000^{2i/d_{model}}}\\right)
    PE_{(pos,2i+1)} = \\cos\\left(\\frac{pos}{10000^{2i/d_{model}}}\\right)

    为什么这样设计?我们一步步拆解。

    4.1 公式详解

    • 周期性:用sin/cos函数,让不同位置的编码有周期性变化。
    • 波长递增:低维度(小i)波长短(变化快),高维度波长长(变化慢)。
    • 10000的指数:让波长从1到10000倍递增,覆盖短距离和长距离相对位置。

    4.2 为什么用正弦/余弦函数?

  • 周期函数:sin/cos天然周期性,模型可以学习到相对位置。
  • 线性组合:任何位置的编码都可以用附近位置线性表示(傅里叶变换性质)。
  • 固定:不需要训练,计算简单。
  • 可外推:即使训练时序列长度是512,推理时能处理1024。
  • 证明相对位置感知:

    假设位置pos和pos+k,PE(pos+k) 可以用PE(pos)的线性组合表示(用三角恒等式)。

    这意味着注意力机制可以“感知”距离。

    4.3 可扩展性与相对位置感知

    正弦位置编码的最大优势:长度外推。训练时max_len=512,推理时可以直接用pos=6000,编码依然合理。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    (图3-5:正弦位置编码可视化,低维变化快,高维变化慢)

    5. 代码实战:PyTorch实现正弦位置编码

    下面给出完整PyTorch实现,复制粘贴就能跑。

    import torch
    import torch.nn as nn
    import math

    class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
    super().__init__()
    pe = torch.zeros(max_len, d_model)
    position = torch.arange(0, max_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) # [1, max_len, d_model]
    self.register_buffer('pe', pe)

    def forward(self, x):
    # x: [batch, seq_len, d_model]
    return x + self.pe[:, :x.size(1), :]

    使用示例:

    emb = nn.Embedding(10000, 512)
    pe = PositionalEncoding(512)
    input_ids = torch.randint(0, 10000, (2, 128)) # batch=2, seq=128
    x = emb(input_ids)
    x = pe(x)
    print(x.shape) # torch.Size([2, 128, 512])

    小实验:把max_len改成10000,试试推理时输入更长序列,看看效果如何?欢迎评论区分享你的实验结果!

    6. 可学习的位置编码

    BERT、GPT等模型常用可学习的位置编码。

    实现方式:一个可训练的Embedding层。

    class LearnedPositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=512):
    super().__init__()
    self.pos_emb = nn.Parameter(torch.zeros(1, max_len, d_model))

    def forward(self, x):
    return x + self.pos_emb[:, :x.size(1), :]

    优缺点对比:

    • 优点:模型可以自己学到最优位置表示。
    • 缺点:不能外推(训练max_len=512,推理6000会报错或效果差)。

    在这里插入图片描述

    在这里插入图片描述

    (图12-13:可学习位置编码的热力图,显示了训练后的相似性)

    7. 相对位置编码的崛起

    近年来,相对位置编码 逐渐取代绝对位置编码,尤其在长序列任务中。

    7.1 RoPE(Rotary Position Embedding)

    RoPE是目前最火的方案(LLaMA、PaLM、Qwen等大模型都在用)。

    核心思想:不是加一个向量,而是旋转查询和键向量。

    对于维度对 (q_{2i}, q_{2i+1}):

    \\begin{pmatrix}
    q'_{2i} \\\\
    q'_{2i+1}
    \\end{pmatrix}
    =
    \\begin{pmatrix}
    \\cos \\theta & -\\sin \\theta \\\\
    \\sin \\theta & \\cos \\theta
    \\end{pmatrix}
    \\begin{pmatrix}
    q_{2i} \\\\
    q_{2i+1}
    \\end{pmatrix}

    其中 θ = pos × 10000^{-2i/d}

    优点:

    • 天然支持相对位置(注意力分数只依赖距离)
    • 长度外推能力极强
    • 计算高效(可以预计算旋转矩阵)

    代码实现(简化版):

    def apply_rotary_emb(x, freqs):
    # x: […, seq_len, dim]
    # freqs: [seq_len, dim//2] complex
    x_ = x.float().reshape(*x.shape[:1], 1, 2)
    x_out = torch.view_as_complex(x_)
    x_out = x_out * freqs
    return torch.view_as_real(x_out).flatten(2).type_as(x)

    在这里插入图片描述

    在这里插入图片描述

    (图7-8:RoPE的旋转示意图,直观理解为什么能捕捉相对位置)

    7.2 ALiBi(Attention with Linear Biases)

    ALiBi更简单:直接在注意力分数上加一个随距离线性衰减的偏置。

    attn = QK^T / \\sqrt{d} – m \\cdot |i-j|

    m是超参数。

    优点:极简,无需额外参数,支持长度外推。

    缺点:相对位置感知不如RoPE精细。

    7.3 T5相对偏置

    T5用可学习的相对位置偏置,桶化处理距离(0~32等)。

    8. 不同位置编码的对比表格

    编码方式类型是否可训练长度外推相对位置感知代表模型优缺点简评
    正弦/余弦 绝对 中等 原Transformer 经典、简单、外推好
    可学习 绝对 BERT, GPT-2 灵活,但不能外推
    RoPE 相对 极强 LLaMA, PaLM, Qwen 当前最优,旋转思想优雅
    ALiBi 相对 中等 MPT, Falcon 超简单,无需额外参数
    T5相对偏置 相对 中等 T5, UL2 桶化处理,适合中短序列

    在这里插入图片描述

    (图10:位置编码对比可视化)

    9. 可视化与实验:一图胜千言

    下面是几种位置编码的热力图对比(余弦相似度):

    • 正弦位置编码:对角线清晰,距离越远相似度越低。
    • 可学习:训练后出现模式。
    • RoPE:相对位置更明显。

    建议读者自己用Matplotlib画图实验:

    import matplotlib.pyplot as plt
    import seaborn as sns

    # 假设pe是 [seq_len, d_model]
    sns.heatmap(pe.detach().cpu().numpy(), cmap='viridis')
    plt.title('Positional Encoding Heatmap')
    plt.show()

    你会发现:低维变化剧烈,高维几乎是直线——这就是为什么能同时捕捉短距和长距关系。

    10. 常见问题

  • Q:位置编码加在Embedding前还是后? A:后,加在词向量上。

  • Q:为什么不用简单的1,2,3…位置向量? A:那样学不到相对位置,且数值爆炸。

  • Q:RoPE怎么实现长度外推? A:因为旋转角度只依赖相对距离,不依赖绝对位置。

  • 互动环节:你更喜欢哪种位置编码?为什么?欢迎评论区讨论!如果你有自己的实现代码,也欢迎分享~

    11. 总结与展望

    位置编码是Transformer成功的基石,从原论文的正弦编码,到如今的RoPE,经历了从绝对到相对、从固定到旋转的演进。

    一句话总结:位置编码让无序的注意力机制“有序”起来,是序列建模的灵魂。

    未来,随着序列越来越长(100k+),相对位置编码(尤其是RoPE)会继续主导。

    希望这篇文章帮你彻底搞懂位置编码!如果觉得有用,欢迎点赞、收藏、转发~

    12. 参考文献

  • Vaswani et al., Attention is All You Need (2017)
  • Su et al., RoFormer: Enhanced Transformer with Rotary Position Embedding (2021)
  • Press et al., ALiBi: Train Short, Test Long (2022)
  • Shaw et al., Self-Attention with Relative Position Representations (2018)
  • 如果你想看更多Transformer系列文章,欢迎关注我!下期见~

    赞(0)
    未经允许不得转载:171主机测评 » 通俗理解Transformer的位置编码
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址