Skip to content
Charles Shao
Go back

模型文件格式分类与选择:.pt / SavedModel / ONNX / safetensors / GGUF / TensorRT / PMML 到底怎么选

Updated:
–views

训练脚本最后那句 save,吐出来的往往只是这一次训练用得顺手的容器,不是上线契约。等要挂到服务端 GPU、交给别的框架加载,.pt、SavedModel、.onnx、.safetensors、.gguf、.engine、.pmml 才会暴露出彼此差得很远:有的只有权重,有的自带计算图,有的绑死某一代 GPU。选错不是换个扩展名的事——轻则加载失败,重则精度漂了,还可能在 torch.load 时执行陌生代码。

打个比方:训练产物像刚写完的稿子,「导出成什么格式」是在决定怎么发货。续训要可编辑的源文件,上线要加载方拿来就能跑的版本,本地跑大模型还得压成带量化的单文件。

先看文件里装了什么,再问给谁跑。 格式没有最好,只有最贴目标栈。

Table of contents

Open Table of contents

一、权重、计算图、元数据

后缀不重要。一个模型文件最多装三样,缺哪样,加载方就要用别的方式补。可以把它想成做菜:

一个模型文件最多装权重、计算图、元数据。权重是唯一必备项;没有计算图,加载方必须自带建模代码;没有元数据,签名、dtype、量化参数就要靠约定补齐。

三种常见组合:

装了什么加载方还要什么
只有权重(safetensors、state_dict、多数 HF 仓库)模型定义代码 + config.json
图 + 权重(ONNX、SavedModel、TorchScript、GGUF、PMML)对应运行时即可,不必有原始建模类
再加训练态(优化器、epoch、随机数种子)这是 checkpoint,为了接着练,不是为了推理

选后缀之前先问两件事:加载方有没有模型代码?没有就不能选只存权重的格式。接下来是接着练,还是只推理?接着练必须留框架原生 checkpoint;只推理应丢掉优化器。


二、config.json:只有权重时用来搭空壳

只存权重的仓库几乎总会带一份 config.json。它是元数据的常见落盘:UTF-8 的 JSON 文本,给建模代码看「该建成什么样的空壳」,再把权装填进去。没有它,类不知道 hidden size、层数、词表长度,from_pretrained 对不上 tensor 形状。

{
  "model_type": "llama",
  "hidden_size": 4096,
  "num_hidden_layers": 32,
  "num_attention_heads": 32,
  "vocab_size": 128256,
  "torch_dtype": "bfloat16"
}

model_type 决定用哪个建模类,后面的数字决定张量形状。

有权重也有计算图时,加载推理不需要 config.json。 图里已经有输入输出、形状、dtype 和算子连法,运行时按图执行即可。config.json 只在「只有权重、没有图」时必需。


三、sidecar:图外的分词和预处理

图里若没带分词和预处理,缺的不是 config.json,而是 sidecar(伴随文件),也叫 tokenizer / preprocessor artifacts。它们描述的是怎么把原始输入变成图要的张量、怎么把输出解回业务含义,不是网络有几层。

伴随文件格式里面是什么
tokenizer.jsonJSON词表、BPE merges、特殊 token、分词算法
tokenizer_config.jsonJSON用哪种 tokenizer 类、截断长度、是否 add_bos
preprocessor_config.jsonJSON图像短边、mean/std、resize 方式(CV)
特征说明(自拟 feature_spec.json 等)多为 JSON表格字段名、类型、归一化、缺省值
SentencePiece tokenizer.model二进制 protobuf另一套词表,和 JSON 词表二选一

文本 sidecar 精简后大致是:

{
  "model": { "type": "BPE", "vocab": { "<unk>": 0, "hello": 1 }, "merges": ["h e"] },
  "added_tokens": [{ "id": 2, "content": "<eos>", "special": true }]
}

加载时用它把 "hello" 变成 token id,再喂给 ONNX / engine。CV 则常见:

{
  "image_size": 224,
  "resample": "bilinear",
  "image_mean": [0.485, 0.456, 0.406],
  "image_std": [0.229, 0.224, 0.225]
}

GGUF、PMML、部分 SavedModel 会把词表或特征变换打进主文件,sidecar 可以少带。ONNX 和 TensorRT 多数只含计算图,分词和减均值仍要这些文件,或写死在服务代码里。

分工是:config.json 搭空壳,sidecar 处理图外的输入输出。


四、训练刚结束:先落什么盘

第一手产物和训练框架绑在一起,任务是能复现、能续训、能分发给自己人。不要在这一步就编 TensorRT engine。

PyTorch。 日常存 state_dict。对外和进社区仓库,立刻转成 safetensors,不要把 pickle 的 .bin 当交付物。若要脱离 Python,再导出 TorchScript 或 torch.export;trace 会录死控制流,script 只覆盖 Python 子集。

.pt / .pth 是歧义后缀:可能是纯 state_dict,也可能是 TorchScript。看加载 API——load_state_dict 还是 torch.jit.load——不要看扩展名。

TensorFlow / Keras。 续训用 checkpoint;上 TF Serving 用 SavedModel(图 + 权重 + 签名)。.keras 是 Keras 内保存的默认;Frozen GraphDef 已逐步被 SavedModel 取代。

JAX / Flax。 常见是参数树(msgpack / Orbax),图靠代码重建,和「只存权重」是同一类。

落地规范:工作目录里同时留两份——一份带优化器的 checkpoint 供续训,一份 safetensors(或 SavedModel)供下游转换。下游只读第二份,避免有人把训练态塞进推理镜像。


五、要上线:按目标栈选终点

部署格式几乎都是转出来的,不是训练框架的默认保存。终点由「在哪跑、用什么运行时」决定,和运行环境怎么选是同一件事的文件侧。

跨框架的中小神经网络 → ONNX。 开放图 IR,权重内嵌,ONNX Runtime / Triton 都能吃。导出时盯 opset 和 dynamic_axes:自定义算子可能不在 opset 里;忘了声明动态维,线上换 batch 会把图写死。适合 CV / 传统 NLP,不适合当 LLM 服务端的主格式。

树模型、逻辑回归、要进 Java / 风控 → PMML。 一份 XML(Data Mining Group 标准),把字段字典、特征变换和模型写在同一个文件里,自包含、可打开审阅。sklearn / Spark / XGBoost 导出后交给 JPMML 评分,线上不必留 Python。它不是张量格式,扛不住大神经网络和 LLM;深度模型应走 ONNX,不要为了「也是跨框架」硬转 PMML。

服务端 GPU 跑 LLM、权重分发 → safetensors。 纯张量:JSON 头 + 二进制块,可 mmap,不含可执行代码。HF 上的事实默认。vLLM / SGLang 直接加载;要再压延迟,才从这份权重量化或编成 TensorRT-LLM engine。它仍然只存权重,config 和建模代码要一起走。

本地 / 端上跑 LLM → GGUF。 llama.cpp 生态的单文件,权重、量化档位、超参、tokenizer 打在一起。Q4_K_M 是量化方案,不是另一种格式。不是训练格式,也不是服务端 GPU 池的主力。

单卡打满、接受换卡重编 → 硬件编译产物。 TensorRT engine 绑 GPU 架构和库版本;CoreML 走 Apple Neural Engine;TFLite 走 Android 与嵌入式;OpenVINO 走 Intel。不要当可移植包提交——换卡或升级驱动等于要重编。

训练产物到上线的主干转换。上路神经网络:PyTorch → ONNX → Runtime / Triton,可选 TensorRT。中路 LLM:safetensors 给 vLLM,或转 GGUF。下路传统模型:sklearn / XGBoost → PMML → JPMML。


六、转换前拍死:安全和量化

不可信来源不要 pickle。 .pt / .pth / pytorch_model.bin 本质是 Python pickle,反序列化会执行其中的构造指令。恶意文件可以在 torch.load() 时读密钥、装后门。

pickle 与 safetensors 对照。上路 torch.load 经 unpickle,可能执行任意代码。下路 load_file 只读 JSON 头并 mmap 张量字节。

对外分发和从社区下载,只用 safetensors。内部续训的 checkpoint 可以继续 pickle,但不要流出训练网。确需 torch.load 外来文件时,加 weights_only=True。

量化不是一种格式。 量化是权重怎么编码(几 bit、什么算法),格式是用什么容器装着。先定质量 / 体积 / 速度,再选容器:GGUF 把档位写成一等公民;ONNX / TensorRT 的 INT8 往往要校准数据;AWQ / GPTQ 的结果仍常落在 safetensors 里。同一套权可以一份 FP16 safetensors 给服务端、一份 Q4 GGUF 给本地——那是两条产物,不是改了个格式名。


七、按场景填格式

三个问题按顺序问:

  1. 给谁跑? 续训 / 分发权重 / 服务端 GPU / 跨框架神经网络 / 树模型进 Java / 本地 LLM / 端上。
  2. 加载方有没有建模代码? 没有就选自包含图的格式。
  3. 能不能接受换卡重编? 能,才配 TensorRT engine 换延迟。
场景落盘格式谁来加载
续训、实验框架原生 checkpointPyTorch / TF / JAX
分发权重、HF、微调产物safetensors建模代码 + config.json
服务端 GPU 跑 LLMsafetensors(可选再编 TensorRT-LLM)vLLM / SGLang / TensorRT-LLM
本地 / 端上 LLMGGUF(选定量化档)llama.cpp / Ollama
跨框架的一次前向(神经网络)ONNXONNX Runtime / Triton
树模型 / LR,Java 或风控评分PMMLJPMML 等评分引擎
NVIDIA 上压一次前向延迟TensorRT engineTriton / TensorRT
iOS / macOSCoreMLCore ML
Android / 嵌入式TFLiteLiteRT
广告深度 CTR 进 GPU 池ONNX 或 TensorRT engineTriton(见落地篇)

广告 / 推荐里,训练侧仍是 checkpoint + safetensors;深度 CTR 进 GPU 池才是 ONNX 或 engine。粗排若是 LR / 树模型、且评分跑在 Java 上,才落到 PMML。权重不要打进镜像,对象存储里按服务框架的仓库目录放——换版改 URI。


八、转换怎么落地

把转换当成带门禁的流水线,而不是笔记本里的一次性 export。导出 ONNX 时,动态维和 opset 写进流水线:

model.eval()
dummy = torch.randn(1, 3, 224, 224)
torch.onnx.export(
    model, dummy, "model.onnx",
    input_names=["input"], output_names=["logits"],
    dynamic_axes={"input": {0: "batch"}, "logits": {0: "batch"}},
    opset_version=17,
)
翻车点上线前怎么拦
算子不在目标 opset / 运行时导出失败即阻断;自定义层单独登记或改写
动态 shape 没声明用两个不同 batch / 序列长度跑加载
trace 录死控制流带分支的输入各走一遍,输出对不上就不要用 trace
FP16 / INT8 精度漂真实样本上对比转换前后,阈值写进发布
tokenizer / 归一化没进图部署侧 sidecar 与导出时用的是同一套
PMML 丢掉变换或树太深特征工程一并打进 XML;同一批样本对原模型与 JPMML 打分

每转一次格式,用一批真实输入做转换前 vs 转换后的数值对比,过线再进对象存储。文件能加载,不等于还能打出原来的分。


一句话总结

看装了什么,再问给谁跑。 只有权重时用 config.json 搭空壳;有图仍可能要 sidecar 做分词和预处理。续训留 checkpoint;分发用 safetensors;神经网络跨框架用 ONNX;树模型和 LR 进 Java 用 PMML;服务端 LLM 用 safetensors 进 vLLM;本地 LLM 用 GGUF;压单卡延迟才编 TensorRT / CoreML / TFLite。

两条门禁:不可信来源不用 pickle;每次转换都做数值对比。产物进对象存储、换版只改 URI,见推理服务业界落地。


–views
Share this post on:

Previous Post
推理运行时与服务框架选型:运行环境、服务框架、编排平台怎么分层
Next Post
Web 变现深挖:GPT、广告标签与可见性如何把页面变成收入