03 - 激活函数详解¶
常用激活函数¶
图注:激活函数对比 - 展示了Sigmoid、Tanh、ReLU、Leaky ReLU、ELU和GELU六种常用激活函数的函数曲线
重要性: ⭐⭐⭐⭐⭐ 实用度: ⭐⭐⭐⭐⭐ 学习时间: 4小时 必须掌握: 是
为什么学这一章?¶
激活函数是神经网络的"灵魂",它赋予网络非线性表达能力。理解激活函数能帮助你: - 理解为什么神经网络能拟合任意复杂函数 - 选择合适的激活函数解决梯度消失/爆炸问题 - 调试训练过程中的异常行为 - 优化网络性能和收敛速度
学完这一章,你将能够: - ✅ 解释为什么需要非线性激活函数 - ✅ 掌握常用激活函数的特点和适用场景 - ✅ 理解激活函数的演化历史 - ✅ 深入分析Dead ReLU问题及其解决方案 - ✅ 理解梯度消失/爆炸问题及其解决方案 - ✅ 根据任务选择合适的激活函数 - ✅ 实现和可视化各种激活函数
📖 为什么需要激活函数?¶
线性变换的局限¶
┌─────────────────────────────────────────────────────────────────────┐
│ 没有激活函数的问题 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 假设我们有一个多层网络,但没有激活函数(只有线性变换): │
│ │
│ 第1层: h₁ = x · W₁ + b₁ │
│ 第2层: h₂ = h₁ · W₂ + b₂ │
│ 第3层: y = h₂ · W₃ + b₃ │
│ │
│ 展开看看: │
│ y = (x · W₁ + b₁) · W₂ + b₂) · W₃ + b₃ │
│ y = x · (W₁ · W₂ · W₃) + (b₁ · W₂ · W₃ + b₂ · W₃ + b₃) │
│ y = x · W' + b' │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 惊人发现:多层线性网络 = 单层线性网络! │ │
│ │ │ │
│ │ 无论多少层,最终都是: y = x · W + b │ │
│ │ 只能学习线性映射,无法解决非线性问题! │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 图示: │
│ │
│ 多层线性网络 等价于 单层网络 │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 输入 │───→│线性 │───→│线性 │───→ 输出 = │线性 │───→ 输出 │
│ └─────┘ │变换 │ │变换 │ │变换 │ │
│ └─────┘ └─────┘ └─────┘ │
│ │
│ 结论:没有非线性激活函数,深层网络毫无意义! │
│ │
└─────────────────────────────────────────────────────────────────────┘
激活函数的作用¶
┌─────────────────────────────────────────────────────────────────────┐
│ 激活函数的核心作用 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 激活函数 = 非线性变换 = 网络的表达能力 │
│ │
│ 加入激活函数后: │
│ │
│ 第1层: h₁ = f(x · W₁ + b₁) ← 非线性激活 │
│ 第2层: h₂ = f(h₁ · W₂ + b₂) ← 非线性激活 │
│ 第3层: y = h₂ · W₃ + b₃ │
│ │
│ 现在无法合并!每一层都在做非线性变换,网络可以学习复杂模式 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 万能逼近定理(Universal Approximation Theorem) │ │
│ │ │ │
│ │ 一个具有单隐藏层和非线性激活函数的前馈神经网络, │ │
│ │ 只要有足够多的隐藏单元,就可以以任意精度逼近任意连续函数! │ │
│ │ │ │
│ │ 这就是深度学习的理论基础! │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 图示对比: │
│ │
│ 线性网络(无激活) 非线性网络(有激活) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 只能拟合直线 │ │ 可以拟合任意曲线 │ │
│ │ ╱ │ │ ╭──╮ │ │
│ │ ╱ │ │ ╱ ╲ │ │
│ │ ╱ │ │ ╱ ╲ │ │
│ │ ╱ │ │ ╱ ╲ │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
🧬 激活函数演化史¶
图注:激活函数演化时间线 - 从1986年的Sigmoid/Tanh时代到2020年的多样化发展
从Sigmoid到ReLU的演进¶
┌─────────────────────────────────────────────────────────────────────┐
│ 激活函数演化时间线 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1986 ───┐ │
│ │ Sigmoid/Tanh 时代 │
│ │ • 反向传播算法提出 │
│ │ • Sigmoid作为默认选择 │
│ │ • 问题:梯度消失逐渐显现 │
│ ↓ │
│ 1998 ───┐ │
│ │ LeNet-5 使用 Sigmoid │
│ │ • 网络较浅(5层),梯度消失不严重 │
│ │ • 奠定了CNN基础 │
│ ↓ │
│ 2012 ───┐ │
│ │ AlexNet 使用 ReLU ⭐ 转折点 │
│ │ • 首次在大规模网络上使用ReLU │
│ │ • 训练速度提升6倍 │
│ │ • 缓解梯度消失问题 │
│ ↓ │
│ 2015 ───┐ │
│ │ ResNet + BatchNorm │
│ │ • 可以训练100+层网络 │
│ │ • ReLU成为隐藏层标准 │
│ ↓ │
│ 2017 ───┐ │
│ │ Transformer + GELU │
│ │ • BERT、GPT使用GELU │
│ │ • 平滑激活函数重新受到关注 │
│ ↓ │
│ 2020+ ───┐ │
│ │ 多样化发展 │
│ │ • Swish、Mish等新激活函数 │
│ │ • 根据任务和架构选择最优激活函数 │
│ │ • 神经架构搜索(NAS)探索激活函数组合 │
│ │
│ 关键洞察: │
│ • 激活函数选择直接影响网络可训练深度 │
│ • ReLU的出现使深度学习成为可能 │
│ • 不同架构(CNN、RNN、Transformer)有各自的最优选择 │
│ │
└─────────────────────────────────────────────────────────────────────┘
演化驱动力¶
┌─────────────────────────────────────────────────────────────────────┐
│ 激活函数演化的核心驱动力 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 梯度流动问题 │
│ ┌─────────────────────────────────────────┐ │
│ │ Sigmoid: 两端饱和,梯度消失 │ │
│ │ ↓ │ │
│ │ ReLU: 正区间梯度恒为1,缓解梯度消失 │ │
│ │ ↓ │ │
│ │ ResNet: 残差连接,梯度直接回传 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 2. 计算效率问题 │
│ ┌─────────────────────────────────────────┐ │
│ │ Sigmoid: 需要指数运算,计算昂贵 │ │
│ │ ↓ │ │
│ │ ReLU: 只需比较操作,计算极快 │ │
│ │ ↓ │ │
│ │ 硬件优化:ReLU易于GPU并行化 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 3. 网络深度增加 │
│ ┌─────────────────────────────────────────┐ │
│ │ 浅层网络(5层):Sigmoid尚可 │ │
│ │ ↓ │ │
│ │ 深层网络(50层):必须ReLU │ │
│ │ ↓ │ │
│ │ 超深网络(1000层):ReLU+残差连接 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 4. 任务特性需求 │
│ ┌─────────────────────────────────────────┐ │
│ │ CNN: ReLU(稀疏性有利于特征学习) │ │
│ │ ↓ │ │
│ │ RNN: Tanh(输出范围适合记忆单元) │ │
│ │ ↓ │ │
│ │ Transformer: GELU(平滑有利于优化) │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
🔧 常用激活函数详解¶
1. Sigmoid 函数¶
┌─────────────────────────────────────────────────────────────────────┐
│ Sigmoid 函数 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 数学公式: │
│ σ(x) = 1 / (1 + e^(-x)) │
│ │
│ 函数图像: │
│ │
│ σ(x) │
│ 1 │ ╭──────╮ │
│ │ ╱ ╲ │
│ 0.5 │──────╱ ╲──────── │
│ │ ╱ ╲ │
│ 0 │────╱ ╲────── │
│ └────────────────────────→ x │
│ -6 -3 0 3 6 │
│ │
│ 导数: │
│ σ'(x) = σ(x) · (1 - σ(x)) │
│ │
│ 导数图像: │
│ │
│ σ'(x) │
│ 0.25 │ ╭──╮ │
│ │ ╱ ╲ │
│ 0 │──────╱ ╲──────── │
│ └────────────────────────→ x │
│ -6 -3 0 3 6 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 特点: │ │
│ │ • 输出范围 (0, 1),可解释为概率 │ │
│ │ • 平滑可导,适合梯度下降 │ │
│ │ • 输出不是0中心化(都是正数) │ │
│ │ │ │
│ │ 问题:梯度消失(Vanishing Gradient) │ │
│ │ • 当 |x| > 5 时,σ(x) ≈ 0 或 1 │ │
│ │ • 此时 σ'(x) ≈ 0,梯度几乎为0 │ │
│ │ • 深层网络中梯度逐层衰减,前几层几乎学不到东西 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 适用场景: │
│ • 二分类问题的输出层(输出概率) │
│ • 浅层网络(1-2层) │
│ • 不推荐用于隐藏层! │
│ │
└─────────────────────────────────────────────────────────────────────┘
2. Tanh 函数¶
┌─────────────────────────────────────────────────────────────────────┐
│ Tanh 函数(双曲正切) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 数学公式: │
│ tanh(x) = (e^x - e^(-x)) / (e^x + e^(-x)) │
│ = 2 · σ(2x) - 1 │
│ │
│ 函数图像: │
│ │
│ tanh(x) │
│ 1 │ ╭──────╮ │
│ │ ╱ ╲ │
│ 0 │──────╱ ╲──────── │
│ │ ╱ ╲ │
│ -1 │────╱ ╲────── │
│ └────────────────────────→ x │
│ -6 -3 0 3 6 │
│ │
│ 导数: │
│ tanh'(x) = 1 - tanh²(x) │
│ │
│ 导数最大值:1(在x=0处) │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 特点: │ │
│ │ • 输出范围 (-1, 1),0中心化 │ │
│ │ • 相比Sigmoid,收敛更快(数据中心化有助于优化) │ │
│ │ • 仍然存在梯度消失问题 │ │
│ │ │ │
│ │ Sigmoid vs Tanh: │ │
│ │ • Sigmoid: 输出 (0, 1),用于概率 │ │
│ │ • Tanh: 输出 (-1, 1),用于特征(0中心化更好) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 适用场景: │
│ • 隐藏层(比Sigmoid更好) │
│ • RNN中的某些变体 │
│ • 不推荐用于深层网络(仍有梯度消失) │
│ │
└─────────────────────────────────────────────────────────────────────┘
3. ReLU 函数 ⭐ 最常用¶
┌─────────────────────────────────────────────────────────────────────┐
│ ReLU(Rectified Linear Unit) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 数学公式: │
│ ReLU(x) = max(0, x) │
│ │
│ 函数图像: │
│ │
│ ReLU(x) │
│ │ ╱ │
│ 5 │ ╱ │
│ │ ╱ │
│ 0 │─────╱──────────────── │
│ │ ╱ │
│ -5 │ ╱ │
│ └────────────────────────→ x │
│ -6 -3 0 3 6 │
│ │
│ 导数: │
│ ReLU'(x) = 1 if x > 0 │
│ 0 if x < 0 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 优点: │ │
│ │ • 计算简单(只需要比较和取max) │ │
│ │ • 缓解梯度消失(正区间梯度恒为1) │ │
│ │ • 收敛速度快(比Sigmoid/Tanh快6倍) │ │
│ │ • 引入稀疏性(负数输出为0,部分神经元不激活) │ │
│ │ │ │
│ │ 缺点:Dead ReLU问题 │ │
│ │ • 负数区域梯度为0,神经元可能"死亡" │ │
│ │ • 如果初始化不当或学习率太大,很多神经元可能永远不被激活 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 适用场景: │
│ • 隐藏层的默认选择 ⭐ │
│ • CNN、MLP的标准激活函数 │
│ • 深层网络的首选 │
│ │
└─────────────────────────────────────────────────────────────────────┘
4. Leaky ReLU¶
┌─────────────────────────────────────────────────────────────────────┐
│ Leaky ReLU │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 数学公式: │
│ LeakyReLU(x) = x if x > 0 │
│ α·x if x ≤ 0 (α 通常取 0.01) │
│ │
│ 函数图像: │
│ │
│ LeakyReLU(x) │
│ │ ╱ │
│ 5 │ ╱ │
│ │ ╱ │
│ 0 │─────╱──────────────── │
│ │ ╱ │
│ -0.1 │ ╱ ← 小的负斜率 │
│ └────────────────────────→ x │
│ -6 -3 0 3 6 │
│ │
│ 导数: │
│ LeakyReLU'(x) = 1 if x > 0 │
│ α if x < 0 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 改进:解决Dead ReLU问题 │ │
│ │ • 负数区域有小的梯度(α),神经元不会完全"死亡" │ │
│ │ • 保留了ReLU的优点(计算简单、收敛快) │ │
│ │ │ │
│ │ 变体: │ │
│ │ • PReLU: α 是可学习的参数 │ │
│ │ • RReLU: α 在训练中随机采样 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 适用场景: │
│ • 当发现很多Dead ReLU时使用 │
│ • 生成模型(GAN)中常用 │
│ │
└─────────────────────────────────────────────────────────────────────┘
5. ELU(Exponential Linear Unit)¶
┌─────────────────────────────────────────────────────────────────────┐
│ ELU │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 数学公式: │
│ ELU(x) = x if x > 0 │
│ α·(e^x - 1) if x ≤ 0 (α 通常取 1) │
│ │
│ 函数图像: │
│ │
│ ELU(x) │
│ │ ╱ │
│ 5 │ ╱ │
│ │ ╱ │
│ 0 │─────╱──────────────── │
│ -0.5 │ ╱╲ │
│ -1 │ ╱ ╲ ← 平滑的负值 │
│ └────────────────────────→ x │
│ -6 -3 0 3 6 │
│ │
│ 特点: │
│ • 负数区域平滑,均值更接近0 │
│ • 缓解Dead ReLU问题 │
│ • 计算比ReLU稍复杂(需要指数运算) │
│ │
│ 适用场景: │
│ • 需要更平滑的激活函数时 │
│ • 对收敛速度要求较高的场景 │
│ │
└─────────────────────────────────────────────────────────────────────┘
6. GELU(Gaussian Error Linear Unit)¶
┌─────────────────────────────────────────────────────────────────────┐
│ GELU ⭐ Transformer标配 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 数学公式: │
│ GELU(x) = x · Φ(x) │
│ 其中 Φ(x) 是标准正态分布的CDF │
│ │
│ 近似公式(Hendrycks & Gimpel, 2016): │
│ GELU(x) ≈ 0.5 · x · (1 + tanh[√(2/π) · (x + 0.044715 · x³)]) │
│ │
│ 函数图像: │
│ │
│ GELU(x) │
│ │ ╱ │
│ 5 │ ╱ │
│ │ ╱ │
│ 0 │─────╱──────────────── │
│ -0.5 │ ╱╲ │
│ │ ╱ ╲ ← 平滑曲线 │
│ └────────────────────────→ x │
│ -6 -3 0 3 6 │
│ │
│ 特点: │
│ • 平滑非单调(在负数区域有小的负值) │
│ • 在Transformer中表现优异(BERT、GPT都用它) │
│ • 有概率解释(随机dropout的期望) │
│ │
│ 适用场景: │
│ • Transformer模型的默认选择 │
│ • 自然语言处理任务 │
│ │
└─────────────────────────────────────────────────────────────────────┘
⚠️ 深入分析:Dead ReLU问题¶
图注:Dead ReLU问题可视化 - 左侧显示正常ReLU神经元,右侧显示Dead ReLU神经元(所有输入都是负数)
什么是Dead ReLU?¶
┌─────────────────────────────────────────────────────────────────────┐
│ Dead ReLU问题详解 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 问题描述: │
│ │
│ 当一个ReLU神经元在训练过程中始终接收到负输入时: │
│ • 输出始终为0 │
│ • 梯度始终为0(因为ReLU在负数区域导数为0) │
│ • 该神经元及其权重永远不会更新 │
│ • 这个神经元"死亡"了 │
│ │
│ 图示: │
│ │
│ 正常ReLU神经元 Dead ReLU神经元 │
│ │
│ 输入分布 输入分布 │
│ │ ╱╲ │ │
│ │ ╱ ╲ │ ╱╲ │
│ │ ╱ ╲ │ ╱ ╲ │
│ │ ╱ ╲ │ ╱ ╲ │
│ └──────────→ │ ╱ ╲ │
│ 0 └──────────→ │
│ 所有输入都是负数! │
│ 输出永远为0 │
│ │
│ 后果: │
│ • 网络表达能力下降(有效神经元减少) │
│ • 如果大量神经元死亡,模型性能严重下降 │
│ • 特别是在网络的前几层发生时影响更大 │
│ │
└─────────────────────────────────────────────────────────────────────┘
Dead ReLU的原因¶
┌─────────────────────────────────────────────────────────────────────┐
│ Dead ReLU的常见原因 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 权重初始化不当 │
│ ┌─────────────────────────────────────────┐ │
│ │ 初始化权重过大 → 加权和大 → 负数 → ReLU输出0 │ │
│ │ 例:He初始化使用不当 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 2. 学习率过大 │
│ ┌─────────────────────────────────────────┐ │
│ │ 大学习率 → 权重更新过大 → 跳到负区域 │ │
│ │ 一旦进入负区域,梯度为0,无法恢复 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 3. 数据预处理问题 │
│ ┌─────────────────────────────────────────┐ │
│ │ 输入数据没有归一化 │ │
│ │ 输入值过大或过小,导致加权和偏向负值 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 4. 网络设计问题 │
│ ┌─────────────────────────────────────────┐ │
│ │ 某一层神经元过多,竞争激烈 │ │
│ │ 部分神经元无法获得正输入 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 检测方法: │
│ • 统计每层死亡神经元的比例 │
│ • 监控激活值的分布 │
│ • 检查梯度是否在某些层为0 │
│ │
└─────────────────────────────────────────────────────────────────────┘
Dead ReLU的解决方案¶
┌─────────────────────────────────────────────────────────────────────┐
│ Dead ReLU的解决方案 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 方案1:使用Leaky ReLU / PReLU / ELU │
│ ┌─────────────────────────────────────────┐ │
│ │ 负数区域有小的梯度,神经元不会完全死亡 │ │
│ │ LeakyReLU: f(x) = max(αx, x), α=0.01 │ │
│ │ PReLU: α是可学习的参数 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 方案2:使用较小的学习率 │
│ ┌─────────────────────────────────────────┐ │
│ │ 避免权重更新过大跳到负区域 │ │
│ │ 配合学习率衰减策略 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 方案3:使用Batch Normalization │
│ ┌─────────────────────────────────────────┐ │
│ │ 归一化输入,使其分布更稳定 │ │
│ │ 减少进入负区域的概率 │ │
│ │ 现代深度网络的标准配置 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 方案4:更好的权重初始化 │
│ ┌─────────────────────────────────────────┐ │
│ │ He初始化(专为ReLU设计) │ │
│ │ W ~ N(0, √(2/n_in)) │ │
│ │ 保持前向传播的方差稳定 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 方案5:使用GELU或Swish │
│ ┌─────────────────────────────────────────┐ │
│ │ 平滑激活函数,负数区域也有梯度 │ │
│ │ Transformer中的成功应用 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 推荐组合: │
│ • 简单任务:ReLU + 好的初始化 │
│ • 深层网络:ReLU + BatchNorm + He初始化 │
│ • 生成模型:LeakyReLU │
│ • Transformer:GELU │
│ │
└─────────────────────────────────────────────────────────────────────┘
📊 激活函数对比总结¶
┌─────────────────────────────────────────────────────────────────────┐
│ 激活函数对比表 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 函数 │ 公式 │ 输出范围 │ 梯度消失 │ 计算成本 │ 推荐度 │
│ ──────────┼───────────────────┼───────────┼─────────┼─────────┼───────│
│ Sigmoid │ 1/(1+e^(-x)) │ (0, 1) │ 严重 │ 中 │ ⭐ │
│ Tanh │ (e^x-e^(-x))/(...)│ (-1, 1) │ 严重 │ 中 │ ⭐⭐ │
│ ReLU │ max(0, x) │ [0, +∞) │ 无 │ 低 │ ⭐⭐⭐⭐⭐│
│ LeakyReLU │ max(αx, x) │ (-∞, +∞) │ 无 │ 低 │ ⭐⭐⭐⭐ │
│ ELU │ x or α(e^x-1) │ (-α, +∞) │ 无 │ 中 │ ⭐⭐⭐ │
│ GELU │ x·Φ(x) │ (-∞, +∞) │ 无 │ 高 │ ⭐⭐⭐⭐ │
│ Softmax │ e^x/Σe^x │ (0, 1) │ - │ 中 │ 输出层 │
│ │
│ 推荐选择: │
│ • 隐藏层默认: ReLU │
│ • 深层网络: ReLU / LeakyReLU │
│ • Transformer: GELU │
│ • 二分类输出: Sigmoid │
│ • 多分类输出: Softmax │
│ │
└─────────────────────────────────────────────────────────────────────┘
⚠️ 梯度消失与梯度爆炸¶
图注:梯度消失与梯度爆炸可视化 - 左侧展示Sigmoid导致的梯度消失问题,右侧展示梯度爆炸问题
问题描述¶
┌─────────────────────────────────────────────────────────────────────┐
│ 梯度消失与梯度爆炸问题 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 深层网络中的梯度传播: │
│ │
│ 输入 → [层1] → [层2] → [层3] → ... → [层N] → 输出 │
│ ↑ ↑ ↑ ↑ │
│ 梯度 梯度 梯度 梯度 │
│ │
│ 梯度计算(链式法则): │
│ ∂L/∂W₁ = ∂L/∂Wₙ · ∂Wₙ/∂Wₙ₋₁ · ... · ∂W₂/∂W₁ │
│ │
│ 问题:多个小数相乘 → 梯度消失 │
│ 多个大数相乘 → 梯度爆炸 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 梯度消失(Vanishing Gradient) │ │
│ │ │ │
│ │ 现象:前面层的梯度几乎为0,参数不更新 │ │
│ │ 原因:Sigmoid/Tanh在饱和区梯度接近0 │ │
│ │ 解决: │ │
│ │ • 使用ReLU等激活函数 │ │
│ │ • 残差连接(ResNet) │ │
│ │ • 批归一化(BatchNorm) │ │
│ │ • 更好的初始化(He/Xavier) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 梯度爆炸(Exploding Gradient) │ │
│ │ │ │
│ │ 现象:梯度变得极大,参数更新失控(NaN) │ │
│ │ 原因:权重初始化过大,或学习率过大 │ │
│ │ 解决: │ │
│ │ • 梯度裁剪(Gradient Clipping) │ │
│ │ • 更小的学习率 │ │
│ │ • 权重正则化 │ │
│ │ • 批归一化 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
🧪 动手实验:激活函数可视化¶
实验代码¶
import numpy as np
import matplotlib.pyplot as plt
# 定义激活函数
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def tanh(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(x) - 1))
def gelu(x):
"""
GELU近似实现
精确公式:GELU(x) = x * Φ(x),其中Φ(x)是标准正态分布的CDF
近似公式(Hendrycks & Gimpel, 2016):
GELU(x) ≈ 0.5 * x * (1 + tanh[√(2/π) * (x + 0.044715 * x³)])
"""
return 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * x**3)))
# 生成输入
x = np.linspace(-5, 5, 1000)
# 创建图形
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
fig.suptitle('Activation Functions Comparison', fontsize=16)
# 绘制每个激活函数
functions = [
('Sigmoid', sigmoid(x)),
('Tanh', tanh(x)),
('ReLU', relu(x)),
('Leaky ReLU', leaky_relu(x)),
('ELU', elu(x)),
('GELU', gelu(x))
]
for ax, (name, y) in zip(axes.flat, functions): # zip按位置配对多个可迭代对象
ax.plot(x, y, linewidth=2)
ax.set_title(name, fontsize=12)
ax.set_xlabel('x')
ax.set_ylabel('f(x)')
ax.grid(True, alpha=0.3)
ax.axhline(y=0, color='k', linewidth=0.5)
ax.axvline(x=0, color='k', linewidth=0.5)
plt.tight_layout()
plt.savefig('activation_functions.png', dpi=150)
plt.show()
print("激活函数可视化完成!")
实验结果分析¶
运行代码后,你将看到: - Sigmoid/Tanh 的饱和特性 - ReLU 的简单高效 - Leaky ReLU/ELU/GELU 的负值处理
💡 核心要点总结¶
激活函数演化历史¶
| 时期 | 代表激活函数 | 特点 |
|---|---|---|
| 1986-2011 | Sigmoid/Tanh | 早期标准,但有梯度消失问题,网络难以做深 |
| 2012-2016 | ReLU | 深度学习转折点,计算简单,缓解梯度消失,网络可以做深 |
| 2017-2020 | GELU/Swish | Transformer时代,平滑激活函数,优化更稳定 |
| 2020+ | 多样化 | 根据任务和架构选择最优激活函数 |
激活函数选择指南¶
| 场景 | 推荐激活函数 | 原因 |
|---|---|---|
| 隐藏层(默认) | ReLU | 简单、快速、缓解梯度消失 |
| 深层网络 | ReLU/LeakyReLU | 避免梯度消失 |
| Transformer | GELU | 平滑、概率解释 |
| 生成模型(GAN) | LeakyReLU | 避免Dead ReLU |
| 二分类输出 | Sigmoid | 输出概率 (0,1) |
| 多分类输出 | Softmax | 输出概率分布 |
| 回归输出 | 线性(无激活) | 输出任意实数 |
Dead ReLU问题¶
- 原因:神经元始终接收负输入,梯度为0
- 检测:监控每层死亡神经元比例
- 解决:LeakyReLU、BatchNorm、小学习率、He初始化
关键洞察¶
- 非线性是必需的:没有激活函数,多层网络退化为单层
- ReLU是默认选择:简单高效,适合大多数场景
- 注意梯度问题:Sigmoid/Tanh在深层网络中容易导致梯度消失
- 没有完美的激活函数:根据任务特点选择
- 激活函数演化推动了深度学习发展:ReLU的出现使训练深层网络成为可能
❓ 常见问题¶
Q1:为什么ReLU比Sigmoid收敛快?
A: - ReLU计算简单(只需要max操作) - ReLU在正区间梯度恒为1,不会饱和 - Sigmoid在两端梯度接近0,导致梯度消失
Q2:什么是Dead ReLU?如何解决?
A: - Dead ReLU:神经元永远输出0(输入总是负数) - 解决: - 使用Leaky ReLU/ELU/GELU - 更小的学习率 - 更好的初始化(He初始化) - Batch Normalization
Q3:为什么Transformer用GELU而不是ReLU?
A: - GELU更平滑,有利于优化 - GELU有概率解释(dropout的期望) - 实验表明GELU在NLP任务上表现更好
Q4:可以同时使用不同的激活函数吗?
A:可以!不同层可以使用不同激活函数,例如: - 隐藏层:ReLU - 输出层:Softmax(分类)或线性(回归)
Q5:如何检测梯度消失/爆炸?
A: - 打印每层的梯度范数 - 观察损失曲线(不下降可能是梯度消失) - 检查参数更新量(太小→消失,太大→爆炸) - 使用梯度裁剪防止爆炸
Q6:激活函数的选择对模型性能影响有多大?
A: - 非常大!激活函数直接影响: - 网络能否训练(梯度流动) - 收敛速度 - 最终性能 - 在深层网络中,错误的激活函数选择可能导致完全无法训练
📝 自测问题¶
- 为什么神经网络需要非线性激活函数?
- Sigmoid和Tanh的主要区别是什么?为什么Tanh通常更好?
- ReLU的优点和缺点分别是什么?
- 什么是Dead ReLU问题?有哪些解决方案?
- 什么是梯度消失问题?哪些激活函数容易导致这个问题?
- 激活函数的选择如何影响网络的可训练深度?
- 为以下场景选择合适的激活函数:
- CNN的卷积层
- Transformer的FFN层
- 二分类问题的输出层
- 多分类问题的输出层
📚 扩展阅读¶
- 《Delving Deep into Rectifiers》 - He et al. (2015) - PReLU和He初始化
- 《Gaussian Error Linear Units》 - Hendrycks & Gimpel (2016) - GELU
- 《Fast and Accurate Deep Network Learning》 - Clevert et al. (2015) - ELU
- 《Searching for Activation Functions》 - Ramachandran et al. (2017) - Swish
🎯 下一步¶
继续学习 04-损失函数与优化,了解如何衡量模型性能并优化参数。



