在神经网络的训练中,激活函数决定了信号的传递方式,而参数初始化则决定了网络的“起点”。两者看似独立,却共同影响着模型的收敛速度与最终性能。今天,我们不仅会解析常见的激活函数(包括近年来流行的GELU等),还会探讨如何根据激活函数选择合适的参数初始化方法,让神经网络“赢在起跑线”。


相关文章

人工智能概念之九:深度学习概述网页链接
人工智能概念之十:人工神经网络(ANN)网页链接


一、激活函数:给网络注入非线性活力

激活函数的核心作用是打破线性映射的局限,让神经网络能够拟合复杂的非线性关系。没有激活函数,再多的网络层也只是“花架子”,无法处理图像、文本等现实世界数据。

假设我们要拟合一个简单的非线性关系: y = x 2 y = x^2 y=x2(比如“面积与边长的平方关系”)。如果不用激活函数,即使堆叠多层神经网络,每一层的输出都是输入的线性组合(如 z = w ⋅ x + b z = w \cdot x + b z=wx+b)。例如,第一层输出 z 1 = w 1 x + b 1 z_1 = w_1 x + b_1 z1=w1x+b1,第二层输出 z 2 = w 2 z 1 + b 2 = w 2 ( w 1 x + b 1 ) + b 2 = ( w 2 w 1 ) x + ( w 2 b 1 + b 2 ) z_2 = w_2 z_1 + b_2 = w_2(w_1 x + b_1) + b_2 = (w_2 w_1)x + (w_2 b_1 + b_2) z2=w2z1+b2=w2(w1x+b1)+b2=(w2w1)x+(w2b1+b2),本质仍是 y = k x + c y = kx + c y=kx+c的线性形式,无论多少层都无法表达 x 2 x^2 x2这样的曲线关系。

但加入激活函数后(如ReLU: f ( z ) = max ⁡ ( 0 , z ) f(z) = \max(0, z) f(z)=max(0,z)),情况完全不同。假设第一层计算 z 1 = w 1 x + b 1 z_1 = w_1 x + b_1 z1=w1x+b1,经ReLU激活后得到 a 1 = max ⁡ ( 0 , z 1 ) a_1 = \max(0, z_1) a1=max(0,z1);第二层用 z 2 = w 2 a 1 + b 2 z_2 = w_2 a_1 + b_2 z2=w2a1+b2,再通过激活函数输出。通过调整权重,网络可以组合出类似“ x > 0 x>0 x>0时输出 x 2 x^2 x2 x < 0 x<0 x<0时输出 x 2 x^2 x2”的非线性映射,最终精准拟合 y = x 2 y = x^2 y=x2。这就是激活函数的核心价值:为网络注入非线性“能力”,让深层结构真正拥有拟合复杂现实关系的潜力。

1.1 经典激活函数:奠定神经网络基础

1.1.1 Sigmoid函数:早期的“概率开关”

Sigmoid函数的公式为:
σ ( x ) = 1 1 + e − x \sigma(x) = \frac{1}{1 + e^{-x}} σ(x)=1+ex1

  • 特性:将输入映射到(0, 1)区间,适合作为二分类任务的输出层(如判断“垃圾邮件/正常邮件”)。
  • 缺陷
    • 输入绝对值较大时(|x| > 6),梯度接近0,易导致深层网络“梯度消失”。
    • 输出均值为0.5(非零中心),会导致梯度更新方向单一,影响训练效率。
      在这里插入图片描述
1.1.2 Tanh函数:零中心的“改进版Sigmoid”

Tanh(双曲正切函数)公式为:
tanh ⁡ ( x ) = e x − e − x e x + e − x \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} tanh(x)=ex+exexex

  • 特性:输出范围为(-1, 1),以0为中心,梯度更新效率优于Sigmoid。
  • 缺陷:输入绝对值较大时(|x| > 3),梯度仍会趋近于0,深层网络仍可能梯度消失。
    在这里插入图片描述
1.1.3 ReLU函数:当代神经网络的“主力选手”

ReLU(修正线性单元)公式极简:
ReLU ( x ) = max ⁡ ( 0 , x ) \text{ReLU}(x) = \max(0, x) ReLU(x)=max(0,x)

  • 特性
    • 输入为正时直接输出原值(梯度为1),避免梯度消失,适合深层网络。
    • 计算高效(无需指数运算),稀疏激活(负输入输出0),缓解过拟合。
  • 缺陷:存在“神经元死亡”问题(输入长期为负时,神经元永久失效),可通过Leaky ReLU(负输入保留小斜率)缓解。
    在这里插入图片描述
1.1.4 Softmax函数:多分类的“概率分配器”

Softmax专为多分类设计,公式为:
Softmax ( z i ) = e z i ∑ j = 1 k e z j \text{Softmax}(z_i) = \frac{e^{z_i}}{\sum_{j=1}^k e^{z_j}} Softmax(zi)=j=1kezjezi

  • 特性:将输入转换为和为1的概率分布,直接输出每个类别的预测概率(如“猫/狗/鸟”的分类)。
    在这里插入图片描述

1.2 现代激活函数:为复杂模型而生

1.2.1 GELU (Gaussian Error Linear Unit)

GELU是近年来Transformer架构的“标配”,公式近似为:
GELU ( x ) ≈ 0.5 x ( 1 + tanh ⁡ ( 2 π ( x − 0.044715 x 3 ) ) ) \text{GELU}(x) \approx 0.5x \left(1 + \tanh\left(\sqrt{\frac{2}{\pi}} \left(x - 0.044715x^3\right)\right)\right) GELU(x)0.5x(1+tanh(π2 (x0.044715x3)))

  • 特性
    • 结合ReLU的非线性与正态分布的随机正则化效果,输出随输入概率性激活。
    • 平滑连续、处处可导,彻底避免“死亡神经元”问题。
    • 在BERT、GPT等大语言模型中表现远超ReLU,成为自然语言处理任务的首选。
      在这里插入图片描述
1.2.2 Swish/SiLU (Sigmoid-weighted Linear Unit)

Swish由Google提出,是带自门控机制的激活函数:
Swish ( x ) = x ⋅ σ ( x ) \text{Swish}(x) = x \cdot \sigma(x) Swish(x)=xσ(x)
(其中 σ \sigma σ为Sigmoid函数)

  • 特性
    • 非单调特性让它能更好地捕捉复杂模式,在深层网络中性能优于ReLU。
    • 计算简单(仅比ReLU多一次Sigmoid运算),梯度流动稳定。
    • PyTorch中F.silu(x) β = 1 \beta=1 β=1的Swish完全等价: SiLU ( x ) = x ⋅ σ ( x ) = Swish ( x ) \text{SiLU}(x) = x \cdot \sigma(x) = \text{Swish}(x) SiLU(x)=xσ(x)=Swish(x),已成为计算机视觉任务的常用选择。
      在这里插入图片描述
1.2.3 Mish

Mish结合了Tanh和Softplus的优点,公式为:
Mish ( x ) = x ⋅ tanh ⁡ ( Softplus ( x ) ) \text{Mish}(x) = x \cdot \tanh(\text{Softplus}(x)) Mish(x)=xtanh(Softplus(x))
(其中 Softplus ( x ) = log ⁡ ( 1 + e x ) \text{Softplus}(x) = \log(1 + e^x) Softplus(x)=log(1+ex)

  • 特性
    • 无上界(避免梯度饱和)、有下界(让负值区域更稳定),梯度流动更顺畅。
    • 在目标检测、图像分类等任务中,常能超越Swish和ReLU,但计算成本略高。
      在这里插入图片描述
1.2.4 ELU (Exponential Linear Unit)

ELU是ReLU的改进版,针对负输入设计:
ELU ( x ) = { x if  x > 0 α ( e x − 1 ) if  x ≤ 0 \text{ELU}(x) = \begin{cases} x & \text{if } x > 0 \\ \alpha(e^x - 1) & \text{if } x \leq 0 \end{cases} ELU(x)={xα(ex1)if x>0if x0

  • 特性
    • 负输入区域通过指数函数平滑过渡,输出均值接近0,加速收敛。
    • 减少“死亡神经元”问题,但指数运算增加了计算量,实际使用中不如Leaky ReLU普及。
      在这里插入图片描述
1.2.5 Leaky ReLU与PReLU

两者均为解决ReLU“神经元死亡”问题而设计:

  • Leaky ReLU:负输入区域保留固定小斜率(如0.01):
    Leaky ReLU ( x ) = { x if  x > 0 0.01 x if  x ≤ 0 \text{Leaky ReLU}(x) = \begin{cases} x & \text{if } x > 0 \\ 0.01x & \text{if } x \leq 0 \end{cases} Leaky ReLU(x)={x0.01xif x>0if x0
  • PReLU:负斜率为可学习参数,让模型自适应数据分布,适合数据量充足的场景。
    在这里插入图片描述
1.2.6 SELU (Scaled Exponential Linear Unit)

SELU是专为自归一化网络设计的激活函数:
SELU ( x ) = λ { x if  x > 0 α ( e x − 1 ) if  x ≤ 0 \text{SELU}(x) = \lambda \begin{cases} x & \text{if } x > 0 \\ \alpha(e^x - 1) & \text{if } x \leq 0 \end{cases} SELU(x)=λ{xα(ex1)if x>0if x0
(其中 α ≈ 1.6733 \alpha \approx 1.6733 α1.6733, λ ≈ 1.0507 \lambda \approx 1.0507 λ1.0507 为固定缩放参数)

  • 特性:配合特定初始化(如LeCun初始化),可让网络层输出自动保持均值0、方差1,无需批量归一化。
  • 局限:仅适用于特定网络结构(如全连接网络),灵活性较低。
    在这里插入图片描述

二、参数初始化:激活函数的“最佳拍档”

神经网络的参数(权重和偏置)初始化直接影响训练效果。不合适的初始化会导致梯度爆炸、收敛缓慢,甚至模型无法训练。初始化方法的选择需与激活函数匹配,才能发挥最佳效果。

2.1 为什么初始化很重要?

  • 避免梯度消失/爆炸:权重过大导致激活值急剧膨胀(梯度爆炸),过小则导致信号衰减(梯度消失)。
  • 打破对称性:若所有权重初始化为相同值,神经元会学习到相同特征,网络失去表达能力。
  • 加速收敛:合理的初始化让激活值分布更稳定,梯度更新更高效。

2.2 常见初始化方法及适用场景

初始化方法 核心思想 适用激活函数 公式(简化)
随机正态/均匀初始化 从正态/均匀分布中随机采样 浅层网络(<5层) 均匀分布: W ∼ U ( − a , a ) W \sim U(-a, a) WU(a,a)(a为小值)
Xavier(Glorot)初始化 平衡输入输出方差,避免信号衰减 Sigmoid、Tanh 均匀分布: W ∼ U ( − 6 n in + n out , 6 n in + n out ) W \sim U\left(-\sqrt{\frac{6}{n_{\text{in}}+n_{\text{out}}}}, \sqrt{\frac{6}{n_{\text{in}}+n_{\text{out}}}}\right) WU(nin+nout6 ,nin+nout6 )
Kaiming(He)初始化 针对ReLU特性,放大输入方差 ReLU、Leaky ReLU、Swish 正态分布: W ∼ N ( 0 , 2 n in ) W \sim N\left(0, \sqrt{\frac{2}{n_{\text{in}}}}\right) WN(0,nin2 )(注: 2 n in \sqrt{\frac{2}{n_{\text{in}}}} nin2 表示标准差)
LeCun初始化 为SELU设计,控制输出方差为1 SELU 正态分布: W ∼ N ( 0 , 1 n in ) W \sim N\left(0, \frac{1}{n_{\text{in}}}\right) WN(0,nin1)(注:此处为方差,标准差为 1 n in \sqrt{\frac{1}{n_{\text{in}}}} nin1
全0/全1初始化 权重全部为0或1 几乎不适用 ——

2.3 初始化与激活函数的匹配逻辑

  • Sigmoid/Tanh:选择Xavier初始化。这类激活函数在输入接近0时梯度最大,Xavier通过平衡输入输出方差,避免信号在多层传递后衰减。
  • ReLU/Leaky ReLU/Swish:选择Kaiming初始化。ReLU会“过滤”一半的负输入,导致输出方差减半,Kaiming通过放大初始化方差( 2 n in \sqrt{\frac{2}{n_{\text{in}}}} nin2 )来补偿。
  • SELU:必须搭配LeCun初始化,才能实现“自归一化”特性,无需额外归一化层。
  • GELU/Mish:实践中常用Kaiming初始化(因与ReLU特性接近),在Transformer中已成为默认选择。

三、总结:激活函数与初始化的“黄金法则”

以下是各激活函数的对比表格,涵盖优缺点、适用场景及典型模型应用:

激活函数 优点 缺点 适用场景 典型模型应用
Sigmoid 输出范围(0,1),可直接作为概率;实现简单 梯度易消失;输出非零中心;计算成本高(指数运算) 二分类输出层 早期神经网络(如MLP)、逻辑回归输出层
Tanh 输出范围(-1,1),零中心;梯度绝对值大于Sigmoid,收敛更快 梯度易消失;计算成本高(指数运算) 早期RNN隐藏层、自编码器 LSTM(早期版本)、传统自编码器
ReLU 计算高效(无指数运算);梯度不衰减(x>0时);稀疏激活缓解过拟合 存在“神经元死亡”问题;输出非零中心 通用隐藏层(CNN、MLP等) ResNet、VGG、AlexNet等主流CNN模型
Leaky ReLU 解决ReLU的“神经元死亡”问题;计算效率接近ReLU 负斜率为超参数,需手动调整 ReLU效果不佳时的替代方案 目标检测模型(如YOLOv3)、部分CNN变体
PReLU 负斜率可学习,自适应数据分布;性能优于固定斜率的Leaky ReLU 增加模型参数;数据量不足时易过拟合 数据充足的计算机视觉任务 ResNet(部分改进版)、图像分割模型
GELU 平滑连续可导;结合随机性与非线性,适合深层网络;无神经元死亡问题 计算成本略高于ReLU Transformer架构、大语言模型 BERT、GPT系列、T5等预训练语言模型
Swish/SiLU 非单调特性捕捉复杂模式;梯度流动稳定;性能优于ReLU 计算成本略高于ReLU(含Sigmoid运算) 计算机视觉、深层CNN MobileNetV3、EfficientNet、Vision Transformer(ViT)
Mish 无上界避免梯度饱和;有下界稳定负值区域;梯度流动更顺畅 计算成本较高(含Tanh和Softplus) 高精度视觉任务 YOLOv4、YOLOv5、部分目标检测与图像分类模型
ELU 负输入区域平滑过渡;输出均值接近0,加速收敛 计算成本高(指数运算);性能提升有限 需避免神经元死亡的场景 少量自编码器、强化学习模型
SELU 自归一化特性,无需批量归一化;适合深层网络 仅适用于特定网络结构(如全连接);对初始化敏感 自归一化神经网络(SNN) 自归一化全连接网络、部分强化学习策略网络
Softmax 输出和为1,直接作为多分类概率;易于解释 计算成本高(指数运算);对异常值敏感 多分类输出层 所有多分类模型(如ResNet分类头、BERT文本分类输出层)
说明:  
1. 表格中“计算成本”以ReLU为基准(设为1),Sigmoid/Tanh约为2-3,GELU/Swish约为1.5,Mish/ELU约为3-4。  
2. “典型模型应用”为该激活函数表现突出的场景,实际中可能存在交叉使用(如Swish也可用于NLP模型)。  
3. 现代模型(如Transformer、CNN)优先选择GELU/Swish/ReLU,传统模型或特定场景(如二分类)仍保留Sigmoid/Softmax。
  1. 激活函数选对“类型”

    • 通用场景:ReLU(简单高效)或Leaky ReLU(避免神经元死亡)。
    • Transformer/大语言模型:GELU(平滑稳定,适配注意力机制)。
    • 计算机视觉:Swish/SiLU(非单调特性捕捉图像细节)。
    • 输出层:二分类用Sigmoid,多分类用Softmax,回归用线性输出。
  2. 初始化找对“搭档”

    • Sigmoid/Tanh配Xavier,ReLU/Swish/GELU配Kaiming,SELU配LeCun——这是经过实践验证的“最优组合”。
    • 偏置通常初始化为0(无需复杂策略)。
  3. 灵活调试:若模型收敛慢或梯度异常,可尝试更换初始化方法(如从正态分布换为均匀分布),或在小范围内调整激活函数的超参数(如Leaky ReLU的负斜率)。

激活函数与参数初始化就像神经网络的“引擎”和“燃料”,只有匹配得当,才能让模型高效运转。理解它们的特性与关联,是构建高性能神经网络的基础。


四、绘图代码

# 导入numpy库,用于数值计算
import numpy as np
# 导入matplotlib.pyplot库,用于绘图
import matplotlib.pyplot as plt
# 从math库中导入pi和sqrt函数
from math import pi, sqrt

# 设置matplotlib的全局参数
# 设置字体为SimHei(黑体),支持中文显示
plt.rcParams["font.family"] = ["SimHei"]
# 设置axes.unicode_minus为False,解决负号显示问题
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 定义激活函数及其导数

# Sigmoid激活函数:将输入映射到(0,1)区间
def sigmoid(x):
    # 使用sigmoid函数公式:1/(1+e^(-x))
    return 1 / (1 + np.exp(-x))

# Sigmoid函数的导数
def sigmoid_derivative(x):
    # 先计算sigmoid值
    s = sigmoid(x)
    # sigmoid导数公式:s*(1-s)
    return s * (1 - s)

# Tanh(双曲正切)激活函数:将输入映射到(-1,1)区间
def tanh(x):
    # 使用tanh函数公式:(e^x - e^(-x))/(e^x + e^(-x))
    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))

# Tanh函数的导数
def tanh_derivative(x):
    # tanh导数公式:1-tanh^2(x)
    return 1 - np.square(tanh(x))

# ReLU(修正线性单元)激活函数:返回输入和0的最大值
def relu(x):
    # 使用numpy的maximum函数,返回x和0的最大值
    return np.maximum(0, x)

# ReLU函数的导数
def relu_derivative(x):
    # 当x>0时导数为1,否则为0
    return np.where(x > 0, 1, 0)

# Softmax激活函数:将输入转换为概率分布
def softmax(x):
    # 为避免数值溢出,减去最大值(数值稳定性技巧)
    exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
    # 返回归一化的概率分布
    return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

# Softmax函数的导数(近似)
def softmax_derivative(x):
    # 将输入reshape为1行多列的矩阵
    s = softmax(x.reshape(1, -1))
    # softmax导数近似计算:s*(1-s)
    return s * (1 - s)

# GELU(高斯误差线性单元)激活函数
def gelu(x):
    # GELU函数的近似计算公式
    return 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * np.power(x, 3))))

# GELU函数的导数
def gelu_derivative(x):
    # GELU导数的近似计算
    # 计算tanh项
    tanh_term = tanh(sqrt(2/pi) * (x + 0.044715 * np.power(x, 3)))
    # 返回GELU导数计算结果
    return 0.5 * (1 + tanh_term) + 0.5 * x * (1 - np.square(tanh_term)) * sqrt(2/pi) * (1 + 3 * 0.044715 * np.square(x))

# Swish激活函数(也称为SiLU)
def swish(x):
    # Swish函数公式:x * sigmoid(x)
    return x * sigmoid(x)

# Swish函数的导数
def swish_derivative(x):
    # 先计算sigmoid值
    s = sigmoid(x)
    # Swish导数公式:s + x*s*(1-s)
    return s + x * s * (1 - s)

# Mish激活函数
def mish(x):
    # Mish函数公式:x * tanh(softplus(x)),其中softplus(x)=log(1+e^x)
    return x * tanh(np.log(1 + np.exp(x)))

# Mish函数的导数
def mish_derivative(x):
    # 计算softplus值
    softplus = np.log(1 + np.exp(x))
    # 计算tanh(softplus)
    tanh_softplus = tanh(softplus)
    # 返回Mish导数计算结果
    return tanh_softplus + x * (1 - np.square(tanh_softplus)) * (np.exp(x) / (1 + np.exp(x)))

# ELU(指数线性单元)激活函数
def elu(x, alpha=1.0):
    # 当x>0时返回x,否则返回alpha*(e^x-1)
    return np.where(x > 0, x, alpha * (np.exp(x) - 1))

# ELU函数的导数
def elu_derivative(x, alpha=1.0):
    # 当x>0时导数为1,否则为alpha*e^x
    return np.where(x > 0, 1, alpha * np.exp(x))

# Leaky ReLU激活函数
def leaky_relu(x, alpha=0.01):
    # 当x>0时返回x,否则返回alpha*x
    return np.where(x > 0, x, alpha * x)

# Leaky ReLU函数的导数
def leaky_relu_derivative(x, alpha=0.01):
    # 当x>0时导数为1,否则为alpha
    return np.where(x > 0, 1, alpha)

# SELU(自归一化指数线性单元)激活函数
def selu(x, lambda_=1.0507, alpha=1.67326):
    # SELU函数:lambda_ * (x if x>0 else alpha*(e^x-1))
    return lambda_ * np.where(x > 0, x, alpha * (np.exp(x) - 1))

# SELU函数的导数
def selu_derivative(x, lambda_=1.0507, alpha=1.67326):
    # SELU导数:lambda_ * (1 if x>0 else alpha*e^x)
    return lambda_ * np.where(x > 0, 1, alpha * np.exp(x))

# 单独绘制每个激活函数的图像
def plot_single_activation(func, deriv, name, formula, x_range=(-6, 6)):
    # 生成x值:在指定范围内生成1000个均匀分布的点
    x = np.linspace(x_range[0], x_range[1], 1000)

    # 计算函数值和导数值
    # 计算激活函数在x上的值
    y = func(x)
    # 计算导数函数在x上的值
    dy = deriv(x)

    # 创建两个子图(函数和导数):1行2列的子图布局
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

    # 绘制函数图像
    # 使用蓝色实线绘制函数图像
    ax1.plot(x, y, 'b-', linewidth=2)
    # 设置第一个子图的标题,包含函数名和公式
    ax1.set_title(f'{name}函数\n{formula}')
    # 添加网格线,设置为虚线样式,透明度0.7
    ax1.grid(True, linestyle='--', alpha=0.7)
    # 添加水平轴线(y=0)
    ax1.axhline(y=0, color='k', linestyle='-', alpha=0.3)
    # 添加垂直轴线(x=0)
    ax1.axvline(x=0, color='k', linestyle='-', alpha=0.3)
    # 设置x轴标签
    ax1.set_xlabel('x')
    # 设置y轴标签
    ax1.set_ylabel('f(x)')

    # 绘制导数图像
    # 使用红色实线绘制导数图像
    ax2.plot(x, dy, 'r-', linewidth=2)
    # 设置第二个子图的标题,显示函数名和"导数"
    ax2.set_title(f'{name}导数')
    # 添加网格线,设置为虚线样式,透明度0.7
    ax2.grid(True, linestyle='--', alpha=0.7)
    # 添加水平轴线(y=0)
    ax2.axhline(y=0, color='k', linestyle='-', alpha=0.3)
    # 添加垂直轴线(x=0)
    ax2.axvline(x=0, color='k', linestyle='-', alpha=0.3)
    # 设置x轴标签
    ax2.set_xlabel('x')
    # 设置y轴标签
    ax2.set_ylabel("f'(x)")

    # 调整子图布局,使子图之间不重叠
    plt.tight_layout()
    # 保存图像到文件(注释掉的代码)
    # plt.savefig(f'{name}_activation.png', dpi=300, bbox_inches='tight')
    # 显示图像
    plt.show()

# 单独绘制Softmax函数
def plot_softmax():
    # 为softmax单独处理(因为它通常用于多分类)
    # 生成softmax的x值:在-5到5之间生成10个点
    x_softmax = np.linspace(-5, 5, 10)
    # 计算softmax函数值
    softmax_values = softmax(x_softmax)
    # 计算softmax导数值
    softmax_deriv_values = softmax_derivative(x_softmax)

    # 创建画布:1行2列的子图布局
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

    # 绘制Softmax函数
    # 使用蓝色柱状图绘制softmax函数值
    ax1.bar(x_softmax, softmax_values.flatten(), color='b', alpha=0.7)
    # 设置第一个子图的标题
    ax1.set_title('Softmax函数\nSoftmax(z_i) = e^z_i / Σe^z_j')
    # 添加网格线
    ax1.grid(True, linestyle='--', alpha=0.7)
    # 添加水平轴线
    ax1.axhline(y=0, color='k', linestyle='-', alpha=0.3)
    # 设置x轴标签
    ax1.set_xlabel('x')
    # 设置y轴标签
    ax1.set_ylabel('Softmax(x)')

    # 绘制Softmax导数
    # 使用红色柱状图绘制softmax导数值
    ax2.bar(x_softmax, softmax_deriv_values.flatten(), color='r', alpha=0.7)
    # 设置第二个子图的标题
    ax2.set_title('Softmax导数近似')
    # 添加网格线
    ax2.grid(True, linestyle='--', alpha=0.7)
    # 添加水平轴线
    ax2.axhline(y=0, color='k', linestyle='-', alpha=0.3)
    # 设置x轴标签
    ax2.set_xlabel('x')
    # 设置y轴标签
    ax2.set_ylabel("Softmax'(x)")

    # 调整子图布局
    plt.tight_layout()
    # 保存图像到文件(注释掉的代码)
    # plt.savefig('Softmax_activation.png', dpi=300, bbox_inches='tight')
    # 显示图像
    plt.show()

# 创建绘图函数
def plot_all_activations_separately():
    # 生成x值:在-6到6之间生成1000个均匀分布的点
    x = np.linspace(-6, 6, 1000)

    # 定义激活函数列表:包含函数、导数、名称和公式
    activation_functions = [
        # Sigmoid函数及其导数、名称和公式
        (sigmoid, sigmoid_derivative, "Sigmoid", "σ(x) = 1/(1 + e^(-x))"),
        # Tanh函数及其导数、名称和公式
        (tanh, tanh_derivative, "Tanh", "tanh(x) = (e^x - e^(-x))/(e^x + e^(-x))"),
        # ReLU函数及其导数、名称和公式
        (relu, relu_derivative, "ReLU", "ReLU(x) = max(0, x)"),
        # GELU函数及其导数、名称和公式
        (gelu, gelu_derivative, "GELU", "GELU(x) ≈ 0.5x(1 + tanh(√(2/π)(x + 0.044715x³)))"),
        # Swish函数及其导数、名称和公式
        (swish, swish_derivative, "Swish", "Swish(x) = x·σ(x)"),
        # Mish函数及其导数、名称和公式
        (mish, mish_derivative, "Mish", "Mish(x) = x·tanh(Softplus(x))"),
        # ELU函数及其导数、名称和公式(使用lambda表达式包装)
        (lambda x: elu(x), lambda x: elu_derivative(x), "ELU", "ELU(x) = x (x>0), α(e^x-1) (x≤0)"),
        # Leaky ReLU函数及其导数、名称和公式(使用lambda表达式包装)
        (lambda x: leaky_relu(x), lambda x: leaky_relu_derivative(x), "Leaky_ReLU", "Leaky ReLU(x) = x (x>0), 0.01x (x≤0)"),
        # SELU函数及其导数、名称和公式(使用lambda表达式包装)
        (lambda x: selu(x), lambda x: selu_derivative(x), "SELU", "SELU(x) = λx (x>0), λα(e^x-1) (x≤0)")
    ]

    # 绘制每个激活函数及其导数(单独图像)
    # 遍历所有激活函数并绘制
    for func, deriv, name, formula in activation_functions:
        plot_single_activation(func, deriv, name, formula)

    # 单独绘制Softmax函数
    plot_softmax()

# 程序入口点:当脚本直接运行时执行
if __name__ == '__main__':
    # 调用绘图函数,绘制所有激活函数
    plot_all_activations_separately()

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐