模型服务化基础:序列化与反序列化
模型服务化的第一步是将训练完成的模型转换为可部署、可推理的格式。序列化(Serialization)将模型的内存结构保存为文件,反序列化(Deserialization)则从文件恢复模型。本章聚焦三种主流格式:ONNX、TorchScript 与 TensorFlow SavedModel,并对比其适用场景与性能特征。
核心概念:序列化与反序列化
序列化:将模型的计算图、权重、元数据写入磁盘文件。
反序列化:从磁盘文件加载模型到内存,恢复推理能力。
关键指标:文件大小、加载速度、跨平台兼容性、推理延迟。
反序列化:从磁盘文件加载模型到内存,恢复推理能力。
关键指标:文件大小、加载速度、跨平台兼容性、推理延迟。
三大主流格式概览
| 格式 | 来源框架 | 核心特点 | 典型后缀 | 跨框架支持 |
|---|---|---|---|---|
| ONNX | PyTorch / TF / 其他 | 开放标准,算子级互操作 | .onnx | 高(多框架互通) |
| TorchScript | PyTorch | Python 无关的静态图 | .pt / .pth | 中(PyTorch 生态) |
| SavedModel | TensorFlow | 完整模型 + 签名 + 资源 | 目录(pb + variables) | 中(TF 生态) |
ONNX:开放神经网络交换格式
ONNX(Open Neural Network Exchange)由微软与 Facebook 联合推出,旨在打破框架壁垒。它将模型表示为计算图,每个节点为算子(Operator),边为张量。ONNX 的序列化过程包括:
- 追踪或导出:从原始框架导出计算图与权重。
- 图优化:常量折叠、算子融合、拓扑排序。
- Protobuf 编码:将图结构序列化为二进制 protobuf 文件。
- 权重外部存储(可选):大模型将权重分拆为外部数据文件。
低延迟技巧:ONNX Runtime 支持执行提供者(CUDA、TensorRT、OpenVINO),可在序列化时指定目标硬件优化,减少推理时的图编译开销。
ONNX 序列化示例(PyTorch → ONNX)
import torch
import torch.onnx
model = torch.load('model.pth')
model.eval()
dummy_input = torch.randn(1, 3, 224, 224)
torch.onnx.export(
model,
dummy_input,
'model.onnx',
input_names=['input'],
output_names=['output'],
dynamic_axes={'input': {0: 'batch_size'},
'output': {0: 'batch_size'}},
opset_version=17
)
TorchScript:PyTorch 的静态图方案
TorchScript 将动态 PyTorch 模型转换为静态图,消除 Python 解释器开销。两种生成方式:
- Tracing(追踪):通过一次前向传播记录算子序列,适合无控制流的模型。
- Scripting(脚本化):编译 TorchScript 源码,支持 if/for 等控制流。
避坑:Tracing 无法捕获动态控制流(如循环次数依赖输入),此时必须使用 Scripting 或混合模式。
TorchScript 序列化示例
import torch
model = torch.load('model.pth')
model.eval()
# Tracing 方式
traced_model = torch.jit.trace(model, torch.randn(1, 3, 224, 224))
traced_model.save('model_traced.pt')
# Scripting 方式
scripted_model = torch.jit.script(model)
scripted_model.save('model_scripted.pt')
TensorFlow SavedModel:完整服务化格式
SavedModel 是 TensorFlow 官方推荐的序列化格式,包含:
- saved_model.pb:计算图定义(MetaGraphDef)。
- variables/:权重 checkpoint 文件。
- assets/:附加资源文件(如词汇表)。
SavedModel 支持签名(Signature)机制,可定义多个输入/输出接口,适合多任务模型服务。
SavedModel 序列化示例
import tensorflow as tf
model = tf.keras.models.load_model('model.h5')
# 定义签名
@tf.function(input_signature=[tf.TensorSpec(shape=[None, 224, 224, 3],
dtype=tf.float32,
name='input')])
def serving_fn(input):
return {'output': model(input)}
# 导出 SavedModel
tf.saved_model.save(
model,
'saved_model/1',
signatures={'serving_default': serving_fn}
)
格式对比:文件大小与加载速度
| 指标 | ONNX | TorchScript | SavedModel |
|---|---|---|---|
| 文件大小(ResNet-50) | ~98 MB | ~102 MB | ~105 MB(目录) |
| 加载时间(SSD) | ~0.8 s | ~0.6 s | ~1.2 s |
| 推理延迟(GPU, batch=1) | ~4.2 ms | ~3.8 ms | ~4.5 ms |
| 跨框架兼容性 | 高 | 低 | 中 |
| 图优化能力 | 强(ONNX Runtime) | 中(TorchScript JIT) | 强(XLA) |
序列化流程对比图
反序列化与推理性能关键点
反序列化速度直接影响服务冷启动时间。以下为优化建议:
- ONNX:使用 ONNX Runtime 的
SessionOptions设置图优化级别,启用内存池复用。 - TorchScript:加载后调用
model.eval()并禁用梯度,使用torch.jit.freeze冻结图。 - SavedModel:使用
tf.saved_model.load时指定tags过滤签名,减少加载内容。
低延迟最佳实践:在生产环境中,将序列化后的模型文件预加载到共享内存(如 /dev/shm),或使用模型缓存层(如 RedisAI、NVIDIA Triton 的模型仓库),避免每次请求都反序列化。