深度学习之激活函数详解

摘要: 激活函数是深度神经网络中最核心的组件之一,它为网络引入非线性表达能力,使得堆叠多层神经元成为可能。本文系统梳理了深度学习中常用的激活函数,包括 Sigmoid、Tanh、ReLU、Leaky ReLU、ELU、SeLU、Swish、Mish 以及 Softmax 等,从数学公式、输出特性、梯度分析、优缺点对比四个维度展开深入剖析,并结合 NumPy 实现完整的代码示例与可视化对比,最后给出针对不同场景的激活函数选择指南,助力读者在实战中做出合理决策。

关键词: 激活函数、深度学习、ReLU、梯度消失、非线性


1. 激活函数的作用

1.1 为什么要非线性激活

神经网络之所以能够拟合任意复杂的非线性函数,核心原因在于激活函数的非线性。假设网络中有两层线性变换:

复制代码
y = W2 · (W1 · x + b1) + b2 = (W2 · W1) · x + (W2 · b1 + b2)

无论我们堆叠多少层线性变换,它们的嵌套结果仍然只是一个线性变换------整个网络等价于一个单层线性模型,根本无法捕捉变量之间的非线性关系。激活函数的出现打破了这个困局:每层线性变换之后,激活函数以非线性方式对结果进行"扭曲",使得网络的表示能力呈指数级增长。Universal Approximation Theorem(通用逼近定理)告诉我们,只要网络中包含足够多的隐藏单元,单层前馈网络即可逼近任意连续函数;而多层的组合则进一步大幅降低了所需的参数量和计算复杂度。

1.2 线性激活的问题

如果执意使用线性激活函数(即令激活值直接等于输入),会产生以下问题:

  1. 表达能力退化为线性:无论网络多深,信息流始终是线性的组合,无法拟合阶跃、曲线、周期性等非线性模式。

  2. 梯度在链式求导中快速衰减(浅层网络)或爆炸(权重过大):线性函数的导数是常数,连乘后数值极不稳定。

  3. 无法构建"特征交叉":深度学习的强大之处在于隐式地自动进行特征交叉组合,而线性激活完全丧失了这一能力。

因此,在隐藏层中使用非线性激活函数是深度学习的基本共识。


2. Sigmoid 函数

2.1 数学公式与输出范围

Sigmoid(亦称 Logistic 函数)的数学表达式为:

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

其输出值域为 (0, 1),曲线呈 S 形,关于点 (0, 0.5) 中心对称。

2.2 梯度特性

Sigmoid 的导数可以由自身表示,形式优雅:

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

这意味着在反向传播中,计算梯度只需用到当前层的激活值,无需额外存储。

2.3 核心优点

  • 平滑可导:函数在整个实数域上无穷阶可导,适合梯度下降。

  • 概率解释:输出天然位于 (0, 1),在二分类任务的输出层可以直接解释为"属于正类的概率"。

2.4 三大缺陷

缺陷 说明
梯度消失 当 $
计算开销大 涉及指数运算 e\^{-x},在硬件层面不如加减法和比较运算高效。
输出非零中心 输出始终为正数((0,1)),导致后一层的输入偏置同号。在梯度反向传播时,同号的梯度信号会让权重更新方向始终在第一、三象限摆动,减缓收敛速度。

2.5 代码实现

复制代码
import numpy as np
​
def sigmoid(x: np.ndarray) -> np.ndarray:
    """
    Sigmoid 激活函数
    公式: σ(x) = 1 / (1 + exp(-x))
    
    参数:
        x: 任意形状的输入数组
    
    返回:
        与输入形状相同的输出,值域 (0, 1)
    """
    # 限制输入范围以避免 exp 溢出
    x_clipped = np.clip(x, -500, 500)
    return 1.0 / (1.0 + np.exp(-x_clipped))
​
​
def sigmoid_derivative(x: np.ndarray) -> np.ndarray:
    """
    Sigmoid 函数的梯度
    公式: σ'(x) = σ(x) * (1 - σ(x))
    
    参数:
        x: 任意形状的输入数组
    
    返回:
        与输入形状相同的梯度数组
    """
    s = sigmoid(x)
    return s * (1.0 - s)
​
​
# ------------------- 示例 -------------------
if __name__ == "__main__":
    import matplotlib.pyplot as plt
​
    x = np.linspace(-10, 10, 400)
    y = sigmoid(x)
    dy = sigmoid_derivative(x)
​
    plt.figure(figsize=(8, 4))
    plt.plot(x, y, label="Sigmoid")
    plt.plot(x, dy, label="导数", linestyle="--")
    plt.axhline(0, color="gray", linewidth=0.5)
    plt.axvline(0, color="gray", linewidth=0.5)
    plt.title("Sigmoid 函数及其导数")
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.show()

3. Tanh 函数

3.1 数学公式与输出范围

Tanh(双曲正切)函数的表达式为:

\\tanh(x) = \\frac{e\^x - e\^{-x}}{e\^x + e\^{-x}}

其值域为 (-1, 1),零中心(zero-centered),关于原点对称。

数学关系tanh(x) = 2σ(2x) - 1,即 Tanh 本质上是 Sigmoid 的一个线性变换版本。

3.2 零中心的优势

由于输出范围对称分布在负侧和正侧,梯度的正负号可以完整保留前一层的信号方向。这使得 Tanh 在实际训练中通常比 Sigmoid 收敛更快,是早期 RNN 和 NLP 任务中的常用选择。

3.3 仍存在的梯度消失

Tanh 的导数为:

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

\|x\| 较大时,\\tanh(x) \\to \\pm 1,导数趋于 0。深层网络中同样面临梯度消失问题,只是缓解程度优于 Sigmoid。

3.4 代码实现

复制代码
import numpy as np
​
def tanh_func(x: np.ndarray) -> np.ndarray:
    """
    Tanh(双曲正切)激活函数
    公式: tanh(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))
    
    参数:
        x: 任意形状的输入数组
    
    返回:
        与输入形状相同的输出,值域 (-1, 1)
    """
    # 稳定计算:避免大值时 exp 溢出
    return np.tanh(x)
​
​
def tanh_derivative(x: np.ndarray) -> np.ndarray:
    """
    Tanh 函数的梯度
    公式: tanh'(x) = 1 - tanh²(x)
    
    参数:
        x: 任意形状的输入数组
    
    返回:
        与输入形状相同的梯度数组
    """
    return 1.0 - np.tanh(x) ** 2
​
​
# ------------------- 示例 -------------------
if __name__ == "__main__":
    import matplotlib.pyplot as plt
​
    x = np.linspace(-10, 10, 400)
    y = tanh_func(x)
    dy = tanh_derivative(x)
​
    plt.figure(figsize=(8, 4))
    plt.plot(x, y, label="Tanh")
    plt.plot(x, dy, label="导数", linestyle="--")
    plt.axhline(0, color="gray", linewidth=0.5)
    plt.axvline(0, color="gray", linewidth=0.5)
    plt.title("Tanh 函数及其导数")
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.show()

4. ReLU 函数

4.1 数学公式

ReLU(Rectified Linear Unit,线性整流单元)的表达式极为简洁:

f(x) = \\max(0, x) = \\begin{cases} 0 \& x \< 0 \\ x \& x \\ge 0 \\end{cases}

4.2 核心优势

优势 说明
计算极快 只需一次比较运算,无需指数操作,比 Sigmoid/Tanh 快数倍。
缓解梯度消失 正区间梯度恒为 1,反向传播时梯度稳定传递,深层网络也能正常训练。
稀疏激活性 负值输出为 0,使得部分神经元处于"关闭"状态,形成稀疏表示,具有一定正则化效果。

4.3 神经元死亡问题(Dying ReLU)

当大量输入持续为负时,对应的权重和偏置在反向传播中梯度始终为 0,参数永远无法更新,这些神经元永久"死亡"。在实际应用中,当学习率过大或权重初始化不当时,这一问题尤为突出。

4.4 代码实现

复制代码
import numpy as np
​
def relu(x: np.ndarray) -> np.ndarray:
    """
    ReLU(线性整流)激活函数
    公式: f(x) = max(0, x)
    
    参数:
        x: 任意形状的输入数组
    
    返回:
        与输入形状相同的输出,值域 [0, +∞)
    """
    return np.maximum(0, x)
​
​
def relu_derivative(x: np.ndarray) -> np.ndarray:
    """
    ReLU 函数的梯度
    公式: f'(x) = 1 if x >= 0 else 0
    
    参数:
        x: 任意形状的输入数组
    
    返回:
        与输入形状相同的梯度数组,元素为 0 或 1
    """
    return np.where(x >= 0, 1.0, 0.0)
​
​
# ------------------- 示例 -------------------
if __name__ == "__main__":
    import matplotlib.pyplot as plt
​
    x = np.linspace(-10, 10, 400)
    y = relu(x)
    dy = relu_derivative(x)
​
    plt.figure(figsize=(8, 4))
    plt.plot(x, y, label="ReLU")
    plt.plot(x, dy, label="导数", linestyle="--")
    plt.axhline(0, color="gray", linewidth=0.5)
    plt.axvline(0, color="gray", linewidth=0.5)
    plt.title("ReLU 函数及其导数")
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.show()

5. Leaky ReLU

5.1 数学公式

Leaky ReLU 在 ReLU 的负区间引入一个极小的斜率 \\alpha(通常取 0.01),避免神经元完全死亡:

f(x) = \\max(\\alpha x, x) = \\begin{cases} \\alpha x \& x \< 0 \\ x \& x \\ge 0 \\end{cases}

5.2 解决神经元死亡

由于负区间有了一个很小的非零梯度,反向传播时即使输入长期为负,权重仍有微小的更新机会,从根本上规避了"Dying ReLU"问题。

5.3 代码实现

复制代码
import numpy as np
​
def leaky_relu(x: np.ndarray, alpha: float = 0.01) -> np.ndarray:
    """
    Leaky ReLU 激活函数
    公式: f(x) = max(alpha * x, x),通常 alpha = 0.01
    
    参数:
        x: 任意形状的输入数组
        alpha: 负区间的斜率,默认 0.01
    
    返回:
        与输入形状相同的输出
    """
    return np.where(x >= 0, x, alpha * x)
​
​
def leaky_relu_derivative(x: np.ndarray, alpha: float = 0.01) -> np.ndarray:
    """
    Leaky ReLU 的梯度
    
    参数:
        x: 任意形状的输入数组
        alpha: 负区间的斜率,默认 0.01
    
    返回:
        与输入形状相同的梯度数组
    """
    return np.where(x >= 0, 1.0, alpha)

6. 其他常用激活函数

6.1 ELU(Exponential Linear Unit)

ELU 在负区间使用指数函数,输出接近零均值,同时在负区间有一定的"软饱和"特性,有助于网络学习更鲁棒的表示:

f(x) = \\begin{cases} \\alpha (e\^x - 1) \& x \< 0 \\ x \& x \\ge 0 \\end{cases}

复制代码
def elu(x: np.ndarray, alpha: float = 1.0) -> np.ndarray:
    return np.where(x >= 0, x, alpha * (np.exp(np.clip(x, -500, 500)) - 1))
​
def elu_derivative(x: np.ndarray, alpha: float = 1.0) -> np.ndarray:
    return np.where(x >= 0, 1.0, elu(x, alpha) + alpha)

6.2 SeLU(Scaled Exponential Linear Unit)

SeLU 由 Klambauer 等人在 2017 年提出,其自归一化特性使得网络在不使用 Batch Normalization 的情况下也能保持稳定的方差传播:

f(x) = \\lambda \\cdot \\text{ELU}(x), \\quad \\lambda \\approx 1.0507

复制代码
def selu(x: np.ndarray) -> np.ndarray:
    """SeLU 激活函数,lambda ≈ 1.0507"""
    ALPHA = 1.6732632423543772848170429916717
    LAMBDA = 1.0507009873554804934193349852946
    return LAMBDA * np.where(x >= 0, x, ALPHA * (np.exp(np.clip(x, -500, 500)) - 1))

6.3 Swish

Swish 由 Google Brain 在 2017 年提出,自门控机制(self-gated)使其具有平滑非单调特性,在许多任务上优于 ReLU:

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

复制代码
def swish(x: np.ndarray) -> np.ndarray:
    """
    Swish 激活函数
    公式: f(x) = x * sigmoid(x)
    """
    return x * sigmoid(x)
​
def swish_derivative(x: np.ndarray) -> np.ndarray:
    """
    Swish 的梯度(使用链式法则推导)
    f'(x) = sigmoid(x) + x * sigmoid(x) * (1 - sigmoid(x))
          = sigmoid(x) + x * sigmoid(x) * (1 - sigmoid(x))
    """
    s = sigmoid(x)
    return s + x * s * (1 - s)

6.4 Mish

Mish 由 Diganta Misra 在 2019 年提出,同样采用自门控设计,在 ImageNet 分类等任务上取得了优于 ReLU 和 Swish 的效果:

f(x) = x \\cdot \\tanh(\\ln(1 + e\^x))

复制代码
def mish(x: np.ndarray) -> np.ndarray:
    """
    Mish 激活函数
    公式: f(x) = x * tanh(softplus(x)),其中 softplus(x) = ln(1 + e^x)
    """
    return x * np.tanh(np.log1p(np.exp(np.clip(x, -500, 500))))

6.5 Softmax(含温度参数)

Softmax 是多分类任务输出层的标准激活函数,将任意实数向量转换为概率分布:

\[\\text{Softmax}(x)\]*i = \\frac{e\^{x_i}}{\\sum*{j=1}\^{N} e\^{x_j}}

温度参数(Temperature)是 Softmax 的重要扩展。引入温度 T 后:

\[\\text{Softmax}*T(x)\]*i = \\frac{e\^{x_i / T}}{\\sum_{j=1}\^{N} e\^{x_j / T}}

  • T \\to 0:输出趋近于 one-hot(最max的项概率趋近于 1),即"硬"决策。

  • T = 1:标准 Softmax。

  • T \\to +\\infty:输出趋近于均匀分布(完全随机)。

在蒸馏学习(Knowledge Distillation)和强化学习策略网络中,温度参数是控制探索与利用平衡的关键超参数。

复制代码
def softmax(x: np.ndarray, axis: int = -1, temperature: float = 1.0) -> np.ndarray:
    """
    Softmax 激活函数(支持温度参数)
    
    公式: softmax(x)_i = exp(x_i / T) / Σ_j exp(x_j / T)
    
    参数:
        x: 输入 logits,任意形状
        axis: 计算 Softmax 的轴(默认为最后一维)
        temperature: 温度参数 T,T 越大输出越平滑(趋近均匀)
    
    返回:
        与输入形状相同的概率分布,每行/列和为 1
    """
    # 减去最大值以提升数值稳定性(不改变 Softmax 结果)
    x_scaled = x / temperature
    x_max = np.max(x_scaled, axis=axis, keepdims=True)
    exp_x = np.exp(x_scaled - x_max)  # 数值稳定的写法
    return exp_x / np.sum(exp_x, axis=axis, keepdims=True)
​
​
def softmax_derivative(x: np.ndarray, temperature: float = 1.0) -> np.ndarray:
    """
    Softmax 的雅可比矩阵(用于理解梯度流动)
    对于第 i 个输出关于第 j 个输入:
    d(softmax_i) / d(x_j) = softmax_i * (δ_ij - softmax_j)
    
    注意:实际反向传播中通常直接使用 cross-entropy + softmax 的组合梯度,
    该梯度简化为 (softmax(x) - y),无需显式计算雅可比矩阵。
    """
    s = softmax(x, temperature=temperature)
    # 构造雅可比矩阵(仅作演示,对大维度矩阵开销较大)
    # jacobian[i, j] = s[i] * (δ[i,j] - s[j])
    I = np.eye(x.shape[-1])
    s_expanded = s[..., np.newaxis]
    jacobian = s_expanded * (I - s_expanded.swapaxes(-1, -2))
    return jacobian

7. 激活函数选择指南

7.1 按层类型选择

层级 推荐激活函数 原因
二分类输出层 Sigmoid 输出值域 (0,1),直接作为概率解释
多分类输出层 Softmax 输出归一化为概率分布,各类别互斥
回归输出层 恒等函数(无激活)/ ReLU(正值输出) 目标值无界时直接线性输出
隐藏层(默认首选) ReLU 计算高效、梯度稳定,是工业界事实标准
隐藏层(需要更高精度) Leaky ReLU / ELU 避免神经元死亡,输出更接近零中心
隐藏层(追求SOTA性能) Mish / Swish 自门控机制带来更强的非线性表达能力
自归一化网络 SeLU 配合 AlphaDropout 可无需 BatchNorm

7.2 按网络架构选择

  • MLP(全连接网络):隐藏层用 ReLU,输出层根据任务选择 Sigmoid/Softmax/恒等。

  • CNN(卷积网络):ReLU 系列为主(ReLU → Leaky ReLU → Mish 逐步升级)。

  • RNN/LSTM/GRU:Tanh 仍是主流(门控机制需要零中心输出),输出层可用 Sigmoid。

  • Transformer/Attention :GELU(近似实现为 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715*x^3))))已成为事实标准。

7.3 实战建议

  1. 从 ReLU 开始:大多数场景先用 ReLU 建立基线,简单高效。

  2. 警惕神经元死亡:训练 loss 不下降且梯度在浅层几乎为零时,换 Leaky ReLU 或 ELU。

  3. 学习率匹配 :ReLU 网络对学习率较敏感,建议配合 He 初始化(fan_in 方差)使用。

  4. BatchNorm + 激活函数的顺序 :标准实践是 Linear → BatchNorm → Activation,注意卷积网络中的 Channel 维度归一化。

  5. 不要在输出层使用 ReLU/Sigmoid/Softmax 之外的函数------输出层通常需要特定的数值范围或概率语义。


8. 实战代码:各激活函数对比可视化

以下代码在一张图上同时展示所有主流激活函数的曲线及其梯度,方便直观对比:

复制代码
import numpy as np
import matplotlib.pyplot as plt
​
# ============================================================
# 激活函数定义(汇总)
# ============================================================
def sigmoid(x):
    x = np.clip(x, -500, 500)
    return 1.0 / (1.0 + np.exp(-x))
​
def tanh_func(x):
    return np.tanh(x)
​
def relu(x):
    return np.maximum(0, x)
​
def leaky_relu(x, alpha=0.01):
    return np.where(x >= 0, x, alpha * x)
​
def elu(x, alpha=1.0):
    return np.where(x >= 0, x, alpha * (np.exp(np.clip(x, -500, 500)) - 1))
​
def selu(x):
    ALPHA = 1.6732632423543772848170429916717
    LAMBDA = 1.0507009873554804934193349852946
    return LAMBDA * np.where(x >= 0, x, ALPHA * (np.exp(np.clip(x, -500, 500)) - 1))
​
def swish(x):
    return x * sigmoid(x)
​
def mish(x):
    return x * np.tanh(np.log1p(np.exp(np.clip(x, -500, 500)))))
​
def gelu(x):
    """GELU(近似公式),Transformer 架构常用"""
    return 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * x**3)))
​
​
# ============================================================
# 梯度定义
# ============================================================
def get_gradients(activations, x):
    """利用数值微分计算各激活函数的梯度(用于绘图)"""
    eps = 1e-7
    return {name: (激活(x + eps) - 激活(x - eps)) / (2 * eps)
            for name, 激活 in activations.items()}
​
​
# ============================================================
# 绘图
# ============================================================
x = np.linspace(-4, 4, 500)
​
activations = {
    "Sigmoid": sigmoid,
    "Tanh": tanh_func,
    "ReLU": relu,
    "Leaky ReLU": leaky_relu,
    "ELU": elu,
    "SeLU": selu,
    "Swish": swish,
    "Mish": mish,
    "GELU": gelu,
}
​
fig, axes = plt.subplots(2, 1, figsize=(14, 10), sharex=True)
​
# 上图:激活函数曲线
ax1 = axes[0]
colors = plt.cm.tab10(np.linspace(0, 1, len(activations)))
for (name, func), color in zip(activations.items(), colors):
    ax1.plot(x, func(x), label=name, color=color, linewidth=2)
ax1.axhline(0, color="black", linewidth=0.8, linestyle="-")
ax1.axvline(0, color="black", linewidth=0.8, linestyle="-")
ax1.set_title("常用激活函数对比", fontsize=14)
ax1.set_ylabel("Activation Output", fontsize=11)
ax1.legend(loc="lower right", ncol=3, fontsize=9)
ax1.grid(True, alpha=0.3)
ax1.set_ylim(-2.5, 3)
​
# 下图:梯度曲线
ax2 = axes[1]
grads = get_gradients(activations, x)
for (name, _), color in zip(activations.items(), colors):
    ax2.plot(x, grads[name], label=name, color=color, linewidth=2)
ax2.axhline(0, color="black", linewidth=0.8, linestyle="-")
ax2.axvline(0, color="black", linewidth=0.8, linestyle="-")
ax2.set_title("激活函数梯度(导数)对比", fontsize=14)
ax2.set_xlabel("x", fontsize=11)
ax2.set_ylabel("Gradient (dy/dx)", fontsize=11)
ax2.legend(loc="upper right", ncol=3, fontsize=9)
ax2.grid(True, alpha=0.3)
ax2.set_ylim(-0.5, 1.5)
​
plt.tight_layout()
plt.savefig("activation_functions_comparison.png", dpi=150)
plt.show()
​
print("图表已保存至 activation_functions_comparison.png")

运行说明 :将上述代码保存为 .py 文件(如 plot_activations.py),确保已安装 numpymatplotlibpip install numpy matplotlib),直接运行即可生成对比图。


9. 总结

激活函数虽小,却是深度学习网络的"灵魂"所在。本文系统梳理了从经典 Sigmoid/Tanh 到现代 ReLU 家族以及 Swish/Mish 等自适应激活函数的数学原理与实现细节,并给出了按场景分类的选择建议。核心要点回顾:

  1. 隐藏层优先选 ReLU,快速建立基线,再根据 Dying ReLU 问题考虑 Leaky ReLU/ELU。

  2. 输出层根据任务选择:二分类用 Sigmoid,多分类用 Softmax,回归用恒等函数。

  3. 前沿网络(Transformer/GNN)推荐 GELU,追求更高精度可尝试 Mish/Swish。

  4. 数值稳定性不可忽视:所有涉及指数运算的激活函数(Sigmoid、ELU、Swish、Mish)都应做好输入裁剪,避免溢出。

  5. 激活函数 + BatchNorm 的顺序 应严格遵循 Linear → BatchNorm → Activation,否则效果会大打折扣。

掌握激活函数的原理与选型逻辑,是构建高效深度学习模型的必经之路。希望本文能为你提供一份完整、实用的参考指南。


相关推荐
程序员cxuan1 小时前
对姚顺宇的4小时访谈整理
人工智能
2601_951515951 小时前
护眼照明进入深度洗牌期:书客SUN2如何重塑健康光行业规则?
大数据·人工智能·书客护眼大路灯·爱眼护眼·护眼大路灯
Joseph Cooper1 小时前
生产级 AI Agent 评估体系:从 12 指标框架到持续评估闭环
人工智能·ai·agent·eval·harness
桂花很香,旭很美2 小时前
有不 delay 的 AI 项目吗?
人工智能·项目管理·agent
爱写代码的小朋友2 小时前
人工智能背景下深度学习在高中信息技术教育中的应用研究
人工智能·深度学习
knight_9___2 小时前
大模型project面试5
人工智能·python·深度学习·面试·agent·rag·mcp
东方佑2 小时前
OpenASH 85M 参数,不用 Softmax,也能通过中文考试
人工智能·深度学习
nujnewnehc2 小时前
第一次接触 agent 概念分享
人工智能·llm·agent
怪祝浙2 小时前
AI实战之地dify应用-nlp数据库查询agent助手的搭建与发布
人工智能