在之前的文章中,我们已经学习了线性回归、MLP 和 CNN,并简单提到了激活函数的概念。激活函数影响着模型的表达能力、梯度的传播以及训练的效率和效果。因此,为了让大家对激活函数有更系统的认识,本篇文章将专注于详细讲解常见的激活函数。
文章目录
1 激活函数的引入
神经网络由使用权重、偏置和激活函数的神经元组成。在学习过程中,这些权重和偏置根据输出误差进行更新,这个过程称为反向传播。激活函数通过提供梯度支持反向传播,从而实现权重和偏置的更新。
没有非线性,即使是深度网络也只能解决简单的线性可分问题。激活函数使神经网络能够建模复杂数据分布并解决高级深度学习任务。添加非线性激活函数可以增加灵活性,帮助网络学习复杂和抽象的模式。
1.1 简单的数学证明
假设一个非常简单的神经网络,只有两层,其中第一层有两个神经元,最后一层有一个神经元,输入大小为2。输入为
x
1
x_1
x1和
x
2
x_2
x2。第一层的权重为
w
11
,
w
12
,
w
21
,
w
22
w_{11}, w_{12}, w_{21}, w_{22}
w11,w12,w21,w22。我们没有使用激活函数,因此第一层神经元的输出为:
o
1
=
w
11
x
1
+
w
12
x
2
o_1 = w_{11}x_1 + w_{12}x_2
o1=w11x1+w12x2
o
2
=
w
21
x
1
+
w
22
x
2
o_2 = w_{21}x_1 + w_{22}x_2
o2=w21x1+w22x2
接着计算最后一层的输出,最后一层的权重为
z
1
z_1
z1和
z
2
z_2
z2:
o
u
t
=
z
1
o
1
+
z
2
o
2
out = z_1o_1 + z_2o_2
out=z1o1+z2o2,将
o
1
o_1
o1和
o
2
o_2
o2代入,得到:
o
u
t
=
z
1
(
w
11
x
1
+
w
12
x
2
)
+
z
2
(
w
21
x
1
+
w
22
x
2
)
=
(
z
1
w
11
+
z
2
w
21
)
x
1
+
(
z
1
w
12
+
z
2
w
22
)
x
2
out = z_1(w_{11}x_1 + w_{12}x_2) + z_2(w_{21}x_1 + w_{22}x_2) = (z_1w_{11} + z_2w_{21})x_1 + (z_1w_{12} + z_2w_{22})x_2
out=z1(w11x1+w12x2)+z2(w21x1+w22x2)=(z1w11+z2w21)x1+(z1w12+z2w22)x2
这意味着如果我们创建一个只有一层的神经网络,其权重为 z 1 w 11 + z 2 w 21 z_1w_{11} + z_2w_{21} z1w11+z2w21和 z 2 w 22 + z 1 w 12 z_2w_{22} + z_1w_{12} z2w22+z1w12,它将等效于我们的两层神经网络。
结论:没有非线性时,多层神经网络的计算能力等同于单层神经网络。
1.2 激活函数的作用
激活函数是神经网络中不可或缺的一部分,它的主要作用是引入非线性,使神经网络能够学习和表示复杂的非线性关系。以下是激活函数的主要作用和意义:
-
引入非线性
如果没有激活函数,神经网络的每一层输出都是输入的线性组合,这使得整个网络等效于一个单层线性模型。激活函数引入非线性,使得网络可以拟合复杂的函数,并解决非线性可分问题。 -
增加表达能力
激活函数允许神经网络学习复杂的数据分布,例如分类、回归和其他高维数据的模式。这是因为它们在每一层中允许特征以非线性方式进行组合。 -
控制梯度传播
激活函数可以控制反向传播过程中梯度的大小,避免梯度消失或爆炸问题。例如,ReLU函数通过将负值置零,有效地减少了梯度消失问题。 -
生物学启发
激活函数的设计受到生物学神经元的启发,例如Sigmoid
和Tanh
函数,它们模仿了生物神经元的激活过程,在特定阈值范围内“激活”输出。 -
适应不同任务需求
不同的激活函数适合不同的任务。例如:Sigmoid
和Tanh
适合输出在固定范围内的任务,例如概率预测。ReLU
及其变种(如Leaky ReLU
和ELU
)适合深度神经网络,因其计算简单且缓解了梯度消失问题。Softmax
常用于分类任务的输出层,用于将结果转化为概率分布。
结论:激活函数的引入不仅使神经网络具备了非线性建模能力,还大幅提升了模型的表达能力和训练效果。选择合适的激活函数是构建高效神经网络的关键之一。
2 常见激活函数
2.1 线性激活函数
线性激活函数类似于定义为 y = x y = x y=x的直线。无论神经网络包含多少层,如果使用线性激活函数,输出只是输入的线性组合。
- 输出范围从 ( − ∞ (-\infty (−∞到 + ∞ ) +\infty) +∞)。
- 线性激活函数仅在输出层使用。
- 在所有层使用线性激活会限制网络学习复杂模式的能力。
线性激活函数对于特定任务有用,但必须与非线性函数结合,以增强神经网络的学习和预测能力。
2.2 非线性激活函数
2.2.1 Sigmoid函数
Sigmoid激活函数以"S"形为特征,有平滑和连续的输出,数学定义为:
A = 1 1 + e − x A = \frac{1}{1+e^{-x}} A=1+e−x1
- 允许神经网络处理和建模线性方程无法解决的复杂模式。
- 输出范围在 0 0 0到 1 1 1之间,因此适用于二分类问题。
- 在 x x x值位于 − 2 -2 −2和 2 2 2之间时函数梯度较大。这种敏感性意味着输入 x x x的微小变化可能导致输出 y y y的显著变化,这在训练过程中非常关键。
2.2.2 Tanh函数
Tanh函数或双曲正切函数是Sigmoid的变种,使其在
y
y
y轴上延伸。数学定义为:
f
(
x
)
=
tanh
(
x
)
=
e
x
−
e
−
x
e
x
+
e
−
x
f(x) = \tanh(x) = \frac{e^{x} - e^{-x}}{e^{x} + e^{-x}}
f(x)=tanh(x)=ex+e−xex−e−x
或者可以通过Sigmoid函数表达为:
tanh
(
x
)
=
2
×
s
i
g
m
o
i
d
(
2
x
)
−
1
\tanh(x) = 2 \times sigmoid(2x) - 1
tanh(x)=2×sigmoid(2x)−1
- 值域范围:输出值从 − 1 -1 −1到 + 1 +1 +1。
- 非线性:使得能够建模复杂的数据模式。
- 隐藏层使用:由于其零中心输出,常用于隐藏层,便于后续层的学习。
2.2.3 ReLU函数
ReLU激活函数定义为:
A
(
x
)
=
max
(
0
,
x
)
A(x) = \max(0, x)
A(x)=max(0,x)
这意味着如果输入
x
x
x为正,ReLU返回
x
x
x;如果输入为负,则返回
0
0
0。
- 值域范围: [ 0 , + ∞ ) [0, +\infty) [0,+∞),表示该函数仅输出非负值。
- 性质:它是一个非线性激活函数,允许神经网络学习复杂模式,并使反向传播更高效。
- 相较其他激活函数的优势:ReLU比Tanh和Sigmoid计算量更低,因为它只涉及简单的数学操作。同时,在一个时刻只有少量神经元被激活,使网络稀疏化,计算更高效。
2.3 指数线性单元
指数线性单元(Exponential Linear Units
, ELU
) 是一类在神经网络中广泛使用的激活函数,之所以被称为“指数线性单元”,是因为它们的定义同时结合了指数增长和线性行为的特点。这些函数根据输入值的范围,在某些区域表现出指数形式的增长,而在其他区域接近线性增长。这种设计能够平衡神经网络在不同输入区间的梯度流动问题,同时具备高效的计算性能。
2.3.1 Softmax函数
Softmax函数用于处理多分类问题。它将神经网络的原始输出分数转换为概率,通过将每个类别的输出值压缩到
0
0
0到
1
1
1的范围内,并确保所有概率之和等于
1
1
1。
f
(
x
i
)
=
e
x
i
∑
j
e
x
j
f(x_i) = \frac{e^{x_i}}{\sum_j e^{x_j}}
f(xi)=∑jexjexi
- Softmax是一个非线性激活函数。
- Softmax函数确保为每个类别分配一个概率,帮助识别输入属于哪个类别。
注意:网上可能看到softmax函数如下,类似于sigmoid函数,但这是错的。这种图常被误用来描述Softmax函数中的单一值的变化关系。实际上,Softmax是一种针对输入向量的归一化操作,不是针对单个值的简单映射。因此,这种图并不是严格意义上的Softmax函数,而更像是单个值在Softmax操作下的比例变化曲线。
2.3.2 SoftPlus函数
Softplus函数的数学定义为:
A ( x ) = log ( 1 + e x ) A(x) = \log(1 + e^x) A(x)=log(1+ex)
此公式确保输出始终为正且在所有点可微分,这是相较于传统ReLU函数的一个优势。
- 性质:Softplus函数是非线性的。
- 值域:函数输出值在 ( 0 , ∞ ) (0, \infty) (0,∞)范围内,与ReLU相似,但没有ReLU的硬性零阈值。
- 平滑性:Softplus是一个平滑、连续的函数,避免了ReLU中的尖锐不连续性,这在优化过程中可能会导致问题。
3 其它激活函数
这张图片展示了不同激活函数的曲线(包括上面介绍的):
- 蓝线:激活函数的值(输出值)随着输入 x x x变化的曲线,即激活函数的原始定义曲线 f ( x ) f(x) f(x)。
- 红色线:激活函数的导数(梯度)随着输入 x x x变化的曲线,即激活函数的导数 f ′ ( x ) f'(x) f′(x)。
激活函数 | 描述 |
---|---|
none | 恒等函数:输出等于输入,无任何非线性效果。 |
exp | 指数函数:输出为 e x e^x ex,用于表示指数增长的关系。 |
gelu | 高斯误差线性单元:平滑近似ReLU,输出为 x ⋅ Φ ( x ) x \cdot \Phi(x) x⋅Φ(x),其中 Φ ( x ) \Phi(x) Φ(x)是标准正态分布的累积分布函数。 |
hard_sigmoid | 简化版Sigmoid函数:输出在 [ 0 , 1 ] [0, 1] [0,1]之间,计算简单但保留非线性特性。 |
linear | 线性激活:与输入一致,无非线性,仅用于特定场景如回归。 |
lrelu | 带泄漏的ReLU:负值部分有小梯度,定义为 f ( x ) = x f(x) = x f(x)=x( x > 0 x > 0 x>0)或 f ( x ) = α x f(x) = \alpha x f(x)=αx( x ≤ 0 x \leq 0 x≤0)。 |
relu | 修正线性单元:输出为 x x x( x > 0 x > 0 x>0)或 0 0 0( x ≤ 0 x \leq 0 x≤0),广泛用于深度学习。 |
selu | 缩放指数线性单元:自动归一化输入流,输出为带缩放因子的指数线性函数。 |
sigmoid | S型函数:将输入压缩到 [ 0 , 1 ] [0, 1] [0,1]之间,适合二分类任务。 |
softmax | 多分类归一化函数:将向量转换为概率分布,输出总和为1。 |
softplus | 平滑近似ReLU:定义为 f ( x ) = log ( 1 + e x ) f(x) = \log(1 + e^x) f(x)=log(1+ex),避免了ReLU的尖锐转折。 |
softsign | 平滑版符号函数:输出为$f(x) = \frac{x}{1 + |
swish | 自适应激活函数:定义为 f ( x ) = x ⋅ s i g m o i d ( x ) f(x) = x \cdot sigmoid(x) f(x)=x⋅sigmoid(x),平滑并具有较好表现。 |
tanh | 双曲正切函数:将输入压缩到 [ − 1 , 1 ] [-1, 1] [−1,1]之间,适合零中心数据。 |
trelu | 带阈值的ReLU:在某些区间内具有自定义输出阈值。 |
elu | 指数线性单元:负值部分指数衰减,正值部分线性增长。 |
4 代码中激活函数的定义
4.1 TensorFlow
参数:
激活函数 | 数学定义 | 描述 |
---|---|---|
tf.nn.relu | f ( x ) = max ( 0 , x ) f(x) = \max(0, x) f(x)=max(0,x) | 修正线性单元(ReLU),对正值线性输出,负值为0。 |
tf.nn.leaky_relu | f ( x ) = { x , x > 0 α x , x ≤ 0 f(x) = \begin{cases} x, & x > 0 \\ \alpha x, & x \leq 0 \end{cases} f(x)={x,αx,x>0x≤0 | Leaky ReLU,引入负值区域的斜率 α \alpha α,缓解ReLU的“神经元死亡”问题。 |
tf.nn.elu | f ( x ) = { x , x > 0 α ( e x − 1 ) , x ≤ 0 f(x) = \begin{cases} x, & x > 0 \\ \alpha (e^x - 1), & x \leq 0 \end{cases} f(x)={x,α(ex−1),x>0x≤0 | 指数线性单元(ELU),负值区域指数增长,正值区域线性输出。 |
tf.nn.sigmoid | f ( x ) = 1 1 + e − x f(x) = \frac{1}{1 + e^{-x}} f(x)=1+e−x1 | 将输入映射到 ( 0 , 1 ) (0, 1) (0,1)区间,常用于二分类问题。 |
tf.nn.tanh | f ( x ) = e x − e − x e x + e − x f(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} f(x)=ex+e−xex−e−x | 将输入映射到 ( − 1 , 1 ) (-1, 1) (−1,1)区间,适合零中心化数据。 |
tf.nn.softplus | f ( x ) = log ( 1 + e x ) f(x) = \log(1 + e^x) f(x)=log(1+ex) | 平滑近似ReLU,避免ReLU的尖锐转折。 |
tf.nn.softsign | f ( x ) = x 1 + ∣ x ∣ f(x) = \frac{x}{1 + |x|} f(x)=1+∣x∣x | 平滑版符号函数,输出在 ( − 1 , 1 ) (-1, 1) (−1,1)之间变化。 |
tf.nn.swish | f ( x ) = x ⋅ s i g m o i d ( x ) f(x) = x \cdot sigmoid(x) f(x)=x⋅sigmoid(x) | 自适应激活函数,平滑且对负值具有一定作用。 |
tf.nn.softmax | f ( x i ) = e x i ∑ j e x j f(x_i) = \frac{e^{x_i}}{\sum_{j} e^{x_j}} f(xi)=∑jexjexi | 多分类归一化函数,将向量转化为概率分布,总和为1。 |
代码:
import tensorflow as tf
# 构建一个简单的神经网络
model = tf.keras.Sequential([
tf.keras.layers.Dense(128, activation='relu', input_shape=(784,)), # ReLU 激活函数
tf.keras.layers.Dense(64, activation=tf.nn.tanh), # Tanh 激活函数
tf.keras.layers.Dense(10, activation=tf.nn.softmax) # Softmax 激活函数,用于多分类任务
])
# 编译模型
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 打印模型结构
model.summary()
自定义:
def custom_activation(x):
return tf.nn.relu(x) - 0.1 # 自定义激活函数:ReLU 减去 0.1
model = tf.keras.Sequential([
tf.keras.layers.Dense(128, activation=custom_activation, input_shape=(784,)),
tf.keras.layers.Dense(10, activation='softmax')
])
4.2 Keras
参数:
激活函数 | 数学定义 | 描述 |
---|---|---|
ReLU | f ( x ) = max ( 0 , x ) f(x) = \max(0, x) f(x)=max(0,x) | ReLU 激活函数,隐藏层的默认选择。 |
LeakyReLU | f ( x ) = { x , x > 0 α x , x ≤ 0 f(x) = \begin{cases} x, & x > 0 \\ \alpha x, & x \leq 0 \end{cases} f(x)={x,αx,x>0x≤0 | Leaky ReLU,适合缓解梯度为零问题。 |
ELU | f ( x ) = { x , x > 0 α ( e x − 1 ) , x ≤ 0 f(x) = \begin{cases} x, & x > 0 \\ \alpha (e^x - 1), & x \leq 0 \end{cases} f(x)={x,α(ex−1),x>0x≤0 | ELU激活函数,能更好地学习负值区域的特征。 |
Sigmoid | f ( x ) = 1 1 + e − x f(x) = \frac{1}{1 + e^{-x}} f(x)=1+e−x1 | Sigmoid 激活函数,适合二分类问题。 |
Tanh | f ( x ) = e x − e − x e x + e − x f(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} f(x)=ex+e−xex−e−x | Tanh激活函数,输出范围为 ( − 1 , 1 ) (-1, 1) (−1,1)。 |
Softplus | f ( x ) = log ( 1 + e x ) f(x) = \log(1 + e^x) f(x)=log(1+ex) | 平滑ReLU,用于减少训练不稳定性。 |
Softsign | f ( x ) = x 1 + ∣ x ∣ f(x) = \frac{x}{1 + |x|} f(x)=1+∣x∣x | 类似于 Tanh,但计算更简单。 |
Swish | f ( x ) = x ⋅ s i g m o i d ( x ) f(x) = x \cdot sigmoid(x) f(x)=x⋅sigmoid(x) | Swish 激活函数,表现优于ReLU,适合深度网络。 |
Softmax | f ( x i ) = e x i ∑ j e x j f(x_i) = \frac{e^{x_i}}{\sum_{j} e^{x_j}} f(xi)=∑jexjexi | Softmax用于多分类任务的输出层,生成概率分布。 |
HardSigmoid | f ( x ) = max ( 0 , min ( 1 , 0.2 x + 0.5 ) ) f(x) = \max(0, \min(1, 0.2x + 0.5)) f(x)=max(0,min(1,0.2x+0.5)) | 简化版Sigmoid,计算速度快,但较粗略。 |
代码:
from keras.models import Sequential
from keras.layers import Dense
# 构建一个简单的神经网络
model = Sequential([
Dense(128, activation='relu', input_dim=784), # ReLU 激活函数
Dense(64, activation='sigmoid'), # Sigmoid 激活函数
Dense(10, activation='softmax') # Softmax 激活函数,用于多分类任务
])
# 编译模型
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
# 打印模型结构
model.summary()
自定义:
from keras import backend as K
def custom_activation(x):
return K.maximum(x, 0) - 0.1 # 自定义激活函数
model = Sequential([
Dense(128, activation=custom_activation, input_dim=784),
Dense(10, activation='softmax')
])
4.3 PyTorch
参数:
激活函数 | 数学定义 | 描述 |
---|---|---|
torch.nn.ReLU | f ( x ) = max ( 0 , x ) f(x) = \max(0, x) f(x)=max(0,x) | ReLU激活函数,隐藏层的常见选择,计算高效。 |
torch.nn.LeakyReLU | f ( x ) = { x , x > 0 α x , x ≤ 0 f(x) = \begin{cases} x, & x > 0 \\ \alpha x, & x \leq 0 \end{cases} f(x)={x,αx,x>0x≤0 | Leaky ReLU,用于缓解ReLU的“神经元死亡”问题。 |
torch.nn.ELU | f ( x ) = { x , x > 0 α ( e x − 1 ) , x ≤ 0 f(x) = \begin{cases} x, & x > 0 \\ \alpha (e^x - 1), & x \leq 0 \end{cases} f(x)={x,α(ex−1),x>0x≤0 | ELU激活函数,能够在负值区域更好地学习特征。 |
torch.nn.Sigmoid | f ( x ) = 1 1 + e − x f(x) = \frac{1}{1 + e^{-x}} f(x)=1+e−x1 | Sigmoid激活函数,常用于二分类任务的输出层。 |
torch.nn.Tanh | f ( x ) = e x − e − x e x + e − x f(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} f(x)=ex+e−xex−e−x | 双曲正切函数(Tanh),将输入压缩到 ( − 1 , 1 ) (-1, 1) (−1,1)范围。 |
torch.nn.Softplus | f ( x ) = log ( 1 + e x ) f(x) = \log(1 + e^x) f(x)=log(1+ex) | Softplus激活函数,平滑近似ReLU,适合需要平滑梯度的场景。 |
torch.nn.Softsign | f ( x ) = x 1 + ∣ x ∣ f(x) = \frac{x}{1 + |x|} f(x)=1+∣x∣x | Softsign函数,类似Tanh,输出范围为 ( − 1 , 1 ) (-1, 1) (−1,1),但计算更简单。 |
torch.nn.Swish | f ( x ) = x ⋅ s i g m o i d ( x ) f(x) = x \cdot sigmoid(x) f(x)=x⋅sigmoid(x) | Swish激活函数,适合深度网络,表现通常优于ReLU。 |
torch.nn.Softmax | f ( x i ) = e x i ∑ j e x j f(x_i) = \frac{e^{x_i}}{\sum_{j} e^{x_j}} f(xi)=∑jexjexi | Softmax函数,用于多分类任务,将logits转换为概率分布。 |
torch.nn.Hardshrink | f ( x ) = { x , ∣ x ∣ > λ 0 , ∣ x ∣ ≤ λ f(x) = \begin{cases} x, & |x| > \lambda \\ 0, & |x| \leq \lambda \end{cases} f(x)={x,0,∣x∣>λ∣x∣≤λ | Hardshrink函数,带阈值的稀疏激活函数,适用于特定场景。 |
torch.nn.Softshrink | f ( x ) = { x − λ , x > λ x + λ , x < − λ 0 , ∣ x ∣ ≤ λ f(x) = \begin{cases} x - \lambda, & x > \lambda \\ x + \lambda, & x < -\lambda \\ 0, & |x| \leq \lambda \end{cases} f(x)=⎩ ⎨ ⎧x−λ,x+λ,0,x>λx<−λ∣x∣≤λ | Softshrink函数,类似Hardshrink,但更平滑。 |
torch.nn.Hardtanh | f ( x ) = max ( min ( x , max_val ) , min_val ) f(x) = \max(\min(x, \text{max\_val}), \text{min\_val}) f(x)=max(min(x,max_val),min_val) | 截断版Tanh,输出限制在指定范围内,计算效率高。 |
torch.nn.GELU | f ( x ) = x ⋅ Φ ( x ) f(x) = x \cdot \Phi(x) f(x)=x⋅Φ(x),其中 Φ ( x ) \Phi(x) Φ(x)是标准正态分布的累积分布函数 | GELU(高斯误差线性单元),平滑近似ReLU,常用于Transformer模型。 |
torch.nn.LogSoftmax | f ( x i ) = log ( e x i ∑ j e x j ) f(x_i) = \log\left(\frac{e^{x_i}}{\sum_{j} e^{x_j}}\right) f(xi)=log(∑jexjexi) | LogSoftmax,将Softmax后取对数,用于数值稳定性较高的交叉熵计算。 |
torch.nn.Silu | f ( x ) = x ⋅ s i g m o i d ( x ) f(x) = x \cdot sigmoid(x) f(x)=x⋅sigmoid(x) | SiLU(Swish的别名),表现与Swish相同。 |
代码
1.使用torch.nn
模块:通过 torch.nn
提供的模块化激活函数,可以在定义网络时直接嵌入激活函数:
import torch
import torch.nn as nn
# 定义一个简单的神经网络
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.model = nn.Sequential(
nn.Linear(784, 128), # 输入层到隐藏层
nn.ReLU(), # 使用 ReLU 激活函数
nn.Linear(128, 64),
nn.Tanh(), # 使用 Tanh 激活函数
nn.Linear(64, 10),
nn.Softmax(dim=1) # 使用 Softmax 激活函数
)
def forward(self, x):
return self.model(x)
# 初始化网络
net = SimpleNet()
# 打印网络结构
print(net)
2.使用 torch.nn.functional
函数式直接调用激活函数:
import torch
import torch.nn as nn
import torch.nn.functional as F
# 定义一个简单的神经网络
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(784, 128) # 输入层到隐藏层1
self.fc2 = nn.Linear(128, 64) # 隐藏层1到隐藏层2
self.fc3 = nn.Linear(64, 10) # 隐藏层2到输出层
def forward(self, x):
x = F.relu(self.fc1(x)) # 使用 ReLU 激活函数
x = F.tanh(self.fc2(x)) # 使用 Tanh 激活函数
x = F.softmax(self.fc3(x), dim=1) # 使用 Softmax 激活函数
return x
# 初始化网络
net = SimpleNet()
# 打印网络结构
print(net)
3.使用自定义激活函数
import torch
import torch.nn as nn
# 自定义激活函数
def custom_activation(x):
return torch.maximum(x, torch.tensor(0.1)) # 类似于 ReLU,但不让输出低于0.1
# 定义网络
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(784, 128)
self.fc2 = nn.Linear(128, 64)
self.fc3 = nn.Linear(64, 10)
def forward(self, x):
x = custom_activation(self.fc1(x)) # 使用自定义激活函数
x = torch.sigmoid(self.fc2(x)) # 使用 Sigmoid 激活函数
x = torch.softmax(self.fc3(x), dim=1) # 使用 Softmax 激活函数
return x
# 初始化网络
net = SimpleNet()
# 打印网络结构
print(net)
5 总结
激活函数的选择对神经网络性能有直接影响,主要体现在以下几个方面:
-
收敛速度:像ReLU这样的函数通过避免梯度消失问题,实现更快的训练,而Sigmoid和Tanh在深度网络中可能会减缓收敛。
-
梯度流动:激活函数如ReLU可以确保更好的梯度流动,从而有效地帮助更深层学习。相比之下,Sigmoid可能会导致梯度较小,从而阻碍深层网络的学习。
-
模型复杂性:像Softmax这样的激活函数允许模型处理复杂的多分类问题,而更简单的激活函数如ReLU或Leaky ReLU则用于基础层。
激活函数是神经网络的核心部分,使其能够捕获数据中的非线性关系。从经典的Sigmoid和Tanh到现代变体如ReLU和Swish,每种激活函数在不同类型的神经网络中都有其特定的用途。理解它们的行为并根据模型需求选择合适的激活函数是关键。