模型保存、加载、推理与部署
模型资产包括结构配置、权重、预处理、类别映射、版本和评估范围。
学习目标
- 在内存保存并恢复固定线性模型
- 为什么 eval 和 inference_mode 都需要?
- 说明:权重数值一致不能单独证明外部预处理、标签顺序和部署输入都一致。
为什么需要这个概念
模型推理资产包含结构配置、state_dict、预处理规则和标签映射;保存与加载后的固定样本一致性检查用来验证恢复契约。
学习前需要掌握
背景与问题
一个可用模型不仅是权重文件,还包含结构、输入加工和类别解释。加载后能得到某个张量,不代表它与训练时具有相同含义。
概念定义
模型推理资产包含结构配置、state_dict、预处理规则和标签映射;保存与加载后的固定样本一致性检查用来验证恢复契约。
直观理解
权重像一组零件参数,配置是装配图,预处理和类别表则是使用说明,缺少任一项都可能装出不同系统。
核心原理
通常保存 state_dict 而非依赖完整 Python 对象序列化,恢复时先创建同一结构再 load_state_dict。state_dict 包含参数和持久 buffer,但不包含 forward 代码、词表或业务阈值。
推理前同时调用 eval 与 inference_mode:前者改变 Dropout、BatchNorm 等行为,后者关闭自动微分开销。map_location 可把原 GPU 权重加载到 CPU,输入与模型仍需在兼容设备和 dtype 上。
weights_only=True 适合限制加载内容,仍应只加载可信来源。结构键匹配不代表输入通道顺序和标签表正确;交付前应比较固定输入的输出并检查输出类别解释。
数学表达
核心公式
模型推理资产包含结构配置、state_dict、预处理规则和标签映射;保存与加载后的固定样本一致性检查用来验证恢复契约。
变量含义
Nbatch sizeD/H特征或隐藏维度
计算步骤
- 1推理得到 2×3−4+0.5=2.5。
- 2保存资产到内存,再重建并加载。
- 3比较恢复前后输出。
代码对应位置
示例 1在内存保存并恢复固定线性模型的最小验证:使用完整构造输入核对在内存保存并恢复固定线性模型,输出只表示本例计算。
变量解释
| 变量 | 含义 |
|---|---|
N | batch size |
D/H | 特征或隐藏维度 |
完整数值示例
在内存保存并恢复固定线性模型
已知条件
- 线性层权重为 (2,-1),偏置为 0.5
- 输入为 (3,4)
- 资产附带输入维数和标签名字
计算步骤
- 推理得到 2×3−4+0.5=2.5。
- 保存资产到内存,再重建并加载。
- 比较恢复前后输出。
计算结果
恢复前后均输出 2.5,最大差为 0。
处理前后对比
- 权重数值一致不能单独证明外部预处理、标签顺序和部署输入都一致。
在内存保存并恢复固定线性模型
示例使用内存缓冲区,避免留下演示文件。真实文件保存可使用同样的资产内容,但还应记录版本和预处理,例如输入是否已标准化。
如果恢复时把两个输入特征交换,即使权重加载零报错,预测仍会改变。固定样本测试最好覆盖一条完整的原始输入到类别输出路径。
计算与实现步骤
- 推理得到 2×3−4+0.5=2.5。
- 保存资产到内存,再重建并加载。
- 比较恢复前后输出。
代码实现
示例 1
在内存保存并恢复固定线性模型的最小验证
example_01.py用途:使用完整构造输入核对在内存保存并恢复固定线性模型,输出只表示本例计算。
import io
import torch
from torch import nn
model = nn.Linear(2, 1)
with torch.no_grad():
model.weight.copy_(torch.tensor([[2., -1.]]))
model.bias.fill_(0.5)
model.eval()
x = torch.tensor([[3., 4.]])
buffer = io.BytesIO()
torch.save({"in_features": 2, "state": model.state_dict(), "labels": ["score"]}, buffer)
buffer.seek(0)
artifact = torch.load(buffer, map_location="cpu", weights_only=True)
restored = nn.Linear(artifact["in_features"], 1)
restored.load_state_dict(artifact["state"])
restored.eval()
with torch.inference_mode():
a, b = model(x), restored(x)
print(f"prediction={b.item():.1f} max_diff={(a - b).abs().max().item():.1f}")
print(artifact["labels"])代码解析
解析始终位于完整代码下方,并按实际代码段逐项对应。
输入数据与任务
使用完整构造输入核对在内存保存并恢复固定线性模型,输出只表示本例计算。
model- 组合预处理和估计器的模型对象。
b- 偏置项。
Step 1 · 1–20 行
导入当前步骤需要的数值计算、预处理、模型或评价工具。依赖集中写在代码开头,便于复现。
import io
import torch
from torch import nn
model = nn.Linear(2, 1)
with torch.no_grad():
model.weight.copy_(torch.tensor([[2., -1.]]))
model.bias.fill_(0.5)
model.eval()
x = torch.tensor([[3., 4.]])
buffer = io.BytesIO()
torch.save({"in_features": 2, "state": model.state_dict(), "labels": ["score"]}, buffer)
buffer.seek(0)
artifact = torch.load(buffer, map_location="cpu", weights_only=True)
restored = nn.Linear(artifact["in_features"], 1)
restored.load_state_dict(artifact["state"])
restored.eval()
with torch.inference_mode():
a, b = model(x), restored(x)
print(f"prediction={b.item():.1f} max_diff={(a - b).abs().max().item():.1f}")
print(artifact["labels"])预期输出或运行结果
prediction=2.5 max_diff=0.0 ['score']
常见错误 · 5 条
- 只保存权重
- 忘 eval
- 预处理不一致
- 类别映射丢失
- 加载不可信 pickle
实际应用
- CPU 推理交付
- 检查保存加载的一致性
常见错误
- 只保存权重
- 忘 eval
- 预处理不一致
- 类别映射丢失
- 加载不可信 pickle
常见问题
为什么 eval 和 inference_mode 都需要?
eval 控制模块训练行为,inference_mode 控制自动微分;只用其中一个不能同时完成两项职责。
这个示例能说明什么边界?
权重数值一致不能单独证明外部预处理、标签顺序和部署输入都一致。
推荐学习资料
Practical Deep Learning for Coders
以项目驱动方式学习视觉、NLP、表格模型、协同过滤和模型部署。
Jeremy Howard · fast.ai