先说结论
Transformer 中的 FFN(Position-wise Feed-Forward Network)是一个逐位置应用的两层全连接网络,负责在自注意力层的"横向"信息交互之后进行"纵向"的非线性特征变换;激活函数的选择(从 ReLU 到 GELU 再到 SwiGLU/GeGLU)显著影响模型的语言建模质量。
为什么我会单独记这一篇
在 Transformer 的每一层中,Self-Attention 负责在 token 之间传递信息("横向"),但仅有线性变换是不够的——模型需要非线性能力来学习复杂的特征表示。FFN 就是提供这种非线性的组件。
具体来说,FFN 解决了以下问题:
- 非线性建模:Self-Attention 本质上是加权求和(线性操作),如果没有 FFN 中的非线性激活函数,多层堆叠的 Transformer 等价于一个大的线性变换,表达能力极为有限。
- 特征空间扩展:FFN 先将特征从 d_model 维升到 d_ff 维(通常 4 倍),在高维空间中进行非线性变换,再降回 d_model 维。这种"膨胀-压缩"结构为模型提供了额外的容量。
- 知识存储:研究表明,Transformer 的 FFN 层承担了类似"键值记忆"的角色——第一个线性层的权重充当"键",第二个线性层的权重充当"值",FFN 通过激活函数选择性地检索和组合这些记忆。
先把核心脉络捋清楚
标准 FFN 结构
FFN(x) = W2 * activation(W1 * x + b1) + b2
- W1: [d_model, d_ff],将输入从模型维度映射到 FFN 中间维度(升维)。
- activation: 非线性激活函数。
- W2: [d_ff, d_model],将中间维度映射回模型维度(降维)。
- d_ff 通常是 d_model 的 4 倍(原始 Transformer:d_model=512, d_ff=2048)。
激活函数的演进
ReLU(原始 Transformer)
ReLU(x) = max(0, x)
最简单的非线性激活:正值不变,负值截断为零。虽然简单有效,但存在"死神经元"问题——当输入持续为负时,梯度恒为零,神经元完全停止学习。
GELU(BERT、GPT-2/3)
GELU(x) = x * Phi(x)
其中 Phi(x) 是标准正态分布的累积分布函数
近似: GELU(x) ≈ 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3)))
GELU 可以理解为一种"软性"的 ReLU:不是硬性地将负值截断为零,而是根据输入值的大小平滑地"缩放"。GELU 的直觉是:较大的正值接近原样保留,较小的正值被适当缩放,负值趋近于零但不完全为零。
GELU 比 ReLU 更平滑,梯度更连续,实验表明在 Transformer 中表现更好。
GLU(Gated Linear Unit)
GLU(x, W, V, b, c) = sigma(xW + b) ⊙ (xV + c)
GLU 引入了门控机制:将输入通过两组不同的线性变换,一组经过 sigmoid 激活后作为"门",另一组作为"值",两者逐元素相乘。门控机制允许模型动态地选择哪些信息通过,哪些被抑制。
SwiGLU(LLaMA、Mistral 等现代 LLM)
SwiGLU(x, W, V, b, c) = Swish(xW + b) ⊙ (xV + c)
Swish(x) = x * sigmoid(x) 也称为 SiLU (Sigmoid Linear Unit)
SwiGLU 将 GLU 中的 sigmoid 门替换为 Swish(SiLU)激活。Swish 相比 sigmoid 的优势:不是将门限制在 [0, 1] 范围内,而是允许门值略大于 1,提供了更丰富的特征调制能力。
GeGLU(PaLM)
GeGLU(x, W, V, b, c) = GELU(xW + b) ⊙ (xV + c)
将 GLU 中的 sigmoid 替换为 GELU。GeGLU 和 SwiGLU 性能接近,两者都比标准 GELU 更好。
完整的 SwiGLU FFN 结构
现代 LLM(如 LLaMA)中 FFN 的完整结构:
FFN_SwiGLU(x) = W2 * (Swish(W_gate * x) ⊙ W_up * x)
其中:
- W_gate: [d_model, d_ff] — 门控投影
- W_up: [d_model, d_ff] — 上投影(值)
- Swish 激活应用于 W_gate 的输出
- 逐元素相乘实现门控
- W2: [d_ff, d_model] — 下投影
注意:SwiGLU FFN 实际上有三个权重矩阵(W_gate, W_up, W2),比标准 FFN 的两个(W1, W2)多一个。为保持参数量可比,通常将 d_ff 设为 8d_model/3(而非 4*d_model),然后向上取整为最近的 256 的倍数。
原理拆开看
为什么门控机制有效
门控机制的直觉可以用"信息过滤"来理解。在标准 FFN 中,所有信息通过同一个非线性函数处理。在门控 FFN 中,模型可以:
- 通过一个分支计算"哪些信息是有用的"(门)。
- 通过另一个分支计算"实际的信息内容"(值)。
- 通过逐元素相乘实现选择性传递。
这比单一的激活函数提供了更精细的控制粒度。实验表明,门控机制带来的性能提升主要来自其"动态选择性",而非单纯的参数增加。
FFN 作为键值记忆
Geva 等人(2021)的研究发现,Transformer 的 FFN 层可以理解为一种键值记忆系统:
- W1 的每一行是一个"键"(key),对应某种输入模式。
- W2 的每一列是一个"值"(value),对应某种输出模式。
- 激活函数充当"选择器",只有当输入与某个键匹配时,对应的值才会被激活。
- 每一层 FFN 存储不同的知识:底层存储语法知识,高层存储语义/事实知识。
这个视角解释了为什么 FFN 的中间维度需要远大于模型维度——更大的中间维度意味着更多的记忆槽位,可以存储更多的知识。
激活函数的性能对比
Shazeer(2020)在 "GLU Variants Improve Transformer" 论文中的实验结论:
| 激活函数 | 相对性能(越低越好) |
|---|---|
| ReLU | 基线 |
| GELU | +少量提升 |
| ReGLU (GLU + ReLU) | +中等提升 |
| GeGLU (GLU + GELU) | +显著提升 |
| SwiGLU (GLU + Swish) | +显著提升(最佳) |
放到工程里怎么落
SwiGLU FFN 的实现
# PyTorch 风格的伪代码
class SwiGLUFFN(nn.Module):
def __init__(self, d_model, d_ff):
self.w_gate = nn.Linear(d_model, d_ff, bias=False)
self.w_up = nn.Linear(d_model, d_ff, bias=False)
self.w_down = nn.Linear(d_ff, d_model, bias=False)
def forward(self, x):
return self.w_down(F.silu(self.w_gate(x)) * self.w_up(x))
注意:现代 LLM 通常不使用偏置项(bias=False),进一步简化计算。
参数量计算
标准 FFN(2 个权重矩阵):2 * d_model * d_ff SwiGLU FFN(3 个权重矩阵):3 * d_model * d_ff
为了在相同参数预算下比较公平,LLaMA 将 d_ff 从 4d_model 调整为约 2.67d_model(即 8/3 * d_model),这样 3 * d_model * (8/3 * d_model) = 8 * d_model^2,与标准 FFN 的 2 * d_model * 4*d_model = 8 * d_model^2 参数量相当。
混合专家(MoE)与 FFN
Mixtral 等模型将单个 FFN 替换为多个专家 FFN,通过路由器(router)为每个 token 选择 Top-K 个专家。这大幅增加了模型总参数量,但每次推理只激活部分参数,保持了计算效率。
与相邻概念的区别
- FFN vs Self-Attention:Self-Attention 是 token 间的信息交互(横向),FFN 是每个 token 内部的特征变换(纵向)。两者互补。
- SwiGLU vs GELU:SwiGLU 引入门控机制和三个权重矩阵,GELU 是简单的逐元素激活。SwiGLU 性能更好但计算量略大。
- FFN vs MoE:MoE 是 FFN 的一种扩展,将单个 FFN 替换为多个并行 FFN 加路由机制。
设计时真正要权衡什么
| 选择 | 优势 | 代价 |
|---|---|---|
| ReLU | 简单、计算快 | 性能不如 GELU/SwiGLU |
| GELU | 比 ReLU 更好、广泛验证 | 不如门控激活 |
| SwiGLU | 最佳性能 | 3 个权重矩阵、实现更复杂 |
| d_ff = 4*d_model | 标准配置 | 参数量大 |
| d_ff = 8/3*d_model (SwiGLU) | 参数效率更高 | 需要仔细调整 |
容易踩的坑
- 使用 ReLU 在现代 LLM 中:性能显著低于 SwiGLU,没有充分理由不应使用。
- SwiGLU 的三个矩阵混淆:w_gate 和 w_up 的角色不同,搞混会导致模型不收敛。
- d_ff 不是 256 的倍数:GPU 张量核心(Tensor Core)对 256 的倍数维度效率最高,非对齐维度会显著降低计算效率。
- 偏置项处理不一致:现代 LLM 通常去掉偏置,但如果训练时和推理时的偏置设置不一致,会导致精度问题。
- MoE 路由不均衡:如果路由器总是选择少数专家,其他专家得不到训练,模型容量浪费。
工程落地时我会怎么做
- 新项目默认使用 SwiGLU(或 SiLU),这是经过充分验证的最佳选择。
- d_ff 设为 8/3 * d_model,向上取整到最近的 256 的倍数。
- 去掉偏置项(bias=False),简化计算且不影响性能。
- 如果使用 MoE,确保负载均衡损失(load balancing loss)正常工作。
- FFN 是 Transformer 中参数量最大的组件(占总参数的约 2/3),是量化和剪枝的主要目标。
如果要对外讲,可以怎么概括
建议结构:
- 写出标准 FFN 的公式,解释"升维-激活-降维"的结构。
- 解释 FFN 的角色:自注意力是横向信息流,FFN 是纵向特征变换。
- 从 ReLU -> GELU -> GLU -> SwiGLU 的演进脉络,每一步解决了什么问题。
- 讨论 FFN 作为键值记忆的视角(这是面试加分项)。
- 提及 MoE 是 FFN 的一种扩展。
常见面试追问:
- 为什么 SwiGLU 比 GELU 好?(门控机制提供动态选择性)
- FFN 的中间维度为什么是 4 倍?(提供足够的非线性容量和记忆槽位)
- FFN 在 Transformer 中存储了什么?(键值记忆,底层语法、高层语义)
最后记几条
- FFN 是 Transformer 中参数量最大的组件,约占总参数的 2/3。
- SwiGLU 是当前最优激活函数,被 LLaMA、Mistral 等所有主流 LLM 采用。
- 门控机制的本质:让模型动态选择"哪些信息通过",比单一激活更精细。
- FFN 可以理解为键值记忆:W1 是键,W2 是值,激活函数是选择器。
- 三个权重矩阵:SwiGLU FFN 有 w_gate、w_up、w_down 三个矩阵,为保持参数预算,d_ff 从 4d_model 调整为约 2.67d_model。
面试高频题
QUESTION Transformer 中 FFN 的作用是什么? FFN 提供逐位置的非线性特征变换。Self-Attention 是 token 间的"横向"信息流(加权求和,本质是线性操作),FFN 是每个 token 内部的"纵向"变换,引入非线性能力。如果没有 FFN,多层 Transformer 等价于一个大线性变换,表达能力极其有限。
QUESTION 为什么现代 LLM 用 SwiGLU 而不是 ReLU? SwiGLU 引入了门控机制,让模型能动态选择"哪些信息通过"。相比 ReLU 的硬截断(负值归零),SwiGLU 的 Swish 激活 提供更平滑的梯度流动,且门控值可略大于 1,允许特征放大。Shazeer (2020) 的实验表明 SwiGLU 在相同参数预算下显著优于 ReLU、GELU。
QUESTION SwiGLU FFN 为什么有三个权重矩阵?参数量如何控制? 标准FFN有 两个矩阵,参数量 。SwiGLU 有 三个矩阵,参数量 。为保持参数预算不变,LLaMA 将 从 调整为 (约 2.67 倍),使 。
QUESTION GELU 和 ReLU 有什么区别?
特性 ReLU GELU 公式 负值处理 硬截断为零 平滑趋近于零 梯度连续性 在 0 处不连续 处处连续可导 死神经元问题 严重(负值梯度恒零) 无(总有非零梯度) 直觉 硬性开关 软性缩放 GELU 可以理解为"更平滑的 ReLU",对 Transformer 训练更友好。
QUESTION GLU 门控机制的本质是什么? GLU 将输入分成两路:一路经 sigmoid/Swish 激活作为"门"(控制信息通过比例),另一路作为"值"(实际信息内容),两者逐元素相乘。直觉上类似于 LSTM 的门控,让模型学会动态过滤信息。实验表明门控带来的提升主要来自"动态选择性",而非单纯参数增加。
QUESTION FFN 占 Transformer 多少参数?为什么中间维度要远大于模型维度? FFN 约占总参数的 2/3。以 LLaMA-7B 为例:,FFN 参数 ,每层总参数约 。中间维度远大于模型维度是因为 FFN 充当"键值记忆": 的每一行是一个"键"(对应某种输入模式), 的每一列是一个"值"(对应某种输出),更大的中间维度意味着更多记忆槽位,可存储更多知识。
激活函数演进总结
ReLU → GELU → GLU → SwiGLU/GeGLU
(2017) (2018) (2017) (2020-至今)
ReLU: 简单但死神经元问题
GELU: 平滑版 ReLU,BERT/GPT-2 采用
GLU: 引入门控机制,信息选择性过滤
SwiGLU: Swish门控,当前最优,LLaMA/Mistral采用
GeGLU: GELU门控,PaLM采用,与SwiGLU性能接近
延伸阅读
-
Transformer — FFN 在整体架构中的位置
-
归一化与残差连接 — FFN 子层外部的包装结构
-
编码器与解码器 — 不同架构中 FFN 的差异
-
MoE基础 — MoE 是 FFN 的稀疏扩展
参考资料
- Shazeer, N. (2020). "GLU Variants Improve Transformer." arXiv:2002.05202 — GLU 变体对比的权威论文。
- Vaswani, A. et al. (2017). "Attention Is All You Need." — 原始 FFN 设计。
- Touvron, H. et al. (2023). "LLaMA: Open and Efficient Foundation Language Models." — SwiGLU FFN 的工程实践。
- Geva, M. et al. (2021). "Transformer Feed-Forward Layers Are Key-Value Memories." EMNLP 2021 — FFN 作为键值记忆的分析。