13 梯度检查(Gradient Checking)
一、目的与意义
梯度检查是一种验证反向传播(Backpropagation)实现是否正确的重要调试技术。
- 在手动推导或编码计算梯度时,极易引入细微错误。
- 梯度检查通过数值微分近似真实梯度,并与反向传播计算出的解析梯度进行比对,从而发现 bug。
- 吴恩达强调: “它帮我节省了大量时间,多次发现反向传播中的错误。”
二、基本思想
神经网络的参数包括所有权重 $W^{[1]}, W^{[2]}, \dots, W^{[L]}$ 和偏置 $b^{[1]}, b^{[2]}, \dots, b^{[L]}$。
为统一处理,将这些参数拼接成一个大向量 $\theta$:
$$
\theta = \begin{bmatrix}
\text{vec}(W^{[1]}) \
\text{vec}(b^{[1]}) \
\text{vec}(W^{[2]}) \
\text{vec}(b^{[2]}) \
\vdots \
\text{vec}(W^{[L]}) \
\text{vec}(b^{[L]})
\end{bmatrix}
$$
其中 $\text{vec}(\cdot)$ 表示将矩阵按列(或行)展开为向量。
此时,损失函数 $J$ 可视为 $\theta$ 的函数:
$$
J = J(\theta)
$$
同理,反向传播得到的梯度(即 $\frac{\partial J}{\partial W^{[l]}}$, $\frac{\partial J}{\partial b^{[l]}}$)也可拼接为与 $\theta$ 同维的向量:
$$
d\theta = \begin{bmatrix}
\text{vec}(dW^{[1]}) \
\text{vec}(db^{[1]}) \
\vdots \
\text{vec}(dW^{[L]}) \
\text{vec}(db^{[L]})
\end{bmatrix}
$$
三、数值梯度近似(Numerical Gradient Approximation)
对每个参数 $\theta_i$,使用中心差分法(two-sided difference) 计算其数值梯度:
$$
d\theta_{\text{approx}, i} = \frac{J(\theta_1, \dots, \theta_i + \varepsilon, \dots) - J(\theta_1, \dots, \theta_i - \varepsilon, \dots)}{2\varepsilon}
$$
其中 $\varepsilon$ 是一个很小的正数(通常取 $\varepsilon = 10^{-7}$)。
✅ 为什么用中心差分?
相比前向差分 $\frac{J(\theta + \varepsilon) - J(\theta)}{\varepsilon}$,中心差分的误差是 $O(\varepsilon^2)$,精度更高。
对所有 $i$ 执行上述操作,得到整个数值梯度向量 $d\theta_{\text{approx}}$。
四、梯度一致性检验
比较数值梯度 $d\theta_{\text{approx}}$ 与反向传播得到的解析梯度 $d\theta$。
使用相对误差(relative error) 来衡量两者接近程度:
$$
\text{error} = \frac{| d\theta_{\text{approx}} - d\theta |2}{| d\theta{\text{approx}} |_2 + | d\theta |_2}
$$
其中 $| \cdot |_2$ 表示欧几里得范数(L2 范数)。
判断标准:
| error 值范围 | 含义 |
|---|---|
| $< 10^{-7}$ | ✅ 非常好,梯度很可能正确 |
| $\sim 10^{-5}$ | ⚠️ 可接受,但建议检查是否有个别分量偏差大 |
| $> 10^{-3}$ | ❌ 极可能有 bug,必须排查 |
🔍 若 error 较大,可逐个检查 $|d\theta_{\text{approx}, i} - d\theta_i|$,定位具体哪一层或哪个参数出错。
五、实施步骤(算法流程)
- 前向传播:计算损失 $J$。
- 反向传播:计算解析梯度 $d\theta$。
- 数值梯度:对每个 $\theta_i$,用中心差分计算 $d\theta_{\text{approx}, i}$。
- 计算 error:用上述公式评估一致性。
- 判断并调试:若 error 过大,检查反向传播实现。
六、注意事项(Tips)
- 仅在调试阶段使用:梯度检查计算开销大(需多次前向传播),训练时应关闭。
- 确保随机种子固定:避免因 dropout、数据 shuffle 等引入随机性,影响梯度比较。
- 先用小网络测试:例如 1 层、少量神经元,便于快速验证。
- 不要在有正则化/BN/dropout 时直接检查:需确保数值梯度和解析梯度在完全相同条件下计算。
七、关键公式
import numpy as np |
🔧 使用示例
假设你已将网络所有参数拼接为 theta,并通过反向传播得到 dtheta,并定义了一个能接受 theta 并返回损失的函数 compute_cost:
# 示例:简单二次损失 |
输出可能为:
Gradient check error: 1.23e-10 |
📌 注意事项
-
compute_cost 必须是一个纯函数:给定theta就返回确定的损失值(关闭 dropout、固定 batch 等)。 - 实际神经网络中,你需要先将
W 和b 展平拼接为 theta,反向传播后也将dW,db 拼接为dtheta。 - 在调试完成后,务必关闭梯度检查,因其时间复杂度为 $O(n)$ 次前向传播,非常慢。
这份代码可直接用于你的深度学习项目中进行梯度验证,是吴恩达课程思想的工程化实现。
✅ 总结
梯度检查是深度学习实现中不可或缺的调试工具。虽然现代框架(如 PyTorch、TensorFlow)自动求导降低了手动实现需求,但在自定义层、损失函数或研究新算法时,掌握梯度检查仍至关重要。
“当你实现了一个新的神经网络,先写反向传播,再用梯度检查验证——这是专业做法。”