神经网络设计与Python实现
1. 多层感知机(MLP)概述
多层感知机(MLP)由若干层神经元组成。在MLP示意图中,每个圆圈代表一个神经元,圆圈之间的线表示相连的神经元。一个神经元的激活仅取决于前一层神经元的激活情况,并且会影响下一层每个神经元的激活。例如,在特定示意图中,各层分别包含3、4、3和2个神经元,总共12个神经元就有12个激活值。由于神经元数量可能很多(如数字分类会用到90个),不能给每个神经元都赋予一个字母变量名,所以用字母
a
表示所有激活值,并通过上标和下标进行索引。上标表示层,下标表示该层内的神经元。例如,$a_{2}^{2}$ 表示第二层的第二个神经元的激活值。
2. 神经网络中的数据流
将神经网络作为一个数学函数进行评估,有四个基本步骤,下面从激活值的角度进行概念性介绍,并给出相应公式。神经网络本质上是一个接收输入向量并产生输出向量的函数,中间步骤就是从给定输入得到输出的过程。
-
步骤1:将输入层的激活值设置为输入向量的元素
输入层即最左边的第一层。在图中的网络里,输入层有3个神经元,所以该神经网络可以接收三维向量作为输入。若输入向量为 (0.3, 0.9, 0.5),则可通过设置 $a_{0}^{1}=0.3$,$a_{0}^{2}=0.9$,$a_{0}^{3}=0.5$ 来完成此步骤,这就填充了网络中12个神经元中的3个。
-
步骤2:将下一层的每个激活值计算为输入层所有激活值的函数
这是计算的核心部分。下一层的每个激活值通常是前一层激活值的不同函数。例如,要计算 $a_{1}^{1}$,它是 $a_{0}^{1}$、$a_{0}^{2}$ 和 $a_{0}^{3}$ 的某个函数,可简单写成 $a_{1}^{1}=f(a_{0}^{1}, a_{0}^{2}, a_{0}^{3})$。假设 $f(0.3, 0.9, 0.5)=0.6$,那么在计算中 $a_{1}^{1}$ 的值就为0.6。计算 $a_{2}^{1}$ 时,它同样是 $a_{0}^{1}$、$a_{0}^{2}$ 和 $a_{0}^{3}$ 的函数,但一般是不同的函数,如 $a_{2}^{1}=g(a_{0}^{1}, a_{0}^{2}, a_{0}^{3})$。若 $g(0.3, 0.9, 0.5)=0.1$,则 $a_{2}^{1}$ 的值为0.1。计算完第一层的所有激活值后,就填充了12个激活值中的7个。
-
步骤3:重复此过程,根据前一层的激活值计算后续每一层的激活值
从计算 $a_{1}^{2}$ 开始,它是第一层激活值 $a_{1}^{1}$、$a_{2}^{1}$、$a_{3}^{1}$ 和 $a_{4}^{1}$ 的函数。接着计算 $a_{2}^{2}$ 和 $a_{3}^{2}$,它们有各自的函数。最后计算 $a_{1}^{3}$ 和 $a_{2}^{3}$,它们是第二层激活值的函数。至此,网络中每个神经元都有了激活值。
-
步骤4:返回一个向量,其元素为输出层的激活值
在这个例子中,向量为 (0.2, 0.9),所以将输入向量 (0.3, 0.9, 0.5) 作为神经网络的输入,得到的输出向量为 (0.2, 0.9)。
下面用 mermaid 流程图展示这个过程:
graph TD;
A[输入向量] --> B[设置输入层激活值];
B --> C[计算下一层激活值];
C --> D{是否计算完所有层};
D -- 否 --> C;
D -- 是 --> E[返回输出层激活值];
3. 计算激活值
计算一层的激活值作为前一层激活值的函数时,会使用熟悉的逻辑函数(即逻辑斯蒂函数)。但神经网络中除输入层外有9个神经元,需要跟踪9个不同的函数,且每个逻辑函数还有几个常数来决定其行为,大部分工作就是跟踪这些常数。
以一个具体例子来说,在示例 MLP 中,激活值依赖于输入层的三个激活值 $a_{0}^{1}$、$a_{0}^{2}$ 和 $a_{0}^{3}$。计算 $a_{1}^{1}$ 的函数是将这些输入的线性函数(包含一个常数)传入一个 sigmoid 函数,这里有四个自由参数,暂时命名为 A、B、C 和 D。参数 A 表示 $a_{0}^{1}$ 对 $a_{1}^{1}$ 的影响强度,B 和 C 分别表示 $a_{0}^{2}$ 和 $a_{0}^{3}$ 对 $a_{1}^{1}$ 的影响强度,这些常数称为神经网络的权重,神经网络通用图中的每条线段都对应一个权重。常数 D 不影响连接,而是独立地增加或减少 $a_{1}^{1}$ 的值,它被恰当地命名为神经元的偏置,因为它衡量了在没有任何输入时做出决策的倾向。
为了更规范地表示,需要对这些权重和偏置进行索引,而不是用 A、B、C 和 D 命名。权重写成 $w_{ij}^{l}$ 的形式,其中 $l$ 是连接右侧的层,$i$ 是 $l - 1$ 层中前一个神经元的索引,$j$ 是 $l$ 层中目标神经元的索引。例如,影响第一层第一个神经元的基于零层第一个神经元值的权重表示为 $w_{11}^{1}$,连接第三层第二个神经元和前一层第一个神经元的权重是 $w_{21}^{3}$。偏置对应于神经元,而不是神经元对,所以每个神经元有一个偏置,用 $b_{j}^{l}$ 表示第 $l$ 层第 $j$ 个神经元的偏置。这样,$a_{1}^{1}$ 的公式可以写成 $a_{1}^{1}=\sigma(w_{11}^{1}a_{0}^{1}+w_{12}^{1}a_{0}^{2}+w_{13}^{1}a_{0}^{3}+b_{1}^{1})$,$a_{2}^{3}$ 的公式为 $a_{2}^{3}=\sigma(w_{21}^{3}a_{1}^{1}+w_{22}^{3}a_{1}^{2}+w_{23}^{3}a_{1}^{3}+w_{24}^{3}a_{1}^{4}+b_{2}^{3})$。
4. 用矩阵表示法计算激活值
以第二层为例,其三个激活值的公式如下:
$a_{2}^{1}=\sigma(w_{11}^{2}a_{1}^{1}+w_{12}^{2}a_{1}^{2}+w_{13}^{2}a_{1}^{3}+w_{14}^{2}a_{1}^{4}+b_{1}^{2})$
$a_{2}^{2}=\sigma(w_{21}^{2}a_{1}^{1}+w_{22}^{2}a_{1}^{2}+w_{23}^{2}a_{1}^{3}+w_{24}^{2}a_{1}^{4}+b_{2}^{2})$
$a_{2}^{3}=\sigma(w_{31}^{2}a_{1}^{1}+w_{32}^{2}a_{1}^{2}+w_{33}^{2}a_{1}^{3}+w_{34}^{2}a_{1}^{4}+b_{3}^{2})$
为了简化表示,将 sigmoid 函数内的量命名为 $z_{1}^{2}$、$z_{2}^{2}$ 和 $z_{3}^{2}$,即:
$z_{1}^{2}=w_{11}^{2}a_{1}^{1}+w_{12}^{2}a_{1}^{2}+w_{13}^{2}a_{1}^{3}+w_{14}^{2}a_{1}^{4}+b_{1}^{2}$
$z_{2}^{2}=w_{21}^{2}a_{1}^{1}+w_{22}^{2}a_{1}^{2}+w_{23}^{2}a_{1}^{3}+w_{24}^{2}a_{1}^{4}+b_{2}^{2}$
$z_{3}^{2}=w_{31}^{2}a_{1}^{1}+w_{32}^{2}a_{1}^{2}+w_{33}^{2}a_{1}^{3}+w_{34}^{2}a_{1}^{4}+b_{3}^{2}$
这些 $z$ 值的公式是前一层激活值的线性组合加上一个常数,可以用矩阵向量表示法来写。将三个方程写成向量形式,并将偏置作为向量和提取出来:
$\begin{pmatrix}z_{1}^{2}\z_{2}^{2}\z_{3}^{2}\end{pmatrix}=\begin{pmatrix}w_{11}^{2}a_{1}^{1}+w_{12}^{2}a_{1}^{2}+w_{13}^{2}a_{1}^{3}+w_{14}^{2}a_{1}^{4}+b_{1}^{2}\w_{21}^{2}a_{1}^{1}+w_{22}^{2}a_{1}^{2}+w_{23}^{2}a_{1}^{3}+w_{24}^{2}a_{1}^{4}+b_{2}^{2}\w_{31}^{2}a_{1}^{1}+w_{32}^{2}a_{1}^{2}+w_{33}^{2}a_{1}^{3}+w_{34}^{2}a_{1}^{4}+b_{3}^{2}\end{pmatrix}=\begin{pmatrix}w_{11}^{2}a_{1}^{1}+w_{12}^{2}a_{1}^{2}+w_{13}^{2}a_{1}^{3}+w_{14}^{2}a_{1}^{4}\w_{21}^{2}a_{1}^{1}+w_{22}^{2}a_{1}^{2}+w_{23}^{2}a_{1}^{3}+w_{24}^{2}a_{1}^{4}\w_{31}^{2}a_{1}^{1}+w_{32}^{2}a_{1}^{2}+w_{33}^{2}a_{1}^{3}+w_{34}^{2}a_{1}^{4}\end{pmatrix}+\begin{pmatrix}b_{1}^{2}\b_{2}^{2}\b_{3}^{2}\end{pmatrix}=\begin{pmatrix}w_{11}^{2}&w_{12}^{2}&w_{13}^{2}&w_{14}^{2}\w_{21}^{2}&w_{22}^{2}&w_{23}^{2}&w_{24}^{2}\w_{31}^{2}&w_{32}^{2}&w_{33}^{2}&w_{34}^{2}\end{pmatrix}\begin{pmatrix}a_{1}^{1}\a_{1}^{2}\a_{1}^{3}\a_{1}^{4}\end{pmatrix}+\begin{pmatrix}b_{1}^{2}\b_{2}^{2}\b_{3}^{2}\end{pmatrix}$
第二层的激活值通过对结果向量的每个元素应用 $\sigma$ 函数得到。这只是一种符号简化,但将 $w_{ij}^{l}$ 和 $b_{j}^{l}$ 提取到各自的矩阵中在心理上很有用。这些矩阵和向量定义了神经网络本身,而激活值 $a_{j}^{l}$ 只是评估过程中的中间步骤。
5. 相关练习
-
练习16.4
:激活值 $a_{2}^{3}$ 代表哪个神经元和层?在给定图像中该激活值是多少?
答案:上标表示层,下标表示层内的神经元,所以 $a_{2}^{3}$ 对应第三层的第二个神经元。在图像中,其激活值为0.9。 -
练习16.5
:如果神经网络的第5层有10个神经元,第6层有12个神经元,那么第5层和第6层的神经元之间总共有多少个连接?
答案:第5层的每个神经元都与第6层的每个神经元相连,所以总共有 $10\times12 = 120$ 个连接。
下面用表格总结这些练习:
| 练习编号 | 问题描述 | 答案 |
| ---- | ---- | ---- |
| 16.4 | 激活值 $a_{2}^{3}$ 代表哪个神经元和层,在图像中的值是多少? | 第三层的第二个神经元,0.9 |
| 16.5 | 第5层10个神经元,第6层12个神经元,两层间总连接数? | 120 |
6. 更多练习及解答
-
练习16.6
:假设我们有一个有12层的MLP,连接第4层的第三个神经元和第5层的第七个神经元的权重 $w_{ij}^{l}$ 的索引 $l$、$i$ 和 $j$ 分别是什么?
答案:$l$ 是连接的目标层,所以这里 $l = 5$。索引 $i$ 和 $j$ 分别指 $l$ 层和 $l - 1$ 层的神经元,所以 $i = 7$,$j = 3$,该权重标记为 $w_{73}^{5}$。 -
练习16.7
:在整个章节使用的网络中,权重 $w_{31}^{3}$ 在哪里?
答案:不存在这样的权重。因为这将连接到第三层(输出层)的第三个神经元,但该层只有两个神经元。 -
练习16.8
:在本章节的神经网络中,用第二层的激活值、权重和偏置表示 $a_{1}^{3}$ 的公式是什么?
答案:前一层的激活值是 $a_{1}^{2}$、$a_{2}^{2}$ 和 $a_{2}^{3}$,连接它们到 $a_{1}^{3}$ 的权重是 $w_{11}^{3}$、$w_{12}^{3}$ 和 $w_{13}^{3}$。$a_{1}^{3}$ 的偏置表示为 $b_{1}^{3}$,所以公式为 $a_{1}^{3}=\sigma(w_{11}^{3}a_{1}^{2}+w_{12}^{3}a_{2}^{2}+w_{13}^{3}a_{2}^{3}+b_{1}^{3})$。 -
练习16.9 - 小项目
:编写一个Python函数
sketch_mlp(*layer_sizes),它接受神经网络的各层大小作为参数,并输出一个类似于本章节使用的示意图,显示所有带标签的神经元并用直线绘制它们之间的连接。调用sketch_mlp(3,4,3,2)应该生成我们在整个章节中用来表示神经网络的示例图。
答案:具体实现可参考相关源代码。
以下是这些练习的表格总结:
| 练习编号 | 问题描述 | 答案 |
| ---- | ---- | ---- |
| 16.6 | 12层MLP中,连接第4层第3个神经元和第5层第7个神经元的权重索引 | $l = 5$,$i = 7$,$j = 3$,权重为 $w_{73}^{5}$ |
| 16.7 | 网络中权重 $w_{31}^{3}$ 的位置 | 不存在,输出层只有2个神经元 |
| 16.8 | 用第二层激活值、权重和偏置表示 $a_{1}^{3}$ 的公式 | $a_{1}^{3}=\sigma(w_{11}^{3}a_{1}^{2}+w_{12}^{3}a_{2}^{2}+w_{13}^{3}a_{2}^{3}+b_{1}^{3})$ |
| 16.9 | 编写
sketch_mlp(*layer_sizes)
函数 | 参考源代码 |
7. Python中构建多层感知机(MLP)
在Python中实现一个MLP类,该类存储权重和偏置(最初随机生成),并提供一个
evaluate
方法,该方法接受一个64维的输入向量并返回一个10维的输出向量。
7.1 MLP类的实现
为了让类表示一个MLP,需要指定层数和每层的神经元数量。构造函数接受一个数字列表,表示每层的神经元数量。评估MLP所需的数据是输入层之后每一层的权重和偏置,可以将权重存储为矩阵(NumPy数组),偏置存储为向量(同样是NumPy数组)。开始时,所有的权重和偏置都使用随机值,训练网络时再逐渐用更有意义的值替换这些随机值。
以下是MLP类的Python代码实现:
import numpy as np
class MLP():
def __init__(self, layer_sizes):
self.layer_sizes = layer_sizes
self.weights = [
np.random.rand(n, m)
for m, n in zip(layer_sizes[:-1],
layer_sizes[1:])
]
self.biases = [np.random.rand(n)
for n in layer_sizes[1:]]
在上述代码中:
-
__init__
方法初始化MLP,接受一个表示每层神经元数量的列表
layer_sizes
。
-
self.weights
是一个列表,存储每层的权重矩阵,权重矩阵是随机生成的 $n\times m$ 矩阵,其中 $m$ 和 $n$ 是相邻层的神经元数量。
-
self.biases
是一个列表,存储每层的偏置向量,偏置向量是随机生成的,每个元素对应一个神经元。
可以通过以下代码验证一个两层MLP的权重矩阵和偏置向量的维度:
nn = MLP([2, 3])
print(nn.weights)
print(nn.biases)
这将输出一个3x2的权重矩阵和一个3维的偏置向量,且元素都是随机的。
7.2 确定网络结构
图像分类问题需要一个64维的输入向量和一个10维的输出向量。本章节采用一个64神经元的输入层、一个10神经元的输出层和一个中间的16神经元层。选择合适的层数和层大小以让神经网络在给定任务上表现良好,这既需要技术也需要经验。对于本章节的目的,这种结构足以得到一个良好的预测模型。可以将神经网络初始化为
MLP([64, 16, 10])
,它比之前绘制的任何网络都要大。
下面用mermaid流程图展示MLP类的初始化过程:
graph TD;
A[输入层大小列表] --> B[初始化层大小属性];
B --> C[生成权重矩阵];
C --> D[生成偏置向量];
D --> E[完成MLP初始化];
8. 总结
通过以上内容,我们详细了解了多层感知机(MLP)的设计和实现。从MLP的基本结构和数据流开始,学习了如何计算神经元的激活值,包括使用逻辑函数和矩阵表示法简化计算。通过一系列练习加深了对概念的理解,最后在Python中实现了MLP类。虽然初始时权重和偏置是随机选择的,网络可能表现不佳,但有了神经网络的结构后,就可以调整权重和偏置以提高其预测能力,这将是进一步研究的方向。
整个过程可以用以下步骤总结:
1. 理解MLP的结构和神经元激活值的表示。
2. 掌握神经网络中数据流的四个基本步骤。
3. 学会使用逻辑函数计算激活值,并使用矩阵表示法简化计算。
4. 通过练习巩固所学知识。
5. 在Python中实现MLP类并初始化网络结构。
超级会员免费看
1205

被折叠的 条评论
为什么被折叠?



