Transformer 解剖:从 Attention 到推理系统
第 5 章 Transformer Block:FFN、LayerNorm、Residual 的角色
第 2、3、4 章我们把 Multi-Head Self-Attention 拆得很透了。但单靠 Attention 没法搭起一个 Transformer——Llama 70B 的 80 层、GPT-3 的 96 层,每一层都不是纯 attention,而是一个完整的 Transformer Block:Attention + 前馈网络(FFN)+ 残差连接(Residual)+ 层归一化(LayerNorm)。
为什么是这四件套?为什么少了任何一个 Transformer 都训不动?这一章我们把 Block 的每个零件单独打开看,再合在一起看它们是怎么互锁的。读完这章你能:
- 在白板上画出一个完整的 Transformer Block 的数据流(含张量形状);
- 解释为什么 FFN 必须存在、为什么是 4×、为什么 ReLU 后来被 SwiGLU 替代;
- 解释 Pre-LN 和 Post-LN 的差别,以及为什么今天的大模型几乎一边倒选 Pre-LN;
- 解释 RMSNorm 为什么取代了 LayerNorm;
- 估算一个模型的参数量、计算量在 Attention 和 FFN 之间是怎么分配的。
5.1 为什么 Attention 不够:缺非线性
回想一下 Self-Attention 的输出是什么:
每个输出位置是 value 向量的加权和。其中 是输入的线性投影,权重 来自 softmax,但加权求和本身仍然是输入的线性组合——把 视作系数,整个 attention 输出就是 的线性组合。
唯一的非线性来自 softmax。但 softmax 只作用在 attention 权重上,不作用在 value 本身的特征空间里。结果就是:经过若干层 Self-Attention 之后,输出仍然停留在「输入向量空间」附近的某种线性变换——这远远不够用来表达自然语言里的复杂结构。
这是一个深度学习里很基础的原则:纯线性的网络(无论多深)等价于一层线性变换。要让深度网络真正「深」起来——也就是每一层都能引入新的表达能力——必须每一层都加上一个非线性变换。
Transformer Block 里这件事由 Feed-Forward Network(前馈网络,FFN)来做。
5.2 FFN:每个 token 独立的非线性变换
FFN 的设计简单到出奇:
也就是一个两层全连接 + 一个非线性激活函数。 把维度从 升到 (典型 ),激活后再用 降回 。
关键点:FFN 是逐 token 独立的。每个位置的输入向量分别通过 FFN,不同位置之间没有交互。这个性质是 Self-Attention 的镜像——Self-Attention 让位置之间交互,FFN 让特征在每个位置上做非线性变换,两者互补。
flowchart TB X["输入 (T, d_model)"] --> SA[Self-Attention<br/>位置之间交互] SA --> FFN[FFN<br/>每个位置独立做<br/>非线性变换] FFN --> OUT["输出 (T, d_model)"] SUB1[所有位置 互相打通] -.对应.-> SA SUB2[每个位置 单独加工] -.对应.-> FFN
可以把它理解成「人开会」的隐喻:Self-Attention 是大家聚在一起讨论(位置间信息交换),FFN 是会后每个人回去自己消化加工(位置内非线性变换)。Transformer Block 是「讨论 → 消化」的循环——每一层都做一次。
FFN 的尺寸为什么是 4×
原始论文里 ,这成了标准 FFN 的事实默认值。改用 SwiGLU 的现代模型则从 起步——那是为了让三个矩阵的总参数量和 的两矩阵 FFN 打平,下文会讲。
为什么是 4?这没有一个严格证明,但有几条经验观察:
第一,FFN 是 Transformer 的「记忆容量」。研究表明,FFN 的权重很大程度上扮演着「key-value 字典」的角色——Geva et al.(2021)的论文 Transformer Feed-Forward Layers Are Key-Value Memories 论证了:FFN 第一层的权重充当一组 key(与输入做内积得到每条「记忆」的激活系数),第二层的权重充当对应的 value,按系数加权取回。 越大,能存的 key-value 对越多,模型记忆容量越大。
第二, 大致是表达力和参数量之间的甜点。更窄则 FFN 容量受限,更宽则参数量和计算量线性上涨、收益递减。原论文没有论证 为何最优,它更像一个被后续模型一路沿用下来的经验默认值。
第三,FFN 占 Transformer 大约 2/3 的参数。一个 Transformer Block 的参数量(不含归一化和 bias):
- Attention:( 各一个 )
- FFN:( 各一个 )
合起来 ,FFN 占 。也就是说Transformer 大部分参数其实在 FFN 里——这个直觉对很多读者是反常识的,因为 attention 名声更大。
激活函数:从 ReLU 到 GELU 到 SwiGLU
原始 Transformer 用的激活函数是 ReLU:。简单粗暴有效。
GPT(初代)和 BERT 改用了 GELU(Gaussian Error Linear Unit):
其中 是标准正态分布的累积分布函数。GELU 是 ReLU 的「平滑版」——在 0 附近不再有硬切,而是平滑地过渡。BERT、GPT-2/3 都用 GELU。
到了 PaLM、Llama 1 这一代,激活函数又一次升级到 SwiGLU(Swish-Gated Linear Unit),由 Shazeer 在论文 GLU Variants Improve Transformer(2020)提出。SwiGLU 是「门控版 FFN」:
其中 (sigmoid 加权), 是逐元素相乘。和原始 FFN 比,SwiGLU 多了一条并行的线性分支: 作为门,逐元素调制线性分支 的输出——本质是「让模型学会哪个特征该开、哪个该关」。在 transformers 的 Llama 实现里,、、 分别对应 gate_proj、up_proj、down_proj(transformers-4815a0a/src/transformers/models/llama/modeling_llama.py:175)。
flowchart LR X["输入 x"] --> W["× W (门控分支 gate_proj)"] X --> V["× V (线性分支 up_proj)"] W --> SW["Swish 激活"] V --> GATE SW --> GATE["逐元素相乘 ⊙"] GATE --> W2["× W_2 (降回 d_model)"] W2 --> OUT[输出]
SwiGLU 多了一个矩阵 ,参数量大约是原始 FFN 的 1.5 倍。为了控制总参数量不爆炸,实际工程把 从 改成 ——参数量大致与原 FFN 相当但表达力更强。这就是 Llama 系列配置里 intermediate_size 以 起步的来历(Llama 1/2 的 7B/13B 再向上对齐到 256 的倍数;Llama 2 70B 和 Llama 3 还会再乘一个系数,见 5.7 节细节六)。
为什么 SwiGLU 比 GELU 好? 主要是经验:门控让 FFN 能「对每个特征独立打开/关闭」。Shazeer 那篇 GLU Variants Improve Transformer 在 T5 的预训练与下游任务上做了对照,相同参数量和计算量下,SwiGLU/GEGLU 这一族的预训练困惑度低于 ReLU / GELU,下游任务也多数更好(幅度不大);Llama 1 报告直接采用了这个结论。今天主流开源大模型大多切到了 SwiGLU(Gemma 用的是同族的 GeGLU)。
主流模型激活函数对照:
| 模型 | 激活 | FFN 类型 | |
|---|---|---|---|
| 原始 Transformer | ReLU | 标准 FFN | |
| BERT / GPT-2/3 | GELU | 标准 FFN | |
| Llama 1/2 (7B/13B) | SwiGLU | GLU 变种 | (4096 → 11008) |
| Llama 2/3 (70B) | SwiGLU | GLU 变种 | (8192 → 28672) |
| Mistral 7B / Qwen2 | SwiGLU | GLU 变种 | / (transformers-4815a0a/src/transformers/models/qwen2/configuration_qwen2.py:63 的缺省是 4096 → 22016) |
| PaLM | SwiGLU | GLU 变种 | (未按 折算,论文接受了三矩阵的额外开销) |
直觉总结:FFN 是「每个 token 单独经过一个深度小、参数大的非线性层」,给整个模型提供主要的非线性表达力和大部分参数容量。激活函数从 ReLU 到 SwiGLU 是工程上「在不改变结构的前提下挤出更多表达力」的演化。
5.3 Residual:让深度可训
设想一下:堆叠 80 层 Transformer,每一层做 Attention + FFN。从第 80 层往前,loss 对第 1 层的梯度要穿过 80 次复合运算——每次都包含矩阵乘法和非线性。链式法则里这相当于 80 个雅可比矩阵相乘,方差累积、梯度消失或爆炸的风险极高。
这是深度神经网络从 2010 年代初一直被困扰的问题。直到 2015 年 ResNet(He et al.)提出残差连接(residual connection / skip connection):
每一层的输入 直接「跳过」函数 ,加到输出上。这意味着梯度可以从 直接传到 ,完全绕过 :
那个「+1」就是关键——它保证梯度有一条系数恒为 1 的快速通道,无论 的雅可比是多少,恒等那一路的梯度都能原样传到底层。
Transformer 把这个想法用在了每个子层(Attention 子层和 FFN 子层)上(这里先按今天主流的 Pre-LN 写法,原始论文的 LayerNorm 放法见 5.4 节):
具体到 Block:
x_in
├─→ LN → MHA → out1
│ |
├────────────────+→ x_mid
│
├─→ LN → FFN → out2
│ |
├────────────────+→ x_out
每个子层(Attention、FFN)都有一条 residual 旁路。这样从输入到输出至少存在一条「线性恒等」的路径——梯度可以无损地传到任何一层。
flowchart TB XIN[x_in] --> LN1[LayerNorm] XIN --> ADD1["+ residual"] LN1 --> MHA[Multi-Head<br/>Attention] MHA --> ADD1 ADD1 --> XMID[x_mid] XMID --> LN2[LayerNorm] XMID --> ADD2["+ residual"] LN2 --> FFN[FFN] FFN --> ADD2 ADD2 --> XOUT[x_out]
Residual 的几何理解
另一个非常有用的视角:把每一层的 看成是「对 的微调」。Residual 让模型默认「保留 的大部分信息,在它上面做一点微调」。深度网络的每一层不需要重新发明轮子——它继承上一层的状态,只在必要的方向上加工。
这个性质和 Transformer 训练动力学非常契合。当 80 层堆叠时,每层只需要做「小改动」,最终累计成大变化。如果没有 residual,每层都要从零构建一个完整的输出——80 次完整构建几乎不可能稳定收敛。
实践中的一个观察:当你在训练一个 Transformer 时把 residual 砍掉(强制 ),层数一多往往就训不起来。residual 不是锦上添花,是必要条件。
5.4 LayerNorm:稳定每个位置的输入分布
加入 residual 之后,新问题来了:每一层 residual 把 输出,下一层的输入就是「原始输入 + 多次累加的修改量」。如果不约束,这个输出的方差会逐层放大(每加一次 都引入新方差),到第 80 层会大到难以控制。
这就需要归一化(normalization)来约束每一层的输入分布。
LayerNorm 的定义
LayerNorm(Ba et al., 2016)是为 RNN/序列模型设计的归一化方法:对每个 token 的特征向量,沿着特征维做归一化:
其中:
- 是特征的均值
- 是特征的方差
- 是可学习的缩放和偏移参数
- 是数值稳定项(典型 )
注意 LayerNorm 是对每个 token 单独做的——位置 的归一化只用位置 自己的特征,与其他位置无关。这是它和 BatchNorm 最大的区别:
| 特性 | BatchNorm | LayerNorm |
|---|---|---|
| 归一化沿哪个维度 | batch 维 | 特征维 |
| 是否跨样本 | 是 | 否 |
| 训练/推理是否一致 | 不一致(推理用滑动平均) | 一致 |
| 序列长度变化 | 敏感 | 不敏感 |
| 对 batch_size 敏感 | 是(小 batch 不稳定) | 否 |
LayerNorm 的「不跨样本」和「不依赖 batch_size」是它在序列模型里大获成功的关键。语言任务里 batch 之间序列长度可以差很多,batch_size 经常被显存约束(特别是大模型),BatchNorm 的统计量会非常不稳定。
Pre-LN vs Post-LN:一个分水岭
原始 Transformer 论文里 LayerNorm 放在 residual 之后——叫 Post-LN:
但很快人们发现,Post-LN 训练 Transformer 很不稳定,特别是在层数多、模型大的情况下。GPT-2 把 LayerNorm 挪到了每个子层的输入端、residual 分支之内(论文只说仿照 pre-activation ResNet,没有展开原因),这就是 Pre-LN:
flowchart TB
subgraph postln ["Post-LN(原始论文,不稳定)"]
P_X[x] --> P_SUB[SubLayer]
P_X --> P_ADD["+"]
P_SUB --> P_ADD
P_ADD --> P_LN[LayerNorm]
P_LN --> P_OUT[y]
end
subgraph preln ["Pre-LN(GPT-2 之后主流)"]
PR_X[x] --> PR_LN[LayerNorm]
PR_X --> PR_ADD["+"]
PR_LN --> PR_SUB[SubLayer]
PR_SUB --> PR_ADD
PR_ADD --> PR_OUT[y]
end
为什么 Pre-LN 更稳定?关键在 residual 的「恒等通道」。Pre-LN 的残差连接把 直接加到 上,不经过任何归一化——梯度可以原路返回,链式法则上是 。
Post-LN 把 LayerNorm 放在 residual 之后,残差连接中间多了一次归一化。LayerNorm 的雅可比矩阵是非平凡的(涉及标准差的倒数等),把它接到 residual 路径上,梯度的「恒等通道」被破坏了——恒等那一路的梯度不再能原样回传,会随层数衰减。
Xiong et al.(2020)在论文 On Layer Normalization in the Transformer Architecture 里给了数学分析:在初始化时刻,Post-LN 靠近输出那几层的参数梯度尺度是 ,不随层数 变小,只能靠 learning-rate warm-up 压住训练初期的更新;而 Pre-LN 的同一个上界带一个 因子,层数越深梯度反而越温和,所以 Pre-LN 可以去掉 warm-up、直接用较大的学习率。
实践中:
- Pre-LN:今天的 GPT、Llama、Mistral、DeepSeek 几乎全部用 Pre-LN。优点是训练稳定,可以堆很深。
- Post-LN:BERT 仍用 Post-LN,BERT-base(12 层)和 BERT-large(24 层)都要靠学习率 warmup(论文是前 10,000 步线性升温)才能稳定训练。
- Sandwich-LN:少数模型在子层前后都加归一化(CogView 的 Sandwich-LN;Gemma 2 也在子层输出上再加一层 RMSNorm,见
transformers-4815a0a/src/transformers/models/gemma2/modeling_gemma2.py:326、:332)——多一次归一化开销,换更稳的训练。 - DeepNorm:微软提出的 Post-LN 改进版,让 Post-LN 也能训上千层。技术上可行,但实际工业基本用 Pre-LN。
总结:你在写一个新 Transformer,无论模型多大,默认选 Pre-LN。
RMSNorm:LayerNorm 的简化版
2020 年之后,大模型逐渐把 LayerNorm 换成更简化的 RMSNorm(Root Mean Square Normalization,Zhang & Sennrich 2019 提出):
和 LayerNorm 比,RMSNorm 去掉了均值的减法——只用 RMS(root mean square,根均方)做缩放,没有偏移项 。
直觉:RMSNorm 论文的假设是,LayerNorm 起作用主要靠「重新缩放」,「减均值」(重新居中)可以省掉。少一次求均值和减法、少一个 参数,每个归一化层都省一点开销。
实验:RMSNorm 的原论文 Root Mean Square Layer Normalization(Zhang & Sennrich 2019)在多种架构和任务上做了对照,结论是质量与 LayerNorm 相当,而运行时间减少 7%~64%——区间这么宽是因为收益取决于模型结构,Norm 占总计算比重越大、省得越多。注意这个数字来自 RMSNorm 论文本身,不是 Llama 论文;Llama 论文只是沿用了 RMSNorm,没有单独为它做消融。
主流大模型几乎全部已切到 RMSNorm:Llama 1/2/3、Mistral、Qwen、DeepSeek、Yi——RMSNorm 是新一代标配。
| 归一化变种 | 公式核心 | 使用模型 |
|---|---|---|
| BatchNorm | 沿 batch 维归一化 | 不用于序列模型 |
| LayerNorm | 沿特征维归一化(含均值减法) | 原始 Transformer / BERT / GPT-2/3 |
| RMSNorm | 沿特征维归一化(只 RMS 缩放) | Llama / Mistral / Qwen / DeepSeek |
| GroupNorm | 沿特征分组归一化 | CV 模型常用,NLP 主干里少见 |
5.5 完整 Block 的数据流
把 5.2、5.3、5.4 节合在一起,今天主流 Transformer Block 长这个样子(Pre-LN + RMSNorm + GQA + SwiGLU):
flowchart TB XIN["x_in (T, d)"] --> RES1["+ residual"] XIN --> RMS1[RMSNorm] RMS1 --> RMSout1[normalized x] RMSout1 --> ROPE[RoPE 旋转 Q,K] ROPE --> MHA[Multi-Head<br/>Self-Attention<br/>GQA] MHA --> RES1 RES1 --> XMID["x_mid (T, d)"] XMID --> RES2["+ residual"] XMID --> RMS2[RMSNorm] RMS2 --> SWI[SwiGLU FFN] SWI --> RES2 RES2 --> XOUT["x_out (T, d)"]
形式化的数据流:
1. x ← x_in shape (T, d)
2. h ← x + Attention(RMSNorm(x)) shape (T, d)
3. y ← h + FFN(RMSNorm(h)) shape (T, d)
4. x_out ← y shape (T, d)
这就是 Llama 一个 Block 的全部内容。整个模型:
embedding → [Block × N] → final RMSNorm → LM head
每个 Block 的输入输出形状完全一致 ——这是 Transformer 能堆叠任意深度的关键性质。Llama-3 70B 的 80 层就是 80 个这样的 Block 串起来的。
5.6 参数量与计算量分解
来一笔账。设 、头数为 、(标准 FFN),单 Block 的参数:
| 子模块 | 参数量 | 占比 |
|---|---|---|
| MHA: | 33% | |
| FFN: (标准) | 67% | |
| RMSNorm × 2(仅 ) | <1% | |
| 合计 | 100% |
如果是 SwiGLU(),FFN 的参数:
(三个矩阵:、、)。所以总参数仍然是 ——SwiGLU 在保持参数量不变的前提下提升了表达力,这就是 5.2 节我们说「Llama 的 intermediate_size 是 倍」的工程合理性。
对应的整模型参数: 层 Block,每层 ,再加 embedding ( 是词表大小)和 LM head(如果不共享权重,又是一个 )。Llama-3 70B 的实际参数:
- , (GQA: ), ,
- G
- embedding(不共享): G
- LM head(不共享): G
- 两处校正(方向相反,正好差不多抵消):
- GQA 让 K/V 投影缩水: 不再是 而是 ,每层比 省 0.117 G,80 层省约 9.4 G;
- FFN 比 更宽:Llama-3 70B 的
intermediate_size是 28672()而不是 ——transformers-4815a0a/src/transformers/models/llama/convert_llama_weights_to_hf.py:170的compute_intermediate_size按 70Bparams.json里的ffn_dim_multiplier=1.3、对齐到 4096 算出这个值,于是每层 FFN 从 涨到 ,80 层多出约 13.4 G。
- 合计 G
按 Llama-3 70B 的配置逐项精确求和(含各层 RMSNorm 的 )是 70,553,706,496,约 70.6B——和我们的估算几乎一致。这里按「LM head 不共享」来算是对的:Llama 1 / 2 / 3 的 LM head 一直都是独立矩阵,并非「Llama 3 扩词表后才放弃共享」(见 §5.8 的说明)。这个估算量级正确,足以让你看到模型卡片就估出参数量。
计算量 / FLOPs 分解(前向,单个 token,标准 FFN):
| 子模块 | FLOPs |
|---|---|
| QKV 投影 | |
| Attention( 与 各一次) | |
| 输出投影 | |
| FFN | |
| 合计 |
注意 正好是「每层参数量 的两倍」——这就是那条常用的经验公式「前向 FLOPs ≈ 2 × 参数量」。上下文短时()FFN 主导计算量;当 越过 ,attention 那一项开始反超(摊到整段序列上,它就是那个 的平方项)。实际工程中 在 4K~128K 之间变化,计算瓶颈会从 FFN 慢慢转向 attention——这是第 13 章「长上下文之战」的工程动因。
计算-访存比(重要,预示推理工程):
- FFN:每一步都要把权重从 HBM 读出来做矩阵乘。prefill 或大 batch 时一份权重被很多 token 复用,是计算密集(compute-bound);小 batch 解码时每份权重只服务几个 token,瓶颈变成读权重的带宽
- Attention:解码时每一步都要读整段 KV Cache、做 softmax——访存密集(memory-bound)
这两种瓶颈对应不同的优化策略。FFN 用低比特量化(INT4 / FP8)把权重压小,让 HBM 读得快——第 16 章。Attention 用 Flash Attention 把 KV 访问局部化,让 SRAM 替 HBM 干活——第 18 章。这两条优化在第六部分会展开。
5.7 一些容易踩的细节
细节一:LayerNorm/RMSNorm 在哪里?
确认两遍:Pre-LN 下,LayerNorm 在子层(Attention 或 FFN)之前,residual 连接绕过 LayerNorm:
# 正确(Pre-LN)
h = x + attention(rmsnorm(x)) # 注意 residual 加的是 x,不是 rmsnorm(x)
y = h + ffn(rmsnorm(h)) # 同上
很多新手写错成 h = rmsnorm(x) + attention(rmsnorm(x))——把 residual 也加了归一化值,破坏了梯度直达通道。
细节二:最后一层归一化
整个模型最后还有一个 final RMSNorm(在 LM head 之前)。这个常被忽略,但它对 logits 的稳定有帮助。Llama 的 model.norm 就是这个最后的 RMSNorm(transformers-4815a0a/src/transformers/models/llama/modeling_llama.py:357)。
细节三:bias 还是不要 bias
LayerNorm 通常带 和 两个可学习参数;RMSNorm 只带 。FFN 的 在原始 Transformer 里有 bias,但 Llama / Mistral 等现代模型把 bias 全部去掉——参数减少、训练略加速、对质量几乎无影响。
细节四:QKV 投影是否带 bias
同样,BERT / GPT-2 这一代的 都带 bias,现代模型几乎都去掉了:transformers-4815a0a/src/transformers/models/llama/configuration_llama.py:81 的 attention_bias 与 :83 的 mlp_bias 缺省都是 False。少数例外是 Qwen2——transformers-4815a0a/src/transformers/models/qwen2/modeling_qwen2.py:189 只给 Q/K/V 三个投影留了 bias,输出投影和 MLP 仍然不带。
细节五:embedding 维度和 head 维度
经典设计里每个头维度 ,所以 要能被 整除;也有模型单独指定 head_dim,这时 不必等于 (如 transformers-4815a0a/src/transformers/models/gemma/configuration_gemma.py:65–:70 的缺省:hidden 3072、16 头、head_dim 256)。 最好也能被某些硬件友好的数(如 128)整除以让 GPU 矩阵乘高效。
细节六:FFN 的 实际值
SwiGLU 配 ,但实际上工程会向上对齐到某个 2 的幂的倍数让 GPU 友好(Llama 1/2 的 7B/13B 是 256)。Llama 7B 的 ,理论 ,实际取 11008(256 的整数倍)。Llama 2 70B 和 Llama 3 还会再乘一个额外系数:transformers-4815a0a/src/transformers/models/llama/convert_llama_weights_to_hf.py:170 的 compute_intermediate_size(n, ffn_dim_multiplier, multiple_of) 从 Meta 原始 params.json 读这两个参数(同文件 :362–:363),70B 的取值是 ffn_dim_multiplier=1.3、对齐到 4096——代入 ()。所以「」是起点而不是铁律。
5.8 把整个 Transformer 模型串起来
到这里,整个模型可以一目了然:
flowchart TB TOK[输入 token ids] --> EMB["Token Embedding"] EMB --> X0[x_0] X0 --> B1[Block 1] B1 --> B2[Block 2] B2 --> BD[...] BD --> BL[Block L] BL --> XL[x_L] XL --> FN[Final RMSNorm] FN --> HEAD[LM Head] HEAD --> LOGITS["logits 形状<br/>(T, V)"] LOGITS --> SM[softmax] SM --> P["词表上的<br/>下一 token<br/>概率分布"]
每个 Block 的内部就是 5.5 节的那套数据流。整个模型就是「embedding → N 层 Block → final RMSNorm → LM head → logits」。LM head 把 维向量投到词表大小 维(常见从 3.2 万(Llama 2)到 25.6 万(Gemma)不等),softmax 之后是下一 token 的概率分布。
LM head 共享权重(weight tying)是常见优化:把 LM head 的权重矩阵设为 token embedding 的转置,可以省一份 的参数(模型越小占比越大:GPT-2 small 里约占三成,Llama-3 70B 里只占约 1.5%)。不是所有模型都共享,而且方向常被记反:GPT-2 共享,Llama 1 / 2 / 3 都不共享(原始 checkpoint 里有独立的 output.weight),只有 Llama 3.2 的小模型(1B / 3B)才启用共享(转换脚本里 tie_word_embeddings=llama_version == "3.2",transformers-4815a0a/src/transformers/models/llama/convert_llama_weights_to_hf.py:400)——在那个参数量级上,省下一份 的收益才够大。
本章小结
- Self-Attention 是线性混合,必须配 FFN 引入非线性——FFN 是「逐 token 独立的两层 MLP」,提供模型主要的非线性表达力和大约 2/3 的参数容量。
- 激活函数演化:ReLU → GELU → SwiGLU。SwiGLU 用门控分支增强表达力,配合 保持参数量。
- Residual 是深度网络可训的必要条件——残差连接给梯度提供恒等通道,让 80 层堆叠也能稳定收敛。
- LayerNorm 把每层每个 token 的特征分布约束住——和 BatchNorm 不同,LayerNorm 不跨样本、不依赖 batch_size。
- Pre-LN 远比 Post-LN 稳定——Post-LN 把 LN 接到 residual 路径上破坏了梯度的恒等性。今天的大模型几乎全部用 Pre-LN。
- RMSNorm 是 LayerNorm 的简化版——去掉均值减法和偏移参数,原论文报告运行时间减少 7%~64%(取决于 Norm 占总计算的比重),质量几乎不损失。
- 完整 Block 的数据流:x → RMSNorm → MHA → +residual → RMSNorm → FFN → +residual。每个 Block 输入输出形状一致,可任意堆叠。
- 参数量分解:MHA 占 1/3,FFN 占 2/3。计算量上短上下文 FFN 主导,长上下文 Attention 的 项主导——这预示了第六部分推理优化的两条路线。
到这里第二部分结束。我们已经把 Transformer 的「数学层」拆完了——Self-Attention、Multi-Head、位置编码、Block 组装,所有零件都讲清楚了。
下一章进入第三部分「架构家族」。同样的 Transformer Block,可以搭出三种不同的架构:Encoder-only(BERT 系)、Encoder-Decoder(T5 系)、Decoder-only(GPT 系)。它们各自适合什么任务?为什么 Decoder-only 最终统治了大模型时代?这是第 6 章要回答的问题。
延伸阅读
- Vaswani et al., Attention Is All You Need, NeurIPS 2017,3.3 节是 FFN,3.1 节交代残差与 LayerNorm 的放法(5.4 节是 residual dropout 等正则化)。
- Ba et al., Layer Normalization, 2016. arXiv:1607.06450——LayerNorm 原始论文。
- Zhang & Sennrich, Root Mean Square Layer Normalization, NeurIPS 2019. arXiv:1910.07467——RMSNorm 原始论文。
- Xiong et al., On Layer Normalization in the Transformer Architecture, ICML 2020. arXiv:2002.04745——Pre-LN vs Post-LN 的严格分析。
- Shazeer, GLU Variants Improve Transformer, 2020. arXiv:2002.05202——SwiGLU 论文。
- Geva et al., Transformer Feed-Forward Layers Are Key-Value Memories, EMNLP 2021——FFN 作为 key-value 记忆的解读。
- He et al., Deep Residual Learning for Image Recognition, CVPR 2016——Residual 的奠基论文(虽然是 CV 的,思想一致)。
- Hendrycks & Gimpel, Gaussian Error Linear Units (GELUs), 2016——GELU 论文。