使用 NumPy 从头开始实现神经网络

2025年3月17日 | 阅读 12 分钟

神经网络导论

在人工智能(AI)不断发展的格局中,有一个概念经久不衰,并已成为现代机器学习的基石:人工神经网络(ANN)。这些受人脑复杂神经元网络启发的计算模型,在从图像识别到自然语言处理的各种任务中都展现出了惊人的能力。在本文中,我们将踏上一段揭开 ANN 内部运作的旅程,特别关注从零开始实现神经网络为何是一次宝贵的学习经历。

人工神经网络

人工神经网络,通常简称为神经网络,其历史可以追溯到 20 世纪中叶。它们起源的动力来自于创造能够模拟人脑复杂动态过程的机器的愿望。早期先驱,如 Frank Rosenblatt 和 Warren McCulloch,为神经网络奠定了理论基础,直到现代计算的出现,这些概念才得以付诸实践。

本质上,人工神经网络是一种包含互连节点或神经元的计算模型。这些神经元协同工作,处理和转换输入数据,最终产生输出。神经元之间的每个连接都关联着一个权重,该权重决定了连接的强度。神经元将激活函数应用于其输入的加权和,为模型引入非线性。

  • 你可能会问,当 TensorFlow 和 PyTorch 等强大的深度学习库可用时,为什么还要从头开始实现神经网络?
  • 深度学习框架提供了预先构建好的神经网络层和训练技术,使得复杂的任务看起来异常简单。尽管如此,这种便利性往往以理解为代价。
  • 从头开始构建神经网络可以让你精细地控制模型的每个部分。当你遇到错误或意外行为时,你不会一无所知。
  • 深度学习领域正处于持续的进步之中。新的模型、激活函数和优化技术层出不穷。

神经元:计算单元

任何神经网络的核心是神经元。它们是负责处理信息的计算单元。神经元接收输入,执行计算,并产生一个输出。在神经网络中,神经元被组织成层,通常包括输入层、一个或多个隐藏层和输出层。神经元之间的连接,每个连接都有一个权重,使得信息能够流动。

前向传播过程

前向传播过程是信息从输入层流向输出层通过神经网络的机制。它可以概括如下:

输入层:数据被输入到输入层。

加权和:后续层中的每个神经元都会计算其输入的加权和。

激活函数:加权和通过激活函数,引入非线性。

输出:激活函数的输出成为下一层的输入,这个过程重复进行,直到到达输出层。

激活函数:引入非线性

激活函数在神经网络中起着至关重要的作用。它们引入非线性,使神经网络能够学习复杂的模式。两种常见的激活函数是 sigmoid 函数和修正线性单元(ReLU)函数。

Sigmoid 函数:它将输入值压缩到 0 到 1 之间,这可以解释为概率。它在二元分类问题的输出层中很有用。

ReLU 函数:如果输入值为正,它会输出该值,否则输出零。由于其简单性和有效性,ReLU 已成为大多数隐藏层的默认选择。

权重初始化和偏置项

神经网络中的权重对于学习至关重要。权重初始化涉及设置这些权重的初始值。常见的方法包括随机初始化、Xavier/Glorot 初始化和 He 初始化。正确的权重初始化会显著影响训练过程。

偏置项是神经元中的加法常数。它们通过移动激活函数的输出,使神经元能够表示更复杂的功能。每个神经元都有自己的偏置项。

Implementation of neural network from scratch using NumPy

使用 NumPy 实现神经网络

步骤 1:创建数据集

为了让我们的神经网络能够工作,我们需要一个数据集来训练和测试其性能。

生成样本:我们为字母 A、B 和 C 定义了矩阵样本。这些样本作为训练和测试神经网络的输入数据。每个样本都是一个表示相应字母形状的二进制序列。

代码

步骤 2:定义标签

对于监督学习,我们需要标签来指示正确的答案。

标记数据:我们为字母 A、B 和 C 指定了独热编码标签。独热编码是一种以数学方式表示分类数据的技术,其中每个类别对应一个唯一的二进制值。在训练过程中,这些标签允许神经网络学习并关联模式及其对应的字母。

代码

步骤 3:导入库并定义激活函数

在此初始步骤中,我们为神经网络奠定了基础。

导入库:我们导入了基础库,例如用于高效数学运算的 NumPy 和用于数据可视化的 Matplotlib。这些库为我们提供了构建和评估神经网络所需的工具。

定义 Sigmoid 激活函数:我们定义了 sigmoid 激活函数,这是神经网络的一个关键组成部分。sigmoid 函数接收一个输入并将其映射到 0 到 1 之间的值。此函数将非线性引入网络的输出,使其能够模拟数据中的复杂关系。

代码

步骤 4:初始化权重

在此步骤中,我们为神经网络的可学习参数设置了初始条件。

权重:我们创建了一个生成神经网络中神经元之间连接的随机权重的函数。这些权重很重要,因为它们控制数据在训练期间通过网络的路径。随机初始化允许网络从数据中学习并随着时间的推移调整其权重。

代码

步骤 5:定义前向传播神经网络

此步骤定义了我们神经网络的架构和前向传播。

定义神经网络架构:我们确定了神经网络的结构,包括层数和每层的神经元数量。在此示例中,我们有一个输入层、一个隐藏层和一个输出层。feed_forward 函数接收输入,并通过将输入传递到这些层来计算网络的输出。

代码

步骤 6:定义损失函数

在此步骤中,我们确定了神经网络如何评估其性能。

定义损失函数:我们定义了均方误差(MSE)损失函数,它衡量预测输出与实际目标值之间的差异。MSE 评估神经网络的预测与真实数据的匹配程度,提供了一个网络在训练期间旨在最小化的数学度量。

代码

步骤 7:误差的反向传播

在此步骤中,我们为根据预测错误更新神经网络权重建立了机制。

实现反向传播:我们实现了反向传播算法,这是训练神经网络的关键部分。反向传播计算损失相对于网络权重的梯度,使我们能够沿着减少损失的方向调整权重。这个迭代过程对于网络学习和改进其性能至关重要。

这些基本步骤为构建、训练和评估各种任务的神经网络奠定了基础。代码中的后续步骤将基于这些原理来创建一个完整的神经网络并将其应用于特定问题。

代码

步骤 8:训练神经网络

在此步骤中,我们专注于训练神经网络从数据集中学习并随着时间的推移进行改进。

训练函数:我们定义了一个专门的函数来训练神经网络。该函数包括对数据集进行预定数量的 epoch 的迭代。在每个 epoch 中,网络都会进行预测,计算准确性和损失度量,并使用反向传播算法更新其权重。结果是一个不断发展的神经网络,它可以从数据中学习并努力改进其性能。

代码

步骤 9:使用训练模型进行预测

一旦神经网络训练完成,就可以使用它进行预测。

预测函数:我们创建了一个函数,该函数将训练好的模型(例如 trained_w1 和 trained_w2)和输入样本作为输入。该函数根据输出层中的最大输出值预测最可能的字母(A、B 或 C)。此步骤演示了如何将训练好的神经网络应用于新数据进行预测。

代码

步骤 10:为训练初始化权重

要开始训练,我们需要为神经网络设置初始条件。

权重初始化:我们使用随机值初始化权重 w1 和 w2。这些权重决定了数据在网络中的流动方式,并将在学习中发挥关键作用。随机初始化为网络提供了一个从数据中学习的起点。

代码

步骤 11:训练神经网络

数据、标签和权重准备就绪后,我们就继续训练神经网络。

训练过程:我们调用训练函数(例如 train_neural_network)来启动训练过程。神经网络会迭代地更新其权重,监控准确性和损失,并在多个 epoch(训练周期)中调整其参数以提高性能。训练涉及使网络能够进行准确的预测。

代码

输出

Epoch: 1, Accuracy: 33.33%
Epoch: 2, Accuracy: 66.67%
Epoch: 3, Accuracy: 66.67%
Epoch: 4, Accuracy: 66.67%
Epoch: 5, Accuracy: 66.67%
Epoch: 6, Accuracy: 66.67%
Epoch: 7, Accuracy: 66.67%
Epoch: 8, Accuracy: 66.67%
Epoch: 9, Accuracy: 66.67%
Epoch: 10, Accuracy: 66.67%
Epoch: 11, Accuracy: 66.67%
Epoch: 12, Accuracy: 66.67%
Epoch: 13, Accuracy: 66.67%
Epoch: 14, Accuracy: 66.67%
Epoch: 15, Accuracy: 66.67%
Epoch: 16, Accuracy: 66.67%
Epoch: 17, Accuracy: 66.67%
Epoch: 18, Accuracy: 66.67%
Epoch: 19, Accuracy: 66.67%
Epoch: 20, Accuracy: 66.67%
Epoch: 21, Accuracy: 66.67%
Epoch: 22, Accuracy: 66.67%
Epoch: 23, Accuracy: 66.67%
Epoch: 24, Accuracy: 66.67%
Epoch: 25, Accuracy: 66.67%
Epoch: 26, Accuracy: 66.67%
Epoch: 27, Accuracy: 66.67%
Epoch: 28, Accuracy: 66.67%
Epoch: 29, Accuracy: 66.67%
Epoch: 30, Accuracy: 66.67%
Epoch: 31, Accuracy: 66.67%
Epoch: 32, Accuracy: 66.67%
Epoch: 33, Accuracy: 66.67%
Epoch: 34, Accuracy: 66.67%
Epoch: 35, Accuracy: 66.67%
Epoch: 36, Accuracy: 66.67%
Epoch: 37, Accuracy: 66.67%
Epoch: 38, Accuracy: 66.67%
Epoch: 39, Accuracy: 66.67%
Epoch: 40, Accuracy: 66.67%
Epoch: 41, Accuracy: 66.67%
Epoch: 42, Accuracy: 66.67%
Epoch: 43, Accuracy: 66.67%
Epoch: 44, Accuracy: 66.67%
Epoch: 45, Accuracy: 66.67%
Epoch: 46, Accuracy: 66.67%
Epoch: 47, Accuracy: 66.67%
Epoch: 48, Accuracy: 66.67%
Epoch: 49, Accuracy: 66.67%
Epoch: 50, Accuracy: 66.67%
Epoch: 50, Accuracy: 66.67%
Epoch: 51, Accuracy: 100.00%
Epoch: 52, Accuracy: 100.00%
Epoch: 53, Accuracy: 100.00%
Epoch: 54, Accuracy: 100.00%
Epoch: 55, Accuracy: 100.00%
Epoch: 56, Accuracy: 100.00%
Epoch: 57, Accuracy: 100.00%
Epoch: 58, Accuracy: 100.00%
Epoch: 59, Accuracy: 100.00%
Epoch: 60, Accuracy: 100.00%
Epoch: 61, Accuracy: 100.00%
Epoch: 62, Accuracy: 100.00%
Epoch: 63, Accuracy: 100.00%
Epoch: 64, Accuracy: 100.00%
Epoch: 65, Accuracy: 100.00%
Epoch: 66, Accuracy: 100.00%
Epoch: 67, Accuracy: 100.00%
Epoch: 68, Accuracy: 100.00%
Epoch: 69, Accuracy: 100.00%
Epoch: 70, Accuracy: 100.00%
Epoch: 71, Accuracy: 100.00%
Epoch: 72, Accuracy: 100.00%
Epoch: 73, Accuracy: 100.00%
Epoch: 74, Accuracy: 100.00%
Epoch: 75, Accuracy: 100.00%
Epoch: 76, Accuracy: 100.00%
Epoch: 77, Accuracy: 100.00%
Epoch: 78, Accuracy: 100.00%
Epoch: 79, Accuracy: 100.00%
Epoch: 80, Accuracy: 100.00%
Epoch: 81, Accuracy: 100.00%
Epoch: 82, Accuracy: 100.00%
Epoch: 83, Accuracy: 100.00%
Epoch: 84, Accuracy: 100.00%
Epoch: 85, Accuracy: 100.00%
Epoch: 86, Accuracy: 100.00%
Epoch: 87, Accuracy: 100.00%
Epoch: 88, Accuracy: 100.00%
Epoch: 89, Accuracy: 100.00%
Epoch: 90, Accuracy: 100.00%
Epoch: 91, Accuracy: 100.00%
Epoch: 92, Accuracy: 100.00%
Epoch: 93, Accuracy: 100.00%
Epoch: 94, Accuracy: 100.00%
Epoch: 95, Accuracy: 100.00%
Epoch: 96, Accuracy: 100.00%
Epoch: 97, Accuracy: 100.00%
Epoch: 98, Accuracy: 100.00%
Epoch: 99, Accuracy: 100.00%
Epoch: 100, Accuracy: 100.00%

步骤 12:打印训练后的权重

训练后,我们可以检查神经网络学习到的参数。

训练后的权重:我们打印出训练后的权重 trained_w1 和 trained_w2。这些权重代表了神经网络通过训练获得的内部知识。它们决定了网络如何处理输入数据和进行预测。

代码

输出

Trained w1:
 [[ 0.06927115  0.7170005   0.33890342 -0.3954869  -0.21926859]
 [ 0.39601103  0.24753036  0.62160906  0.63111717 -0.05278294]
 [ 0.18818433 -0.37276832 -0.30979065  0.11647701 -0.21607048]
 [-0.46407453 -0.36126427  0.57640027  0.0487203  -0.42107692]
 [-0.46414489  0.46295603  0.46470836 -0.00936389 -0.22831723]
 [-0.26479341 -0.46326617 -0.37647503  0.0966695   0.13562841]
 [ 0.15554735  0.48902724  0.09104169  0.44204394 -0.36605067]
 [-0.03405944  0.04821714  0.29438268 -0.18500043 -0.18425598]
 [ 0.4287369  -0.43919064  0.20607397 -0.39208925  0.01190859]
 [ 0.03697536 -0.2565317   0.49294935 -0.40878913 -0.0538658 ]
 [ 0.09247709 -0.46691897  0.2993611  -0.44151021 -0.11589155]
 [ 0.44711659  0.43345584 -0.14237594 -0.26750368 -0.35513504]
 [-0.40188012  0.15407098  0.20029012 -0.37305893  0.31064633]
 [-0.10865365 -0.19502014 -0.35841558  0.42077795  0.01395011]
 [-0.47082317  0.42016455  0.33085363 -0.36138929 -0.01264705]
 [ 0.35739056 -0.44810259  0.28772079 -0.47210845 -0.01000285]
 [ 0.28254812  0.23523711 -0.33713807  0.26875264 -0.29387359]
 [ 0.42204963  0.00467407 -0.35421835 -0.33040688 -0.00828009]
 [-0.17754328  0.01683268 -0.48541489 -0.44149293 -0.18717054]
 [ 0.09807447  0.22537154  0.18945719  0.43059284 -0.08616275]
 [ 0.44205102 -0.37058663 -0.00536636 -0.16823561 -0.38458703]
 [-0.2124118   0.26696481 -0.22020708  0.30137899  0.45820239]
 [-0.20769434 -0.10460072 -0.30606344 -0.36652411  0.31277555]
 [-0.2070754   0.15518452  0.23822402  0.45175688 -0.20221053]
 [ 0.29657844 -0.44922159 -0.20853836 -0.05554754 -0.05145613]
 [ 0.37466171 -0.26950233 -0.34559416 -0.45765506 -0.29738787]
 [ 0.16390897  0.45549364 -0.29073935 -0.21781543  0.41897006]
 [-0.37249767 -0.35012648 -0.08188436 -0.2364289   0.35996595]
 [ 0.21530958  0.12035867  0.2270742  -0.1902335   0.17511611]
 [-0.38768864  0.0706518  -0.02868765  0.00820532 -0.33814097]]

Trained w2:
 [[-0.51545134 -1.00575607  0.19420588]
 [-0.49013258 -0.9501272  -0.1448687 ]
 [ 0.76347089  1.28824926 -0.10032262]
 [ 0.43072838  0.72880314 -0.02726307]
 [ 0.08850882  0.22300994 -0.47452975]]

步骤 13:使用训练模型进行预测

使用训练好的神经网络,我们可以将其应用于实际场景。

预测:我们使用训练好的模型对新输入样本进行预测。通过将输入传递到网络,我们得到一个预测,表明网络认为该样本对应于哪个字母(A、B 或 C)。此步骤展示了训练有素的模型的实际应用。

代码

输出

Predicted Letter: A