跳到正文
本地阅读 · 保存、加载、推理与复现

保存、加载、推理与复现

计算机基础 · PyTorch

前置知识:MLP 与完整训练验证 · 文件、异常及常用标准库

本章目录

本章学习目标

  • 保存权重及预处理合同
  • 核对加载前后预测一致性
  • 定位形状、设备、版本和训练状态错误

模型交付需要权重之外的信息

state_dict 保存模型参数和登记缓冲的名称到张量映射,并不自动保存完整业务预测规则。相同的权重配上不同的特征顺序或标准化参数,会产生完全不同的预测。因此交付应同时记录特征列顺序、缺失处理参数、缩放参数、网络结构、目标定义和验证环境。

本章用三列输入,明确顺序为 x1、x2、x3。保存训练时采用的均值与标准差张量;推理时严格用这些值处理新输入,而不是重新对新批次计算均值。新批次即使只有一条记录,也应得到定义明确的转换。标准差为零的列用约定规则替换分母,避免除零。

仅保存“这是一个三特征模型”不够。特征同样为三列,但把第二列换成另一个变量,形状检查仍会通过。推理入口应先核对名称和顺序,再检查类型、有限值和数量。样本键与预测一起导出,才能追踪结果对应哪条记录。

权重加载与训练恢复是两种交付

只用于推理的文件可以保存 model_state 和预处理参数,重新构建同样的网络后加载权重。继续训练则还需保存优化器状态、已完成轮数、随机状态及必要的采样器状态。Adam 的动量等状态会影响后续更新;只加载模型权重不能保证接续原训练轨迹。

课程示例使用 state_dict 类型的检查点,并在 torch.load 时明确 map_location='cpu' 和 weights_only=True,匹配本章保存的基础类型与张量。map_location 决定张量加载的位置,它不会自动替你修正输入设备或网络结构。加载失败时先看键名和尺寸差异,不能随意用 strict=False 忽略全部问题。

模型完整对象的序列化依赖 Python 定义与序列化机制,跨脚本和版本可能更脆弱。教学优先展示清晰的网络定义加参数映射。检查点保存在课程 outputs 内,脚本自己生成并读回,不要求从外部下载任何模型或数据。

加载后设置推理模式并核对结果

加载权重后仍需调用 eval,并在 inference_mode 中产生输出。输出要脱离梯度、移到 CPU、转成分析格式,再与样本键合并。推理模式只约束执行行为,并不能自动识别异常输入;数据合同检查仍是独立步骤。

本章在保存前对固定输入做一次预测,加载后用相同预处理与结构再预测,并用 torch.testing.assert_close 核对。这是模型交付的基本测试:如果同环境同输入不能恢复原预测,说明保存、加载、预处理或模式有遗漏。使用浮点容差比跨设备强求位级相等更稳妥。

结果一致只是交付正确性的必要条件,不证明模型有业务价值。还需要合法评价集合、适当指标、覆盖率和基线比较。预测输出如果缺少样本键或丢失顺序,模型数值即使正确,也可能在下游统计中错误连接。

把错误定位到具体环节

mat1 与 mat2 shapes cannot be multiplied 通常指输入末尾维度与线性层输入维度不符。设备不一致要检查模型、输入、目标及预处理张量的位置。期望浮点而得到整型要核对读入类型;NaN 可能来自未处理缺失、除零、溢出或梯度发散。先打印小批次的形状和范围,再修改代码。

验证损失忽高忽低时,要检查是否忘了 eval、验证加载器是否随机变换、预处理是否每批重新拟合。结果无法复现则核对输入文件、样本划分、种子、库版本和线程配置。记录运行清单能把问题缩小到具体变动,不能只写一句“设置过随机种子”。

推理代码应该对不符合合同的输入给出明确错误,而不是悄悄调整列数。若新特征缺失,先处理数据来源问题,不能补一列零让维度凑齐。将异常输入、保存加载一致和零方差处理加入测试,能比单纯扩大训练轮数更有效地提高流程可靠性。

完整示例与实际输出

from pathlib import Path
import torch
torch.manual_seed(42)
ROOT=Path(__file__).resolve().parents[1]
out=ROOT/'outputs'; out.mkdir(exist_ok=True)
features=['x1','x2','x3']
X=torch.tensor([[1.,2.,3.],[2.,4.,6.],[3.,6.,9.]])
mean=X.mean(dim=0); scale=X.std(dim=0,unbiased=False).clamp_min(1e-6)
model=torch.nn.Sequential(torch.nn.Linear(3,4),torch.nn.ReLU(),torch.nn.Linear(4,1))
model.eval()
with torch.inference_mode(): before=model((X-mean)/scale)
checkpoint={'model_state':model.state_dict(),'mean':mean,'scale':scale,'features':features,'torch_version':str(torch.__version__)}
path=out/'torch-save.pt'; torch.save(checkpoint,path)
loaded=torch.load(path,map_location='cpu',weights_only=True)
assert loaded['features']==features
restored=torch.nn.Sequential(torch.nn.Linear(3,4),torch.nn.ReLU(),torch.nn.Linear(4,1))
restored.load_state_dict(loaded['model_state']); restored.eval()
with torch.inference_mode(): after=restored((X-loaded['mean'])/loaded['scale'])
torch.testing.assert_close(before,after)
assert torch.isfinite(after).all()
print('checkpoint=',path.name,'prediction_shape=',tuple(after.shape),'roundtrip=PASS')
print('predictions=',after.flatten().tolist())

实际运行通过CPU Python execution.

checkpoint= torch-save.pt prediction_shape= (3, 1) roundtrip=PASS
predictions= [-0.23036831617355347, -0.12516054511070251, 0.1492994725704193]

五道自测:先回答,再核对

01 · 只保存权重能恢复全部业务预测规则吗?

查看提示

模型输入是否仍具有原含义?

查看参考答案

不能,还要保存结构、特征顺序、预处理参数与目标规则。

02 · 推理时可以按新批次重新计算标准化均值吗?

查看提示

单样本批次会发生什么?

查看参考答案

不能,使用训练拟合的参数;重新计算会改变模型输入含义。

03 · map_location="cpu" 是否等于 model.eval?

查看提示

设备和层行为不同。

查看参考答案

不是,前者控制加载设备,后者控制推理模式。

04 · 恢复 Adam 训练为什么还要优化器状态?

查看提示

优化器除了学习率还记什么?

查看参考答案

动量与步数等状态影响后续更新,只有权重无法接续原轨迹。

05 · 预测形状正确但列顺序改变,会报错吗?

查看提示

形状检查不验证业务语义。

查看参考答案

不一定,因此必须显式核对特征名称和顺序。

两项动手任务

任务 1 · 给三列模型写一个拒绝错误列顺序的输入检查。

提示

先写出输入、输出、形状和判断条件,再运行小数据。

完整参考解答与检查方式
def check_features(actual,expected):
    if list(actual)!=list(expected):
        raise ValueError(f'特征顺序不一致: {actual},期望{expected}')
check_features(['x1','x2','x3'],['x1','x2','x3'])
try:
    check_features(['x2','x1','x3'],['x1','x2','x3'])
except ValueError as e: print(e)
else: raise AssertionError('错误列顺序未被拒绝')

任务 2 · 验证一列为常数时标准化仍产生有限数值。

提示

先写出输入、输出、形状和判断条件,再运行小数据。

完整参考解答与检查方式
import torch
x=torch.tensor([[1.,7.],[2.,7.],[3.,7.]])
mean=x.mean(0)
scale=x.std(0,unbiased=False)
scale=torch.where(scale>0,scale,torch.ones_like(scale))
z=(x-mean)/scale
assert torch.isfinite(z).all()
assert torch.equal(z[:,1],torch.zeros(3))
print(z)

官方资料与掌握状态

完成两项任务后,按独立完成程度记录。状态会尽量保存在当前浏览器,可导出备份。

动手任务记录

掌握状态与复习

关掉资料独立完成后记 A;提示后完成记 B;需要重学记 C。

记录尽量保存在当前浏览器,建议定期导出备份。