1. 为什么需要激活函数?
1.1 线性变换的局限性
假设我们有一个没有激活函数的神经网络,它实际上只是线性变换的复合:
输出 = Wn × (Wn-1 × (… × (W2 × (W1 × 输入)))) = W' × 输入
无论网络有多少层,没有激活函数的深度神经网络等价于单层线性模型!这意味着:
-
无法学习非线性关系
-
无法解决XOR等问题
-
表达能力极其受限
1.2 激活函数引入非线性
直观理解:激活函数就是神经元的“开关”,决定神经元是否被“激活”(传递信息 -> 神经元的触发电位阈值)
2. 激活函数的核心作用
2.1 数学角度
激活函数 f(x)使网络能够拟合任意复杂的函数:

如果没有 f,这就是简单的线性变换。
2.2 几何角度
|
激活函数 |
几何意义 |
|
无 |
超平面(直线) |
|
Sigmoid |
S形曲线 |
|
ReLU |
折线 |
|
多个ReLU组合 |
可以逼近任意凸函数 |
2.3 神经科学角度
激活函数模拟了生物神经元“全有或全无”特性
-
当输入超过阈值时,神经元“激活”
-
否则保持“静默”
3. 常用激活函数详解
3.1 Sigmoid(S形函数)
数学公式
导数


优缺点分析
优点
输出范围(0, 1), 表示概率方便
光滑,可导
适合二分类问题
缺点
梯度消失问题:x很大或很小时梯度接近于0
输出非零去中心化(影响收敛速度)
指数运算计算较慢
适用场景
-
二分类问题的输出层
-
需要概率输出的场景
-
不推荐用于深层网络的隐藏层
3.2 Tanh(双曲正切函数)
数学公式
导数


优缺点
优点
输出范围(-1, 1),零中心化
收敛速度比 Sigmoid 快
缺点
仍有梯度消失问题
指数运算计算较慢
适用场景
-
适合循环神经网络(RNN、LSTM、GRU)
-
适合隐藏层(比 Sigmoid 好)
-
不适合输出层(除非需要(-1, 1)输出)
3.3 ReLU(线性整流单元)
数学公式
导数


优缺点
优点
计算简单快速(无指数运算)
解决梯度消失问题
稀疏激活(50%神经元被抑制)
缺点
死神经元问题(ReLU死亡)
输出非零中心化
不可微(在x=0处)
什么是“ReLU死亡”?
当某个神经元的输入始终为负,其梯度始终为0,权重无法更新,该神经元“死亡”。
原因:
-
学习率过大
-
负偏置初始化
-
大量负输入数据
适用场景
-
首选的隐藏层激活函数
-
卷积神经网络(CNN)
-
深度前馈网络
3.4 Softmax(归一化指数函数)
数学公式
特点
-
输出总和为1
-
适合多分类问题的输出层
-
每个输出可以理解为概率
适用场景
-
多分类问题的输出层
-
注意力机制(Transformer)
-
不用于隐藏层
3.5 LeakyReLU(带泄露的ReLU)
数学公式

其中
是很小的正数(0.01,0.1)
导数



优缺点分析
优点
解决ReLU死亡问题
保持ReLU的优点
缺点
是超参数,需要调整(通过实验)
负区间有少量梯度流出
适用场景
-
出现大量神经元死亡时
-
需要 ReLU 但担心死亡问题时
3.6 ELU(指数线性单元)
数学公式
导数


优缺点分析
优点
输出接近零中心化
负区间有平滑梯度
缺点
指数运算,计算较慢
超参数
需要通过实验调整
适用场景
-
需要零中心化输出的网络
-
对精度要求较高的场景
3.7 GELU(高斯误差线性单元)
数学公式
-

-
其中
是标准正态分布的累积分布函数(CDF)
近似计算


优缺点分析
优点
平滑且非零中心化
性能优于 ReLU 和 LeakyReLU
Transformer模型的标准选择
缺点
计算复杂度高
适用场景
-
Transformer架构(GPT, BERT, ViT等)
-
最先进的自然语言处理模型
3.7 Swish(自门控激活函数)
数学公式


优缺点分析
优点
平滑、无界、非单调
子门控机制
缺点
计算比ReLU稍慢
适用场景
-
需要更高精度的场景
-
深度残差网络
4. 激活函数选择场景
4.1 根据网络类型选择
|
网络类型 |
隐藏层 |
输出层 |
|
CNN |
ReLU / LeakyReLU |
Sigmoid(二分类) / Softmax(多分类) |
|
MLP |
ReLU / GELU |
Sigmoid / Softmax |
|
RNN / LSTM |
Tanh / ReLU |
Tanh |
|
Transformer |
GELU |
– |
4.2 根据问题类型选择
| 问题类型 | 输出层激活函数 |
| 二分类 | Sigmoid |
| 多分类 | Softmax |
| 回归 | Linear(无激活函数) |
| 多标签分布 | Sigmoid(每个标签独立) |
5. 代码补充
import torch
import torch.nn.functional as F
import matplotlib.pyplot as plt
import numpy as np
# 设置中文字体(如果系统支持)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 创建x值范围
x = torch.linspace(-5, 5, 1000, requires_grad=True)
def sigmoid(x):
"""Sigmoid激活函数"""
return 1 / (1 + torch.exp(-x))
def tanh(x):
"""Tanh激活函数"""
return torch.tanh(x)
def relu(x):
"""ReLU激活函数"""
return F.relu(x)
def leaky_relu(x, negative_slope=0.1):
"""LeakyReLU激活函数"""
return F.leaky_relu(x, negative_slope=negative_slope)
def elu(x, alpha=1.0):
"""ELU激活函数"""
return F.elu(x, alpha=alpha)
def gelu(x):
"""GELU激活函数(GPT/BERT使用)"""
return F.gelu(x)
def swish(x):
"""Swish激活函数(又称 SiLU)"""
return x * sigmoid(x)
def softmax(x, dim=0):
"""Softmax激活函数"""
return F.softmax(x, dim=dim)
def compute_derivative(func, x):
"""计算函数的导数"""
y = func(x)
# 计算梯度
dy = torch.autograd.grad(y.sum(), x, create_graph=True)[0]
return y.detach().numpy(), dy.detach().numpy()
# 定义激活函数及其参数
activations = [
('Sigmoid', sigmoid, {}),
('Tanh', tanh, {}),
('ReLU', relu, {}),
('LeakyReLU', leaky_relu, {'negative_slope': 0.1}),
('ELU', elu, {'alpha': 1.0}),
('GELU', gelu, {}),
('Swish', swish, {}),
# ('Softmax', softmax, {}), # Softmax通常用于多分类输出层,这里单独处理
]
# 创建画布
fig, axes = plt.subplots(4, 4, figsize=(16, 12))
fig.suptitle('深度学习常用激活函数及其导数', fontsize=18, fontweight='bold')
plt.subplots_adjust(hspace=0.4, wspace=0.3)
# 绘制每个激活函数
for idx, (name, func, params) in enumerate(activations):
row = idx // 2
col = (idx % 2) * 2
# 计算函数值和导数
y, dy = compute_derivative(lambda x: func(x, **params), x)
# 绘制函数图像
ax_func = axes[row, col]
ax_func.plot(x.detach().numpy(), y, 'b-', linewidth=2, label=f'{name}(x)')
ax_func.axhline(y=0, color='k', linestyle='–', linewidth=0.5, alpha=0.5)
ax_func.axvline(x=0, color='k', linestyle='–', linewidth=0.5, alpha=0.5)
ax_func.set_title(f'{name} 激活函数', fontsize=12, fontweight='bold')
ax_func.set_xlabel('x', fontsize=10)
ax_func.set_ylabel('f(x)', fontsize=10)
ax_func.grid(True, alpha=0.3)
ax_func.legend(loc='upper left', fontsize=9)
ax_func.set_xlim(-5, 5)
# 绘制导数图像
ax_deriv = axes[row, col + 1]
ax_deriv.plot(x.detach().numpy(), dy, 'r-', linewidth=2, label=f"{name}'(x)")
ax_deriv.axhline(y=0, color='k', linestyle='–', linewidth=0.5, alpha=0.5)
ax_deriv.axvline(x=0, color='k', linestyle='–', linewidth=0.5, alpha=0.5)
ax_deriv.set_title(f'{name} 导数', fontsize=12, fontweight='bold')
ax_deriv.set_xlabel('x', fontsize=10)
ax_deriv.set_ylabel("f'(x)", fontsize=10)
ax_deriv.grid(True, alpha=0.3)
ax_deriv.legend(loc='upper left', fontsize=9)
ax_deriv.set_xlim(-5, 5)
# 隐藏未使用的子图
for i in range(4):
for j in range(4):
if i * 2 + j >= len(activations) * 2:
axes[i, j].set_visible(False)
plt.tight_layout()
plt.savefig('activation_functions.png', dpi=300, bbox_inches='tight')
print("激活函数图像已保存为 'activation_functions.png'")
plt.show()
# 单独绘制 Softmax
fig2, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
fig2.suptitle('Softmax 激活函数', fontsize=16, fontweight='bold')
# 示例输入
x_softmax = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
y_softmax = softmax(x_softmax, dim=1)
# 绘制原始值
ax1.bar(['x1', 'x2', 'x3'], x_softmax[0].detach().numpy(), color='blue', alpha=0.7)
ax1.set_title('原始输入值', fontweight='bold')
ax1.set_ylabel('值')
ax1.set_ylim(0, 4)
ax1.grid(True, alpha=0.3, axis='y')
# 绘制 Softmax 输出
ax2.bar(['p1', 'p2', 'p3'], y_softmax[0].detach().numpy(), color='red', alpha=0.7)
ax2.set_title('Softmax 输出(概率)', fontweight='bold')
ax2.set_ylabel('概率')
ax2.set_ylim(0, 1)
ax2.grid(True, alpha=0.3, axis='y')
ax2.text(0.02, 0.95, f'总和: {y_softmax[0].sum().item():.4f}',
transform=ax2.transAxes, fontsize=10)
plt.tight_layout()
plt.savefig('softmax_function.png', dpi=300, bbox_inches='tight')
print("Softmax 函数图像已保存为 'softmax_function.png'")
plt.show()
print("\\n" + "="*60)
print("激活函数可视化完成!")
print("="*60)
print("\\n各激活函数特点总结:")
print("- Sigmoid: 输出范围(0,1),适合二分类,容易梯度消失")
print("- Tanh: 输出范围(-1,1),零中心化,适合隐藏层")
print("- ReLU: 简单高效,解决梯度消失,但有死神经元问题")
print("- LeakyReLU: ReLU改进,负区间有小斜率,缓解死神经元")
print("- ELU: 负区间指数衰减,输出接近零,但计算较慢")
print("- GELU: 平滑激活函数,GPT/BERT等Transformer模型使用")
print("- Swish: 自门控激活,x*sigmoid(x),在某些任务表现优异")
print("- Softmax: 多分类输出层,将输出转换为概率分布")
6. 总结
|
激活函数 |
核心特点 |
最佳场景 |
|
Sigmoid |
(0, 1)输出,适合概率 |
二分类输出层 |
|
Tanh |
(-1, 1)输出,零中心化 |
RNN / LSTM |
|
ReLU |
简单高效,解决梯度消失 |
CNN隐藏层(首选) |
|
Softmax |
概率分布,总和为1 |
多分类输出层 |
|
LeakyReLU |
解决死神经元 |
有死ReLU问题时 |
|
ELU |
平滑,零中心化 |
高精度场景 |
|
GELU |
Transformer标准选择 |
NLP / Transformer |
|
Swish |
自门控,性能优异 |
深度残差网络 |
















