深度学习模型调试
调试按数据、shape/dtype、前向损失、梯度、参数更新、极小 batch 和曲线顺序。
学习目标
- 同一训练步检查四个诊断信号
- 损失下降但准确率不变一定是 bug 吗?
- 说明:小批次拟合成功是必要线索,不能证明完整评估协议和泛化能力正确。
为什么需要这个概念
模型调试是以可复现的小输入定位数据和计算链错误的过程。检查顺序从输入契约开始,逐步走到损失、梯度、参数变化和小样本拟合。
学习前需要掌握
背景与问题
模型不学习时,一次更换多个超参数会让原因更难定位。先把任务缩小到一批可检查样本,逐层验证数据、目标、梯度和更新,可以更快区分实现错误与泛化问题。
概念定义
模型调试是以可复现的小输入定位数据和计算链错误的过程。检查顺序从输入契约开始,逐步走到损失、梯度、参数变化和小样本拟合。
直观理解
先确认发动机能在台架上转起来,再去讨论长途油耗。一个连固定小批都无法拟合的模型,不适合立即扩大训练。
核心原理
首先打印输入范围、shape、dtype、标签集合与样本图像,核对是否缺少归一化、类别映射或目标对齐。对分类可检查初始损失是否与近似均匀预测的 log(C) 同量级,但这只是线索,不是通用断言。
然后检查 loss 是否有限、参数 grad 是否为 None 或异常大、step 前后参数是否改变。detach、错误优化器参数组、忘记 zero_grad 或 step 都能造成不同症状,应该用证据分别排除。
固定 8–32 条样本、关闭不必要增强和正则,尝试过拟合,是综合检查。即使成功,也只说明局部训练链可用;数据泄漏、分布差异和评估错误仍需单独排查。
数学表达
核心公式
模型调试是以可复现的小输入定位数据和计算链错误的过程。检查顺序从输入契约开始,逐步走到损失、梯度、参数变化和小样本拟合。
变量含义
Nbatch sizeD/H特征或隐藏维度
计算步骤
- 1确认预测和目标均为 (2,1)。
- 2检查损失有限且梯度非空。
- 3比较 step 前后的权重。
代码对应位置
示例 1同一训练步检查四个诊断信号的最小验证:使用完整构造输入核对同一训练步检查四个诊断信号,输出只表示本例计算。
变量解释
| 变量 | 含义 |
|---|---|
N | batch size |
D/H | 特征或隐藏维度 |
完整数值示例
同一训练步检查四个诊断信号
已知条件
- 线性模型初始权重为 0
- 两条输入 1、2,对应目标 2、4
- 使用 SGD 做一次更新
计算步骤
- 确认预测和目标均为 (2,1)。
- 检查损失有限且梯度非空。
- 比较 step 前后的权重。
计算结果
初始损失为 10,梯度为 -10,一次更新后权重从 0 变为 1。
处理前后对比
- 小批次拟合成功是必要线索,不能证明完整评估协议和泛化能力正确。
同一训练步检查四个诊断信号
若梯度为 None,应先检查损失是否依赖该参数,而不是立刻调大学习率;若梯度存在但权重不动,应检查优化器参数组、学习率和是否调用 step。
本例不需要循环训练,因为目标是定位一次更新链是否连通。实际调试可继续固定这两条样本直到拟合,再逐步恢复完整数据与正则。
计算与实现步骤
- 确认预测和目标均为 (2,1)。
- 检查损失有限且梯度非空。
- 比较 step 前后的权重。
代码实现
示例 1
同一训练步检查四个诊断信号的最小验证
example_01.py用途:使用完整构造输入核对同一训练步检查四个诊断信号,输出只表示本例计算。
import torch
from torch import nn
model = nn.Linear(1, 1, bias=False)
with torch.no_grad():
model.weight.zero_()
x, y = torch.tensor([[1.], [2.]]), torch.tensor([[2.], [4.]])
opt = torch.optim.SGD(model.parameters(), lr=0.1)
opt.zero_grad(set_to_none=True)
pred = model(x)
assert pred.shape == y.shape
loss = (pred - y).square().mean()
loss.backward()
grad = model.weight.grad.item()
before = model.weight.item()
opt.step()
print(f"finite={bool(torch.isfinite(loss))} loss={loss.item():.1f} grad={grad:.1f}")
print(f"weight_before={before:.1f} weight_after={model.weight.item():.1f}")代码解析
解析始终位于完整代码下方,并按实际代码段逐项对应。
输入数据与任务
使用完整构造输入核对同一训练步检查四个诊断信号,输出只表示本例计算。
y- 目标标签或连续目标值。
model- 组合预处理和估计器的模型对象。
mean- 训练特征逐列均值。
loss- 当前预测与目标之间的损失值。
Step 1 · 1–17 行
导入当前步骤需要的数值计算、预处理、模型或评价工具。依赖集中写在代码开头,便于复现。
import torch
from torch import nn
model = nn.Linear(1, 1, bias=False)
with torch.no_grad():
model.weight.zero_()
x, y = torch.tensor([[1.], [2.]]), torch.tensor([[2.], [4.]])
opt = torch.optim.SGD(model.parameters(), lr=0.1)
opt.zero_grad(set_to_none=True)
pred = model(x)
assert pred.shape == y.shape
loss = (pred - y).square().mean()
loss.backward()
grad = model.weight.grad.item()
before = model.weight.item()
opt.step()
print(f"finite={bool(torch.isfinite(loss))} loss={loss.item():.1f} grad={grad:.1f}")
print(f"weight_before={before:.1f} weight_after={model.weight.item():.1f}")代码与数学原理
减去训练均值并除以训练标准差,对应代码中的 mean/std 或 StandardScaler。
平方误差代码对应均方损失。
预期输出或运行结果
finite=True loss=10.0 grad=-10.0 weight_before=0.0 weight_after=1.0
常见错误 · 6 条
- 不要在测试集上调用 fit 或 fit_transform;测试集只能复用训练参数。
- 先换大模型
- 静默广播
- 随意 detach
- retain_graph 掩盖错误
- 确认样本轴、特征轴以及 X 与 y 的第一维完全对应。
实际应用
- 定位梯度断图与参数遗漏
- 建立固定小批次调试流程
常见错误
- 先换大模型
- 静默广播
- 随意 detach
- retain_graph 掩盖错误
常见问题
损失下降但准确率不变一定是 bug 吗?
不一定。概率可能改善却尚未跨越 argmax 或阈值边界;也可能类别不平衡掩盖进步,应检查概率、混淆矩阵与目标定义。
这个示例能说明什么边界?
小批次拟合成功是必要线索,不能证明完整评估协议和泛化能力正确。