推理格式是模型走出训练框架的通用语言: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/BF16 | 16 | ~14 GB | 基准 |
| Q8_0 | 8.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) | GGUF | ONNX/TensorRT |
|---|---|---|---|
| 定位 | 服务器分发标准 | 端侧量化推理 | 跨框架交换、GPU 引擎优化 |
| 结构信息 | config.json | 内嵌 metadata | 计算图自身 |
| 权重精度 | FP32/FP16/BF16 | 2–8 bit 量化 | 原精度或 INT8 |
| 代码执行风险 | 无 | 无 | 无(静态图) |
| 典型运行时 | Transformers/vLLM | llama.cpp/Ollama | ONNX 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原则:转换一律走官方脚本,不手改文件名或拼接权重;量化有损,源目录永远保留全精度版本。