-
Notifications
You must be signed in to change notification settings - Fork 724
DDUF parser v0.1 #2692
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DDUF parser v0.1 #2692
Changes from 19 commits
3b533a6
953bbae
d30558b
0f21bd3
b4bf030
1b11f0b
f349cbe
bf7dc84
16c3e15
ba1e6a4
0546ca1
706597e
a6588aa
947a593
2881a57
2b7baf5
02a9532
c7ce20a
f5f0f25
0d1045d
dca1586
f6bee85
157633c
5cf560d
e6c62da
4078626
381ac7e
76265b4
d796252
5c2bb63
360ddd1
c168e23
4553f4c
6ad29e7
ce2b858
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,116 @@ rendered properly in your Markdown viewer. | |
|
||
`huggingface_hub` contains helpers to help ML libraries serialize models weights in a standardized way. This part of the lib is still under development and will be improved in future releases. The goal is to harmonize how weights are serialized on the Hub, both to remove code duplication across libraries and to foster conventions on the Hub. | ||
|
||
## DDUF file format | ||
|
||
DDUF is a file format designed for diffusers models. It allows saving all the information to run a model in a single file. This work is inspired by the GGUF format. `huggingface_hub` provides helpers to save and load DDUF files, ensuring the file format is respected. | ||
|
||
<Tip warning={true}> | ||
|
||
This is a very early version of the parser. The API and implementation can evolve in the near future. | ||
|
||
The parser currently does very little validation. For more details about the file format, check out https://github.com/huggingface/huggingface.js/tree/main/packages/dduf. | ||
|
||
</Tip> | ||
|
||
### How to write a DDUF file? | ||
|
||
Here is how to export a folder containing different parts of a diffusion model using [`export_folder_as_dduf`]: | ||
|
||
```python | ||
# 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") | ||
``` | ||
|
||
For more flexibility, to can use [`export_entries_as_dduf`] and pass a list of files to include in the final DDUF file: | ||
Wauplin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```python | ||
# Export specific files from the local disk. | ||
>>> from huggingface_hub import export_entries_as_dduf | ||
>>> export_entries_as_dduf( | ||
... "stable-diffusion-v1-4-FP16.dduf", | ||
Wauplin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
... 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 | ||
... ] | ||
... ) | ||
``` | ||
|
||
The `entries` parameter also support passing an iterable of paths or bytes. This can prove useful if you have a loaded model and want to serialize it directly in a DDUF file instead of having to serialize each component to disk first and then as a DDUF file. Here is an example on how a `StableDiffusionPipeline` can be serialized as DDUF: | ||
Wauplin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
```python | ||
# Export state_dicts one by one from a loaded pipeline | ||
>>> from diffusers import DiffusionPipeline | ||
>>> from typing import Generator, Tuple | ||
>>> import safetensors.torch | ||
>>> from huggingface_hub import export_entries_as_dduf | ||
>>> pipe = DiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4") | ||
... # ... do some work with the pipeline | ||
|
||
>>> def as_entries(pipe: DiffusionPipeline) -> Generator[Tuple[str, bytes], None, None]: | ||
... # Build an generator that yields the entries to add to the DDUF file. | ||
Wauplin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
... # The first element of the tuple is the filename in the DDUF archive (must use UNIX separator!). 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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was confused that this is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be up for another solution tbh. I just wanted to show that you need to serialize things as bytes by yourself. |
||
... 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("stable-diffusion-v1-4.dduf", entries=as_entries(pipe)) | ||
``` | ||
|
||
**Note:** in practice, `diffusers` provides a method to directly serialize a pipeline in a DDUF file. The snippet above is only meant as an example. | ||
Wauplin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### How to read a DDUF file? | ||
|
||
```python | ||
>>> import json | ||
>>> import safetensors.load | ||
Wauplin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
>>> from huggingface_hub import read_dduf_file | ||
|
||
# Read DDUF metadata | ||
>>> dduf_entries = read_dduf_file("FLUX.1-dev.dduf") | ||
sayakpaul marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Returns a mapping filename <> DDUFEntry | ||
>>> dduf_entries["model_index.json"] | ||
DDUFEntry(filename='model_index.json', offset=66, length=587) | ||
sayakpaul marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Load model index as 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', 'scheduler': ['diffusers', 'FlowMatchEulerDiscreteScheduler'], 'text_encoder': ['transformers', 'CLIPTextModel'], 'text_encoder_2': ['transformers', 'T5EncoderModel'], 'tokenizer': ['transformers', 'CLIPTokenizer'], 'tokenizer_2': ['transformers', 'T5TokenizerFast'], 'transformer': ['diffusers', 'FluxTransformer2DModel'], 'vae': ['diffusers', 'AutoencoderKL']} | ||
|
||
# Load VAE weights using safetensors | ||
>>> with dduf_entries["vae/diffusion_pytorch_model.safetensors"].as_mmap() as mm: | ||
Wauplin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
... state_dict = safetensors.torch.load(mm) | ||
``` | ||
|
||
### Helpers | ||
|
||
[[autodoc]] huggingface_hub.export_entries_as_dduf | ||
|
||
[[autodoc]] huggingface_hub.export_folder_as_dduf | ||
|
||
[[autodoc]] huggingface_hub.read_dduf_file | ||
|
||
[[autodoc]] huggingface_hub.DDUFEntry | ||
|
||
### Errors | ||
|
||
[[autodoc]] huggingface_hub.errors.DDUFError | ||
|
||
[[autodoc]] huggingface_hub.errors.DDUFCorruptedFileError | ||
|
||
[[autodoc]] huggingface_hub.errors.DDUFExportError | ||
|
||
[[autodoc]] huggingface_hub.errors.DDUFInvalidEntryNameError | ||
|
||
## Save torch state dict | ||
|
||
The main helper of the `serialization` module takes a torch `nn.Module` as input and saves it to disk. It handles the logic to save shared tensors (see [safetensors explanation](https://huggingface.co/docs/safetensors/torch_shared_tensors)) as well as logic to split the state dictionary into shards, using [`split_torch_state_dict_into_shards`] under the hood. At the moment, only `torch` framework is supported. | ||
|
Uh oh!
There was an error while loading. Please reload this page.