Appearance
排错与 FAQ:新手最常撞的 8 堵墙
遇到报错先别慌着搜答案——本篇每条都给出"报错原文 → 原因 → 解决 → 调试思路"。建议当字典用:撞墙了 Ctrl+F 报错关键词。
Q1:mat1 and mat2 shapes cannot be multiplied (Ax B and CxD)
原因:矩阵乘的中间维度对不上,即进入 nn.Linear 的最后一维 ≠ in_features。
解决:沿报错向上追一层,print(x.shape) 找到形状开始"不对味"的位置。常见修法:缺 nn.Flatten() / view 参数算错 / batch 维丢了。
思路:拿草稿纸把每层"输入形状 → 权重形状 → 输出形状"列成表。这道题没有捷径,形状表就是深度世界的堆栈跟踪。
Q2:Expected all tensors to be on the same device
原因:张量在 CPU、模型在 GPU(或反之)。
解决:统一三件套——model.to(device);每个 batch images.to(device);中途手工创建的张量(如 mask)也要 .to(device)。
思路:报错信息会给出两边的 device(cuda:0 vs cpu)。顺着"新出现的那个张量是谁创建的"找漏网之鱼。
Q3:loss 不降 / 训练不收敛(不报错的那种)
最阴险的问题,按概率从高到低排查:
- 忘
zero_grad()—— 梯度累加,参数原地打摆(实战篇踩过)。 - 学习率不当 —— 太大:loss 震荡或 NaN;太小:几乎不动。先做 3 档扫描。
- 标签或数据管道错位 ——
shuffle=True时 Dataset 的__getitem__返回了不配对的 (x, y)。 - 忘了
model.train()/ 参数没进优化器 —— 打印len(opt.param_groups[0]['params'])核对。
通用杀招:过拟合单个 batch——拿一个 batch 反复训 100 步,loss 不趋近 0 就说明管线有 bug,与数据集无关。
Q4:CUDA out of memory
原因:显存超限。注意:报错时的占用包含其他进程(同事的训练任务、Jupyter 里没释放的旧 notebook)。
解决(按序尝试):
- 减小
batch_size(减半试) - 评估代码套
torch.no_grad()(忘记它等于白白存下整张计算图) - 开启 AMP 混合精度
del 大张量; torch.cuda.empty_cache()手动释放(应急)- 梯度累积模拟大 batch
思路:nvidia-smi(Linux 终端 / Windows PowerShell)边训边看,区分"启动就炸"(模型太大)还是"训着训着炸"(内存泄漏,常因把 loss 张量直接 append 进列表——应存 loss.item())。
Q5:Can't call numpy() on Tensor that requires grad... / Converting a tensor with requires_grad=True
原因:带梯度的张量不能直接转 NumPy;GPU 张量也不能直接转。
解决:x.detach().cpu().numpy() —— 剪断梯度 → 搬回 CPU → 转数组。
思路:报错第一行通常直接给了解药关键词(detach / cpu)。记住这条流水线,可视化、指标计算全靠它。
Q6:Windows 上运行直接卡死 / 疯狂递归打印 / Attempt to start a new process before booting...
原因:DataLoader(num_workers>0) 在 Windows 用 spawn 启动子进程,子进程重新导入主模块,而主逻辑没被保护块拦住。
解决:所有入口代码放进 if __name__ == "__main__":(实战篇的 train.py 从第一天就是这么写的);或先用 num_workers=0 验证逻辑,再加并发。
Q7:Error(s) in loading state_dict for XXX: Missing key(s) ... Unexpected key(s) ...
常见三种情形:
| 报错特征 | 原因 | 解决 |
|---|---|---|
Unexpected key "module.xxx" | 训练用了 DataParallel/DDP,保存的 state_dict 带 module. 前缀 | 加载时去掉前缀,或加载进 model.module |
| Missing/Unexpected 都是若干层 | 模型结构与权重不匹配(改了网络定义) | 用同一份模型定义加载;strict=False 只能应急,必须检查差在哪 |
| 尺寸不匹配 | in_features/out_features 对不上 | 对照保存时的结构改回来 |
思路:先 print(model.state_dict().keys()) 对比权重文件 torch.load(..., weights_only=True).keys(),差异一目了然。
Q8:明明切了评估模式,为什么每次推理结果还不一样?
可能原因:
- 评估前没
model.eval()(Dropout 还在工作)。 torch.rand没设种子,数据增强每次不同。- 模型里含 BatchNorm 且在评估时又误调了
model.train()。
解决:评估/推理固定套路 model.eval() + with torch.no_grad():;复现实验最前面统一设种(torch.manual_seed,需要更强复现见 torch.use_deterministic_algorithms(True))。
通用调试心法
- 缩小规模:模型削到 2 层、数据取 1 个 batch、epoch 设 1——问题还在才好定位。
- 打印形状和统计量:
x.shape、x.dtype、x.device、x.min()/max(),四个属性覆盖一半 bug。 - 梯度体检:
for n, p in model.named_parameters(): print(n, p.grad is None, p.grad.norm())——None 说明图断了,inf/nan 说明爆炸了。 - 读报错从最后往上读到自己写的那一行:PyTorch 的 traceback 很长,但最后一帧指向的才是你的代码。