升级 PyTorch 2.6 后,旧检查点报 Weights only load failed
torch.load 从 2.6 起在未指定 pickle_module 时默认受限加载。按检查点内容和可信程度选择 state_dict、精确允许列表或受控旧格式迁移。
同一个 .pt 检查点在旧环境中能加载,升级到 PyTorch 2.6 后却出现:
_pickle.UnpicklingError: Weights only load failed
WeightsUnpickler error: Unsupported global: GLOBAL ...
这不一定是文件损坏。PyTorch 从 2.6 起,在没有传入 pickle_module 时,torch.load 默认使用 weights_only=True。本文按 2.8 接口说明这个变更,不能把所有加载失败都归到版本默认值上。
看文件保存的是权重,还是 Python 对象
纯 Tensor 的 state_dict 与保存整个 nn.Module 是两种格式。前者主要保存参数和缓冲区;后者依赖 Python 对象序列化,可能需要执行类或函数的恢复逻辑。扩展检查点中的 NumPy 对象、自定义类、整个优化器对象,也可能触发 Unsupported global。
weights_only=True 使用受限反序列化器,限制可构建的对象,并禁止动态导入。它降低了一部分加载风险,但不是任意文件的安全沙箱,也不等于只会返回一个“权重数组”。不要因文件扩展名是 .pt 就认定内容可信。
先核对生成检查点的保存代码、来源与完整错误。文件读取路径、完整性和模型结构不匹配是其他分支;删掉旧文件或直接重训通常不是第一步。
新保存格式优先使用 state_dict
下面是针对已经创建好的 model 的权重保存与加载示例。weights.pt 是项目指定的本地目标路径,保存会写文件,使用前应避免覆盖唯一检查点:
import torch
torch.save(model.state_dict(), "weights.pt")
state = torch.load("weights.pt", map_location="cpu", weights_only=True)
model.load_state_dict(state)
加载前的 model 必须按相同结构创建。map_location="cpu" 控制张量存储映射,不能改变允许的 Python 对象类型。训练恢复需要的优化器状态,通常保存 optimizer.state_dict(),再交给对应优化器实例加载;不要仅为了续训,把整个优化器对象塞进新格式。
这段不会自动转换已经存在的旧文件。只拿到旧检查点、没有原保存环境时,应确认提供方能否给出标准权重格式,或在受控、可信环境中迁移。
精确允许对象,不照着报错批量导入
PyTorch 2.8 可以静态查看检查点引用了哪些尚未允许的全局对象:
names = torch.serialization.get_unsafe_globals_in_checkpoint("checkpoint.pt")
print(names)
checkpoint.pt 表示待检查的本地文件。这个列表不是安全审计结论,动态构造的类型也可能不在其中。例如 NumPy dtype 在不同版本中的类型表示不同,静态列表为空仍不保证后续加载成功。
确实知道所需对象实现、信任文件与类时,可在局部 torch.serialization.safe_globals([...]) 中精确允许它,再保留 weights_only=True 加载。列表里放的是经过核对、已经导入的真实类或函数,不是报错字符串;不要把列表中的名字自动 eval、动态导入或一次性全部允许。上下文退出后不会把这次允许范围永久留在后续加载中。
允许某个类或函数,就允许受限恢复过程使用它;“加进 safe_globals”不能把未知实现变成安全实现。若应用内部调用加载,优先采用该应用明确支持的格式和兼容版本,不直接修改安装目录中的库文件来压掉异常。
旧完整模型需要明确的迁移前提
weights_only=False 恢复旧的 pickle 行为,可能执行任意代码。只有来源、内容和恢复代码可信,并在受控环境中处理时,才考虑用它迁移旧完整模型;不要对下载来的未知模型默认关闭保护,也不要全局设置环境变量来让所有加载都失去限制。
迁移时保留原文件,输出新的 state_dict 到不同路径,核对参数键、形状和受控输入结果。仅仅不再出现 Unsupported global,不代表模型完整恢复。若结构对不上,继续检查模型定义与版本,不用 strict=False 一律掩盖缺失键。


