Skip to content

激活函数

激活函数为神经网络引入非线性——没有它,不管堆叠多少层,整个网络等价于一个线性变换(矩阵连乘还是矩阵)。本页系统讲解从 Sigmoid 到现代 GELU/SiLU/Mish/SwiGLU 的激活函数谱系。前置阅读:梯度下降与优化器、损失函数。

对于两层无激活函数的全连接网络,设第一层权重为 W1∈Rh×dW_1 \in \mathbb{R}^{h \times d}、偏置 b1∈Rhb_1 \in \mathbb{R}^{h},第二层权重为 W2∈Rk×hW_2 \in \mathbb{R}^{k \times h}、偏置 b2∈Rkb_2 \in \mathbb{R}^{k},输入 x∈Rdx \in \mathbb{R}^{d}:

h=W1x+b1y=W2h+b2=W2(W1x+b1)+b2=(W2W1)x+(W2b1+b2)=W′x+b′\mathbf{h} = W_1 \mathbf{x} + \mathbf{b}_1 \\ \mathbf{y} = W_2 \mathbf{h} + \mathbf{b}_2 = W_2(W_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2 = (W_2 W_1)\mathbf{x} + (W_2 \mathbf{b}_1 + \mathbf{b}_2) = W' \mathbf{x} + \mathbf{b}'

其中 W′=W2W1∈Rk×dW' = W_2 W_1 \in \mathbb{R}^{k \times d},b′=W2b1+b2∈Rkb' = W_2 b_1 + b_2 \in \mathbb{R}^{k}。无论堆叠多少层,都可以合并成 y=W′x+b′y = W'x + b'。这意味着:

  • 参数空间没有变大(N 层的权重本质上只用一个矩阵就能表示)
  • 表达能力不增加(无论多少层,仍然只能表示线性映射)
  • 深度没有意义(无法利用层次化抽象来降低复杂度)

只要在层间插入任意非线性激活函数 σ\sigma(如 σ(x)=max⁡(0,x)\sigma(x) = \max(0, x)),上述合并就不再成立:

h=σ(W1x+b1)y=W2 σ(W1x+b1)+b2\mathbf{h} = \sigma(W_1 \mathbf{x} + \mathbf{b}_1) \\ \mathbf{y} = W_2 \, \sigma(W_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2

这就是激活函数在数学上的根本作用——阻断线性变换的可合并性,使网络深度真正发挥作用。

把激活函数想象成神经元的”触发开关”——它决定一个神经元在什么条件下”激活”(输出非零值)、以什么方式激活:

  • Sigmoid= 温和的开关。把任意实数压到 (0, 1) 区间,像”概率旋钮”。但输入稍大或稍小梯度就近乎为零——这就是梯度消失(Vanishing Gradient:反向传播——即从损失向输出层逐层求导回传梯度——过程中,激活函数导数过小导致梯度逐层指数衰减,深层网络几乎学不动)。
  • Tanh= 零中心的 Sigmoid。输出范围 (-1, 1),梯度比 Sigmoid 稍强(原点处导数为 1),但两端仍有消失问题。
  • ReLU= 硬开关。正数原样通过、负数直接归零——简单、快、梯度不会消失(正区间梯度恒为 1)。缺点是”死亡 ReLU”(Dying ReLU:一旦某个神经元持续输出负数,梯度永远为 0,参数再也不会更新,相当于这个神经元永久”死亡”)。
  • Leaky ReLU / PReLU= 给死亡 ReLU 留一条缝。负数不是归零而是乘一个小斜率(如 0.01),保证梯度不为零。PReLU 把斜率也设为可学习参数(反向传播时会自动调整)。
  • GELU= 平滑版 ReLU。在 0 附近用高斯函数做平滑过渡(而非硬切),BERT/GPT 等 Transformer 的标配。
  • SiLU / Swish= x * sigmoid(x)。Google 通过神经架构搜索(NAS:用机器学习自动搜索最优结构)发现的自适应激活函数,YOLOv8 等现代模型在用。
  • Mish= x * tanh(softplus(x))。比 Swish 更平滑一点,在部分检测模型上有微小提升。
  • SwiGLU= SiLU 门控的 GLU 变体。将 FFN 中的激活函数升级为门控机制(一条路径过 SiLU 激活,另一条路直通,两条逐元素相乘),LLaMA 2/3、PaLM、Mistral 等现代大语言模型的 FFN 标配。

一个不加激活函数的全连接层只是线性变换:y = Wx + b。即使堆叠 N 层:

y=WN⋯(W2(W1x+b1)+b2)⋯+bN=W′x+b′y = W_N \cdots (W_2(W_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2) \cdots + \mathbf{b}_N = W' \mathbf{x} + \mathbf{b}'

多次线性变换的复合仍然是线性变换,整个网络等价于一层。激活函数在层与层之间插入非线性,使网络能够拟合任意复杂的函数(万能逼近定理)。

σ(x)=11+e−x\sigma(x) = \frac{1}{1 + e^{-x}}

导数为 σ(x)(1−σ(x))\sigma(x)(1-\sigma(x)),最大值仅 0.250.25(在 x=0x=0 时),这意味着反向传播时每经过一层梯度至少缩水 75%。深层网络中梯度指数衰减,即”梯度消失”。此外输出恒为正(非零均值),会导致下一层权重的梯度恒同号,更新路径呈锯齿形。现代深层网络很少在隐藏层用 Sigmoid,主要用于二分类输出层(输出概率)。

曲线特征:Sigmoid 曲线呈 S 形(Sigmoid 意即 “S-shaped”),值域为 (0,1)(0, 1),在 x=0x=0 处输出 0.50.5。两端渐近线分别为 y=0y=0(xx 趋向负无穷)和 y=1y=1(xx 趋向正无穷),当输入绝对值大于 4 时曲线几乎水平——这正是梯度消失的直观来源。

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 200)
y = 1 / (1 + np.exp(-x)) # Sigmoid 函数
plt.figure(figsize=(8, 4))
plt.plot(x, y, linewidth=2, label='Sigmoid')
plt.axhline(0.5, color='gray', linestyle='--', alpha=0.5) # 标记 y=0.5
plt.axvline(0, color='gray', linestyle='--', alpha=0.5) # 标记 x=0
plt.xlabel('x')
plt.ylabel('σ(x)')
plt.title('Sigmoid 激活函数曲线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('sigmoid.png', dpi=150)
plt.show()

Sigmoid 激活函数曲线

tanh⁡(x)=ex−e−xex+e−x\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}

是零中心化的 Sigmoid(tanh⁡(x)=2σ(2x)−1\tanh(x) = 2\sigma(2x) - 1),导数最大值为 11(在 x=0x=0 时),比 Sigmoid 好,但两端仍有梯度消失问题。RNN / LSTM 中仍在用(如 LSTM 的门控)。

曲线特征:Tanh 曲线同样呈 S 形,但值域为 (-1, 1),在 x=0 处输出 0(零中心化)。两端渐近线分别为 y=-1 和 y=1,曲线通过原点且关于原点中心对称。与 Sigmoid 相比,Tanh 的斜率更陡、在原点处导数为 1(Sigmoid 仅 0.25),因此梯度传播效率更高。

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 200)
y = np.tanh(x) # Tanh 函数
plt.figure(figsize=(8, 4))
plt.plot(x, y, linewidth=2, color='orange', label='Tanh')
plt.axhline(0, color='gray', linestyle='--', alpha=0.5)
plt.axvline(0, color='gray', linestyle='--', alpha=0.5)
plt.xlabel('x')
plt.ylabel('tanh(x)')
plt.title('Tanh 激活函数曲线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('tanh.png', dpi=150)
plt.show()

Tanh 激活函数曲线

ReLU(x)=max⁡(0,x)\text{ReLU}(x) = \max(0, x)

正区间梯度恒为 1(梯度不消失!)、计算极快(只需一个 max 操作)。引入了网络的稀疏激活(负数输出 0),有正则化效果。缺点是”死亡 ReLU”问题:如果某个神经元的输入持续为负,梯度恒为 0,参数不再更新。此外 ReLU 不是零中心化(输出恒非负)。ReLU 是 CNN 训练的默认激活函数。

曲线特征:ReLU 曲线在 x 小于 0 时恒为 0(水平线),在 x 大于 0 时 y=x(斜率为 1 的直线),在原点处形成一个硬折角。值域为 [0, +∞)。这个硬折角虽不可导,但实践中用次梯度(subgradient)处理即可,不影响训练。

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 200)
y = np.maximum(0, x) # ReLU 函数
plt.figure(figsize=(8, 4))
plt.plot(x, y, linewidth=2, color='green', label='ReLU')
plt.axhline(0, color='gray', linestyle='--', alpha=0.5)
plt.axvline(0, color='gray', linestyle='--', alpha=0.5)
plt.xlabel('x')
plt.ylabel('ReLU(x)')
plt.title('ReLU 激活函数曲线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('relu.png', dpi=150)
plt.show()

ReLU 激活函数曲线

LeakyReLU(x)=max⁡(αx, x)PReLU(x)=max⁡(αx, x)(α 为可学习参数)\text{LeakyReLU}(x) = \max(\alpha x, \, x) \\ \text{PReLU}(x) = \max(\alpha x, \, x) \quad (\alpha \text{ 为可学习参数})

负区间引入一个小斜率 α,保证梯度非零,解决死亡 ReLU。PReLU(Parametric ReLU)把 α 也作为可学习参数,让网络自己决定负区间的斜率。在图像分类等任务上有微小提升。

曲线特征:Leaky ReLU 曲线在 x 大于 0 时与 ReLU 完全一致(y=x),在 x 小于 0 时不再水平归零,而是一条斜率为 α(如 0.01)的略微下倾的直线。值域为 (-∞, +∞)。负区间的微弱斜率让”死亡”神经元仍能保留非零梯度,从而有机会恢复更新。

import numpy as np
import matplotlib.pyplot as plt
alpha = 0.01
x = np.linspace(-5, 5, 200)
y = np.where(x >= 0, x, alpha * x) # Leaky ReLU 函数
plt.figure(figsize=(8, 4))
plt.plot(x, y, linewidth=2, color='red', label=f'Leaky ReLU (α={alpha})')
plt.axhline(0, color='gray', linestyle='--', alpha=0.5)
plt.axvline(0, color='gray', linestyle='--', alpha=0.5)
plt.xlabel('x')
plt.ylabel('LeakyReLU(x)')
plt.title('Leaky ReLU 激活函数曲线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('leaky_relu.png', dpi=150)
plt.show()

Leaky ReLU 激活函数曲线

GELU(x)=x⋅Φ(x)≈0.5x(1+tanh⁡ ⁣(2π (x+0.044715 x3)))\text{GELU}(x) = x \cdot \Phi(x) \\ \approx 0.5x \left(1 + \tanh\!\left(\sqrt{\frac{2}{\pi}}\,(x + 0.044715\, x^3)\right)\right)

直觉:x 越大,通过的概率越高(Φ(x) 越接近 1);x 越小(越负),被丢弃的概率越高。与 ReLU 的硬截断不同,GELU 在 0 附近做概率性的平滑过渡,保留了少量负值信息。GELU 是 BERT、GPT 系列等所有主流 Transformer 模型的默认激活函数。

曲线特征:GELU 曲线整体形状接近 ReLU,但在原点附近不是硬折角而是平滑的 S 形过渡。在 x 略小于 0 的区域(约 -0.17 附近)曲线有一个很浅的负值”凹陷”(最小值约为 -0.17),保留了少量负值信息。x 大于 1 时曲线迅速趋近于 y=x。处处可导,曲率平滑,这也是 Transformer 偏好它的原因之一。

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 200)
# GELU 的 tanh 近似公式(实践中最常用)
y = 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * x**3)))
plt.figure(figsize=(8, 4))
plt.plot(x, y, linewidth=2, color='blue', label='GELU')
plt.axhline(0, color='gray', linestyle='--', alpha=0.5)
plt.axvline(0, color='gray', linestyle='--', alpha=0.5)
plt.xlabel('x')
plt.ylabel('GELU(x)')
plt.title('GELU 激活函数曲线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('gelu.png', dpi=150)
plt.show()

GELU 激活函数曲线

SiLU(x)=x⋅σ(x)=x1+e−x\text{SiLU}(x) = x \cdot \sigma(x) = \frac{x}{1 + e^{-x}}

Google 通过神经网络搜索发现的自适应激活函数。在 x 大于 0 时接近 ReLU 的行为,但 x 小于 0 时不像 ReLU 那样硬截断到零,而是有一个小的负值”凹陷”。这种非单调性使其在某些任务上优于 ReLU。YOLOv8、EfficientNet 等现代模型大量使用。

曲线特征:SiLU 曲线在 x 大于 0 时与 ReLU 几乎重合(趋近 y=x),但在 x 小于 0 的区域不归零,而是在约 x=-1.28 处达到最小值(约 -0.28),形成一个明显的下凹”波谷”。函数是非单调的:先随 x 减小而下降到谷底,再随 x 继续减小而回升趋近于 0。这种非单调平滑特性被认为有助于信息流动。

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 200)
y = x / (1 + np.exp(-x)) # SiLU = x * sigmoid(x)
plt.figure(figsize=(8, 4))
plt.plot(x, y, linewidth=2, color='purple', label='SiLU (Swish)')
plt.axhline(0, color='gray', linestyle='--', alpha=0.5)
plt.axvline(0, color='gray', linestyle='--', alpha=0.5)
plt.xlabel('x')
plt.ylabel('SiLU(x)')
plt.title('SiLU / Swish 激活函数曲线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('silu.png', dpi=150)
plt.show()

SiLU / Swish 激活函数曲线

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

比 Swish 更平滑(处处可导且导数平滑),在 YOLOv4 等检测模型上报告了比 Swish 略好的性能。计算代价略高(需要 tanh + softplus),提升幅度通常很小。

曲线特征:Mish 曲线形状与 SiLU 非常相似——同样是非单调函数,x 大于 0 时趋近线性,x 小于 0 时有下凹波谷。但由于外层多套了一个 tanh(softplus(x)),波谷更浅(最小值约 -0.31,在 x≈-1.19 处),曲线整体比 SiLU 更”圆润”平滑,尤其在谷底和趋近零的尾部过渡更和缓。

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 200)
# Mish = x * tanh(ln(1 + e^x)) = x * tanh(softplus(x))
y = x * np.tanh(np.log1p(np.exp(x)))
plt.figure(figsize=(8, 4))
plt.plot(x, y, linewidth=2, color='teal', label='Mish')
plt.axhline(0, color='gray', linestyle='--', alpha=0.5)
plt.axvline(0, color='gray', linestyle='--', alpha=0.5)
plt.xlabel('x')
plt.ylabel('Mish(x)')
plt.title('Mish 激活函数曲线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('mish.png', dpi=150)
plt.show()

Mish 激活函数曲线

import torch
import torch.nn as nn
import torch.nn.functional as F
x = torch.randn(4, 8) # 模拟某层的输出
# 方式一:用 nn 模块(适合写在网络定义里)
acts = {
"ReLU": nn.ReLU(),
"LeakyReLU": nn.LeakyReLU(0.01),
"GELU": nn.GELU(),
"SiLU": nn.SiLU(),
"Sigmoid": nn.Sigmoid(),
"Tanh": nn.Tanh(),
"Mish": nn.Mish(),
}
for name, act in acts.items():
out = act(x)
print(f"{name:12s} 均值={out.mean():.3f} 范围=[{out.min():.3f}, {out.max():.3f}]")
# 方式二:函数式接口(适合临时调用)
y = F.gelu(x, approximate='tanh') # 用 tanh 近似加速 GELU
import numpy as np
x = np.linspace(-6, 6, 200)
# 各激活函数定义
sigmoid = 1 / (1 + np.exp(-x))
tanh = np.tanh(x)
relu = np.maximum(0, x)
gelu = 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * x**3)))
silu = x * (1 / (1 + np.exp(-x)))
# 打印 x=0 和 x=-2 处的值,对比行为
for name, y in [("Sigmoid", sigmoid), ("ReLU", relu), ("GELU", gelu), ("SiLU", silu)]:
idx0 = 100 # x≈0 的索引
print(f"{name:8s} f(0)={y[idx0]:.3f}")

所有激活函数曲线对比(一张图)

Section titled “所有激活函数曲线对比(一张图)”

把七个激活函数画在同一张图上,可以直观对比它们的形状差异——值域范围、负区间行为、平滑程度一目了然。

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 200)
# 计算所有激活函数的输出
sigmoid = 1 / (1 + np.exp(-x))
tanh = np.tanh(x)
relu = np.maximum(0, x)
leaky_relu = np.where(x >= 0, x, 0.01 * x)
gelu = 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * x**3)))
silu = x / (1 + np.exp(-x))
mish = x * np.tanh(np.log1p(np.exp(x)))
# 所有激活函数曲线对比
plt.figure(figsize=(10, 6))
plt.plot(x, sigmoid, label='Sigmoid')
plt.plot(x, tanh, label='Tanh')
plt.plot(x, relu, label='ReLU')
plt.plot(x, leaky_relu, label='Leaky ReLU')
plt.plot(x, gelu, label='GELU')
plt.plot(x, silu, label='SiLU')
plt.plot(x, mish, label='Mish')
plt.axhline(0, color='gray', linestyle='--', alpha=0.3)
plt.axvline(0, color='gray', linestyle='--', alpha=0.3)
plt.ylim(-1.5, 5) # 限制纵轴范围,让负区间的细节可见
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('激活函数曲线对比')
plt.legend(loc='upper left')
plt.grid(True, alpha=0.3)
plt.savefig('activation_functions.png', dpi=150)
plt.show()

所有激活函数曲线对比

从对比图可以看出几个关键差异:Sigmoid 和 Tanh 在两端趋于水平(梯度消失);ReLU 和 Leaky ReLU 在原点有明显的硬折角;GELU、SiLU、Mish 在负区间有平滑的下凹波谷且处处可导——这正是现代 Transformer 模型偏好它们的原因。

  • 隐藏层默认用 ReLU:CNN、MLP 的第一选择。简单、快、梯度不消失。如果效果不理想再试 GELU / SiLU。
  • Transformer 必须用 GELU(或 SiLU):BERT、GPT、LLaMA 等所有主流 Transformer 都用 GELU 或其变体。原始论文中 GELU 略优于 ReLU。
  • 输出层的激活函数由任务决定:二分类用 Sigmoid、多分类用 Softmax、回归用恒等(不激活)。
  • Sigmoid / Tanh 只在特殊位置用:LSTM 门控用 Sigmoid、注意力分数归一化用 Softmax。隐藏层不要用——梯度消失。
  • Leaky ReLU 的斜率 α 通常取 0.01:增大(如 0.1)不一定是好事。PReLU 让 α 可学习,在大规模图像分类上有微小优势。
  • 不要过度调激活函数:激活函数选择对最终性能的影响通常远小于学习率、正则化和数据质量。先用 ReLU 跑通基线,再考虑替换。
  • CNN 图像分类:ResNet / VGG 用 ReLU,EfficientNet 用 SiLU。详见CNN 卷积神经网络。
  • Transformer / 大语言模型:BERT / GPT / LLaMA 全部用 GELU 或 SiLU。详见Transformer 架构。
  • YOLO 系列目标检测:YOLOv5 用 Leaky ReLU,YOLOv8 用 SiLU。详见目标检测与 YOLO。
  • RNN / LSTM:门控用 Sigmoid(输出 0-1 的门控信号)、候选状态用 Tanh(输出 -1 到 1)。详见RNN 与序列模型。
  • 生成模型:GAN 的判别器用 Leaky ReLU、扩散模型的 U-Net 用 SiLU。详见扩散模型。
类库语言说明
torch.nnPythonPyTorch 激活函数模块:ReLU、GELU、SiLU、Mish、LeakyReLU、PReLU 等
torch.nn.functionalPython函数式接口,灵活插入任意位置
tf.keras.activationsPythonTensorFlow / Keras 激活函数模块
numpyPython手动实现激活函数曲线,便于理解和可视化
术语英文解释
激活函数Activation Function为神经网络引入非线性的函数,没有它多层网络等价于一层
梯度消失Vanishing Gradient激活函数导数过小,反向传播时梯度逐层衰减至接近零,深层网络无法训练
死亡 ReLUDying ReLUReLU 神经元持续接收负输入,梯度恒为零,参数永远不再更新
零中心化Zero-Centered输出均值约为零(如 Tanh),有助于梯度方向多样化,收敛更快
稀疏激活Sparse ActivationReLU 使部分神经元输出为零,产生稀疏表示,有一定正则化效果
GELUGaussian Error Linear Unit基于高斯分布的平滑 ReLU,Transformer 的标配激活函数
SiLU / SwishSiLU / Swishx * sigmoid(x),非单调平滑激活函数,用于 YOLOv8 / EfficientNet
PReLUParametric ReLULeaky ReLU 的参数化版本,负区间斜率可学习
  • Nair & Hinton,「Rectified Linear Units Improve Restricted Boltzmann Machines」(ICML 2010):ReLU 在深度学习中的开创性应用论文,如今 ReLU 已成为隐藏层的默认选择。
  • Hendrycks & Gimpel,「Gaussian Error Linear Units (GELU)」(2016):GELU 原始论文,提出用高斯累积分布函数替代 ReLU 的硬截断。
  • Ramachandran et al.,「Searching for Activation Functions」(2017):Swish/SiLU 的发现论文,Google 用自动搜索技术找到 x·sigmoid(x)。
  • Misra,「Mish: A Self Regularized Non-Monotonic Activation Function」(BMVC 2020):Mish 激活函数论文,在各基准上有微小提升。
  • Goodfellow et al.,「Deep Learning」第 6 章:系统讲解激活函数的数学性质和梯度行为。