"""
演示
常用的激活函数
人工神经元组成:
加权求和 + 激活函数(引入非线性)
sigmoid
用于二分类任务的输出层,通常用于浅层网络,不超过5层,容易出现梯度消失,目前使用很少
输出范围(0,1),导数范围(0,0.25]
ReLU
常用于 隐藏层,目前使用最多.
计算公式max(0,x),输出范围[0,+∞),导数{0,1}
优势:
1.计算简单,模型收敛快
2.正输入的导数恒为1,缓解了梯度消失问题
DeadReLU问题:
随着训练的推进,部分输入会落入负区域,恒为负,模型输出恒为0,导致对应权重无法更新。被称为“神经元死亡”。大模型训练时,通常采用GELU。
tanh
用于隐藏层,浅层网络,同样存在梯度消失问题,很少使用,主流网络中只有RNN(LSTM,GRU)使用了tanh
输出范围(-1,1),导数范围(0,1]
softmax
用于单标签多分类任务的输出层,二分类任务上可以替代sigmoid.
输出各个类别的概率分布,范围(0,1), 并且概率总和为1.
激活函数的选择方法
隐藏层:
ReLU -> Leaky ReLU/PReLU/GELU -> (Tanh)
输出层:
多分类问题选择 softmax
二分类问题选择 sigmoid 或 softmax
回归问题多数选择 identity (就是没有激活函数),正数ReLU,区间Sigmoid/Tanh
需要掌握的:
sigmoid公式、应用(面试)
torch.relu
torch.softmax
bug解决:
绘制激活函数图像时出现以下提示:
OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.
解决方法:
方法1:更换版本matplotlib, pip install matplotlib==3.7.5 -i https://pypi.tuna.tsinghua.edu.cn/simple
方法2:更换版本numpy, pip install numpy==1.26.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
"""
# 导包
import torch # pytorch框架
import torch.nn.functional as F # 函数模块
import matplotlib.pyplot as plt
import torch.nn as nn # 神经网络模块,提供各种网络层
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 1.定义函数,演示sigmoid激活函数的函数图像和导函数图像
def demo01():
# 1.创建画布
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 2.生成x值,-10到10之间,总数量100
x = torch.linspace(-10, 10, 100)
# 3.计算y值,sigmoid激活函数
y = torch.sigmoid(x)
# y = F.sigmoid(x)
# y = nn.Sigmoid()(x)
# 4.绘制函数图像,在第一个子图上
axes[0].plot(x, y)
axes[0].set_title('sigmoid激活函数')
axes[0].grid()
# 5.绘制导函数图像,在第二个子图上
x = torch.linspace(-10, 10, 100,requires_grad=True) # 1D:(100,)
y = torch.sigmoid(x) # 1D:(100,)
# y = F.sigmoid(x)
# y = nn.Sigmoid()(x)
y.sum().backward() # 方向传播获取导数,x.grad
# 如果.mean()就是先求和,再除以元素数量,会变成导数的1/100
axes[1].plot(x.detach(), x.grad)
axes[1].set_title('sigmoid激活函数导数')
axes[1].grid()
plt.show()
...
# 2.定义函数,演示relu激活函数的函数图像和导函数图像
def demo02():
# 1.创建画布
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 2.生成x值,-10到10之间,总数量100
x = torch.linspace(-10, 10, 1000)
# 3.计算y值,relu激活函数
y = torch.relu(x)
# y = F.relu(x)
# y = nn.ReLU()(x)
# 4.绘制函数图像,在第一个子图上
axes[0].plot(x, y)
axes[0].set_title('relu激活函数')
axes[0].grid()
# 5.绘制导函数图像,在第二个子图上
x = torch.linspace(-10, 10, 1000,requires_grad=True)
y = torch.relu(x)
y.sum().backward() # 方向传播获取导数,x.grad
axes[1].plot(x.detach(), x.grad)
axes[1].set_title('relu激活函数导数')
axes[1].grid()
plt.show()
...
# 3.定义函数,演示tanh激活函数的函数图像和导函数图像
def demo03():
# 1.创建画布
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 2.生成x值,-10到10之间,总数量100
x = torch.linspace(-10, 10, 1000)
# 3.计算y值,tanh激活函数
y = torch.tanh(x)
# 4.绘制函数图像,在第一个子图上
axes[0].plot(x, y)
axes[0].set_title('tanh激活函数')
axes[0].grid()
# 5.绘制导函数图像,在第二个子图上
x = torch.linspace(-10, 10, 1000,requires_grad=True)
y = torch.tanh(x)
y.sum().backward() # 方向传播获取导数,x.grad
axes[1].plot(x.detach(), x.grad)
axes[1].set_title('tanh激活函数导数')
axes[1].grid()
plt.show()
...
# 4.定义函数,演示softmax激活函数的函数图像和softmax(softmax)图像
def demo04():
# 1.创建画布
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 2.定义x,模拟模型的预测分数logits
x = torch.tensor([0.2,-0.9,1,-10,1,2,3,4,2.5,3.9]) # (10,)
# 举例说明:老板请客,你可以点一个菜,菜单上有10个菜,选了2对应的菜,最大预测分数10对应的类别
# 3.经过softmax运算
y = torch.softmax(x, dim=-1) # 通常特征维度/轴为-1轴
# y = F.softmax(x, dim=-1)
# 4.绘制softmax函数图像,在第一个子图上
axes[0].bar(range(len(y)), y)
axes[0].set_title('softmax激活函数')
axes[0].grid()
# 5.绘制softmax(softmax)函数图像,在第二个子图上
z = torch.softmax(y, dim=-1)
axes[1].bar(range(len(z)), z)
axes[1].set_title('softmax(softmax)激活函数')
axes[1].grid()
plt.show()
...
# 5.定义函数,演示gelu激活函数的函数图像和导函数图像
def demo05():
# 1.创建画布
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 2.生成x值,-10到10之间,总数量100
x = torch.linspace(-10, 10, 1000)
# 3.计算y值,gelu激活函数
# y = torch.gelu(x)
# y = F.gelu(x)
y = nn.GELU()(x)
# 4.绘制函数图像,在第一个子图上
axes[0].plot(x, y)
axes[0].set_title('gelu激活函数')
axes[0].grid()
# 5.绘制导函数图像,在第二个子图上
x = torch.linspace(-10, 10, 1000,requires_grad=True)
# y = torch.gelu(x)
# y = F.gelu(x)
y = nn.GELU()(x)
y.sum().backward() # 方向传播获取导数,x.grad
axes[1].plot(x.detach(), x.grad)
axes[1].set_title('gelu激活函数导数')
axes[1].grid()
plt.show()
...
# 测试
if __name__ == '__main__':
# demo01()
# demo02()
# demo03()
demo04()
# demo05()