位置编码、归一化与激活函数¶
本章导读: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\) 是一个置换矩阵,则:
也就是说,打乱 token 顺序,输出只是跟着打乱,模型本身感知不到“谁在前谁在后”。但语言是有序的: “我打你”和“你打我”含义完全不同。因此必须把位置信息注入表示。
位置编码要回答三个问题:
- 绝对还是相对:模型需要知道“第 5 个位置”,还是只需要知道“两个 token 相距 3”?
- 如何外推:训练长度 4K,推理 32K 时还能不能用?
- 计算代价:是否增加参数、是否影响 KV Cache、是否兼容 FlashAttention?
2. 绝对位置编码¶
2.1 正弦位置编码¶
原始 Transformer 使用固定正弦函数:
其中 \(pos\) 是位置,\(i\) 是维度索引,\(d\) 是模型维度。
性质:
- 不同维度对应不同频率,低维变化快、高维变化慢,形成类似二进制计数的多尺度表示。
- 对于固定偏移 \(k\),\(PE_{pos+k}\) 可以表示为 \(PE_{pos}\) 的线性变换,理论上模型能学到相对关系。
- 无参数,不增加显存。
缺点:外推能力有限;模型在训练长度之外的位置上没见过,性能会下降;对长上下文不够灵活。
2.2 可学习绝对位置编码¶
为每个位置分配一个可训练向量 \(p_i\in\mathbb{R}^d\),与 token embedding 相加:
代表模型:BERT、GPT-2(GPT-2 实际使用可学习位置编码)。
优点:简单、表达自由;缺点:最大长度在训练前固定,超出范围没有定义;参数量为 \(L_{\max}\times d\),长上下文下不可忽视。
3. 相对位置编码¶
相对位置编码不关心“绝对第几位”,而是建模 token 之间的距离。
3.1 T5 相对位置偏置¶
在注意力 logits 上加一个可学习的标量偏置,偏置按距离分桶:
距离被映射到若干桶(例如 32 个),近距离用精确距离,远距离共享桶。这样参数少,且能泛化到未见过的距离。
3.2 ALiBi¶
ALiBi(Attention with Linear Biases)直接按距离加线性惩罚:
每个头有不同斜率 \(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})\) 按位置相关的角度旋转:
其中 \(m\) 是位置,\(\theta_i\) 是第 \(i\) 对的基频:
4.2 为什么内积只依赖相对位置¶
二维旋转矩阵 \(R(m\theta)\) 满足 \(R(m\theta)^\top R(n\theta)=R((n-m)\theta)\)。把 RoPE 分别作用于 \(q_m\) 和 \(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 的每个维度对应一个波长:
低维(\(i\) 小)波长短,变化快,负责局部位置;高维波长长,变化慢,负责全局位置。训练长度决定了模型见过哪些相对距离,超出后高频维度会“转过多圈”,导致位置混淆。
4.5 长度外推的三类方法¶
位置插值(Position Interpolation, PI) 把推理位置 \(m\) 压缩到训练范围内:
相当于整体降低旋转角度。优点是简单,缺点是所有频率一起压缩,局部分辨率下降,需要少量微调恢复。
NTK-aware 缩放 不直接改位置,而是调整基频:
低频维度几乎不变(保留全局),高频维度被拉伸(保持局部分辨率)。比 PI 更平滑,通常配合少量长文本微调。
YaRN 结合 NTK 缩放与注意力温度修正,并对不同频率分段处理,减少长上下文下的注意力熵变化。YaRN 在无需大量微调的情况下即可扩展数倍长度,但仍建议做长文本继续训练。
其他:LongRoPE(分维度搜索缩放因子)、NTK-by-parts、动态 NTK(按当前长度实时调整)。
面试表达:外推不是“改个 base 就无限长”。真正的长上下文能力还依赖训练数据、注意力实现(FlashAttention/Ring Attention)、KV Cache 显存和评测方法。位置编码只是其中一环。
5. 归一化¶
5.1 LayerNorm¶
对每个样本的每个位置,在特征维度上归一化:
其中 \(\gamma,\beta\in\mathbb{R}^d\) 是可学习缩放与偏移。
特点:
- 与 batch size 无关,训练/推理行为一致;
- 对每个 token 独立归一化,适合变长序列;
- 计算需要均值和方差两次归约。
5.2 RMSNorm¶
去掉了均值中心化和偏移 \(\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:
简单、稀疏,但在 0 点不可导,负半轴梯度为 0,深层可能出现“死亡神经元”。
GELU:
其中 \(\Phi\) 是标准正态分布的累积分布函数。常用近似:
GELU 比 ReLU 更平滑,在 Transformer(BERT、GPT-2)中广泛使用。
6.2 SwiGLU¶
门控线性单元(GLU)的一般形式:
SwiGLU 用 Swish 作为门控:
其中 \(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\) 对齐:
实际会向上取整到 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 显存与延迟要单独估算,必要时用稀疏/分层注意力。
本文档为面试复习资料,公式与数字建议结合原始论文复核。