杰瑞科技汇

Python中activation函数如何选择与使用?

  1. 什么是激活函数? (核心概念)
  2. 为什么需要激活函数? (它的作用)
  3. 常用的激活函数及其特点 (重点介绍)
  4. 如何在 Python (PyTorch/TensorFlow) 中使用激活函数? (代码实践)
  5. 如何选择激活函数? (实践指南)

什么是激活函数?

激活函数是人工神经网络中神经元的一个核心组件,它的作用是接收神经元的输入(通常是上一层所有神经元的加权和),然后通过一个特定的数学函数进行非线性变换,最终产生神经元的输出。

Python中activation函数如何选择与使用?-图1
(图片来源网络,侵删)

可以把它想象成一个“决策开关”或“信号放大器”:

  • 输入z = (w₁x₁ + w₂x₂ + ... + wₙxₙ) + b (权重加权和 + 偏置)
  • 输出a = Activation(z) (经过激活函数处理后的最终输出)

这个输出 a 会作为下一层神经元的输入。

为什么需要激活函数?

这是初学者最常问的问题,也是理解深度学习的关键。

一句话总结:为了引入非线性,让神经网络能够学习复杂的模式。

Python中activation函数如何选择与使用?-图2
(图片来源网络,侵删)
  • 如果没有激活函数

    • 神经网络的每一层,无论有多少层,其计算都可以简化为 output = W * input + b
    • 多层叠加后,整个网络仍然只是一个线性变换 output = W_total * input + b_total
    • 这样的网络能力非常有限,本质上就是一个线性回归模型,连解决 XOR 这样的简单非线性问题都做不到。
  • 有了激活函数

    • 每一层引入了一个非线性变换,即使每一层都很简单,多层叠加后,网络的表达能力(或称“容量”)会呈指数级增长。
    • 这使得神经网络能够拟合任意复杂的函数,从而解决图像识别、自然语言处理等高度复杂的任务。

核心要点:激活函数为网络带来了“非线性”,这是深度学习有效性的基石。


常用的激活函数及其特点

下面是几种最主流的激活函数,我们来看它们的数学公式、图像和优缺点。

a) Sigmoid (S型函数)

  • 公式: σ(z) = 1 / (1 + e⁻ᶻ)
  • 输出范围: (0, 1)
  • 特点:
    • 优点: 输出在 (0, 1) 之间,可以很好地表示一个“概率”。
    • 缺点:
      1. 梯度消失: 当输入 z 极大或极小时(如 z > 5z < -5),函数曲线会变得非常平缓,导致梯度(导数)接近于0,在反向传播时,梯度会不断衰减,导致靠近输入层的权重几乎无法更新,网络难以训练。
      2. 输出非零中心化: 输出均值不是0,这可能会影响下一层梯度的更新,导致梯度下降的“之”字形路径,收敛速度变慢。
  • 应用场景: 现在已不常用作为隐藏层的激活函数,主要用于二分类问题的输出层,表示类别概率。

b) Tanh (双曲正切函数)

  • 公式: tanh(z) = (eᶻ - e⁻ᶻ) / (eᶻ + e⁻ᶻ)
  • 输出范围: (-1, 1)
  • 特点:
    • 优点: 输出范围是零中心的 (-1, 1),这通常比 Sigmoid 更好,因为下一层得到的输入均值为0,有助于梯度更新。
    • 缺点: 同样存在梯度消失问题,当输入 z 极大或极小时,梯度趋近于0。
  • 应用场景: 在某些循环神经网络中仍有使用,现在也较少作为首选的隐藏层激活函数。

c) ReLU (Rectified Linear Unit, 修正线性单元)

  • 公式: f(z) = max(0, z)
  • 输出范围: [0, +∞)
  • 特点:
    • 优点:
      1. 解决梯度消失: 对于正输入,梯度恒为1,不存在梯度消失问题,使得网络可以训练得更深。
      2. 计算简单: 公式简单,计算速度快,没有复杂的指数运算。
      3. 引入稀疏性: 当输入为负时,输出为0,使得网络中的一部分神经元“失活”,起到了类似稀疏自动编码器的作用,有助于减轻过拟合。
    • 缺点:
      1. Dying ReLU Problem: 如果一个神经元的权重更新导致其输入在任何样本下都为负,那么该神经元的梯度将永远为0,导致它再也无法被激活(“死亡”),学习率过高时容易发生。
  • 应用场景: 目前最主流、最常用的隐藏层激活函数,在绝大多数CNN和深度前馈网络中作为首选。

d) Leaky ReLU / PReLU (带泄漏的 ReLU)

  • 公式: f(z) = max(αz, z), 是一个很小的正数(如 0.01)。
  • 特点:
    • 优点: 为了解决 Dying ReLU 问题,当输入为负时,不再输出0,而是输出一个很小的线性值 αz,这样,即使输入为负,梯度也不为0,神经元就不会“死亡”。
    • 缺点: 需要额外调整超参数 (虽然实践中 α=0.01 通常效果不错)。
  • 应用场景: ReLU 的一个优秀替代品,当 ReLU 表现不佳(如神经元大量死亡)时可以尝试。

e) ELU (Exponential Linear Unit)

  • 公式: f(z) = z if z > 0 else α(eᶻ - 1)
  • 特点:
    • 优点: 结合了 ReLU 和 Leaky ReLU 的优点,同时其负值部分是平滑的,这有助于让神经元的激活均值更接近于0,有助于收敛。
    • 缺点: 计算比 ReLU 稍复杂,因为有指数运算。
  • 应用场景: ReLU 的又一个强力竞争者,在某些任务上表现优于 ReLU。

f) Softmax

  • 公式: σ(zᵢ) = eᶻⁱ / Σⱼ eᶻʲ
  • 输出范围: (0, 1),并且所有类别的输出之和为1。
  • 特点:
    • 作用: 它不是一个简单的逐元素激活函数,而是对一个向量进行操作,它会将一个实数向量转换为一个概率分布,每个元素代表对应类别的概率。
  • 应用场景: 多分类问题的输出层

如何在 Python 中使用激活函数?

在主流的深度学习框架中,激活函数通常作为层来使用,或者作为函数直接调用,下面以 PyTorch 和 TensorFlow 为例。

PyTorch 示例

在 PyTorch 中,激活函数通常在 torch.nn 模块中定义,作为 nn.Module 的子类。

import torch
import torch.nn as nn
# 创建一个模拟输入 (一个批次,一个样本,一个特征)
x = torch.tensor([-2.0, -1.0, 0.0, 1.0, 2.0])
# 1. 定义激活函数层
sigmoid_layer = nn.Sigmoid()
relu_layer = nn.ReLU()
leaky_relu_layer = nn.LeakyReLU(negative_slope=0.01) # negative_slope alpha
# 2. 前向传播
x_sigmoid = sigmoid_layer(x)
x_relu = relu_layer(x)
x_leaky_relu = leaky_relu_layer(x)
print("原始输入:", x)
print("Sigmoid 输出:", x_sigmoid)
print("ReLU 输出:", x_relu)
print("LeakyReLU 输出:", x_leaky_relu)
# 在网络模型中定义
class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.layer1 = nn.Linear(10, 5)  # 一个线性层
        self.activation = nn.ReLU()     # 激活层
    def forward(self, x):
        x = self.layer1(x)
        x = self.activation(x)
        return x
# 也可以使用函数式接口 (更简洁)
output = torch.relu(x)
print("函数式 ReLU 输出:", output)

TensorFlow/Keras 示例

在 Keras 中,使用起来非常方便,可以直接在 Dense 层中指定 activation 参数,或者单独使用激活层。

import tensorflow as tf
# 创建一个模拟输入 (一个批次,一个样本,一个特征)
x = tf.constant([-2.0, -1.0, 0.0, 1.0, 2.0])
# 1. 在 Keras 层中直接指定
# 注意:Keras 的 ReLU 默认没有负斜率,即 f(x)=max(0,x)
# 如果要带泄漏,使用 tf.keras.layers.LeakyReLU
model = tf.keras.Sequential([
    tf.keras.layers.Dense(1, activation='relu', input_shape=[1])
])
# 单独使用激活函数
x_sigmoid = tf.keras.activations.sigmoid(x)
x_relu = tf.keras.activations.relu(x)
x_leaky_relu = tf.keras.layers.LeakyReLU(alpha=0.01)(x)
print("原始输入:", x)
print("Sigmoid 输出:", x_sigmoid)
print("ReLU 输出:", x_relu)
print("LeakyReLU 输出:", x_leaky_relu)
# 在模型定义中
model = tf.keras.Sequential([
    tf.keras.layers.Dense(64, activation='relu', input_shape=(784,)),
    tf.keras.layers.Dense(64, activation='leaky_relu'), # 或者用 tf.keras.layers.LeakyReLU() 层
    tf.keras.layers.Dense(10, activation='softmax')    # 多分类输出层
])

如何选择激活函数?

这是一个经验性的问题,但也有一些公认的指导原则:

场景 推荐激活函数 原因
隐藏层 ReLU 首选,计算简单,有效缓解梯度消失,是目前最通用、效果最好的选择。
隐藏层 (ReLU 效果不佳时) Leaky ReLU / ELU 当使用 ReLU 出现大量“死亡”神经元时,可以尝试带泄漏的版本。
二分类输出层 Sigmoid 输出在 (0, 1) 之间,可以解释为“属于正类的概率”。
多分类输出层 Softmax 将输出转换为一个概率分布,所有类别概率之和为1,适合多分类任务。
回归问题输出层 Linear (无激活函数) 直接输出一个实数值,不需要进行非线性变换。
RNN/LSTM/GRU Tanh / Sigmoid 传统循环网络中常用,因为它们的输出范围有界,适合处理序列数据。

总结建议:

  • 对于初学者和大多数任务隐藏层用 ReLU,输出层根据任务类型选择 Sigmoid 或 Softmax,这是一个不会出错的起点。
  • 如果遇到训练困难:可以尝试用 Leaky ReLU 或 ELU 替换 ReLU。
  • 避免在隐藏层使用 Sigmoid:除非有非常特殊的需求,否则应尽量避免,因为梯度消失问题太严重了。
分享:
扫描分享到社交APP
上一篇
下一篇