跳转至

位置编码、归一化与激活函数

本章导读:Transformer 的“零件”决定了它能否训练得深、扩展得长。位置编码解决顺序感知,归一化解决数值稳定,激活函数影响表达力与计算效率。面试中经常以“RoPE 为什么能外推”“RMSNorm 和 LayerNorm 差在哪”“SwiGLU 为什么好用”等形式出现。

阅读建议:RoPE 的复数/旋转推导要能画图解释;归一化要能写出公式并说明放在残差前还是后;激活函数要能说清参数量与 \(d_{ff}\) 的对应关系。

核心要点

  • 自注意力是置换不变的,必须显式注入位置信息,否则模型无法区分“猫追狗”和“狗追猫”。
  • 绝对位置编码实现简单,但长度外推差;相对位置编码直接建模距离,泛化更好。
  • RoPE 通过对 Q/K 做旋转,使内积只依赖相对位置 \(m-n\),兼具绝对编码的实现简单与相对编码的性质。
  • RoPE 的外推依赖基频 \(\theta\)、训练长度与频率分布;PI、NTK-aware、YaRN 是三类常见扩展思路。
  • Pre-LN + RMSNorm 是现代大模型的稳定组合;RMSNorm 去掉均值中心化,计算更省且效果相当。
  • SwiGLU 通过门控提升表达力,但多一个投影矩阵,需把 \(d_{ff}\) 调到约 \(8d/3\) 以对齐参数量。
  • 残差分支的初始化尺度、warmup 和梯度裁剪共同决定深层模型能否稳定训练。
  • 位置编码、归一化、激活函数都不是“孤立技巧”,它们与并行策略、精度、推理长度共同影响最终效果。

1. 为什么需要位置编码

自注意力对输入顺序是置换等变的。设 \(P\) 是一个置换矩阵,则:

\[ \mathrm{Attention}(PX)=P\,\mathrm{Attention}(X) \]

也就是说,打乱 token 顺序,输出只是跟着打乱,模型本身感知不到“谁在前谁在后”。但语言是有序的: “我打你”和“你打我”含义完全不同。因此必须把位置信息注入表示。

位置编码要回答三个问题:

  1. 绝对还是相对:模型需要知道“第 5 个位置”,还是只需要知道“两个 token 相距 3”?
  2. 如何外推:训练长度 4K,推理 32K 时还能不能用?
  3. 计算代价:是否增加参数、是否影响 KV Cache、是否兼容 FlashAttention?

2. 绝对位置编码

2.1 正弦位置编码

原始 Transformer 使用固定正弦函数:

\[ PE_{(pos,2i)}=\sin\left(\frac{pos}{10000^{2i/d}}\right),\qquad PE_{(pos,2i+1)}=\cos\left(\frac{pos}{10000^{2i/d}}\right) \]

其中 \(pos\) 是位置,\(i\) 是维度索引,\(d\) 是模型维度。

性质:

  • 不同维度对应不同频率,低维变化快、高维变化慢,形成类似二进制计数的多尺度表示。
  • 对于固定偏移 \(k\),\(PE_{pos+k}\) 可以表示为 \(PE_{pos}\) 的线性变换,理论上模型能学到相对关系。
  • 无参数,不增加显存。

缺点:外推能力有限;模型在训练长度之外的位置上没见过,性能会下降;对长上下文不够灵活。

2.2 可学习绝对位置编码

为每个位置分配一个可训练向量 \(p_i\in\mathbb{R}^d\),与 token embedding 相加:

\[ h_i^{(0)}=E[x_i]+p_i \]

代表模型:BERT、GPT-2(GPT-2 实际使用可学习位置编码)。

优点:简单、表达自由;缺点:最大长度在训练前固定,超出范围没有定义;参数量为 \(L_{\max}\times d\),长上下文下不可忽视。

3. 相对位置编码

相对位置编码不关心“绝对第几位”,而是建模 token 之间的距离。

3.1 T5 相对位置偏置

在注意力 logits 上加一个可学习的标量偏置,偏置按距离分桶:

\[ A_{ij}=\frac{q_i^\top k_j}{\sqrt{d_k}}+b_{\mathrm{bucket}(i-j)} \]

距离被映射到若干桶(例如 32 个),近距离用精确距离,远距离共享桶。这样参数少,且能泛化到未见过的距离。

3.2 ALiBi

ALiBi(Attention with Linear Biases)直接按距离加线性惩罚:

\[ A_{ij}=\frac{q_i^\top k_j}{\sqrt{d_k}}-m\cdot|i-j| \]

每个头有不同斜率 \(m\)(通常按几何级数设置)。距离越远,惩罚越大,鼓励模型关注近邻。ALiBi 的优点是实现极简、外推较好;缺点是线性惩罚可能过强,长距离依赖需要靠多层堆叠。

3.3 相对位置与 KV Cache

相对位置编码通常不需要在 KV Cache 中存储位置向量,只需在计算注意力时根据相对距离加偏置,因此对推理友好。RoPE 则把位置信息旋转进 Q/K,KV Cache 中缓存的是已旋转的向量,也需要额外处理位置扩展。

4. RoPE:旋转位置编码

RoPE(Rotary Position Embedding)是目前最主流的方案,Llama、Qwen、Gemma、DeepSeek 等均采用。

4.1 核心思想

把 \(d\) 维向量看成 \(d/2\) 个二维平面,对每一对维度 \((x_{2i},x_{2i+1})\) 按位置相关的角度旋转:

\[ \begin{pmatrix}x'_{2i}\\x'_{2i+1}\end{pmatrix} = \begin{pmatrix}\cos(m\theta_i)&-\sin(m\theta_i)\\\sin(m\theta_i)&\cos(m\theta_i)\end{pmatrix} \begin{pmatrix}x_{2i}\\x_{2i+1}\end{pmatrix} \]

其中 \(m\) 是位置,\(\theta_i\) 是第 \(i\) 对的基频:

\[ \theta_i=\mathrm{base}^{-2i/d},\qquad \mathrm{base}=10000\ \text{(常见默认)} \]

4.2 为什么内积只依赖相对位置

二维旋转矩阵 \(R(m\theta)\) 满足 \(R(m\theta)^\top R(n\theta)=R((n-m)\theta)\)。把 RoPE 分别作用于 \(q_m\) 和 \(k_n\):

\[ (R(m\theta)q_m)^\top(R(n\theta)k_n)=q_m^\top R((n-m)\theta)k_n \]

结果只依赖 \(m-n\),与绝对位置无关。这正是相对位置编码想要的性质,但 RoPE 的实现仍然是在每个位置上做绝对旋转,因此兼具两者优点。

4.3 代码实现

import torch


def build_rope_cache(seq_len: int, dim: int, base: float = 10000.0, device="cpu"):
    # 每两个维度一组,频率按几何级数递减
    inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2, device=device).float() / dim))
    positions = torch.arange(seq_len, device=device).float()
    freqs = torch.outer(positions, inv_freq)          # [seq_len, dim/2]
    cos = torch.cos(freqs)
    sin = torch.sin(freqs)
    return cos, sin


def rotate_half(x: torch.Tensor) -> torch.Tensor:
    # [..., d] -> 前后半交换并取负,配合 cos/sin 实现成对旋转
    x1, x2 = x.chunk(2, dim=-1)
    return torch.cat([-x2, x1], dim=-1)


def apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
    # x: [batch, heads, seq_len, d_head]
    # cos/sin: [seq_len, d_head/2] -> 拼成 [seq_len, d_head]
    cos = torch.cat([cos, cos], dim=-1).unsqueeze(0).unsqueeze(0)
    sin = torch.cat([sin, sin], dim=-1).unsqueeze(0).unsqueeze(0)
    return x * cos + rotate_half(x) * sin

说明:不同框架的维度排列(交错 vs 前后半)不同,但数学等价。Llama 的 rotate_half 采用前后半交换;一些实现采用相邻维度成对。面试时说明“成对旋转”即可,不必死记代码。

4.4 RoPE 的频率与长上下文

RoPE 的每个维度对应一个波长:

\[ \lambda_i=2\pi\cdot \mathrm{base}^{2i/d} \]

低维(\(i\) 小)波长短,变化快,负责局部位置;高维波长长,变化慢,负责全局位置。训练长度决定了模型见过哪些相对距离,超出后高频维度会“转过多圈”,导致位置混淆。

4.5 长度外推的三类方法

位置插值(Position Interpolation, PI) 把推理位置 \(m\) 压缩到训练范围内:

\[ m'=\frac{m\cdot L_{\text{train}}}{L_{\text{target}}} \]

相当于整体降低旋转角度。优点是简单,缺点是所有频率一起压缩,局部分辨率下降,需要少量微调恢复。

NTK-aware 缩放 不直接改位置,而是调整基频:

\[ \mathrm{base}'=\mathrm{base}\cdot\left(\frac{L_{\text{target}}}{L_{\text{train}}}\right)^{d/(d-2)} \]

低频维度几乎不变(保留全局),高频维度被拉伸(保持局部分辨率)。比 PI 更平滑,通常配合少量长文本微调。

YaRN 结合 NTK 缩放与注意力温度修正,并对不同频率分段处理,减少长上下文下的注意力熵变化。YaRN 在无需大量微调的情况下即可扩展数倍长度,但仍建议做长文本继续训练。

其他:LongRoPE(分维度搜索缩放因子)、NTK-by-parts、动态 NTK(按当前长度实时调整)。

面试表达:外推不是“改个 base 就无限长”。真正的长上下文能力还依赖训练数据、注意力实现(FlashAttention/Ring Attention)、KV Cache 显存和评测方法。位置编码只是其中一环。

5. 归一化

5.1 LayerNorm

对每个样本的每个位置,在特征维度上归一化:

\[ \mu=\frac{1}{d}\sum_{i=1}^{d}x_i,\qquad \sigma^2=\frac{1}{d}\sum_{i=1}^{d}(x_i-\mu)^2 \]
\[ \mathrm{LN}(x)=\gamma\odot\frac{x-\mu}{\sqrt{\sigma^2+\epsilon}}+\beta \]

其中 \(\gamma,\beta\in\mathbb{R}^d\) 是可学习缩放与偏移。

特点:

  • 与 batch size 无关,训练/推理行为一致;
  • 对每个 token 独立归一化,适合变长序列;
  • 计算需要均值和方差两次归约。

5.2 RMSNorm

\[ \mathrm{RMSNorm}(x)=\gamma\odot\frac{x}{\sqrt{\frac{1}{d}\sum_{i=1}^{d}x_i^2+\epsilon}} \]

去掉了均值中心化和偏移 \(\beta\),只保留缩放。

为什么有效?

  • 假设特征均值接近 0,则方差与均方根近似相等,LayerNorm 的主要作用(控制尺度)被保留。
  • 少了均值计算和偏移参数,kernel 更简单,速度略快。
  • 实践中效果与 LayerNorm 相当,甚至更稳定。

代表模型:Llama、Qwen、Gemma、PaLM。

5.3 Pre-LN vs Post-LN

结构 公式 梯度路径 训练稳定性 代表
Post-LN \(x_{l+1}=\mathrm{LN}(x_l+F(x_l))\) 每层都经过 LN 对 warmup 敏感 原始 Transformer、BERT
Pre-LN \(x_{l+1}=x_l+F(\mathrm{LN}(x_l))\) 残差路径无归一化 稳定 GPT-2/3、Llama

Pre-LN 的残差路径是恒等映射,梯度可以无衰减地回传;但所有子层输入都被归一化,深层表达力略弱,因此需要在最后加一层 LN(final norm)。

还有 Sandwich-LN(子层前后都归一化)和 DeepNorm(调整残差缩放),用于超深模型,但主流大模型仍是 Pre-RMSNorm。

5.4 归一化的位置与数值稳定

  • 归一化放在子层前(Pre-Norm),残差相加后再进入下一层;
  • \(\epsilon\) 通常取 \(10^{-5}\) 到 \(10^{-6}\),BF16 下可适当放大;
  • 归一化参数初始化为 1(缩放)和 0(偏移);
  • 混合精度下,归一化通常在 FP32 中计算,避免方差溢出。

6. 激活函数

6.1 ReLU 与 GELU

ReLU:

\[ \mathrm{ReLU}(x)=\max(0,x) \]

简单、稀疏,但在 0 点不可导,负半轴梯度为 0,深层可能出现“死亡神经元”。

GELU:

\[ \mathrm{GELU}(x)=x\cdot\Phi(x) \]

其中 \(\Phi\) 是标准正态分布的累积分布函数。常用近似:

\[ \mathrm{GELU}(x)\approx0.5x\left(1+\tanh\left(\sqrt{\frac{2}{\pi}}(x+0.044715x^3)\right)\right) \]

GELU 比 ReLU 更平滑,在 Transformer(BERT、GPT-2)中广泛使用。

6.2 SwiGLU

门控线性单元(GLU)的一般形式:

\[ \mathrm{GLU}(x)=(xW_1)\odot\sigma(xW_2) \]

SwiGLU 用 Swish 作为门控:

\[ \mathrm{SwiGLU}(x)=\big(\mathrm{Swish}(xW_1)\odot xW_3\big)W_2,\qquad \mathrm{Swish}(z)=z\cdot\sigma(z) \]

其中 \(W_1,W_3\in\mathbb{R}^{d\times d_{ff}}\),\(W_2\in\mathbb{R}^{d_{ff}\times d}\)。

参数量为 \(3d\cdot d_{ff}\)。若希望与标准 FFN 的 \(8d^2\) 对齐:

\[ 3d\cdot d_{ff}=8d^2\Rightarrow d_{ff}=\frac{8}{3}d \]

实际会向上取整到 128 的倍数(如 Llama 3 8B:\(d=4096\),\(d_{ff}=14336=3.5d\),因实现对齐略有差异)。

优点:门控机制让模型可以动态选择信息,表达力更强;在相同参数量下通常优于 ReLU/GELU FFN。缺点:多一个矩阵,显存与计算略增,且 \(d_{ff}\) 不是 4 的整数倍时对硬件不够友好。

6.3 激活函数选择建议

  • 通用大模型:SwiGLU(Llama 系)或 GeGLU;
  • 追求简单/推理速度:GELU;
  • 不要只看激活函数,FFN 维度、归一化、初始化共同决定效果;
  • 面试时给出“参数量对齐”的推导,比背结论加分。

7. 初始化、残差缩放与训练稳定性

7.1 初始化

线性层常用:

  • Xavier/Glorot:适合 tanh/sigmoid,保持前向/反向方差;
  • Kaiming:适合 ReLU;
  • 大模型常用正态分布 \(\mathcal{N}(0,0.02)\)(GPT-2 风格),或按层深度缩放。

7.2 残差分支缩放

深层 Transformer 中,残差流方差会随层数累积。GPT-2 对残差分支的输出乘以 \(1/\sqrt{2L}\)(\(L\) 为层数),控制方差增长。Llama 等则通过 Pre-RMSNorm 与初始化共同稳定。

7.3 其他稳定手段

  • warmup:前几千步线性增加学习率;
  • 梯度裁剪:全局范数裁剪到 1.0;
  • BF16 替代 FP16:动态范围更大,减少溢出;
  • QK-Norm:对 Query/Key 做归一化,稳定 attention logits(部分模型使用);
  • 监控:梯度范数、激活范数、loss 分位、attention 熵。

8. 面试高频问题

Q1:为什么 Transformer 需要位置编码?不用会怎样?

  • 自注意力是置换等变的,打乱输入只会打乱输出,模型无法感知顺序。
  • 不用位置编码,模型退化为“词袋 + 注意力”,无法区分语序相关的语义。
  • 位置编码可以是绝对、相对或旋转形式。

Q2:正弦位置编码和可学习位置编码的区别?

  • 正弦是固定函数,无参数,理论上可外推,但实际外推有限。
  • 可学习编码为每个位置分配向量,表达自由,但最大长度固定,超出范围无定义。
  • 两者都是与 token embedding 相加,属于绝对位置编码。

Q3:RoPE 的原理是什么?为什么内积只依赖相对位置?

  • 把向量分成二维平面,按位置乘旋转矩阵。
  • 旋转矩阵满足 \(R(m)^\top R(n)=R(n-m)\),因此 \(q_m^\top k_n\) 只依赖 \(m-n\)。
  • 实现上只旋转 Q/K,不改变 V;位置信息通过内积进入注意力分数。

Q4:RoPE 怎么做长度外推?

  • PI:把位置线性压缩到训练范围,简单但损失局部分辨率。
  • NTK-aware:调整 base,拉伸高频、保留低频。
  • YaRN:NTK + 分段频率 + 注意力温度修正。
  • 外推还需长文本继续训练与评测,单改编码不够。

Q5:LayerNorm 和 RMSNorm 的区别?为什么 RMSNorm 更快?

  • LayerNorm 减均值、除标准差,再缩放平移;RMSNorm 只除均方根,保留缩放。
  • RMSNorm 少一次均值归约和偏移参数,kernel 更简单。
  • 在特征均值接近 0 的假设下,两者效果相当;RMSNorm 在实践中更稳定、更快。

Q6:Pre-LN 和 Post-LN 怎么选?

  • Pre-LN 残差路径干净,梯度稳定,适合深层大模型;需 final norm。
  • Post-LN 表达力略强,但对 warmup 和初始化敏感,深层易发散。
  • 现代大模型默认 Pre-RMSNorm。

Q7:SwiGLU 是什么?为什么 \(d_{ff}\) 取 \(8d/3\)?

  • SwiGLU 是带 Swish 门控的 FFN,包含 \(W_1,W_2,W_3\) 三个矩阵。
  • 标准 FFN 两个矩阵、\(d_{ff}=4d\),参数量 \(8d^2\)。
  • SwiGLU 三个矩阵,令 \(3d\cdot d_{ff}=8d^2\),得 \(d_{ff}=8d/3\),保持参数量对齐。

Q8:激活函数对模型能力影响大吗?

  • 有影响,但不是唯一因素;相同参数量下 SwiGLU/GeGLU 通常优于 ReLU。
  • 激活函数与 FFN 维度、归一化、初始化耦合,不能孤立比较。
  • 工程上还要考虑算子支持与推理速度。

Q9:为什么大模型训练容易不稳定?位置编码/归一化/激活各有什么责任?

  • 深层残差方差累积、attention logits 过大、FP16 溢出、学习率过大都可能不稳定。
  • 归一化控制数值尺度,Pre-LN 保证梯度路径,激活函数影响稀疏性与梯度。
  • 位置编码在外推时可能导致 attention 熵异常,表现为长文本性能骤降。
  • 解决要联合:BF16、warmup、梯度裁剪、QK-Norm、监控与回滚。

Q10:如果要把 4K 模型扩展到 128K,你会怎么做?

  • 位置编码:选择 PI/NTK/YaRN,按目标长度调整 base 或缩放因子。
  • 数据:加入长文本继续预训练,构造长依赖任务(检索、摘要、多跳)。
  • 工程:FlashAttention/Ring Attention、序列并行、KV Cache 分页/量化。
  • 评测:Needle-in-a-Haystack、RULER、LongBench,区分“检索到”和“推理好”。
  • 成本:128K 的 KV Cache 显存与延迟要单独估算,必要时用稀疏/分层注意力。

本文档为面试复习资料,公式与数字建议结合原始论文复核。