首页 / 文章 / 神经网络详解:它们是什么以及如何用 Python 构建一个
← 返回
IT技术

神经网络详解:它们是什么以及如何用 Python 构建一个

✍️ zhirenhun 📅 2026/8/23 👁 144 阅读 ⏱ 60 分钟
神经网络详解:它们是什么以及如何用 Python 构建一个

你是否曾好奇过,计算机如何识别手写数字、预测一封邮件是否为垃圾邮件、推荐视频,或理解一句话?

许多现代 AI 系统依赖于一种称为 神经网络 的东西。

现在,这个名称可能会让它们听起来比实际要复杂得多。你可能会认为,要构建一个需要高级微积分、一台巨型计算机以及数千行代码。

你不需要。

在最基本的层面上,神经网络是一个数学模型,它接收一些数字作为输入,对这些数字进行计算,做出预测,检查预测与正确答案之间的偏差,然后调整自身,以便下次能做得稍微好一点。

在本教程中,我们将使用 Python 和 NumPy 亲手构建一个。

以下是我们将涵盖的内容:

21. 前向传播

  • 22. 计算损失

  • 23. 代码中的反向传播

  • 24. 更新权重

  • 25. 完整的 NumPy 神经网络

  • 26. 测试网络

  • 27. 为什么需要隐藏层?

  • 28. 在更大的神经网络中会发生什么?

  • 29. 我们必须从零开始构建神经网络吗?

  • 30. 使用 PyTorch 构建相同的网络

  • 31. 使用 PyTorch 训练网络

  • 32. NumPy 与 PyTorch

  • 33. 什么是深度学习?

  • 34. 神经网络用于何处?

  • 35. 一张图看完整流程

  • 36. 需要记住的最重要概念

  • 37. 接下来应该学什么?

  • 最终要点

  • 我们将从一个单一的人工神经元开始,逐步构建完整的神经网络。到最后,你将了解权重和偏置是什么,激活函数的作用,网络如何从错误中学习,反向传播和梯度下降到底意味着什么,以及这些部分如何组合在一起。

    你不需要了解高级机器学习就能跟上。一些基本的 Python 和代数知识会有所帮助,但我会在讲解过程中解释重要的数学概念。

    1. 什么是神经网络?

    让我们从一个简单的例子开始。

    想象我们希望电脑能够预测一个学生是否会通过考试。

    我们可以给电脑提供以下信息:

    例如:

    Study Hours = 5 Practice Questions = 80 Previous Score = 82

    我们还知道学生实际是否通过:

    Passed = 1

    看到许多这样的例子后,我们希望计算机能学习到一种模式。

    学习时间更长的学生可能表现更好。之前的考试成绩可能很有用。做练习题也可能有帮助。

    与其自己编写所有这些规则,不如把例子交给神经网络,让它自行学习其中的关系。

    基本思想如下:

    关于神经网络如何工作的直观概念

    预测结果可能类似于:0.92

    如果我们在预测及格的概率,可以将其理解为大约有92%的及格机会。

    重要的是,我们并没有告诉网络……

    "学习时间很重要,之前的成绩稍微更重要一些。"

    相反,网络会学习到称为权重的数字,这些数字决定了不同输入对其预测的影响程度。

    2. 为什么它们被称为神经网络?

    名称来源于生物大脑。

    你的大脑包含神经元,它们接收信号、处理信息,并将信号传递给其他神经元。

    人工神经网络并不是人工大脑。它们的工作方式并不完全像生物神经元。但将许多简单处理单元连接起来的总体思想启发了这个名称。

    一个非常简化的人工神经元看起来像这样:

    通过神经网络的输入和输出

    神经元接收数字,执行一些数学运算,并输出另一个数字。

    神经网络是通过将许多人工神经元连接在一起构成的。

    3. 神经网络的三个主要部分

    一个简单的神经网络可以分为三种类型的层:

    1. 输入层

    2. 隐藏层(们)

    3. 输出层

    让我们逐一查看。

    输入层

    输入层包含我们提供给网络的信息。

    以我们的学生例子为例,我们可能有三个输入:

    Input 1 = Study Hours
    Input 2 = Practice Questions
    Input 3 = Previous Score
    

    因此,一个学生的输入可能如下所示:

    [5, 80, 82]
    

    网络并不一定明白这些数字代表“学习时长”或“考试得分”。在网络的数学部分,它们仅仅是数字。

    这是一个需要牢记的重要概念:

    神经网络处理的是数字。

    图像、文本、音频以及其他信息在被神经网络处理之前,最终都必须转换为数字形式。

    隐藏层

    输入层之后是隐藏层。

    网络可能如下所示:

    图像展示了数据如何从输入层流向隐藏层,然后到达输出层

    隐藏层包含对输入进行计算的神经元。

    网络可以具有一个隐藏层或多个隐藏层。

    当网络拥有许多层时,我们通常称其为深度神经网络

    输出层

    输出层产生最终结果。

    对于简单的是/否问题,我们可能将答案表示为:

    0 = No
    1 = Yes
    

    例如:

    0.12 → probably No
    0.91 → probably Yes
    

    对于包含多个类别的问题,输出可能包含多个数字:

    Cat  = 0.05
    Dog  = 0.90
    Bird = 0.05
    

    最大的值与 "Dog" 关联,因此模型会预测为 Dog。

    4. 神经元是什么?

    现在让我们放大来看一个神经元。

    假设我们的神经元接收到三个输入:

    x₁
    x₂
    x₃
    

    每个输入都有对应的权重

    w₁
    w₂
    w₃
    

    神经元将每个输入乘以其权重,然后将结果相加。

    它还会加上一个称为 偏置 的项。

    方程如下:

    z = x₁w₁ + x₂w₂ + x₃w₃ + b
    

    别担心,这个等式看起来吓人。

    其实它基本上就是:

    input × weight
    +
    input × weight
    +
    input × weight
    +
    bias
    

    让我们使用实际数字。

    假设:

    x₁ = 2
    x₂ = 3
    x₃ = 4
    
    w₁ = 0.5
    w₂ = 0.2
    w₃ = 0.8
    
    b = 1
    

    然后:

    z = (2 × 0.5) + (3 × 0.2) + (4 × 0.8) + 1
    

    计算每个部分:

    2 × 0.5 = 1.0
    3 × 0.2 = 0.6
    4 × 0.8 = 3.2
    

    现在将它们添加:

    z = 1.0 + 0.6 + 3.2 + 1
    z = 5.8
    

    神经元已经产生了 5.8

    但我们还没有完成。

    5. 什么是权重?

    权重控制输入对神经元的影响强度。

    假设我们有:

    x = 5
    

    如果重量是:

    w = 2
    

    然后:

    x × w = 5 × 2
          = 10
    

    但是如果重量是:

    w = 0.1
    

    然后:

    x × w = 5 × 0.1
          = 0.5
    

    相同的输入由于权重的变化而产生了截然不同的结果。

    可以把权重想象成音量旋钮。

    较大的正权重会使输入产生更强的正向影响;接近零的权重则使输入影响甚微;负权重则会把结果推向相反的方向。

    网络在训练过程中会学习这些权重。

    6. 什么是偏置?

    偏置是加到神经元计算中的另一个数值。

    如果没有偏置,我们将得到:

    z = x₁w₁ + x₂w₂ + x₃w₃
    

    带有偏置:

    z = x₁w₁ + x₂w₂ + x₃w₃ + b
    

    为什么要再加一个数字?因为这能让神经元的计算更灵活。

    可以把它想象成调整神经元计算的起点。

    在训练过程中,网络学习偏置的方式与学习权重相同。

    所以当你看到:

    weights + bias
    

    您正在查看神经网络在学习过程中可以调节的一些参数。

    7. 为什么需要激活函数?

    此时,我们的神经元可以计算加权和:

    z = x₁w₁ + x₂w₂ + ... + b
    

    但神经网络需要学习的关系比简单的加权和更复杂。

    这就是 激活函数 发挥作用的地方。激活函数会取神经元计算得到的值并进行变换。

    一种常见的激活函数是 ReLU。ReLU 代表 修正线性单元

    它的公式是:

    ReLU(x) = max(0, x)
    

    简单来说:

    例如:

    ReLU(-5) = 0
    ReLU(-2) = 0
    ReLU(0)  = 0
    ReLU(3)  = 3
    ReLU(10) = 10
    

    在Python中:

    def relu(x):
        return max(0, x)
    

    使用NumPy数组时,我们可以使用:

    def relu(x):
        return np.maximum(0, x)
    

    激活函数很重要,因为它们使得多层神经网络能够学习更复杂的模式。

    8. 用 Python 构建我们的第一个神经元

    让我们把数学转化为 Python 代码。

    首先,导入 NumPy:

    import numpy as np
    

    NumPy 为我们提供了处理数字、数组、向量和矩阵的工具。

    现在让我们创建输入:

    x = np.array([2, 3, 4])
    

    这会创建一个包含三个值的数组:

    [2, 3, 4]
    

    现在创建权重:

    weights = np.array([0.5, 0.2, 0.8])
    

    每个输入都有一个权重:

    x₁ = 2    w₁ = 0.5
    x₂ = 3    w₂ = 0.2
    x₃ = 4    w₃ = 0.8
    

    接下来,创建偏置:

    bias = 1
    

    接下来,我们计算加权和:

    z = np.dot(x, weights) + bias
    

    np.dot() 执行我们之前描述的乘法-加法运算。

    在这种情况下:

    np.dot(x, weights)
    

    等同于:

    (2 × 0.5) + (3 × 0.2) + (4 × 0.8)
    

    即:

    4.8
    

    然后我们添加偏置:

    4.8 + 1 = 5.8
    

    现在应用 ReLU:

    output = np.maximum(0, z)
    

    由于 z5.8,ReLU 将其保持不变:

    output = 5.8
    

    最后:

    print(output)
    

    打印:

    5.8
    

    所以我们的整个神经元是:

    import numpy as np
    
    x = np.array([2, 3, 4])
    weights = np.array([0.5, 0.2, 0.8])
    bias = 1
    
    z = np.dot(x, weights) + bias
    output = np.maximum(0, z)
    
    print(output)
    

    我们刚刚创建了一个微小的人工神经元。

    9. 从一个神经元到一层

    单个神经元不足以解决大多数有趣的问题。

    相反,我们可以将多个神经元连接在一起。

    例如:

    输入、输出和隐藏层以神经元形式描绘

    这些神经元共同形成一个

    一个小型神经网络可能看起来像:

    Input Layer
         ↓
    Hidden Layer
         ↓
    Output Layer
    

    一层中的每个神经元都能够将其输出传递给下一层的神经元。

    这就是神经网络开始变得更强大的地方。

    10. 神经网络究竟是如何学习的?

    到目前为止,我们一直手动选择权重:

    0.5
    0.2
    0.8
    

    但真实的神经网络一开始并不知道正确的权重。

    相反,它的初始权重通常是一些小的随机值。

    然后它会经历一个循环:

    Make a prediction
           ↓
    Compare prediction with correct answer
           ↓
    Measure the error
           ↓
    Figure out how to change the weights
           ↓
    Update the weights
           ↓
    Try again
    

    此过程会反复进行,网络会逐步调整其参数,以在训练数据上做出更好的预测。

    让我们逐部分分解。

    11. 预测与损失

    假设正确答案是:

    1
    

    但我们的网络预测:

    0.3
    

    预测结果与目标相差甚远。

    我们需要一种方法来衡量它有多错误。这就是 损失函数 的作用。

    损失函数接收预测值和正确答案,输出一个表示模型误差的数值。

    举个简单的例子,我们可以使用平方误差:

    Loss = (prediction - actual)²
    

    使用我们的数据:

    Loss = (0.3 - 1)²
    

    首先:

    0.3 - 1 = -0.7
    

    然后将其平方:

    (-0.7)² = 0.49
    

    所以:

    Loss = 0.49
    

    通常,损失越小,说明预测越接近目标。

    在实际的神经网络中,不同的问题会采用不同的损失函数。对于二分类问题,常用的是二元交叉熵。

    12. 梯度是什么?

    现在我们遇到了一个问题。

    我们知道预测是错误的,但应该如何调整权重呢?

    这就是梯度发挥作用的地方。梯度告诉我们改变一个参数会如何影响损失。

    你可以把它想象成站在山坡上。假设你的目标是到达最低点。如果你知道哪个方向是上坡,你就可以朝相反的方向走,从而下坡。

    训练神经网络的思路与此类似。我们希望降低损失。梯度提供了参数应朝哪个方向移动的信息。

    13. 什么是梯度下降?

    梯度下降 是利用梯度调整网络参数的过程。

    一个简化的更新规则是:

    new weight = old weight - learning rate × gradient
    

    在Python中:

    weight = weight - learning_rate * gradient
    

    学习率控制更新幅度的大小。

    例如:

    learning_rate = 0.01
    

    如果学习率太大,网络可能会产生巨大的变化,甚至在寻找良好解之前就跳来跳去。

    如果太小,学习过程可能会非常漫长。

    因此,训练的目标是寻找能够使模型朝着损失减小的方向更新参数,同时保持训练过程的稳定。

    14. 什么是反向传播?

    仍然有一个重要的问题:

    如果一个神经网络有成千上万甚至数百万个权重,它如何确定哪些权重导致了误差?

    这就是 反向传播 发挥作用的地方。反向传播通过从后往前遍历网络来计算参数的梯度。

    想象一个类似这样的网络:

    Input
      ↓
    Hidden Layer
      ↓
    Output
      ↓
    Loss
    

    在前向传播过程中,信息按以下方向流动:

    Input → Hidden Layer → Output
    

    在反向传播过程中,梯度信息向后传播:

    Loss → Output → Hidden Layer → Input
    

    网络利用这些梯度来确定其权重和偏置应如何变化。

    在构建真实神经网络时,通常不会手动计算所有这些导数。PyTorch 等库可以自动完成计算。

    但理解基本思想很重要:

    反向传播计算参数对误差的贡献,梯度下降则利用此信息更新参数。

    15. 完整的学习循环

    现在我们可以把所有内容综合起来。

    神经网络的学习循环:输入、预测、损失、梯度、更新(然后再回到预测……)

    更具体地说:

    Give the network data
              ↓
    Calculate a prediction
              ↓
    Compare it with the correct answer
              ↓
    Calculate the loss
              ↓
    Calculate gradients
              ↓
    Update weights and biases
              ↓
    Repeat
    

    一次完整遍历训练数据通常被称为 epoch

    例如:

    Epoch 1 → Loss: 0.82
    Epoch 2 → Loss: 0.61
    Epoch 3 → Loss: 0.43
    Epoch 4 → Loss: 0.29
    Epoch 5 → Loss: 0.18
    

    这些数字仅作示例,但理想情况下,随着训练的进行,损失应会下降。

    16. 从零构建神经网络

    恭喜!你现在已经掌握了神经网络的基础知识。接下来是把这些想法付诸实践的时候了。

    我们将仅使用以下内容构建一个小型神经网络:

    Python + NumPy
    

    我们的网络将学习一个经典的机器学习问题,称为 XOR

    XOR 是一个有两个输入的逻辑运算。

    其规则如下:

    0 XOR 0 → 0
    0 XOR 1 → 1
    1 XOR 0 → 1
    1 XOR 1 → 0
    

    换句话说,当输入中恰好有一个为 1 时,输出为 1

    因此,我们的训练数据将是:

    X = np.array([
        [0, 0],
        [0, 1],
        [1, 0],
        [1, 1]
    ])
    

    而正确的答案是:

    y = np.array([
        [0],
        [1],
        [1],
        [0]
    ])
    

    我们希望神经网络能够学习这个模式。

    17. 理解网络架构

    我们的网络将包含:

    2 input neurons
           ↓
    4 hidden neurons
           ↓
    1 output neuron
    

    两个输入分别代表每个 XOR 示例中的两个数字。

    四个隐藏神经元使网络具有足够的灵活性来学习 XOR 关系。

    输出神经元产生一个介于 01 之间的数字。

    18. 设置数据

    让我们开始编写 Python 程序。

    import numpy as np
    

    这会导入 NumPy。我们将使用 NumPy 进行数组、矩阵乘法以及数学运算。

    接下来:

    X = np.array([
        [0, 0],
        [0, 1],
        [1, 0],
        [1, 1]
    ])
    

    X 包含我们的四个训练样本。

    每行是一个样本:

    [0, 0]
    [0, 1]
    [1, 0]
    [1, 1]
    

    现在创建正确的答案:

    y = np.array([
        [0],
        [1],
        [1],
        [0]
    ])
    

    X 的第一行与 y 的第一行相对应。

    因此:

    [0, 0] → 0
    [0, 1] → 1
    [1, 0] → 1
    [1, 1] → 0
    

    19. 创建权重和偏置

    现在我们需要网络的参数。

    首先:

    np.random.seed(42)
    

    这使得我们的随机数可重复。

    如果没有这一行,网络在每次运行程序时会得到不同的随机初始权重。

    现在创建第一层的权重:

    W1 = np.random.randn(2, 4)
    

    为什么 (2, 4)

    因为:

    所以 W1 需要一个权重,将每个输入与每个隐藏神经元相连。

    有:

    2 × 4 = 8
    

    权重。

    下一步:

    b1 = np.zeros((1, 4))
    

    这会创建四个偏置,每个隐藏神经元对应一个。

    现在是第二层:

    W2 = np.random.randn(4, 1)
    

    这里包含四个隐藏神经元和一个输出神经元,因此我们需要:

    4 × 1 = 4
    

    权重。

    最后:

    b2 = np.zeros((1, 1))
    

    这样输出神经元就有一个偏置。

    因此,我们的网络参数为:

    W1 → input-to-hidden weights
    b1 → hidden-layer biases
    
    W2 → hidden-to-output weights
    b2 → output-layer bias
    

    20. Sigmoid 函数

    我们的输出代表一个概率,所以我们希望它介于 01 之间。

    我们可以使用 sigmoid 函数

    它的方程是:

    sigmoid(x) = 1 / (1 + e⁻ˣ)
    

    在Python中:

    def sigmoid(x):
        return 1 / (1 + np.exp(-x))
    

    我们来看看它的作用:

    sigmoid(-5) ≈ 0.007
    sigmoid(0)  = 0.5
    sigmoid(5)  ≈ 0.993
    

    无论输入多大或多小,结果始终在 01 之间。

    当我们的输出表示概率时,这一点非常有用。

    21. 前向传播

    现在我们可以将数据通过网络传输。这被称为 前向传播

    首先,计算隐藏层:

    z1 = X @ W1 + b1
    

    这里有一个新符号:

    @
    

    在 Python 中,@ 执行矩阵乘法。

    可以把这个操作看作一次性完成许多加权求和。

    而不是手动计算每个神经元:

    input × weight + input × weight + bias
    

    NumPy 可以一次性计算所有这些值。

    结果存储在 z1 中。

    接下来:

    a1 = np.tanh(z1)
    

    在这里,我们使用 tanh 激活函数 来处理隐藏层。

    Tanh 将其输入转换为 -11 之间的值。

    为什么在这里使用 tanh?

    因为 XOR 不是单个简单线性计算能够解决的问题。非线性激活函数为隐藏层提供了学习该模式所需的灵活性。

    现在计算输出层:

    z2 = a1 @ W2 + b2
    

    这一步使用第二组权重将隐藏层的输出组合起来。

    最后:

    a2 = sigmoid(z2)
    

    现在 a2 中存放着我们的预测结果。

    例如,在训练之前,网络可能会产生类似下面的输出:

    0.52
    0.61
    0.48
    0.55
    

    这些预测目前还没有用处,但这是可以预期的。网络尚未学到任何东西。

    22. 计算损失

    现在我们需要衡量这些预测的好坏。

    对于二分类任务,我们将使用 二元交叉熵,这是机器学习中用于二分类的损失函数。它衡量的是模型输出为 0 到 1 之间概率值的性能。

    公式是:

    Loss = -mean(
        y × log(prediction)
        +
        (1 - y) × log(1 - prediction)
    )
    

    这看起来比之前的平方误差示例复杂得多,但我们不需要记住公式。

    在 Python 中:

    loss = -np.mean(
        y * np.log(a2 + 1e-8) +
        (1 - y) * np.log(1 - a2 + 1e-8)
    )
    

    1e-8 是一个非常小的数。

    如果 a2 极其接近 01,就会导致问题,因为对恰好为零的数取对数是无效的。

    在训练开始时,损失可能会相对较高。但随着网络的学习,我们希望它能够降低。

    23. 代码中的反向传播

    现在是我们程序中最数学的部分。

    我们需要计算梯度。

    从以下开始:

    dz2 = a2 - y
    

    这给出了在 sigmoid + 二元交叉熵 组合下,损失相对于输出层前激活值的梯度。

    接下来:

    dW2 = (a1.T @ dz2) / len(X)
    

    这计算了 W2 的梯度。

    .T 表示转置。

    我们的隐藏层输出有四个神经元,而 dz2 表示输出层的误差。通过矩阵乘法将它们结合,可以计算出每个隐藏层到输出层的权重对损失的贡献。

    我们将其除以:

    len(X)
    

    因为我们有四个训练样本,并且我们正在计算平均梯度。

    现在计算输出偏置梯度:

    db2 = np.mean(dz2, axis=0, keepdims=True)
    

    这将计算输出偏置的平均梯度。

    接下来:

    da1 = dz2 @ W2.T
    

    这会把梯度信息从输出层向后传播到隐藏层。

    现在我们需要考虑 tanh 激活函数的导数。

    tanh 的导数可以表示为:

    1 - tanh(x)²
    

    既然我们已经在 a1 中得到了隐藏层的激活值,我们可以写出:

    dz1 = da1 * (1 - a1**2)
    

    这告诉我们隐藏层预激活值如何影响损失。

    现在计算第一层权重的梯度:

    dW1 = (X.T @ dz1) / len(X)
    

    以及隐藏层的偏置:

    db1 = np.mean(dz1, axis=0, keepdims=True)
    

    此时,我们已经获得了所有可训练参数的梯度。

    24. 更新权重

    现在我们使用梯度下降。

    首先:

    W2 -= learning_rate * dW2
    

    这会更新第二层的权重。

    这里的-=表示:

    W2 = W2 - learning_rate * dW2
    

    然后:

    b2 -= learning_rate * db2
    

    更新输出偏置。

    以及:

    W1 -= learning_rate * dW1
    

    更新第一层的权重。

    最后:

    b1 -= learning_rate * db1
    

    更新隐藏层偏置。

    这些更新才是使网络能够学习的关键。

    25. 完整的 NumPy 神经网络

    现在让我们把所有内容整合在一起。

    import numpy as np
    
    # 1. Training data
    
    X = np.array([
        [0, 0],
        [0, 1],
        [1, 0],
        [1, 1]
    ])
    
    y = np.array([
        [0],
        [1],
        [1],
        [0]
    ])
    
    # 2. Initialize parameters
    
    np.random.seed(42)
    
    W1 = np.random.randn(2, 4)
    b1 = np.zeros((1, 4))
    
    W2 = np.random.randn(4, 1)
    b2 = np.zeros((1, 1))
    
    learning_rate = 0.1
    
    # 3. Activation functions
    
    def sigmoid(x):
        return 1 / (1 + np.exp(-x))
    
    # 4. Training
    
    for epoch in range(10000):
    
        # Forward propagation
    
        z1 = X @ W1 + b1
        a1 = np.tanh(z1)
    
        z2 = a1 @ W2 + b2
        a2 = sigmoid(z2)
    
        # Calculate loss
    
        loss = -np.mean(
            y * np.log(a2 + 1e-8) +
            (1 - y) * np.log(1 - a2 + 1e-8)
        )
    
        # Backpropagation
    
        dz2 = a2 - y
    
        dW2 = (a1.T @ dz2) / len(X)
        db2 = np.mean(dz2, axis=0, keepdims=True)
    
        da1 = dz2 @ W2.T
    
        dz1 = da1 * (1 - a1**2)
    
        dW1 = (X.T @ dz1) / len(X)
        db1 = np.mean(dz1, axis=0, keepdims=True)
    
        # Update parameters
    
        W2 -= learning_rate * dW2
        b2 -= learning_rate * db2
    
        W1 -= learning_rate * dW1
        b1 -= learning_rate * db1
    
        # Display progress
    
        if epoch % 1000 == 0:
            print(f"Epoch {epoch}, Loss: {loss:.4f}")
    

    让我们从上到下逐步浏览程序。

    完整代码逐行解释

    导入 NumPy:

    import numpy as np
    

    我们导入 NumPy,因为我们的网络需要进行数组和矩阵运算。

    创建输入

    X = np.array([
        [0, 0],
        [0, 1],
        [1, 0],
        [1, 1]
    ])
    

    每行代表一个 XOR 示例。

    共有四个示例,每个示例包含两个输入值。

    因此,X 的形状是:

    4 × 2
    

    创建答案

    y = np.array([
        [0],
        [1],
        [1],
        [0]
    ])
    

    每行 X 对应一个正确答案,共有四个正确答案。

    使随机初始化可重现

    np.random.seed(42)
    

    这会让 NumPy 每次生成相同的起始随机值。

    数字 42 并没有特别之处。你可以使用其他数字。

    创建第一个权重矩阵

    W1 = np.random.randn(2, 4)
    

    这会生成一个包含随机数的矩阵。

    其形状为 2*4

    有两个输入和四个隐藏神经元。

    创建第一个偏置

    b1 = np.zeros((1, 4))
    

    这会创建四个零:

    [0, 0, 0, 0]
    

    每个隐藏神经元对应一个偏置。

    创建第二个权重矩阵

    W2 = np.random.randn(4, 1)
    

    这里有四个隐藏神经元和一个输出神经元。

    因此:

    4 × 1
    

    需要权重。

    创建输出偏置

    b2 = np.zeros((1, 1))
    

    输出层只有一个神经元,因此需要一个偏置。

    设置学习率

    learning_rate = 0.1
    

    这控制梯度对每次更新的影响程度。

    创建 sigmoid

    def sigmoid(x):
        return 1 / (1 + np.exp(-x))
    

    这将输出转换为介于 01 之间的值。

    开始训练循环

    for epoch in range(10000):
    

    这会让 Python 重复训练过程 10,000 次。

    每次重复称为一个 epoch,即完整地将整个训练数据集通过神经网络一次。

    计算隐藏层

    z1 = X @ W1 + b1
    

    这会对所有四个隐藏神经元和所有四个训练样本进行加权求和计算。

    应用 tanh

    a1 = np.tanh(z1)
    

    这将非线性激活函数应用于隐藏层。

    计算输出层

    z2 = a1 @ W2 + b2
    

    它会取用隐藏层的数值,并计算输出神经元的加权和。

    应用 Sigmoid

    a2 = sigmoid(z2)
    

    这将输出转换为介于 01 之间的概率。

    计算损失

    loss = -np.mean(
        y * np.log(a2 + 1e-8) +
        (1 - y) * np.log(1 - a2 + 1e-8)
    )
    

    这用来衡量预测结果与正确答案的差异。

    数值越低通常表示预测效果越好。

    计算输出梯度

    dz2 = a2 - y
    

    这会计算出更新输出层所需的梯度。

    更新第二层权重梯度

    dW2 = (a1.T @ dz2) / len(X)
    

    这决定了每个连接隐藏层与输出层的权重对损失的贡献程度。

    更新输出偏置梯度

    db2 = np.mean(dz2, axis=0, keepdims=True)
    

    这计算了输出偏置的平均梯度。

    向隐藏层反向传播

    da1 = dz2 @ W2.T
    

    这将梯度信息通过输出层向后传播。

    应用 tanh 导数

    dz1 = da1 * (1 - a1**2)
    

    这解释了 tanh 激活函数的作用。

    计算第一层梯度

    dW1 = (X.T @ dz1) / len(X)
    

    这决定了输入到隐藏层的权重是如何对损失产生影响的。

    然后:

    db1 = np.mean(dz1, axis=0, keepdims=True)
    

    计算隐藏层偏置的梯度。

    更新参数

    W2 -= learning_rate * dW2
    b2 -= learning_rate * db2
    
    W1 -= learning_rate * dW1
    b1 -= learning_rate * db1
    

    这四行代码是网络修改其已学内容的地方。

    梯度告诉我们应该朝哪个方向移动,而学习率则决定移动的幅度。

    打印损失

    if epoch % 1000 == 0:
        print(f"Epoch {epoch}, Loss: {loss:.4f}")
    

    % 运算符返回除法运算后的余数。

    因此:

    epoch % 1000 == 0
    

    每 1000 个周期成立一次。

    这意味着我们不会打印 10,000 次。相反,我们会偶尔得到如下更新:

    Epoch 0, Loss: ...
    Epoch 1000, Loss: ...
    Epoch 2000, Loss: ...
    ...
    

    如果训练进行顺利,损失应总体下降。

    26. 测试网络

    训练完成后,我们可以使用网络进行预测。

    z1 = X @ W1 + b1
    a1 = np.tanh(z1)
    
    z2 = a1 @ W2 + b2
    predictions = sigmoid(z2)
    
    print(predictions)
    

    网络应该输出接近以下的值:

    [[0],
     [1],
     [1],
     [0]]
    

    实际值可能不会恰好是 01

    你可能会得到类似这样的结果:

    [[0.01],
     [0.98],
     [0.99],
     [0.02]]
    

    没关系。

    网络正在输出概率。

    我们可以用阈值把这些概率转换为类别:

    classes = (predictions >= 0.5).astype(int)
    
    print(classes)
    

    结果应该是:

    [[0],
     [1],
     [1],
     [0]]
    

    我们的网络已经学会了 XOR 模式。

    27. 为什么我们需要隐藏层?

    你可能会好奇为什么我们不能直接把两个输入连接到输出。

    原因在于单个线性层无法表示 XOR。

    隐藏层为网络提供了额外的变换,使其能够学习更复杂的关系。

    这是神经网络背后最重要的思想之一:网络不一定会学习一个巨大的规则。相反,不同的层可以逐步转换信息。

    对于图像识别系统,你可以想象一个简化的过程如下:

    图像识别系统的可视化表示

    真实的神经网络并不会真的创建称为“边缘”、“形状”和“物体”的整齐层。这只是一种直觉,说明通过多层可以产生越来越复杂的表示。

    28. 在更大的神经网络中会发生什么?

    我们构建的网络很小。现代神经网络可以拥有数百万、数十亿甚至更多的参数。

    一个简化的网络可能看起来像这样:

    简化的神经网络可视化表示

    每个连接可以有自己的权重。

    网络拥有的神经元和连接越多,它可能需要学习的参数就越多。

    因此,大型模型需要大量的计算能力和内存。

    但请记住基本过程:

    Input
     ↓
    Calculations
     ↓
    Prediction
     ↓
    Loss
     ↓
    Gradients
     ↓
    Parameter Updates
    

    网络的规模可能会发生巨大变化,但基本的训练思想保持不变。

    29. 是否必须从零开始构建神经网络?

    不必。从零开始构建神经网络有助于学习,因为它迫使你去理解库背后发生的事情。

    但在构建实际的机器学习应用时,你通常不会手动计算每个梯度。

    这时机器学习框架就派上用场了。常用的 Python 库包括:

    在深度学习中,PyTorch 是最常用的框架之一。它能够自动计算梯度,并处理训练过程中涉及的许多数学运算。

    30. 使用 PyTorch 构建相同的网络

    让我们看看使用 PyTorch 能让网络变得多么简洁。

    首先,安装它:

    pip install torch
    

    然后导入它:

    import torch
    import torch.nn as nn
    

    现在创建模型:

    model = nn.Sequential(
        nn.Linear(2, 4),
        nn.Tanh(),
        nn.Linear(4, 1),
        nn.Sigmoid()
    )
    

    我们来分解一下吧。

    nn.Linear(2, 4)
    

    创建一个接受两个输入、产生四个输出的层。

    这就是我们的隐藏层。

    接下来:

    nn.Tanh()
    

    应用tanh激活函数。

    然后:

    nn.Linear(4, 1)
    

    将四个隐藏神经元连接到一个输出神经元上。

    最后:

    nn.Sigmoid()
    

    将输出转换为介于 01 之间的值。

    因此架构是:

    2 inputs
       ↓
    4 hidden neurons
       ↓
    Tanh
       ↓
    1 output neuron
       ↓
    Sigmoid
    

    注意,这段代码比我们的 NumPy 实现要短得多。

    这是因为 PyTorch 帮我们完成了许多计算。

    31. 使用 PyTorch 训练网络

    首先,创建训练数据:

    X = torch.tensor([
        [0., 0.],
        [0., 1.],
        [1., 0.],
        [1., 1.]
    ])
    
    y = torch.tensor([
        [0.],
        [1.],
        [1.],
        [0.]
    ])
    

    小数点至关重要,因为神经网络通常处理的是浮点数。

    现在创建模型:

    model = nn.Sequential(
        nn.Linear(2, 4),
        nn.Tanh(),
        nn.Linear(4, 1),
        nn.Sigmoid()
    )
    

    接下来,选择损失函数:

    loss_function = nn.BCELoss()
    

    BCELoss 计算二元交叉熵损失。

    现在创建一个优化器:

    optimizer = torch.optim.Adam(
        model.parameters(),
        lr=0.01
    )
    

    Adam 是一种在训练过程中更新模型参数的优化算法。

    model.parameters() 告诉优化器需要更新哪些参数。

    lr=0.01 设置学习率。

    现在我们可以开始训练:

    for epoch in range(5000):
    
        predictions = model(X)
    
        loss = loss_function(predictions, y)
    
        optimizer.zero_grad()
    
        loss.backward()
    
        optimizer.step()
    
        if epoch % 500 == 0:
            print(
                f"Epoch {epoch}, Loss: {loss.item():.4f}"
            )
    

    我们来看看重要的部分。

    首先:

    predictions = model(X)
    

    这会将训练数据通过网络传递。

    然后:

    loss = loss_function(predictions, y)
    

    将预测结果与正确答案进行比较。

    下一步:

    optimizer.zero_grad()
    

    清除上一次训练步骤中的梯度。

    然后:

    loss.backward()
    

    使用反向传播自动计算梯度。

    最后:

    optimizer.step()
    

    使用这些梯度更新模型参数。

    这就是我们用 NumPy 手动实现的基本学习过程;不同之处在于 PyTorch 负责了许多计算。

    32. NumPy 与 PyTorch

    那么为什么我们要构建两次网络?因为这两个版本教授的内容不同。

    使用 NumPy 时,我们手动处理了权重、偏置、前向传播、
    损失、梯度、反向传播和参数更新。这样机制就更易于观察。

    使用 PyTorch,我们可以用更少的代码表达相同的思想,因为框架处理了许多这些计算。

    你可以这样理解:

    NumPy 与 PyTorch 的对比

    了解 NumPy 版本的工作原理会让 PyTorch 版本变得不那么神秘。

    33. 什么是深度学习?

    你可能已经听说过 深度学习 这个术语。深度学习是机器学习的一个分支,它使用多层神经网络。

    例如:

    Input
      ↓
    Layer 1
      ↓
    Layer 2
      ↓
    Layer 3
      ↓
    Layer 4
      ↓
    Output
    

    "深度"一词指的是网络的深度,即所涉及的层数。

    没有神奇的临界点,神经网络会突然变得智能。仅仅增加层数只是为模型提供了更多将输入转化为有用表示的机会。

    34. 神经网络的应用领域

    神经网络在许多不同领域都有应用。以下是一些例子……

    计算机视觉

    神经网络可以处理图像。

    例如:

    Image
      ↓
    Neural Network
      ↓
    Prediction
    

    它们可以用于图像分类和目标检测等任务。

    自然语言处理

    神经网络也可以处理文本。

    例如:

    Text
      ↓
    Neural Network
      ↓
    Prediction
    

    现代语言模型使用神经网络来处理和生成文本。

    语音识别

    神经网络能够处理音频,并帮助将口语转化为文本。

    Audio
      ↓
    Neural Network
      ↓
    Words
    

    推荐系统

    神经网络可以从用户行为中学习模式,并帮助预测哪些内容或产品可能对某人有用。

    生成式 AI

    大型神经网络也可以用于生成文本、图像、音频、代码、视频等更多内容。

    这些系统比我们构建的小型 XOR 网络复杂得多,但它们仍然依赖于从数据中学习参数的相同基本思想。

    35. 一图看尽整个过程

    到目前为止,我们已经介绍了很多。

    以下是完整的训练过程:

    Data
       ↓
    Neural Network
       ↓
    Prediction
       ↓
    Loss
      ↓
    Backpropagation
      ↓
    Update Parameters
      ↓
    Repeat
    

    训练完成后,我们使用学习到的参数对新数据进行预测:

    New Data
       ↓
    Trained Neural Network
       ↓
    Prediction
    

    这就是神经网络训练的基本思想。

    36. 需要记住的最重要思想

    如果你记不住本教程中的每个方程,也没有关系。

    从这些概念开始。

    输入

    我们给网络的数字。

    x₁, x₂, x₃...
    

    权重

    决定输入对神经元影响程度的数值。

    w₁, w₂, w₃...
    

    偏置

    为神经元提供更大灵活性的额外值。

    b
    

    激活函数

    激活函数用于变换神经元输出,使网络能够学习非线性模式。

    常见例子包括:

    ReLU
    Tanh
    Sigmoid
    

    前向传播

    将数据从输入端向输出端传送。

    Input → Hidden Layers → Output
    

    损失

    衡量预测结果与正确答案之间差异的指标。

    反向传播

    通过在网络中逆向传播来计算梯度。

    梯度下降

    利用这些梯度来更新网络的参数。

    整个学习过程可以总结为:

    Predict
       ↓
    Measure Error
       ↓
    Calculate Gradients
       ↓
    Update Parameters
       ↓
    Repeat
    

    37. 你接下来应该学什么?

    如果你想继续用 Python 学习神经网络,则无需直接跳入复杂的研究论文。

    一个有用的学习路径是:

    Python
      ↓
    NumPy
      ↓
    Basic Linear Algebra
      ↓
    Probability & Statistics
      ↓
    Machine Learning Basics
      ↓
    Neural Networks
      ↓
    PyTorch
      ↓
    Deep Learning
      ↓
    Computer Vision / NLP / Generative AI
    

    你也可以通过构建小项目来学习。

    例如:

    1. 异或分类器

    2. 房价预测器

    3. 手写数字分类器

    4. 简单图像分类器

    5. 垃圾信息分类器

    6. 能学习数学函数的神经网络

    项目不必规模庞大。你完全理解的小项目往往比你不懂就照搬代码的大项目更有用。

    最终要点

    现代 AI 系统中的神经网络往往包含巨量参数,因而看起来令人望而生畏。

    但其基本思想其实非常简单。

    神经网络以数字作为输入,利用权重和偏置将它们组合,应用数学函数,得到预测,评估预测的误差,然后根据误差调整参数。

    这个循环如下所示:

    Input
      ↓
    Weighted Calculations
      ↓
    Activation Functions
      ↓
    Prediction
      ↓
    Loss
      ↓
    Gradients
      ↓
    Parameter Updates
      ↓
    Repeat
    

    这就是基础。

    本教程中我们搭建的 XOR 网络相较于现代 AI 中使用的神经网络来说非常小。但你刚才学到的这些思想(参数、层、激活函数、前向传播、损失、反向传播、梯度和优化)是深度学习中反复出现的基本概念。

    下次当你听说一个 AI 模型拥有数百万甚至数十亿个参数时,可能仍会感到吃惊。

    但在如此庞大的规模之下,基本的学习循环仍然熟悉:

    1. 进行预测。

    2. 测量误差。

    3. 找出如何改进。

    4. 更新参数。

    5. 再次尝试。

    这就是神经网络背后的核心思想。

    编码愉快,持续学习!

    ——

    🧑‍💻

    zhirenhun

    一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。

    ← 上一篇
    数据本地性税:错误区域向量数据库对 RAG 流水线的成本
    下一篇 →
    如何在 Hermes Agent 中使用 DigitalOcean 知识库添加 RAG

    📌 相关推荐

    GraphRAG 是推理问题,而非数据库问题
    2026/8/30
    构建市场时光机:使用 Python 和 WebSocket 重放交易会话
    2026/8/30
    如何自行基准测试LLM推理:值得信赖的数字设计标准
    2026/8/30
    ← 返回文章列表