SafeTensors
SafeTensors 是只保存张量的安全序列化格式。它用一个受约束的 JSON Header 描述张量名称、数据类型、形状和字节范围,再把所有张量数据连续存放;读取过程不需要反序列化 Python 对象,也不会执行文件携带的代码。
SafeTensors 是权重容器,不是完整模型包。Transformers 模型通常还需要 config.json、分词器文件和生成配置;如果目标是 llama.cpp 等 GGML 执行器,应进一步转换为 GGUF。
文件结构
一个 SafeTensors 文件由三部分组成:
┌──────────────────────────────────────────────┐
│ Header Size │
│ N: uint64 little-endian │
├──────────────────────────────────────────────┤
│ JSON Header │
│ tensor name → dtype / shape / data_offsets │
├──────────────────────────────────────────────┤
│ Byte Buffer │
│ densely packed tensor data │
└──────────────────────────────────────────────┘前 8 字节是小端 uint64,表示 JSON Header 的字节数 N。接下来的 N 字节是 UTF-8 JSON,剩余区域是张量数据。
Header 示例:
{
"model.layers.0.self_attn.q_proj.weight": {
"dtype": "BF16",
"shape": [4096, 4096],
"data_offsets": [0, 33554432]
},
"model.layers.0.self_attn.k_proj.weight": {
"dtype": "BF16",
"shape": [1024, 4096],
"data_offsets": [33554432, 41943040]
},
"__metadata__": {
"format": "pt"
}
}data_offsets = [BEGIN, END] 相对于 Byte Buffer 起点,张量占用 END - BEGIN 字节。__metadata__ 是可选的字符串到字符串映射,不能嵌入任意 JSON 对象。
安全边界
PyTorch 传统 .bin 或 .pt 检查点通常基于 pickle。pickle 可以重建 Python 对象,也意味着加载不可信文件时可能执行任意代码。SafeTensors 刻意只表达张量和少量字符串元数据,不提供对象构造或代码执行机制。
格式还通过结构约束减少恶意输入的空间:
- Header 有大小上限,避免解析异常巨大的 JSON。
- 张量字节范围必须落在文件内且不能重叠。
- Byte Buffer 必须被完整索引,不能留下可用于拼接其他格式的空洞。
- 张量名称不能重复。
“安全”主要指格式没有类似 pickle 的代码执行语义,不代表张量数值可信。文件仍然可以包含 NaN、Inf 或无意义权重,读取库本身也仍需按照普通软件处理安全更新。
按需读取
Header 提供每个张量的字节范围,因此读取器可以先建立索引,再只访问目标张量:
from safetensors import safe_open
with safe_open("model.safetensors", framework="pt", device="cpu") as file:
shape = file.get_slice("model.embed_tokens.weight").get_shape()
tensor = file.get_tensor("model.embed_tokens.weight")在 CPU 上,SafeTensors 可以利用内存映射减少额外拷贝;在多 GPU 或分布式加载中,也可以只读取当前 rank 需要的张量。数据最终进入 GPU 时仍然需要设备传输,因此“zero-copy”不能理解为磁盘到 GPU 全程没有复制。
模型目录与分片
SafeTensors 文件只保存张量。一个可由 Transformers 加载的模型目录通常还包含:
model/
├── config.json
├── tokenizer.json
├── tokenizer_config.json
├── generation_config.json
├── model.safetensors.index.json
├── model-00001-of-00004.safetensors
├── model-00002-of-00004.safetensors
├── model-00003-of-00004.safetensors
└── model-00004-of-00004.safetensors模型较大时,Transformers 会把权重拆成多个 .safetensors 分片,并用 model.safetensors.index.json 的 weight_map 记录“参数名 → 分片文件”。这个 index 是 Transformers 模型目录的组织方式,不属于单个 SafeTensors 文件格式。
保存与加载示例:
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
"model-name",
use_safetensors=True,
)
model.save_pretrained(
"./model",
safe_serialization=True,
max_shard_size="5GB",
)数据类型与量化
SafeTensors 在 Header 中记录 dtype,并原样保存对应字节。它支持浮点、整数和布尔等张量类型,但不规定“某组整数张量、scale 和 zero-point 共同构成哪一种量化算法”。
因此 GPTQ、AWQ、TorchAO 等量化权重可以使用 SafeTensors 作为底层容器,但量化语义由模型配置、张量命名和上层加载器约定。SafeTensors 本身既不执行量化,也不等价于某一种量化格式。
与 GGUF 的边界
| 维度 | SafeTensors | GGUF |
|---|---|---|
| 核心对象 | 通用张量集合 | GGML 推理模型 |
| 模型配置 | 通常位于外部文件 | 主要信息写入元数据 |
| 分词器 | 外部文件 | 可写入元数据 |
| 张量编码 | dtype 与原始字节 | ggml_type,包含块量化编码 |
| 典型使用 | 训练、微调、GPU 推理、模型交换 | llama.cpp、CPU/本地推理、量化部署 |
二者经常处在同一条流水线上:模型先以 SafeTensors 在训练和模型仓库中交换,再转换为 GGUF 供 GGML 生态部署。