|
| 1 | +<!--Copyright 2024 The HuggingFace Team. All rights reserved. |
| 2 | +
|
| 3 | +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
| 4 | +the License. You may obtain a copy of the License at |
| 5 | +
|
| 6 | +http://www.apache.org/licenses/LICENSE-2.0 |
| 7 | +
|
| 8 | +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
| 9 | +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
| 10 | +specific language governing permissions and limitations under the License. |
| 11 | +--> |
| 12 | + |
| 13 | +# Overview |
| 14 | + |
| 15 | +The Modular Diffusers Framework consist of three main components |
| 16 | + |
| 17 | +## ModularPipelineBlocks |
| 18 | + |
| 19 | +Pipeline blocks are the fundamental building blocks of the Modular Diffusers system. All pipeline blocks inherit from the base class `ModularPipelineBlocks`, including: |
| 20 | +- [`PipelineBlock`](TODO) |
| 21 | +- [`SequentialPipelineBlocks`](TODO) |
| 22 | +- [`LoopSequentialPipelineBlocks`](TODO) |
| 23 | +- [`AutoPipelineBlocks`](TODO) |
| 24 | + |
| 25 | + |
| 26 | +Each block defines: |
| 27 | + |
| 28 | +**Specifications:** |
| 29 | +- Inputs: User-provided parameters that the block expects |
| 30 | +- Intermediate inputs: Variables from other blocks that this block needs |
| 31 | +- Intermediate outputs: Variables this block produces for other blocks to use |
| 32 | +- Components: Models and processors the block requires (e.g., UNet, VAE, scheduler) |
| 33 | + |
| 34 | +**Computation:** |
| 35 | +- `__call__` method: Defines the actual computational steps within the block |
| 36 | + |
| 37 | +Pipeline blocks are essentially **"definitions"** - they define the specifications and computational steps for a pipeline, but are not runnable until converted into a `ModularPipeline` object. |
| 38 | + |
| 39 | +All blocks interact with a global `PipelineState` object that maintains the pipeline's state throughout execution. |
| 40 | + |
| 41 | +### Load/save a custom `ModularPipelineBlocks` |
| 42 | + |
| 43 | +You can load a custom pipeline block from a hub repository directly |
| 44 | + |
| 45 | +```py |
| 46 | +from diffusers import ModularPipelineBlocks |
| 47 | +diffdiff_block = ModularPipelineBlocks.from_pretrained(repo_id, trust_remote_code=True) |
| 48 | +``` |
| 49 | + |
| 50 | +to save, and publish to a hub repository |
| 51 | + |
| 52 | +```py |
| 53 | +diffdiff_block.save(repo_id) |
| 54 | +``` |
| 55 | + |
| 56 | +## PipelineState & BlockState |
| 57 | + |
| 58 | +`PipelineState` and `BlockState` manage dataflow between pipeline blocks. `PipelineState` acts as the global state container that `ModularPipelineBlocks` operate on - each block gets a local view (`BlockState`) of the relevant variables it needs from `PipelineState`, performs its operations, and then updates PipelineState with any changes. |
| 59 | + |
| 60 | +<Tip> |
| 61 | + |
| 62 | +You typically don't need to manually create or manage these state objects. The `ModularPipeline` automatically creates and manages them for you. However, understanding their roles is important for developing custom pipeline blocks. |
| 63 | + |
| 64 | +</Tip> |
| 65 | + |
| 66 | +## ModularPipeline |
| 67 | + |
| 68 | +`ModularPipeline` is the main interface to create and execute pipelines in the Modular Diffusers system. |
| 69 | + |
| 70 | +### Create a `ModularPipeline` |
| 71 | + |
| 72 | +Each `ModularPipelineBlocks` has an `init_pipeline` method that can initialize a `ModularPipeline` object based on its component and configuration specifications. |
| 73 | + |
| 74 | +```py |
| 75 | +>>> pipeline = blocks.init_pipeline(pretrained_model_name_or_path) |
| 76 | +``` |
| 77 | + |
| 78 | +`ModularPipeline` only works with modular repositories, so make sure `pretrained_model_name_or_path` points to a modular repo (you can see an example [here](https://huggingface.co/YiYiXu/modular-diffdiff)). |
| 79 | + |
| 80 | +The main differences from standard diffusers repositories are: |
| 81 | + |
| 82 | +1. `modular_model_index.json` vs `model_index.json` |
| 83 | + |
| 84 | +In standard `model_index.json`, each component entry is a `(library, class)` tuple: |
| 85 | + |
| 86 | +```py |
| 87 | +"text_encoder": [ |
| 88 | + "transformers", |
| 89 | + "CLIPTextModel" |
| 90 | +], |
| 91 | +``` |
| 92 | + |
| 93 | +In `modular_model_index.json`, each component entry contains 3 elements: `(library, class, loading_specs {})` |
| 94 | + |
| 95 | +- `library` and `class`: Information about the actual component loaded in the pipeline at the time of saving (can be `None` if not loaded) |
| 96 | +- **`loading_specs`**: A dictionary containing all information required to load this component, including `repo`, `revision`, `subfolder`, `variant`, and `type_hint` |
| 97 | + |
| 98 | +```py |
| 99 | +"text_encoder": [ |
| 100 | + null, # library (same as model_index.json) |
| 101 | + null, # class (same as model_index.json) |
| 102 | + { # loading specs map (unique to modular_model_index.json) |
| 103 | + "repo": "stabilityai/stable-diffusion-xl-base-1.0", # can be a different repo |
| 104 | + "revision": null, |
| 105 | + "subfolder": "text_encoder", |
| 106 | + "type_hint": [ # (library, class) for the expected component class |
| 107 | + "transformers", |
| 108 | + "CLIPTextModel" |
| 109 | + ], |
| 110 | + "variant": null |
| 111 | + } |
| 112 | +], |
| 113 | +``` |
| 114 | + |
| 115 | +2. Cross-Repository Component Loading |
| 116 | + |
| 117 | +Unlike standard repositories where components must be in subfolders within the same repo, modular repositories can fetch components from different repositories based on the `loading_specs` dictionary. In our example above, the `text_encoder` component will be fetched from the "text_encoder" folder in `stabilityai/stable-diffusion-xl-base-1.0` while other components come from different repositories. |
| 118 | + |
| 119 | + |
| 120 | +<Tip> |
| 121 | + |
| 122 | +💡 We recommend using `ModularPipeline` with Component Manager by passing a `components_manager`: |
| 123 | + |
| 124 | +```py |
| 125 | +>>> components = ComponentsManager() |
| 126 | +>>> pipeline = blocks.init_pipeline(pretrained_model_name_or_path, components_manager=components) |
| 127 | +``` |
| 128 | + |
| 129 | +This helps you to: |
| 130 | +1. Detect and manage duplicated models (warns when trying to register an existing model) |
| 131 | +2. Easily reuse components across different pipelines |
| 132 | +3. Apply offloading strategies across multiple pipelines |
| 133 | + |
| 134 | +You can read more about Components Manager [here](TODO) |
| 135 | + |
| 136 | +</Tip> |
| 137 | + |
| 138 | + |
| 139 | +Unlike `DiffusionPipeline`, you need to explicitly load model components using `load_components`: |
| 140 | + |
| 141 | +```py |
| 142 | +>>> pipeline.load_components(torch_dtype=torch.float16) |
| 143 | +>>> pipeline.to(device) |
| 144 | +``` |
| 145 | + |
| 146 | +You can partially load specific components using the `component_names` argument, for example to only load unet and vae: |
| 147 | + |
| 148 | +```py |
| 149 | +>>> pipeline.load_components(component_names=["unet", "vae"]) |
| 150 | +``` |
| 151 | + |
| 152 | +<Tip> |
| 153 | + |
| 154 | +💡 You can inspect the pipeline's `config` attribute (which contains the same structure as `modular_model_index.json` we just walked through) to check the "loading status" of the pipeline, e.g. what components this pipeline expects to load and their loading specs, what components are already loaded and their actual class & loading specs etc. |
| 155 | + |
| 156 | +</Tip> |
| 157 | + |
| 158 | +### Execute a `ModularPipeline` |
| 159 | + |
| 160 | +The API to run the `ModularPipeline` is very similar to how you would run a regular `DiffusionPipeline`: |
| 161 | + |
| 162 | +```py |
| 163 | +>>> image = pipeline(prompt="a cat", num_inference_steps=15, output="images")[0] |
| 164 | +``` |
| 165 | + |
| 166 | +There are a few key differences though: |
| 167 | +1. You can also pass a `PipelineState` object directly to the pipeline instead of individual arguments |
| 168 | +2. If you do not specify the `output` argument, it returns the `PipelineState` object |
| 169 | +3. You can pass a list as `output`, e.g. `pipeline(... output=["images", "latents"])` will return a dictionary containing both the generated image and the final denoised latents |
| 170 | + |
| 171 | +Under the hood, `ModularPipeline`'s `__call__` method is a wrapper around the pipeline blocks' `__call__` method: it creates a `PipelineState` object and populates it with user inputs, then returns the output to the user based on the `output` argument. It also ensures that all pipeline-level config and components are exposed to all pipeline blocks by preparing and passing a `components` input. |
| 172 | + |
| 173 | +### Load a `ModularPipeline` from hub |
| 174 | + |
| 175 | +You can directly load a `ModularPipeline` from a HuggingFace Hub repository, as long as it's a modular repo |
| 176 | + |
| 177 | +```py |
| 178 | +pipeine = ModularPipeline.from_pretrained(repo_id, components_manager=..., collection=...) |
| 179 | +``` |
| 180 | + |
| 181 | +Loading custom code is also supported, just pass a `trust_remote_code=True` argument: |
| 182 | + |
| 183 | +```py |
| 184 | +diffdiff_pipeline = ModularPipeline.from_pretrained(repo_id, trust_remote_code=True, ...) |
| 185 | +``` |
| 186 | + |
| 187 | +The ModularPipeine created with `from_pretrained` method also would not load any components and you would have to call `load_components` to explicitly load components you need. |
| 188 | + |
| 189 | + |
| 190 | +### Save a `ModularPipeline` |
| 191 | + |
| 192 | +to save a `ModularPipeline` and publish it to hub |
| 193 | + |
| 194 | +```py |
| 195 | +pipeline.save_pretrained("YiYiXu/modular-loader-t2i", push_to_hub=True) |
| 196 | +``` |
| 197 | + |
| 198 | +<Tip> |
| 199 | + |
| 200 | +We do not automatically save custom code and share it on hub for you, please read more about how to share your custom pipeline on hub [here](TODO: ModularPipeline/CustomCode) |
| 201 | + |
| 202 | + |
| 203 | + |
| 204 | + |
| 205 | + |
| 206 | + |
0 commit comments