Skip to content

SafeTensors ​

SafeTensors 是只保存张量的安全序列化格式。它用一个受约束的 JSON Header 描述张量名称、数据类型、形状和字节范围,再把所有张量数据连续存放;读取过程不需要反序列化 Python 对象,也不会执行文件携带的代码。

SafeTensors 是权重容器,不是完整模型包。Transformers 模型通常还需要 config.json、分词器文件和生成配置;如果目标是 llama.cpp 等 GGML 执行器,应进一步转换为 GGUF。

文件结构 ​

一个 SafeTensors 文件由三部分组成:

text
┌──────────────────────────────────────────────┐
│ 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 示例:

json
{
  "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 提供每个张量的字节范围,因此读取器可以先建立索引,再只访问目标张量:

python
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 加载的模型目录通常还包含:

text
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 文件格式。

保存与加载示例:

python
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 的边界 ​

维度SafeTensorsGGUF
核心对象通用张量集合GGML 推理模型
模型配置通常位于外部文件主要信息写入元数据
分词器外部文件可写入元数据
张量编码dtype 与原始字节ggml_type,包含块量化编码
典型使用训练、微调、GPU 推理、模型交换llama.cpp、CPU/本地推理、量化部署

二者经常处在同一条流水线上:模型先以 SafeTensors 在训练和模型仓库中交换,再转换为 GGUF 供 GGML 生态部署。

参考 ​

  1. SafeTensors format specification
  2. SafeTensors documentation
  3. Transformers:Loading models