推理格式是模型走出训练框架的通用语言:HF 标准目录定义结构与分发约定,Safetensors 解决安全与加载速度,GGUF 面向量化与端侧;三者覆盖服务器到消费级设备的全谱系部署。

格式定位

模型权重保存 中的训练态格式(.pt/.pth、各框架分片 ckpt)保存优化器状态与并行信息,与框架强绑定;推理格式只保留权重 + 结构描述,可跨框架、跨设备加载。主力三件套:

  • HF Transformers 标准目录:结构 + 权重的目录约定,服务器端事实标准
  • Safetensors:权重文件格式,替代 Pickle 系的 .bin/.pt
  • GGUF:单文件自包含 + 量化,llama.cpp 端侧生态标准

ONNX/TensorRT 属于计算图交换与引擎格式,走另一条导出链路,见 ONNX 模型导出。

HuggingFace Transformers 标准目录

model_dir/
├── config.json                      # 结构配置:architectures、层数、hidden_size、头数
├── model.safetensors                # 小模型:单文件权重
├── model-00001-of-00003.safetensors # 大模型:分片权重(每片默认约 5 GB)
├── model.safetensors.index.json     # weight_map:张量名 → 分片文件
├── tokenizer.json                   # fast tokenizer
├── tokenizer_config.json
└── generation_config.json           # 采样默认参数

要点:

  • config.json 是加载核心:AutoModel 靠它实例化网络结构,权重只是填充 state_dict;缺它则无法建模。
  • index.json 记录 total_size 与 weight_map,支撑大模型按需加载单个分片;分片缺一或索引损坏即失败。旧命名 pytorch_model.bin / pytorch_model.bin.index.json 仍存在于大量存量仓库。
  • from_pretrained 在 .bin 与 .safetensors 并存时优先加载后者。
  • 大模型加载用 device_map="auto"(由 Accelerate 提供调度),把各层切分到多 GPU/CPU。
  • 与 HuggingFace Hub 生态互为因果:目录约定让 Hub 能做分片并行下载与增量缓存,safetensors 让上传扫描可判定无恶意代码。

Safetensors

文件结构

8 字节 header 长度(u64 LE)+ JSON header + 连续二进制数据。header 是纯数据描述:每张量的 dtype、shape、data_offsets,不含任何可执行语义。

为何杜绝代码执行

Pickle 是基于栈的序列化虚拟机:GLOBAL + REDUCE 操作码可引用并调用任意可调用对象,经典 payload 就是 (os.system, ("rm -rf /",))——torch.load 来路不明的 .pt/.bin 等价于执行任意代码。PyTorch 2.6 起 torch.load 默认 weights_only=True,收紧了反序列化面,但本质仍是”限制解释器”,而非根除。

Safetensors 从格式层面消除执行:header 只描述张量布局,没有操作码、没有对象引用;加载器对文件做 mmap 后按 offsets 直接构造张量视图——零拷贝,跳过逐对象反序列化,加载显著快于 torch.load,且支持按单张量惰性读取。Rust 核心带 Python/C++/JS 等绑定,推理引擎可跨语言直接读。

局限:只能存张量,不能存优化器/RNG 状态(本就只该用于推理权重);共享存储的绑定张量(tied embeddings)保存会报错,需 clone 或依赖 config 的 tie_word_embeddings 元数据只存一份。

.bin → safetensors 转换

import torch
from safetensors.torch import save_file
 
state = torch.load("pytorch_model.bin", map_location="cpu", weights_only=True)
# 解除张量间共享存储,否则 save_file 拒绝保存
state = {k: v.clone().contiguous() for k, v in state.items()}
save_file(state, "model.safetensors")

更稳的路径是经 Transformers 重新落盘,新版 save_pretrained 已默认 safetensors:

model.save_pretrained("out_dir", safe_serialization=True)

GGUF

llama.cpp 生态的单文件格式:magic + 版本 + metadata KV(架构与超参,等价于内嵌 config.json)+ tensor info + 对齐后的量化权重。取代旧 GGML(无版本化元数据,每支持一个新模型都要改解析器)。

量化等级与体积直觉

估算式:文件体积 ≈ 参数量 × bits/权重 ÷ 8;运行时另加随上下文长度线性增长的 KV cache。

等级bits/权重7B 模型体积质量
F16/BF1616~14 GB基准
Q8_08.5~7.4 GB几乎无损
Q4_K_M~4.8~4.2 GB甜点档,损失很小
Q2_K~3.4~3 GB明显退化

K 系(k-quants)按块混合精度量化,M 档对敏感张量保留更高精度。经验法则:预算内优先 Q4_K_M,追求质量上 Q8_0,Q2/Q3 仅作下限尝试。

加载与转换

  • 只能由 llama.cpp 系运行时加载(llama.cpp、Ollama、LM Studio、llama-cpp-python);PyTorch/Transformers 不能直接读。
  • 量化等级之间不能互转——Q8 不是 Q4 的超集,需回到 F16 源重新量化。
# HF 目录 → 未量化 GGUF(F16/BF16)
python convert_hf_to_gguf.py ./model_dir --outfile model-f16.gguf
# 再量化到目标等级
llama-quantize model-f16.gguf model-q4km.gguf Q4_K_M

格式对照与转换链路

维度HF 目录(safetensors)GGUFONNX/TensorRT
定位服务器分发标准端侧量化推理跨框架交换、GPU 引擎优化
结构信息config.json内嵌 metadata计算图自身
权重精度FP32/FP16/BF162–8 bit 量化原精度或 INT8
代码执行风险无无无(静态图)
典型运行时Transformers/vLLMllama.cpp/OllamaONNX Runtime/TensorRT

训练态检查点(Megatron/DeepSpeed/FSDP 分片格式见 大模型检查点)先经各框架官方合并工具归一为单份 state_dict,落成 HF 标准目录,再分叉到端侧或引擎格式:

训练分片 ckpt ──官方合并工具──► HF 标准目录(safetensors,上 Hub 分发)
                                 ├─ convert_hf_to_gguf.py + llama-quantize ─► GGUF(端侧)
                                 └─ optimum / torch.onnx.export ─► ONNX ─► TensorRT

原则:转换一律走官方脚本,不手改文件名或拼接权重;量化有损,源目录永远保留全精度版本。