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, 2i1) cos(pos / 10000^(2i/d))直觉想象很多根不同粗细的弹簧——高频维度区分相邻位置低频维度区分远距离位置。它有两个漂亮性质唯一性每个位置得到不同指纹平移一致性PE[posk] - 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_model64,16petorch.zeros(max_len,d_model)postorch.arange(max_len).unsqueeze(1).float()# (64, 1) 位置列divtorch.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平移一致性——间隔相同的相似度也相同d1F.cosine_similarity(pe[0],pe[3],dim0)# 间隔3d2F.cosine_similarity(pe[10],pe[13],dim0)# 同样间隔3print(f间隔3的相似度: 位置0vs3{d1.item():.3f}, 位置10vs13{d2.item():.3f})输出结果怎么看真实运行输出位置编码矩阵 shape: torch.Size([64, 16]) 位置0 与 位置5 是否相同: False 间隔3的相似度: 位置0vs30.693, 位置10vs130.693 两个不同位置相似度 0.693完全相等——正弦编码的平移一致性不是理论空谈是可直接复现的数值事实 间隔越大相似度越低你可以把 3 改成 7 验证模型由此学会距离感。六、可学习位置编码GPT 风格# 可学习位置编码直接查表训练自动学习importtorchimporttorch.nnasnnclassLearnablePE(nn.Module):def__init__(self,max_len512,d_model64):super().__init__()# 和词 Embedding 一样也是一张可训练的表self.penn.Embedding(max_len,d_model)defforward(self,seq_len):# 返回前 seq_len 个位置的位置向量postorch.arange(seq_len)returnself.pe(pos)# shape: (seq_len, d_model)lpeLearnablePE(max_len512,d_model64)outlpe(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参数零增加。八、练习题把d_model改成 8重跑平移一致性实验观察不同维度的数值差异用打乱顺序的输入torch.randperm过一遍nn.MultiheadAttention数值验证Attention 无序这个说法查一下 RoPE旋转位置编码资料对比它和正弦编码在长文本外推上的差异写 3 句话小结。一句话总结位置编码用一组不同频率的位置指纹或一张可学习查表补回 Attention 丢掉的顺序信息——没有它Transformer 只是一袋无序词的集合。下一篇transformer你不学05自注意力机制QKV 的魔法。