一句话总结:Token Embedding 本身不带位置,Positional Encoding 就是给每个位置加上独特的"门牌号",让标准 Transformer 能够利用"我爱你"和"你爱我"的顺序差别。
5.1 为什么需要位置信息?
上一章我们把文字转换成了向量。但这里有一个关键问题:相同的 token,查 Embedding 表得到的起点向量一样,向量本身并没有写着它出现在哪里。
5.1.1 一个关键问题
考虑这两个句子:
- "我爱你"
- "你爱我"
如果只看 Embedding,"我"在两个句子中的向量完全相同,"爱"完全相同,"你"也完全相同。
但这两个句子的意思完全不同!
问题出在哪里?这些内容向量还缺少位置信号。标准自注意力没有 RNN 那样的递归,也没有 CNN 那样的局部卷积,所以原始 Transformer 在输入端主动注入顺序。
5.1.2 位置信息的三个作用
Positional Encoding 解决了三个问题:
-
每个 Token 都能包含它的位置信息
- 模型知道这是第 1 个字、第 2 个字、还是第 100 个字
-
模型可以看到文字之间的"距离"
- "我"和"爱"相邻,"我"和"你"隔了一个字
- 这种相对位置关系对理解语义很重要
-
模型可以学习解读一致的位置规则
- 正弦公式可以计算任意位置,不需要为每个新位置再存一行参数
- 它给了模型外推的可能,但不保证训练长度以外一定表现良好
5.2 最简单的方案:直接用整数
在讲 Transformer 的方案之前,让我们先看最直观的方法。
5.2.1 整数位置编码
最简单的想法:直接把位置编号加到向量上。
假设输入是"LLM张老师":
Embedding 向量(每个字的语义表示):
| 字 | 维度1 | 维度2 | 维度3 | 维度4 |
|---|---|---|---|---|
| L | 0.62 | -0.51 | 0.09 | 0.85 |
| L | 0.62 | -0.51 | 0.09 | 0.85 |
| M | 0.07 | ... | ... | ... |
| 张 | 0.43 | ... | ... | ... |
| 老 | 1.30 | ... | ... | ... |
| 师 | 0.98 | ... | ... | ... |
位置向量(每个位置的编号):
| 位置 | 维度1 | 维度2 | 维度3 | 维度4 |
|---|---|---|---|---|
| 1 | 1 | 1 | 1 | 1 |
| 2 | 2 | 2 | 2 | 2 |
| 3 | 3 | 3 | 3 | 3 |
| 4 | 4 | 4 | 4 | 4 |
| 5 | 5 | 5 | 5 | 5 |
| 6 | 6 | 6 | 6 | 6 |
相加结果:
| 字 | 维度1 | 维度2 | 维度3 | 维度4 |
|---|---|---|---|---|
| L | 0.62+1 | -0.51+1 | 0.09+1 | 0.85+1 |
| L | 0.62+2 | -0.51+2 | 0.09+2 | 0.85+2 |
| ... | ... | ... | ... | ... |
5.2.2 整数方案的问题
这个方案并非数学上“不能用”,但直接把原始整数复制到每一维,有两个明显的局限:
-
尺度会不断增长:位置越靠后,加的数字越大。位置 1000 就要加 1000,容易压过原本在零附近的 Embedding 信号,让优化更困难。这不等于“梯度必然爆炸”,但是一个很差的尺度设计。
-
信号太单一:每一维都加同一个数,位置只沿着一个方向变化。网络并非完全无法学习这个线性信号,但它没有多尺度的距离结构,用起来不够灵活。
所以 Transformer 用了一个更聪明的方案:正弦位置编码(Sinusoidal Positional Encoding)。
5.3 Transformer 的方案:正弦位置编码
5.3.1 核心思想
Transformer 的位置编码使用 sin 和 cos 函数 来生成位置向量。
公式是:
偶数维度:PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
奇数维度:PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
别被公式吓到!核心思想很简单:
- 偶数维度用 sin 函数
- 奇数维度用 cos 函数
- 不同维度用不同频率的波
5.3.2 波形可视化
把位置沿着序列画出来,每一对维度都像一只 sin/cos 时钟:
- 前面的维度分母小,变化快,像秒针
- 后面的维度分母大,变化慢,像时针
把快慢不同的时钟组合起来,实用范围内的每个位置就有了可区分的"指纹"。
5.3.3 实际数值
图中下方的表格展示了具体数值:
| 位置 | 维度0 | 维度1 | 维度2 | 维度3 |
|---|---|---|---|---|
| L (pos=0) | 0.00 | 1.00 | 0.00 | 1.00 |
| L (pos=1) | 0.84 | 0.54 | 0.01 | 1.00 |
| M (pos=2) | 0.91 | -0.42 | 0.02 | 1.00 |
| 张 (pos=3) | 0.14 | -0.99 | 0.03 | 1.00 |
| 老 (pos=4) | -0.76 | -0.65 | 0.04 | 1.00 |
| 师 (pos=5) | -0.96 | 0.28 | 0.05 | 1.00 |
注:位置从 0 开始编号(pos=0, 1, 2...)。pos=0 时 sin(0)=0, cos(0)=1。
注意:
- 所有数值都在 [-1, 1] 范围内(sin/cos 的值域)
- 在这个例子和实用上下文范围内,不同位置得到可区分的向量
- 数值变化有规律可循
5.3.4 为什么用 sin/cos?
正弦位置编码有几个优点:
-
数值稳定:永远在 [-1, 1] 范围内,不会爆炸
-
公式可以外推:训练长度之外仍然能计算位置向量;但模型是否真能用好它,还要看训练和架构
-
固定偏移有线性关系:对于任意固定的
k,PE(pos+k)可以由PE(pos)经过一个只与k有关的线性旋转得到。这是原论文选择 sin/cos 的动机之一
公式能外推,不等于模型实际长度也会自动外推。后来的 RoPE、ALiBi 等方案会更直接地把相对位置放进 Attention,但它们的长上下文能力同样取决于训练长度、缩放方法和模型配置。第 25 章再详细拆开。
5.4 Embedding + Position = Input
现在让我们看完整的过程:把 Embedding 向量和位置向量加起来。
5.4.1 向量相加
三个矩阵的关系:
Embedding Vector(蓝色):词的语义信息
L: [0.62, -0.51, 0.09, 0.85]
L: [0.62, -0.51, 0.09, 0.85] # 注意:两个 L 的 embedding 相同
M: [0.07, ..., ..., ...]
张: [0.43, ..., ..., ...]
老: [1.30, ..., ..., ...]
师: [0.98, ..., ..., ...]
Position Vector(黄色):位置信息
位置1: [0.00, 1.00, 0.00, 1.00]
位置2: [0.84, 0.54, 0.01, 1.00]
位置3: [0.91, -0.42, 0.02, 1.00]
位置4: [0.14, -0.99, 0.03, 1.00]
位置5: [-0.76, -0.65, 0.04, 1.00]
位置6: [-0.96, 0.28, 0.05, 1.00]
Input Embeddings(绿色):相加结果
L(位置1): [0.62+0.00, -0.51+1.00, 0.09+0.00, 0.85+1.00]
L(位置2): [0.62+0.84, -0.51+0.54, 0.09+0.01, 0.85+1.00]
...
关键观察:两个 L 虽然 Embedding 相同,但加上不同的位置编码后,结果不同了!
这样模型就能区分"第一个 L"和"第二个 L"了。
为了让加法看得清楚,上面用了直接
Embedding + Position。原始 Transformer 论文在相加前还会把 token embedding 乘以sqrt(d_model):Input = sqrt(d_model) × Embedding + PE。不同现代架构的细节可能不同,但“把内容与位置合成同形状输入”这个主线不变。
5.4.2 几何直觉
让我们用 2D 简化例子来理解向量相加的几何意义。
假设:
- Embedding 向量 L = [1, 3](蓝色箭头)
- Position 向量 = [2, 1](红色箭头)
相加结果:
- Input = [1+2, 3+1] = [3, 4](合成向量)
从几何上看,这就是向量加法的平行四边形法则:
- 两个向量首尾相接
- 结果是从原点到对角的向量
5.4.3 相对距离 vs 绝对位置
一个重要的洞察:在许多任务中,模型更关心的是"相对距离"而不是"绝对位置"。
注意:这并非绝对——不同的位置编码方案(如学习式 vs RoPE vs ALiBi)对绝对/相对位置的处理方式不同,详见第 25 章。
要注意,位置向量本身不会决定一个词的语义。它的作用是让 Attention 知道谁在谁前面、隔了多远。于是“我爱你”和“你爱我”虽然由同一组 token 组成,后面的 Attention 却能读到两种不同的排列和邻域,再把它们组合成不同的上下文表示。
5.5 训练过程中的位置编码
5.5.1 Embedding 是可学习的,Position 是固定的
一个重要的区别:
- Embedding 向量:是模型参数,训练时会接收梯度并更新
- Position 向量(正弦版本):由公式确定,不是可学习参数,优化器不会更新它
训练的是“如何解读位置信号”,不是改写这个固定信号本身。
有些模型(如 BERT)使用"可学习的位置编码",Position 向量也作为参数训练。但原始 Transformer 使用的是固定的正弦编码。
5.5.2 在完整架构中的位置
看这张详细的架构图:
- 输入文字经过 Embedding(文字转数字 → 嵌入向量)
- 加上 Positional Encoding(位置信息编码)
- 得到 Input Embeddings,送入 Attention 层
对本章的“相加式正弦编码”来说,位置信号在进入第一个 Transformer Block 之前加入。RoPE 一类现代方案则在 Attention 内部旋转 Q/K,不走这条“输入向量相加”的路。
5.6 深入理解:为什么是"相加"?
你可能会问:为什么是把 Embedding 和 Position 相加?为什么不是拼接?
5.6.1 拼接 vs 相加
拼接(Concatenation):
- 把两个向量接在一起
- 如果两边都保留
d_model宽度,[embedding | position]会变成2 × d_model - 优点:信息完全独立
- 缺点:后面的层要么承受更宽的矩阵和更高计算量,要么先投影回
d_model
相加(Addition):
- 对应维度直接相加
- embedding + position → 相同维度的向量
- 优点:维度不变,计算高效
- 缺点:两种信息混合在一起
5.6.2 为什么相加可行?
关键洞察:高维空间很"宽敞"。
在 768 维或 4096 维的空间里,后面的投影矩阵可以学会如何同时解读 Embedding 和 Position 信号。它们有可能形成部分分离的方向,但“必然正交”不是加法本身的保证。
这就像在一个巨大的房间里,放两套家具。虽然在同一个房间,但各自有各自的区域,不会混乱。
5.7 本章总结
这一章我们学习了 Transformer 如何处理位置信息。
5.7.1 核心概念
| 概念 | 解释 |
|---|---|
| Positional Encoding | 给每个位置添加独特的向量表示 |
| 正弦位置编码 | 用 sin/cos 函数生成位置向量 |
| 相加操作 | Embedding + Position = Input Embeddings |
| 相对位置 | 模型关心词之间的距离,而不只是绝对位置 |
5.7.2 数据变换过程
Embedding [seq_len, d_model] # 词的语义信息
+
Position [seq_len, d_model] # 位置信息(正弦编码)
=
Input [seq_len, d_model] # 完整的输入表示
5.7.3 核心认知
Positional Encoding 补上了 Token Embedding 的“位置盲点”。它把顺序信号注入输入,Attention 才能结合内容、前后和距离,理解“我爱你”和“你爱我”的区别。
本章交付物
学完这一章,你应该能够:
- 解释为什么 Transformer 需要位置编码
- 理解正弦位置编码的基本原理(sin/cos 函数)
- 说明 Embedding 和 Position 是如何结合的
- 区分"可学习位置编码"和"固定正弦编码"
下一章预告
现在我们的输入已经完整了:语义信息(Embedding)+ 位置信息(Position)。
下一章,我们来学习两个重要的数学工具:LayerNorm(层归一化) 和 Softmax。它们在 Transformer 中无处不在,理解它们是理解整个架构的基础。