第 3 章:神经网络如何学习

发布于:2025-06-29 ⋅ 阅读:(18) ⋅ 点赞:(0)

第 3 章:神经网络如何学习

在第二章中,我们详细了解了神经网络的静态结构:由神经元组成的层,以及连接它们的权重和偏置。现在,我们将进入整个教程最核心的部分:神经网络是如何从数据中"学习"的?

这个学习过程是一个动态的、不断调整自身参数以求更佳预测的过程。我们将通过四个关键概念来揭示这个秘密:

  1. 前向传播 (Forward Propagation):数据如何通过网络产生一个预测?
  2. 损失函数 (Loss Function):如何量化这个预测的"好坏"?
  3. 梯度下降 (Gradient Descent):如何根据"好坏"程度,找到参数优化的方向?
  4. 反向传播 (Backpropagation):如何高效地在整个网络中执行这个优化?

让我们从第一步开始。

3.1 前向传播:从输入到输出

前向传播,顾名思义,是信息在神经网络中 从前向后 传递的过程。它描述了当给定一个输入样本时,网络是如何一步步进行计算,并最终在输出层得到一个预测值的完整流程。

这个过程非常直观,就是将我们在第二章学到的所有知识串联起来。

前向传播的步骤

我们以一个简单的、用于二元分类的网络为例。假设它有一个输入层、一个隐藏层和一个输出层。

在这里插入图片描述

图 3.1: 前向传播流程示意图。数据从输入层(绿色)开始,流经隐藏层(蓝色),最终到达输出层(红色)产生预测值。

对于一个输入样本 X,其前向传播的计算流程如下:

  1. 输入层 -> 隐藏层

    • 首先,隐藏层的每一个神经元都会接收来自输入层所有神经元的信号。
    • 对于隐藏层中的 第 j 个神经元,它会计算一个加权和 z_j,这和我们在感知器中学到的一样:
      z j = ( ∑ i ( x i ⋅ w i j ) ) + b j z_j = (\sum_{i} (x_i \cdot w_{ij})) + b_j zj=(i(xiwij))+bj
      其中,x_i 是第 i 个输入,w_ij 是从输入层第 i 个神经元到隐藏层第 j 个神经元的权重,b_j 是隐藏层第 j 个神经元的偏置。
    • 然后,将这个加权和 z_j 通过一个激活函数(比如我们学过的 ReLU 或 Sigmoid)处理,得到该神经元的输出 a_j
      a j = Activation ( z j ) a_j = \text{Activation}(z_j) aj=Activation(zj)
    • 对隐藏层中的所有神经元重复这个过程,我们就得到了整个隐藏层的输出 A_hidden
  2. 隐藏层 -> 输出层

    • 现在,隐藏层的输出 A_hidden 成为了输出层的输入。
    • 输出层的计算过程与隐藏层完全相同。假设我们的输出层只有一个神经元(用于二元分类),它的计算过程是:
      • 计算加权和 z_output
        z output = ( ∑ j ( a j ⋅ w j , output ) ) + b output z_{\text{output}} = (\sum_{j} (a_j \cdot w_{j,\text{output}})) + b_{\text{output}} zoutput=(j(ajwj,output))+boutput
      • 应用激活函数得到最终预测 y_pred
        y pred = Activation output ( z output ) y_{\text{pred}} = \text{Activation}_{\text{output}}(z_{\text{output}}) ypred=Activationoutput(zoutput)
        (对于二元分类,这里的激活函数通常是 Sigmoid)

至此,一次完整的前向传播就完成了。 我们从一个原始输入 X 开始,通过网络中预设的权重和偏置,一步步计算,最终得到了一个预测结果 y_pred

值得注意的是,在网络未经训练时,由于权重和偏置都是随机初始化的,这个 y_pred 几乎肯定是错误的。

那么,我们如何知道它"错得有多离谱"?又该如何利用这个"错误"来指导网络调整参数,让下一次的预测更准一些呢?

这便是我们下一节要讨论的 损失函数

3.2 损失函数:衡量预测的"错误"程度

损失函数(Loss Function),有时也被称为 成本函数(Cost Function)目标函数(Objective Function),是神经网络学习过程中的"导航员"和"裁判"。

它的作用非常明确:用一个具体的数值来量化模型的预测值(y_pred)与真实值(y_true)之间的差距。

这个差距,我们称之为"损失"(Loss)或"误差"(Error)。

  • 损失值越大,说明模型的预测越不准确,离真实答案"越远"。
  • 损失值越小,说明模型的预测越精准,离真实答案"越近"。

因此,整个神经网络训练的 最终目标,就是通过调整权重和偏置,来 最小化这个损失函数的值

选择哪种损失函数取决于我们正在处理的任务类型。下面我们介绍两种最常见的场景。

场景一:回归问题(Regression)

在回归任务中,我们的目标是预测一个连续的数值,比如房价、气温或者股票价格。对于这类问题,最常用的损失函数是 均方误差(Mean Squared Error, MSE)

工作原理:MSE 计算的是所有样本的"预测值与真实值之差的平方"的平均值。

在这里插入图片描述

图 3.2: 均方误差(MSE)的可视化。它计算的是每个数据点(蓝点)到模型预测线(红线)的垂直距离(绿色虚线,即残差)的平方的平均值。(来源: Neuromatch Academy)

数学公式(假设我们有 n 个样本):
L MSE = 1 n ∑ i = 1 n ( y true ( i ) − y pred ( i ) ) 2 L_{\text{MSE}} = \frac{1}{n} \sum_{i=1}^{n} (y_{\text{true}}^{(i)} - y_{\text{pred}}^{(i)})^2 LMSE=n1i=1n(ytrue(i)ypred(i))2

  • 我们先计算每个样本的预测值和真实值之差 (y_true - y_pred)
  • 然后将这个差值平方,这有两个好处:1) 保证结果是正数;2) 对较大的误差给予更重的"惩罚"。
  • 最后将所有样本的平方误差加起来,求一个平均值。

场景二:分类问题(Classification)

在分类任务中,我们的目标是预测一个离散的类别标签,例如判断一封邮件是否为垃圾邮件(二元分类),或者识别一张图片中的动物是猫、狗还是鸟(多元分类)。

对于分类问题,最强大的损失函数是 交叉熵损失(Cross-Entropy Loss)

工作原理:它的核心思想是:对于正确的预测,我们给予较小的"惩罚"(损失);对于错误的预测,我们给予巨大的"惩罚"。

在这里插入图片描述

图 3.3: 不同损失函数在二元分类中的对比(当真实标签为1时)。交叉熵损失(绿色实线)显示,当模型对正确类别的预测概率接近1时,损失趋近于0;而当预测概率接近0时,损失会急剧增加,给予错误的预测巨大的惩罚。(来源: Wikimedia Commons)

对于最常见的 二元分类(Binary Classification),其交叉熵损失(也称为 BCE Loss)公式如下:

L BCE = − 1 n ∑ i = 1 n [ y true ( i ) log ⁡ ( y pred ( i ) ) + ( 1 − y true ( i ) ) log ⁡ ( 1 − y pred ( i ) ) ] L_{\text{BCE}} = - \frac{1}{n} \sum_{i=1}^{n} \left[ y_{\text{true}}^{(i)} \log(y_{\text{pred}}^{(i)}) + (1 - y_{\text{true}}^{(i)}) \log(1 - y_{\text{pred}}^{(i)}) \right] LBCE=n1i=1n[ytrue(i)log(ypred(i))+(1ytrue(i))log(1ypred(i))]

让我们来理解一下这个公式:

  • 如果真实标签 y_true 是 1:公式简化为 -log(y_pred)
    • 如果我们的预测 y_pred 也很接近 1(比如 0.99),那么 log(y_pred) 接近 0,损失就很小。
    • 如果我们的预测 y_pred 离谱地接近 0(比如 0.01),那么 log(y_pred) 会趋近于负无穷,损失就会变得非常大。
  • 如果真实标签 y_true 是 0:公式简化为 -log(1 - y_pred)
    • 如果我们的预测 y_pred 也很接近 0(比如 0.01),那么 1 - y_pred 接近 1,log(1-y_pred) 接近 0,损失就很小。
    • 如果我们的预测 y_pred 离谱地接近 1(比如 0.99),那么 1 - y_pred 接近 0,log(1-y_pred) 会趋近于负无穷,损失就会变得非常大。

这正是我们想要的:预测越有信心且越正确,损失越小;预测越有信心但越错误,损失就越大。


现在,我们有了一个明确的目标(最小化损失函数),也知道了如何衡量我们距离这个目标有多远。

接下来的问题是:我们应该 如何 调整那成千上万的权重和偏置,才能让这个损失值降低呢?我们是应该把某个权重调高一点,还是调低一点?调多少才合适?

明确的目标(最小化损失函数),也知道了如何衡量我们距离这个目标有多远。

接下来的问题是:我们应该 如何 调整那成千上万的权重和偏置,才能让这个损失值降低呢?我们是应该把某个权重调高一点,还是调低一点?调多少才合适?

这就是下一节 “梯度下降” 将要为我们解答的问题。

3.3 梯度下降:找到最小化损失的路径

现在,我们站在了问题的核心:我们有了一个可以量化错误的损失函数,我们如何系统地调整网络中成千上万的参数(权重 w 和偏置 b),来让损失值变得越来越小呢?

暴力尝试显然是行不通的。我们需要一个聪明且高效的策略。这个策略就是 梯度下降(Gradient Descent)

一个下山的类比

为了理解梯度下降,想象一个非常生动的场景:

你正置身于一座连绵起伏的山脉中,四周一片浓雾,你看不清山谷的最低点在哪里。你的任务是尽快到达山谷的底部。你该怎么办?

一个非常直观的策略是:环顾四周,找到脚下最陡峭的下坡方向,然后朝着这个方向迈出一步。 到达新位置后,你再次重复这个过程:环顾四周,找到新的最陡峭的下坡方向,再迈出一步。只要你坚持这么做,最终你将一步步地走到山谷的最低点。

这就是梯度下降算法的全部直觉。在这个类比中:

  • 山脉的地形:就是我们的 损失函数。这是一个由所有网络参数(权重和偏置)决定的复杂、高维度的"地形"。
  • 你在山上的位置:由当前网络的 所有参数值 决定。
  • 你的海拔高度:就是当前参数所对应的 损失值
  • 我们的目标:找到这片地形的 最低点(Global Minimum),也就是损失函数的最小值。
  • 最陡峭的下坡方向:这就是 梯度(Gradient) 的反方向。

在这里插入图片描述

图 3.4: 梯度下降的可视化。在这个损失函数的"地形"上,无论从哪个点开始(红点),梯度下降算法都会引导参数沿着最陡峭的下坡路径前进,最终到达一个局部或全局的最低点。

核心概念:梯度与学习率

梯度下降算法的核心由两个概念组成:

  1. 梯度(Gradient, ∇)
    在数学上,梯度是一个向量,它指向函数值 增长最快 的方向。换句话说,梯度就是函数在当前位置的 最陡峭的上坡方向

    那么,最陡峭的 下坡方向 自然就是梯度的 反方向-∇)。

    在神经网络中,这个"函数"就是我们的损失函数 L。梯度 ∇L 就是损失函数 L 对所有参数(w_1, w_2, ..., b_1, b_2, ...)求偏导数后组成的向量。这个向量告诉我们,在当前的位置,如何微调每一个参数,才能让损失值上升得最快。而我们只需要沿着它的反方向更新参数,就能最高效地降低损失。

  2. 学习率(Learning Rate, α)
    找到了下山的方向后,我们还需要决定 每一步该迈多大。这个步长,就是 学习率。它是一个超参数(需要我们手动设定的值),用来控制每次参数更新的幅度。

    • 学习率太小:我们会像个谨小慎微的婴儿一样,每次只挪动一小步。虽然方向是对的,但下山速度会非常非常慢,训练过程会极其漫长。
    • 学习率太大:我们可能会因为步子迈得太大而"冲过头",直接越过了山谷的最低点,甚至可能跳到了对面的山坡上,导致损失值不降反升,永远无法收敛到最低点。

    因此,选择一个合适的学习率是训练神经网络中最关键的环节之一。

梯度下降的更新规则

结合梯度和学习率,我们就得到了梯度下降的参数更新规则。对于网络中的任何一个参数 θ(它可以是任何权重 w 或偏置 b),其更新过程如下:

θ new = θ old − α ⋅ ∇ θ L \theta_{\text{new}} = \theta_{\text{old}} - \alpha \cdot \nabla_{\theta}L θnew=θoldαθL

这个公式的含义是:

  1. 计算损失函数 L 在当前参数 θ_old 处的梯度 ∇_θ L
  2. 将梯度乘以学习率 α,得到本次更新的步长。
  3. 从旧的参数值 θ_old 中减去这个步长,得到新的参数值 θ_new

通过在整个训练数据集上反复迭代这个过程(即,对于每个样本或每个批次的样本,都计算梯度并更新一次参数),网络中的所有参数都会被逐步推向能使总损失最小化的最优值。

但是,这里又出现了一个巨大的挑战:一个现代神经网络的参数动辄成千上万,甚至数百万、数十亿。我们该如何高效地计算出损失函数对这每一个参数的梯度呢?

这就要引出神经网络优化中的最后一块,也是最神奇的一块拼图——反向传播。我们将在下一节详细探讨它。

3.4 反向传播:高效的梯度计算引擎

**反向传播(Backpropagation, BP)**是迄今为止训练神经网络最成功的算法。可以说,没有反向传播,就没有深度学习的今天。它解决的正是上一节末尾提出的那个核心挑战:如何在一个具有数百万甚至数十亿参数的复杂网络中,快速、高效地计算出损失函数对每一个参数的梯度。

它的基本思想非常优雅,完全建立在微积分的 链式法则(Chain Rule) 之上。

直观理解:责任的层层回溯

让我们先抛开复杂的数学公式,用一个直观的方式来理解反向传播。

在前向传播中,信息从输入层流向输出层,最终产生一个预测,并计算出总损失。现在,想象一下,这个最终的损失(误差)是一个"责任",我们需要将这个"总责任"公平且准确地分配回网络中的每一个参数(权重和偏置),看看它们各自对这个最终的错误贡献了多少"力量"。

反向传播做的就是这件事:它将损失 L 这个"总责任",从网络的 输出层开始,一层一层地向后传递,直到输入层。

  1. 输出层:在输出层,我们可以直接计算出损失对该层参数(权重和偏置)的梯度。这相对简单,因为它们离损失函数最近。
  2. 倒数第二层:这一层的参数并没有直接影响最终的损失,而是通过影响了输出层的输出来间接影响损失。那么,这一层某个参数的"责任"有多大呢?根据链式法则,它的大小等于:(它对输出层的影响) x (输出层对最终损失的影响)。由于后者我们已经在第一步算出来了,我们只需要计算前者,就能得到当前层参数的梯度。
  3. 继续向后:这个逻辑可以一直向后传递。任何一层参数的梯度,都可以通过它对下一层的影响,乘以【下一层已经计算出的梯度】来得到。

在这里插入图片描述

图 3.5: 反向传播中的梯度(或误差信号 δ)流动示意图。误差从最后一层(右侧)产生,并利用链式法则逐层向后传递,计算出每一层参数所应承担的"责任"。

就这样,误差信号像涟漪一样从后向前传播,每经过一层,我们就利用链式法则计算出该层参数的梯度。当这个过程到达输入层时,我们就已经拥有了网络中所有参数的梯度。

反向传播的两个阶段

因此,一次完整的参数更新(即梯度下降的一步)实际上包含两个阶段:

  1. 前向传播(Forward Pass)

    • 将一批训练数据输入网络。
    • 从输入层开始,逐层计算,直到输出层得到预测值。
    • 根据预测值和真实值,计算出这一批数据的总损失。
    • 在这个过程中,每一层的计算结果(比如加权和 z 和激活值 a)都需要被缓存下来,因为它们在反向传播阶段需要被用到。
  2. 反向传播(Backward Pass)

    • 从最终的损失开始,计算损失函数对输出层参数的梯度。
    • 利用链式法则,逐层向后计算每一层参数的梯度,直到输入层。
    • 这个过程会用到前向传播中缓存的中间值。

当反向传播完成后,我们就得到了所有参数的梯度。此时,我们就可以使用上一节学到的梯度下降更新规则,来更新所有的权重和偏置了:

θ new = θ old − α ⋅ ∇ θ L \theta_{\text{new}} = \theta_{\text{old}} - \alpha \cdot \nabla_{\theta}L θnew=θoldαθL

这个 “前向计算 -> 反向求导 -> 更新参数” 的循环,就是神经网络训练的核心。这个循环会不断地重复,成千上万次,直到损失函数的值收敛到一个足够小的范围,我们的网络也就"学会"了如何处理这类任务。

至此,我们已经完整地解构了神经网络学习的四大核心组件。在下一章,我们将把这些理论知识应用到实践中,使用 PyTorch 这个强大的深度学习框架,来亲手搭建和训练我们的第一个神经网络。


网站公告

今日签到

点亮在社区的每一天
去签到