跳到主要内容

2-神经网络反馈

2026-5-7重启深度学习,随笔记录一些不理解且容易遗忘的知识点,学习路线跟随复旦大学邱锡鹏老师

此文记录深度学习中的反向传播,以及自己的迷惑点和解答,参考csdn-blog

:::

1.反向传播基本概念

前向传播

在前向传播中,输入数据从输入层通过隐藏层传递到输出层。网络通过层与层之间的连接(即权重)来计算每个节点的输出,最终生成网络的预测结果。也就是从头走到尾,逐层计算输出。

计算损失

将网络的预测输出与真实值进行比较,计算损失函数(如均方误差),用来衡量网络的预测输出与真实值的差距。

反向传播

反向传播的过程主要由链式法则驱动。它通过逐层计算误差对权重的偏导数(梯度),从输出层反向传递到隐藏层,再传递到输入层(与前向传播顺序相反),以反向更新每层的权重,减少预测误差。

  • 前向传播相当于将输入数据从输入层逐步传递到输出层,得到预测结果。
  • 反向传播相当于从输出层开始反向传递误差,更新每一层的权重,使得网络在下次预测时能够减少误差。

权重更新

使用优化算法(如梯度下降)根据梯度更新权重。使得下一次前向传播时损失函数值减小。

2.反向传播计算

反向传播图示

image-20260512110131537

  • 前向传播: 蓝色,负责计算经过神经元后的输出值
  • 反向传播:红色,输出误差,逐层反向传递给前面的隐藏层神经元,计算权重梯度,后续更新

反向传播示例

image-20260512140336071

如上图是一个简单的两层神经网络:

  • 输入层(x):一个节点,输入值为 xx
  • 隐藏层(a):一个节点,激活函数为 SigmoidSigmoid 函数。
  • 输出层(y):一个节点,激活函数为线性函数,输出值为 yy
  • 权重(w):输入w1w_1w2w_2

隐藏层输入: z=w1xz = w_1 \cdot x

隐藏层的激活输出(使用 Sigmoid 函数): a=σ(z)=11+eza = \sigma(z) = \frac{1}{1 + e^{-z}}

计算输出层的输入和输出:

y=w2ay = w_2 \cdot a

计算损失:

L=12(yt)2L = \frac{1}{2}(y - t)^2

计算输出层对权重 w2w_2 的梯度

Lw2=Lyyw2\frac{\partial L}{\partial w_2} = \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial w_2}

计算隐藏层对权重w2w_2的梯度

Lw1=Laazzw1\frac{\partial L}{\partial w_1} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z} {\partial w_1}

代入数据计算梯度,同时引入η\eta,也就是学习率,更新步长,计算更新梯度w2(new)w_2^{(new)}

w2(new)=w2ηLw2w_2^{(new)}=w_2 - \eta \cdot \frac{\partial L}{\partial w_2}

然后代入新权重计算新的层,计算输出,计算损失函数的值,如果lossloss有变小,说明更新方向正确,也就是有在变好。

3.为什么是反向传播

我一开始的疑惑就是,为什么是反向传播?为什么梯度更新是从后往前推,而且对于隐藏层神经元,为什么不是在当前的神经元上更新自己的梯度?再推导下去?(思考之后发现自己的这个问题确实很蠢)

3.1.链式核心

反向传播的核心就是求导的链式法则,计算梯度求偏导,在最后一层,就可以抽茧剥丝的逐层往前计算梯度,因为当前层梯度计算,可以往前递用计算。我们的目标是求出损失函数 LL 对网络中每一个权重 ww 和偏置 ww 的偏导数(梯度),然后用梯度下降法更新它们。

3.2. 求输出层的梯度(从后往前第一步)

设两层神经元

首先,我们计算损失 LL 对输出层参数 w1(2),w2(2),b(2)w_1^{(2)}, w_2^{(2)}, b^{(2)} 的偏导。

引入中间误差项 δ(2)\delta^{(2)}(表示损失对输出层未激活值 z(2)z^{(2)} 的导数):

δ(2)=Lz(2)=Ly^y^z(2)=(y^y)f(z(2))\delta^{(2)} = \frac{\partial L}{\partial z^{(2)}} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z^{(2)}} = (\hat{y} - y) \cdot f'(z^{(2)})

有了 δ(2)\delta^{(2)},输出层权重的梯度就很简单了:

Lw1(2)=Lz(2)z(2)w1(2)=δ(2)a1(1)\frac{\partial L}{\partial w_1^{(2)}} = \frac{\partial L}{\partial z^{(2)}} \cdot \frac{\partial z^{(2)}}{\partial w_1^{(2)}} = \delta^{(2)} \cdot a_1^{(1)} Lw2(2)=δ(2)a2(1)\frac{\partial L}{\partial w_2^{(2)}} = \delta^{(2)} \cdot a_2^{(1)} Lb(2)=δ(2)1=δ(2)\frac{\partial L}{\partial b^{(2)}} = \delta^{(2)} \cdot 1 = \delta^{(2)}

3.3. 求隐藏层(2个神经元)的梯度(从后往前第二步)

现在我们需要求损失 LL 对隐藏层参数(例如 w11(1)w_{11}^{(1)})的偏导。根据链式法则,误差必须穿过隐藏层的神经元传导回来。

我们需要先计算隐藏层神经元 1 和 2 的误差项 δ1(1)\delta_1^{(1)}δ2(1)\delta_2^{(1)}

对于隐藏层神经元 1:

δ1(1)=Lz1(1)=Lz(2)z(2)a1(1)a1(1)z1(1)\delta_1^{(1)} = \frac{\partial L}{\partial z_1^{(1)}} = \frac{\partial L}{\partial z^{(2)}} \cdot \frac{\partial z^{(2)}}{\partial a_1^{(1)}} \cdot \frac{\partial a_1^{(1)}}{\partial z_1^{(1)}}

代入已知项,发现它高度依赖于后一层的误差 δ(2)\delta^{(2)}

δ1(1)=δ(2)w1(2)f(z1(1))\delta_1^{(1)} = \delta^{(2)} \cdot w_1^{(2)} \cdot f'(z_1^{(1)})

对于隐藏层神经元 2:

δ2(1)=δ(2)w2(2)f(z2(1))\delta_2^{(1)} = \delta^{(2)} \cdot w_2^{(2)} \cdot f'(z_2^{(1)})

有了隐藏层的误差项,我们就可以求出隐藏层的权重和偏置梯度:

Lw11(1)=δ1(1)x1\frac{\partial L}{\partial w_{11}^{(1)}} = \delta_1^{(1)} \cdot x_1 Lw12(1)=δ1(1)x2\frac{\partial L}{\partial w_{12}^{(1)}} = \delta_1^{(1)} \cdot x_2 Lb1(1)=δ1(1)\frac{\partial L}{\partial b_{1}^{(1)}} = \delta_1^{(1)}

(神经元2的参数 w21(1),w22(1),b2(1)w_{21}^{(1)}, w_{22}^{(1)}, b_2^{(1)} 同理,将 δ1(1)\delta_1^{(1)} 换成 δ2(1)\delta_2^{(1)} 即可)

通过上面的公式推导,为什么反向传播必须 从后往前 (Backward) 计算。原因有两点:

3.4. 数学上的依赖关系(链式法则的必然)

请观察隐藏层神经元 1 的误差公式:

δ1(1)=δ(2)w1(2)f(z1(1))\delta_1^{(1)} = \delta^{(2)} \cdot w_1^{(2)} \cdot f'(z_1^{(1)})

在数学计算上,当前层(隐藏层)的梯度,直接依赖于下一层(输出层)的误差项 δ(2)\delta^{(2)}。 如果你从前往后算(先算隐藏层),你此时根本不知道 δ(2)\delta^{(2)} 是多少,计算无法进行。只有先算出最后一层的误差,才能像“剥洋葱”一样,一层一层把误差按权重分配退回去。

3.5. 计算效率极高(避免重复计算)

反向传播本质上是一种动态规划 (Dynamic Programming)。 在推导中你可能会发现,δ(2)\delta^{(2)} 被使用了多次:

  • 计算 Lw1(2)\frac{\partial L}{\partial w_1^{(2)}} 用到了它
  • 计算 Lw2(2)\frac{\partial L}{\partial w_2^{(2)}} 用到了它
  • 计算 δ1(1)\delta_1^{(1)} 用到了它
  • 计算 δ2(1)\delta_2^{(1)} 也用到了它

如果我们“从前向后”求导(前向微分模式),为了求某一个输入权重的梯度,我们需要把整个网络的导数顺着推一遍。如果有 1000 个参数,就要正向推 1000 遍。 而“从后向前”算(反向传播),我们只需要反向扫过网络一次。算出后一层的 δ\delta 后,将其缓存起来,前一层可以直接拿来乘,这使得计算复杂度从 O(N2)O(N^2) 骤降到了 O(N)O(N),这也是深度学习能够有效训练成百上千层网络的核心基石。

3.6.为什么不逐节点

有想过,直接更新w1w_1w2w_2,在当前节点计算不好吗,每次输出都计算了,这里是不现实的,这样就会导致网络断了,每个节点只考虑自己目前的状况。

打个比方,你负责生产产品A,然后交付同事处理产品A,输出B,继续往下......如果你私自更改了流程,A不再是A,同事拿到流程不对生产的A,工作会出问题,正确的应该是,从最终产品输出端,根据需求调整,逐层往前反馈,同事告诉你,我要A改成.......你再修改,这样是最好的,也符合神经网络,网络这一特性。