3、模型序列化与版本控制:模型保存格式与版本管理

模型训练完了,怎么把它存起来?怎么让不同框架都能用?怎么追踪模型的变化?

这三个问题,说白了就是模型序列化与版本控制要解决的事。我在项目里见过太多次,模型文件乱放、格式不统一、回滚时找不到旧版本……嗯,今天咱们就把这块彻底理清楚。

3.1 模型保存格式:选对格式,少走弯路

模型训练好之后,第一步就是序列化——把内存里的模型参数和结构,变成磁盘上的文件。不同的框架有不同的原生格式,但为了跨平台、跨语言部署,我们通常需要一种「通用语言」。

3.1.1 ONNX:跨框架的「世界语」

ONNX(Open Neural Network Exchange)是我个人最推荐的中间格式。它由微软和Facebook联合推出,目标就是让模型能在PyTorch、TensorFlow、Caffe2等框架之间自由迁移。

为什么选ONNX?

  • 跨框架兼容:PyTorch训练的模型,导出ONNX后,可以直接在TensorRT、ONNX Runtime上推理。
  • 推理优化:ONNX Runtime做了大量算子融合和内存优化,推理速度通常比原生框架快10%-30%。
  • 硬件适配好:NVIDIA、Intel、AMD都提供了ONNX的加速后端。

核心要点:ONNX适合作为「部署中间格式」,而不是训练格式。训练时用原生框架,部署时转ONNX。

导出示例(PyTorch → ONNX)

import torch
import torch.onnx

# 假设你有一个训练好的模型
model = MyModel()
model.load_state_dict(torch.load('model.pth'))
model.eval()

# 构造一个 dummy 输入
dummy_input = torch.randn(1, 3, 224, 224)

# 导出 ONNX
torch.onnx.export(
    model,
    dummy_input,
    "model.onnx",
    input_names=['input'],
    output_names=['output'],
    dynamic_axes={'input': {0: 'batch_size'},
                  'output': {0: 'batch_size'}}
)
print("ONNX 模型已保存")

避坑指南:我曾经在导出ONNX时,因为模型里用了自定义的F.interpolate操作,导致导出失败。后来发现需要添加算子支持,或者用torch.onnx.export的custom_opsets参数。建议导出前先用torch.onnx.export的check验证一下。

3.1.2 TorchScript:PyTorch的「亲儿子」

TorchScript是PyTorch官方推出的序列化格式。它把Python代码编译成静态图,可以脱离Python环境运行。

什么时候用TorchScript?

  • 你的部署环境是C++,不想依赖Python。
  • 模型里有复杂的控制流(if/else、循环),ONNX可能不支持。
  • 需要极致性能,TorchScript的JIT编译器能做更多优化。

导出示例

import torch

model = MyModel()
model.eval()

# 方法1:Tracing(跟踪)
example = torch.randn(1, 3, 224, 224)
traced_script_module = torch.jit.trace(model, example)
traced_script_module.save("model_traced.pt")

# 方法2:Scripting(脚本化)
scripted_model = torch.jit.script(model)
scripted_model.save("model_scripted.pt")

注意:Tracing方式只记录实际执行路径,如果模型有动态分支,可能会丢失部分逻辑。Scripting方式更安全,但要求模型代码符合TorchScript语法(不能有Python原生list、dict等)。

3.1.3 SavedModel:TensorFlow的「全家桶」

SavedModel是TensorFlow的官方序列化格式。它不仅仅保存模型参数,还包含了计算图、签名(Signature)、资源文件等。

SavedModel的特点

  • 自包含:一个文件夹里包含了模型的所有信息,部署时直接加载。
  • 版本管理友好:每个版本一个子目录,TensorFlow Serving原生支持。
  • 多签名支持:可以定义多个输入输出接口(比如训练、推理、特征提取)。

导出示例

import tensorflow as tf

model = tf.keras.models.load_model('model.h5')

# 保存为 SavedModel 格式
tf.saved_model.save(model, "saved_model/1/")
# 注意:这里的 "1" 是版本号,TensorFlow Serving 会按版本号加载

个人经验:我在做TF Serving部署时,习惯把SavedModel的版本号放在子目录里,比如saved_model/1/、saved_model/2/。这样服务启动时自动加载最新版本,回滚时只需要改个软链接。

3.2 模型版本管理:别让你的模型「失忆」

模型文件通常很大(几百MB甚至几个GB),用Git直接管理会非常痛苦。你想想看,每次commit都要上传几百MB,团队协作时clone一次就要等半天。

所以,我们需要专门的工具来管理模型版本。

3.2.1 DVC:数据版本控制的「瑞士军刀」

DVC(Data Version Control)是我个人最常用的工具。它把模型文件存储在远程存储(S3、GCS、NAS等),只在Git里保存一个轻量的指针文件。

DVC的核心工作流

  1. 初始化:在项目根目录运行dvc init
  2. 添加模型dvc add models/model.onnx,会生成一个.dvc文件。
  3. 推送远程dvc push,把模型文件上传到远程存储。
  4. 版本关联:把.dvc文件提交到Git,这样Git就记录了模型版本。

实际项目中的DVC配置

# 初始化 DVC
dvc init

# 配置远程存储(以 S3 为例)
dvc remote add -d myremote s3://my-bucket/models

# 添加模型文件
dvc add models/model.onnx

# 提交到 Git
git add models/model.onnx.dvc .gitignore
git commit -m "Add model v1.0"

# 推送模型到远程
dvc push

核心要点:DVC的核心理念是「Git存元数据,DVC存大文件」。你可以在Git里看到每次commit对应的模型版本,需要时用dvc checkout恢复。

避坑指南:我曾经在团队里推广DVC时,有人忘记执行dvc push,导致其他人dvc pull时找不到文件。后来我加了个CI/CD检查:每次合并到主分支前,必须确保所有.dvc文件对应的远程文件存在。

3.2.2 Git LFS:简单粗暴的大文件管理

Git LFS(Large File Storage)是Git官方的大文件扩展。它把大文件替换成指针,实际内容存储在LFS服务器上。

什么时候用Git LFS?

  • 团队已经熟悉Git,不想引入新工具。
  • 模型文件不是特别大(<5GB),且更新频率不高。
  • 你用的是GitHub、GitLab等支持LFS的平台。

配置示例

# 安装 Git LFS
git lfs install

# 跟踪模型文件类型
git lfs track "*.onnx"
git lfs track "*.pt"
git lfs track "*.pb"

# 提交 .gitattributes
git add .gitattributes
git commit -m "Configure Git LFS for model files"

# 正常使用 Git 命令
git add models/model.onnx
git commit -m "Add model v1.0"
git push

注意:Git LFS有存储和带宽限制。GitHub免费版只有1GB存储和1GB/月带宽。如果模型文件很大或更新频繁,建议用DVC+对象存储。

3.3 格式与版本管理的组合策略

在实际项目中,我通常这样组合使用:

场景 保存格式 版本管理工具 理由
PyTorch模型部署到云端 ONNX DVC 跨平台兼容,DVC适合大文件
PyTorch模型部署到边缘设备 TorchScript DVC 边缘设备通常用C++推理
TensorFlow模型部署到TF Serving SavedModel Git LFS TF Serving原生支持SavedModel版本目录
小团队快速迭代 原生格式(.pt/.h5) Git LFS 简单直接,学习成本低

我的建议:无论选哪种组合,一定要在项目初期就定好规范。我见过太多项目,模型文件散落在各个目录,命名混乱,版本号全靠文件名里的「v1_final_final2」来区分……嗯,那场面,你懂的。

3.4 实战:一个完整的模型保存与版本管理流程

最后,我分享一个我在实际项目中用过的流程,你可以直接参考:

# 1. 训练完成后,导出 ONNX
python export_onnx.py --checkpoint model.pth --output models/v1/model.onnx

# 2. 用 DVC 管理模型版本
cd models
dvc add v1/model.onnx
git add v1/model.onnx.dvc
git commit -m "Model v1.0: 初始版本,准确率92.3%"
dvc push

# 3. 部署时,从 DVC 拉取指定版本
dvc checkout v1/model.onnx.dvc

# 4. 如果需要回滚到旧版本
git checkout <old_commit>
dvc checkout

小技巧:我习惯在模型文件名里加上日期和commit hash,比如model_20231015_a1b2c3d.onnx。这样即使脱离DVC,也能知道这个模型对应哪个代码版本。

好了,模型序列化与版本控制这块,核心就是「选对格式,管好版本」。ONNX、TorchScript、SavedModel各有适用场景,DVC和Git LFS各有优劣。你根据团队和项目情况选就行。

下一章,咱们聊聊模型部署的自动化测试——怎么确保模型上线后不出幺蛾子。