文章目录
一、Sigmoid 函数简介
Sigmoid 函数(也称为 Logistic 函数)是一个在数学、机器学习(尤其是在逻辑回归和早期神经网络中)广泛使用的函数。它的主要特点是将任意实数输入映射到 (0, 1) 这个开区间内。
1. 数学公式
Sigmoid 函数通常用希腊字母 σ (sigma) 表示,其数学表达式为:
σ ( x ) = 1 1 + e − x σ(x) = \frac{1}{1 + e^{-x}} σ(x)=1+e−x1
其中:
x
是函数的输入(一个实数)。e
是自然对数的底(欧拉数,约等于 2.71828)。
2. 关键特性
(1) 输出范围 (0, 1): 这是 Sigmoid 最重要的特性之一。无论输入
x
x
x 是什么值(正无穷、负无穷或介于两者之间),输出值
σ
(
x
)
σ(x)
σ(x) 总是严格大于 0 且严格小于 1。这使得它非常适合用来表示概率值,或者在二元分类问题中表示属于某个类别的可能性。
(2) S 形曲线: 函数的图形呈 “S” 形。当
x
x
x 趋近于负无穷时,
e
−
x
e^{-x}
e−x 趋近于正无穷,分母变得非常大,
σ
(
x
)
σ(x)
σ(x) 趋近于 0。当
x
x
x 趋近于正无穷时,
e
−
x
e^{-x}
e−x 趋近于 0,分母趋近于 1,
σ
(
x
)
σ(x)
σ(x) 趋近于 1。
(3) 单调递增: 函数在其整个定义域内是严格单调递增的,这意味着输入
x
x
x 越大,输出
σ
(
x
)
σ(x)
σ(x) 也越大。
(4) 中心对称: 函数关于点 (0, 0.5) 中心对称。即
σ
(
0
)
=
1
/
(
1
+
e
0
)
=
1
/
(
1
+
1
)
=
0.5
σ(0) = 1 / (1 + e^0) = 1 / (1 + 1) = 0.5
σ(0)=1/(1+e0)=1/(1+1)=0.5。
(5) 导数易于计算: Sigmoid 函数的导数可以用其自身来表示:
σ
′
(
x
)
=
σ
(
x
)
∗
(
1
−
σ
(
x
)
)
σ'(x) = σ(x) * (1 - σ(x))
σ′(x)=σ(x)∗(1−σ(x))
这个特性在神经网络的反向传播算法中非常有用,因为计算梯度时可以直接利用前向传播计算出的 Sigmoid 值。
3. 应用场景
- 逻辑回归 (Logistic Regression): Sigmoid 函数是逻辑回归模型的核心,用于将线性模型的输出转换为概率。
- 神经网络激活函数: 在早期的神经网络中,Sigmoid 曾被广泛用作隐藏层和输出层的激活函数。
- 输出层: 对于二元分类问题,输出层使用 Sigmoid 可以直接输出概率。
- 隐藏层: 现在在深度神经网络的隐藏层中,Sigmoid 的使用已大大减少,主要因为梯度消失 (Vanishing Gradient) 问题。当输入
x
的绝对值很大时,Sigmoid 的导数趋近于 0,这会导致在反向传播过程中梯度逐层乘以接近 0 的数,使得深层网络的权重更新非常缓慢甚至停滞。ReLU 及其变种(如 Leaky ReLU, ELU)已成为更常用的隐藏层激活函数。
- 概率建模: 任何需要将实数值压缩到 (0, 1) 区间以表示概率或置信度的场景。
缺点 (尤其是在深度学习隐藏层中):
- 梯度消失: 如上所述,饱和区域(输入绝对值大时)梯度接近 0。
- 输出非零中心 (Not Zero-Centered): Sigmoid 的输出恒大于 0。这会导致后续层接收到的输入总是正数,可能在梯度下降过程中引起参数更新的“之”字形抖动(Zigzagging dynamics),降低收敛速度。
- 计算复杂度: 相对于 ReLU 函数 (
max(0, x)
),指数运算e^x
的计算成本稍高。
二、Python 实现
我们可以使用 Python 的 math
库(处理单个数值)或 numpy
库(处理数值或数组/向量/矩阵)来实现 Sigmoid 函数。在机器学习中,通常使用 numpy
因为它能高效地处理向量化运算。
1. 使用 math
库 (适用于单个数值)
import math
def sigmoid_math(x):
"""
计算单个数值的 Sigmoid 值。
Args:
x: 输入的实数。
Returns:
x 的 Sigmoid 值,范围在 (0, 1) 之间。
"""
# 防止 e^(-x) 过大导致 OverflowError (当 x 是非常小的负数时)
# Sigmoid(x) = 1 / (1 + exp(-x)) = exp(x) / (exp(x) + 1)
# 当 x 非常小时,exp(x) 接近 0,Sigmoid(x) 接近 0
# 当 x 非常大时,exp(-x) 接近 0,Sigmoid(x) 接近 1
# 这里直接计算标准形式,对于极端值 math.exp 会处理或抛出异常
try:
result = 1 / (1 + math.exp(-x))
except OverflowError:
# 如果 exp(-x) 溢出,说明 -x 非常大,即 x 是非常小的负数
# 此时 Sigmoid 值接近 0
result = 0.0
return result
# 示例
print(f"Sigmoid(0) = {sigmoid_math(0)}")
print(f"Sigmoid(10) = {sigmoid_math(10)}")
print(f"Sigmoid(-10) = {sigmoid_math(-10)}")
# 一个较大的正数,接近 1
print(f"Sigmoid(100) = {sigmoid_math(100)}")
# 一个较小的负数,接近 0
print(f"Sigmoid(-100) = {sigmoid_math(-100)}")
2. 使用 numpy
库 (适用于数值、列表、数组、矩阵)
这是在机器学习和数据科学中最常用的方式,因为 numpy
的函数可以对整个数组进行元素级 (element-wise) 操作,效率很高。
import numpy as np
def sigmoid_numpy(x):
"""
计算输入 x (可以是数值、列表、numpy 数组等) 的 Sigmoid 值。
Args:
x: 输入,可以是单个数值、列表、元组、numpy 数组等。
Returns:
与 x 相同形状的 numpy 数组,包含每个元素的 Sigmoid 值。
"""
# np.exp() 可以直接处理数组
# 对于非常大的负数 x, -x 很大, np.exp(-x) 可能溢出或得到 inf
# 对于非常大的正数 x, -x 很小, np.exp(-x) 接近 0
# Numpy 通常能较好地处理这些边界情况,例如 exp(很大负数) -> 0, exp(很大正数) -> inf
# 1 / (1 + inf) -> 0. 这不是我们想要的,当 x 很大时,结果应为 1
# Sigmoid(x) = 1 / (1 + exp(-x))
# 为了数值稳定性,可以做一些处理,但通常 numpy 的标准实现足够健壮
# 或者使用 scipy.special.expit(x) 是一个数值上更稳定的实现
# 标准实现:
x = np.array(x) # 确保 x 是 numpy 数组
return 1 / (1 + np.exp(-x))
# 示例
# 单个值
print(f"Sigmoid(0) = {sigmoid_numpy(0)}")
# 列表
input_list = [-10, -1, 0, 1, 10]
print(f"Sigmoid({input_list}) = {sigmoid_numpy(input_list)}")
# Numpy 数组
input_array = np.array([[-2, -0.5], [0.5, 2]])
print(f"Sigmoid(\n{input_array}\n) =\n{sigmoid_numpy(input_array)}")
# 极端值
print(f"Sigmoid(710) approx = {sigmoid_numpy(710)}") # np.exp(-710) 接近 0, 结果接近 1
# print(f"Sigmoid(800) = {sigmoid_numpy(800)}") # np.exp(-800) 可能下溢为 0,结果为 1
print(f"Sigmoid(-710) approx = {sigmoid_numpy(-710)}") # np.exp(710) 接近 inf, 1/(1+inf) -> 0
# print(f"Sigmoid(-800) = {sigmoid_numpy(-800)}") # np.exp(800) 可能溢出为 inf, 结果为 0
3. 使用 scipy.special.expit
(数值稳定)
scipy
库提供了一个专门为 Sigmoid 函数优化的实现,通常在数值上更稳定。
from scipy.special import expit # expit is the logistic sigmoid function
# 示例
print(f"Scipy Sigmoid(0) = {expit(0)}")
print(f"Scipy Sigmoid({input_list}) = {expit(input_list)}")
print(f"Scipy Sigmoid(\n{input_array}\n) =\n{expit(input_array)}")
# 处理极端值更稳健
print(f"Scipy Sigmoid(800) = {expit(800)}")
print(f"Scipy Sigmoid(-800) = {expit(-800)}")
图像绘制代码如下:
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
"""计算 Sigmoid 函数值"""
return expit(x) # 使用 scipy 的 expit 函数
# 1. 生成 x 值范围
# 我们需要一系列 x 值来绘制平滑的曲线
# np.linspace(start, stop, num) 在指定的间隔内返回均匀间隔的数字。
x = np.linspace(-10, 10, 200) # 生成从 -10 到 10 的 200 个点
# 2. 计算对应的 y 值 (Sigmoid 输出)
y = sigmoid(x)
# 3. 使用 matplotlib 绘图
plt.figure(figsize=(8, 5)) # 创建一个图形窗口,可以指定大小
plt.plot(x, y, label='Sigmoid Function', color='blue', linewidth=2) # 绘制曲线
# 4. 添加图形元素,使其更清晰
plt.title('Sigmoid Function: σ(x) = 1 / (1 + e^(-x))') # 图形标题
plt.xlabel('x') # x 轴标签
plt.ylabel('σ(x)') # y 轴标签
# 添加网格线
plt.grid(True, linestyle='--', alpha=0.6)
# 添加关键水平线和垂直线
plt.axhline(0.5, color='red', linestyle=':', linewidth=1, label='y = 0.5') # y=0.5 水平线
plt.axhline(1.0, color='gray', linestyle=':', linewidth=1, label='y = 1.0') # y=1.0 水平线
plt.axhline(0.0, color='gray', linestyle=':', linewidth=1, label='y = 0.0') # y=0.0 水平线
plt.axvline(0, color='gray', linestyle=':', linewidth=1) # x=0 垂直线
# 设置 y 轴范围,更清晰地显示 0 到 1 的区间
plt.ylim(-0.1, 1.1)
# 显示图例 (需要 plot 时指定了 label)
plt.legend()
# 5. 显示图形
plt.show()
三、小结
在实际的机器学习项目中,如果你使用了像 TensorFlow
或 PyTorch
这样的深度学习框架,它们内部都已经内置了高效且数值稳定的 Sigmoid
函数实现,你通常会直接调用框架提供的函数。但理解其原理和基本的 Python
实现仍然很重要。