应显式指定 map_location='cpu' 并启用 weights_only=True(PyTorch ≥2.0),区分保存的是完整模型还是 state_dict,加载后校验类型、key 结构及数值合法性。
torch.load 读取 .pth 文件时抛出 PickleError 或 RuntimeError 怎么办
直接用 torch.load 加载非安全来源的 .pth 文件,极大概率触发 RuntimeError: Attempting to deserialize object on a CUDA device 或更危险的 pickle.UnpicklingError —— 因为 .pth 本质是 Python 的 pickle 序列化文件,可能含任意代码执行逻辑。
安全做法是强制指定 map_location 并禁用自动反序列化代码对象:
- 始终显式传入
map_location='cpu',避免设备不匹配错误 - 若模型保存时用了
torch.save(model.state_dict(), ...),则加载后得到的是dict,不是完整模型对象;别试图直接model.load_state_dict(...)前不做初始化 - 对不可信文件,加
weights_only=True(PyTorch ≥2.0),它会跳过所有自定义类/函数反序列化,只允许基础类型和torch.Tensor
示例:
import torch
state_dict = torch.load('model.pth', map_location='cpu', weights_only=True)
加载后得到的字典 key 长得不像层名?检查是否保存了整个模型还是仅 state_dict
两种常见保存方式导致字典结构完全不同:
-
torch.save(model, 'full_model.pth')→ 加载结果是完整nn.Module实例,带forward、__dict__、甚至自定义属性 -
torch.save(model.state_dict(), 'weights.pth')→ 加载结果是纯dict,key 形如'conv1.weight'、'bn2.running_mean',无模块嵌套逻辑
用 type(...) 和 list(...keys())[:3] 快速判断:
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
ckpt = torch.load('model.pth', map_location='cpu')
print(type(ckpt)) # 还是 ?
print(list(ckpt.keys())[:3]) # 若报 AttributeError,则不是 dict
想查看某层权重形状但 key 名不确定?用字典遍历 + 字符串匹配
state_dict 的 key 是字符串,没有层级结构,但习惯上用点号分隔模块名。别硬记全路径,用 in 或 startswith 快速过滤:
-
[k for k in state_dict if 'layer4' in k]找所有 layer4 相关参数 -
[k for k in state_dict if k.endswith('.bias')]找全部偏置项 -
state_dict['backbone.conv1.weight'].shape直接取张量看torch.Size([64, 3, 7, 7])
注意:key 区分大小写,且不含 .module. 前缀——除非模型曾用 nn.DataParallel 保存,此时所有 key 会多一层前缀,需用 {k.replace('module.', ''): v for k, v in state_dict.items()} 清洗
加载后权重数值全是 nan 或 inf?检查保存时是否启用了梯度计算
如果训练中误将 requires_grad=True 的中间变量(比如 loss、optimizer.state)存进 state_dict,或保存了未 detach 的计算图节点,torch.load 可能还原出非法浮点值。
- 加载后立刻检查:
torch.isnan(next(iter(state_dict.values()))).any() - 正常权重应全为 finite 值:
torch.isfinite(weight).all().item() == True - 根本解法:保存前确保只存
.state_dict(),而非model.__dict__或手动构造的 dict
这种问题在调试自定义训练循环时特别容易被忽略——因为模型跑着不报错,但保存下来的文件已损坏。