PyTorch 激活函数及非线性变换详解

激活函数是深度学习模型的重要组成部分,它们引入非线性,从而使模型能够更好地拟合复杂的数据模式。本文将详细介绍激活函数的作用、常见类型、经典应用示例,并比较它们的优缺点。

激活函数的作用

激活函数的主要作用是引入非线性变换,使神经网络能够拟合复杂的数据模式。为了理解这句话的含义,我们需要详细探讨以下几个方面:

  1. 线性变换的局限性
  2. 非线性变换的必要性
  3. 激活函数的作用
  4. 实际应用中的激活函数
1. 线性变换的局限性

线性变换是指一种保持向量加法和标量乘法的运算。在几何上,线性变换通常包括旋转、缩放、平移等操作。线性变换可以用矩阵乘法来表示。对于一个输入向量 x \mathbf{x} x 和一个线性变换矩阵 A \mathbf{A} A,线性变换的输出 y \mathbf{y} y 可以表示为:

y = A x + b \mathbf{y} = \mathbf{A} \mathbf{x} + \mathbf{b} y=Ax+b

其中, A \mathbf{A} A 是一个矩阵, b \mathbf{b} b 是一个偏置向量。

特性

  • 线性变换的输出是输入的线性组合。
  • 线性变换不会改变输入数据的线性关系。
  • 线性变换的图形表示通常是直线或平面。

示例

假设我们有一个向量 x = [ x 1 , x 2 ] T \mathbf{x} = [x_1, x_2]^T x=[x1,x2]T,一个线性变换矩阵 A = [ a 11 a 12 a 21 a 22 ] \mathbf{A} = \begin{bmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{bmatrix} A=[a11a21a12a22] 和一个偏置向量 b = [ b 1 , b 2 ] T \mathbf{b} = [b_1, b_2]^T b=[b1,b2]T,线性变换的结果是:

y = A x + b = [ a 11 a 12 a 21 a 22 ] [ x 1 x 2 ] + [ b 1 b 2 ] \mathbf{y} = \mathbf{A} \mathbf{x} + \mathbf{b} = \begin{bmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \end{bmatrix} + \begin{bmatrix} b_1 \\ b_2 \end{bmatrix} y=Ax+b=[a11a21a12a22][x1x2]+[b1b2]

2. 非线性变换的必要性

现实世界中的数据往往具有复杂的非线性关系。例如,图像中的物体识别、语音识别和自然语言处理等任务,数据的模式通常是高度非线性的。为了捕捉这些复杂的关系,我们需要引入非线性变换。

非线性变换是指一种不保持向量加法和标量乘法的运算。非线性变换可以引入复杂的关系,使得模型能够拟合复杂的数据模式。在神经网络中,非线性变换通常由激活函数实现。

特性

  • 非线性变换的输出不是输入的线性组合。
  • 非线性变换可以改变输入数据的线性关系,捕捉到更复杂的模式。
  • 非线性变换的图形表示通常是曲线或复杂的几何形状。

示例

假设我们有一个输入向量 x = [ x 1 , x 2 ] T \mathbf{x} = [x_1, x_2]^T x=[x1,x2]T 和一个非线性变换函数 f f f,非线性变换的结果是:

y = f ( x ) \mathbf{y} = f(\mathbf{x}) y=f(x)

如果 f f f 是 ReLU 激活函数,那么非线性变换可以表示为:

y = max ⁡ ( 0 , x ) \mathbf{y} = \max(0, \mathbf{x}) y=max(0,x)

3. 激活函数的作用

激活函数的主要作用是引入非线性变换,从而使神经网络能够拟合复杂的数据模式。激活函数在每个神经元的输出上进行非线性变换,使得整个网络能够学习和表示复杂的非线性关系。

在没有激活函数的情况下,神经网络的每一层都只是对上一层的线性变换。无论网络有多少层,这种线性组合的结果仍然是线性的。因此,没有激活函数的深度网络实际上等价于一个线性模型,无法有效地处理复杂的非线性关系。

通过在每一层之间引入激活函数,神经网络能够在每一层进行非线性变换,使得整个网络可以表示高度复杂的非线性函数。这种非线性变换赋予了神经网络强大的表达能力,使其能够拟合复杂的数据模式。

常见的激活函数
1. ReLU(Rectified Linear Unit)

数学表达式
ReLU ( x ) = max ⁡ ( 0 , x ) \text{ReLU}(x) = \max(0, x) ReLU(x)=max(0,x)

优点

  • 计算简单,速度快。
  • 缓解梯度消失问题,尤其在深层网络中表现良好。
  • 稀疏激活:大部分神经元的输出为零,有助于网络的稀疏性。

缺点

  • "神经元死亡"问题:在训练过程中,如果神经元的输出一直为零,那么这个神经元将永远不会激活。
  • 负值部分梯度为零,可能导致部分神经元无法更新。

适用场景

  • 广泛应用于各种深度学习模型,尤其是卷积神经网络(CNN)。

代码示例

python 复制代码
import torch
import torch.nn as nn

# 定义 ReLU 激活函数
relu = nn.ReLU()

# 示例输入张量
input_tensor = torch.tensor([-1.0, 0.0, 1.0, 2.0])

# 应用 ReLU 激活函数
output_tensor = relu(input_tensor)
print(output_tensor)
2. Sigmoid

数学表达式
Sigmoid ( x ) = 1 1 + e − x \text{Sigmoid}(x) = \frac{1}{1 + e^{-x}} Sigmoid(x)=1+e−x1

优点

  • 输出范围在 (0, 1) 之间,适用于输出概率的场景。
  • 平滑且连续,适用于某些需要概率输出的任务。

缺点

  • 容易导致梯度消失问题,尤其在深层网络中。
  • 输出不以零为中心,可能导致训练过程中的梯度不平衡。

适用场景

  • 适用于二分类问题的输出层。

代码示例

python 复制代码
import torch
import torch.nn as nn

# 定义 Sigmoid 激活函数
sigmoid = nn.Sigmoid()

# 示例输入张量
input_tensor = torch.tensor([-1.0, 0.0, 1.0, 2.0])

# 应用 Sigmoid 激活函数
output_tensor = sigmoid(input_tensor)
print(output_tensor)
3. Tanh(Hyperbolic Tangent)

数学表达式
Tanh ( x ) = e x − e − x e x + e − x \text{Tanh}(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} Tanh(x)=ex+e−xex−e−x

优点

  • 输出范围在 (-1, 1) 之间,输出以零为中心,有助于梯度的平衡。
  • 平滑且连续,能够捕捉到输入的细微变化。

缺点

  • 容易导致梯度消失问题,尤其在深层网络中。

适用场景

  • 适用于需要对称输出的场景,如生成对抗网络(GAN)中的生成器。

代码示例

python 复制代码
import torch
import torch.nn as nn

# 定义 Tanh 激活函数
tanh = nn.Tanh()

# 示例输入张量
input_tensor = torch.tensor([-1.0, 0.0, 1.0, 2.0])

# 应用 Tanh 激活函数
output_tensor = tanh(input_tensor)
print(output_tensor)
4. LeakyReLU

数学表达式
LeakyReLU ( x ) = { x if x ≥ 0 α x if x < 0 \text{LeakyReLU}(x) = \begin{cases} x & \text{if } x \geq 0 \\ \alpha x & \text{if } x < 0 \end{cases} LeakyReLU(x)={xαxif x≥0if x<0

其中, α \alpha α 是一个小的常数,通常取值为 0.01。

优点

  • 缓解了 ReLU 的"神经元死亡"问题。
  • 保持了 ReLU 的大部分优点,如计算简单和稀疏激活。

缺点

  • 需要额外的超参数 α \alpha α,可能需要进行调优。

适用场景

  • 适用于需要避免"神经元死亡"问题的场景。

代码示例

python 复制代码
import torch
import torch.nn as nn

# 定义 LeakyReLU 激活函数,负斜率为 0.01
leaky_relu = nn.LeakyReLU(negative_slope=0.01)

# 示例输入张量
input_tensor = torch.tensor([-1.0, 0.0, 1.0, 2.0])

# 应用 LeakyReLU 激活函数
output_tensor = leaky_relu(input_tensor)
print(output_tensor)
激活函数的比较
特性 ReLU Sigmoid Tanh LeakyReLU
数学表达式 max ⁡ ( 0 , x ) \max(0, x) max(0,x) 1 1 + e − x \frac{1}{1 + e^{-x}} 1+e−x1 e x − e − x e x + e − x \frac{e^x - e^{-x}}{e^x + e^{-x}} ex+e−xex−e−x { x if x ≥ 0 α x if x < 0 \begin{cases} x & \text{if } x \geq 0 \\ \alpha x & \text{if } x < 0 \end{cases} {xαxif x≥0if x<0
输出范围 [ 0 , ∞ ) [0, \infty) [0,∞) ( 0 , 1 ) (0, 1) (0,1) ( − 1 , 1 ) (-1, 1) (−1,1) ( − ∞ , ∞ ) (-\infty, \infty) (−∞,∞)
优点 计算简单,缓解梯度消失问题 输出范围固定,适合概率输出 输出以零为中心,梯度平衡 缓解"神经元死亡"问题
缺点 "神经元死亡"问题 梯度消失,输出不以零为中心 梯度消失 需要调优超参数 α \alpha α
适用场景 广泛应用于各种模型 二分类问题的输出层 对称输出的场景 避免"神经元死亡"的场景
激活函数的经典应用示例

激活函数在深度学习中的应用非常广泛。下面介绍几个经典的应用示例,包括卷积神经网络(CNN)、循环神经网络(RNN)、生成对抗网络(GAN)和全连接神经网络(FCNN)中的激活函数应用。

1. 卷积神经网络(CNN)

卷积神经网络广泛应用于图像分类、目标检测等任务中。ReLU 激活函数是 CNN 中最常用的激活函数。

示例:使用 ReLU 激活函数的简单 CNN

python 复制代码
import torch
import torch.nn as nn
import torch.nn.functional as F

class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1)
        self.fc1 = nn.Linear(in_features=64*7*7, out_features=128)
        self.fc2 = nn.Linear(in_features=128, out_features=10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2)
        x = x.view(-1, 64*7*7)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# 创建模型实例
model = SimpleCNN()
print(model)
2. 循环神经网络(RNN)

循环神经网络广泛应用于序列数据,如自然语言处理和时间序列预测。Tanh 和 Sigmoid 激活函数常用于 RNN 的隐藏层和输出层。

示例:使用 Tanh 和 Sigmoid 激活函数的简单 RNN

python 复制代码
import torch
import torch.nn as nn

class SimpleRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleRNN, self).__init__()
        self.hidden_size = hidden_size
        self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
        self.i2o = nn.Linear(input_size + hidden_size, output_size)
        self.sigmoid = nn.Sigmoid()
        self.tanh = nn.Tanh()

    def forward(self, input, hidden):
        combined = torch.cat((input, hidden), 1)
        hidden = self.tanh(self.i2h(combined))
        output = self.sigmoid(self.i2o(combined))
        return output, hidden

    def init_hidden(self):
        return torch.zeros(1, self.hidden_size)

# 创建模型实例
input_size = 10
hidden_size = 20
output_size = 1
model = SimpleRNN(input_size, hidden_size, output_size)
print(model)
3. 生成对抗网络(GAN)

生成对抗网络由生成器和判别器组成,用于生成高质量的图像。生成器通常使用 Tanh 激活函数,而判别器使用 LeakyReLU 激活函数。

示例:使用 Tanh 和 LeakyReLU 激活函数的简单 GAN

python 复制代码
import torch
import torch.nn as nn

class Generator(nn.Module):
    def __init__(self, input_size, output_size):
        super(Generator, self).__init__()
        self.fc1 = nn.Linear(input_size, 128)
        self.fc2 = nn.Linear(128, 256)
        self.fc3 = nn.Linear(256, output_size)
        self.tanh = nn.Tanh()

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

class Discriminator(nn.Module):
    def __init__(self, input_size):
        super(Discriminator, self).__init__()
        self.fc1 = nn.Linear(input_size, 256)
        self.fc2 = nn.Linear(256, 128)
        self.fc3 = nn.Linear(128, 1)
        self.leaky_relu = nn.LeakyReLU(0.2)
        self.sigmoid = nn.Sigmoid()

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

# 创建生成器和判别器实例
input_size = 100
output_size = 28*28
generator = Generator(input_size, output_size)
discriminator = Discriminator(output_size)
print(generator)
print(discriminator)
4. 全连接神经网络(FCNN)

全连接神经网络广泛应用于各种分类和回归任务中。不同的激活函数可以在不同的层中使用,具体取决于任务的需求。

示例:使用 ReLU 和 Sigmoid 激活函数的简单 FCNN

python 复制代码
import torch
import torch.nn as nn

class SimpleFCNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleFCNN, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.fc2 = nn.Linear(hidden_size, hidden_size)
        self.fc3 = nn.Linear(hidden_size, output_size)
        self.relu = nn.ReLU()
        self.sigmoid = nn.Sigmoid()

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

# 创建模型实例
input_size = 784  # 28x28 图像展平后的尺寸
hidden_size = 128
output_size = 10  # 10 个类别
model = SimpleFCNN(input_size, hidden_size, output_size)
print(model)

线性变换与非线性变换在神经网络中的应用

线性变换在神经网络中的应用

线性变换在神经网络中的应用主要体现在每一层的加权求和操作。对于一个输入向量 x \mathbf{x} x 和权重矩阵 W \mathbf{W} W,线性变换的输出 z \mathbf{z} z 可以表示为:

z = W x + b \mathbf{z} = \mathbf{W} \mathbf{x} + \mathbf{b} z=Wx+b

非线性变换在神经网络中的应用

非线性变换在神经网络中的应用主要体现在激活函数的使用。激活函数对每一个神经元的输出进行非线性变换,使得整个网络能够表示复杂的非线性关系。

示例

假设我们有一个简单的前馈神经网络,包含一个输入层、一个隐藏层和一个输出层。隐藏层的输出 h \mathbf{h} h 可以表示为:

h = f ( W 1 x + b 1 ) \mathbf{h} = f(\mathbf{W}_1 \mathbf{x} + \mathbf{b}_1) h=f(W1x+b1)

其中, W 1 \mathbf{W}_1 W1 是输入层到隐藏层的权重矩阵, b 1 \mathbf{b}_1 b1 是偏置向量, f f f 是激活函数(如 ReLU)。

输出层的输出 y \mathbf{y} y 可以表示为:

y = g ( W 2 h + b 2 ) \mathbf{y} = g(\mathbf{W}_2 \mathbf{h} + \mathbf{b}_2) y=g(W2h+b2)

其中, W 2 \mathbf{W}_2 W2 是隐藏层到输出层的权重矩阵, b 2 \mathbf{b}_2 b2 是偏置向量, g g g 是激活函数(如 Sigmoid)。

多次函数与神经网络中的非线性变换

多次函数(多项式函数)确实是非线性的,它们可以表示为变量的多次幂及其线性组合的形式。例如,一个二次函数可以表示为:

y = a x 2 + b x + c y = ax^2 + bx + c y=ax2+bx+c

尽管多次函数是非线性的,但在神经网络中并不常用来实现非线性变换。下面我们详细讨论为什么在神经网络中更常用激活函数(如 ReLU、Sigmoid、Tanh 等)来实现非线性,而不是多次函数。

多次函数的局限性
  1. 计算复杂度

    多次函数的计算复杂度较高,尤其是高次多项式。每个神经元需要计算输入的多次幂,这在计算上会非常耗时,尤其是对于大规模神经网络。

  2. 梯度爆炸和梯度消失

    多次函数的导数是幂函数的形式,高次幂函数的导数在输入较大或较小时会导致梯度爆炸或梯度消失。这使得训练深度神经网络变得非常困难。

  3. 参数量和过拟合

    多次函数需要更多的参数来表示复杂的非线性关系。这增加了模型的复杂度,容易导致过拟合,尤其是在训练数据量较少的情况下。

  4. 表达能力有限

    尽管多次函数可以表示某些非线性关系,但它们的表达能力在高维空间中受到限制。激活函数如 ReLU、Sigmoid 和 Tanh 可以通过简单的非线性变换实现更强的表达能力,适用于更广泛的非线性模式。

为什么选择常用的激活函数
  1. 计算简单

    常用的激活函数(如 ReLU、Sigmoid、Tanh)计算简单,计算开销较低,适合大规模神经网络的训练和推理。

  2. 缓解梯度消失和梯度爆炸

    激活函数如 ReLU 在输入大于 0 时的导数恒为 1,有效缓解了梯度消失问题。LeakyReLU 等变种激活函数通过在负值区域引入小斜率,进一步缓解了梯度消失问题。

  3. 通用性强

    常用的激活函数在各种任务和网络结构中表现良好,具有广泛的适用性。它们能够通过简单的非线性变换实现复杂的非线性关系,增强神经网络的表达能力。

  4. 经验验证

    大量的研究和实践表明,常用的激活函数在训练深度神经网络时表现出色,能够有效提升模型的性能和稳定性。

结论

激活函数在深度学习模型中起着关键作用,通过引入非线性,使得模型能够更好地拟合复杂的数据模式。不同的激活函数适用于不同的任务和网络结构,选择合适的激活函数可以显著提升模型的性能。本文详细介绍了 ReLU、Sigmoid、Tanh 和 LeakyReLU 激活函数的作用、优缺点及其经典应用示例,希望能帮助读者更好地理解和应用这些激活函数。

线性变换和非线性变换在神经网络中扮演着不同但互补的角色。线性变换通过权重矩阵和偏置向量实现输入的加权求和,而非线性变换通过激活函数引入非线性,使得神经网络能够拟合复杂的数据模式。理解这两种变换的区别和应用,对于构建和优化神经网络至关重要。

尽管多次函数是非线性的,但由于其计算复杂度高、容易导致梯度爆炸或梯度消失、参数量大且容易过拟合等问题,它们在神经网络中并不常用。相反,常用的激活函数如 ReLU、Sigmoid 和 Tanh 计算简单、能够有效缓解梯度问题且具有强大的表达能力,因此在深度学习中得到了广泛应用。理解这些激活函数的优缺点和适用场景,有助于构建和优化高效的深度学习模型。

PyTorch概述
Pytorch :张量(Tensor)详解
PyTorch 卷积层详解
PyTorch 全连接层(Fully Connected Layer)详解
PyTorch 池化层详解
PyTorch 激活函数及非线性变换详解

相关推荐
Yan-英杰3 分钟前
Encountered error while trying to install package.> lxml
开发语言·python·pandas·pip·issue
RS&6 分钟前
python学习笔记
笔记·python·学习
AI原吾6 分钟前
解锁自动化新境界:KeymouseGo,让键盘和鼠标动起来!
运维·python·自动化·计算机外设·keymousego
卡卡_R-Python7 分钟前
海洋气象编程工具-Python
开发语言·python
Colddd_d7 分钟前
动手学深度学习(五)循环神经网络RNN
人工智能·rnn·深度学习
北愚9 分钟前
Scrapy爬虫实战——某瓣250
python·scrapy
.别止步春天.11 分钟前
Python中lambda表达式的使用——完整通透版
数据结构·python·算法
sp_fyf_202415 分钟前
人工智能-大语言模型-微调技术-LoRA及背后原理简介
人工智能·语言模型·自然语言处理
RaidenQ18 分钟前
2024.9.20 Python模式识别新国大EE5907,PCA主成分分析,LDA线性判别分析,GMM聚类分类,SVM支持向量机
python·算法·机器学习·支持向量机·分类·聚类
xuehaishijue20 分钟前
射击靶标检测系统源码分享
人工智能·计算机视觉·目标跟踪