LECTURE 03

架构与超参数:现代 LM 到底长什么样

从 2017 年的原始 Transformer 到 2026 年的 LLaMA-like 默认配方,每一处改动背后的动机、证据与代价。

讲师:Tatsunori Hashimoto 日期:2026-04-06 原始材料:lecture_03.pdf

0. 本讲导读

你在作业里实现的那个 Transformer——前置 RMSNorm、SwiGLU 前馈层、RoPE 位置编码、所有线性层都不带偏置——和 2017 年《Attention Is All You Need》里的原始模型已经不是同一个东西了。每一处改动都有人做过消融实验,也都有模型反着来还活得好好的。本讲要回答的就是:这些选择是怎么来的,哪些是真有证据的共识,哪些只是大家互相抄。

与前后两讲的关系:Lecture 02 教你把一个模型的 FLOPs、参数量、显存占用算清楚,那是「预算」;本讲讲的是怎么花这笔预算——同样的参数量,放在深度上还是宽度上?放在前馈层还是注意力头上?Lecture 04 会把注意力这一块单独拆开,讲线性注意力、状态空间模型和混合专家(MoE)。

Tatsu 在开场就点明了本讲的方法论:最好的学习方式是亲手做实验,第二好的方式是从别人的实验里学。你没有几千张 GPU 去跑消融实验,但过去六年有几十个开源模型把它们的架构选择写进了技术报告。本讲的做法就是把这些选择摊在一张表上,看什么在收敛、什么在分化、什么根本没人验证过。

按年份整理的 dense 模型架构选择表
本讲的数据来源:一张按年份排列的 dense 模型清单,列出每个模型的词表大小、归一化类型、串行/并行块、位置编码、激活函数、稳定性技巧、MLP 倍率、头数和模型维度。看这张表的正确姿势不是背下来,而是问三个问题:所有模型的共同点是什么?哪些列在分化?我们能从分化里学到什么?
核心结论
  • 架构高度趋同。2023 年之后发布的 dense 模型,绝大多数是「LLaMA-like」:pre-norm + RMSNorm + SwiGLU/GeGLU + RoPE + 无 bias + GQA。真正还在分化的只有三处:位置编码的细节、激活函数选 Swi 还是 Ge、以及 tokenizer 词表大小。
  • 超参数的共识比架构还强。$d_{ff}/d_{model}\approx 4$(用 GLU 时 $8/3$)、$n_{head}\times d_{head}\approx d_{model}$、$d_{model}/n_{layer}\approx 100\text{–}200$。但要注意:第一条有实打实的扫描证据,第二条几乎没有人系统验证过。
  • 近两年的增量几乎全在稳定性上。QK-norm、z-loss、logit soft-capping、非残差 post-norm——这些都是为了让损失曲线不出现尖峰(loss spike),而不是为了降低最终损失。
  • 判断一个技巧值不值得抄,要看它省的是 FLOPs 还是数据搬运。归一化只占 0.17% 的 FLOPs,却占 25.5% 的运行时间。FLOPs 不等于时间,这是贯穿整门课的一条主线。
  • 「大家都这么做」不等于「这是最优的」。T5 用了 $d_{ff}=64\,d_{model}$ 照样训得出来;GPT-3 不用任何门控激活也是当年最强的模型。共识给你一个安全的起点,不是天花板。

1. 起点:原始 Transformer 与你实现的现代变体

1.1 原始 Transformer 的四个选择

2017 年的 Transformer 在四个地方做了具体选择,这四个地方后来全被改掉了:

  • 位置编码:正弦/余弦函数(sinusoidal),加到 token 嵌入上,只加一次。
  • 前馈网络(FFN):$\mathrm{FFN}(x)=\max(0,\,xW_1+b_1)W_2+b_2$,即 ReLU,带偏置。
  • 归一化类型:LayerNorm,带可学习的缩放 $\gamma$ 和平移 $\beta$。
  • 归一化位置:post-norm,即 $x_{l+1}=\mathrm{LN}(x_l+\mathrm{Sublayer}(x_l))$,归一化层在残差流内部。

1.2 你实现的版本,以及四处差异

现代 Transformer 块结构图与差异清单
作业里实现的现代变体。右边的块图关键在于那条从下往上贯通的「Add」链路:归一化被挪到了子层之前(Norm → Attention → Add,Norm → FFN → Add),残差流上不再有任何非线性变换。左边是整体堆叠结构,注意最后一个 Transformer 块之后还有一个 Norm——pre-norm 架构必须补这个 final norm,否则输出到 LM head 的激活尺度是不受控的。
组件原始 Transformer (2017)现代默认 (2023+)主要动机
归一化位置post-norm(残差流内)pre-norm(残差流外)梯度传播、去掉 warmup、支持更大学习率
归一化类型LayerNormRMSNorm更少的数据搬运 → 更快的 wallclock
激活函数ReLUSwiGLU / GeGLU同参数量下稳定的质量增益
位置编码正弦函数(加性、只加一次)RoPE(乘性、每层都加)注意力分数只依赖相对位置、外推性
偏置项线性层、LayerNorm 都有全部去掉内存/数据搬运、优化稳定性
注意力MHAGQA(+ 滑窗/全局交错)推理时 KV cache 的带宽瓶颈

1.3 为什么要读几十个模型的技术报告

光 2024–2025 两年就有 19 个以上的新 dense 模型发布(还不算 MoE),每个都带着一点点架构微调。单看任何一篇技术报告,你只能得到「我们用了 X,效果不错」;把几十篇放在一起看,才能区分出真正的共识(所有人都收敛到同一个值,说明有人踩过坑)和路径依赖(大家都在抄 LLaMA 的配置文件,没人重新验证过)。

2024-2025 年发布的大量模型技术报告封面
光是 2024–2025 年的 dense 模型技术报告就堆成这样:Llama 3、Nemotron-4 340B、Reka、InternLM2、Qwen、Phi-3、Gemma、Falcon 2……每一篇都在架构上做了一点微调。没有人能把这些组合全部亲手实验一遍,所以「读别人的实验」是必需的技能。
直觉 读架构表时最有信息量的不是众数,而是方差。一个超参数如果所有模型都取同一个值(比如 $n_{head}\times d_{head}=d_{model}$),有两种可能:它真的很重要,或者它根本不重要所以没人去动。区分这两者的唯一办法,是去找有没有人真的做过扫描实验——本讲后面会发现,有些「共识」属于后者。

2. 归一化之一:pre-norm、post-norm 与残差流

这是整张架构表上共识度最高的一列。2024 年的模型里,除了一个非常滑稽的例外(OPT-350M,Tatsu 说他也不知道为什么这个模型是 post-norm),几乎全部使用某种形式的「残差流外归一化」。而 BERT 时代的模型(包括 BERT 自己)都是 post-norm。

2.1 两种写法

把一个子层(注意力或 FFN)记作 $F(\cdot)$,两种排布是:

$$ \text{Post-LN:}\quad x_{l+1} = \mathrm{LN}\big(x_l + F(x_l)\big) \qquad\qquad \text{Pre-LN:}\quad x_{l+1} = x_l + F\big(\mathrm{LN}(x_l)\big) $$

差别看起来只是括号位置,但后果完全不同。在 pre-norm 里把整个网络展开:

$$ x_L = x_0 + \sum_{l=0}^{L-1} F_l\big(\mathrm{LN}(x_l)\big) $$

从输入到输出存在一条完全没有被任何非线性/归一化触碰过的恒等路径——这就是所谓的「残差流(residual stream)」。在 post-norm 里,这条路径每经过一层都要被 LayerNorm 处理一次,恒等性被破坏了。

post-LN 与 pre-LN 的块结构图与公式展开
左边两张块图是关键:灰色的粗箭头就是残差流。post-LN(左)的残差流上串着两个 Layer Norm 方块;pre-LN(右)把 Layer Norm 挪到了分支上,主干上只剩加法。右边表格是两种排布的逐步公式;注意 pre-LN 多了一行 Final LayerNorm——因为残差流一路不归一化,输出前必须补一次。

2.2 为什么 post-norm 的梯度会出问题

推导

LayerNorm 对输入 $x\in\R^d$ 的雅可比矩阵是

$$ \frac{\partial\,\mathrm{LN}(x)}{\partial x} = \frac{\diag(\gamma)}{\sigma}\left(I - \frac{1}{d}\mathbf{1}\mathbf{1}^\top - \frac{1}{d}\hat{x}\hat{x}^\top\right), \qquad \hat{x}=\frac{x-\mu}{\sigma} $$

关键因子是 $1/\sigma$:归一化层的梯度增益反比于输入的标准差。在 post-norm 里,反向传播从第 $L$ 层走到第 $1$ 层,要连乘 $L$ 个这样的雅可比。只要各层的 $\sigma$ 不是恰好为 1,误差就以 $L$ 次幂的速度累积——要么爆炸,要么衰减。

Xiong 等人(2020)的分析给出了初始化时的量级:post-LN 中靠近输出层的参数梯度范数是 $O(d\sqrt{\ln d})$,与深度 $L$ 无关;而 pre-LN 是 $O\!\left(d\sqrt{\ln d / L}\right)$,随深度自动衰减。含义是:post-LN 里浅层和深层的梯度尺度差异随深度迅速拉大,一个全局学习率没法同时适配它们。

pre-LN 与 post-LN 的逐层梯度大小,以及梯度范数尖峰对比
左图是初始化时 FFN 第一层权重 $W^1$ 的梯度期望,横轴是层号:pre-LN(蓝)从第 1 层到第 6 层基本是一条水平线(约 0.2),post-LN(橙)从 0.1 一路涨到 1.3——六层就差了一个数量级,几十层会是什么情况可想而知。绿色是 post-LN 经过 warmup 之后的梯度,几乎被压到了 0,这解释了为什么 post-LN 必须靠 warmup 才能活下来。右图是训练全程的梯度全局范数(对数刻度):虚线的 PostNorm 全程都在放炮,实线的 PreNorm 变体安静得多。
pre-norm 与 post-norm 在机器翻译和 BERT 预训练上的对比曲线
实证对比。左:英语-越南语翻译的 dev BLEU,PostNorm+LayerNorm(点线)明显收敛更慢、终点更低,而各种 PreNorm 变体挤成一团。右上:IWSLT 上 post-LN 不做 warmup 直接崩(验证损失停在 4.5 以上),pre-LN 不做 warmup 也没事。右下:BERT 预训练,pre-LN 在同样步数下损失低约 0.05 nat。这两组独立的实验指向同一个结论。

2.3 从「去掉 warmup」到「支持更大的学习率」

pre-norm 最初被提出时,宣传的卖点是可以去掉学习率 warmup——既然梯度尺度在各层之间是一致的,就不需要用一段小学习率的热身期来「等」二阶矩估计稳定下来。

但今天大家用 pre-norm 的理由已经变了:不是为了省掉 warmup(几乎所有大模型仍然做 warmup),而是为了训练稳定性和更大的学习率。在几十上百层、上千亿参数的规模下,post-norm 的梯度尖峰会直接导致 loss spike,甚至发散到 NaN。pre-norm 让你可以把峰值学习率开得更高,从而在同样的 token 预算下走得更远。

2.4 新玩法:double norm 与非残差 post-norm

pre-norm 也不是没有代价。注意到残差流是累加的:$\mathrm{Var}(x_l)\approx \mathrm{Var}(x_0)+\sum_{j<l}\mathrm{Var}(F_j)$,方差随层数近似线性增长。于是第 $l$ 层内部的 $\mathrm{LN}$ 会除以一个 $\sim\sqrt{l}$ 的数,等价于深层子层对最终输出的相对贡献被自动压小了。另一个问题是:pre-norm 无法约束「加进残差流的东西」的量级,某一层如果输出爆了,它会一路污染后面所有层。

于是有了折中方案:既然「把 LayerNorm 放进残差流」是坏事,那就把 post-norm 放在残差流外面:

$$ \text{double norm:}\quad x_{l+1}=x_l+\mathrm{LN}_{\text{post}}\Big(F\big(\mathrm{LN}_{\text{pre}}(x_l)\big)\Big) \qquad \text{非残差 post-norm:}\quad x_{l+1}=x_l+\mathrm{LN}\big(F(x_l)\big) $$

前者(子层前后各一个 norm)被 Grok 和 Gemma 2 采用;后者(只保留子层输出上的 norm)是 OLMo 2 的做法,它们把这叫做「reordered norm」。两者的共同点是:残差流主干上依然只有加法,但每个子层往残差流里注入的量被显式地归一化了,量级可控。

post-norm 与 double norm 的块结构对比
左边是传统 post-norm:Layer Norm 直接压在残差流(灰色主干)上。右边是「双归一化」:子层的输入和输出各套一个 Layer Norm,但两个都在分支上,主干仍然是纯加法。OLMo 2 只保留右边那个位于子层输出上的 norm——既保住了干净的残差流,又限制住了每层往里加的量。
常见误区 「pre-norm 好、post-norm 坏」是过度简化。真正重要的判据是归一化层在不在残差流的主干上。OLMo 2 的非残差 post-norm 在结构图上看着像 post-norm,实际上残差流依然是干净的恒等路径,它享受的是 pre-norm 的梯度性质。当你看到一篇论文说「我们用了 post-norm」时,一定要去看它的公式而不是名字。

3. 归一化之二:LayerNorm vs RMSNorm,以及为什么删掉 bias

3.1 两个公式

LayerNorm 在 $d_{model}$ 这一维上同时归一化均值和方差:

$$ \mathrm{LN}(x)=\frac{x-\E[x]}{\sqrt{\mathrm{Var}[x]+\varepsilon}}\odot\gamma+\beta, \qquad \gamma,\beta\in\R^{d} $$

RMSNorm 去掉了减均值和加偏置这两步,只做尺度归一:

$$ \mathrm{RMSNorm}(x)=\frac{x}{\sqrt{\frac{1}{d}\sum_{i=1}^{d} x_i^2+\varepsilon}}\odot\gamma, \qquad \gamma\in\R^{d} $$

阵营划分很清楚:LayerNorm 阵营是 GPT-1/2/3、OPT、GPT-J、BLOOM;RMSNorm 阵营是 LLaMA 全家、PaLM、Chinchilla、T5,以及基本上所有 2023 年之后的模型。

3.2 「因为它更快」——这个解释站得住吗?

教科书式的回答是:RMSNorm 更快,因为操作更少(不用算均值)、参数更少(没有 $\beta$ 要存)。

Tatsu 在这里停下来追问:这个解释真的说得通吗?做一下量纲分析。对一个 token,一层里的矩阵乘法是 $O(d^2)$ 量级的 FLOPs(比如 $d=4096$ 时,QKVO 四个投影就是 $4\times 2\times 4096^2\approx 1.34\times 10^{8}$ FLOPs),而一次 RMSNorm 大约是 $5d\approx 2\times 10^{4}$ FLOPs。比例是 $1/d$ 量级,也就是万分之几。参数量上,$\beta$ 只有 $d$ 个,相对于每层几千万参数完全可以忽略。

实测数据也印证了这一点:在一个典型 Transformer 里,张量收缩(矩阵乘)占 99.80% 的 FLOPs,统计归一化占 0.17%,逐元素操作占 0.03%。按 FLOPs 算,归一化根本不该被讨论。

3.3 真正的原因:FLOPs 不等于运行时间

但如果你去测时间,画风完全变了:

算子类别% FLOPs% 运行时间
张量收缩(矩阵乘)99.8061.0
统计归一化(LayerNorm、softmax)0.1725.5
逐元素操作(激活、dropout、加法)0.0313.5

占 0.2% FLOPs 的两类操作,吃掉了 39% 的运行时间。原因是算术强度(arithmetic intensity,FLOP/byte):矩阵乘每读一个字节能做上百次浮点运算,而 LayerNorm 每读一个元素只做几次运算,完全被显存带宽卡住。

按算子类别拆分的 FLOPs 与运行时间,以及每个算子的 FLOP/内存比
右边的算子图是关键:多头注意力(MHA)标着 43 GFLOP、FLOP-to-memory 比 153;而 LayerNorm 只有 29 MFLOP,FLOP-to-memory 比 3.5。现代 GPU 的「ridge point」(算力/带宽之比)大约在 100–300 FLOP/byte 之间,所以 153 的 MHA 勉强算 compute-bound,而 3.5 的 LayerNorm 是彻头彻尾的 memory-bound——它慢不是因为算得多,而是因为搬得多。
核心结论 RMSNorm 的收益不在「少算一个均值」,而在少搬一份数据:不需要额外读一遍 $x$ 来求均值,不需要读写 $\beta$,融合 kernel 也更简单。这条逻辑在整门课里会反复出现——凡是看到「省 FLOPs」的说法,先去看它省的是不是 memory-bound 的那部分。

3.4 实证:RMSNorm 不只是更快

Narang 等人(2021)在 223M 参数、11.1T ops 的相同预算下横扫了各种归一化方案:

模型参数量步/秒早期损失最终损失SGLUEXSumWebQWMT EnDe
Vanilla Transformer (LayerNorm)223M3.502.182 ± 0.0051.83871.6617.7823.0226.62
RMSNorm223M3.682.167 ± 0.0081.82175.4517.9424.0727.14
ReZero223M3.512.262 ± 0.0031.93961.6915.6420.9026.37
ReZero + LayerNorm223M3.262.223 ± 0.0061.85870.4217.5823.0226.29
Fixup223M2.952.382 ± 0.0122.06758.5614.4223.0226.31

读法:吞吐 +5%(3.50 → 3.68 步/秒),而且最终损失还低了 0.017 nat,下游任务全面小胜。这不是一个「牺牲质量换速度」的取舍,而是白捡的。相比之下 ReZero、Fixup 这些「去掉归一化」的方案不但更慢,质量也明显更差。

3.5 更进一步:删掉所有 bias

原始 Transformer 的 FFN 是

$$ \mathrm{FFN}(x)=\max(0,\,xW_1+b_1)W_2+b_2 $$

现代实现(未使用门控时)是

$$ \mathrm{FFN}(x)=\sigma(xW_1)W_2 $$

连 RMSNorm 的 $\beta$ 也一起删掉了。理由和 RMSNorm 完全一样:

  • 内存与数据搬运。bias 的参数量占比可以忽略(每层 $d_{ff}+d_{model}$,相对 $3d_{model}d_{ff}$ 是万分之几),但它是一次额外的读取 + 一次额外的逐元素加法,属于最贵的那类操作。
  • 优化稳定性。bias 项是模型里少数「不随输入尺度变化」的自由度,容易在低精度训练中变成 outlier feature 的温床;同时它和 weight decay 的相互作用也很尴尬(大多数实现要把 bias 从 weight decay 里排除,又是一处特判)。
直觉 删掉 bias 之后,模型的每一个线性层都变成了严格的线性映射(无仿射项),整个网络对输入尺度的响应更「齐次」。加上 RMSNorm 也没有平移项,从嵌入到 LM head 的整条路径上几乎不存在常数偏移。经验上这让大规模训练的数值行为可预测得多——这也是为什么它成了默认选项,而不是因为省了那点参数。

3.6 归一化小结

  • 基本所有人都做残差流外的归一化(通常是 pre-norm)。直觉:保住残差连接的好处;观察:梯度传播更均匀、尖峰更少。有些人在残差流外再加一个 post-norm。
  • 大多数人用 RMSNorm。实践中和 LayerNorm 效果相当,但要搬运的参数更少,省的是 wallclock 时间。
  • bias 项被普遍删除,因为它们的「计算/参数」收益比很差。

4. 激活函数:从 ReLU 到 GLU 家族

激活函数是个动物园:ReLU、GeLU、Swish、ELU、SeLU、GLU、GeGLU、ReGLU、SwiGLU、LiGLU……先把它们理清楚,再看谁真的有用。

4.1 三个非门控激活

ReLU——原始 Transformer、T5、Gopher、Chinchilla、OPT:

$$ \mathrm{FF}_{\mathrm{ReLU}}(x)=\max(0,\,xW_1)W_2 $$

GeLU(Gaussian Error Linear Unit)——GPT-1/2/3、GPT-J、GPT-NeoX、BLOOM:

$$ \mathrm{FF}_{\mathrm{GeLU}}(x)=\mathrm{GELU}(xW_1)W_2, \qquad \mathrm{GELU}(x):=x\,\Phi(x) $$

其中 $\Phi$ 是标准正态分布的 CDF。直觉上 GeLU 是 ReLU 的「软化」版本:ReLU 用硬阈值 $\mathbb{1}[x>0]$ 决定是否通过,GeLU 用「$x$ 比一个标准正态随机变量大的概率」来加权,因此在 0 附近光滑可导。实践中常用 tanh 近似:

$$ \mathrm{GELU}(x)\approx 0.5\,x\left(1+\tanh\!\left[\sqrt{2/\pi}\,\big(x+0.044715\,x^3\big)\right]\right) $$

Swish / SiLU:$\mathrm{Swish}_\beta(x)=x\cdot\sigma(\beta x)$,$\beta=1$ 时就是 SiLU。它和 GeLU 形状极其接近,都是「自门控(self-gated)」的:用输入自己的 sigmoid 去缩放自己。

4.2 门控线性单元(GLU)

GLU 改的是 FFN 的第一层。原来是「线性 + 激活」:

$$ \max(0,\,xW_1) $$

现在再加一路纯线性分支,两路逐元素相乘:

$$ \max(0,\,xW_1)\;\longrightarrow\;\max(0,\,xW_1)\otimes(xV) $$

于是得到门控版本(这里是 ReGLU):

$$ \mathrm{FF}_{\mathrm{ReGLU}}(x)=\big(\max(0,\,xW_1)\otimes xV\big)W_2 $$

注意多出了一个参数矩阵 $V$,FFN 从两个矩阵变成三个矩阵。把激活换成 GELU 或 Swish 就得到今天的两大主流:

$$ \mathrm{FFN}_{\mathrm{GeGLU}}(x,W,V,W_2)=\big(\mathrm{GELU}(xW)\otimes xV\big)W_2 $$ $$ \mathrm{FFN}_{\mathrm{SwiGLU}}(x,W,V,W_2)=\big(\mathrm{Swish}_1(xW)\otimes xV\big)W_2 $$
  • GeGLU:T5 v1.1、mT5、LaMDA、Phi-3、Gemma 2/3/4
  • SwiGLU:LLaMA 1/2/3、PaLM、Mistral、OLMo,以及 2023 年后的绝大多数模型

4.3 为什么隐层宽度要乘 2/3

推导

标准 FFN 有两个矩阵:$W_1\in\R^{d_{model}\times d_{ff}}$、$W_2\in\R^{d_{ff}\times d_{model}}$,参数量

$$ P_{\text{标准}} = 2\,d_{model}\,d_{ff} $$

门控版本多了 $V\in\R^{d_{model}\times d_{ff}}$,参数量

$$ P_{\text{GLU}} = 3\,d_{model}\,d_{ff}^{\text{GLU}} $$

要让两者参数量(以及 FLOPs,因为 FFN 的 FLOPs 正比于参数量)相等:

$$ 3\,d_{model}\,d_{ff}^{\text{GLU}} = 2\,d_{model}\,d_{ff} \;\Longrightarrow\; \boxed{\,d_{ff}^{\text{GLU}} = \tfrac{2}{3}\,d_{ff}\,} $$

代入常见的 $d_{ff}=4d_{model}$,得到 $d_{ff}^{\text{GLU}}=\frac{8}{3}d_{model}\approx 2.67\,d_{model}$。

数值检查:LLaMA-7B 的 $d_{model}=4096$。若用标准 FFN,$d_{ff}=16384$,每层 FFN 参数 $2\times4096\times16384=1.342\times10^8$。LLaMA 实际用 SwiGLU 且 $d_{ff}=11008$($=2.6875\,d_{model}$,把 $\frac{8}{3}\times4096\approx10923$ 向上取整到 256 的倍数),每层 FFN 参数 $3\times4096\times11008=1.352\times10^8$——两者相差 0.7%。这就是 $2/3$ 规则在真实模型里的样子。

这个「向上取整到 128 或 256 的倍数」的细节值得单独提一句:OLMo 的做法是把 $\frac{8}{3}d$ 提升到最接近的 128 的倍数。理由纯粹是硬件——GPU 的 tensor core 对矩阵维度是 8/16/128 的倍数最友好,一个 10923 的维度会让 GEMM kernel 掉到慢速路径上。

4.4 门控到底有没有用?

Shazeer(2020)在 T5 的框架下做了受控对比(所有变体参数量对齐):

FFN 变体平均分CoLA (MCC)SST-2 (Acc)
ReLU83.8051.3294.04
GELU83.8653.4894.04
Swish83.6049.7993.69
GLU(sigmoid 门控)84.2049.1694.27
GEGLU84.1253.6593.92
Bilinear(无激活的门控)83.7951.0294.38
SwiGLU84.3651.5993.92
ReGLU84.6756.1694.38
Raffel 等(2019)基线83.2853.8492.68
该基线的标准差0.2351.1110.569

关键在最后一行:平均分的运行间标准差是 0.235,而门控变体比非门控变体高出 0.4–0.9 分,也就是2–4 个标准差。这不是噪声。

Narang 等人对各种激活函数的对照实验表
Narang 等人(2021)在 223M 参数、11.1T ops 的固定预算下独立复现了同一结论。看「Final loss」列:Vanilla(ReLU)1.838,GeLU 1.838,Swish 1.847——非门控之间基本没差别;而 GLU 1.814、ReGLU 1.803、LiGLU 1.798、GeGLU 1.792、SwiGLU 1.789——门控变体整体低了 0.04–0.05 nat。同时「Step/s」列显示速度几乎没变(3.50 vs 3.53–3.59),说明 $2/3$ 的缩放确实把额外的第三个矩阵抵消掉了。注意 ELU(1.932)和 SeLU(1.948)是明显更差的,不是所有激活都能随便换。
注意 Shazeer 在论文结尾留下了一句被反复引用的话,大意是「我们无法解释这些架构为什么有效,只能像对待其他一切一样,把它归功于神明的恩典」。这不是玩笑——GLU 至今没有一个被广泛接受的理论解释。常见的直觉(门控提供了乘性交互 / 让模型能表达二次型 / 相当于一种软性的特征选择)都只是事后叙事。这门课的态度是:证据一致且代价可控,那就用;但别假装你知道为什么。

4.5 激活小结

  • 各模型之间确实存在多种选择(ReLU、GeLU、*GLU)。
  • 门控不是必需的——GPT-3 用纯 GeLU 也是当年最强的模型——但 2023 年之后几乎看不到不用门控的了。
  • 少数离群模型:Nemotron 340B 用的是 squared ReLU($\max(0,x)^2$)。
  • 证据方向一致:Swi/GeGLU 能带来稳定但不大的收益。在同参数量下白拿 0.04 nat,值得。

5. 串行块 vs 并行块

标准 Transformer 块是串行的:先算注意力,再把结果喂给 MLP。

$$ y = x + \mathrm{MLP}\Big(\mathrm{LN}\big(x+\mathrm{Attention}(\mathrm{LN}(x))\big)\Big) $$

并行版本让两个子层同时读取同一个输入:

$$ y = x + \mathrm{MLP}\big(\mathrm{LN}(x)\big) + \mathrm{Attention}\big(\mathrm{LN}(x)\big) $$

好处是纯系统层面的:两个分支共享同一次 LayerNorm(少算一次归一化,而归一化恰好是 memory-bound 的),而且 $W_Q,W_K,W_V$ 和 MLP 的第一个矩阵读的是同一个输入张量,可以融合成一次更大的 GEMM——大矩阵乘的效率高于多个小矩阵乘。在张量并行的场景里还能少一次 all-reduce。

PaLM 论文中关于并行层的描述
PaLM 论文的原文说明:并行形式在大规模下带来约 15% 的训练加速,因为 MLP 和 Attention 的输入矩阵乘可以融合。消融实验显示 8B 规模下有轻微的质量下降,62B 规模下没有下降,于是他们外推认为 540B 规模上是「质量中性」的。GPT-NeoX 也报告了约 15% 的吞吐提升。

采用并行层的模型:GPT-J(首创)、PaLM、GPT-NeoX,近期的 Cohere Command A、Falcon 2 11B、Command R+。但大多数模型今天仍然用串行块——15% 的加速在「质量可能有一点点损失」面前,多数团队选择不冒险。这是一个典型的「有明确收益但没成为共识」的技巧。

6. 位置编码:从正弦到 RoPE

自注意力本身是置换不变(permutation-invariant)的:把输入 token 打乱,输出也只是跟着打乱,模型完全不知道「谁在谁前面」。所以必须显式注入位置信息。这是本讲里分歧最大的一列。

6.1 四类方案

类型形式代表模型
正弦(sinusoidal)$\mathrm{Embed}(x,i)=v_x+\mathrm{PE}_{i}$,$\mathrm{PE}$ 由不同频率的 sin/cos 构成原始 Transformer
绝对(learned absolute)$\mathrm{Embed}(x,i)=v_x+u_i$,$u_i$ 是可学习的位置向量GPT-1/2/3、OPT
相对(relative)在注意力打分里加一项,如 $e_{ij}=\dfrac{x_iW^Q(x_jW^K+a^K_{ij})^\top}{\sqrt{d_z}}$T5、Gopher、Chinchilla
旋转(RoPE)对 Q/K 做与位置有关的旋转GPT-J、PaLM、LLaMA,以及几乎所有 2024+ 模型

正弦编码的具体形式是 $\mathrm{PE}_{i,2k}=\sin(i/10000^{2k/d})$、$\mathrm{PE}_{i,2k+1}=\cos(i/10000^{2k/d})$:用一组几何级数排列的频率,把位置 $i$ 编码成一个「多尺度的时钟读数」。低频维度区分远距离,高频维度区分近距离。

6.2 RoPE 想解决的问题

先把目标写清楚。一个真正的相对位置编码,应该存在函数 $f(x,i)$ 使得

$$ \big\langle f(x,i),\,f(y,j)\big\rangle = g(x,y,\,i-j) $$

也就是说:注意力分数只能依赖相对位置 $i-j$,不能依赖绝对位置。为什么已有方案都不满足?

  • 正弦编码:加性的编码在做内积时会展开出交叉项, $\langle v_x+\mathrm{PE}_i,\;v_y+\mathrm{PE}_j\rangle=\langle v_x,v_y\rangle+\langle \mathrm{PE}_i,v_y\rangle+\langle v_x,\mathrm{PE}_j\rangle+\langle \mathrm{PE}_i,\mathrm{PE}_j\rangle$。 只有最后一项是相对的($\langle \mathrm{PE}_i,\mathrm{PE}_j\rangle$ 确实只依赖 $i-j$),中间两个交叉项混杂了内容和绝对位置。
  • 绝对位置编码:显然不是相对的。
  • 相对位置偏置(T5 那种):确实只依赖 $i-j$,但它是直接往注意力 logits 上加一个标量,不再是一个内积。这在工程上是个真问题——FlashAttention 这类融合 kernel 假定注意力分数来自 $QK^\top$,额外的偏置矩阵要么破坏融合,要么需要专门实现。

6.3 关键洞察:内积对旋转不变

RoPE 的思路极其简洁:

  • 我们希望嵌入对绝对位置不敏感;
  • 而内积对任意旋转不变:$\langle Rq, Rk\rangle=\langle q,k\rangle$,因为 $R^\top R=I$。

所以:把位置编码成一个旋转。把 query 按位置 $m$ 旋转 $m$ 个「刻度」,key 按位置 $n$ 旋转 $n$ 个刻度,则

$$ \langle R_m q,\;R_n k\rangle = q^\top R_m^\top R_n k = q^\top R_{n-m} k $$

(因为旋转矩阵满足 $R_m^\top=R_{-m}$、$R_{-m}R_n=R_{n-m}$。)内积自动只依赖 $n-m$,目标达成。把「we know that」和「of course we know」这两个句子里的同一对词旋转到不同的绝对角度,它们之间的夹角——也就是注意力分数——完全一样。

6.4 选哪个旋转?

$d$ 维空间里的旋转有 $d(d-1)/2$ 个自由度,得挑一个。RoPE 的选择是:把坐标两两配对,每一对在自己的 2 维平面里旋转(动机来自复数——一个 2 维旋转就是乘以 $e^{i\theta}$)。第 $k$ 对的旋转角速度是

$$ \theta_k = 10000^{-2(k-1)/d},\qquad k=1,\dots,d/2 $$

位置 $m$ 处,第 $k$ 对被旋转 $m\theta_k$ 弧度。这和正弦编码用的是同一套几何级数频率:$k$ 小的是高频(转得快,编码近距离),$k$ 大的是低频(转得慢,编码远距离)。

RoPE 的旋转示意图:坐标两两配对,按位置旋转
上方是单个 2 维平面里的操作:取 query/key 的一对坐标 $(x_1,x_2)$,按角度 $m\theta_1$ 旋转得到 $(x'_1,x'_2)$。下方是整个向量的情况:$d/2$ 对坐标各自旋转,每对用自己的角速度 $\theta_k$,乘以自己的位置 $m$(图中 1 到 6)。右侧展示了 Gemma 4 的变体:只旋转前面那些高频维度(partial RoPE),剩下的维度保持不动、纯粹承载语义信息——GPT-NeoX 也有类似的 rotary_pct 参数。

6.5 完整的数学形式

写成矩阵,RoPE 就是在 Q/K 投影之后左乘一个块对角的旋转矩阵:

$$ f_{\{q,k\}}(\boldsymbol{x}_m, m) = \boldsymbol{R}^d_{\Theta,m}\,\boldsymbol{W}_{\{q,k\}}\,\boldsymbol{x}_m $$ $$ \boldsymbol{R}^d_{\Theta,m}= \begin{pmatrix} \cos m\theta_1 & -\sin m\theta_1 & 0 & 0 & \cdots & 0 & 0\\ \sin m\theta_1 & \cos m\theta_1 & 0 & 0 & \cdots & 0 & 0\\ 0 & 0 & \cos m\theta_2 & -\sin m\theta_2 & \cdots & 0 & 0\\ 0 & 0 & \sin m\theta_2 & \cos m\theta_2 & \cdots & 0 & 0\\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots\\ 0 & 0 & 0 & 0 & \cdots & \cos m\theta_{d/2} & -\sin m\theta_{d/2}\\ 0 & 0 & 0 & 0 & \cdots & \sin m\theta_{d/2} & \cos m\theta_{d/2} \end{pmatrix} $$

和正弦编码的两个本质区别:它是乘性的而不是加性的,因此不产生内容 × 位置的交叉项。

直觉 这个矩阵极度稀疏(每行只有 2 个非零元),所以永远不要真的构造它。实际实现只需要两个形状为 $(\text{seq\_len}, d/2)$ 的 cos/sin 表,做几次逐元素乘加即可,开销是 $O(d)$ 而不是 $O(d^2)$。

6.6 最小实现

import torch

def build_rope_cache(seq_len: int, head_dim: int, base: float = 10000.0):
    """返回 cos/sin 表,形状均为 (seq_len, head_dim // 2)。"""
    # theta_k = base^(-2k/d), k = 0, 1, ..., d/2 - 1
    k = torch.arange(0, head_dim, 2).float()            # 0, 2, 4, ...
    inv_freq = base ** (-k / head_dim)                  # (head_dim/2,)
    pos = torch.arange(seq_len).float()                 # (seq_len,)
    angles = torch.outer(pos, inv_freq)                 # (seq_len, head_dim/2)
    return angles.cos(), angles.sin()

def apply_rope(x, cos, sin):
    """x: (batch, n_heads, seq_len, head_dim)。cos/sin: (seq_len, head_dim/2)。"""
    x1 = x[..., 0::2]                                   # 偶数下标:每对的第一个分量
    x2 = x[..., 1::2]                                   # 奇数下标:每对的第二个分量
    cos = cos[None, None, :, :]                         # 广播到 batch / head 维
    sin = sin[None, None, :, :]
    # 逐对做 2D 旋转: [x1, x2] -> [x1 cos - x2 sin,  x1 sin + x2 cos]
    o1 = x1 * cos - x2 * sin
    o2 = x1 * sin + x2 * cos
    return torch.stack((o1, o2), dim=-1).flatten(-2)    # 交错还原成 head_dim

# --- 验证相对性:内积只依赖 (m - n) ---
cos, sin = build_rope_cache(seq_len=64, head_dim=8)
q = torch.randn(1, 1, 64, 8)
k = torch.randn(1, 1, 64, 8)
qr, kr = apply_rope(q, cos, sin), apply_rope(k, cos, sin)
# 把同一对向量放在位置 (3, 7) 和位置 (20, 24),相对距离都是 4
a = (qr[0, 0, 3] * kr[0, 0, 7]).sum()
b = (qr[0, 0, 20] * kr[0, 0, 24]).sum()
print(a.item(), b.item())   # 一般来说这两个数并不相等 —— 见下方说明

最后那个「验证」故意留了一个坑:上面的写法里 q[3] 和 q[20] 是不同的随机向量,所以内积当然不同。要真正验证相对性,必须把同一个内容向量放到不同位置上:

qv = torch.randn(8)     # 同一个 query 内容
kv = torch.randn(8)     # 同一个 key   内容

def rope_one(v, pos, cos, sin):
    v1, v2 = v[0::2], v[1::2]
    c, s = cos[pos], sin[pos]
    return torch.stack((v1 * c - v2 * s, v1 * s + v2 * c), dim=-1).flatten()

for (m, n) in [(3, 7), (20, 24), (50, 54)]:
    print(m, n, torch.dot(rope_one(qv, m, cos, sin), rope_one(kv, n, cos, sin)).item())
# 三行输出完全相同 —— 因为 n - m 都等于 4

在真实模型里,RoPE 是这样插进注意力的:

query_states = self.q_proj(hidden_states)
key_states   = self.k_proj(hidden_states)
value_states = self.v_proj(hidden_states)

query_states = query_states.view(bsz, q_len, self.num_heads,          self.head_dim).transpose(1, 2)
key_states   = key_states.view(  bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)
value_states = value_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)

cos, sin = self.rotary_emb(value_states, position_ids)               # 取 RoPE 的 cos/sin
query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)

# ... 下面和普通的多头自注意力完全一样 ...
注意 三个容易踩的实现细节:
  • 只旋转 Q 和 K,不旋转 V。位置信息的作用是调制「谁该看谁」,value 携带的是内容,不需要旋转。
  • 每一层的每一次注意力都要重新施加,而不是像正弦编码那样在嵌入层加一次。原因很直接:相对性来自 $R_m^\top R_n$ 这个内积恒等式,只有在做内积的那一刻两侧都带着旋转才成立。经过一层 FFN 之后,「旋转」这个结构早就被非线性打散了。
  • 配对约定有两种。上面的代码用的是原论文的「相邻交错」($(x_1,x_2),(x_3,x_4),\dots$),而 HuggingFace 的 rotate_half 用的是「前后对半」($(x_1,x_{d/2+1}),\dots$)。两者只差一个固定的坐标置换,数学上等价,但权重不能互换——转换模型格式时这是经典的踩坑点。

6.7 RoPE 的旋钮:base 与外推

公式里的 10000 叫做 RoPE base(常记作 $\theta$ 或 rope_theta),它决定了最低频维度的「周期」有多长。默认的 10000 对应约几千 token 的有效分辨率;要支持长上下文,主流做法是把 base 调大——OLMo 3 用 $5\times10^5$,不少长上下文模型用 $10^6$——让低频维度转得更慢,从而在更长的距离上仍能区分位置。更系统的方案(YaRN、NTK-aware 插值等)会对不同频段做不同的缩放,OLMo 3 就只在全注意力层上做 YaRN 缩放。

7. 超参数:那些惊人的共识

这一节回答几个在入门课上没人告诉你的问题:前馈层该比隐藏层大多少?要多少个头,头维度必须能整除 $d_{model}$ 吗?词表该多大?模型该做深还是做宽?

7.1 共识一:前馈维度是模型维度的 4 倍

这条几乎是普适的。原始 Transformer 是 $512\to2048$,GPT-3 是 $12288\to49152$,中间六年、上千倍的规模跨度,倍率没变过。

例外一:GLU 变体。前面推过,门控会把倍率乘 $2/3$,得到 $d_{ff}=\frac{8}{3}d_{model}\approx2.67$。实际模型的取值:

模型$d_{ff}/d_{model}$说明
PaLM4用了 SwiGLU 但没缩,等于加宽了 FFN
Mistral 7B3.5略宽于 8/3
LLaMA-2 70B3.5略宽于 8/3
LLaMA 70B2.68标准 8/3
Qwen 14B2.67标准 8/3
DeepSeek 67B2.68标准 8/3
Yi 34B2.85略宽
T5 v1.12.5略窄

整体落在 2.5–4 之间,PaLM、LLaMA-2、Mistral 稍微偏大一点。

例外二:T5。大多数 LM 的超参数都保守得乏味,T5 是个大胆的异类——11B 版本用了

$$ d_{ff}=65536,\qquad d_{model}=1024\quad\Longrightarrow\quad \frac{d_{ff}}{d_{model}}=64 $$

64 倍!而且它训得出来、效果也不错。近期的其他例外:Gemma 2(8×)、SmolLM / Gemma 3 / Gemma 4(在 GLU 下取 4×,相当于比 8/3 宽 50%)。

Kaplan 等人扫描前馈倍率与损失增幅的曲线
为什么大家都落在这个区间?Kaplan 等人(2020)在 50M 参数模型上扫了 $d_{ff}/d_{model}$:横轴对数刻度,纵轴是相对最优点的损失增幅。1 到 10 之间是一个很平的盆地,损失增幅不到 1–2%;到 20 以上开始明显变差(约 5%),到 50 已经是 8%。换句话说,4 和 2.67 都稳稳落在盆地里,这个超参数不敏感——但也不是随便取,T5 的 64 倍确实在盆地之外。

那 T5 的 64 倍到底是不是好选择?有个很有说服力的间接证据:T5 的后续改进版 T5 v1.1 把倍率改回了标准的 2.5(配 GeGLU)。作者自己回退了这个选择,说明 64 倍多半是次优的。

核心结论 $d_{ff}=4d_{model}$(GLU 时 $\frac{8}{3}d_{model}$)几乎适用于所有现代 LLM,而且有扫描证据支持。但 T5 证明了即使是 64 倍这样激进的选择也能工作——这条规则不是刻在石头上的,只是一个方差很小的安全区。

7.2 共识二:头数 × 头维度 = 模型维度

多头注意力之所以「不比单头更贵」,是因为标准做法把 $d_{model}$ 切成 $h$ 份:算出 $XQ\in\R^{n\times d}$ 之后 reshape 成 $\R^{n\times h\times d/h}$,再转置成 $\R^{h\times n\times d/h}$,头维度就变成了一个 batch 维。所有矩阵尺寸和单头时完全一样。

但这不是必须的——完全可以让 $d_{head}>d_{model}/h$,代价是 QKV 投影矩阵变成非方阵,参数量和 FLOPs 都上升。实际情况:

模型头数 $h$头维度 $d_{head}$模型维度 $d_{model}$比值
GPT-396128122881
T5128128102416
T5 v1.1646440961
LaMDA12812881922
PaLM48256184321.48†
LLaMA-26412881921
Qwen 3.5 (27B)2425651201.2

† 注意 PaLM 这一行的方向是反的:$48\times256=12288$,比 $d_{model}=18432$ 小,比值 1.48 指的是 $d_{model}/(h\cdot d_{head})$。其余各行的比值都是 $(h\cdot d_{head})/d_{model}$。

绝大多数模型的比值就是 1,例外集中在 Google 的几个模型(T5 的 16 倍尤其夸张:$128\times128=16384$ 对 $d_{model}=1024$)。

常见误区 不要把「所有人都这么做」当成「有人验证过这么做最好」。Tatsu 在总结里明确说:$n_{head}\times d_{head}=d_{model}$ 是标准做法,但几乎没有系统的验证。它更像是从原始 Transformer 一路继承下来的实现便利(reshape 就能实现多头,不用改任何矩阵形状)。相比之下 $d_{ff}$ 的 4 倍规则至少有 Kaplan 的扫描图。共识的强度和证据的强度是两回事。

7.3 共识三:aspect ratio(深度 vs 宽度)

给定参数预算,是做深还是做宽?用 $d_{model}/n_{layer}$ 这个「长宽比」来衡量:

模型$d_{model}/n_{layer}$
BLOOM205
T5 v1.1171
PaLM (540B)156
GPT-3 / OPT / Mistral / Qwen / OLMo 3128
LLaMA / LLaMA-2102
Gemma 387
Gemma 461
T5 (11B)33

「甜区」大致在 100–200。同样,也有例外——T5 11B 的 33(很深很窄)和 Gemma 4 的 61。

Kaplan 与 Tay 关于 aspect ratio 的实验曲线
左图(Kaplan 等,2020):横轴是 aspect ratio(对数刻度),三条线分别是 50M / 274M / 1.5B 参数模型。中间标注写着「a wide range of architectures achieve similar performance」——从大约 10 到 300 都是一片平地,只有两端才明显变差。也就是说这个超参数比 FFN 倍率还不敏感。右图(Tay 等,2021)在固定 FLOPs 下分别扫深度(DM)和层数(NL),同样显示很宽的可行区间。

既然性能对它不敏感,那什么在决定实际取值?系统因素。

Tay 等人关于深度不可并行的论述与流水线并行示意图
Tay 等人明确指出了做深的天花板:深度无法跨设备并行——每一层都必须等前一层算完;而宽度可以轻松地切到成百上千个设备上(张量并行)。下方的流水线示意图说明了代价:把 4 层放在 4 张 GPU 上,任何时刻都有 GPU 在空转(pipeline bubble),而且推理延迟随层数线性增长。所以「做深」在大规模训练和低延迟推理上都要付出实打实的代价。
直觉 把这三条规则合起来看,一个「标准」模型的所有维度其实只由 $d_{model}$ 一个数决定:$d_{ff}\approx\frac{8}{3}d_{model}$、$n_{layer}\approx d_{model}/128$、$h\approx d_{model}/128$(取 $d_{head}=128$)。所以当有人说「我们训了个 7B」,你基本能反推出它的形状:$d_{model}=4096$、32 层、32 头、$d_{ff}\approx11008$——这正是 LLaMA-7B。架构搜索的自由度,实际上远比想象中小。

7.4 词表大小

词表大小明确地分成两个阵营:

单语模型:30–50k多语言 / 生产系统:100–260k
原始 Transformer37,000mT5250,000
GPT40,257PaLM256,000
GPT-2 / GPT-350,257GPT-4100,276
T5 / T5 v1.132,128Gemma 4262,144
LLaMA32,000DeepSeek100,000
Qwen 15B152,064
Yi64,000

逻辑很清楚:单语(尤其是英文)不需要很大的词表,BPE 在 32k 上就能把英文压得相当好;而要覆盖上百种语言、代码、数学符号,词表不够大就会导致低资源语言被切成一堆字节,同样一句话消耗数倍的 token(既费钱又费上下文窗口)。

代价是:嵌入矩阵和输出层各是 $V\times d_{model}$。以 $d_{model}=4096$ 为例,$V=32000$ 时是 1.3 亿参数(× 2 层如果不共享权重),$V=262144$ 时是 10.7 亿——对一个 7B 模型来说这是巨大的一块,而且输出 softmax 的 logits 张量也从 $B\times T\times 32000$ 涨到 $B\times T\times 262144$,是训练时最占显存的中间张量之一。

8. 正则化:大模型预训练还需要 dropout 和 weight decay 吗?

8.1 理论上不需要

反对在预训练阶段加正则化的论据非常合理:

  • 数据远多于参数。训练语料是万亿级 token,模型是十亿到千亿参数。经典的过拟合场景(参数 >> 数据)根本不成立。
  • 只过一遍数据。SGD 在语料上通常只走一个 epoch(甚至不到),模型压根没有机会去「记住」某个样本。

8.2 实践中大家在做什么

模型Dropout*Weight decay
原始 Transformer0.10
GPT-20.10.1
T50.10
GPT-30.10.1
T5 v1.100
PaLM0(可变)
OPT0.10.1
LLaMA00.1
Qwen 14B0.10.1

* 大多数论文根本不讨论 dropout。在开源模型上,「没提」基本等于「没用」;闭源模型就不好说了。

规律:老模型普遍在预训练时用 dropout;新模型(Qwen 是个例外)只保留 weight decay。这很好理解——dropout 拖慢训练(相当于每步只更新一部分网络),在「一遍过数据」的设定下收益几乎为零。

8.3 那为什么还留着 weight decay?

这是本节最有意思的地方。既然不是为了防过拟合,weight decay 为什么还在?

weight decay 对训练/验证损失以及学习率调度的影响
左图直接否定了「防过拟合」这个解释:把不同 $\lambda_{WD}$(0.0 / 0.1 / 0.3)跑出来的点画在「训练损失 vs 验证损失」平面上,所有点落在同一条直线上——weight decay 完全没有改变泛化间隙,它只是改变了模型停在这条线的哪个位置。中图和右图才是真正的机制:在 10× 余弦学习率衰减下,$\lambda_{WD}=0$ 早期领先,但衰减到后期被 $\lambda_{WD}=0.1$ 反超,终点更低;在恒定学习率下(右图)这个反超不出现,而把学习率手动降到极小(虚线)立刻带来阶梯式下降。weight decay 是通过和学习率调度相互作用来起效的,属于优化动力学,不是统计正则化。
核心结论 现代 LM 预训练依然「正则化」,但它的作用主要在优化动力学上,而不是控制过拟合。直觉性的解释是:weight decay 持续把权重往原点拉,等价于在训练后期维持一个更大的「有效学习率」($\eta/\|w\|$),从而让模型在余弦衰减的尾段仍能有效移动。所以调 weight decay 的时候,应该把它和学习率调度一起调,而不是当成一个独立的「防过拟合强度」旋钮。

一个相关的实践细节:嵌入层通常不加 weight decay(OLMo 3 的配置表里明确写着 “Weight decay on embeddings: No”),bias 和 norm 的 $\gamma$ 也一般被排除在外。

8.4 超参数小结

  • 前馈层:4 倍规则(GLU 时 8/3)是标准,且有扫描证据。
  • 头维度:$d_{head}\times n_{head}=d_{model}$ 是标准,但几乎没有验证。
  • Aspect ratio:「好」的取值范围很宽(100–200),最终由系统因素(并行度、延迟)决定。
  • 正则化:你仍然会「正则化」LM,但它的作用主要体现在优化动力学上。

9. 训练稳定性:不要训出那条蓝色曲线

近两年架构上的新增量,几乎全部集中在稳定性上。原因很实际:一次 7B 模型的预训练要烧掉几十万美元的算力,中途炸了要回滚到几千步之前的 checkpoint,损失是真金白银。

OLMo 与 OLMo 2 的训练损失与梯度范数对比
上图是训练损失,下图是梯度的 L2 范数,横轴是训练步数(到 60 万步)。蓝色是 OLMo 0424 7B,橙色是 OLMo 2 1124 7B。看下面那张图:蓝色曲线的梯度范数从 5 万步开始就不断向上放炮,一路持续到训练结束;橙色则始终平贴在 0.2 以下。上图对应的损失曲线上,蓝色也满是尖峰(每一根竖线就是一次 loss spike)。不要训出蓝色那种曲线——它不只是难看,每次尖峰都意味着一次可能的回滚。

9.1 问题的源头:softmax

不稳定的根源几乎总是 softmax,因为它含有指数和除法:

$$ \softmax(u)_r=\frac{e^{u_r}}{Z},\qquad Z=\sum_{r'=1}^{|V|}e^{u_{r'}} $$

两类故障模式:

  • 指数溢出。虽然标准实现都会减去最大值($e^{u_r-\max u}$)来避免上溢,但在 bf16 下,logits 的绝对量级如果一路漂移到几百,中间量的精度会严重退化,梯度也随之失真。
  • 除零 / 饱和。如果 $Z$ 极小(所有 logits 都是 $-\infty$,比如滑窗注意力里某行被完全 mask 掉),就会出现 0/0;如果某个 logit 远大于其他,softmax 饱和成 one-hot,梯度直接变成 0,注意力头就此「死掉」(attention entropy collapse)。

9.2 输出 softmax:z-loss

把交叉熵展开写:

$$ \log P(x) = \log\frac{e^{U_r(x)}}{Z(x)} = U_r(x)-\log Z(x), \qquad Z(x)=\sum_{r'=1}^{|V|}e^{U_{r'}(x)} $$

注意 $\log Z$ 是可以自由漂移的:把所有 logits 同时加一个常数 $c$,概率分布完全不变,但 $\log Z$ 变成 $\log Z+c$。也就是说模型的损失函数对 logits 的整体平移是不敏感的,于是它会毫无约束地漂。z-loss 就是给这个自由度加一个软约束:

$$ L = \sum_i\Big[\log P(x_i)-\alpha\big(\log Z(x_i)-0\big)^2\Big] = \sum_i\Big[\log P(x_i)-\alpha\log^2 Z(x_i)\Big] $$

即在负对数似然之外,额外加一项 $\alpha\log^2 Z$,鼓励 $\log Z\to 0$(也就是 $Z\to1$)。PaLM 用 $\alpha=10^{-4}$,OLMo 3 用 $10^{-5}$。

推导

z-loss 对第 $r$ 个 logit 的梯度是

$$ \frac{\partial}{\partial U_r}\Big(\alpha\log^2 Z\Big) = 2\alpha\log Z\cdot\frac{1}{Z}\cdot e^{U_r} = 2\alpha\log Z\cdot\softmax(U)_r $$

解读:当 $\log Z>0$(logits 整体偏大)时,梯度按 softmax 概率的比例把所有 logits 往下压;$\log Z<0$ 时反向。因为压的比例正好是 softmax 概率,所以它几乎不改变分布的形状,只改变整体尺度——这正是我们想要的:约束数值范围,不干扰建模。

使用 z-loss 的模型:PaLM(首创于机器翻译时代的 Devlin 2014)、Baichuan 2、DCLM、OLMo 2、OLMo 3。

9.3 注意力 softmax:QK-norm

注意力 logits 是 $q^\top k/\sqrt{d_{head}}$,其量级由 $\|q\|\cdot\|k\|$ 决定,而这两个范数在训练中是可以无限增长的。QK-norm 的做法极其简单:在做点积之前,对 query 和 key 各做一次 (RMS)LayerNorm。

$$ \text{score}_{ij}=\frac{\mathrm{Norm}(q_i)^\top\,\mathrm{Norm}(k_j)}{\sqrt{d_{head}}} $$

归一化之后 $\|q\|,\|k\|$ 被固定在 $\sqrt{d_{head}}$(乘上可学习的 $\gamma$)附近,于是 logits 的绝对上界被 Cauchy–Schwarz 直接卡住:$|q^\top k|\le\|q\|\|k\|$。attention logits 再也炸不了。

QK-norm 在多头注意力中的位置示意
结构上的位置:在 QKV 投影之后、softmax 之前,给 Q 和 K 各插一个 Norm。这个技巧最早来自视觉和多模态模型(ViT-22B、Idefics、Chameleon),因为它们更早遇到了大规模训练的发散问题。现在用它的语言模型包括 DCLM、OLMo 2、Gemma 2、Qwen 3、OLMo 3、Gemma 4——这是过去两年扩散得最快的一个技巧。

9.4 Logit soft-capping

另一条路线:用 tanh 把 logits 硬性压在一个区间里。

$$ \text{logits}\leftarrow c\cdot\tanh\!\left(\frac{\text{logits}}{c}\right) $$

当 $|\text{logits}|\ll c$ 时这近似是恒等映射($\tanh x\approx x$),超过 $c$ 之后平滑饱和到 $\pm c$。Gemma 2 在自注意力层用 $c=50$,在最终输出层用 $c=30$。

Gemma 2 的 logit soft-capping 描述与困惑度对比表
下方那张小表是关键的对照实验(困惑度,95% 置信区间 ±0.1):bf16 基线 11.19,只加 soft-cap 反而变差到 11.24;而 QKV_norm 10.85、QK_norm 10.84、QK_FC_norm 10.87 都明显更好;QK_norm + soft-cap 的组合是 11.00,比单用 QK_norm 差。结论很直白:soft-capping 能防炸,但会伤性能;QK-norm 既能防炸又能提升质量。这也解释了为什么 QK-norm 成了主流而 soft-capping 没有——另外 soft-capping 还和 FlashAttention 的融合 kernel 不兼容,Gemma 2 之后 Google 自己也在弱化它。

9.5 其他稳定性手段

  • 梯度裁剪(gradient clipping)。按全局范数裁剪:若 $\|g\|_2>c$,则 $g\leftarrow g\cdot c/\|g\|_2$。$c=1.0$ 是压倒性的默认值(OLMo 3 的配置表里就是 1.0)。它不能防止不稳定的原因,但能防止单个坏 batch 把权重一步打飞——相当于给训练买了个保险。
  • Warmup。在前 $T_w$ 步把学习率从 0(或极小值)线性升到峰值。理由是 Adam 的二阶矩 $v_t$ 在最初几十步内是极不可靠的估计,此时 $m/\sqrt{v}$ 的方差巨大,用峰值学习率会直接把模型打乱。典型取值是总步数的 0.1%–1%,或者干脆固定几千步。pre-norm 大幅降低了对 warmup 的依赖,但没人真的把它去掉。
  • 学习率调度。主流是 warmup + 余弦衰减到峰值的 10%。近两年 WSD(warmup-stable-decay:先恒定、最后再快速衰减)越来越流行,因为它允许你在任意时刻「切一刀」得到一个可用模型,不必事先确定总步数。
  • 关于精度。今天几乎所有预训练都用 bf16(而不是 fp16):bf16 的指数位和 fp32 一样宽(8 位),代价是尾数只有 7 位。这意味着不会溢出,但精度低,所以上面这些「约束数值范围」的技巧才格外重要——它们的作用与其说是防溢出,不如说是防止有效位数被浪费在一个巨大的偏移量上。
直觉 把这些稳定性技巧串起来看,它们做的是同一件事:掐掉网络里所有「可以无限增长且不影响损失」的自由度。logits 的整体平移(z-loss 管)、Q/K 的范数(QK-norm 管)、残差流注入量的尺度(非残差 post-norm 管)、单步更新的幅度(梯度裁剪管)。每一处都是一个「模型不在乎,但数值格式很在乎」的方向。

10. 优化器:AdamW 和它的挑战者

架构定下来之后,第二个几乎所有人都抄同一份配置的地方就是优化器。

10.1 AdamW 的完整更新式

记第 $t$ 步的梯度为 $g_t=\nabla_\theta L(\theta_{t-1})$:

$$ \begin{aligned} m_t &= \beta_1 m_{t-1} + (1-\beta_1)\,g_t &&\text{一阶矩(动量)}\\[2pt] v_t &= \beta_2 v_{t-1} + (1-\beta_2)\,g_t^{\odot 2} &&\text{二阶矩(逐坐标梯度平方)}\\[2pt] \hat m_t &= \frac{m_t}{1-\beta_1^{\,t}},\qquad \hat v_t=\frac{v_t}{1-\beta_2^{\,t}} &&\text{偏差修正}\\[2pt] \theta_t &= \theta_{t-1} - \eta_t\left(\frac{\hat m_t}{\sqrt{\hat v_t}+\varepsilon} + \lambda\,\theta_{t-1}\right) &&\text{更新(解耦的 weight decay)} \end{aligned} $$

AdamW 相对于 Adam 的唯一改动在最后一行:weight decay 项 $\lambda\theta$ 是直接加在更新上的,而不是先加进梯度 $g_t$ 再过一遍 $1/\sqrt{\hat v}$ 的缩放。这个区别很重要——在原始 Adam 里,L2 正则项会被二阶矩自适应地「除掉」,导致梯度大的参数实际受到的衰减反而更小,与 weight decay 的本意相反。

超参数典型取值说明
$\beta_1$0.9动量,几乎没人动过
$\beta_2$0.95(LLM)/ 0.999(原始 Adam)LLM 普遍调低,让二阶矩「忘得更快」,对梯度尺度突变更敏感、更稳
$\varepsilon$$10^{-8}$防除零;有些实现用 $10^{-5}$ 以增加稳定性
$\lambda$(weight decay)0.1嵌入层、bias、norm 的 $\gamma$ 通常排除在外
梯度裁剪全局范数 1.0见 §9.5
峰值学习率$10^{-3}\sim 10^{-4}$随模型规模下降:小模型可以 $3\times10^{-4}$ 以上,百亿以上通常到 $1.5\times10^{-4}$ 甚至更低

内存代价:每个参数要额外存 $m$ 和 $v$ 两份状态。混合精度训练下的典型账本是:fp32 主权重 4 字节 + 梯度 4 字节 + $m$ 4 字节 + $v$ 4 字节 = 每参数 16 字节。一个 7B 模型光优化器状态就要 56 GB,这正是为什么分布式训练必须做优化器状态分片(ZeRO / FSDP)。

直觉 为什么不用 SGD?因为 Transformer 里不同参数的梯度尺度差异巨大——嵌入层(稀疏、每个 token 只更新几行)、注意力投影、LayerNorm 的 $\gamma$、LM head,它们的梯度范数能差好几个数量级。Adam 做的是逐坐标的尺度归一化:$\hat m/\sqrt{\hat v}$ 的量级永远在 1 附近,与梯度的绝对大小无关。这让你可以用一个全局学习率同时管住所有参数——这也是所有「新优化器」都在试图做得更好的那件事。

10.2 学习率与 batch size 怎么选

  • batch size 有一个「临界值」。McCandlish 等人(2018)提出的 critical batch size 概念是说:batch 小的时候,加倍 batch 能几乎减半所需步数(完美的数据并行);超过某个临界值之后,梯度噪声已经被平均掉了,再加大 batch 只是浪费算力。这个临界值随训练进程增大(loss 越低、梯度信噪比越差),所以 GPT-3 这类模型会做 batch ramp——从 3.2 万 token 逐步升到 320 万 token。
  • 学习率和 batch size 要一起调。经典的经验法则是 batch 增大 $k$ 倍时学习率乘 $\sqrt{k}$(对自适应优化器)或 $k$ 倍(对 SGD)。实践中更常见的做法是固定 batch 之后直接扫学习率——Chinchilla 的一个关键改进就是为每个算力预算单独调学习率,而 Kaplan 的原始 scaling law 没有这么做,这是两者结论差异的重要来源。
  • 规模越大,学习率越小。这是经验规律,也可以用 μP(maximal update parametrization)之类的理论来「迁移」:先在小模型上把超参数调好,再按理论公式外推到大模型,从而省下在大模型上调参的天价成本。

10.3 挑战者:Muon 与 SOAP

Adam 逐坐标看待参数,但神经网络的权重是矩阵——坐标之间不是独立的。近两年最受关注的两个新优化器都是从这个观察出发的:

  • Muon(MomentUm Orthogonalized by Newton-schulz)。核心想法:把动量缓冲区 $M$(一个矩阵)用 Newton–Schulz 迭代近似正交化,再拿正交化后的矩阵去更新权重: $$ \theta \leftarrow \theta - \eta \cdot \mathrm{NewtonSchulz}(M),\qquad \mathrm{NewtonSchulz}(M)\approx UV^\top \ \text{(其中 } M=U\Sigma V^\top\text{)} $$ 直觉:原始的动量矩阵往往被少数几个奇异方向主导(谱高度集中),意味着更新几乎全部落在一个低维子空间里。正交化把所有奇异值拉平成 1,让更新在各个方向上均衡地推进。实践中 Muon 只用在隐藏层的 2D 权重上,嵌入层和输出头仍然交给 AdamW。
  • SOAP(Shampoo with Adam in the Preconditioner's eigenbasis)。Shampoo 这类二阶方法维护左右两个预条件矩阵,效果好但超参数敏感、实现复杂。SOAP 的洞察是:Shampoo 大致等价于在预条件子的特征基里跑 Adafactor;那不如干脆在这个旋转后的基里跑标准 Adam,并且让特征基低频更新(每几十步才做一次特征分解)。这样既拿到二阶方法的收益,又把超参数减少回 Adam 的水平。
注意 这些新优化器在中小规模上有很有说服力的加速结果(相同损失下减少 30%–50% 的步数是常见的报告值),但它们和 AdamW 的差距是否能在真正的大规模、长训练上保持,目前仍在验证中。如果你只训一个模型,AdamW + 余弦调度 + 梯度裁剪 1.0 仍然是最安全的选择;新优化器额外的每步开销(Newton–Schulz 迭代、特征分解)也需要计入总账。

11. 注意力头的变体:MQA、GQA 与稀疏注意力

大部分模型对注意力头本身几乎不做改动,例外集中在两类:

  • GQA / MQA:通过减少 key/value 头的数量来省推理成本;
  • 稀疏 / 滑动窗口注意力:限制注意力模式来省计算成本。

(更奇特的东西——Jamba、Falcon 3、Qwen 3.5 里的状态空间模型——留到下一讲。)

11.1 先算清楚:训练时的注意力是 compute-bound 的

符号:$d$ = 隐藏维度,$b$ = batch,$n$ = 序列长度(假设 $n<d$),$h$ = 头数,$k=d/h$ = 头维度。

注意力的计算与内存访问分解示意图
训练(或推理的 prefill 阶段)时注意力的数据流:$XQ$ 与 $K^\top X^\top$ 相乘得到 $h$ 套 $n\times n$ 的注意力分数(图中标注「3 sets of all pairs of attention scores!」),过 softmax 后与 $XV$ 混合,最后投影回 $\R^{n\times d}$。

总算术操作 $\Theta(bn^2d)$,总内存访问 $\Theta(bnd+bhn^2+d^2)$(分别是激活、注意力矩阵、权重)。于是算术强度

$$ \text{intensity}=O\!\left(\left(\frac{1}{k}+\frac{1}{bn}\right)^{-1}\right)\approx \min(k,\;bn) $$

取 $k=128$、$b\cdot n$ 上万,强度就在 100 以上——足够让 GPU 跑满。所以训练时的注意力不是带宽瓶颈。

11.2 增量解码:一切都变了

生成文本时的关键差异是:无法并行,必须一个 token 一个 token 地来。为了不重算历史,我们用 KV cache 把之前所有 token 的 $K,V$ 存下来,每步只算新 token 的 query 并与整个 cache 做注意力。

重新算账:生成 $n$ 个 token 的总操作是 $\Theta(bnd^2)$(每步 $b\cdot d^2$ 的投影矩阵乘),总内存访问是 $\Theta(bn^2d + nd^2)$(第一项是每步都要重读一遍越来越长的 KV cache,第二项是每步都要重读一遍全部权重)。于是

$$ \text{intensity}=O\!\left(\left(\frac{n}{d}+\frac{1}{b}\right)^{-1}\right) $$

这个数非常糟糕。两项的含义:

  • $1/b$:权重重读的成本,靠大 batch 摊薄;
  • $n/d$:KV cache 重读的成本,序列越长越致命。$n=4096$、$d=4096$ 时这一项就等于 1,强度直接掉到 1 以下——完全 memory-bound,GPU 的算力有 99% 在空转。

大 batch 和短序列能救 $1/b$,但 $n/d$ 这一项很难降——这就是 MQA/GQA 要解决的问题。

推导

把 KV cache 的大小算出来会更直观。每个 token 每层需要存 $K$ 和 $V$ 各一份:

$$ \text{KV bytes/token} = 2\;(\text{K,V})\times n_{layer}\times n_{kv}\times d_{head}\times \text{bytes} $$

以 LLaMA-2 70B 为例($n_{layer}=80$,64 个 query 头,$d_{head}=128$,bf16 2 字节):

  • 如果是标准 MHA($n_{kv}=64$):$2\times80\times64\times128\times2=2.62$ MB / token。4096 上下文 = 10.7 GB,batch 32 就是 344 GB——一张 H100 只有 80 GB。
  • 实际用 GQA($n_{kv}=8$):$0.33$ MB / token,4096 上下文 = 1.34 GB,batch 32 = 43 GB。整整省了 8 倍,而且省的是每一步都要重读的那份数据。

11.3 MQA:只留一份 K 和 V

Multi-Query Attention 的想法直白到有点粗暴:保留 $h$ 个 query 头,但 key 和 value 只有一份,被所有头共享。

MQA 的数据流:共享的 K/V 与每头独立的 Q
QKV 投影之后,K 和 V 各只产生一份(图中 “K (shared)” / “V (shared)”),广播给每一个头去做点积注意力;Q 仍然是每头一份。KV cache 因此缩小了 $h$ 倍。新的算术强度是 $O\big((\frac{1}{d}+\frac{n}{dh}+\frac{1}{b})^{-1}\big)$——注意关键的 $n/d$ 项被除以了 $h$,变成 $n/(dh)$。$h=64$ 时,原本压垮一切的那一项一下小了 64 倍。

11.4 GQA:不要走极端

Grouped-Query Attention 是自然的折中:不要一路砍到 1 份 KV,而是砍到 $g$ 组($1<g<h$),每组内的 query 头共享一份 K/V。$g=1$ 退化成 MQA,$g=h$ 就是 MHA。这给了你一个连续的旋钮,在表达力和推理效率之间调节。

更近期的方案是 DeepSeek-V2 提出的 MLA(Multi-head Latent Attention):不是共享 KV 头,而是把 K/V 压缩成一个低秩的潜在向量,只缓存这个潜在向量,用的时候再解压回各个头。这在同样的 cache 预算下保留了更多表达力。

MQA 与 GQA 的质量-速度对比
左表(Shazeer 2019):Billion-Word 基准上,multi-head(8 头、$d_k=d_v=128$)dev-PPL 是 29.9,multi-query 是 30.2——只掉了 0.3。作为对照,下面几行是「单纯减少头数或头维度」的基线:1 头 128 维 → 31.2,8 头 16 维 → 30.9,全都比 MQA 更差。MQA 是一个比「直接砍头」聪明得多的省法。右图(Ainslie 2023):GQA-XXL 在几乎等同于 MQA 的延迟下,质量逼近 MHA-XXL;下面那张图显示延迟只有在 GQA 组数超过 32 之后才开始爆炸,所以 8 组是个非常划算的点——这也是今天几乎所有模型都取 8 的原因。

11.5 稀疏与滑动窗口注意力

另一条正交的路线是不要让每个 token 都看所有 token。全注意力的代价是 $O(n^2)$,上下文一长就无法承受。

全注意力、strided 稀疏注意力与 fixed 稀疏注意力的掩码模式
Child 等人(2019)的三种模式。(a) 标准因果注意力:下三角全填满,$O(n^2)$。(b) strided:一个头看局部窗口,另一个头按固定步长跳着看,两者组合起来仍能覆盖全部历史。(c) fixed:固定若干「汇总列」,让远距离信息通过这些列中转。核心权衡是表达力 vs 运行时。GPT-3 用的是稠密层和稀疏层交替,Mistral 用滑动窗口,GPT-OSS、Gemma 4 也走这条路。

11.6 当前的标准做法:全局层与局部层交错

纯滑窗有个明显缺陷:信息只能靠层层传递慢慢往远处走,真正的长程依赖会被削弱。今天的主流方案是混合——大部分层用滑动窗口(便宜),每隔几层插一个全注意力层(贵但能看到全局)。

Cohere Command A 的交错注意力结构图
Cohere Command A 的结构:每 4 层里有 3 层是滑窗自注意力(带 RoPE),第 4 层是全注意力。关键细节在右边的两个块的对比——滑窗层用 RoPE 位置编码,全注意力层则「No positional embeddings」,也就是 NoPE。分工非常清晰:短程信息交给 RoPE + 滑窗,长程信息交给 NoPE 的全注意力层。因为全注意力层没有位置编码,它对长度的外推行为要好得多(token 顺序信息仍然通过因果掩码隐式存在)。
Gemma 4、OLMo 3、Qwen 3.5 的交错注意力结构
三个 2025–2026 年模型的做法。左:Gemma 4 用多个 Local Attention 层夹一个 Global Attention 层,并且最后一层永远是全局的,位置信息只加在局部层上。中:OLMo 3 的配置表非常值得抄下来——梯度裁剪 1.0、z-loss 权重 $10^{-5}$、嵌入层不做 weight decay、3/4 的层用 4096 窗口的滑窗注意力、RoPE 只在全注意力层上做 YaRN 缩放、RoPE base $5\times10^5$、layer norm 施加在输出上(即非残差 post-norm)。右:Qwen 3.5 / Qwen 3 Next 把门控注意力、Gated DeltaNet 和 MoE 混在一起——这已经是下一讲的内容了。

12. 汇总:各大模型的共识与分歧

把前面十一节的结论压进一张表。纵向看是一个模型的完整配方,横向看是某个设计维度上的行业演化。

模型Norm 类型Norm 位置激活位置编码bias注意力词表
原始 Transformer (2017)LayerNormpost(残差流内)ReLU正弦有MHA37,000
GPT-3 (2020)LayerNormpreGeLU学习式绝对有稠密/稀疏交替50,257
T5 v1.1 (2020)RMSNormpreGeGLU相对位置偏置无MHA32,128
PaLM (2022)RMSNormpre(并行块)SwiGLURoPE无MQA256,000
LLaMA / LLaMA-2 (2023)RMSNormpreSwiGLURoPE无MHA → GQA32,000
Mistral 7B (2023)RMSNormpreSwiGLURoPE无GQA + 滑窗32,000
DeepSeek 67B (2024)RMSNormpreSwiGLURoPE无GQA(V2 起用 MLA)100,000
Gemma 2 (2024)RMSNormdouble normGeGLURoPE无局部/全局交错 + soft-cap256,000
OLMo 2 (2025)RMSNorm非残差 postSwiGLURoPE无GQA + QK-norm + z-loss~100k
Qwen 3 (2025)RMSNormpreSwiGLURoPE无GQA + QK-norm~152k
OLMo 3 (2025)RMSNorm非残差 postSwiGLURoPE($\theta=5\!\times\!10^5$)+ YaRN无3/4 层滑窗(4096) + 全注意力~100k
Gemma 3 / 4 (2025–26)RMSNormdouble normGeGLURoPE(部分维度)无局部/全局交错 + QK-norm262,144
按年份排列的架构选择表与高层观察
把同一份数据按年份纵向排开,两个趋势一目了然:一是 2023 年之后「LLaMA-like」配置的统治地位——Norm 列几乎全是 RMSNorm,Position 列几乎全是 RoPE,Activation 列几乎全是 SwiGLU/GeGLU;二是 「Other tricks」这一列随年份逐渐被填满——z-loss、QK-norm、logit soft-capping、混合注意力,全都是 2023 年之后才出现的条目。架构本体停止了演化,创新转移到了稳定性和推理效率上。
核心结论 Tatsu 对整讲的收尾判断:大模型在架构和超参数上的绝大多数方面已经趋同。真正还在分化的主要是三处——位置编码(RoPE 的 base、部分旋转、NoPE、混合注意力下的分工)、激活函数(SwiGLU vs GeGLU,本质上是无关紧要的分歧)、tokenization(词表大小从 32k 到 262k,差了 8 倍,而且这个选择会连带影响嵌入层参数量、输出 softmax 显存、多语言能力)。如果你要在架构上花时间,花在这三处(以及下一讲的注意力/MoE)上的回报最高。

本讲小结

一页速查:从零搭一个模型的默认配方

决策点默认选择证据强度什么时候该偏离
Norm 位置pre-norm(+ 最后一个 final norm)强:梯度分析 + 多组独立实验想更稳可以试 OLMo 2 的非残差 post-norm
Norm 类型RMSNorm强:同质量 + 约 5% 吞吐基本没有理由用 LayerNorm
bias全部去掉中:内存 + 稳定性论证基本没有理由保留
激活SwiGLU,$d_{ff}=\frac{8}{3}d_{model}$ 取整到 128 的倍数强:两组独立受控实验GeGLU 等价;追求极简可以用 ReLU
位置编码RoPE,base 10000(长上下文用 $5\times10^5\sim10^6$)强:数学动机清晰 + 全行业采用长上下文时考虑部分旋转 / NoPE 全局层
$d_{ff}/d_{model}$4(GLU 时 8/3)强:Kaplan 扫描图,1–10 是平盆地盆地很宽,不必纠结
$n_{head}\times d_{head}$$=d_{model}$,$d_{head}=128$弱:几乎无人验证可以大胆试,反正没人知道最优是什么
$d_{model}/n_{layer}$100–200中:很宽的平地由并行策略和推理延迟决定,不是由损失决定
词表单语 32–50k;多语言 100–260k中取决于语言覆盖;注意嵌入/softmax 的显存代价
Dropout0(预训练)强:单遍数据无需防过拟合微调阶段可以用
Weight decay0.1(排除嵌入/bias/norm)中:作用在优化动力学上必须和 LR 调度一起调
优化器AdamW,$\beta=(0.9,0.95)$,裁剪 1.0强Muon / SOAP 值得关注但尚未定论
稳定性QK-norm(+ 可选 z-loss $10^{-5}$)中:新但扩散极快soft-capping 效果不如 QK-norm,谨慎
注意力GQA,8 个 KV 组强:质量几乎无损,KV cache 省 8 倍长上下文再叠滑窗 + 全局层交错

三条方法论要点

  1. FLOPs 不等于运行时间。归一化占 0.17% 的 FLOPs、25.5% 的运行时间。任何架构决策都要同时算「算多少」和「搬多少」——这是本讲里 RMSNorm、去 bias、并行块、GQA 四个选择背后的共同逻辑。
  2. 区分「有证据的共识」和「路径依赖的共识」。$d_{ff}=4d_{model}$ 有扫描图,$n_{head}\times d_{head}=d_{model}$ 什么都没有。前者放心抄,后者是留给你的实验空间。
  3. 稳定性技巧的共同模式:掐掉「模型不在乎但数值格式在乎」的自由度。logits 的整体平移、Q/K 的范数、注入残差流的量级、单步更新的幅度——每一个都是模型损失函数看不见、但 bf16 很在意的方向。

附录:延伸阅读

归一化

激活与前馈层

位置编码

超参数与缩放

系统与效率

稳定性与优化器

模型技术报告(当架构参考书用)