神经网络反向传播基础知识点总结
本文档总结理解神经网络反向传播所需的核心数学概念,并以一个两层网络(输入层-隐藏层-输出层)为例,推导损失函数对权重 w1 和 w2 的梯度计算公式,最后讨论损失接近 0 时的训练注意事项。
1. 导数
导数描述单变量函数在某一点的变化率。
对函数 ,导数定义为:
直观上,导数就是函数曲线在 处的切线斜率。它表示当自变量 发生微小变化时,函数值 的变化方向和变化快慢。
2. 偏导数
当函数有多个自变量时(如 ),偏导数表示固定其余变量,只改变一个变量时的变化率。
- 对 的偏导:(把 视为常数)
- 对 的偏导:(把 视为常数)
在神经网络中,损失函数 依赖大量参数,因此要计算 对每个参数的偏导,这些偏导组成梯度(向量或矩阵)。
3. 链式法则
链式法则用于复合函数求导。
若:
则:
多变量情形下,若 且 ,则:
反向传播本质上就是链式法则在计算图上的逐层应用:从输出层向前一层层反传梯度。
4. 反向传播的核心思想
目标:求损失函数 对各参数的梯度(偏导数),并用梯度下降更新参数,使损失减小。
其中 是学习率。沿梯度反方向更新参数,可使损失下降。
5. 损失对 w2 的梯度推导
考虑两层网络:
- 输入 ,形状
- 隐藏层激活输出
temp_relu,形状 - 输出预测:
形状 。
损失函数(MSE 求和形式):
5.1 链式分解
第一项:
记作 grad_y_pred,形状 。
第二项由线性层得到:
5.2 矩阵形式
即:
grad_w2 = temp_relu.T.dot(grad_y_pred)
形状检查:
temp_relu.T:grad_y_pred:grad_w2: (与w2同形状)
等价逐元素形式:
6. 损失对 w1 的梯度推导
6.1 前向关系
隐藏层线性输出:
形状 。
ReLU 激活:
输出层:
6.2 链式分解
其中:
- 需通过 ReLU 反传得到
6.3 逐步推导
先求损失对 temp_relu 的梯度:
记为 grad_temp_relu,形状 。
ReLU 导数:
因此:
实现上可写作:
grad_temp = grad_temp_relu.copy()
grad_temp[temp <= 0] = 0
最后:
即:
grad_w1 = x.T.dot(grad_temp)
形状检查:
x.T:grad_temp:grad_w1: (与w1同形状)
6.4 合并后的完整公式
其中 为逐元素乘法,体现了 ReLU 的门控作用。
7. 当损失接近 0 时的训练现象
当达到理论最优 (即 )时:
则后续梯度 grad_w2、grad_temp_relu、grad_w1 都为 0,参数更新量也为 0:
模型参数不再变化,损失保持稳定。
实际数值中的情况
由于浮点精度限制, 往往是极小非零值(如 ),梯度也会很小。若学习率为 ,更新量可能只有 量级,几乎可忽略。
训练注意事项
- 学习率过大时,即使梯度很小,也可能引起轻微震荡或越过最优点
- 可使用早停(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 函数定义为 ,在 处左导数为 0,右导数为 1。在实现中通常将 的梯度设为 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)背后的逻辑:
- 局部梯度如何定义(导数/偏导数)
- 梯度如何通过链式法则逐层传递
- 矩阵形式下梯度的形状与计算规则
这也是从”会调用框架”走向”理解训练机制”的关键基础。
参考资源
神经网络反向传播基础知识点总结
https://github.com/px6707/myblogpanxiao
2026 - 03 - 17
Unlicensed
评论