登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

Safetensors 只读取权重元数据的操作方法

来源:17golang原创

时间:2026-11-13 15:10:30 318浏览 收藏

Safetensors 的张量名称、dtype、shape 和 data_offsets 位于文件头的 JSON 中。先读取前 8 字节获得头部长度,再只读取这段头部,就能取得这些描述,而无需加载张量数据、初始化模型或分配 GPU 显存。另一个容易混淆的对象是 __metadata__:它是可选的文件自定义字符串元数据,不等于全部张量描述。

拿到一个很大的权重文件,第一步如果只是判断有哪些层、形状是否合适,就没必要先调用模型加载器。下面用 Python 标准库读取本地头部,示例没有在写作环境运行,展示的是方法,不是实际模型检测报告。

Safetensors 官方文档:https://huggingface.co/docs/safetensors/

先区分两种元数据

需要的信息所在位置能说明什么
自定义说明头部 __metadata__写入者保存的字符串键值,可缺失
张量结构头部中的张量名称条目dtype、shape 和字节范围
张量具体数值文件的数据区真正的权重内容,本例不读取
模型架构参数模型发布包的配置等材料不能仅靠头部完整推断

格式以一个小端无符号 64 位整数保存头部字节数,随后是相应长度的 UTF-8 JSON 头部,之后才是张量数据区。张量描述中的 data_offsets 以数据区开头为基准,不能直接当作整个文件的绝对偏移。

Safetensors 长度前缀、JSON头部和张量数据区的静态组成
结构说明图:文件头包含自定义元数据与张量描述,数据区保存权重字节;本例只读取前缀和头部。

只读文件头而不构造张量

把下面代码保存为自己的查看脚本,将文件名改成需要查看的本地 Safetensors 文件。不要把 Git LFS 指针文本、下载错误页或压缩包仅改后缀后传入。文件读取错误、UTF-8 或 JSON 解析错误会直接抛出异常,由调用方展示或记录。

import json
import os
import struct
from pathlib import Path

def read_header(path, max_header_bytes=16 * 1024 * 1024):
    # 本例自设 16 MiB 读取预算,不是格式规定的头部上限。
    with Path(path).open("rb", buffering=0) as stream:
        size = os.fstat(stream.fileno()).st_size
        prefix = stream.read(8)
        if len(prefix) != 8:
            raise ValueError("文件不足 8 字节,无法读取头部长度")
        header_size = struct.unpack(" max_header_bytes:
            raise ValueError("头部超出本程序读取预算")
        if header_size > size - 8:
            raise ValueError("文件截断或头部长度异常")

        # 只请求头部字节,不读取之后的张量数据区。
        raw_header = stream.read(header_size)
        if len(raw_header) != header_size:
            raise ValueError("头部读取不完整")
    header = json.loads(raw_header)
    if not isinstance(header, dict):
        raise ValueError("头部应为 JSON 对象")
    return header, 8 + header_size

# 示例路径换成自己的本地文件;不需要 torch 或 GPU。
header, data_start = read_header("model.safetensors")
custom_metadata = header.get("__metadata__", {})
print("custom_metadata:", custom_metadata)
print("data_start:", data_start)

for name, descriptor in header.items():
    if name == "__metadata__":
        continue
    # 这里只展示头部描述,不跟随 offsets 去读取张量内容。
    print(name, {
        "dtype": descriptor["dtype"],
        "shape": descriptor["shape"],
        "data_offsets": descriptor["data_offsets"],
    })

读取量主要由头部大小决定,与权重数据区是否有数十 GB 没有直接的线性关系;但头部本身也可能很大,JSON 解析和打印仍需要内存与时间。代码限制 16 MiB 是示例的资源预算,合法文件的头部也可能超过这个预算,应结合受控数据来源决定是否提高。

这个脚本只解释头部,不完整检查所有偏移、dtype 约束和数据区内容,也不代表文件已经通过完整格式检查。正式加载仍使用维护中的 Safetensors 库;不能把“JSON 读出来了”当成权重可以正确推理的证明。

按名称查看形状与数据类型

遍历时跳过 __metadata__,其余条目按张量描述读取。shape 是维度数组,dtype 是格式使用的数据类型代码,data_offsets 是数据区中的起止字节位置,终点不包含在范围内。脚本只打印这些数字,不会按偏移读取权重。

想盘点参数规模时,可以对每个 shape 的维度求乘积,再按需要汇总。标量的 shape 可以为空数组,其元素数为 1;包含零维长度的张量元素数为 0。不要简单用维度数组长度当作参数数量,也不要在存在共享、量化或特定表示时直接把“记录的元素个数”解释成原始模型的唯一逻辑参数数。

若只找一个前缀,例如 encoder.,在打印前按名称过滤即可;这只是减少输出,不会让 JSON 头部变成按层随机读取的索引。自定义 metadata 中写着架构名称或精度时,它属于写入者声明,实际张量仍应看对应 dtype 与 shape。

自定义元数据与张量名称dtype shape偏移字段的关系
结构说明图:张量名称对应其描述字段,自定义元数据独立存在;字段汇总可以帮助盘点,但不构造真实张量。

分片模型需要逐文件汇总

模型可能发布为多个 .safetensors 分片,而不是单个完整文件。此时一个头部只反映当前分片。若发布包附带 model.safetensors.index.json,可根据其中的 weight_map 理解张量与分片文件的映射,再逐个读取相应头部。

不要把索引中的总大小直接解释为当前分片大小,也不要只查看第一个分片就判断模型缺层。汇总时保留“文件名、张量名、dtype、shape”,遇到重复名称先保留来源并调查,不要让普通字典覆盖前一份记录。配置文件、分词器和模型类的依赖关系仍由对应模型框架决定。

远程权重也可以通过 HTTP Range 获取前缀和头部,官方有专门示例。但必须处理服务端是否真的返回范围内容、鉴权与文件版本一致性,不能假定每次请求都只下载少量字节。只需检查本地已下载文件时,上面的文件读取方案更直接。

读取失败与信息缺失怎么处理

__metadata__ 是空的,是否说明没有张量?

不是。它是可选自定义信息,张量描述在其他顶层条目中。即使没有模型说明,仍可能有完整的张量名称和形状。

用 load_file 并指定 CPU,是否就是只读元数据?

不是同一操作。Torch API 的 load_file 返回张量字典,CPU 参数改变目标设备,不把它变成头部查看接口;不能以“没有显存增长”证明没有访问权重数据。

读取头部后能知道权重是否损坏吗?

只能发现本脚本覆盖的头部读取或解析问题。数据区具体数值、完整格式约束以及模型兼容性不在这个轻量查看方法的结论范围内。

能仅凭 dtype 判断模型实际推理精度吗?

不能。文件存储类型与加载时转换、量化实现和计算精度可能不同,头部只能说明文件中记录的张量表示。

元数据读取说明:https://huggingface.co/docs/safetensors/metadata_parsing。文件格式定义:https://github.com/safetensors/safetensors。Torch 加载接口:https://huggingface.co/docs/safetensors/api/torch。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>