Lecture 3:Transformer 架构

9414 字
47 分钟
Lecture 3:Transformer 架构
文章摘要

Stanford CS336 Language Modeling from Scratch - Lecture 3: Architectures,时长 01:29:14。

架构调查方法论:从众多模型中提取共性#

Transformer 架构的设计空间极其广阔,但真正要做出好的架构决策,最高效的方式并不是闭门造车地搜索,而是系统性地横向对比已有的成功模型,从中提取共性与差异。这种方法论的核心逻辑是:当数十个独立团队在不同计算预算下做出相似选择时,这些收敛点很可能反映了某种深层规律。

回顾架构的历史脉络,可以大致划分为三个阶段。第一阶段(从原始 Transformer 到 GPT-3 左右)是自由探索期,各团队尝试大量不同设计,没有统一的”黄金标准”。第二阶段以 LLaMA 2 的发布为转折点——LLaMA 2 开源且性能强劲,几乎所有后续开源模型都以它为起点做微调变体,形成了一个短暂的”LLaMA-alike”收敛期。第三阶段(2024-2025 年至今)则出现了两个明确的分化趋势:一是对训练稳定性(stability)的架构级改进成为核心关切,二是对长上下文处理的架构创新(如混合注意力模式)开始大规模铺开。

为了系统化地观察这些趋势,一种有效的做法是将所有公开技术报告中的架构选择整理成一张对比表——行是各个模型(从原始 Transformer、GPT-2/3、LLaMA 系列到 Qwen、Gemma、InternLM、OLMo、Falcon 等),列是各个架构维度(norm 类型、激活函数、位置编码、是否并行 block 等)。通过这张表,可以直观地看出哪些选择已经高度收敛(例如几乎所有现代模型都用 RMS Norm + Pre-norm + SwiGLU + RoPE),哪些仍然存在显著分歧(例如位置编码的具体实现、是否使用滑动窗口注意力)。

图1:横跨数十个语言模型的架构选择对比表,蓝色/红色/黄色标记不同的设计选择,右侧显示趋势向 LLaMA-like 架构收敛
图1:横跨数十个语言模型的架构选择对比表,蓝色/红色/黄色标记不同的设计选择,右侧显示趋势向 LLaMA-like 架构收敛

这种”横向调查”方法论的价值在于:单独阅读任何一篇技术报告都很难获得全局视角,因为现代模型的技术报告普遍不公开完整细节。但当你把足够多的报告放在一起对比时,收敛的模式(patterns of convergence)本身就是极其宝贵的信息——它告诉你哪些决策是”安全的默认选择”,哪些决策的变化对最终性能影响很小(即存在一个”宽容的盆地”),以及哪些决策仍然值得探索。

在具体的架构选择上,有一个贯穿整个设计空间的核心约束需要始终牢记:架构必须同时满足三个目标。它必须能有效地从数据中学习(expressiveness),必须能高效地在 GPU 上运行(systems efficiency),还必须在长时间训练过程中不崩溃(stability)。这三个目标之间存在复杂的 tradeoff,而这正是架构设计”看起来不那么优雅”的根本原因——很多看似奇怪的设计选择,实际上是在这三维约束空间中的务实权衡。

Layer Norm:位置、类型与残差流的清洁性#

Post-Norm vs Pre-Norm:残差流必须保持清洁#

在原始 Transformer 中,Layer Norm 被放在残差路径内部(即 post-norm 配置):输入 xx 经过注意力或 FFN 子层后,先加回残差连接,然后才做归一化。这个设计在当时看起来自然,但后来被证明存在严重的梯度问题。

现代模型几乎无一例外地将 Layer Norm 移到了残差流外部,即所谓的 pre-norm 配置:先对 xx 做归一化,再送入子层计算(注意力或 FFN),计算结果作为 Δ\Delta 加回到未被归一化的残差流中。这样一来,原始的残差流 xx 从输入一路畅通地传播到输出,中间没有任何归一化操作对它进行”污染”。

为什么这很重要? 核心原理可以用一句话概括:保持残差流的清洁性(keep your residual stream clean)。在 pre-norm 配置下,xx 从网络底部一直不受干扰地传播到顶部,这意味着反向传播时梯度也可以沿着这条”高速公路”直达底层,不会被中间的归一化操作衰减或扭曲。具体而言,在 pre-norm + 适当初始化的条件下,梯度的范数在各层之间基本保持不变;而在 post-norm 配置下,每一层的归一化操作都会引入复杂的梯度交互效应,导致深层网络中梯度的行为难以预测。

这个差异的实际影响主要体现在两方面。第一是训练稳定性:Xiong et al. 的早期研究表明,pre-norm 即使不做 learning rate warm-up 也能稳定收敛,而 post-norm 在没有 warm-up 的情况下几乎无法训练。虽然现代训练流程通常仍然包含 warm-up,但 pre-norm 提供的额外稳定性裕度在大模型训练中极其宝贵。第二是深度可扩展性:Salazar 和 Nguyen 等人的实验表明,pre-norm 模型在增加层数时性能持续改善,而 post-norm 模型在深度增加到一定程度后会出现退化。

图2:Pre-norm vs Post-norm 的实验对比——左图(Salazar 2019)显示 pre-norm 变体的收敛曲线更优;右图(Xiong 2020)显示 post-norm 在无 warm-up 时完全无法收敛
图2:Pre-norm vs Post-norm 的实验对比——左图(Salazar 2019)显示 pre-norm 变体的收敛曲线更优;右图(Xiong 2020)显示 post-norm 在无 warm-up 时完全无法收敛

有一个模型在这个问题上构成了有趣的反面教材——OPT-350M,它是 OPT 系列中唯一仍然使用 post layer norm 的变体,而这个模型的训练过程也确实是”一团糟”(a mess)。至于为什么唯独这个尺寸的模型保留了 post-norm,原因不得而知,但它的存在恰好从反面印证了 pre-norm 的重要性。

Pre-Norm 之外的变体#

如果仅仅要求”Layer Norm 不在残差流内部”,那么 pre-norm 并不是唯一选择。Layer Norm 也可以放在子层之后(但仍然在残差流外部),这同样满足残差流清洁性的原则。

近年来若干模型采用了这种非残差 post-norm 的配置,包括 Grok、Gemma 2 和 OLMo 2——它们在注意力或 FFN 计算之后放置了 Layer Norm,但这个 norm 作用于子层的输出而非残差流本身。还有些模型走得更远,采用了 double norm 配置:在子层前后各放一个 Layer Norm。这看起来几乎是”哪里不稳定就往哪里撒 Layer Norm”的策略,听起来荒谬,但事实证明每次都管用——后续在 QK Norm 的讨论中还会再次看到这个模式。

图3:非残差 post-norm(右)与 pre-norm(左)的结构对比——两者都保持残差流清洁,Layer Norm 均在残差路径之外
图3:非残差 post-norm(右)与 pre-norm(左)的结构对比——两者都保持残差流清洁,Layer Norm 均在残差路径之外

RMS Norm:去掉不必要的计算#

在确定了 norm 的位置之后,下一个问题是 norm 的类型。原始 Transformer 使用标准的 Layer Norm,其计算过程是:减去均值、除以标准差、用可学习参数缩放和偏移。而现代模型基本全部切换到了 RMS Norm(Root Mean Square Normalization),它省去了均值减除和偏置项,仅保留了”除以 RMS 后重新缩放”的操作。

从表达能力角度看,Layer Norm 严格强于 RMS Norm(多了均值偏移的能力)。但大量实验表明,这个额外的表达能力在语言建模任务中几乎没有贡献——RMS Norm 的建模性能与 Layer Norm 基本相当。

那为什么要切换?原因纯粹是系统效率。这里涉及一个关键概念:算术强度(arithmetic intensity),即计算量与数据搬运量的比值。GPU 的设计使其擅长高算术强度的操作(如矩阵乘法),但对低算术强度的操作(需要大量数据搬运但计算量小)非常低效。Layer Norm 恰好属于后者——虽然它在总 FLOPs 中只占约 0.17%,但由于涉及逐元素的统计计算和参数读写,在某些配置下可以占到总运行时间的 25%。这个巨大的差距正是”FLOPs 不等于 runtime”这一事实的典型体现。

图4:FLOPs 与 Runtime 的巨大差距——统计归一化操作仅占 0.17% 的 FLOPs 却占 25.5% 的 Runtime,右图显示各操作的算术强度(FLOP-to-memory ratio)
图4:FLOPs 与 Runtime 的巨大差距——统计归一化操作仅占 0.17% 的 FLOPs 却占 25.5% 的 Runtime,右图显示各操作的算术强度(FLOP-to-memory ratio)

RMS Norm 通过去掉均值计算和偏置加法,减少了数据搬运量(少了一组参数需要读写,少了一次逐元素减法),从而提升了这部分的算术强度。实测数据(Narang et al. 2020 等)表明,在 200M 参数的 Transformer 上,切换到 RMS Norm 可以直接获得更高的每秒训练步数,同时性能甚至略有提升——这是一个罕见的”免费午餐”。

去掉 Bias Terms:同一思路的推广#

RMS Norm 去掉均值和偏置的思路,实际上是一个更一般原则的具体实例:对于算术强度低但表达能力贡献微弱的操作,直接去掉。沿着这个思路,现代模型进一步去掉了几乎所有线性层中的 bias terms

原始 Transformer 中每个线性层都带有偏置项 bb(即 y=Wx+by = Wx + b),但大多数现代实现直接去掉了它(y=Wxy = Wx)。原因与 RMS Norm 完全一致:bias 项的参数量很小(与权重矩阵相比可忽略),但每次前向和反向传播都需要额外的内存读写和逐元素加法操作。在偏置项对最终性能几乎没有贡献的前提下,去掉它们换取系统效率的提升是值得的。此外,也有一些证据表明 bias 项在某些情况下还会引入训练不稳定性

一个反复出现的架构设计原则:事先无法通过理论推导知道哪些组件可以安全去掉——这些结论来自大量实验和集体经验的积累。从多个成功模型的共同实践中,我们大致知道在典型的语言建模负载下,去掉 RMS Norm 的偏置和线性层的偏置都是安全的。

激活函数与 Gated Linear Units#

从 ReLU 到 GELU:激活函数的微小改进#

在讨论 FFN(前馈网络)层的激活函数时,首先需要明确一点:仅使用 ReLU 也完全可以训练出合格的语言模型。Chinchilla(可能是 ReLU 阵营中最好的模型)证明了这一点。所以这里讨论的不是”必须换”,而是”换了能获得多少增益”。

从 ReLU 到 GELU(Gaussian Error Linear Unit)的变化非常微小:两者的形状在绝大部分区间完全一致,唯一的差异是 GELU 在零点附近有一个小小的凹陷(divot),使得梯度在零点处平滑过渡而非突变。GPT-3 使用的就是 GELU,这是一个完全合格的大语言模型选择。

Gated Linear Units:现代 FFN 的标准配置#

真正带来显著改进的是 Gated Linear Units(GLU)家族。要理解 GLU,先回顾标准的 ReLU FFN 结构:

FFN(x)=W2ReLU(W1x)\text{FFN}(x) = W_2 \cdot \text{ReLU}(W_1 x)

这是一个简单的两层结构:W1W_1 将输入从 dmodeld_{\text{model}} 映射到 dffd_{\text{ff}}(通常是 4×dmodel4 \times d_{\text{model}}),经过 ReLU 后,W2W_2 将其映射回 dmodeld_{\text{model}}

GLU 的核心思想是在此基础上引入一个门控机制

GLU(x)=W2[σ(W1x)(Vx)]\text{GLU}(x) = W_2 \cdot \left[\sigma(W_1 x) \odot (Vx)\right]

其中 VV 是一个额外的权重矩阵,VxVx 作为”门控信号”逐元素地调制 σ(W1x)\sigma(W_1 x) 的输出。σ\sigma 可以是任何激活函数,不同的选择产生不同的 GLU 变体:

  • ReGLUσ=ReLU\sigma = \text{ReLU},即 ReLU(W1x)(Vx)\text{ReLU}(W_1 x) \odot (Vx)
  • GeGLUσ=GELU\sigma = \text{GELU}
  • SwiGLUσ=Swish\sigma = \text{Swish},其中 Swish(x)=xsigmoid(x)\text{Swish}(x) = x \cdot \text{sigmoid}(x)

命名规则很简单:取激活函数名 + “GLU” 后缀。门控(gating)本身是深度学习中一个历史悠久且被反复验证有效的原语(primitive),它在语言建模的 FFN 层中同样有效。

在实际使用分布上,SwiGLU 是目前最主流的选择——几乎所有 LLaMA 系列的后代模型(LLaMA、Mistral、Qwen、InternLM 等)都使用 SwiGLU。Google 系的模型(Gemma、T5 等)则倾向于使用 GeGLU。两者之间的性能差异极小,重要的是使用某种 GLU 变体而非不使用这个二元选择。

2/3 缩放规则:保持参数量不变#

引入 GLU 后有一个重要的工程细节。标准 FFN 有两个权重矩阵 W1W_1W2W_2;GLU 变体则有三个——W1W_1VVW2W_2。如果保持 dffd_{\text{ff}} 不变,GLU 的参数量会比标准 FFN 多出约 50%。

为了做公平比较(parameter-matched comparison),标准做法是将 GLU 的 dffd_{\text{ff}} 缩小为原来的 2/3。这样三个矩阵的总参数量大致等于原来两个矩阵的参数量。Noam Shazeer 在原始论文中正是这样做的,他的实验在参数量严格对齐的条件下反复证明 GLU 变体几乎总是优于非 GLU 基线——虽然每次的增量不大,但方向一致且统计显著。

图5:GLU 变体 vs 非 GLU 基线的系统性对比(Narang et al. 2020)——在参数量对齐的条件下,GeGLU、SwiGLU 等门控变体在 loss 和下游任务上一致优于 Vanilla Transformer
图5:GLU 变体 vs 非 GLU 基线的系统性对比(Narang et al. 2020)——在参数量对齐的条件下,GeGLU、SwiGLU 等门控变体在 loss 和下游任务上一致优于 Vanilla Transformer

后续 Google 在 T5 架构上的大规模对比实验(Narang et al. 2020)进一步确认了这一结论:在下游任务指标和语言建模 loss 上,SwiGLU 和 GeGLU 都显著优于 ReLU 和 GELU。

非 GLU 模型:存在但稀少#

虽然 GLU 的证据很强,但也有少数知名模型不使用 GLU 而仍然表现良好。GPT-3 使用标准 GELU,NeMoTron 340B 使用了一个颇为激进的选择——Squared ReLUReLU(x)2\text{ReLU}(x)^2)。这些模型的存在说明 GLU 并非绝对必要条件,但在当前的开源模型生态中,不使用 GLU 的模型已经极其罕见,证据指向 GLU 带来的增益是一致且免费的(在参数量对齐后几乎没有额外计算成本)。

并行 vs 串行 Transformer Block#

标准 Transformer Block 的结构是串行的:先计算注意力,再计算 FFN,两者依次执行。从系统优化的角度看,这引入了一个天然的瓶颈——必须等待注意力计算完成后才能开始 FFN 计算,两者无法重叠。

一个自然的想法是:能否将注意力和 FFN 并行计算? 即同时从残差流 xx 出发,分别计算注意力的 Δattn\Delta_{\text{attn}} 和 FFN 的 Δffn\Delta_{\text{ffn}},然后将两者同时加回残差流。用公式表达:

  • 串行xl+1=xl+FFN(xl+Attn(xl))x_{l+1} = x_l + \text{FFN}(x_l + \text{Attn}(x_l))
  • 并行xl+1=xl+Attn(xl)+FFN(xl)x_{l+1} = x_l + \text{Attn}(x_l) + \text{FFN}(x_l)

这个想法最早出现在 GPT-J(EleutherAI 的开源模型)中,后来被 Google 的 PaLM 大规模采用并详细论证。并行配置的系统优势在于:注意力和 FFN 可以共享 Layer Norm(只需做一次而非两次),QKV 投影和 FFN 的第一个矩阵乘法可以融合(fuse),从而获得约 15% 的系统吞吐提升(PaLM 报告的数据)。

受 Google 影响的团队(如 Cohere,其创始人是 Transformer 论文的作者之一)也采用了并行配置。但整体来看,这种设计在 2024 年后明显失去了人气。原因有两方面:

  1. 表达能力损失:并行配置本质上等价于将网络深度减半——注意力和 FFN 不再以串行方式逐层精化表示,而是同层并列地各自作用,丧失了”先注意、后处理”的分层抽象能力。

  2. 串行优化已足够好:随着 GPU 编程和算子融合技术的进步(如 FlashAttention 等),串行配置的系统瓶颈已经大幅缓解,并行配置带来的那 15% 系统增益不再值得付出表达能力的代价。

值得注意的是,PaLM 论文对并行配置相当自信,声称”无性能损失、15% 系统提升”。但后续的 Google 模型(如 Gemma 系列)已经放弃了并行配置,转回串行结构——这可以被视为一个隐含的信号,说明并行配置可能确实存在不可忽略的性能损失。遗憾的是,据目前公开文献所知,还没有人做过严格控制变量的并行 vs 串行消融实验来定量回答这个问题。

从更高层面看,这些”核心架构”层面的变化(norm 位置、激活函数类型、是否并行)实际上都是相当温和的改动——原始 Transformer 的整体结构(残差连接 + 注意力 + FFN 的交替堆叠)至今仍然是几乎所有密集注意力模型的基础框架。真正产生大分化的领域是位置编码和注意力头的变体设计。

位置编码:从 Sinusoidal 到 RoPE#

为什么位置编码是必需的#

标准的自注意力机制本质上是位置无关的——它只计算 token 嵌入之间的内积,将输入序列打乱后注意力的输出完全不变。这意味着如果不引入任何位置信息,模型无法区分 “the cat sat on the mat” 和 “mat the on sat cat the”。因此,位置编码是 Transformer 架构中一个不可或缺的组件。

位置编码方案的演进#

Sinusoidal 编码(原始 Transformer):使用不同频率的正弦和余弦函数为每个位置生成一个固定的向量,将其加到 token embedding 上。这种方案的动机来自傅里叶变换的直觉——不同频率的正弦波组合理论上可以唯一表示任何位置。但问题在于,当我们计算两个位置嵌入的内积时,会产生绝对位置的交叉项,使得注意力分数依赖于 token 的绝对位置而非相对位置。

Absolute position embeddings(GPT-2/3 等):为每个位置分配一个可学习的嵌入向量。简单直接,但显然是绝对位置编码——模型直接”记住”每个位置对应什么向量,推理时超出训练长度的位置没有对应的嵌入。

Relative position embeddings(T5、Chinchilla 等 Google 模型):不在 token embedding 层面加位置信息,而是直接在注意力矩阵上加一个依赖于两个 token 相对距离的偏置。这确实实现了相对位置编码,但它是以注意力矩阵上的加性偏置形式实现的,而非作为嵌入函数之间内积的结果——从数学结构上看,这种编码不能分解为 f(xi,i)f(x_i, i)f(xj,j)f(x_j, j) 的内积形式。

RoPE:旋转位置编码#

RoPE(Rotary Position Embedding)是 2021 年提出的方案,来自一篇相对低调的中国作者的博客和论文,但迅速成为 2024 年之后几乎所有主流语言模型的标准选择。

RoPE 的设计动机可以表述为一个精确的数学需求:我们希望存在一个位置编码函数 ff,使得对于任意两个 token xix_ixjx_j 位于位置 iijj,满足:

f(xi,i), f(xj,j)=g(xi,xj,ij)\langle f(x_i, i),\ f(x_j, j) \rangle = g(x_i, x_j, i-j)

即两个编码后向量的内积只依赖于 token 本身的身份和它们之间的相对距离 iji-j,完全不依赖绝对位置。

RoPE 的解决方案出人意料地简单:旋转。核心思想是,取每个 token 的语义嵌入向量(不含任何位置信息),然后根据该 token 所处的位置对向量进行旋转。旋转具有一个关键性质:两个向量之间的夹角(从而内积)在旋转后只取决于它们相对旋转的角度差,而与各自的绝对旋转角度无关。

以二维情形为例。假设序列 “we know that” 中,“we” 在位置 0、“know” 在位置 1:

  • “we” 不旋转(位置 0),保持原始方向
  • “know” 旋转一个角度 θ\theta(位置 1)

现在考虑另一个序列 “of course we know”,其中 “we” 在位置 2、“know” 在位置 3:

  • “we” 旋转 2θ2\theta(位置 2)
  • “know” 旋转 3θ3\theta(位置 3)

关键观察:虽然绝对旋转角度不同,但 “we” 和 “know” 之间的相对角度差始终是 θ\theta,因此它们的内积在两种情况下完全相同。这正是我们要的——相对位置编码的性质。

从二维到高维:分块旋转#

在二维情况下旋转的选择是唯一的(顺时针或逆时针),但在 DD 维空间中旋转方式有无穷多种。RoPE 采用了最简单的策略:将 DD 维向量切分为 D/2D/2 对,每对维度独立进行二维旋转。不同的维度对使用不同的旋转频率 θk\theta_k

  • 低频维度对旋转缓慢 → 编码长程依赖
  • 高频维度对旋转迅速 → 编码近邻关系

图6:RoPE 旋转位置编码的几何直觉——上方展示二维旋转的基本思想,下方展示高维情况下的分块旋转策略(每对维度独立旋转),右侧为 Gemma 4 的 P-RoPE 变体(仅旋转前两个维度对)
图6:RoPE 旋转位置编码的几何直觉——上方展示二维旋转的基本思想,下方展示高维情况下的分块旋转策略(每对维度独立旋转),右侧为 Gemma 4 的 P-RoPE 变体(仅旋转前两个维度对)

具体实现上,对于第 kk 对维度(共 D/2D/2 对),在位置 mm 处的旋转角度为 mθkm \cdot \theta_k,其中 θk=100002k/D\theta_k = 10000^{-2k/D}。最终的操作等价于一个稀疏的矩阵乘法:

f(x,m)=(x1cosmθ1x2sinmθ1x1sinmθ1+x2cosmθ1xD1cosmθD/2xDsinmθD/2xD1sinmθD/2+xDcosmθD/2)f(x, m) = \begin{pmatrix} x_1 \cos m\theta_1 - x_2 \sin m\theta_1 \\ x_1 \sin m\theta_1 + x_2 \cos m\theta_1 \\ \vdots \\ x_{D-1} \cos m\theta_{D/2} - x_D \sin m\theta_{D/2} \\ x_{D-1} \sin m\theta_{D/2} + x_D \cos m\theta_{D/2} \end{pmatrix}

虽然这看起来像 sinusoidal embedding(也涉及 sin 和 cos),但关键区别在于 RoPE 是乘法性的(对向量做旋转),而 sinusoidal 是加法性的(将位置向量加到 token 嵌入上)。乘法性确保了没有交叉项,因此内积严格只依赖相对位置。

实现要点与最新变体#

RoPE 应用于注意力计算中的 Q 和 K 向量(不是 V),且是在每一层的注意力操作中独立应用(而非仅在输入层加一次)。这意味着每一层的注意力计算都独立地获得位置信息,形成更强的位置编码信号。

Gemma 4(2026 年 5 月发布)引入了 P-RoPE(Proportional RoPE),核心变化令人意外地简单:只对前两个维度进行旋转,其余维度保持不动。这背后的直觉是:低频维度对的旋转速度极慢,对位置编码的贡献微乎其微,不如直接省略。这在隐藏维度较小的模型中尤其有意义——将有限的维度预算集中在真正有效的高频旋转上。

超参数:FF 比例、头维度与 Aspect Ratio#

当从抽象层面转向实际训练一个语言模型时,一系列具体的数值选择变得不可回避:FF 层的维度应该是隐藏维度的多少倍?注意力头的维度应该设为多少?模型应该更深还是更宽?这些问题构成了一个看似庞大的搜索空间,但令人欣慰的是,绝大多数现代模型在这些选择上已经高度收敛——存在一组”安全的默认值”,偏离它们需要有充分的理由。

Feed-Forward 比例:4x 法则及其变体#

dff/dmodeld_{\text{ff}} / d_{\text{model}} 这个比值控制着 FFN 层的”宽度”,决定了 MLP 内部中间表示的维度。原始 Transformer 选择了 4x(dff=4×dmodeld_{\text{ff}} = 4 \times d_{\text{model}}),而这个看似随意的选择居然成为了延续至今的黄金标准。

Kaplan et al. 2020(经典 neural scaling laws 论文)的一个附带实验揭示了原因:当在小模型上系统性地扫描这个比值时,发现从约 1 到约 10 的范围内存在一个非常平坦的盆地——loss 对这个超参数极不敏感。只有当比值偏离到 10 以上时,性能才会开始急剧恶化。这意味着 2.6 到 8 之间的任何选择都”差不多一样好”,4 恰好是这个盆地中间的一个方便数字。

但现代模型中这个比值的实际分布出现了一些有趣的变化:

  • GLU 变体的 2/3 修正:使用 SwiGLU 等门控激活时,为保持参数量不变需要将 dffd_{\text{ff}} 乘以 2/3,因此实际比值变为 4×2/32.674 \times 2/3 \approx 2.67。查看实际模型配置会发现大量模型的比值在 2.5-2.67 附近。

  • LLaMA 的偏移:LLaMA 团队因为使用了高效的多查询注意力(MQA)节省了参数预算,决定将 FFN 比值上调到约 3.5(即标准 2.67 乘以 1.33),实质上将节省的参数重新分配给 MLP 层。这个选择被后续的 LLaMA 后代广泛沿用。

  • T5 的极端实验:T5 V1 使用了惊人的 64x 比值——这是一个极其激进的系统导向选择,理由是更大的矩阵乘法具有更高的算术强度,能更充分利用 GPU。但有趣的是,T5 V1.1(官方的”改进版”)悄悄回退到了标准的 2.5x,暗示 64x 在计算效率上确实不如标准选择。

注意力头维度:dhead×H=dmodeld_{\text{head}} \times H = d_{\text{model}}#

在多头注意力中,一个几乎所有模型都遵循的约定是:每个头的维度 dheadd_{\text{head}} 乘以头的数量 HH 恰好等于模型维度 dmodeld_{\text{model}}。换言之,多头注意力的总参数量与单头注意力完全相同,只是被”切分”成了多个独立的注意力子空间。

虽然这个比值完全可以任意调整,但绝大多数成功模型都严格遵循这个等式。这又是一个”宽容的”超参数——消融实验表明围绕 1.0 存在一个宽广的盆地,偏离不大时性能变化很小。典型的 dheadd_{\text{head}} 选择是 64 或 128。

Aspect Ratio:深度与宽度的平衡#

Aspect ratio 定义为 dmodel/nlayersd_{\text{model}} / n_{\text{layers}},控制着模型在深度和宽度之间如何分配参数预算。实证数据给出了一个出人意料地一致的答案:大约 100。无论是 GPT-3(12288 维 / 96 层 ≈ 128)、LLaMA 65B(8192 维 / 80 层 ≈ 102)还是其他主流模型,aspect ratio 都落在 80-128 的范围内。

图7:Aspect ratio 的实验证据——左图(Kaplan et al. 2020)显示不同模型尺寸下的 loss 随 aspect ratio 变化,最优值稳定在 100 附近;右图(Tay et al. 2021)确认总参数量对性能的影响远大于具体的 aspect ratio 选择
图7:Aspect ratio 的实验证据——左图(Kaplan et al. 2020)显示不同模型尺寸下的 loss 随 aspect ratio 变化,最优值稳定在 100 附近;右图(Tay et al. 2021)确认总参数量对性能的影响远大于具体的 aspect ratio 选择

这个收敛点反映了表达能力和系统效率之间的 tradeoff。更深的模型在并行化时非常痛苦——pipeline parallelism 是所有并行策略中实现最复杂、效率最低的;而宽度方向的 tensor parallelism 则简单得多。另一方面,过浅的模型在表达能力上可能受限。约 100 的 aspect ratio 恰好是这两个约束的交汇点。

词表大小与正则化的反直觉作用#

词表大小:单语 vs 多语的分界#

词表大小(vocabulary size)是一个与模型用途强相关的超参数。从历史数据中可以清晰地看到一条分界线:

  • 单语模型(早期开源模型,目标仅为英文性能):词表大小约在 30,000 量级。典型代表是 GPT-2(50,257)、早期 LLaMA(32,000)等。
  • 多语/生产级模型(后 LLaMA 时代,面向多语言和实际部署):词表大小跃升到 100,000-260,000 量级。LLaMA 衍生模型大多在 100K 左右,Google 模型(Gemini/Gemma 系列)则更高。

这种分化有清晰的因果逻辑:多语言模型需要覆盖更大的字符空间,每种语言都需要足够的 subword 单元来实现合理的压缩率。此外,scaling law 研究还揭示了另一个维度:模型越大,能够有效利用的词表就越大——在小模型中过大的词表会使 embedding 层占据不成比例的参数预算。

关于 tokenizer 比较的一个重要方法论点:bits per byte (BPB) 是跨不同 tokenizer 比较语言模型质量的正确指标。不同 tokenizer 将同一文本切分为不同数量的 token,因此 per-token 的 perplexity 不可直接比较。但 BPB 将似然值归一化到原始字节数(对同一文本固定),消除了 tokenizer 选择的影响。

正则化:在不过拟合的世界中为何仍然有用#

从教科书的逻辑出发,语言模型训练几乎不需要正则化:训练数据量远超模型容量,大多数训练流程只做单次遍历(single epoch),训练 loss 和验证 loss 之间几乎没有 gap。

Dropout 在现代大规模语言模型训练中已经基本被淘汰Weight decay 的情况则令人困惑得多:它在现代模型中仍然被广泛使用(典型值 0.1),尽管从正则化角度看完全没有必要。

这种看似矛盾的现象有一个精妙的解释:weight decay 在语言模型训练中的角色已经从正则化器变成了优化器的辅助组件。具体证据是:在恒定学习率下,weight decay 几乎不产生任何效果;但当学习率衰减与 weight decay 结合时,训练后期的 loss 显著低于无 weight decay 的基线。解释是 weight decay 通过持续将权重向零收缩,使得后期的学习率衰减更加有效——它帮助优化器在训练末期更平滑地收敛到更好的 minimum。

深层教训:架构和超参数的选择之所以困难,正是因为这类非直觉的交互效应无处不在。一个组件的功能不能仅从其数学定义推断——它与优化器、学习率调度、模型深度等因素的交互可能完全改变其角色。

训练稳定性:Softmax 危险区与防御策略#

当训练成本达到数百万美元量级时,模型在训练中途”炸掉”(loss 突然飙升或梯度爆炸导致训练不可恢复)是灾难性的。因此,训练稳定性已经从”锦上添花”变成了架构设计的核心约束之一。

从数值分析的角度看,Transformer 中有两个天然的数值危险区:输出层的 softmax(将 logits 转换为概率分布)和注意力层的 softmax(将 QK 内积转换为注意力权重)。softmax 的数值脆弱性来自两个操作:指数函数(增长极快,极易溢出)和除法(分母接近零时结果爆炸)。

输出 Softmax 的防御:Z Loss#

语言模型的交叉熵 loss 为 L=uy+logZ\mathcal{L} = -u_y + \log Z,其中 Z=jexp(uj)Z = \sum_j \exp(u_j)。softmax 具有平移不变性——对所有 logits 同时加一个常数不改变输出概率分布。这意味着 logits 的”整体水平”是一个未被约束的自由度,可能漂移到数值不安全的区域。

Z loss trick 直接惩罚这个漂移:Ltotal=LCE+α(logZ)2\mathcal{L}_{\text{total}} = \mathcal{L}_{\text{CE}} + \alpha \cdot (\log Z)^2。当 logZ0\log Z \approx 0 时,整个 softmax 计算处于数值最稳定的状态。这个 trick 最早由 Jacob Devlin 在 2014 年提出,后通过 Baichuan、DCLM、OLMo 等开源模型重新流行。

注意力 Softmax 的防御:QK Norm#

QK Norm 在计算 QKTQK^T 之前,对 QQKK 分别做一次 RMS Norm。由于 RMS Norm 将向量范数归一化到大约 1,softmax 的输入值域被稳定在可预测的范围内。

QK Norm 的发现有一个有趣的历史背景:它最初并非来自纯语言模型的研究,而是来自多模态模型的训练——Dehghani et al. 在做视觉-语言模型时最先发现了这个技巧,Chameleon 等模型进一步验证了它的效果。后来人们意识到,同样的注意力退化问题在纯语言模型中也存在,QK Norm 的价值被推广到了整个语言模型领域。

大量实验表明 QK Norm 不损害模型性能,但能有效预防注意力退化。更进一步,由于注意力分数的方差被控制住了,训练过程实际上可以使用更高的学习率,反而带来轻微的性能提升。QK Norm 已经成为 2025 年后的标准干预手段,进一步印证了”哪里不稳定就往哪里加 Layer Norm”这个屡试不爽的策略。

Logit Soft Capping:更强但有代价的防御#

Logit soft capping 直接对 softmax 输入值施加上下界:logitscapped=ctanh(logits/c)\text{logits}_{\text{capped}} = c \cdot \tanh(\text{logits} / c),确保值永远在 [c,c][-c, c] 范围内。Google 的 Gemma 2/3/4 都使用了这个技巧。

但 Nvidia 的系统性对比实验揭示了一个 tradeoff:logit soft capping 虽然极其安全,但会轻微降低模型性能——它剥夺了模型表达高置信度注意力的能力。相比之下,QK Norm 既能稳定训练又不损害性能,是更受推荐的首选。

注意力机制变体:GQA 与 Sliding Window Attention#

推理瓶颈:KV Cache 的内存代价#

在推理阶段,模型必须逐 token 自回归生成。为避免重复计算,实践中使用 KV cache 缓存历史的 K 和 V 向量。这节省了计算但引入了新瓶颈。

训练/预填充(prefill)阶段,整个序列并行处理,总算术操作量为 O(BND2+BN2D)O(BND^2 + BN^2D),总内存访问为 O(BND+BN2+D2)O(BND + BN^2 + D^2),算术强度的主导项为 O(dhead)O(d_{\text{head}})O(BN)O(BN)——只要头维度够大、batch×序列够长,GPU 就能被充分利用。

但在 KV cache 推理(decode)阶段,每步只生成一个 token,KV cache 中的所有历史 K/V 必须被读入但只参与极少的计算。总内存访问中出现了 O(ND2)O(ND^2) 项(读取整个 KV cache × 模型维度),导致算术强度退化为 O(N/D+1/B)O(N/D + 1/B)。这要求大 batch + 短序列 + 大隐藏维度才能维持效率——而这些条件在实际服务场景中往往难以同时满足。这就是推理过程受限于内存带宽的根本数学原因。

图8:KV cache 的增量计算机制——每步只需计算新 token 的 Q 与整个 KV cache 的注意力,历史的 K/V(灰色)被缓存复用
图8:KV cache 的增量计算机制——每步只需计算新 token 的 Q 与整个 KV cache 的注意力,历史的 K/V(灰色)被缓存复用

从 MQA 到 GQA:精确控制效率-质量 Tradeoff#

Multi-Query Attention (MQA) 让所有注意力头共享同一组 K 和 V,KV cache 缩小 HH 倍。算术强度中的问题项从 N/DN/D 变为 HN/DHN/DHH(头数,通常 32-128)带来的提升是巨大的。但代价是表达能力大幅下降——所有头被迫使用完全相同的 Key-Value 映射。

Grouped Query Attention (GQA) 是 MQA 和标准 MHA 之间的连续插值:将 HH 个 query head 分成 GG 组,每组共享一组 KV。当 G=HG = H 时退化为 MHA,G=1G = 1 时退化为 MQA,典型选择 G=4G = 4G=8G = 8

GQA 的 tradeoff 曲线非常有利。在 T5 时期的实验中,MHA 性能最好但推理成本最高;MQA 大幅降低成本但性能显著下降;如果试图通过缩小模型来匹配 MQA 的成本,性能损失更大。GQA 在这三者之间找到了一个甜点:从 MHA 减少到 G=8G = 8 组 KV 时,性能几乎没有下降,但推理成本大幅降低——“small reduction in the number of KV groups gets you most of the gains”。从 LLaMA 2/3 到 Mistral、Qwen 3,GQA 已经成为事实上的标准配置

此外,DeepSeek-V2 提出了另一条路线——Multi-Head Latent Attention (MLA),通过对 KV 做低秩分解来压缩 cache,与 GQA 有着不同的 factorization 结构和 tradeoff 特征,但同样致力于解决推理阶段的 KV cache 瓶颈。

Sliding Window Attention:混合架构管理长上下文#

标准全局注意力的成本与序列长度的平方成正比。Sliding window attention 将注意力范围限制在固定大小的局部窗口内,将复杂度降为 O(NW)O(N \cdot W)

这个想法实际上相当古老——GPT-3 在其论文中就已经描述了在全局注意力和 banded matrix 风格的窗口注意力之间交替的策略,OpenAI 也有更早期的工作研究不同的注意力模式。但直到 2024-2025 年,随着长上下文需求的急剧增长,这种混合设计才真正大规模铺开。

现代最佳实践是混合架构:每 4 层中 1 层全局注意力、3 层滑动窗口。滑动窗口层高效处理局部模式,全局层负责跨长距离传递信息。近期开源模型中,Cohere Command A 是最早大规模推广这种结构的之一,其后 LLaMA 4Gemma 4OLMo 3 都采纳了类似设计。

更进一步的探索中,有些模型在全局注意力层中去掉位置编码(NoPE——No Position Embedding),使长程信息以”是否出现”而非”出现在哪里”的方式被使用。Qwen 3.5 则用 State Space Model 替代滑动窗口作为”廉价层”,以每 4 层 1 层全局注意力的比例交替。

注意力机制的变体设计是当前架构研究中最活跃的前沿领域——混合结构(廉价的局部/循环层 + 稀疏的全局注意力层)已经成为 2025-2026 年的主流范式。

Lecture 3:Transformer 架构
https://www.xwysyy.cn/posts/cs336/lec03/
作者
xwysyy
发布于
2026-05-17
许可协议
CC BY-NC-SA 4.0
© 2026 xwysyy. All Rights Reserved.
Powered by Astro & Firefly

文章目录