模型服务化基础:序列化与反序列化

模型服务化的第一步是将训练完成的模型转换为可部署、可推理的格式。序列化(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 的序列化过程包括:

  1. 追踪或导出:从原始框架导出计算图与权重。
  2. 图优化:常量折叠、算子融合、拓扑排序。
  3. Protobuf 编码:将图结构序列化为二进制 protobuf 文件。
  4. 权重外部存储(可选):大模型将权重分拆为外部数据文件。
低延迟技巧: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 导出 Protobuf 编码 权重外部存储(可选) model.onnx TorchScript 导出 Tracing / Scripting JIT 编译优化 model.pt SavedModel 导出 签名定义 + 图保存 权重 + 资源打包 saved_model/1/

反序列化与推理性能关键点

反序列化速度直接影响服务冷启动时间。以下为优化建议:

  • ONNX:使用 ONNX Runtime 的 SessionOptions 设置图优化级别,启用内存池复用。
  • TorchScript:加载后调用 model.eval() 并禁用梯度,使用 torch.jit.freeze 冻结图。
  • SavedModel:使用 tf.saved_model.load 时指定 tags 过滤签名,减少加载内容。
低延迟最佳实践:在生产环境中,将序列化后的模型文件预加载到共享内存(如 /dev/shm),或使用模型缓存层(如 RedisAI、NVIDIA Triton 的模型仓库),避免每次请求都反序列化。

格式选择决策树

格式选择决策树 需要跨框架部署? 是 → ONNX 否 → 看框架 PyTorch → TorchScript TF → SavedModel

总结