【前馈神经网络详解与实例】2——激活函数

我的前馈神经网络系列文章如下,便于读者成体系学习:

【前馈神经网络详解与实例】1——网络结构-CSDN博客

【前馈神经网络详解与实例】2——激活函数-CSDN博客

【前馈神经网络详解与实例】3——初始化方法-CSDN博客

【前馈神经网络详解与实例】4——损失函数-CSDN博客

【前馈神经网络详解与实例】5——参数优化方法-CSDN博客

【前馈神经网络详解与实例】6——正则化方法-CSDN博客

【前馈神经网络详解与实例】7——典型网络介绍-CSDN博客

【前馈神经网络详解与实例】8——实战应用-CSDN博客

2、📈 激活函数

2.1 激活函数的作用

  • 增加模型的非线性分割能力

图1图2

在神经元中引入激活函数的本质是向神经网络中引入非线性因素,通过激活函数,神经网络就可以拟合各种曲线。如果不用激活函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合

如图2,在图1基础上增加了一层隐藏层

输出由y = w_1 x_1 + w_2 x_2 + b

变为了y = w_{2-1}(w_{1-11}x_1 + w_{1-21}x_2 + b_{1-1}) + w_{2-2}(w_{1-12}x_1 + w_{1-22}x_2 + b_{1-2}) + w_{2-3}(w_{1-13}x_1 + w_{1-23}x_2 + b_{1-3})

但仍然为线性,没有任何改进

引入非线性函数作为激活函数,那输出不再是输入的线性组合,可以逼近任意函数。

  • 提高模型鲁棒性

  • 缓解梯度消失问题

  • 加速模型收敛等

2.2 激活函数的种类

2.2.1 Sigmoid 函数

  • 数学公式


    \sigma(x)=\frac{1}{1+e^{-x}}

  • 函数及其导数图像

    Sigmoid 函数Sigmoid 导数
  • 使用场景

    • sigmoid 在定义域内处处可导,且两侧导数逐渐趋近于0。如果 x 的值很大或者很小的时候,那么函数的梯度(函数的斜率)会非常小,在反向传播的过程中,导致了向低层传递的梯度也变得非常小。此时,网络参数很难得到有效训练。这种现象被称为梯度消失 。一般来说, sigmoid 网络在 5 层之内就会产生梯度消失现象。而且,该激活函数并不是以 0 为中心的(是以 0.5 为中心的),所以在实践中这种激活函数使用的很少。

    • sigmoid函数将输入映射到 (0, 1) 区间,一般只用于二分类的输出层

  • 实现代码

    import torch
     import torch.nn as nn
     import torch.nn.functional as F
     ​
     # 方法1:使用类(作为网络层)
     sigmoid_layer = nn.Sigmoid()
     ​
     # 方法2:使用函数式接口
     x = torch.tensor([-2.0, 0.0, 2.0])
     output1 = sigmoid_layer(x)  # 类调用
     output2 = F.sigmoid(x)      # 函数调用
     ​
     print("Sigmoid输出:", output1)  # 输出:tensor([0.1192, 0.5000, 0.8808])

2.2.2 Tanh函数

  • 数学公式


    tanh(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}

  • 函数及其导数图像

    Tanh 函数Tanh 导数
  • 使用场景

    tanh 也是一种非常常见的激活函数。与 sigmoid 相比,它是以 0 为中心的,使得其收敛速度要比 sigmoid 快(相比之下,tanh 曲线更为陡峭一些),减少迭代次数。然而,从图中可以看出,tanh 两侧的导数也为 0,同样会造成梯度消失

    若使用时可在隐藏层使用 tanh 函数,在输出层使用 sigmoid 函数

  • 实现代码

     import torch
     import torch.nn as nn
     import torch.nn.functional as F
     ​
     # 方法1:使用类
     tanh_layer = nn.Tanh()
     ​
     # 方法2:使用函数式接口
     x = torch.tensor([-1.0, 0.0, 1.0])
     output1 = tanh_layer(x)  # 类调用
     output2 = F.tanh(x)      # 函数调用
     ​
     print("Tanh输出:", output1)  # 输出:tensor([-0.7616,  0.0000,  0.7616])

2.2.3 ReLU函数

  • 数学公式


    ReLU(x)=max(0,x)

  • 函数及其导数图像

    ReLU 函数ReLU 导数
  • 使用场景

    • ReLU是目前最常用的激活函数。 从图中可以看到,当 x<0 时,ReLU导数为0,而当 x>0 时,则不存在饱和问题。所以,ReLU 能够在 x>0 时保持梯度不衰减,从而缓解梯度消失问题。然而,随着训练的推进,部分输入会落入小于0区域,导致对应权重无法更新。这种现象被称为“神经元死亡”。

    • Relu是输入只能大于0,如果你输入含有负数,Relu就不适合,如果你的输入是图片格式,Relu就挺常用的,因为图片的像素值作为输入时取值为[0,255]。

    • 与sigmoid相比,RELU的优势是:

      • 采用sigmoid函数,计算量大(指数运算),反向传播求误差梯度时,求导涉及除法,计算量相对大,而采用Relu激活函数,整个过程的计算量节省很多。

      • sigmoid函数反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。

      • Relu会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题。

  • 实现代码

     import torch
     import torch.nn as nn
     import torch.nn.functional as F
     ​
     # 方法1:使用类
     relu_layer = nn.ReLU()
     ​
     # 方法2:使用函数式接口
     x = torch.tensor([-1.0, 0.0, 1.0])
     output1 = relu_layer(x)  # 类调用
     output2 = F.relu(x)      # 函数调用
     ​
     # 类接口(如nn.ReLU):
     #    适合在__init__中定义为网络的固定层,参数可通过model.parameters()管理(如 Swish 的\(\beta\))
     # 函数接口(如F.relu):
     #    适合在forward中灵活调用,无需在__init__中预先定义
     ​
     print("ReLU输出:", output1)  # 输出:tensor([0., 0., 1.])

2.2.4 Leaky ReLU 函数

  • 数学公式(一般取 \alpha =0.01

    \text{Leaky ReLU}(x) = \begin{cases} x & \text{if } x > 0 \\ \alpha x & \text{if } x \leq 0 \end{cases}

  • 函数及其导数图像

    Leaky ReLU 函数Leaky ReLU 导数
  • 使用场景

    该激活函数是对RELU的改进,一定程度上缓解了神经元死亡问题

  • 实现代码

    import torch
     import torch.nn as nn
     import torch.nn.functional as F
     ​
     # 方法1:使用类(可指定alpha)
     leaky_relu_layer = nn.LeakyReLU(negative_slope=0.01)  # negative_slope即alpha
     ​
     # 方法2:使用函数式接口
     x = torch.tensor([-1.0, 0.0, 1.0])
     output1 = leaky_relu_layer(x)  # 类调用
     output2 = F.leaky_relu(x, negative_slope=0.01)  # 函数调用
     ​
     print("Leaky ReLU输出:", output1)  # 输出:tensor([-0.0100,  0.0000,  1.0000])

2.2.5 ELU(Exponential Linear Unit)函数

  • 数学公式


    \text{ELU}(x) = \begin{cases} x & \text{if } x > 0 \\ \alpha (e^x - 1) & \text{if } x \leq 0 \end{cases}

  • 函数及其导数图像

    ELU 函数ELU 导数
  • 使用场景

    ELU激活函数是一种改进的激活函数,它在处理负值时具有独特的优势。ELU函数通过引入指数函数来处理负输入,从而在理论上可以提供更好的性能。因此,ELU激活函数适用于需要处理大量负值输入的各种深度学习任务。

  • 实现代码

     import torch
     import torch.nn as nn
     import torch.nn.functional as F
     ​
     # 方法1:使用类(可指定alpha)
     elu_layer = nn.ELU(alpha=1.0)
     ​
     # 方法2:使用函数式接口
     x = torch.tensor([-1.0, 0.0, 1.0])
     output1 = elu_layer(x)  # 类调用
     output2 = F.elu(x, alpha=1.0)  # 函数调用
     ​
     print("ELU输出:", output1)  # 输出:tensor([-0.6321,  0.0000,  1.0000])

2.2.6 Softmax函数

  • 数学公式

    \text{softmax}(x_i) = \frac{e^{x_i}}{\sum_j e^{x_j}}

  • 使用场景

    softmax用于多分类过程中,它是二分类函数 sigmoid 在多分类上的推广,目的是将多分类的结果以概率的形式展现出来。

    softmax 直白来说就是将网络输出的 logits 通过softmax函数,就映射成为(0,1)的值,而这些值的累和为1(满足概率的性质),那么我们将它理解成概率,选取概率最大(也就是值对应最大的)接点,作为我们的预测目标类别。

  • 实现代码

     import torch
     import torch.nn as nn
     import torch.nn.functional as F
     ​
     # 方法1:使用类(dim参数指定计算维度,通常为-1表示最后一维)
     softmax_layer = nn.Softmax(dim=-1)
     ​
     # 方法2:使用函数式接口
     x = torch.tensor([[1.0, 2.0, 3.0]])  # 假设是批次中的一个样本(形状:[1, 3])
     output1 = softmax_layer(x)  # 类调用
     output2 = F.softmax(x, dim=-1)  # 函数调用
     ​
     print("Softmax输出:", output1)  # 输出:tensor([[0.0900, 0.2447, 0.6652]])
     print("概率和:", output1.sum())  # 输出:tensor(1.0)(满足概率分布性质)

2.3 激活函数的选择

  • 隐藏层

    • 优先选择RELU激活函数

    • 如果ReLu效果不好,那么尝试其他激活,如Leaky ReLu等

    • 如果使用了Relu, 需要注意避免出现大的梯度从而导致过多的神经元死亡

    • 不要使用sigmoid激活函数,可以尝试使用tanh激活函数

  • 输出层

    • 二分类问题选择sigmoid激活函数

    • 多分类问题选择softmax激活函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值