Python 深度学习入门:反向传播到底在做什么?为什么梯度能一层层传回来(附三层网络手写示例)

很多初学者在学完“梯度下降到底在做什么”之后,都会遇到一个很自然的问题:

既然训练时要根据损失去更新参数,
那么网络里这么多层、这么多参数,它们各自的梯度到底是怎么来的?

这其实就是反向传播要解决的问题。

前面我们已经知道:

  • 机器学习不是抽象地“让机器变聪明”,而是在学习从输入到输出的映射关系
  • 模型真正能调整的是参数,参数决定了模型如何对输入做出反应
  • 损失函数的作用,是把预测结果和真实答案之间的差距,变成一个可以计算、可以优化的数值

但当模型从单层变成多层之后,一个新的问题就出现了:

最后的损失,为什么能反过来指导前面每一层参数的更新?

这就是反向传播的核心。

很多人一听“反向传播”,就会想到复杂公式。
但从本质上说,它做的事情其实很明确:

从最终损失出发,沿着网络的计算路径,一层层算出每个参数对损失的影响。

本文围绕“反向传播到底在做什么”这一主题,介绍:

  • 为什么训练多层网络一定会用到反向传播
  • 反向传播和梯度下降到底有什么区别
  • 为什么反向传播离不开链式法则
  • 如何用一个三层小网络手写反向传播过程
  • 如何通过代码和结果,真正看懂“梯度是怎么传回来的”

摘要

反向传播是神经网络训练中的核心机制之一,它解决的不是“参数怎么更新”,而是“每个参数的梯度怎么得到”。训练的目标本质上是不断调整参数,让损失函数变小 ;而在多层网络中,参数往往不会直接影响损失,而是通过中间层间接影响输出,因此必须借助链式法则,把损失对各层参数的影响一层层算回去。本文以一个三层线性网络为例,结合公式、手写 Python 代码和结果分析,说明反向传播到底在做什么,以及为什么多层网络的训练离不开它。


一、为什么要学习反向传播?

如果只看模型训练的表面流程,事情好像很简单:

  1. 输入数据进入模型
  2. 模型给出预测结果
  3. 预测结果和真实答案进行比较
  4. 然后更新参数

但问题是,“更新参数”这一步并不是一句话就能完成的。

因为在真实模型里,参数通常不止一个。
它们可能分布在不同层里,彼此之间还通过很多中间变量连接在一起。

而前面已经讲过,训练的目标不是直接去“提高准确率”,而是不断调整参数,让损失函数变小 。

所以训练时真正要回答的问题其实是:

每个参数变化一点点,损失会怎么变?

如果是一个很简单的模型,这件事也许还能手算。
但一旦模型变成多层网络,参数数量一多,关系一复杂,就必须有一种系统的方法,把这些梯度算出来。

这就是为什么要学习反向传播。


二、反向传播到底在解决什么问题?

一句话概括:

反向传播解决的是“梯度怎么来”的问题。

这一点一定要和梯度下降区分开。

梯度下降解决的是

  • 已经知道梯度以后,参数该怎么更新

比如:

w←w−η∂L∂w w \leftarrow w - \eta \frac{\partial L}{\partial w} wwηwL

这里说的是:

如果已经得到了 ∂L∂w\frac{\partial L}{\partial w}wL,那就沿着让损失减小的方向去更新参数。

反向传播解决的是

  • 这个梯度 ∂L∂w\frac{\partial L}{\partial w}wL 到底怎么求出来

所以可以把两者理解成分工关系:

  • 反向传播负责算梯度
  • 梯度下降负责用梯度更新参数

如果没有反向传播,梯度下降就没有“材料”可用。


三、为什么多层网络一定会遇到这个问题?

在单层模型里,一个参数对输出的影响路径比较短。
但在多层网络里,一个前面层的参数,往往不会直接影响损失,而是要经过很多中间步骤。

比如这样一条链:

输入 x
→ 第1层输出 z1
→ 第2层输出 z2
→ 最终输出 y_hat
→ 损失 L

如果我们关心的是第1层某个参数 w1w_1w1 对损失 LLL 的影响,
那它不是直接作用在损失上的,而是:

  • 先影响第1层输出
  • 再影响第2层输出
  • 再影响最终预测
  • 最后才影响损失

所以:

越靠前的参数,离损失越远,影响链条也越长。

这就决定了,多层网络训练时不能只看最后一层。
必须有一种方法,把最终损失对前面参数的影响一层层传回来。

这正是反向传播存在的意义。

另外,前面也已经讨论过,单层模型表达能力有限,多层结构的关键不只是“更大”,而是开始具备中间表示和更复杂的组合关系 。
但多层网络要真正学起来,不能只靠“结构存在”,还必须能训练,而训练就离不开反向传播。


四、反向传播为什么离不开链式法则?

如果只用一句话概括反向传播的数学基础,那就是:

链式法则。

原因很简单:

在神经网络里,一个参数通常不会直接影响最终损失,
而是通过多个中间变量,间接地影响最终结果。

所以要计算“参数对损失的影响”,就必须把这条路径拆成一段一段,然后把每一段的影响连起来。

比如:

w1→z1→z2→yhat→L w_1 \rightarrow z_1 \rightarrow z_2 \rightarrow y_{\text{hat}} \rightarrow L w1z1z2yhatL

那么 w1w_1w1 对损失的影响,就不能跳着算,而必须沿着路径写成:

∂L∂w1=∂L∂yhat⋅∂yhat∂z2⋅∂z2∂z1⋅∂z1∂w1 \frac{\partial L}{\partial w_1} = \frac{\partial L}{\partial y_{\text{hat}}} \cdot \frac{\partial y_{\text{hat}}}{\partial z_2} \cdot \frac{\partial z_2}{\partial z_1} \cdot \frac{\partial z_1}{\partial w_1} w1L=yhatLz2yhatz1z2w1z1

这就是链式法则。

所以反向传播并不是某种神秘技巧,
它只是把“链式法则在多层网络里系统地执行了一遍”。


五、案例:一个三层小网络里,梯度是怎么传回来的?

为了把这个过程讲清楚,我们先不引入激活函数,也不引入复杂结构,
只看一个最简单的三层网络:

z1=w1x z_1 = w_1 x z1=w1x

z2=w2z1 z_2 = w_2 z_1 z2=w2z1

yhat=w3z2 y_{\text{hat}} = w_3 z_2 yhat=w3z2

L=(yhat−y)2 L = (y_{\text{hat}} - y)^2 L=(yhaty)2

这里:

  • xxx 是输入
  • yyy 是真实标签
  • w1,w2,w3w_1, w_2, w_3w1,w2,w3 是三层参数
  • LLL 是损失函数

这个例子虽然简单,但已经足够把反向传播的主线讲清楚。

前向传播在做什么?

先按顺序一层层算:

  • 输入 xxx 经过第一层,得到 z1z_1z1
  • z1z_1z1 再经过第二层,得到 z2z_2z2
  • z2z_2z2 再经过第三层,得到预测值 yhaty_{\text{hat}}yhat
  • 最后根据 yhaty_{\text{hat}}yhat 和真实值 yyy,计算损失 LLL

反向传播在做什么?

从最后的损失开始,沿着相反方向一层层往回求导:

  • 先算损失对输出的导数
  • 再算输出对上一层的导数
  • 再算上一层对更前一层的导数
  • 最后得到每个参数对损失的导数

这就是“反向传播”。


六、把具体数字代进去,会更容易看清楚

下面给这个三层网络代入一组具体数值:

x=2,y=1 x = 2,\quad y = 1 x=2,y=1

w1=0.5,w2=1.5,w3=2.0 w_1 = 0.5,\quad w_2 = 1.5,\quad w_3 = 2.0 w1=0.5,w2=1.5,w3=2.0


第一步:先做前向传播

先一层层算:

z1=w1x=0.5×2=1 z_1 = w_1 x = 0.5 \times 2 = 1 z1=w1x=0.5×2=1

z2=w2z1=1.5×1=1.5 z_2 = w_2 z_1 = 1.5 \times 1 = 1.5 z2=w2z1=1.5×1=1.5

yhat=w3z2=2.0×1.5=3 y_{\text{hat}} = w_3 z_2 = 2.0 \times 1.5 = 3 yhat=w3z2=2.0×1.5=3

L=(yhat−y)2=(3−1)2=4 L = (y_{\text{hat}} - y)^2 = (3 - 1)^2 = 4 L=(yhaty)2=(31)2=4

也就是说:

  • 第一层输出是 1
  • 第二层输出是 1.5
  • 最终预测值是 3
  • 损失是 4

第二步:开始反向传播

现在从损失开始,往回求导。

先看损失对预测值的导数:

∂L∂yhat=2(yhat−y)=2(3−1)=4 \frac{\partial L}{\partial y_{\text{hat}}} = 2(y_{\text{hat}}-y)=2(3-1)=4 yhatL=2(yhaty)=2(31)=4

接下来分别看后面各段关系。

第三层相关导数

∂yhat∂w3=z2=1.5 \frac{\partial y_{\text{hat}}}{\partial w_3} = z_2 = 1.5 w3yhat=z2=1.5

∂yhat∂z2=w3=2.0 \frac{\partial y_{\text{hat}}}{\partial z_2} = w_3 = 2.0 z2yhat=w3=2.0

第二层相关导数

∂z2∂w2=z1=1 \frac{\partial z_2}{\partial w_2} = z_1 = 1 w2z2=z1=1

∂z2∂z1=w2=1.5 \frac{\partial z_2}{\partial z_1} = w_2 = 1.5 z1z2=w2=1.5

第一层相关导数

∂z1∂w1=x=2 \frac{\partial z_1}{\partial w_1} = x = 2 w1z1=x=2


第三步:把链条乘起来

对第三层参数求导

∂L∂w3=∂L∂yhat⋅∂yhat∂w3=4⋅1.5=6 \frac{\partial L}{\partial w_3} = \frac{\partial L}{\partial y_{\text{hat}}} \cdot \frac{\partial y_{\text{hat}}}{\partial w_3} = 4 \cdot 1.5 = 6 w3L=yhatLw3yhat=41.5=6

对第二层参数求导

∂L∂w2=∂L∂yhat⋅∂yhat∂z2⋅∂z2∂w2=4⋅2.0⋅1=8 \frac{\partial L}{\partial w_2} = \frac{\partial L}{\partial y_{\text{hat}}} \cdot \frac{\partial y_{\text{hat}}}{\partial z_2} \cdot \frac{\partial z_2}{\partial w_2} = 4 \cdot 2.0 \cdot 1 = 8 w2L=yhatLz2yhatw2z2=42.01=8

对第一层参数求导

∂L∂w1=∂L∂yhat⋅∂yhat∂z2⋅∂z2∂z1⋅∂z1∂w1=4⋅2.0⋅1.5⋅2=24 \frac{\partial L}{\partial w_1} = \frac{\partial L}{\partial y_{\text{hat}}} \cdot \frac{\partial y_{\text{hat}}}{\partial z_2} \cdot \frac{\partial z_2}{\partial z_1} \cdot \frac{\partial z_1}{\partial w_1} = 4 \cdot 2.0 \cdot 1.5 \cdot 2 = 24 w1L=yhatLz2yhatz1z2w1z1=42.01.52=24

这时候就能很直观地看出:

  • 越靠后的参数,离损失越近,梯度链越短
  • 越靠前的参数,离损失越远,梯度链越长
  • 所谓“反向传播”,就是把这些影响一层层乘回去

七、Python 实战代码:手写一个最简单的反向传播过程

下面把上面的过程完整写成 Python 代码。

x = 2.0
y = 1.0
w1 = 0.5
w2 = 1.5
w3 = 2.0

# =========================
# 1. 前向传播
# =========================
z1 = w1 * x
z2 = w2 * z1
y_hat = w3 * z2
L = (y_hat - y) ** 2

# =========================
# 2. 反向传播
# =========================
dL_dyhat = 2 * (y_hat - y)
dyhat_dw3 = z2
dyhat_dz2 = w3
dz2_dw2 = z1
dz2_dz1 = w2
dz1_dw1 = x

dL_dw3 = dL_dyhat * dyhat_dw3
dL_dw2 = dL_dyhat * dyhat_dz2 * dz2_dw2
dL_dw1 = dL_dyhat * dyhat_dz2 * dz2_dz1 * dz1_dw1

# =========================
# 3. 输出结果
# =========================
print("前向传播结果:")
print("z1 =", z1)
print("z2 =", z2)
print("y_hat =", y_hat)
print("L =", L)

print("\n反向传播结果:")
print("dL/dw3 =", dL_dw3)
print("dL/dw2 =", dL_dw2)
print("dL/dw1 =", dL_dw1)

八、代码结果怎么看?

运行后会得到:

前向传播结果:
z1 = 1.0
z2 = 1.5
y_hat = 3.0
L = 4.0

反向传播结果:
dL/dw3 = 6.0
dL/dw2 = 8.0
dL/dw1 = 24.0

下面重点看这几个结果分别说明了什么。

1. z1z2y_hat 表示前向传播一层层算出来的中间结果

它们说明,网络不是一下子直接得到损失的,
而是先经过若干层计算,最后才得到预测值和损失。

这一步很重要,因为反向传播不是凭空出现的,
它一定是建立在前向传播已经完成的基础上。


2. dL/dw3dL/dw2dL/dw1 是各层参数的梯度

这三个值就是训练真正需要的东西。

因为训练的目标本质上是让损失函数变小 ,
而要想让损失变小,就必须知道每个参数变化时,损失会怎么变。

也就是说:

  • dL/dw3 = 6.0 表示第3层参数对损失的影响
  • dL/dw2 = 8.0 表示第2层参数对损失的影响
  • dL/dw1 = 24.0 表示第1层参数对损失的影响

这些导数不是随便算出来的,而是通过链式法则,一层层乘回来的。


3. 为什么最前面的 dL/dw1 最大?

这里要特别提醒一点:
梯度数值大,不代表这一层就“最重要”。

在这个例子里,dL/dw1 更大,只是因为它离损失更远,
它的梯度需要把后面多层的影响一起乘回来,所以数值会受到整条链的共同影响。

这正好说明了反向传播的核心特征:

前面的参数梯度,往往依赖后面所有层。

也正因为如此,网络一旦变深,梯度传播这件事就会变得更值得关注。


九、把这个过程画成一条链,会更容易理解“反向”是什么意思

这个三层网络可以写成下面这样的结构:

前向传播:
x → z1 → z2 → y_hat → L

如果把数值带进去,就是:

前向传播:
x = 2
→ z1 = 1
→ z2 = 1.5
→ y_hat = 3
→ L = 4

而反向传播则是沿着相反方向,把梯度一层层传回来:

反向传播:
L
→ dL/dy_hat = 4
→ dL/dw3 = 6
→ dL/dw2 = 8
→ dL/dw1 = 24

这时候“反向传播”这个名字就很容易理解了:

  • 前向传播:从输入走到损失
  • 反向传播:从损失回到参数

所以它的“反向”,反的不是数据本身,
而是导数的计算方向


十、为什么反向传播对多层网络特别重要?

前面已经讲过,单层模型能力有限,而多层网络之所以有意义,是因为它开始具备更复杂的中间表示和组合关系 。
但“有结构”不等于“能训练”。

多层网络之所以能学起来,不是因为它自己会变聪明,
而是因为训练过程中,损失能够通过反向传播,把每一层参数的梯度算出来,然后再交给梯度下降去更新。

所以从训练机制上看:

  • 多层网络提供表达能力
  • 损失函数提供优化目标
  • 反向传播提供梯度
  • 梯度下降完成参数更新

这几步合在一起,才构成了完整训练过程。


十一、学习反向传播时常见的误区

1. 把反向传播和梯度下降当成同一个东西

不是。
反向传播负责算梯度,梯度下降负责用梯度更新参数。

2. 以为反向传播是在“倒着传输入”

不是。
输入在前向传播中已经用过了,反向传播传回去的是导数信息,而不是原始输入。

3. 只会背链式法则,但不知道它为什么会出现

链式法则不是为了考试才写的公式,
而是因为在多层网络里,一个参数影响损失本来就必须经过很多中间变量,所以只能一段一段地连起来算。

4. 觉得反向传播很神秘

其实它并不神秘。
它做的事情很朴素:

从最终损失出发,把损失对每个参数的影响一层层算回去。


十二、总结

反向传播是神经网络训练中的核心步骤之一。
它解决的不是“参数怎么更新”,而是:

每个参数的梯度到底怎么得到。

通过本文的三层网络案例,可以建立以下几个基本认识:

  • 训练的目标本质上是让损失函数变小
  • 多层网络中,参数通常不会直接影响损失,而是通过中间层间接影响输出
  • 因此必须借助链式法则,把损失对参数的影响一层层算回去
  • 这整个过程,就是反向传播

从入门角度看,可以把反向传播概括为一句话:

反向传播并不是神秘算法,它只是把损失对参数的影响,沿着网络结构一层层算回来。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐