欢迎光临
我们一直在努力

一文看懂:深度学习激活函数

1. 为什么需要激活函数?

1.1 线性变换的局限性

假设我们有一个没有激活函数的神经网络,它实际上只是线性变换的复合:

输出 = Wn × (Wn-1 × (… × (W2 × (W1 × 输入)))) = W' × 输入

无论网络有多少层,没有激活函数的深度神经网络等价于单层线性模型!这意味着:

  • 无法学习非线性关系

  • 无法解决XOR等问题

  • 表达能力极其受限

1.2 激活函数引入非线性

直观理解:激活函数就是神经元的“开关”,决定神经元是否被“激活”(传递信息 -> 神经元的触发电位阈值)


2. 激活函数的核心作用

2.1 数学角度

激活函数 f(x)使网络能够拟合任意复杂的函数:

y = f(W \\cdot x + b)

如果没有 f,这就是简单的线性变换。

2.2 几何角度

激活函数

几何意义

超平面(直线)

Sigmoid

S形曲线

ReLU

折线

多个ReLU组合

可以逼近任意凸函数

2.3 神经科学角度

激活函数模拟了生物神经元“全有或全无”特性

  • 当输入超过阈值时,神经元“激活”

  • 否则保持“静默”

3. 常用激活函数详解

3.1 Sigmoid(S形函数)

数学公式
  • \\sigma(x) = \\frac{1}{1 + e^{-x}}

导数
  • \\sigma'(x) = \\sigma(x) \\cdot (1 - \\sigma(x))

优缺点分析

优点

  • 输出范围(0, 1), 表示概率方便

  • 光滑,可导

  • 适合二分类问题

  • 缺点

  • 梯度消失问题:x很大或很小时梯度接近于0

  • 输出非零去中心化(影响收敛速度)

  • 指数运算计算较慢

  • 适用场景
    • 二分类问题的输出层

    • 需要概率输出的场景

    • 不推荐用于深层网络的隐藏层

    3.2 Tanh(双曲正切函数)

    数学公式
    • \\tanh(x) = \\frac{e^x - e^{-x}}{e^x + e^{-x}}

    导数
    • \\tanh'(x) = 1 - \\tanh^2(x)

    优缺点

    优点

  • 输出范围(-1, 1),零中心化

  • 收敛速度比 Sigmoid 快

  • 缺点

  • 仍有梯度消失问题

  • 指数运算计算较慢

  • 适用场景
    • 适合循环神经网络(RNN、LSTM、GRU)

    • 适合隐藏层(比 Sigmoid 好)

    • 不适合输出层(除非需要(-1, 1)输出)

    3.3 ReLU(线性整流单元)

    数学公式
    • ReLU(x) = \\max(0, x) = \\begin{cases} x, & x > 0 \\\\ 0, & x \\leq 0 \\end{cases}

    导数
    • ReLU'(x) = \\begin{cases} 1, & x > 0 \\\\ 0, & x \\leq 0 \\end{cases}

    优缺点

    优点

  • 计算简单快速(无指数运算)

  • 解决梯度消失问题

  • 稀疏激活(50%神经元被抑制)

  • 缺点

  • 死神经元问题(ReLU死亡)

  • 输出非零中心化

  • 不可微(在x=0处)

  • 什么是“ReLU死亡”?

    当某个神经元的输入始终为负,其梯度始终为0,权重无法更新,该神经元“死亡”。

    原因:

    • 学习率过大

    • 负偏置初始化

    • 大量负输入数据

    适用场景
    • 首选的隐藏层激活函数

    • 卷积神经网络(CNN)

    • 深度前馈网络

    3.4 Softmax(归一化指数函数)

    数学公式
    • Softmax(x_i) = \\frac{e^{x_i}}{\\sum_{j=1}^{K} e^{x_j}}

    特点
    • 输出总和为1

    • 适合多分类问题的输出层

    • 每个输出可以理解为概率

    适用场景
    • 多分类问题的输出层

    • 注意力机制(Transformer)

    • 不用于隐藏层

    3.5 LeakyReLU(带泄露的ReLU)

    数学公式

    LeakyReLU(x) = \\begin{cases} x, & x > 0 \\\\ \\alpha \\cdot x, & x \\leq 0 \\end{cases}

    其中 \\alpha是很小的正数(0.01,0.1)

    导数

    LeakyReLU'(x) = \\begin{cases} 1, & x > 0 \\\\ \\alpha, & x \\leq 0 \\end{cases}

    优缺点分析

    优点

  • 解决ReLU死亡问题

  • 保持ReLU的优点

  • 缺点

  • \\alpha是超参数,需要调整(通过实验)

  • 负区间有少量梯度流出

  • 适用场景
    • 出现大量神经元死亡时

    • 需要 ReLU 但担心死亡问题时

    3.6 ELU(指数线性单元)

    数学公式
    • ELU(x) = \\begin{cases} x, & x > 0 \\\\ \\alpha \\cdot (e^x - 1), & x \\leq 0 \\end{cases}

    导数
    • ELU(x) = \\begin{cases} 1, & x > 0 \\\\ \\alpha \\cdot e^x, & x \\leq 0 \\end{cases}

    优缺点分析

    优点

  • 输出接近零中心化

  • 负区间有平滑梯度

  • 缺点

  • 指数运算,计算较慢

  • 超参数 \\alpha 需要通过实验调整

  • 适用场景
    • 需要零中心化输出的网络

    • 对精度要求较高的场景

    3.7 GELU(高斯误差线性单元)

    数学公式
    • GELU(x) = x \\cdot \\Phi(x)

    • 其中 \\Phi(x)是标准正态分布的累积分布函数(CDF)

    近似计算
    • GELU(x) \\approx 0.5 \\cdot x \\cdot (1 + \\tanh(\\sqrt{\\frac{2}{\\pi}} \\cdot (x + 0.044715 \\cdot x^3)))

    优缺点分析

    优点

  • 平滑且非零中心化

  • 性能优于 ReLU 和 LeakyReLU

  • Transformer模型的标准选择

  • 缺点

  • 计算复杂度高

  • 适用场景
    • Transformer架构(GPT, BERT, ViT等)

    • 最先进的自然语言处理模型

    3.7 Swish(自门控激活函数)

    数学公式
    • Swish(x) = x \\cdot \\sigma(x) = x \\cdot \\frac{1}{1 + e^{-x}}

    优缺点分析

    优点

  • 平滑、无界、非单调

  • 子门控机制

  • 缺点

  • 计算比ReLU稍慢

  • 适用场景
    • 需要更高精度的场景

    • 深度残差网络

    4. 激活函数选择场景

    4.1 根据网络类型选择

    网络类型

    隐藏层

    输出层

    CNN

    ReLU / LeakyReLU

    Sigmoid(二分类) / Softmax(多分类)

    MLP

    ReLU / GELU

    Sigmoid / Softmax

    RNN / LSTM

    Tanh / ReLU

    Tanh

    Transformer

    GELU

    4.2 根据问题类型选择
    问题类型 输出层激活函数
    二分类 Sigmoid
    多分类 Softmax
    回归 Linear(无激活函数)
    多标签分布 Sigmoid(每个标签独立)

    5. 代码补充

    import torch
    import torch.nn.functional as F
    import matplotlib.pyplot as plt
    import numpy as np

    # 设置中文字体(如果系统支持)
    plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
    plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题

    # 创建x值范围
    x = torch.linspace(-5, 5, 1000, requires_grad=True)

    def sigmoid(x):
    """Sigmoid激活函数"""
    return 1 / (1 + torch.exp(-x))

    def tanh(x):
    """Tanh激活函数"""
    return torch.tanh(x)

    def relu(x):
    """ReLU激活函数"""
    return F.relu(x)

    def leaky_relu(x, negative_slope=0.1):
    """LeakyReLU激活函数"""
    return F.leaky_relu(x, negative_slope=negative_slope)

    def elu(x, alpha=1.0):
    """ELU激活函数"""
    return F.elu(x, alpha=alpha)

    def gelu(x):
    """GELU激活函数(GPT/BERT使用)"""
    return F.gelu(x)

    def swish(x):
    """Swish激活函数(又称 SiLU)"""
    return x * sigmoid(x)

    def softmax(x, dim=0):
    """Softmax激活函数"""
    return F.softmax(x, dim=dim)

    def compute_derivative(func, x):
    """计算函数的导数"""
    y = func(x)
    # 计算梯度
    dy = torch.autograd.grad(y.sum(), x, create_graph=True)[0]
    return y.detach().numpy(), dy.detach().numpy()

    # 定义激活函数及其参数
    activations = [
    ('Sigmoid', sigmoid, {}),
    ('Tanh', tanh, {}),
    ('ReLU', relu, {}),
    ('LeakyReLU', leaky_relu, {'negative_slope': 0.1}),
    ('ELU', elu, {'alpha': 1.0}),
    ('GELU', gelu, {}),
    ('Swish', swish, {}),
    # ('Softmax', softmax, {}), # Softmax通常用于多分类输出层,这里单独处理
    ]

    # 创建画布
    fig, axes = plt.subplots(4, 4, figsize=(16, 12))
    fig.suptitle('深度学习常用激活函数及其导数', fontsize=18, fontweight='bold')
    plt.subplots_adjust(hspace=0.4, wspace=0.3)

    # 绘制每个激活函数
    for idx, (name, func, params) in enumerate(activations):
    row = idx // 2
    col = (idx % 2) * 2

    # 计算函数值和导数
    y, dy = compute_derivative(lambda x: func(x, **params), x)

    # 绘制函数图像
    ax_func = axes[row, col]
    ax_func.plot(x.detach().numpy(), y, 'b-', linewidth=2, label=f'{name}(x)')
    ax_func.axhline(y=0, color='k', linestyle='–', linewidth=0.5, alpha=0.5)
    ax_func.axvline(x=0, color='k', linestyle='–', linewidth=0.5, alpha=0.5)
    ax_func.set_title(f'{name} 激活函数', fontsize=12, fontweight='bold')
    ax_func.set_xlabel('x', fontsize=10)
    ax_func.set_ylabel('f(x)', fontsize=10)
    ax_func.grid(True, alpha=0.3)
    ax_func.legend(loc='upper left', fontsize=9)
    ax_func.set_xlim(-5, 5)

    # 绘制导数图像
    ax_deriv = axes[row, col + 1]
    ax_deriv.plot(x.detach().numpy(), dy, 'r-', linewidth=2, label=f"{name}'(x)")
    ax_deriv.axhline(y=0, color='k', linestyle='–', linewidth=0.5, alpha=0.5)
    ax_deriv.axvline(x=0, color='k', linestyle='–', linewidth=0.5, alpha=0.5)
    ax_deriv.set_title(f'{name} 导数', fontsize=12, fontweight='bold')
    ax_deriv.set_xlabel('x', fontsize=10)
    ax_deriv.set_ylabel("f'(x)", fontsize=10)
    ax_deriv.grid(True, alpha=0.3)
    ax_deriv.legend(loc='upper left', fontsize=9)
    ax_deriv.set_xlim(-5, 5)

    # 隐藏未使用的子图
    for i in range(4):
    for j in range(4):
    if i * 2 + j >= len(activations) * 2:
    axes[i, j].set_visible(False)

    plt.tight_layout()
    plt.savefig('activation_functions.png', dpi=300, bbox_inches='tight')
    print("激活函数图像已保存为 'activation_functions.png'")
    plt.show()

    # 单独绘制 Softmax
    fig2, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
    fig2.suptitle('Softmax 激活函数', fontsize=16, fontweight='bold')

    # 示例输入
    x_softmax = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
    y_softmax = softmax(x_softmax, dim=1)

    # 绘制原始值
    ax1.bar(['x1', 'x2', 'x3'], x_softmax[0].detach().numpy(), color='blue', alpha=0.7)
    ax1.set_title('原始输入值', fontweight='bold')
    ax1.set_ylabel('值')
    ax1.set_ylim(0, 4)
    ax1.grid(True, alpha=0.3, axis='y')

    # 绘制 Softmax 输出
    ax2.bar(['p1', 'p2', 'p3'], y_softmax[0].detach().numpy(), color='red', alpha=0.7)
    ax2.set_title('Softmax 输出(概率)', fontweight='bold')
    ax2.set_ylabel('概率')
    ax2.set_ylim(0, 1)
    ax2.grid(True, alpha=0.3, axis='y')
    ax2.text(0.02, 0.95, f'总和: {y_softmax[0].sum().item():.4f}',
    transform=ax2.transAxes, fontsize=10)

    plt.tight_layout()
    plt.savefig('softmax_function.png', dpi=300, bbox_inches='tight')
    print("Softmax 函数图像已保存为 'softmax_function.png'")
    plt.show()

    print("\\n" + "="*60)
    print("激活函数可视化完成!")
    print("="*60)
    print("\\n各激活函数特点总结:")
    print("- Sigmoid: 输出范围(0,1),适合二分类,容易梯度消失")
    print("- Tanh: 输出范围(-1,1),零中心化,适合隐藏层")
    print("- ReLU: 简单高效,解决梯度消失,但有死神经元问题")
    print("- LeakyReLU: ReLU改进,负区间有小斜率,缓解死神经元")
    print("- ELU: 负区间指数衰减,输出接近零,但计算较慢")
    print("- GELU: 平滑激活函数,GPT/BERT等Transformer模型使用")
    print("- Swish: 自门控激活,x*sigmoid(x),在某些任务表现优异")
    print("- Softmax: 多分类输出层,将输出转换为概率分布")

    6. 总结

    激活函数

    核心特点

    最佳场景

    Sigmoid

    (0, 1)输出,适合概率

    二分类输出层

    Tanh

    (-1, 1)输出,零中心化

    RNN / LSTM

    ReLU

    简单高效,解决梯度消失

    CNN隐藏层(首选)

    Softmax

    概率分布,总和为1

    多分类输出层

    LeakyReLU

    解决死神经元

    有死ReLU问题时

    ELU

    平滑,零中心化

    高精度场景

    GELU

    Transformer标准选择

    NLP / Transformer

    Swish

    自门控,性能优异

    深度残差网络

    赞(0)
    未经允许不得转载:171主机测评 » 一文看懂:深度学习激活函数
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址