深度解析:Safetensors——新一代模型权重存储格式的全面指南

在日常AI模型训练过程中,需要好的模型权重通常需要以一种格式存储在磁盘中。比如:目前最流行的AI框架 PyTorch 使用 pickle 格式存储模型权重文件。但 PyTorch 文档中有一段话说明如下:使用 torch.load() 保存模型,除非 weights_only 参数设置为True (只加载张量、原始类型和字典),否则隐式使用 pickle 模块,这是不安全的。可以构造恶意的 pickle 数据,该数据将在 unpickling 期间执行任意代码。切勿在不安全模式下加载可能来自不受信任来源的数据或可能已被篡改的数据,仅加载您信任的数据。为了规避类似的问题,新的权重存储格式 Safetensors 应运而生。

Safetensors 简介

Safetensors 是一种用于安全地存储张量的新格式,非常简单,但速度仍然很快(零拷贝)。它是 pickle 格式的替代品,因为,pickle 格式不安全,可能包含可以执行的恶意代码。

Safetensors 内部格式

假设现在有一个名为 model.safetensors 的Safetensors文件,那么 model.safetensors 内部格式如下:

image.png

  • 前面的 8 bytes是一个无符号的整数,表示 header 占的字节数。
  • 中间的 N bytes是一个UTF-8编码JSON字符串,存储 header 的内容,里面为模型权重的元数据信息。
  • 文件的剩余部分存储模型权重 tensor 的值。

以GPT2的Safetensors文件为例,其元数据信息可以通过如下代码获取:

import requests # pip install requests
import struct

def parse_single_file(url):
    # Fetch the first 8 bytes of the file
    headers = {'Range': 'bytes=0-7'}
    response = requests.get(url, headers=headers)
    # Interpret the bytes as a little-endian unsigned 64-bit integer
    length_of_header = struct.unpack('<Q', response.content)[0]
    # Fetch length_of_header bytes starting from the 9th byte
    headers = {'Range': f'bytes=8-{7 + length_of_header}'}
    response = requests.get(url, headers=headers)
    # Interpret the response as a JSON object
    header = response.json()
    return header

url = "https://huggingface.co/gpt2/resolve/main/model.safetensors"
header = parse_single_file(url)

print(header)
# {
#   "__metadata__": { "format": "pt" },
#   "h.10.ln_1.weight": {
#     "dtype": "F32",
#     "shape": [768],
#     "data_offsets": [223154176, 223157248]
#   },
#   ...
# }

不同模型权重格式大比拼

对于不同模型权重格式,从如下几个方面进行全面的对比:

  • 安全性:是否可以使用随机下载的文件并期望不运行任意代码吗?
  • 零拷贝:读取文件是否需要比原始文件更多的内存?
  • 延迟加载:可以在不加载所有内容的情况下检查文件吗?并仅加载其中的一些张量而不扫描整个文件吗?
  • 布局控制:延迟加载不一定足够,因为如果有关张量的信息分散在您的文件中,那么即使可以延迟访问该信息,您也可能必须访问大部分文件才能读取可用的张量(导致许多磁盘到 RAM 的拷贝)。因此,控制布局以快速访问单个张量非常重要。
  • 无文件大小限制:文件大小有限制吗?
  • 灵活性:是否可以以该格式保存自定义代码并能够在以后以零额外代码使用它吗? (意味着我们可以存储不仅仅是纯张量,还有代码)
  • 支持Bfloat16:该格式是否支持原生 bfloat16(意味着不需要奇怪的解决方法)?因为 Bfloat16 数据格式在机器学习领域变得越来越重要。

下图展示了常见的存储格式的特性。

image.png

safetensorsONNX 的不同

safetensorsONNX 具有不同的用途。

safetensors 是一种简单、安全、快速的文件格式,用于存储和加载张量。它是 Python 的 pickle 实用程序的替代品,更加安全;而后者不安全,可能包含可以执行的恶意代码。

ONNX(开放神经网络交换)是一种用于表示深度学习模型的开放格式。它允许您用不同深度学习框架(例如:PyTorch、TensorFlow、Caffe2 等)加载模型以一种方式保存模型。这使得在不同框架之间共享模型变得更容易。

综上所述, safetensors 用于安全快速地存储和加载张量,而 ONNX 用于在不同深度学习框架之间共享模型。这同样适用于其他模型共享框架。

补充:零拷贝技术

零拷贝的主要任务就是避免CPU将数据从一块存储中拷贝到另一块存储,主要就是利用各种技术,避免让CPU做大量的数据拷贝任务,以此减少不必要的拷贝。或者借助其他的一些组件来完成简单的数据传输任务,让CPU解脱出来专注别的任务,使得系统资源的利用更加有效。

下面通过从磁盘读取数据并将其发送到套接字(socket)的示例来了解零拷贝(这种情况在大多数 Web 应用程序中经常发生)。为了完成此操作,内核会将数据读取到用户空间。

操作系统术语:

用户空间和内核空间是由操作系统分隔的两个虚拟内存区域,以提供内存保护和硬件保护,防止恶意或错误的软件行为。

用户空间是应用程序软件和一些驱动程序执行的内存区域。每个用户空间进程通常运行在自己的虚拟内存空间中,除非明确允许,否则不能访问其他进程的内存或内核空间。用户空间进程只能通过系统调用与内核交互,系统调用是内核向用户空间公开的一组函数。

内核空间是操作系统内核、内核扩展和大多数设备驱动程序运行的内存区域。内核空间程序运行在内核模式下,也称为管理模式,这是一种特权模式,允许访问所有CPU指令和硬件资源。

一旦数据加载到用户空间,它将再次执行内核调用,内核会将数据写入套接字(socket)。每次数据穿越user-kernel boundary时,都必须进行复制,这会消耗 CPU 周期和内存带宽。

而零拷贝请求即内核直接将数据从磁盘文件复制到套接字,而不经过应用程序。

下图为传统的拷贝操作流程。除了拷贝操作之外,这些系统调用会导致用户空间和内核空间之间发生大量上下文切换,使得这个过程变慢。

image.png

下图为零拷贝数据传输流程:

image.png

image.png

零拷贝的特点是 CPU 不全程负责内存中的数据写入其他组件,CPU 仅仅起到管理的作用。但注意,零拷贝不是不进行拷贝,而是 CPU 不再全程负责数据拷贝时的搬运工作。如果数据本身不在内存中,那么必须先通过某种方式拷贝到内存中(这个过程 CPU 可以不参与),因为数据只有在内存中,才能被转移,才能被 CPU 直接读取计算。

Safetensors 优势

上面对比了不同模型权重存储格式,从中可以发现 Safetensors 主要优势有:

  • 安全:使用 torch.load() 加载模型权重可能会执行被插入的恶意代码(因为 pickle 模块是不安全的),不过可以设置weights_only=False 避免这个问题。而 Safetensors 天然就没有这个问题。

  • 速度快:Safetensors 接口的速度比原生的 Pytorch 接口加载权重更快。

    • CPU 上加载提速的原因:通过直接映射文件,避免了不必要的复制(零拷贝)
    • GPU 上加载提速的原因:跳过不必要的CPU分配。
  • 惰性加载:可以在不加载整个文件的情况下查看文件的信息或者只加载文件中的部分张量而不是所有张量。当我们有一个包含许多键和值对的大文件时,延迟加载非常重要。如果我们可以单独加载单个键的值,它将提高内存效率并且速度更快,否则我们将不得不将完整文件加载到内存中以检查任何键。

Safetensors 实践

安装

pip install safetensors

保存与加载张量

保存张量:

import torch
from safetensors.torch import save_file

tensors = {
    "embedding": torch.zeros((2, 2)),
    "attention": torch.zeros((2, 3))
}
save_file(tensors, "model.safetensors")

加载张量:

from safetensors import safe_open

tensors = {}
# with safe_open("model.safetensors", framework="pt", device=0) as f:
with safe_open("model.safetensors", framework="pt", device='cpu') as f:
    for k in f.keys():
        tensors[k] = f.get_tensor(k) # loads the full tensor given a key
print(tensors)
# {'attention': tensor([[0., 0., 0.],
#          [0., 0., 0.]], device='cuda:0'),
#  'embedding': tensor([[0., 0.],
#          [0., 0.]], device='cuda:0')}

仅加载部分张量(延迟加载):

tensors = {}
# with safe_open("model.safetensors", framework="pt", device=0) as f:
with safe_open("model.safetensors", framework="pt", device='cpu') as f:

    tensor_slice = f.get_slice("embedding")
    vocab_size, hidden_dim = tensor_slice.get_shape()
    print(vocab_size, hidden_dim)
    tensor = tensor_slice[:, :hidden_dim] # change the hidden_dim to load part of the tensor
    print(tensor)
# 2 2
# tensor([[0., 0.],
#         [0., 0.]])

将PyTorch模型保存为Safetensors格式

from torchvision.models import resnet18
from safetensors.torch import load_model, save_model
import torch

model_pt = resnet18(pretrained=True)

# 保存 state dict 为 safetensors格式
save_model(model_pt, "resnet18.safetensors")
# Instead of save_file(model.state_dict(), "model.safetensors")

# 加载没有权重的模型
model_st = resnet18(pretrained=False) 
load_model(model_st, "resnet18.safetensors")
# Instead of model.load_state_dict(load_file("model.safetensors"))

# 使用随机图像对初始模型和从safetensors中加载权重的新模型进行推理
img = torch.randn(2, 3, 224, 224)

model_pt.eval()
model_st.eval()

with torch.no_grad():
    print(torch.all(model_pt(img)==model_st(img))) # tensor(True)
    

结语

本文简要介绍了模型权重存储新格式 Safetensors,它具备安全、加载速度快等多个优点;并且可以在 HuggingFace 上面看到越来越多的模型使用Safetensors格式进行存储。

如何学习AI大模型?

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;

第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;

第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;

第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;

第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;

第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;

第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。

在这里插入图片描述

👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;

• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;

• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;

• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。

在这里插入图片描述

1.AI大模型学习路线图
2.100套AI大模型商业化落地方案
3.100集大模型视频教程
4.200本大模型PDF书籍
5.LLM面试题合集
6.AI产品经理资源合集

👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓

在这里插入图片描述

  • 17
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值