Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

DDUF

概述

DDUF (DDUF’s Diffusion Unified Format) 是一种用于扩散模型的单文件格式,旨在通过将所有模型组件打包到单个文件中来统一不同的模型分发方法和权重保存格式。它是语言无关的,并且构建为可以从远程位置解析而无需下载整个文件。

这项工作从 GGUF 格式中汲取了灵感。

查看 DDUF 组织,开始使用 DDUF 中一些最流行的扩散模型。

我们张开双臂欢迎贡献!

为了创建一个被广泛采用的文件格式,我们需要来自社区的早期反馈。没有什么是一成不变的,我们重视每个人的意见。您的用例未涵盖?请在 DDUF 组织的讨论中告知我们。

其主要功能包括以下几点。

  1. 单文件打包。
  2. 基于 ZIP 文件格式,以利用现有的工具。
  3. 无压缩,确保 mmap 兼容性,实现快速加载和保存。
  4. 语言无关:工具可以使用 Python、JavaScript、Rust、C++ 等语言实现。
  5. HTTP 友好:元数据和文件结构可以使用 HTTP Range 请求远程获取。
  6. 灵活:每个模型组件都存储在自己的目录中,遵循当前的 Diffusers 结构。
  7. 安全:使用 Safetensors 作为权重保存格式,并禁止嵌套目录以防止 ZIP 炸弹。

技术规格

从技术上讲,.dduf 文件就是一个 .zip 压缩包。通过构建在通用支持的文件格式之上,我们确保已经存在强大的工具。但是,为了满足扩散模型的要求,强制执行了一些约束

  • 数据必须以未压缩方式存储(标志 0),允许使用内存映射进行延迟加载。
  • 数据必须使用 ZIP64 协议存储,从而可以保存超过 4GB 的文件。
  • 压缩包只能包含 .json.safetensors.model.txt 文件。
  • model_index.json 文件必须存在于压缩包的根目录中。它必须包含一个键值映射,其中包含有关模型及其组件的元数据。
  • 每个组件必须存储在自己的目录中(例如,vae/text_encoder/)。嵌套文件必须使用 UNIX 风格的路径分隔符 (/)。
  • 每个目录必须对应于 model_index.json 索引中的一个组件。
  • 每个目录必须包含一个 json 配置文件(config.jsontokenizer_config.jsonpreprocessor_config.jsonscheduler_config.json 中的一个)。
  • 禁止子目录。

想要检查您的文件是否有效?使用此 Space 进行检查:https://huggingface.co/spaces/DDUF/dduf-check

用法

huggingface_hub 提供了在 Python 中处理 DDUF 文件的工具。它包括用于验证文件完整性的内置规则和用于读取和导出 DDUF 文件的助手。目标是看到此工具在 Python 生态系统中被采用,例如在 diffusers 集成中。可以为其他语言(JavaScript、Rust、C++ 等)开发类似的工具。

如何读取 DDUF 文件?

传递 read_dduf_file 的路径以读取 DDUF 文件。仅读取元数据,这意味着这是一个轻量级调用,不会占用大量内存。在下面的示例中,我们假设您已在本地下载了 FLUX.1-dev.dduf 文件。

>>> from huggingface_hub import read_dduf_file

# Read DDUF metadata
>>> dduf_entries = read_dduf_file("FLUX.1-dev.dduf")

read_dduf_file 返回一个映射,其中每个条目对应于 DDUF 压缩包中的一个文件。文件由 DDUFEntry 数据类表示,该数据类包含原始 DDUF 文件中条目的文件名、偏移量和长度。此信息对于读取其内容而无需加载整个文件非常有用。在实践中,您不必处理底层读取,而是依赖助手。

例如,以下是如何加载 model_index.json 内容

>>> import json
>>> json.loads(dduf_entries["model_index.json"].read_text())
{'_class_name': 'FluxPipeline', '_diffusers_version': '0.32.0.dev0', '_name_or_path': 'black-forest-labs/FLUX.1-dev', ...

对于二进制文件,您需要使用 as_mmap 访问原始字节。这将字节作为原始文件上的内存映射返回。内存映射允许您仅读取所需的字节,而无需将所有内容加载到内存中。例如,以下是如何加载 safetensors 权重

>>> import safetensors.torch
>>> with dduf_entries["vae/diffusion_pytorch_model.safetensors"].as_mmap() as mm:
...     state_dict = safetensors.torch.load(mm) # `mm` is a bytes object

as_mmap 必须在上下文管理器中使用,才能受益于内存映射的属性。

如何写入 DDUF 文件?

传递文件夹路径到 export_folder_as_dduf 以导出 DDUF 文件。

# Export a folder as a DDUF file
>>> from huggingface_hub import export_folder_as_dduf
>>> export_folder_as_dduf("FLUX.1-dev.dduf", folder_path="path/to/FLUX.1-dev")

此工具扫描文件夹,添加相关条目,并确保导出的文件有效。如果在该过程中出现任何问题,则会引发 DDUFExportError

为了获得更大的灵活性,请使用 [export_entries_as_dduf] 显式指定要包含在最终 DDUF 文件中的文件列表

# Export specific files from the local disk.
>>> from huggingface_hub import export_entries_as_dduf
>>> export_entries_as_dduf(
...     dduf_path="stable-diffusion-v1-4-FP16.dduf",
...     entries=[ # List entries to add to the DDUF file (here, only FP16 weights)
...         ("model_index.json", "path/to/model_index.json"),
...         ("vae/config.json", "path/to/vae/config.json"),
...         ("vae/diffusion_pytorch_model.fp16.safetensors", "path/to/vae/diffusion_pytorch_model.fp16.safetensors"),
...         ("text_encoder/config.json", "path/to/text_encoder/config.json"),
...         ("text_encoder/model.fp16.safetensors", "path/to/text_encoder/model.fp16.safetensors"),
...         # ... add more entries here
...     ]
... )

如果您已将模型保存在磁盘上,则 export_entries_as_dduf 效果很好。但是,如果您在内存中加载了模型并想将其直接序列化到 DDUF 文件中怎么办?export_entries_as_dduf 允许您通过提供一个 Python generator 来迭代地告诉如何序列化数据来实现这一点

(...)

# Export state_dicts one by one from a loaded pipeline
>>> def as_entries(pipe: DiffusionPipeline) -> Generator[Tuple[str, bytes], None, None]:
...     # Build a generator that yields the entries to add to the DDUF file.
...     # The first element of the tuple is the filename in the DDUF archive. The second element is the content of the file.
...     # Entries will be evaluated lazily when the DDUF file is created (only 1 entry is loaded in memory at a time)
...     yield "vae/config.json", pipe.vae.to_json_string().encode()
...     yield "vae/diffusion_pytorch_model.safetensors", safetensors.torch.save(pipe.vae.state_dict())
...     yield "text_encoder/config.json", pipe.text_encoder.config.to_json_string().encode()
...     yield "text_encoder/model.safetensors", safetensors.torch.save(pipe.text_encoder.state_dict())
...     # ... add more entries here

>>> export_entries_as_dduf(dduf_path="my-cool-diffusion-model.dduf", entries=as_entries(pipe))

使用 Diffusers 加载 DDUF 文件

Diffusers 具有 DDUF 文件的内置集成。以下是如何从 Hub 上存储的检查点加载管道的示例

from diffusers import DiffusionPipeline
import torch

pipe = DiffusionPipeline.from_pretrained(
    "DDUF/FLUX.1-dev-DDUF", dduf_file="FLUX.1-dev.dduf", torch_dtype=torch.bfloat16
).to("cuda")
image = pipe(
    "photo a cat holding a sign that says Diffusers", num_inference_steps=50, guidance_scale=3.5
).images[0]
image.save("cat.png")

常见问题解答

为什么在 ZIP 之上构建?

ZIP 提供了几个优势

  • 通用支持的文件格式
  • 读取无需额外的依赖项
  • 内置文件索引
  • 广泛的语言支持

为什么不使用在压缩包开头带有目录表的 TAR?

请参阅此评论中的解释。

为什么不压缩?

  • 启用大型文件的直接内存映射
  • 确保一致且可预测的远程文件访问
  • 防止文件读取期间的 CPU 开销
  • 保持与 safetensors 的兼容性

我可以修改 DDUF 文件吗?

不可以。目前,DDUF 文件被设计为不可变的。要更新模型,请创建一个新的 DDUF 文件。

哪些框架/应用支持 DDUF?

我们不断与其他库和框架联系。如果您有兴趣为您的项目添加支持,请在 DDUF 组织中打开一个讨论。

< > 在 GitHub 上更新