第 3 讲:模型架构与超参数

目录 · ← l2 · l4 →

第 3 讲:模型架构与超参数

日期:4 月 6 日(周一,Spring 2026) | 讲师:Tatsu Hashimoto | 材料:lecture_03.pdf

概览

本讲回答两个问题:大型语言模型有哪些共同点?哪些地方在变? 先回顾”原始 Transformer”的设计选择(post-norm + LayerNorm、正弦位置编码、ReLU 前馈),再对比”简洁的现代变体”(pre-norm、RoPE、SwiGLU、去掉 bias),随后系统梳理几十个已发布模型在归一化、激活函数、位置编码以及超参数(FFN 比例、head 维度、长宽比、词表大小、正则化)上的经验共识,最后讲稳定性技巧(z-loss、QK-norm、logit soft-capping)。

核心概念与定义

  • Pre-norm 与 post-norm:把 LayerNorm 放在子层之前(pre-norm),从而不让它落在主残差信号通路上。几乎所有现代 LM 都是 pre-norm(BERT 是 post-norm);OPT-350M 是个有趣的反例。
    • 类比:pre-norm 像把滤水器装在水龙头上(用水时才净化,主管道保持干净);post-norm 像把滤水器装在蓄水池出口,所有水回流时都得穿过它。
    • 原因:更好的梯度传播、更少的梯度尖峰、大规模下的稳定性,并允许更大的学习率;最初宣称的好处是”可以去掉 warmup”。
  • LayerNorm 与 RMSNorm:LayerNorm 在隐藏维度上减均值、除方差;RMSNorm 只用均方根做缩放(y = x / sqrt(mean(x²)+ε) * γ),不减均值、没有 bias。GPT-3/OPT/GPT-J 用 LayerNorm;LLaMA/PaLM/Chinchilla/T5 用 RMSNorm。
    • 为什么用 RMSNorm:运算更少(不算均值)、参数更少(没有 bias);两者的 FLOPs 本来就微不足道,但FLOPs ≠ 运行时间——数据搬运才是关键,RMSNorm 搬的字节更少,因此实测墙钟时间更快(Narang 等,2020)。
  • 门控线性单元(*GLU):把 FFN(x) = activation(xW1) W2 换成门控版本,例如 SwiGLU(x) = (swish(xW1) ⊙ (xV)) W2,多出一个门控投影 V。GeGLU(高斯误差门控)与 SwiGLU 是标准选择;门控 FFN 的中间维度取约 2/3。证据显示收益稳定(Shazeer 2020;Narang 等 2020)。2023 年后的模型大多用 SwiGLU。
    • 类比:门控单元像夜店门口的保安:一个投影逐元素决定”内容”投影能通过多少。
  • 串行层与并行层:标准块先 attention 再 MLP(串行);”并行”块(GPT-J、PaLM)把两者并列计算后相加。并行层可共享 LayerNorm、融合矩阵乘法,但现代模型大多是串行。
  • 位置编码(position embeddings):正弦式(加 sin/cos,原始 Transformer)、绝对可学习式(GPT-1/2/3、OPT)、相对式(T5、Gopher),以及 RoPE(旋转位置编码,GPT-J/PaLM/LLaMA 及绝大多数 2024+ 模型)。
  • RoPE(旋转位置编码):把 query/key 的坐标成对旋转一个与位置成正比的角,使 attention 分数只依赖相对位置:⟨f(x,i), f(y,j)⟩ = g(x, y, i−j)
    • 类比:把每个 token 的向量想成一根钟表指针;RoPE 把指针旋转一个代表位置的角。两根指针旋转后的点积只取决于角度差(相对位置),与钟表的绝对读数无关。
  • 超参数共识:FFN 维度 ≈ 4 × 模型维度(GLU 变体约 8/3,实际多用 2.5–2.7);head_dim × num_heads ≈ model_dim(比例大多围绕 1);长宽比 model_dim/layers ≈ 100–200;词表大小:单语 30–50K,多语 100–250K。
  • 正则化:新模型在预训练中基本不用 dropout(数据量太大、只过一遍、难以记住),只依赖 weight decay——而在 LLM 中 weight decay 更多是影响优化动力学(与学习率调度耦合),而非控制过拟合(Andriushchenko 等 2023)。
  • 稳定性技巧
    • z-loss:加一项惩罚 log-sum-exp 过大的损失,防止 logits 漂移;PaLM、Baichuan 2、DCLM、OLMo 2/3 使用。
    • QK-norm:在 attention softmax 之前对 query/key 做归一化(RMSNorm/LayerNorm),让 attention logits 有界;DCLM、OLMo 2、Gemma 2、Qwen 3、Chameleon 使用。
    • Logit soft-capping:用 tanh 把 logits 截断到上限。
  • GQA/MQA 回顾:减少 key/value head 数量以削减 KV cache、提升推理性能(详见第 10 讲)。
  • 交错 attention(interleaved attention):例如 Cohere Command A 每第 4 层用 full attention、其余用局部 attention;LLaMA 4、Gemma 3/4、OLMo 3 交错使用滑窗(SWA)与 full attention。

代码示例:RMSNorm(作业 1 的公式)

讲义幻灯片定义了要实现什么,作业 1 handout 给出了形式化描述。给定激活向量 a ∈ R^d_model,RMSNorm 逐元素重缩放:

代码(Python):

import torch
from torch import nn

class RMSNorm(nn.Module):
    def __init__(self, dim: int, eps: float = 1e-6):
        super().__init__()
        self.eps = eps
        self.weight = nn.Parameter(torch.ones(dim))  # 可学习的逐维增益 γ

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # x: (..., dim)
        rms = torch.sqrt(x.pow(2).mean(dim=-1, keepdim=True) + self.eps)
        return x / rms * self.weight

代码做了什么: 计算最后一维的均方根,用它(加 ε)去除原张量,再乘以可学习的增益。不减均值、没有 bias——正是现代配方。

实现深挖:

  • 为什么不用减均值和 bias:运算更少、需要搬运的参数更少;经验上与 LayerNorm”一样好”,却节省墙钟时间(FLOPs 由矩阵乘法主导,但数据搬运不是)。
  • 为什么要 ε:当均方根极小时保证数值稳定。
  • 为什么它属于”现代变体”清单:pre-norm 位置 + RMSNorm + RoPE + SwiGLU + 无 bias 正是作业 1 要实现的内容——讲义明确发问”我们为什么这样选?你应该怎么选?”(答案:经验共识 + 稳定性 + 效率)。

与作业的联系:作业 1 第 3 节:实现 RMSNorm,接口与上面一致(它也会被用在你的 attention 与 FFN 块里)。作业 2 的 RMSNorm 融合 Triton kernel 则把同一个算子写成单个 GPU kernel——它是 memory-bound 的逐元素运算,融合收益显著。

代码示例:RoPE(概念实现)

代码(Python):

def precompute_rope_cache(seq_len: int, head_dim: int, base: float = 10000.0):
    # positions: [seq_len]
    positions = torch.arange(seq_len)
    # frequencies: [head_dim // 2](等比数列)
    freqs = 1.0 / (base ** (torch.arange(0, head_dim, 2) / head_dim))
    # angles: [seq_len, head_dim // 2]
    angles = positions[:, None] * freqs[None, :]
    cos = torch.cos(angles)
    sin = torch.sin(angles)
    return cos, sin  # 预计算一次,所有层/头复用

def apply_rope(x, cos, sin):
    # x: [..., seq, head_dim];成对旋转坐标
    x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2 :]
    # 复数式旋转:(x1 + i x2) * (cos + i sin)
    return torch.cat([x1 * cos - x2 * sin, x1 * sin + x2 * cos], dim=-1)

代码做了什么: 先为所有位置构建 cos/sin 缓存(采用经典的等比频率序列),再把 query/key 的每一对坐标 (x1, x2) 按位置相关的角度旋转——即复数乘法 (x1 + i·x2)(cos + i·sin)

实现深挖:

  • 为什么成对旋转:每次旋转二维坐标对是 RoPE 的标准构造(动机来自复数);Gemma 4 的变体只旋转前 2 个坐标。关键性质是:⟨RoPE(x,i), RoPE(y,j)⟩ = g(x,y,i−j)——内积(attention 分数)只依赖相对位置,且没有交叉项(这正与加法式正弦编码不同)。
  • 为什么要预计算:cos/sin 表只取决于 (seq_len, head_dim),算一次、每次 attention 直接索引即可。
  • 为什么这在 GQA/MLA 里很微妙:RoPE 与 MLA 式 KV 压缩不兼容(见第 4 讲),所以 DeepSeek 额外保留了若干”不旋转”的隐层 key 维度——即使你只实现普通 RoPE,这个实现细节也值得知道。

与作业的联系:作业 1 第 3 节:实现 RoPE 并在多头注意力中对 Q、K 应用(在算 attention 分数之前或之中)。旋转的轴搞错(每头、每位置的对应关系)是经典调试点。

关键要点

  1. 架构共识:pre-norm(非残差式)、RMSNorm(或 LayerNorm)、SwiGLU 门控、串行层、无 bias、RoPE——这个”现代变体”就是你作业 1 要实现的东西。
  2. FLOPs ≠ 运行时间:数据搬运才是关键;去掉 bias、使用 RMSNorm 即使不改变 FLOPs 也能节省墙钟时间。
  3. 超参数共识:FFN ≈ 4× 模型维度(GLU 为 8/3×)、head_dim×heads ≈ model_dim、长宽比 100–200、词表 30–50K(单语)。
  4. 大规模下的正则化主要关乎优化动力学而非过拟合(dropout 基本消失;weight decay 与学习率调度耦合)。
  5. 稳定性技巧(z-loss、QK-norm、soft-capping)之所以存在,是因为”带指数的 softmax”正是训练爆炸的地方。

常见陷阱

  • post-norm 配大规模训练:post-norm 把归一化放进残差通路,大规模下容易不稳定;现代做法是 pre-norm(可选再加一个非残差的 post-norm,如 Grok/Gemma 2/OLMo 2)。
  • GLU 用了错误的 FFN 比例:SwiGLU 若仍用 4× 模型维度,参数量会多出约 1.5 倍;应当用约 8/3×。
  • RoPE 应用到错误维度、或忘记逐头旋转:attention 会静默退化;务必与参考实现比对。
  • 盲目照搬词表大小:32K 词表适合英文,但多语模型需要 100–250K。
  • 忽视 softmax 稳定性:没有 z-loss/QK-norm,logits 可能漂移,大训练直接发散。

复习题

  1. 问: 现代模型为什么用 pre-norm 而不用 post-norm?
    • 答: pre-norm 让 LayerNorm 不落在主残差通路上,改善梯度流动、减少尖峰,从而在大规模下更稳定并支持更大的学习率(最初宣称的好处是省掉 LR warmup)。
  2. 问: 某模型 model_dim=4096、采用 SwiGLU,FFN 维度大致应取多少?为什么?
    • 答: 约 8/3 × 4096 ≈ 10,923(实际模型多取 2.5–2.7 倍)。门控变体之所以只要 ReLU FFN 的 2/3 维度,是因为门控在增加参数的同时提升了表达能力。
  3. 问: 是什么性质让 RoPE 在”使用绝对位置索引”的同时具有相对性?
    • 答: 旋转让两个位置编码向量的内积只依赖角度,即 ⟨f(x,i), f(y,j)⟩ = g(x,y,i−j)——绝对位置被消掉。