跳到主要内容

主流激活函数原理与演进详解:从 Sigmoid 到 SwiGLU

· 阅读需 12 分钟
Zhiyuan Pan
Blog Author

从 Sigmoid/Tanh 到 ReLU 家族,再到 GELU/SiLU 和当前 LLM 标准 SwiGLU,全面梳理激活函数的演进脉络、数学原理与工程实现。

1. 为什么需要激活函数

没有激活函数,多层神经网络等价于单层线性变换:

$$f(x) = W_2(W_1 x + b_1) + b_2 = W'x + b'$$

无论堆多少层,都只能学习线性关系。激活函数引入非线性,让网络能拟合任意复杂函数。

2. 早期经典激活函数

2.1 Sigmoid

$$\sigma(x) = \frac{1}{1 + e^{-x}}$$

$$\sigma'(x) = \sigma(x)(1 - \sigma(x))$$

import torch
import torch.nn.functional as F

def sigmoid(x):
return 1.0 / (1.0 + torch.exp(-x))

# 导数
def sigmoid_grad(x):
s = sigmoid(x)
return s * (1 - s)
输出范围: (0, 1)
最大梯度: 0.25 (在 x=0 处)

1 ───────────────────────── ← 饱和区,梯度≈0
/
/ ← 梯度最大区域
/
0 ──/─────────────────────────────── ← 饱和区,梯度≈0
-6 -4 -2 0 2 4 6

致命问题

  1. 梯度饱和:$|x| > 4$ 时梯度趋近于 0 → 深层网络梯度消失
  2. 输出不以 0 为中心:输出恒正 → 后续层的梯度要么全正要么全负 → 更新效率低(zigzag 问题)
  3. 指数运算开销

现在的用途:二分类输出层、门控机制(LSTM 的门、GLU 的门)。

2.2 Tanh

$$\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} = 2\sigma(2x) - 1$$

$$\tanh'(x) = 1 - \tanh^2(x)$$

def tanh(x):
return torch.tanh(x)

# 等价于 sigmoid 的线性变换
def tanh_from_sigmoid(x):
return 2 * sigmoid(2 * x) - 1
输出范围: (-1, 1)       ← 以 0 为中心,解决了 sigmoid 的问题
最大梯度: 1.0 (在 x=0) ← 比 sigmoid 的 0.25 好很多

1 ─────────────────────



╱─────────────────────── -1

改进:输出以 0 为中心 → 梯度更新效率更高。 仍有问题:$|x| > 3$ 时梯度仍然饱和 → 深层网络依然梯度消失。 现在的用途:RNN/LSTM 中仍常用。

3. ReLU 家族

3.1 ReLU(Rectified Linear Unit)

$$\text{ReLU}(x) = \max(0, x) = \begin{cases} x & x > 0 \ 0 & x \leq 0 \end{cases}$$

$$\text{ReLU}'(x) = \begin{cases} 1 & x > 0 \ 0 & x \leq 0 \end{cases}$$

def relu(x):
return torch.maximum(x, torch.tensor(0.0))

# 等价
def relu_v2(x):
return x * (x > 0).float()


╱───────── 0
-4 -2 0 2 4

深度学习里程碑

  • 正区间梯度恒为 1 → 完全解决梯度消失(正区间)
  • 计算极简:一次比较 → 比 sigmoid/tanh 快 6~10 倍
  • 稀疏激活:约 50% 神经元输出 0 → 天然正则化

致命问题——死亡 ReLU

如果某个神经元的输入持续为负:
输出 = 0, 梯度 = 0 → 权重永远不更新 → 神经元"死掉"

场景: 学习率过大 → 权重更新跳到全负区域 → 永远回不来
实际: 训练后期可能有 10~40% 的神经元死亡

3.2 Leaky ReLU

$$\text{LeakyReLU}(x) = \begin{cases} x & x > 0 \ \alpha x & x \leq 0 \end{cases}, \quad \alpha = 0.01$$

def leaky_relu(x, alpha=0.01):
return torch.where(x > 0, x, alpha * x)



_╱───────── 负区间有微小斜率 (0.01x)

解决死亡 ReLU:负区间有梯度 $\alpha$ → 神经元不会完全死掉。 问题:$\alpha$ 是超参数,需要手动设定。

3.3 PReLU(Parametric ReLU)

$$\text{PReLU}(x) = \begin{cases} x & x > 0 \ \alpha_i x & x \leq 0 \end{cases}$$

# α 是可学习参数,每个通道一个
class PReLU(torch.nn.Module):
def __init__(self, num_channels):
super().__init__()
self.alpha = torch.nn.Parameter(torch.full((num_channels,), 0.25))

def forward(self, x):
return torch.where(x > 0, x, self.alpha * x)

改进:$\alpha$ 可学习 → 网络自己决定最佳负区间斜率。 代价:多了少量参数(每个通道一个 $\alpha$)。

3.4 ELU(Exponential Linear Unit)

$$\text{ELU}(x) = \begin{cases} x & x > 0 \ \alpha(e^x - 1) & x \leq 0 \end{cases}, \quad \alpha = 1.0$$

def elu(x, alpha=1.0):
return torch.where(x > 0, x, alpha * (torch.exp(x) - 1))



_╱~~~~~~~~~~ -α ← 负区间平滑趋近于 -α,不像 Leaky ReLU 无界下降

优势

  • 输出均值更接近 0 → 收敛更快
  • 负区间有饱和(趋近 $-\alpha$)→ 对噪声更鲁棒
  • 处处连续,x=0 处平滑

劣势:有指数运算 → 比 ReLU 慢

3.5 SELU(Scaled ELU)

$$\text{SELU}(x) = \lambda \begin{cases} x & x > 0 \ \alpha(e^x - 1) & x \leq 0 \end{cases}$$

$$\lambda \approx 1.0507, \quad \alpha \approx 1.6733$$

def selu(x):
alpha = 1.6732632423543772
lam = 1.0507009873554805
return lam * torch.where(x > 0, x, alpha * (torch.exp(x) - 1))

特殊性质——自归一化

精心选择的 $\lambda$ 和 $\alpha$ 使得在特定条件下:

  • 每层输出自动保持均值≈0、方差≈1
  • 不需要 BatchNorm/LayerNorm

条件很苛刻:需要用 LeCun 初始化 + AlphaDropout + 全连接网络。实际使用有限。

3.6 ReLU 家族对比

函数负区间梯度消失死亡问题计算开销
ReLU$0$正区间无最低
Leaky ReLU$0.01x$
PReLU$\alpha_i x$ (可学习)
ELU$\alpha(e^x-1)$
SELU$\lambda\alpha(e^x-1)$无(自归一化)

4. 现代主流激活函数

4.1 GELU(Gaussian Error Linear Unit)

Transformer 的默认选择:BERT、GPT、ViT 都用它。

$$\text{GELU}(x) = x \cdot \Phi(x)$$

其中 $\Phi(x) = P(X \leq x)$ 是标准正态分布的累积分布函数。

直觉:根据输入值的"大小"概率性地决定是否保留:

  • $x$ 很大 → $\Phi(x) \approx 1$ → 几乎完全保留
  • $x$ 很小 → $\Phi(x) \approx 0$ → 几乎完全抑制
  • $x$ 在 0 附近 → 平滑过渡
# 精确实现
def gelu_exact(x):
return x * 0.5 * (1.0 + torch.erf(x / math.sqrt(2.0)))

# 近似实现(更快,常用)
def gelu_approx(x):
return 0.5 * x * (1.0 + torch.tanh(
math.sqrt(2.0 / math.pi) * (x + 0.044715 * x ** 3)
))

# PyTorch 内置
y = F.gelu(x)

导数

$$\text{GELU}'(x) = \Phi(x) + x \cdot \phi(x)$$

其中 $\phi(x) = \frac{1}{\sqrt{2\pi}} e^{-x^2/2}$ 是标准正态的概率密度函数。

GELU vs ReLU 形状对比:

ReLU: ╱ GELU: ╱
╱ _╱
_____╱ ___~_╱
硬截断 平滑过渡 + 负区间有个小"坑"

为什么比 ReLU 好?

  • 平滑 → 优化更容易(梯度连续)
  • 非单调 → 负区间的"小坑"提供了正则化效果
  • 概率性门控 → 比硬阈值更灵活

4.2 SiLU / Swish

$$\text{SiLU}(x) = x \cdot \sigma(x) = \frac{x}{1 + e^{-x}}$$

def silu(x):
return x * torch.sigmoid(x)

# PyTorch 内置
y = F.silu(x)

导数

$$\text{SiLU}'(x) = \sigma(x) + x \cdot \sigma(x)(1 - \sigma(x)) = \sigma(x)(1 + x(1 - \sigma(x)))$$

def silu_grad(x):
s = torch.sigmoid(x)
return s * (1 + x * (1 - s))

Google 通过 NAS(神经架构搜索)发现:从搜索空间中自动找到 $x \cdot \sigma(\beta x)$,当 $\beta=1$ 时就是 SiLU。

形状与 GELU 极其接近,实践中效果差异极小。LLaMA、PaLM、Mistral 等用 SiLU。

4.3 GELU vs SiLU 对比

# 数值对比
x = torch.tensor([-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0])

gelu_vals = F.gelu(x) # [-0.045, -0.159, -0.154, 0.0, 0.346, 0.841, 1.955]
silu_vals = F.silu(x) # [-0.238, -0.269, -0.186, 0.0, 0.311, 0.731, 1.762]

# 差异很小,趋势一致:
# - 正区间: 接近恒等 (≈x)
# - 负区间: 有一个小"谷底"(非单调)
# - x=0: 都等于 0
特性GELUSiLU/Swish
公式$x \cdot \Phi(x)$$x \cdot \sigma(x)$
来源概率论推导NAS 自动搜索
典型使用BERT, GPT, ViTLLaMA, PaLM, Mistral
计算开销稍高(erf 函数)中(sigmoid)
负区间最小值≈ -0.17 (x≈-0.74)≈ -0.28 (x≈-1.28)

5. GLU 系列(Gated Linear Units)

5.1 GLU 的核心思想

不是单个激活函数,而是一种门控机制:把输入分成两半,一半通过激活函数做门控,另一半作为信息流,两者逐元素相乘。

$$\text{GLU}(x, W_1, W_2) = \sigma(xW_1) \odot (xW_2)$$

输入 x: (batch, hidden_size)

┌──────┴──────┐
↓ ↓
x @ W₁ x @ W₂
↓ ↓
激活函数 恒等(信息流)
↓ ↓
└──── ⊙ ──────┘ ← 逐元素相乘

输出: (batch, intermediate_size)

门控的直觉:$\sigma(xW_1)$ 的值在 0~1 之间,决定 $xW_2$ 的每个维度"放多少信息通过"。这比单个激活函数更灵活——模型可以学习在哪些维度上保留信息、在哪些维度上抑制。

5.2 SwiGLU — 当前 LLM 的标准配置

$$\text{SwiGLU}(x, W_1, W_2, W_3) = \text{SiLU}(xW_1) \odot (xW_3)$$

然后再经过一个 down projection:

$$\text{output} = \text{SwiGLU}(x) \cdot W_2$$

def swiglu_ffn(x, w1, w2, w3):
"""
LLaMA / DeepSeek / Mistral 的 FFN 层

x: (batch, hidden_size)
w1: (hidden_size, intermediate_size) gate_proj
w3: (hidden_size, intermediate_size) up_proj
w2: (intermediate_size, hidden_size) down_proj
"""
gate = F.silu(x @ w1) # (batch, intermediate_size) 门控
up = x @ w3 # (batch, intermediate_size) 信息流
x = gate * up # (batch, intermediate_size) 逐元素相乘
x = x @ w2 # (batch, hidden_size) 降维
return x

与标准 FFN 的对比

标准 FFN (BERT/GPT-2):           SwiGLU FFN (LLaMA/DeepSeek):
x → W₁ → GELU → W₂ → out x → W₁ → SiLU ─┐
x → W₃ ─────── ⊙ → W₂ → out
参数: 2 个矩阵 参数: 3 个矩阵(多 50%)
计算: 1 次激活 计算: 1 次激活 + 1 次乘法

代价:多了一个投影矩阵 $W_3$(参数量增加 50%)。 收益:PaLM 论文的系统对比显示,同等计算预算下 SwiGLU 效果显著优于 GELU FFN。

为什么要调小 intermediate_size?

标准 FFN:   intermediate = 4 × hidden     (如 4 × 4096 = 16384)
SwiGLU FFN: intermediate = 8/3 × hidden (如 8/3 × 4096 ≈ 10944)

因为 SwiGLU 多了一个矩阵,为了保持总参数量不变,需要缩小 intermediate_size。
8/3 ≈ 2.67,3 个矩阵 × 2.67 ≈ 8,约等于标准 FFN 的 2 个矩阵 × 4 = 8。

这也解释了 DeepSeek-V2-Lite 中 Dense 层 intermediate_size=109448/3 × 4096 的由来(实际 hidden=2048,10944 ≈ 8/3 × 2048 × 2,做了 TP 相关调整)。

5.3 GeGLU

$$\text{GeGLU}(x) = \text{GELU}(xW_1) \odot (xW_3)$$

def geglu(x, w1, w3):
return F.gelu(x @ w1) * (x @ w3)

用 GELU 做门控,效果与 SwiGLU 接近。Gemma 模型使用。

5.4 ReGLU

$$\text{ReGLU}(x) = \text{ReLU}(xW_1) \odot (xW_3)$$

def reglu(x, w1, w3):
return F.relu(x @ w1) * (x @ w3)

用 ReLU 做门控,最简单但效果略差。

5.5 GLU 变体对比

PaLM 论文的系统实验结果(同等计算量):

效果排序: SwiGLU ≈ GeGLU > ReGLU > 标准 GELU FFN > 标准 ReLU FFN
变体门控函数使用模型
SwiGLUSiLU/SwishLLaMA, DeepSeek, Mistral, Qwen
GeGLUGELUGemma
ReGLUReLU较少使用
原始 GLUSigmoid最初的 GLU 论文

5.6 vLLM 中的实现

# vllm/vllm/model_executor/layers/activation.py 中:

class SiluAndMul(nn.Module):
"""SwiGLU 的融合实现: SiLU(x[:half]) * x[half:]"""
def forward(self, x):
d = x.shape[-1] // 2
return F.silu(x[..., :d]) * x[..., d:]

class GeluAndMul(nn.Module):
"""GeGLU 的融合实现"""
def forward(self, x):
d = x.shape[-1] // 2
return F.gelu(x[..., :d]) * x[..., d:]

注意:vLLM 中 $W_1$ 和 $W_3$ 合并为一个 gate_up_proj,输出维度是 $2 \times \text{intermediate_size}$,然后 SiluAndMul 将前后两半分别做 SiLU 和直通,再相乘。

6. 特殊用途的激活函数

6.1 Softmax

$$\text{Softmax}(x_i) = \frac{e^{x_i}}{\sum_j e^{x_j}}$$

def softmax(x, dim=-1):
# 数值稳定版本:减去最大值防止溢出
x_max = x.max(dim=dim, keepdim=True).values
e_x = torch.exp(x - x_max)
return e_x / e_x.sum(dim=dim, keepdim=True)

不是逐元素函数,而是对整个向量归一化为概率分布。

用途:

  • Transformer Attention 中的 $\text{softmax}(QK^T/\sqrt{d})$
  • 分类输出层
  • MoE Router 的专家选择

6.2 Log-Softmax

$$\text{LogSoftmax}(x_i) = x_i - \log\sum_j e^{x_j}$$

def log_softmax(x, dim=-1):
return x - torch.logsumexp(x, dim=dim, keepdim=True)

与 NLLLoss 配合使用,比先 Softmax 再取 log 数值更稳定。

6.3 Mish

$$\text{Mish}(x) = x \cdot \tanh(\text{softplus}(x)) = x \cdot \tanh(\ln(1 + e^x))$$

def mish(x):
return x * torch.tanh(F.softplus(x))

与 SiLU/GELU 形状类似,在 YOLOv4 等 CV 模型中使用。

7. 激活函数演进时间线

1990s     Sigmoid, Tanh          ← 早期标准,梯度消失问题

2010 ReLU (AlexNet) ← 深度学习爆发的关键

2015 PReLU, ELU, SELU ← 修复死亡 ReLU

2016 GELU ← Transformer 时代开启

2017 Swish/SiLU (NAS) ← Google 自动搜索发现

2020 SwiGLU (PaLM) ← 当前 LLM 的事实标准

现在 SwiGLU 统治 LLM FFN
GELU 用于 Embedding/其他
Sigmoid 仅用于门控

8. LLM 中的激活函数使用

模型FFN 激活类型
BERTGELU标准 FFN
GPT-2GELU标准 FFN
GPT-3GELU标准 FFN
LLaMA 1/2/3SwiGLUGLU FFN
DeepSeek-V2/V3SwiGLUGLU FFN
Mistral/MixtralSwiGLUGLU FFN
Qwen 1.5/2/3SwiGLUGLU FFN
GemmaGeGLUGLU FFN
PaLMSwiGLUGLU FFN
T5ReLU → GeGLU (v1.1)标准/GLU FFN

结论:2023 年后的主流开源 LLM,FFN 层几乎全部采用 SwiGLU。

9. 完整代码:所有激活函数对比

import torch
import torch.nn.functional as F
import math

x = torch.linspace(-3, 3, 1000)

activations = {
"Sigmoid": torch.sigmoid(x),
"Tanh": torch.tanh(x),
"ReLU": F.relu(x),
"LeakyReLU": F.leaky_relu(x, 0.01),
"ELU": F.elu(x),
"GELU": F.gelu(x),
"SiLU/Swish": F.silu(x),
"Mish": x * torch.tanh(F.softplus(x)),
}

# 打印 x=[-2, -1, 0, 1, 2] 处的值
test_x = torch.tensor([-2.0, -1.0, 0.0, 1.0, 2.0])
print(f"{'函数':<15} {'x=-2':>8} {'x=-1':>8} {'x=0':>8} {'x=1':>8} {'x=2':>8}")
print("-" * 60)
for name, fn in [
("Sigmoid", torch.sigmoid),
("Tanh", torch.tanh),
("ReLU", F.relu),
("GELU", F.gelu),
("SiLU", F.silu),
]:
vals = fn(test_x)
print(f"{name:<15} {vals[0]:>8.4f} {vals[1]:>8.4f} {vals[2]:>8.4f} "
f"{vals[3]:>8.4f} {vals[4]:>8.4f}")

输出:

函数              x=-2     x=-1      x=0      x=1      x=2
------------------------------------------------------------
Sigmoid 0.1192 0.2689 0.5000 0.7311 0.8808
Tanh -0.9640 -0.7616 0.0000 0.7616 0.9640
ReLU 0.0000 0.0000 0.0000 1.0000 2.0000
GELU -0.0454 -0.1587 0.0000 0.8413 1.9545
SiLU -0.2384 -0.2689 0.0000 0.7311 1.7616

10. 一句话总结

Sigmoid/Tanh → ReLU(解决梯度消失)→ GELU/SiLU(平滑非单调)→ SwiGLU(门控机制,当前 LLM 标准)。核心趋势是从硬阈值走向平滑门控,从单一激活走向双路门控,SwiGLU 以多 50% 参数换来显著的效果提升,成为 2023 年后 LLM FFN 层的事实标准。