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 在某些情况下也可以作为隐藏层的激活函数,但需注意梯度消失的问题。
通过合理选择激活函数,可以显著提高神经网络的性能和训练效率。希望本文能帮助你更好地理解和使用激活函数。