4.1 正弦位置编码:频率与外推的直觉

原始 Transformer 使用了一种精巧的固定位置编码方案——基于不同频率的正弦和余弦函数。这不是一个拍脑袋的选择,而是一个经过深思熟虑的数学设计。

4.1.1 编码公式

正弦位置编码(Sinusoidal Positional Encoding)的定义为:

PE(pos,2i)=sin(pos100002i/dmodel)\text{PE}(pos, 2i) = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)

PE(pos,2i+1)=cos(pos100002i/dmodel)\text{PE}(pos, 2i+1) = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)

其中 $pos$ 是位置索引(0, 1, 2, ...),$i$ 是维度索引(0, 1, 2, ..., $d_{\text{model}}/2 - 1$)。每个位置被编码为一个 $d_{\text{model}}$ 维的向量,偶数维度使用正弦函数,奇数维度使用余弦函数。

4.1.2 为什么使用正弦函数

这个编码方案的设计动机可以从几个层面理解:

多尺度的频率分解。 不同维度使用不同频率的三角函数——低维度使用高频(波长短,变化快),高维度使用低频(波长长,变化慢)。频率由 $1/10000^{2i/d_{\text{model}}}$ 决定,波长从 $2\pi$($i=0$)到 $10000 \cdot 2\pi$($i=d_{\text{model}}/2-1$)。

这类似于傅里叶变换的思想:用不同频率的信号叠加来表示位置信息。低频分量区分远距离的位置,高频分量区分相邻位置。两者结合,可以为每个位置提供唯一的编码。

类比二进制计数。 一种直观的理解方式是类比二进制数:最低位变化最快(每次加 1 就翻转),最高位变化最慢。正弦编码中的不同频率维度也是如此——低维度(高频)变化快,高维度(低频)变化慢。

4.1.3 频率分解的可视化

上文描述的多尺度频率特性,用图形来理解更加直观。下面的代码生成两幅图:一幅是位置编码矩阵的热力图,展示所有位置和所有维度的编码值;另一幅选取不同频率的通道叠加展示其波形。

import torch
import matplotlib.pyplot as plt
import math

d_model = 64   # 编码维度
max_pos = 100  # 位置数量

# 计算正弦位置编码
pe = torch.zeros(max_pos, d_model)
position = torch.arange(0, max_pos).unsqueeze(1).float()
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)  # 奇数维度

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 左图:位置编码热力图
im = axes[0].imshow(pe.numpy().T, aspect="auto", cmap="RdBu_r",
                     origin="lower")
axes[0].set_xlabel("位置 (pos)")
axes[0].set_ylabel("维度 (i)")
axes[0].set_title("正弦位置编码热力图(PE 矩阵)")
plt.colorbar(im, ax=axes[0])

# 右图:选取 4 个不同频率通道的波形
channels = [0, 10, 20, 30]  # 从高频到低频
for ch in channels:
    freq = 1.0 / (10000 ** (ch / d_model))
    axes[1].plot(pe[:, ch].numpy(),
                 label=f"维度 {ch}(频率 {freq:.4f})")
axes[1].set_xlabel("位置 (pos)")
axes[1].set_ylabel("编码值")
axes[1].set_title("不同频率通道的波形对比")
axes[1].legend(fontsize=8)
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("sinusoidal_pe_visualization.png", dpi=150)
plt.show()

图 4-1:正弦位置编码的频率分解可视化

在热力图(左)中可以观察到:底部的低编号维度变化非常密集(高频振荡),而顶部的高编号维度变化缓慢(低频、平滑过渡)。这种从高频到低频的渐变正是多尺度编码的核心——低频分量区分远距离的位置,高频分量区分相邻位置。在波形图(右)中,维度 0 的波形振荡极快,而维度 30 的波形几乎在整个范围内只完成了不到一个周期。

4.1.4 相对位置的线性表示

正弦编码的一个关键数学性质是:任意两个位置之间的偏移量可以用线性变换表示。

具体而言,存在一个只依赖于偏移量 $k$(而非绝对位置)的矩阵 $M_k$,使得:

PE(pos+k)=MkPE(pos)\text{PE}(pos + k) = M_k \cdot \text{PE}(pos)

这源于三角函数的和角公式。例如对于单个频率 $\omega$:

sin(ω(pos+k))=sin(ωpos)cos(ωk)+cos(ωpos)sin(ωk)\sin(\omega(pos + k)) = \sin(\omega \cdot pos)\cos(\omega k) + \cos(\omega \cdot pos)\sin(\omega k)

这意味着模型理论上可以通过学习这种线性关系来感知相对位置——注意力计算中两个位置编码之间的关系自然地编码了它们的距离。这个性质是正弦编码的核心优势之一。

4.1.5 外推能力

正弦编码的另一个重要优势是外推能力:由于使用了连续的数学函数,即使推理时遇到的序列长度超过训练时的最大长度,正弦函数仍然可以为新位置产生有意义的编码。

不过,外推能力的实际效果取决于模型是否在训练中真正学习了利用三角函数的数学性质。实验表明,直接外推到训练长度的 2 倍以上时,性能会有所下降——这推动了后续更强外推方案的研究。

4.1.6 固定 vs 可学习:原始论文的对比

Vaswani 等人在论文中指出,正弦编码和可学习编码在翻译任务上的效果几乎相同。选择固定正弦编码的原因主要是:

  1. 不增加模型参数

  2. 理论上具有外推能力

  3. 数学结构清晰,便于分析

最后更新于