神经网络反向传播基础知识点总结

本文档总结理解神经网络反向传播所需的核心数学概念,并以一个两层网络(输入层-隐藏层-输出层)为例,推导损失函数对权重 w1w2 的梯度计算公式,最后讨论损失接近 0 时的训练注意事项。


1. 导数

导数描述单变量函数在某一点的变化率。

对函数 f(x)f(x),导数定义为:

f(x)=dfdx=limΔx0f(x+Δx)f(x)Δxf'(x)=\frac{df}{dx}=\lim_{\Delta x\to 0}\frac{f(x+\Delta x)-f(x)}{\Delta x}

直观上,导数就是函数曲线在 xx 处的切线斜率。它表示当自变量 xx 发生微小变化时,函数值 f(x)f(x) 的变化方向和变化快慢。


2. 偏导数

当函数有多个自变量时(如 f(x,y)f(x,y)),偏导数表示固定其余变量,只改变一个变量时的变化率。

  • xx 的偏导:fx\frac{\partial f}{\partial x}(把 yy 视为常数)
  • yy 的偏导:fy\frac{\partial f}{\partial y}(把 xx 视为常数)

在神经网络中,损失函数 LL 依赖大量参数,因此要计算 LL 对每个参数的偏导,这些偏导组成梯度(向量或矩阵)。


3. 链式法则

链式法则用于复合函数求导。

若:

y=f(g(x))y=f(g(x))

则:

dydx=dfdgdgdx\frac{dy}{dx}=\frac{df}{dg}\cdot\frac{dg}{dx}

多变量情形下,若 L=f(u)L=f(u)u=g(w)u=g(w),则:

Lw=Luuw\frac{\partial L}{\partial w}=\frac{\partial L}{\partial u}\cdot\frac{\partial u}{\partial w}

反向传播本质上就是链式法则在计算图上的逐层应用:从输出层向前一层层反传梯度。


4. 反向传播的核心思想

目标:求损失函数 LL 对各参数的梯度(偏导数),并用梯度下降更新参数,使损失减小。

wnew=woldηLww_{\text{new}}=w_{\text{old}}-\eta\cdot\frac{\partial L}{\partial w}

其中 η\eta 是学习率。沿梯度反方向更新参数,可使损失下降。


5. 损失对 w2 的梯度推导

考虑两层网络:

  • 输入 xx,形状 (n,din)(n,d_{in})
  • 隐藏层激活输出 temp_relu,形状 (n,h)(n,h)
  • 输出预测:
ypred=temp_reluw2y_{pred}=temp\_relu\cdot w_2

形状 (n,dout)(n,d_{out})

损失函数(MSE 求和形式):

L=i=1nk=1dout(ypred,ikyik)2L=\sum_{i=1}^{n}\sum_{k=1}^{d_{out}}(y_{pred,ik}-y_{ik})^2

5.1 链式分解

Lw2=Lypredypredw2\frac{\partial L}{\partial w_2}=\frac{\partial L}{\partial y_{pred}}\cdot\frac{\partial y_{pred}}{\partial w_2}

第一项:

Lypred=2(ypredy)\frac{\partial L}{\partial y_{pred}}=2(y_{pred}-y)

记作 grad_y_pred,形状 (n,dout)(n,d_{out})

第二项由线性层得到:

ypredw2=temp_reluT\frac{\partial y_{pred}}{\partial w_2}=temp\_relu^T

5.2 矩阵形式

Lw2=temp_reluTLypred\frac{\partial L}{\partial w_2}=temp\_relu^T\cdot\frac{\partial L}{\partial y_{pred}}

即:

grad_w2 = temp_relu.T.dot(grad_y_pred)

形状检查:

  • temp_relu.T: (h,n)(h,n)
  • grad_y_pred: (n,dout)(n,d_{out})
  • grad_w2: (h,dout)(h,d_{out})(与 w2 同形状)

等价逐元素形式:

L(w2)jk=i=1n(temp_relu)ij(grad_y_pred)ik\frac{\partial L}{\partial (w_2)_{jk}}=\sum_{i=1}^{n}(temp\_relu)_{ij}(grad\_y\_pred)_{ik}

6. 损失对 w1 的梯度推导

6.1 前向关系

隐藏层线性输出:

temp=xw1temp=x\cdot w_1

形状 (n,h)(n,h)

ReLU 激活:

temp_relu=max(0,temp)temp\_relu=\max(0,temp)

输出层:

ypred=temp_reluw2y_{pred}=temp\_relu\cdot w_2

6.2 链式分解

Lw1=Ltemptempw1\frac{\partial L}{\partial w_1}=\frac{\partial L}{\partial temp}\cdot\frac{\partial temp}{\partial w_1}

其中:

  • Ltemp\frac{\partial L}{\partial temp} 需通过 ReLU 反传得到
  • tempw1=xT\frac{\partial temp}{\partial w_1}=x^T

6.3 逐步推导

先求损失对 temp_relu 的梯度:

Ltemp_relu=Lypredypredtemp_relu=grad_y_predw2T\frac{\partial L}{\partial temp\_relu}=\frac{\partial L}{\partial y_{pred}}\cdot\frac{\partial y_{pred}}{\partial temp\_relu}=grad\_y\_pred\cdot w_2^T

记为 grad_temp_relu,形状 (n,h)(n,h)

ReLU 导数:

temp_relutemp={1,temp>00,temp0\frac{\partial temp\_relu}{\partial temp}= \begin{cases} 1, & temp>0 \\ 0, & temp\le 0 \end{cases}

因此:

Ltemp=Ltemp_relu1temp>0\frac{\partial L}{\partial temp}=\frac{\partial L}{\partial temp\_relu}\odot\mathbf{1}_{temp>0}

实现上可写作:

grad_temp = grad_temp_relu.copy()
grad_temp[temp <= 0] = 0

最后:

Lw1=xTLtemp\frac{\partial L}{\partial w_1}=x^T\cdot\frac{\partial L}{\partial temp}

即:

grad_w1 = x.T.dot(grad_temp)

形状检查:

  • x.T: (din,n)(d_{in},n)
  • grad_temp: (n,h)(n,h)
  • grad_w1: (din,h)(d_{in},h)(与 w1 同形状)

6.4 合并后的完整公式

Lw1=xT[(2(ypredy)w2T)1temp>0]\frac{\partial L}{\partial w_1}=x^T\cdot\left[(2(y_{pred}-y)\cdot w_2^T)\odot\mathbf{1}_{temp>0}\right]

其中 \odot 为逐元素乘法,体现了 ReLU 的门控作用。


7. 当损失接近 0 时的训练现象

当达到理论最优 L=0L=0(即 ypred=yy_{pred}=y)时:

grad_y_pred=2(ypredy)=0grad\_y\_pred=2(y_{pred}-y)=0

则后续梯度 grad_w2grad_temp_relugrad_w1 都为 0,参数更新量也为 0:

ηgrad=0-\eta\cdot grad=0

模型参数不再变化,损失保持稳定。

实际数值中的情况

由于浮点精度限制,ypredyy_{pred}-y 往往是极小非零值(如 101510^{-15}),梯度也会很小。若学习率为 10610^{-6},更新量可能只有 102110^{-21} 量级,几乎可忽略。

训练注意事项

  • 学习率过大时,即使梯度很小,也可能引起轻微震荡或越过最优点
  • 可使用早停(Early Stopping):当损失在连续若干轮下降幅度低于阈值时提前停止训练
  • 真实任务中损失严格为 0 很少见(数据噪声、模型误差等原因),但该极端情况有助于理解梯度下降的数值行为

8. NumPy 完整实现

import numpy as np
import matplotlib.pyplot as plt

# 设置中文字体,解决中文显示乱码问题
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

# n为样本大小,d_in为输入维度,h为隐藏层维度,d_out为输出维度
n, d_in, h, d_out = 64, 1000, 100, 10

# 随机生成输入数据x和目标输出y
x = np.random.randn(n, d_in)      # 输入数据,形状为(64, 1000)
y = np.random.randn(n, d_out)     # 目标输出,形状为(64, 10)

# 随机初始化权重参数
w1 = np.random.randn(d_in, h)     # 输入层到隐藏层的权重 (1000, 100)
w2 = np.random.randn(h, d_out)    # 隐藏层到输出层的权重 (100, 10)
learning_rate = 1e-6              # 学习率

# 用于记录每次迭代的loss值
loss_history = []

# 训练500次
for t in range(500):
    # 前向传播
    temp = x.dot(w1)                  # 输入层到隐藏层的线性变换
    temp_relu = np.maximum(temp, 0)   # ReLU激活函数,隐藏层输出
    y_pred = temp_relu.dot(w2)        # 隐藏层到输出层的线性变换,得到预测值

    # 计算损失函数(均方误差和)
    loss = np.square(y_pred - y).sum()
    loss_history.append(loss)
    print(t, loss)

    # 反向传播,计算梯度
    grad_y_pred = 2.0 * (y_pred - y)              # 损失对预测输出的梯度
    grad_w2 = temp_relu.T.dot(grad_y_pred)        # 损失对w2的梯度
    grad_temp_relu = grad_y_pred.dot(w2.T)        # 损失对隐藏层输出的梯度
    grad_temp = grad_temp_relu.copy()             # 复制一份用于ReLU处理
    grad_temp[temp <= 0] = 0                       # ReLU小于等于0的部分梯度置零
    grad_w1 = x.T.dot(grad_temp)                  # 损失对w1的梯度

    # 更新权重参数
    w1 = w1 - learning_rate * grad_w1
    w2 = w2 - learning_rate * grad_w2

# 绘制Loss曲线
plt.figure(figsize=(10, 6))
plt.plot(loss_history, 'b-', linewidth=2)
plt.title('训练过程中的Loss变化曲线', fontsize=14)
plt.xlabel('迭代次数', fontsize=12)
plt.ylabel('Loss值', fontsize=12)
plt.grid(True, alpha=0.3)
plt.show()

# 输出最终训练得到的权重参数
print("最终权重 w1:", w1)
print("最终权重 w2:", w2)

9. 错误说明

校验原文代码和推导,发现以下问题:

9.1 代码错误(已修正)

错误 1:ReLU 梯度条件不完整

# 原文(错误)
grad_temp[temp < 0] = 0

# 修正后
grad_temp[temp <= 0] = 0

原因:ReLU 函数定义为 max(0,x)\max(0, x),在 x=0x=0 处左导数为 0,右导数为 1。在实现中通常将 x0x \leq 0 的梯度设为 0,这与 np.maximum(temp, 0) 的导数定义一致。

9.2 概念说明

注意:代码中的损失函数是 MSE 的求和形式(sum),而不是平均值:

loss = np.square(y_pred - y).sum()  # 求和形式

因此梯度是:

grad_y_pred = 2.0 * (y_pred - y)  # 正确

如果是平均 MSE:

loss = np.mean(np.square(y_pred - y))  # 平均形式
grad_y_pred = 2.0 * (y_pred - y) / n   # 需要除以 n

总结

反向传播的本质是链式法则在计算图中的系统化应用。掌握以下三点即可理解自动微分框架(如 PyTorch、TensorFlow)背后的逻辑:

  1. 局部梯度如何定义(导数/偏导数)
  2. 梯度如何通过链式法则逐层传递
  3. 矩阵形式下梯度的形状与计算规则

这也是从”会调用框架”走向”理解训练机制”的关键基础。


参考资源

神经网络反向传播基础知识点总结

https://github.com/px6707/myblog
作者

panxiao

发布日期

2026 - 03 - 17

许可证

Unlicensed

评论