Python NumPy 在神经网络中的矩阵运算与激活函数详解
文章目录
- Python NumPy 在神经网络中的矩阵运算与激活函数详解
- 一 矩阵乘法回顾
- 1 定义矩阵
- 2 矩阵乘法计算
- 二 NumPy 矩阵乘法
- 三 神经网络的分类与回归
- 1 回归问题
- 2 分类问题
- 1)定义 `sigmoid` 函数
- 2)定义学生数据
- 3)初始化模型权重
- 4)计算输出
- 5)计算结果并判断是否及格
- 6)全部代码
- 四 多层神经网络前向
- 1 噪声数据模拟不确定性
- 2 定义简单的神经网络层
- 五 numpy 实现激活函数
- 六 激活函数使用场景
- 1 分类
- 2 回归
- 划重点
- 七 完整代码示例
- 八 源码地址
本文结合 Python 中的 NumPy 库,深入讲解了神经网络中的矩阵运算及其在前向传播中的应用。通过详细的代码示例,展示如何利用 NumPy 实现矩阵乘法,并应用于神经网络的回归和分类问题。此外,文章还介绍了常见的激活函数(如 Sigmoid、ReLU、Tanh)的定义及其在神经网络中的作用,帮助提升模型的非线性拟合能力。最后,归纳了激活函数在不同任务中的最佳应用场景,助力读者高效构建深度学习模型。
导入依赖包
import numpy as np
import matplotlib.pyplot as plt
一 矩阵乘法回顾
在矩阵乘法中,左矩阵的列数必须与右矩阵的行数相等。让我们来看一个具体的例子,计算一个 4 × 3
的矩阵 A 与一个 3 × 2
的矩阵 B 的乘积。
1 定义矩阵
假设矩阵 A 和 B 如下所示
A = [ 1 0 2 − 1 3 1 0 1 1 2 3 0 ] A = \begin{bmatrix} 1 & 0 & 2 \\ -1 & 3 & 1 \\ 0 & 1 & 1 \\ 2 & 3 & 0 \end{bmatrix} A= 1−10203132110
B = [ 3 1 2 1 1 0 ] B = \begin{bmatrix} 3 & 1 \\ 2 & 1 \\ 1 & 0 \end{bmatrix} B= 321110
2 矩阵乘法计算
结果矩阵 C = AB 的维度是 4 × 2
,每个元素 c 𝑖 𝑗 是 A 的第 𝑖 行与 B 的第 𝑗 列的点积
C = A B = [ ( 1 × 3 + 0 × 2 + 2 × 1 ) ( 1 × 1 + 0 × 1 + 2 × 0 ) ( − 1 × 3 + 3 × 2 + 1 × 1 ) ( − 1 × 1 + 3 × 1 + 1 × 0 ) ( 0 × 3 + 1 × 2 + 1 × 1 ) ( 0 × 1 + 1 × 1 + 1 × 0 ) ( 2 × 3 + 3 × 2 + 0 × 1 ) ( 2 × 1 + 3 × 1 + 0 × 0 ) ] C = AB = \begin{bmatrix} (1 \times 3 + 0 \times 2 + 2 \times 1) & (1 \times 1 + 0 \times 1 + 2 \times 0) \\ (-1 \times 3 + 3 \times 2 + 1 \times 1) & (-1 \times 1 + 3 \times 1 + 1 \times 0) \\ (0 \times 3 + 1 \times 2 + 1 \times 1) & (0 \times 1 + 1 \times 1 + 1 \times 0) \\ (2 \times 3 + 3 \times 2 + 0 \times 1) & (2 \times 1 + 3 \times 1 + 0 \times 0) \end{bmatrix} C=AB= (1×3+0×2+2×1)(−1×3+3×2+1×1)(0×3+1×2+1×1)(2×3+3×2+0×1)(1×1+0×1+2×0)(−1×1+3×1+1×0)(0×1+1×1+1×0)(2×1+3×1+0×0)
计算这些元素的具体值
C = [ 5 1 4 2 3 1 12 5 ] C = \begin{bmatrix} 5 & 1 \\ 4 & 2 \\ 3 & 1 \\ 12 & 5 \end{bmatrix} C= 543121215
这样,我们就得到了两个矩阵的乘积,这在许多领域如物理、工程和计算机科学中都是非常重要的操作。
二 NumPy 矩阵乘法
import numpy as np
data = np.random.rand(4, 3)
weights = np.random.rand(3, 2)
output = np.dot(data, weights)print("data shape:", data.shape)
print("weights shape:", weights.shape)
print("output shape:", output.shape)
三 神经网络的分类与回归
在进行前向传播(计算最终结果)的过程中,首先需要确定所处理的问题是回归问题(预测具体数值)还是分类问题(预测类别),因为这两者有着不同的运算需求。先来讨论回归问题:回归相对简单,例如,使用模型来预测一个学生的期末考试成绩。
1 回归问题
student = np.array([[1,2,3]])
model = np.random.rand(3, 1)
score = np.dot(student, model)
print(score)
这里显示的 score
是模型预测的分数。需要注意的是,这个 student
数据是虚构的,它包含三个特征,可能代表前几次的考试成绩。model
内部存储了权重等参数,而 score
则是根据这些参数计算得到的预测结果。
2 分类问题
简单的线性模型(单层神经网络),通过学习学生的特征来预测他们是否会及格。
这段代码首先定义了一个 sigmoid
函数,然后使用这个函数在一个简单的神经网络模型中对学生数据进行分类,以判断学生是否及格。下面是逐行解释:
1)定义 sigmoid
函数
def sigmoid(x):return 1 / (1 + np.exp(-x))
这个函数计算 sigmoid 激活函数的值。Sigmoid 函数常用于二分类问题中,输出值在 0 和 1 之间,可以被解释为概率。
Sigmoid 函数的公式是:
sigmoid ( x ) = 1 1 + e − x \text{sigmoid}(x) = \frac{1}{1 + e^{-x}} sigmoid(x)=1+e−x1
这里,np.exp(-x)
用于计算 e-x。
2)定义学生数据
student = np.array([[0.1, 0.2, -0.3]])
这行代码创建了一个 NumPy 数组,表示单个学生的特征向量。这里的特征有三个(例如可能代表了学生的三个不同方面的评分)。
3)初始化模型权重
model = np.random.rand(3, 1)
这行代码生成了一个 3x1 的随机值矩阵,代表模型的权重。这些权重将与学生的特征进行点积,以生成预测。
4)计算输出
output = np.dot(student, model)
使用 NumPy 的 dot
函数,计算学生特征向量和模型权重的点积。结果 output
是一个数值,表示网络的原始输出。
5)计算结果并判断是否及格
result = sigmoid(output)
if result < 0.5:level = "不及格"
else:level = "及格"
print(level)
这几行代码首先通过 sigmoid 函数将原始输出转换成概率(即 result
),然后检查这个概率是否小于 0.5。如果小于 0.5,认为学生不及格,否则认为及格。
6)全部代码
student = np.array([[0.1, 0.2, -0.3]])model = np.random.rand(3, 1)output = np.dot(student, model)# 用 level 表示是否及格result = sigmoid(output)print(result)if result < 0.5:level = "不及格"else:level = "及格"print(level)def sigmoid(x):return 1 / (1 + np.exp(-x))
四 多层神经网络前向
前向传播是指在神经网络中,输入数据通过网络层传递,每一层对数据进行处理,直到输出层产生最终结果的过程。在这个过程中,网络利用已有的权重和偏置值来计算输出。
1 噪声数据模拟不确定性
目前只考虑了权重而未引入偏置,权重和偏置均扮演着参数的角色。下面用神经网络的思路构建模型。如下
import matplotlib.pyplot as plt
import numpy as npdef draw_scatter(x, y):# 使用 matplotlib 的 scatter 方法来绘制散点图# x.ravel() 和 y.ravel() 将 x 和 y 的二维数组转换为一维数组,适合作为散点图的输入plt.scatter(x.ravel(), y.ravel())# 显示图表plt.show()# 数据生成部分
# x 是从 -1 到 1 之间均匀分布的 10 个点,[:, None] 将一维数组转换为二维数组,形状为 [10, 1]
x = np.linspace(-1, 1, 10)[:, None]
# y 是生成的噪声数据,基于 x 的值并添加以 0 为均值,0.2 为标准差的正态分布噪声
y = np.random.normal(loc=0, scale=0.2, size=[10, 1]) + x# 调用函数 draw_scatter 来绘制 x 和 y 的散点图
draw_scatter(x, y)
一次的运行结果
噪声数据 y 值在 x 值的基础上有一定的随机波动,模拟现实世界数据的不确定性。
2 定义简单的神经网络层
定义了一个简单的神经网络层,并使用该网络层来构建一个两层的简单模型,然后对一组数据进行前向传播计算。
# 定义一个函数来创建神经网络层
def layer(in_dim, out_dim):# 生成权重数组,权重初始值来自均值为0,标准差为0.1的正态分布weights = np.random.normal(loc=0, scale=0.1, size=[in_dim, out_dim])# 生成偏置数组,每个偏置的初始值为0.1bias = np.full([1, out_dim], 0.1)# 返回包含权重和偏置的字典return {"w": weights, "b": bias}# 创建第一层网络,输入维度为1,输出维度为3
l1 = layer(1, 3)
# 创建第二层网络,输入维度为3,输出维度为1
l2 = layer(3, 1)# 使用输入数据x进行第一层的前向传播计算
o = x.dot(l1["w"]) + l1["b"] # 矩阵乘法和偏置相加
print("第一层出来后的 shape:", o.shape) # 打印第一层输出的数据形状# 使用第一层的输出作为第二层的输入,进行第二层的前向传播计算
o = o.dot(l2["w"]) + l2["b"] # 矩阵乘法和偏置相加
print("第二层出来后的 shape:", o.shape) # 打印第二层输出的数据形状print("output:", o) # 打印最终输出结果# 使用自定义的draw_scatter函数,以x为横坐标,最终输出o为纵坐标绘制散点图
draw_scatter(x, o)
一次的运行结果
神经网络模型尚未经过训练,没啥意义。但是可以更深刻地理解网络层的设置、前向传播的流程、以及神经网络中权重和偏置的作用。
五 numpy 实现激活函数
numpy 中的激活函数,加入激活函数,增强模型的非线性拟合能力,提高神经网络性能,提升模型解决实际问题的能力。
# 定义ReLU激活函数,这是一个非线性函数,通常用于增加模型的表达能力
def relu(x):# np.maximum用于比较两个数组并返回各个位置的最大值,这里用于实现ReLU:f(x) = max(0, x)return np.maximum(0, x)# 定义双曲正切激活函数,它输出范围为[-1, 1],用于处理输入数据的非线性变换
def tanh(x):# np.tanh计算输入数组x的双曲正切值return np.tanh(x)# 使用第一层网络的权重和偏置计算第一层的输出
o = x.dot(l1["w"]) + l1["b"]# 在第一层的输出上应用ReLU激活函数,提高网络处理非线性问题的能力
o = relu(o)# 使用第二层网络的权重和偏置计算第二层的输出
o = o.dot(l2["w"]) + l2["b"]# 打印第二层输出的形状,通常用于调试和理解输出数据的维度
print(o.shape)# 使用自定义的draw_scatter函数,以x为横坐标,最终输出o为纵坐标绘制散点图
draw_scatter(x, o)
一次的运行结果
六 激活函数使用场景
不同的激活函数适合不同的任务类型,下面是它们的分类及推荐使用场景。
1 分类
分类问题通常输出的是离散类别的概率值,常用的激活函数有以下几种:
激活函数 | 适用类型 | 说明 |
---|---|---|
Sigmoid | 二分类 | Sigmoid 将输出限制在 (0, 1) 之间,适合用于 二分类 问题,输出的是概率。 |
Tanh | 分类问题(不常用) | 虽然可以用于分类,但 Tanh 常用于隐藏层,输出零中心化 (-1, 1),在实际分类问题中使用较少。 |
Softmax | 多分类 | Softmax 将输出转换为概率分布,适合 多分类 问题的最后一层,输出多个类别的概率。 |
2 回归
回归问题的输出通常是连续数值,常用的激活函数是:
激活函数 | 适用类型 | 说明 |
---|---|---|
ReLU | 回归问题 | ReLU 输出正数且不限制上限,适合大部分 回归问题,因为它不会像 Sigmoid 和 Tanh 那样将输出压缩。 |
Linear(线性激活函数) | 回归问题 | 在回归问题中,输出层常使用线性激活函数(不做任何变换)以输出连续值。 |
Tanh | 回归问题(不常用) | Tanh 的输出范围为 (-1, 1),有时也可用于回归问题,但由于其输出范围有限,在回归问题中不常使用。 |
划重点
-
分类问题:
- Sigmoid:用于二分类问题的输出层。
- Softmax:用于多分类问题的输出层。
- ReLU/Tanh:常用于 隐藏层,ReLU 更常见。
-
回归问题:
- ReLU:常用于隐藏层和输出层。
- Linear(线性激活函数):输出层通常直接使用线性激活(即不进行任何激活)。
对于回归问题,最终输出层通常使用线性激活,而隐藏层可以使用 ReLU 等激活函数。
七 完整代码示例
# This is a sample Python script.# Press ⌃R to execute it or replace it with your code.
# Press Double ⇧ to search everywhere for classes, files, tool windows, actions, and settings.import numpy as np
import matplotlib.pyplot as pltdef relu(x):return np.maximum(0, x)def tanh(x):return np.tanh(x)# 定义一个函数来创建神经网络层
def layer(in_dim, out_dim):# 生成权重数组,权重初始值来自均值为0,标准差为0.1的正态分布weights = np.random.normal(loc=0, scale=0.1, size=[in_dim, out_dim])# 生成偏置数组,每个偏置的初始值为0.1bias = np.full([1, out_dim], 0.1)# 返回包含权重和偏置的字典return {"w": weights, "b": bias}def draw_scatter(x, y):# 使用 matplotlib 的 scatter 方法来绘制散点图# x.ravel() 和 y.ravel() 将 x 和 y 的二维数组转换为一维数组,适合作为散点图的输入plt.scatter(x.ravel(), y.ravel())# 显示图表plt.show()def sigmoid(x):return 1 / (1 + np.exp(-x))def print_hi(name):# Use a breakpoint in the code line below to debug your script.print(f'Hi, {name}') # Press ⌘F8 to toggle the breakpoint.# 回顾矩阵运算# 矩阵的点乘data = np.random.rand(4, 3)weights = np.random.rand(3, 2)output = np.dot(data, weights)print("data shape:", data.shape)print("weights shape:", weights.shape)print("output shape:", output.shape)# 4 * 3 的矩阵点乘一个 3 * 2 的矩阵,结果会是 4 * 2 的矩阵。# 神经网络的分类与回归# 分类student = np.array([[1, 2, 3]])model = np.random.rand(3, 1)score = np.dot(student, model)print(score)# 回归student = np.array([[0.1, 0.2, -0.3]])model = np.random.rand(3, 1)output = np.dot(student, model)# 用 level 表示是否及格result = sigmoid(output)print(result)if result < 0.5:level = "不及格"else:level = "及格"print(level)# 多层神经网络前向# 数据生成部分# x 是从 -1 到 1 之间均匀分布的 10 个点,[:, None] 将一维数组转换为二维数组,形状为 [10, 1]x = np.linspace(-1, 1, 10)[:, None]# y 是生成的噪声数据,基于 x 的值并添加以 0 为均值,0.2 为标准差的正态分布噪声y = np.random.normal(loc=0, scale=0.2, size=[10, 1]) + x# 调用函数 draw_scatter 来绘制 x 和 y 的散点图# draw_scatter(x, y)# 创建第一层网络,输入维度为1,输出维度为3l1 = layer(1, 3)# 创建第二层网络,输入维度为3,输出维度为1l2 = layer(3, 1)# 使用输入数据x进行第一层的前向传播计算o = x.dot(l1["w"]) + l1["b"] # 矩阵乘法和偏置相加print("第一层出来后的 shape:", o.shape) # 打印第一层输出的数据形状# 使用第一层的输出作为第二层的输入,进行第二层的前向传播计算o = o.dot(l2["w"]) + l2["b"] # 矩阵乘法和偏置相加print("第二层出来后的 shape:", o.shape) # 打印第二层输出的数据形状print("output:", o) # 打印最终输出结果# 使用自定义的draw_scatter函数,以x为横坐标,最终输出o为纵坐标绘制散点图# draw_scatter(x, o)# 第一层o = x.dot(l1["w"]) + l1["b"]# 可以在这里添加激活函数,增强非线性拟合能力o = relu(o)# 第二层o = o.dot(l2["w"]) + l2["b"]print(o.shape)draw_scatter(x, o)# Press the green button in the gutter to run the script.
if __name__ == '__main__':print_hi('神经网络-矩阵运算')# See PyCharm help at https://www.jetbrains.com/help/pycharm/
复制粘贴并覆盖到你的 main.py 中运行,运行结果如下。
Hi, 神经网络-矩阵运算
data shape: (4, 3)
weights shape: (3, 2)
output shape: (4, 2)
[[2.95119131]]
[[0.50209837]]
及格
第一层出来后的 shape: (10, 3)
第二层出来后的 shape: (10, 1)
output: [[0.09653124][0.0974829 ][0.09843456][0.09938623][0.10033789][0.10128955][0.10224122][0.10319288][0.10414454][0.10509621]]
(10, 1)
八 源码地址
代码地址:
国内看 Gitee 之 numpy/神经网络-矩阵运算.py
国外看 GitHub 之 numpy/神经网络-矩阵运算.py
引用 莫烦 Python