模型存储与导出

从权重文件到可执行模型

核心解释

一个模型要运行,需要参数、计算逻辑和输入输出约定。以线性层 Y=XW⊤+bY=XW^{\top}+b 为例,权重文件可以只保存 WW 和 bb;模型代码或计算图还要说明怎样使用它们。选择格式时,先确定要保存的是哪一部分。[1]

PyTorch 常用 .pt 或 .pth 命名文件,内容取决于保存接口。torch.save(state_dict) 保存命名状态,加载时由模型代码提供计算结构;torch.save 也能保存包含优化器、步数等内容的字典。后缀相同,文件承担的用途可以不同。[2]

Safetensors 把张量描述与数据字节分开存储,方便按名称或切片读取。模型目录通常还包含配置、tokenizer 和分片索引。GGUF 则把 GGML 系推理所需的张量与元数据组织在一起,也支持不同的浮点和量化类型。[3] [4] [5]

ONNX 用计算图描述算子、输入输出和常量权重。PyTorch 导出器把捕获到的计算转换成 ONNX 表示,ONNX Runtime 等后端负责执行。导出时的动态尺寸、算子版本和目标后端,会共同决定模型能接受什么输入、在哪些设备上运行。[6] [7]

格式转换完成后,要检查张量是否齐全,以及目标运行时的输出是否符合预期。训练恢复还需要优化器和数据进度,保存这些状态的流程见 Checkpoint。这里继续关注文件内容、交换方式和加载约定。

资料

QA

同一个模型、同样精度,哪种方式保存更快、占磁盘更小?

只保存同一组稠密权重、保持 dtype 且无额外压缩时,体积主要由张量数据决定:Sdata=∑inibiS_{\mathrm{data}}=\sum_i n_i b_i,其中 nin_i 是实际写入的元素数,bib_i 是每个元素的字节数。70 亿个独立的 FP16 或 BF16 权重约占 14 GB(十进制)。PyTorch 默认写未压缩的 ZIP64,Safetensors 写头部和张量数据;在这些条件下,两者体积通常接近,文件头、对齐和元数据带来额外开销。

明显的大小差异,常来自共享与保存范围。PyTorch 会保留 storage 共享,也可能为一个小切片写入整个底层 storage;转成 Safetensors 时,复制共享权重可能增加体积,使用 save_model 则会处理可识别的共享权重。包含优化器等训练状态的 checkpoint 还会增加数据量。比较时先核对实际写入了哪些张量及状态。

保存速度由设备搬运、连续化与复制、序列化和存储写入共同决定。仅凭 .pt 或 .safetensors 无法排出固定先后:应在同一设备、磁盘和库版本下保存相同数据,并记录耗时与峰值内存。Safetensors 官方 speed 页面测的是加载,不能直接用作保存速度结论。

ONNX 导出还要捕获、转换和优化计算图,应把首次导出总耗时与已导出程序的写盘耗时分开。图优化也可能改变保存的常量集合,体积比较要合计 .onnx 与全部 external data。测保存耗时时还需明确终点:API 返回与数据持久化到磁盘是两种口径;从 GPU 开始测量时,要同步并计入设备到主机的搬运。

.pt、.pth、.bin 是三种不同的模型格式吗?

在 PyTorch 项目中,这些后缀常是命名习惯。torch.save 可以把同一个对象写入不同后缀的文件。要判断内容,先看保存接口和传入的对象:是 state_dict、包含优化器的字典,还是完整模型对象。其他工具也可能使用 .bin,后缀本身不能确定加载方式。

保存 state_dict 和直接保存 model,有什么区别?

state_dict 通常包含命名参数和持久 buffer。加载时先用模型代码与配置创建实例,再通过 load_state_dict 写入状态。forward 的计算逻辑由代码提供。

torch.save(model) 序列化整个模块对象,但仍依赖可导入的 Python 类及其位置。代码组织变化会影响加载。长期维护时,模型代码、配置和权重各有明确版本,更容易重建。

TorchScript 的 .pt 和 torch.export 的 .pt2 又是什么?

torch.jit.save 保存 TorchScript 模块,包括方法、参数与属性,用 torch.jit.load 加载。它与 torch.save(state_dict) 得到的 .pt 内容不同。TorchScript 已进入弃用路线,新的导出流程主要看 torch.export。

通过 torch.export.save 保存的 .pt2 包含 ExportedProgram,其图、状态和输入约束供后续执行或编译使用。.pt2 也用于其他 PT2 打包流程,因此仍需匹配具体的保存与加载 API。

weights_only=True 是只读取模型权重吗?

它限制的是反序列化允许构造的对象和调用的函数,不是按“模型权重”字段筛选内容。包含普通张量、数值和字典的优化器状态,也可以处在这个加载范围内。检查文件实际包含哪些状态,仍要查看它的结构。对于需要自定义类的文件,应先核对来源和类定义,再决定允许哪些对象。

Safetensors 里有没有网络结构、配置和 tokenizer?

它的核心内容是命名张量:头部记录 dtype、shape 和数据偏移,数据区存张量字节。格式还允许字符串元数据,但不会替应用定义 forward、配置或 tokenizer 的解释方式。

因此,常见模型仓库会把权重与 config、tokenizer 文件一起分发。Safetensors 也能装优化器中的张量;恢复训练所需的非张量状态和字段约定仍由上层保存流程管理。

转成 Safetensors 后,为什么共享权重或文件大小变了?

PyTorch 序列化会保留张量的存储共享关系。一个很小的切片也可能引用很大的 storage,保存时连同整块 storage 写入。clone 可以缩小这类文件,但会改变共享关系。

Safetensors 不按 PyTorch 的任意视图关系存储张量。对于 embedding 与输出层绑定权重,使用框架支持的保存加载接口,或 Safetensors 的 save_model / load_model,让模型定义重建共享关系。逐个复制张量值并不能表达参数绑定。

model-00001-of-00008.safetensors 是第 1 张 GPU 的权重吗?

Transformers 的这些文件通常按大小组织参数。index.json 的 weight_map 指明每个参数在哪个文件,文件编号不代表 GPU rank。运行时怎样分配到设备,由加载配置决定。

DCP 的训练分片还要表达全局张量与局部分片的位置。把两种分片目录互换,需要相应的加载或转换流程。

为什么有了 model.onnx,还要带一个很大的 data 文件?

ONNX 可以把张量数据放在 protobuf 内,也可以通过 external_data 指向外部文件。外部记录包含相对路径及可选的 offset、length。大模型常将图和权重分开存储。

发布时要同时保留外部数据及其路径关系。超过 2 GB 的大模型用路径调用 onnx.checker.check_model,相关外部数据也要可访问。只复制 .onnx 文件会丢失这部分权重。

ONNX 能打开,为什么换个运行时就不能执行?

先区分 IR version 与 opset:前者规定模型表示,后者规定所用算子的语义版本。运行时还需要支持对应算子、dtype 和自定义 domain。选 opset 时,应对照目标后端。

在 ONNX Runtime 中,Execution Provider 决定算子用哪个硬件实现。配置 CUDA 与 CPU 两种 provider 时,不被 CUDA 支持的节点可以分配到 CPU。文件能解析、图能执行、整图都在 GPU 上执行,是不同的检查结果。

导出一次 ONNX,就能接受任意 batch 和序列长度吗?

导出时需要指定动态维度,并满足图的尺寸约束。当前 dynamo 导出路径使用 dynamic_shapes;示例输入中的普通 Python 参数还可能被固定进图。检查输入 shape、维度关系与分支覆盖,再用实际会出现的尺寸验证。

格式转换会改变数值吗?应该怎样验证?

保留 dtype 和张量内容的容器转换,可以保持张量数值。转成 ONNX 还会改变计算表示;后端融合、计算顺序或精度选择会影响输出。量化则会直接改变权重表示。

权重转换先逐张量核对名称、形状、dtype 和数值。计算图转换用相同预处理和输入,对照 PyTorch 与目标运行时的输出。测试实际尺寸和分支,并按精度设置误差容限。PyTorch 导出器的 verify=True 可做一轮 ONNX Runtime 对照,更多输入仍需补测。

GGUF 就是量化格式吗?转成 GGUF 一定有损吗?

GGUF 是容器格式,能存浮点张量,也能存多种量化张量。是否有损,要看转换时是否降低精度、量化或改变数值处理。架构参数、tokenizer 等元数据帮助 GGML 系运行时解释权重;运行时仍需实现对应模型架构。

.engine / .plan 和 ONNX 有什么区别?

TensorRT engine 是针对运行环境构建的执行产物,已包含构建时选择的实现。ONNX 描述交换用的计算图,运行时或编译器再把它落实到设备。

TensorRT engine 的默认兼容范围受版本、设备和平台约束。构建时可配置更广的兼容范围,具体覆盖面取决于所选模式。跨环境发布时,要记录构建配置,并在目标环境验证。