前向传播与反向传播 - MACHINE-LEARNING教程

前向传播与反向传播

在深度学习中,前向传播与反向传播是支撑其运转的两大核心支柱。它们如同一个硬币的两面,共同构成了神经网络从学习到应用的完整闭环,透彻理解这两个过程,是打开深度学习大门的第一把钥匙。

本文将带你一步步拆解这两个看似复杂的概念,用清晰的逻辑和生动的比喻,让你不仅知其然,更知其所以然。


什么是前向传播与反向传播?

在深入细节之前,让我们先建立一个宏观的认知。

想象一下,你正在教一个孩子识别猫和狗。你给他看一张图片( 输入 ),他根据自己大脑中已有的知识( 网络参数 ,即权重和偏置)进行判断,然后告诉你这是猫( 输出 )。这个看图片 -> 大脑处理 -> 给出答案的过程,就是 前向传播

但孩子的判断可能出错。你告诉他:不对,这是狗。这个正确答案与孩子答案之间的差异,就是 误差 。孩子需要根据这个误差,回头去反思:我大脑里的哪些知识(参数)导致了这次误判?我应该如何调整它们,下次才能认对?这个根据误差,从后往前调整知识的过程,就是 反向传播

在神经网络中:

  • 前向传播 :数据从输入层,经过隐藏层,最终到达输出层,并产生预测结果的过程。这是一个 推理 过程。
  • 反向传播 :根据前向传播产生的预测结果与真实值之间的误差,从输出层开始,反向逐层计算每个参数(权重和偏置)对总误差的"贡献"大小(即梯度),并据此更新参数。这是一个 学习 过程。

它们的关系可以用一个简单的学习循环图来表示:


前向传播:神经网络的推理之路

前向传播是神经网络进行预测的前向通道。让我们通过一个最简单的三层神经网络(输入层、一个隐藏层、输出层)来理解它。

核心概念与计算

假设我们要预测房价,输入是房屋面积 x 。我们的微型网络结构如下:

  • 输入层 :一个神经元,接收 x
  • 隐藏层 :一个神经元,拥有权重 w1 和偏置 b1
  • 输出层 :一个神经元,拥有权重 w2 和偏置 b2 ,输出预测房价 y_pred

前向传播的计算分为两步:

1、隐藏层计算 :输入 x 与权重 w1 、偏置 b1 结合,然后通过一个激活函数(例如Sigmoid,记作 σ ),产生隐藏层输出 a1

z1 = w1 * x + b1

a1 = σ(z1) = 1 / (1 + exp(-z1))

  • z1 是线性变换结果。
  • a1 是经过非线性激活后的输出,这赋予了网络学习复杂模式的能力。

2、输出层计算 :隐藏层输出 a1 作为输入,与输出层的权重 w2 、偏置 b2 结合,产生最终预测 y_pred 。这里为了简化,假设输出层不使用激活函数(即线性输出)。

y_pred = w2 * a1 + b2

代码示例:手动实现前向传播

示例代码
importnumpyasnpdefsigmoid(x):"""Sigmoid激活函数"""return1/(1+ np.exp(-x))# 初始化网络参数(通常随机初始化,这里为演示指定值)w1,b1=2.0,-1.0# 隐藏层参数w2,b2=1.5,0.5# 输出层参数defforward_pass(x):"""执行一次前向传播"""# 隐藏层计算z1=w1 * x + b1a1=sigmoid(z1)# 应用激活函数# 输出层计算y_pred=w2 * a1 + b2# 线性输出# 返回中间结果和最终预测,便于后续理解return{'z1': z1,'a1': a1,'y_pred': y_pred}# 假设房屋面积为 3(单位:百平方米)x_input=3.0result=forward_pass(x_input)print(f"输入 x = {x_input}")print(f"隐藏层线性输出 z1 = w1*x + b1 = {result['z1']:.4f}")print(f"隐藏层激活输出 a1 = sigmoid(z1) = {result['a1']:.4f}")print(f"最终预测房价 y_pred = w2*a1 + b2 = {result['y_pred']:.4f}")

输出示例:

输入 x = 3.0

隐藏层线性输出 z1 = w1*x + b1 = 5.0000

隐藏层激活输出 a1 = sigmoid(z1) = 0.9933

最终预测房价 y_pred = w2*a1 + b2 = 1.9899

这个 y_pred 就是网络对面积为3的房屋的预测价格。但显然,这个预测值(基于我们随便设定的参数)很可能与真实房价相差甚远。如何衡量这个差距并改进呢?这就需要 损失函数 和接下来的 反向传播


损失函数:好坏的衡量标尺

在反向传播开始之前,我们必须先量化预测值 y_pred 与真实值 y_true 之间的差距。这就是损失函数的作用。

常用损失函数

  • 均方误差 :适用于回归问题(如预测房价、温度)。 Loss = (1/N) * Σ (y_true - y_pred)^2
  • 交叉熵损失 :适用于分类问题(如图像分类、垃圾邮件识别)。

以均方误差为例,对于单个样本:

Loss = (y_true - y_pred)^2

我们的目标就是通过调整 w1, b1, w2, b2 ,让这个 Loss 值尽可能小。


反向传播:神经网络的学习引擎

反向传播是深度学习的学习算法核心。其本质是 链式法则 在神经网络中的高效应用。目标是计算损失函数 L 对每个参数( w1, b1, w2, b2 )的 偏导数(梯度) ,即 ∂L/∂w1 , ∂L/∂b1 等。这些梯度指明了为了减小损失,每个参数应该朝哪个方向、以多大的幅度调整。

理解梯度:下山的方向与步幅

想象你蒙着眼站在一座山上( 损失曲面 ),目标是找到山谷的最低点( 最小损失 )。你每走一步前,都需要用脚感受一下周围最陡的下坡方向。这个最陡的下坡方向就是 梯度 。反向传播就是帮你精确计算出脚下每一个点(对应每一组参数)的梯度。

反向传播计算步骤(链式求导)

我们继续沿用前面的微型网络,并假设真实房价 y_true = 2.5 ,损失函数为均方误差 L = (y_true - y_pred)^2

反向传播从输出层开始, 反向 逐层计算梯度:

计算输出层参数的梯度

  • 损失 L 对预测值 y_pred 的梯度: ∂L/∂y_pred = -2 * (y_true - y_pred)
  • 因为 y_pred = w2 * a1 + b2 ,所以: ∂L/∂w2 = (∂L/∂y_pred) * (∂y_pred/∂w2) = (∂L/∂y_pred) * a1 ∂L/∂b2 = (∂L/∂y_pred) * (∂y_pred/∂b2) = (∂L/∂y_pred) * 1

计算隐藏层参数的梯度

  • 首先,需要损失 L 对隐藏层输出 a1 的梯度。 a1 通过 y_pred 影响 L ∂L/∂a1 = (∂L/∂y_pred) * (∂y_pred/∂a1) = (∂L/∂y_pred) * w2
  • 然后, a1 = σ(z1) ,Sigmoid函数的导数 σ'(z) = σ(z)*(1-σ(z))
  • 最后,计算 L 对隐藏层参数 w1 , b1 的梯度: ∂L/∂w1 = (∂L/∂a1) * (∂a1/∂z1) * (∂z1/∂w1) = (∂L/∂a1) * σ'(z1) * x ∂L/∂b1 = (∂L/∂a1) * (∂a1/∂z1) * (∂z1/∂b1) = (∂L/∂a1) * σ'(z1) * 1

代码示例:手动实现反向传播

示例代码
# 接续前向传播的代码和结果y_true=2.5y_pred=result['y_pred']a1=result['a1']z1=result['z1']x=x_inputprint(f"真实值 y_true = {y_true}")print(f"预测值 y_pred = {y_pred:.4f}")print(f"初始损失 Loss = {(y_true - y_pred)**2:.4f}")print("\n--- 开始反向传播计算梯度 ---")# 1. 计算损失对y_pred的梯度dL_dy_pred=-2*(y_true - y_pred)print(f"梯度 ∂L/∂y_pred = -2*(y_true - y_pred) = {dL_dy_pred:.4f}")# 2. 计算输出层参数 w2, b2 的梯度dL_dw2=dL_dy_pred * a1dL_db2=dL_dy_pred *1print(f"梯度 ∂L/∂w2 = (∂L/∂y_pred) * a1 = {dL_dw2:.4f}")print(f"梯度 ∂L/∂b2 = (∂L/∂y_pred) * 1 = {dL_db2:.4f}")# 3. 计算损失对隐藏层输出a1的梯度dL_da1=dL_dy_pred * w2print(f"梯度 ∂L/∂a1 = (∂L/∂y_pred) * w2 = {dL_da1:.4f}")# 4. 计算Sigmoid函数的导数在z1处的值defsigmoid_derivative(x):"""Sigmoid函数的导数"""s=sigmoid(x)returns *(1- s)sigma_prime_z1=sigmoid_derivative(z1)print(f"Sigmoid导数 σ'(z1) = σ(z1)*(1-σ(z1)) = {sigma_prime_z1:.4f}")# 5. 计算隐藏层参数 w1, b1 的梯度dL_dw1=dL_da1 * sigma_prime_z1 * xdL_db1=dL_da1 * sigma_prime_z1 *1print(f"梯度 ∂L/∂w1 = (∂L/∂a1) * σ'(z1) * x = {dL_dw1:.4f}")print(f"梯度 ∂L/∂b1 = (∂L/∂a1) * σ'(z1) * 1 = {dL_db1:.4f}")

输出示例:

真实值 y_true = 2.5

预测值 y_pred = 1.9899

初始损失 Loss = 0.2602



--- 开始反向传播计算梯度 ---

梯度 ∂L/∂y_pred = -2*(y_true - y_pred) = -1.0202

梯度 ∂L/∂w2 = (∂L/∂y_pred) * a1 = -1.0134

梯度 ∂L/∂b2 = (∂L/∂y_pred) * 1 = -1.0202

梯度 ∂L/∂a1 = (∂L/∂y_pred) * w2 = -1.5303

Sigmoid导数 σ'(z1) = σ(z1)*(1-σ(z1)) = 0.0066

梯度 ∂L/∂w1 = (∂L/∂a1) * σ'(z1) * x = -0.0304

梯度 ∂L/∂b1 = (∂L/∂a1) * σ'(z1) * 1 = -0.0101

现在,我们得到了所有参数的梯度。这些负值意味着,如果 增加 这些参数的值,损失会 增大 (因为梯度方向是上升方向)。为了减小损失,我们应该 沿着梯度的反方向 调整参数。


参数更新:梯度下降

拿到梯度后,我们使用 梯度下降 算法来更新参数:

参数 = 参数 - 学习率 * 该参数的梯度

其中, 学习率 是一个非常重要的超参数,它控制了每次参数更新的步长。步长太小,学习缓慢;步长太大,可能无法收敛甚至发散。

代码示例:应用梯度下降更新参数

示例代码
learning_rate=0.1# 更新参数w1_new=w1 - learning_rate * dL_dw1b1_new=b1 - learning_rate * dL_db1w2_new=w2 - learning_rate * dL_dw2b2_new=b2 - learning_rate * dL_db2print("--- 更新后的参数 ---")print(f"w1: {w1:.4f} -> {w1_new:.4f}")print(f"b1: {b1:.4f} -> {b1_new:.4f}")print(f"w2: {w2:.4f} -> {w2_new:.4f}")print(f"b2: {b2:.4f} -> {b2_new:.4f}")# 用新参数做一次前向传播,验证损失是否减小defforward_pass_with_params(x,w1,b1,w2,b2):z1=w1 * x + b1a1=sigmoid(z1)y_pred=w2 * a1 + b2returny_predy_pred_new=forward_pass_with_params(x_input,w1_new,b1_new,w2_new,b2_new)loss_new=(y_true - y_pred_new)**2print(f"\n用新参数预测: y_pred_new = {y_pred_new:.4f}")print(f"更新后的损失 New Loss = {loss_new:.4f}")print(f"损失变化: {loss_new - (y_true-y_pred)**2:.4f} (负值表示损失减小)")

输出示例:

--- 更新后的参数 ---

w1: 2.0000 -> 2.0030

b1: -1.0000 -> -0.9990

w2: 1.5000 -> 1.6013

b2: 0.5000 -> 0.6020



用新参数预测: y_pred_new = 2.1933

更新后的损失 New Loss = 0.0940

损失变化: -0.1662 (负值表示损失减小)

太好了!经过一次 前向传播 -> 计算损失 -> 反向传播 -> 梯度下降更新 的完整循环,我们的预测值 y_pred 1.99 更接近真实值 2.5 ,损失也从 0.260 下降到了 0.094 。将这个循环重复成千上万次(在大量数据上),神经网络就能学习到有效的参数,做出准确的预测。


实践练习:巩固你的理解

现在,是时候动手巩固所学知识了。

练习 1:扩展网络 修改上面的代码,将隐藏层神经元增加到2个。你需要初始化 w1 为一个形状为 (2,) 的数组(两个权重), b1 (2,) 的数组。相应地调整前向传播和反向传播的计算。观察网络能力的变化。

练习 2:更换激活函数 将Sigmoid激活函数替换为ReLU函数( f(x) = max(0, x) )。你需要重新推导并实现ReLU的导数( f'(x) = 1 if x>0 else 0 )。比较使用不同激活函数时,训练过程有何不同。

练习 3:实现一个训练循环 编写一个完整的训练循环,在某个简单数据集(例如,自己构造 y = 2x + 1 + 噪声 的数据)上,训练一个微型网络去拟合它。设置迭代次数(epoch),在每次迭代后打印损失,观察损失是否随着训练持续下降。

练习 4:理解学习率的影响 在练习3的基础上,尝试不同的 learning_rate (如 0.01, 0.1, 0.5, 1.0)。观察学习率过大或过小时,损失曲线的变化(是震荡、发散还是收敛缓慢),深刻理解学习率作为步幅的重要性。


总结

前向传播与反向传播是神经网络学习的核心动态:

  1. 前向传播 推理路径 ,它利用当前参数将输入映射为输出,并计算当前表现的得分(损失)。
  2. 反向传播 学习算法 ,它利用链式法则,高效地计算出损失函数对网络中每一个参数的梯度,指明了参数优化的方向。
  3. 梯度下降 优化策略 ,它根据反向传播提供的梯度,以学习率为步长,实际更新参数,使网络的表现逐步改善。