☰
transformer你不学?04|位置编码:让模型知道词的顺序
2026/10/10 12:45:09 网站建设 项目流程

transformer你不学?04|位置编码:让模型知道词的顺序

作 者:吴佳浩(Alben)

公众号:全栈架构师笔记


导读
Self-Attention 有一个天生的缺陷:它对输入顺序完全无感——“猫咬狗"和"狗咬猫"在它眼里是同一件事。位置编码就是给每个位置盖一个"门牌号”,把顺序信息重新注入向量。
从论文原版的正弦编码,到今天主流的可学习编码,本篇把"顺序"这个被 Attention 弄丢的维度补回来。


本篇你会学到什么

  • 🔸 为什么 Attention 是"无序"的,这为什么是个问题;
  • 🔸 正弦位置编码的构造与平移不变性;
  • 🔸 可学习位置编码(GPT 风格)的实现。

一、为什么要学这个

做一个 10 秒的实验:把 第 3 篇 的输出[[我, 爱, NLP]]换成[[NLP, 爱, 我]]——如果不加位置信息,Attention 的输出完全一样。因为 Attention 只做两两相似度计算,而相似度与位置无关。

RNN 靠"逐步喂入"天然带顺序;Transformer 一次看全序列,顺序信息必须显式注入。解法:在词向量上加一个位置向量——最终输入 = 词 Embedding + 位置 Embedding。

二、核心理论:正弦编码的巧妙

论文原版用不同频率的正弦/余弦给每个位置生成唯一"指纹":

PE(pos, 2i) = sin(pos / 10000^(2i/d)) PE(pos, 2i+1) = cos(pos / 10000^(2i/d))

直觉:想象很多根不同粗细的弹簧——高频维度区分相邻位置,低频维度区分远距离位置。它有两个漂亮性质:

  • 🔸唯一性:每个位置得到不同指纹;
  • 🔸平移一致性:PE[pos+k] - PE[pos]只与 k 有关,与 pos 无关——模型能学到"相对距离"。

今天的主流模型(GPT/BERT)更多用可学习位置编码:直接建一个nn.Embedding(max_len, d_model),让训练自己决定每个位置长什么样。

三、真实项目里怎么用

  • 🔸 长文本外推:RoPE(旋转位置编码)让 LLaMA/Qwen 能处理超出训练长度的输入——本质还是位置编码的进化;
  • 🔸 多模态:图像 patch 的 2D 位置编码、语音的时间戳编码,思想同源。

四、流程图

Token Embedding 词向量

相加融合

Positional Encoding 位置向量

带顺序信息的输入

进入 Attention

五、真实代码:正弦位置编码

# 正弦位置编码:论文原版实现 + 平移一致性验证importtorchimporttorch.nn.functionalasFimportmath max_len,d_model=64,16pe=torch.zeros(max_len,d_model)pos=torch.arange(max_len).unsqueeze(1).float()# (64, 1) 位置列div=torch.exp(torch.arange(0,d_model,2).float()*(-math.log(10000.0)/d_model))# 频率衰减项pe[:,0::2]=torch.sin(pos*div)# 偶数维放 sinpe[:,1::2]=torch.cos(pos*div)# 奇数维放 cosprint(f"位置编码矩阵 shape:{pe.shape}")# 性质1:唯一性——不同位置的编码不同print(f"位置0 与 位置5 是否相同:{torch.allclose(pe[0],pe[5])}")# 性质2:平移一致性——间隔相同的相似度也相同d1=F.cosine_similarity(pe[0],pe[3],dim=0)# 间隔3d2=F.cosine_similarity(pe[10],pe[13],dim=0)# 同样间隔3print(f"间隔3的相似度: 位置0vs3={d1.item():.3f}, 位置10vs13={d2.item():.3f}")

输出结果怎么看(真实运行输出):

位置编码矩阵 shape: torch.Size([64, 16]) 位置0 与 位置5 是否相同: False 间隔3的相似度: 位置0vs3=0.693, 位置10vs13=0.693
  • 🔸 两个不同位置相似度 0.693完全相等——正弦编码的平移一致性不是理论空谈,是可直接复现的数值事实;
  • 🔸 间隔越大相似度越低(你可以把 3 改成 7 验证),模型由此学会"距离感"。

六、可学习位置编码(GPT 风格)

# 可学习位置编码:直接查表,训练自动学习importtorchimporttorch.nnasnnclassLearnablePE(nn.Module):def__init__(self,max_len=512,d_model=64):super().__init__()# 和词 Embedding 一样,也是一张可训练的表self.pe=nn.Embedding(max_len,d_model)defforward(self,seq_len):# 返回前 seq_len 个位置的位置向量pos=torch.arange(seq_len)returnself.pe(pos)# shape: (seq_len, d_model)lpe=LearnablePE(max_len=512,d_model=64)out=lpe(10)print(f"可学习PE输出:{out.shape}, 可训练:{out.requires_grad}")

输出:可学习PE输出: torch.Size([10, 64]), 可训练: True——用法与词 Embedding 完全同构,这就是 GPT 系列的做法。

七、常见错误与排查

  • 🔸忘记加位置编码:模型能跑但效果诡异地差,尤其语序敏感的任务——这是新手最常见的隐性 Bug;
  • 🔸输入超过 max_len:可学习编码查表越界报 IndexError。解决:截断、滑窗或换 RoPE 类外推方案;
  • 🔸相加而非拼接:词向量 + 位置向量(论文做法)不是 concat——维度保持 d_model,参数零增加。

八、练习题

  1. 把d_model改成 8,重跑平移一致性实验,观察不同维度的数值差异;
  2. 用打乱顺序的输入(torch.randperm)过一遍nn.MultiheadAttention,数值验证"Attention 无序"这个说法;
  3. 查一下 RoPE(旋转位置编码)资料,对比它和正弦编码在"长文本外推"上的差异,写 3 句话小结。

一句话总结:位置编码用一组不同频率的"位置指纹"(或一张可学习查表)补回 Attention 丢掉的顺序信息——没有它,Transformer 只是一袋无序词的集合。

下一篇:transformer你不学?05|自注意力机制:QKV 的魔法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询