2025年6月16日

FFN 与激活函数

Transformer 中的 FFN(Position-wise Feed-Forward Network)是一个逐位置应用的两层全连接网络,负责在自注意力层的"横向"信息交互之后进行"纵向"的非线性特征变换;激活函数的选择(从 ReLU ...

知识库大模型基础原理llmmodeltransformeractivation

先说结论

Transformer 中的 FFN(Position-wise Feed-Forward Network)是一个逐位置应用的两层全连接网络,负责在自注意力层的"横向"信息交互之后进行"纵向"的非线性特征变换;激活函数的选择(从 ReLU 到 GELU 再到 SwiGLU/GeGLU)显著影响模型的语言建模质量。

为什么我会单独记这一篇

在 Transformer 的每一层中,Self-Attention 负责在 token 之间传递信息("横向"),但仅有线性变换是不够的——模型需要非线性能力来学习复杂的特征表示。FFN 就是提供这种非线性的组件。

具体来说,FFN 解决了以下问题:

  1. 非线性建模:Self-Attention 本质上是加权求和(线性操作),如果没有 FFN 中的非线性激活函数,多层堆叠的 Transformer 等价于一个大的线性变换,表达能力极为有限。
  2. 特征空间扩展:FFN 先将特征从 d_model 维升到 d_ff 维(通常 4 倍),在高维空间中进行非线性变换,再降回 d_model 维。这种"膨胀-压缩"结构为模型提供了额外的容量。
  3. 知识存储:研究表明,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 中,模型可以:

  1. 通过一个分支计算"哪些信息是有用的"(门)。
  2. 通过另一个分支计算"实际的信息内容"(值)。
  3. 通过逐元素相乘实现选择性传递。

这比单一的激活函数提供了更精细的控制粒度。实验表明,门控机制带来的性能提升主要来自其"动态选择性",而非单纯的参数增加。

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) 参数效率更高 需要仔细调整

容易踩的坑

  1. 使用 ReLU 在现代 LLM 中:性能显著低于 SwiGLU,没有充分理由不应使用。
  2. SwiGLU 的三个矩阵混淆:w_gate 和 w_up 的角色不同,搞混会导致模型不收敛。
  3. d_ff 不是 256 的倍数:GPU 张量核心(Tensor Core)对 256 的倍数维度效率最高,非对齐维度会显著降低计算效率。
  4. 偏置项处理不一致:现代 LLM 通常去掉偏置,但如果训练时和推理时的偏置设置不一致,会导致精度问题。
  5. MoE 路由不均衡:如果路由器总是选择少数专家,其他专家得不到训练,模型容量浪费。

工程落地时我会怎么做

  1. 新项目默认使用 SwiGLU(或 SiLU),这是经过充分验证的最佳选择。
  2. d_ff 设为 8/3 * d_model,向上取整到最近的 256 的倍数。
  3. 去掉偏置项(bias=False),简化计算且不影响性能。
  4. 如果使用 MoE,确保负载均衡损失(load balancing loss)正常工作。
  5. FFN 是 Transformer 中参数量最大的组件(占总参数的约 2/3),是量化和剪枝的主要目标。

如果要对外讲,可以怎么概括

建议结构:

  1. 写出标准 FFN 的公式,解释"升维-激活-降维"的结构。
  2. 解释 FFN 的角色:自注意力是横向信息流,FFN 是纵向特征变换。
  3. 从 ReLU -> GELU -> GLU -> SwiGLU 的演进脉络,每一步解决了什么问题。
  4. 讨论 FFN 作为键值记忆的视角(这是面试加分项)。
  5. 提及 MoE 是 FFN 的一种扩展。

常见面试追问:

  • 为什么 SwiGLU 比 GELU 好?(门控机制提供动态选择性)
  • FFN 的中间维度为什么是 4 倍?(提供足够的非线性容量和记忆槽位)
  • FFN 在 Transformer 中存储了什么?(键值记忆,底层语法、高层语义)

最后记几条

  1. FFN 是 Transformer 中参数量最大的组件,约占总参数的 2/3。
  2. SwiGLU 是当前最优激活函数,被 LLaMA、Mistral 等所有主流 LLM 采用。
  3. 门控机制的本质:让模型动态选择"哪些信息通过",比单一激活更精细。
  4. FFN 可以理解为键值记忆:W1 是键,W2 是值,激活函数是选择器。
  5. 三个权重矩阵: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 激活 xσ(x)x \cdot \sigma(x) 提供更平滑的梯度流动,且门控值可略大于 1,允许特征放大。Shazeer (2020) 的实验表明 SwiGLU 在相同参数预算下显著优于 ReLU、GELU。

QUESTION SwiGLU FFN 为什么有三个权重矩阵?参数量如何控制? 标准FFN有 W1,W2W_1, W_2 两个矩阵,参数量 2×dmodel×dff2 \times d_{model} \times d_{ff}。SwiGLU 有 Wgate,Wup,WdownW_{gate}, W_{up}, W_{down} 三个矩阵,参数量 3×dmodel×dff3 \times d_{model} \times d_{ff}。为保持参数预算不变,LLaMA 将 dffd_{ff}4dmodel4d_{model} 调整为 83dmodel\frac{8}{3}d_{model}(约 2.67 倍),使 3×83=8=2×43 \times \frac{8}{3} = 8 = 2 \times 4

QUESTION GELU 和 ReLU 有什么区别?

特性 ReLU GELU
公式 max(0,x)\max(0, x) xΦ(x)x \cdot \Phi(x)
负值处理 硬截断为零 平滑趋近于零
梯度连续性 在 0 处不连续 处处连续可导
死神经元问题 严重(负值梯度恒零) 无(总有非零梯度)
直觉 硬性开关 软性缩放

GELU 可以理解为"更平滑的 ReLU",对 Transformer 训练更友好。

QUESTION GLU 门控机制的本质是什么? GLU 将输入分成两路:一路经 sigmoid/Swish 激活作为"门"(控制信息通过比例),另一路作为"值"(实际信息内容),两者逐元素相乘。直觉上类似于 LSTM 的门控,让模型学会动态过滤信息。实验表明门控带来的提升主要来自"动态选择性",而非单纯参数增加。

QUESTION FFN 占 Transformer 多少参数?为什么中间维度要远大于模型维度? FFN 约占总参数的 2/3。以 LLaMA-7B 为例:dmodel=4096,dff=11008d_{model}=4096, d_{ff}=11008,FFN 参数 3×4096×11008135M3 \times 4096 \times 11008 \approx 135M,每层总参数约 200M200M。中间维度远大于模型维度是因为 FFN 充当"键值记忆":W1W_1 的每一行是一个"键"(对应某种输入模式),W2W_2 的每一列是一个"值"(对应某种输出),更大的中间维度意味着更多记忆槽位,可存储更多知识。

激活函数演进总结

ReLU → GELU → GLU → SwiGLU/GeGLU
(2017)  (2018)  (2017)  (2020-至今)

ReLU:  简单但死神经元问题
GELU:  平滑版 ReLU,BERT/GPT-2 采用
GLU:   引入门控机制,信息选择性过滤
SwiGLU: Swish门控,当前最优,LLaMA/Mistral采用
GeGLU:  GELU门控,PaLM采用,与SwiGLU性能接近

延伸阅读

参考资料

  • 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 作为键值记忆的分析。