Tracks
如果您最近从 Hugging Face 下载过模型,可能已经注意到出现的是 .safetensors 文件,而不是 .bin 或 .pkl。这背后的变化比看起来更大。
多年来,大多数机器学习框架依赖基于 pickle 的序列化来存储模型检查点。这种方式虽然“堪用”,但隐藏着代价:在反序列化过程中,加载检查点可能会执行任意 Python 代码,从而为供应链攻击和藏在看似无害模型文件中的恶意载荷打开大门。
SafeTensors 正是为弥补这一缺口而生。它只存储原始张量权重,加载时不执行任何代码,因此成为 Hugging Face Hub 上的默认格式。2026 年 4 月,它与 PyTorch、vLLM、DeepSpeed 和 Ray 等项目一道,正式加入 Linux 基金会旗下的 PyTorch 基金会。
继续阅读,了解该格式的底层工作原理,以及它与其他格式的比较。
什么是 SafeTensors?
SafeTensors 是一种专为安全存储模型权重而设计的文件格式。
该格式将原始数值张量数据与可执行代码分离,并且只存储权重。这正是 SafeTensors 与基于 pickle 的格式的区别所在。
该格式也自然契合现代机器学习流水线,因为它专注于大多数检查点本就包含的内容:
- 张量
- 形状
- 数据类型
- 权重值
SafeTensors 不是通用的 Python 序列化系统,而是作为模型参数的专用存储层发挥作用。
尽管 Hugging Face 最初为其生态开发了 SafeTensors,但该格式本身与框架无关。它近期(2026 年 4 月)加入 PyTorch 基金会,支持 PyTorch、TensorFlow、JAX、Flax、NumPy 以及其他机器学习框架。
Pickle 的问题:为何需要新格式
Python 的 Pickle 格式诞生于通用 Python 应用场景,开发者往往需要保存并恢复完整的 Python 对象,包括其内部状态、方法以及重建逻辑。
而机器学习检查点通常不需要如此全面的存储。大多数模型文件主要存储张量:权重矩阵、嵌入、偏置和其他数值参数。实际上,这意味着检查点大多只是结构化的数值数据。
但 .pkl 文件不仅仅存储数据。它还可以包含在加载时指导 Python 如何重建对象的指令。也就是说,反序列化不是被动的读取操作;它可能会执行代码。如果这些代码是恶意的,就会构成严重的安全问题。
pickle 如何实现任意代码执行
Python 在 pickle 反序列化过程中通过 __reduce__() 等特殊方法重建对象。类可以定义该方法,以告诉 pickle 在将对象加载回内存时应如何重建。
例如,__reduce__() 可以返回一个可调用函数及其参数。反序列化期间,Python 会执行该可调用对象以重建对象。示例代码:
import pickle
import os
class Demo:
def __reduce__(self):
return (os.system, ("echo 'Code executed during deserialization'",))
payload = pickle.dumps(Demo())
pickle.loads(payload)
当 pickle.loads() 运行时,Python 会执行 __reduce__() 返回的函数。在此示例中,反序列化通过 os.system() 触发了一个 shell 命令。
问题不在于 shell 命令本身。__reduce__() 可以根据其逻辑返回任意可调用对象及任意参数。这意味着 Pickle 文件可以调用其他函数、下载文件、修改环境,或在加载时执行恶意代码。因此,Python 文档明确警告不要从不受信任的来源加载 pickle 数据。
模型共享时代的风险
像 Hugging Face Hub 这样的平台如今托管了超过一百万个由研究者、初创公司、爱好者和匿名贡献者共享的模型。开发者可以立刻下载并测试模型,使得生态高速发展。但大多数上传的检查点在分发前并未进行逐一审计。
许多 PyTorch 检查点仍通过 .pt 或 .bin 等格式依赖基于 pickle 的序列化。当有人加载这些文件时,Python 可能会执行嵌入在检查点中的反序列化逻辑。如果检查点是恶意的,这些逻辑可能会窃取凭证、读取环境变量、下载载荷,或在加载时执行远程代码。

这正是 SafeTensors 要解决的核心问题。它不再序列化任意 Python 对象,而是只存储张量数据和正确加载这些张量所需的元数据。加载 .safetensors 文件无需执行 Python 的重建逻辑,从而显著降低攻击面。
SafeTensors 格式如何工作
现在我们已经了解了 SafeTensors 的定义和存在的原因,接下来看看它的结构与工作方式。
头部-数据结构
一个 SafeTensors 文件包含两部分:JSON 头部,随后是原始张量数据。
这个头部为每个张量存储元数据,包括
- 张量名称
- 形状
- 数据类型
- 字节偏移
头部之后,文件会连续存储原始张量字节。
头部的作用类似目录。它告诉加载器文件中有哪些张量以及它们在文件中的位置,有点像数据库索引指向存储的记录。为防止元数据负载过大,SafeTensors 将头部大小限制为 100MB。
零拷贝与内存映射加载
SafeTensors 通过内存映射加载来提升加载速度。与其在反序列化过程中重建 Python 对象,各框架可以将张量数据直接从磁盘映射到内存。这减少了不必要的内存拷贝,降低了加载过程中的 CPU 开销。
根据 Hugging Face 的基准测试,SafeTensors 在 CPU 上加载权重比 PyTorch 快约 76 倍,在 GPU 工作负载上快约 2 倍。当然,具体加速效果仍取决于硬件和检查点大小,但避开 Python 反序列化通常会稳定提升加载性能。
惰性加载与部分反序列化
SafeTensors 可以按名称加载特定张量,而不是一次性将整个检查点读入内存。
这对于跨多块 GPU 运行的大型分布式模型很有用。以 BLOOM 的 1760 亿参数模型为例。在标准 PyTorch 检查点下,系统必须先反序列化完整模型权重,再将其切分分发到各设备,耗时约 10 分钟。
使用 SafeTensors 时,每块 GPU 只加载实际需要的张量分片。这将模型启动时间缩短至约 45 秒(8 块 GPU)。
SafeTensors 与其他序列化格式对比
SafeTensors 非常适合安全高效地存储与加载模型权重,但这并不意味着它可以普适替代所有格式。正确的选择取决于您要存储的内容以及模型在流水线中的位置。
我们已经讨论了很多有关 pickle 的内容,下面将重点放在不同的格式上。
SafeTensors vs GGUF
SafeTensors 与 GGUF 解决的是不同问题。
GGUF(GGML Unified Format 的缩写)专为 llama.cpp 等运行时时的量化推理工作负载而构建。该格式关注压缩模型的高效部署,尤其是 CPU 推理与边缘设备。
SafeTensors 位于流水线的更前端。大多数 SafeTensors 检查点存储的是全精度或可用于训练的张量,适用于训练、微调、权重合并或分布式推理等工作流。该格式优先保证安全加载、与 PyTorch 等框架的兼容性,以及训练与服务过程中对张量的高效访问。
二者并非直接竞争,而是可以互为补充。示例工作流:
- 使用 PyTorch 与 SafeTensors 检查点训练或微调模型
- 对最终模型进行量化
- 导出为 GGUF,以便在 llama.cpp 或边缘推理运行时中部署
SafeTensors vs ONNX
SafeTensors 专注于在 PyTorch 等框架内安全地存储模型权重并高效加载。它仅存储张量与元数据,因此在检查点共享、微调和训练工作流中轻量且快速。
ONNX 的范围更广。它不仅存储模型参数,还保存完整的计算图。这使得 ONNX 适用于希望将模型从一个框架导出并在完全不同环境中运行的场景。
例如,使用 PyTorch 训练与微调大语言模型的团队通常会更偏好 SafeTensors 检查点,因为它加载迅速并可直接融入既有工作流。但如果同一团队需要将模型部署到 TensorRT、ONNX Runtime 或边缘推理引擎,则将模型导出为 ONNX 更为合理。
实践中使用 SafeTensors
SafeTensors 能在 PyTorch 生态中迅速普及的一个原因是其 API 十分熟悉。您仍可像往常一样处理 state dict 与张量。
保存与加载张量
基本工作流与标准的 PyTorch 检查点处理几乎一致。示例如下:
import torch
from safetensors.torch import save_file, load_file
tensors = {
"weights": torch.randn(2, 2),
"bias": torch.zeros(2)
}
save_file(tensors, "model.safetensors")
loaded_tensors = load_file("model.safetensors")
print(loaded_tensors["weights"])
save_file() 将张量写入 .safetensors 格式,而 load_file() 则将其加载回内存。
SafeTensors 还通过 safe_open() 支持选择性加载,这在只需要少量张量的大型检查点场景中很有用。
from safetensors import safe_open
with safe_open("model.safetensors", framework="pt") as f:
weights = f.get_tensor("weights")
print(weights)
与其加载完整检查点,get_tensor() 只会读取您请求的张量。
将现有模型转换为 safetensors
标准模式为:
- 加载现有模型
- 提取 state dict
- 使用
save_file()保存
from transformers import AutoModel
from safetensors.torch import save_file
model = AutoModel.from_pretrained("bert-base-uncased")
save_file(model.state_dict(), "model.safetensors")
state_dict() 以张量形式返回模型权重,SafeTensors 可直接存储。
如果模型已经托管在 Hugging Face Hub 上,您甚至可能无需本地转换代码。Hugging Face 为托管模型提供通过 Hub 界面进行检查点转换的内置支持。
序列化格式:对比表
| 特性 | SafeTensors | Pickle (.pkl/.bin/.pt) |
GGUF | ONNX |
|---|---|---|---|---|
| 任意代码执行 | 否 | 是 | 否 | 否 |
| 主要使用场景 | 训练、微调、检查点共享 | 通用 Python 序列化 | 量化的边缘/CPU 推理 | 跨框架部署 |
| 是否存储计算图 | 否 | 否 | 否 | 是 |
| 内存映射加载 | 是 | 否 | 是 | 否 |
| 惰性/部分张量加载 | 是 | 否 | 是 | 否 |
| 框架支持 | PyTorch、TF、JAX、Flax、NumPy | Python(所有框架) | llama.cpp、边缘运行时 | ONNX Runtime、TensorRT、边缘 |
| 量化支持 | 在扩展中(FP8、GPTQ、AWQ) | 否 | 是(原生) | 是 |
| Hugging Face Hub 默认格式 | 是 | 否 | 否 | 否 |
2026 年的 SafeTensors:加入 PyTorch 基金会与后续方向
2026 年 4 月,Hugging Face 将 SafeTensors 贡献给 Linux 基金会旗下的 PyTorch 基金会。该项目现与 PyTorch、vLLM、DeepSpeed 和 Ray 一同纳入基金会治理。
此举表明,SafeTensors 已不再只是一个 Hugging Face 项目,而正在成为机器学习生态的共享基础设施。
公告还提到了该格式的未来方向。
面向设备的加载
一个重要焦点是面向设备的加载。当前,许多工作流仍会先将张量加载到 CPU 内存,再传输到 CUDA 或 ROCm 设备。这个额外的中转步骤会增加启动时延,尤其是在大型分布式系统中。
SafeTensors 的维护者正在推进直接设备加载路径,减少不必要的 CPU 拷贝,将张量直接移动到加速器上。
分布式加载
分布式加载支持也在演进。 现代推理系统很少再将模型仅运行在单块 GPU 上。张量并行与流水线并行已成为大模型的标准部署模式,但各框架的检查点加载 API 仍较为分散。
SafeTensors 正在扩展对分片感知加载和分布式张量布局的支持,以便各框架能更高效地在多设备间协调检查点加载。
现代量化工作流
该格式也在适配更新的量化工作流。 推理系统日益依赖 FP8、GPTQ 和 AWQ 等格式来降低内存占用和服务成本。
SafeTensors 并非强迫框架通过自定义序列化逻辑来处理这些格式,而是将对低精度与分块量化张量格式的正式支持直接加入到格式本身。
目前,开发者仍需通过切换保存与加载工作流来手动采用 SafeTensors。但围绕与 PyTorch 原生序列化系统的更深集成正在推进。若最终落地,SafeTensors 可能不再只是替代性检查点格式,而会成为 PyTorch 存储模型的默认方式。
结语
多年来,开发者一直使用在加载时可能执行任意 Python 代码的序列化系统来共享模型检查点。随着公共模型平台开始托管数以百万计的检查点,这类安全风险已难以忽视。
SafeTensors 通过收窄格式范围改变了这一点。它不再尝试序列化完整的 Python 对象,而是仅专注于存储张量及其所需的加载元数据。更简洁的设计既消除了基于 pickle 的检查点所带来的反序列化风险,也提升了加载速度与内存效率。
因此,当您只需要模型权重时,没有理由在反序列化过程中执行任意代码——在这些场景下使用 SafeTensors 更合适。
如果您想更深入地学习现代机器学习工具链、模型格式与 Hugging Face 工作流,我们的 Deep Learning in Python 和 Working with Hugging Face 课程是不错的下一步选择。它们涵盖了在当今 AI 生态中常用工具下的训练、微调与部署的实用工作流。
SafeTensors 常见问题
我可以将现有的 .bin 或 .pt 模型转换为 SafeTensors 吗?
可以。您可以在 PyTorch 或 Transformers 中正常加载模型,提取 state_dict(),并使用 save_file()(来自 safetensors 库)进行保存。Hugging Face Hub 也支持对许多托管模型的自动转换。
SafeTensors 能存储量化模型吗?
可以。SafeTensors 已支持多种低精度张量类型,且随着量化推理的普及,对 FP8、GPTQ、AWQ 等格式的支持也在不断扩展。
无需加载完整模型,就能查看 SafeTensors 文件内容吗?
可以。该格式将张量元数据单独存储在头部,因此您无需将完整检查点加载到内存中,也能查看张量名称、形状和数据类型。
使用 SafeTensors 会影响模型精度吗?
不会。SafeTensors 改变的是权重的存储与加载方式,而非其数值本身。模型在加载后表现不变。
SafeTensors 只对推理有用吗?
不止于推理。开发者也会在训练、微调、检查点共享以及分布式加载等工作流中使用它。
