PyTorch 教程:构建神经网络 3.3 常用激活函数

在构建神经网络时,激活函数是一个至关重要的组成部分。它们的主要作用是引入非线性,使得神经网络能够学习复杂的模式和特征。本文将详细介绍几种常用的激活函数,包括它们的优缺点、使用场景以及在 PyTorch 中的实现示例。

1. 激活函数的基本概念

激活函数的主要作用是决定一个神经元的输出。它接收输入信号(通常是前一层的输出),并通过某种非线性变换生成输出信号。激活函数的选择会直接影响到模型的性能和收敛速度。

2. 常用激活函数

2.1 Sigmoid 函数

定义

Sigmoid 函数的公式为: [ S(x) = \frac{1}{1 + e^{-x}} ]

优点

  • 输出范围在 (0, 1) 之间,适合用于二分类问题的输出层。
  • 函数光滑且可导,便于优化。

缺点

  • 当输入值较大或较小时,梯度接近于 0,导致梯度消失问题。
  • 输出不以零为中心,可能导致后续层的学习效率降低。

使用示例

import torch
import torch.nn as nn

# 定义一个简单的神经网络
class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        self.fc1 = nn.Linear(10, 5)
        self.sigmoid = nn.Sigmoid()
        self.fc2 = nn.Linear(5, 1)

    def forward(self, x):
        x = self.fc1(x)
        x = self.sigmoid(x)
        x = self.fc2(x)
        return x

# 创建模型并测试
model = SimpleNN()
input_data = torch.randn(1, 10)
output = model(input_data)
print(output)

2.2 Tanh 函数

定义

Tanh 函数的公式为: [ T(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} ]

优点

  • 输出范围在 (-1, 1) 之间,零中心化,有助于加速收敛。
  • 相比于 Sigmoid,Tanh 的梯度在输入值较大时仍然较大,减轻了梯度消失的问题。

缺点

  • 仍然存在梯度消失的问题,尤其是在深层网络中。

使用示例

class SimpleNN_Tanh(nn.Module):
    def __init__(self):
        super(SimpleNN_Tanh, self).__init__()
        self.fc1 = nn.Linear(10, 5)
        self.tanh = nn.Tanh()
        self.fc2 = nn.Linear(5, 1)

    def forward(self, x):
        x = self.fc1(x)
        x = self.tanh(x)
        x = self.fc2(x)
        return x

# 创建模型并测试
model_tanh = SimpleNN_Tanh()
output_tanh = model_tanh(input_data)
print(output_tanh)

2.3 ReLU(Rectified Linear Unit)

定义

ReLU 函数的公式为: [ R(x) = \max(0, x) ]

优点

  • 计算简单,收敛速度快。
  • 在正区间内,梯度恒为 1,减轻了梯度消失的问题。

缺点

  • 在负区间,梯度为 0,可能导致“死亡神经元”现象,即某些神经元在训练过程中永远不会被激活。

使用示例

class SimpleNN_ReLU(nn.Module):
    def __init__(self):
        super(SimpleNN_ReLU, self).__init__()
        self.fc1 = nn.Linear(10, 5)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(5, 1)

    def forward(self, x):
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        return x

# 创建模型并测试
model_relu = SimpleNN_ReLU()
output_relu = model_relu(input_data)
print(output_relu)

2.4 Leaky ReLU

定义

Leaky ReLU 函数的公式为: [ L(x) = \begin{cases} x & \text{if } x > 0 \ \alpha x & \text{if } x \leq 0 \end{cases} ] 其中,(\alpha) 是一个小的常数(通常取 0.01)。

优点

  • 解决了 ReLU 的“死亡神经元”问题,负区间仍然有小的梯度。
  • 计算简单,收敛速度快。

缺点

  • 选择合适的 (\alpha) 值可能需要一些经验。

使用示例

class SimpleNN_LeakyReLU(nn.Module):
    def __init__(self):
        super(SimpleNN_LeakyReLU, self).__init__()
        self.fc1 = nn.Linear(10, 5)
        self.leaky_relu = nn.LeakyReLU(0.01)
        self.fc2 = nn.Linear(5, 1)

    def forward(self, x):
        x = self.fc1(x)
        x = self.leaky_relu(x)
        x = self.fc2(x)
        return x

# 创建模型并测试
model_leaky_relu = SimpleNN_LeakyReLU()
output_leaky_relu = model_leaky_relu(input_data)
print(output_leaky_relu)

2.5 Softmax 函数

定义

Softmax 函数通常用于多分类问题的输出层,其公式为: [ S(x_i) = \frac{e^{x_i}}{\sum_{j} e^{x_j}} ]

优点

  • 将输出转换为概率分布,适合用于多分类问题。
  • 输出值在 (0, 1) 之间,且所有输出的和为 1。

缺点

  • 对于大输入值,可能会导致数值不稳定(溢出)。

使用示例

class SimpleNN_Softmax(nn.Module):
    def __init__(self):
        super(SimpleNN_Softmax, self).__init__()
        self.fc1 = nn.Linear(10, 5)
        self.fc2 = nn.Linear(5, 3)  # 假设有3个类别

    def forward(self, x):
        x = self.fc1(x)
        x = self.fc2(x)
        return nn.Softmax(dim=1)(x)

# 创建模型并测试
model_softmax = SimpleNN_Softmax()
output_softmax = model_softmax(input_data)
print(output_softmax)

3. 总结

在选择激活函数时,需根据具体问题和网络结构进行权衡。以下是一些选择激活函数的建议:

  • 对于隐藏层,ReLU 和其变种(如 Leaky ReLU)通常是首选。
  • 对于输出层,Sigmoid 适合二分类问题,Softmax 适合多分类问题。
  • Tanh 在某些情况下也可以作为隐藏层的激活函数,但需注意梯度消失的问题。

通过合理选择激活函数,可以显著提高神经网络的性能和训练效率。希望本文能帮助你更好地理解和使用激活函数。