modular_pipeline.md 13.2 KB
Newer Older
YiYi Xu's avatar
YiYi Xu committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--Copyright 2025 The HuggingFace Team. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
-->

# ModularPipeline

Steven Liu's avatar
Steven Liu committed
15
[`ModularPipeline`] converts [`~modular_pipelines.ModularPipelineBlocks`]'s into an executable pipeline that loads models and performs the computation steps defined in the block. It is the main interface for running a pipeline and it is very similar to the [`DiffusionPipeline`] API.
YiYi Xu's avatar
YiYi Xu committed
16

Steven Liu's avatar
Steven Liu committed
17
The main difference is to include an expected `output` argument in the pipeline.
YiYi Xu's avatar
YiYi Xu committed
18

Steven Liu's avatar
Steven Liu committed
19
20
<hfoptions id="example">
<hfoption id="text-to-image">
YiYi Xu's avatar
YiYi Xu committed
21

Steven Liu's avatar
Steven Liu committed
22
23
24
25
```py
import torch
from diffusers.modular_pipelines import SequentialPipelineBlocks
from diffusers.modular_pipelines.stable_diffusion_xl import TEXT2IMAGE_BLOCKS
YiYi Xu's avatar
YiYi Xu committed
26

Steven Liu's avatar
Steven Liu committed
27
blocks = SequentialPipelineBlocks.from_blocks_dict(TEXT2IMAGE_BLOCKS)
YiYi Xu's avatar
YiYi Xu committed
28

Steven Liu's avatar
Steven Liu committed
29
30
modular_repo_id = "YiYiXu/modular-loader-t2i-0704"
pipeline = blocks.init_pipeline(modular_repo_id)
YiYi Xu's avatar
YiYi Xu committed
31

32
pipeline.load_components(torch_dtype=torch.float16)
Steven Liu's avatar
Steven Liu committed
33
pipeline.to("cuda")
YiYi Xu's avatar
YiYi Xu committed
34

Steven Liu's avatar
Steven Liu committed
35
36
37
image = pipeline(prompt="Astronaut in a jungle, cold color palette, muted colors, detailed, 8k", output="images")[0]
image.save("modular_t2i_out.png")
```
YiYi Xu's avatar
YiYi Xu committed
38

Steven Liu's avatar
Steven Liu committed
39
40
</hfoption>
<hfoption id="image-to-image">
YiYi Xu's avatar
YiYi Xu committed
41

Steven Liu's avatar
Steven Liu committed
42
43
44
45
```py
import torch
from diffusers.modular_pipelines import SequentialPipelineBlocks
from diffusers.modular_pipelines.stable_diffusion_xl import IMAGE2IMAGE_BLOCKS
YiYi Xu's avatar
YiYi Xu committed
46

Steven Liu's avatar
Steven Liu committed
47
blocks = SequentialPipelineBlocks.from_blocks_dict(IMAGE2IMAGE_BLOCKS)
YiYi Xu's avatar
YiYi Xu committed
48

Steven Liu's avatar
Steven Liu committed
49
50
modular_repo_id = "YiYiXu/modular-loader-t2i-0704"
pipeline = blocks.init_pipeline(modular_repo_id)
YiYi Xu's avatar
YiYi Xu committed
51

52
pipeline.load_components(torch_dtype=torch.float16)
Steven Liu's avatar
Steven Liu committed
53
pipeline.to("cuda")
YiYi Xu's avatar
YiYi Xu committed
54

Steven Liu's avatar
Steven Liu committed
55
56
57
58
59
url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/sdxl-text2img.png"
init_image = load_image(url)
prompt = "a dog catching a frisbee in the jungle"
image = pipeline(prompt=prompt, image=init_image, strength=0.8, output="images")[0]
image.save("modular_i2i_out.png")
YiYi Xu's avatar
YiYi Xu committed
60
61
```

Steven Liu's avatar
Steven Liu committed
62
63
</hfoption>
<hfoption id="inpainting">
YiYi Xu's avatar
YiYi Xu committed
64
65

```py
Steven Liu's avatar
Steven Liu committed
66
67
68
69
import torch
from diffusers.modular_pipelines import SequentialPipelineBlocks
from diffusers.modular_pipelines.stable_diffusion_xl import INPAINT_BLOCKS
from diffusers.utils import load_image
YiYi Xu's avatar
YiYi Xu committed
70

Steven Liu's avatar
Steven Liu committed
71
blocks = SequentialPipelineBlocks.from_blocks_dict(INPAINT_BLOCKS)
YiYi Xu's avatar
YiYi Xu committed
72

Steven Liu's avatar
Steven Liu committed
73
74
modular_repo_id = "YiYiXu/modular-loader-t2i-0704"
pipeline = blocks.init_pipeline(modular_repo_id)
YiYi Xu's avatar
YiYi Xu committed
75

76
pipeline.load_components(torch_dtype=torch.float16)
Steven Liu's avatar
Steven Liu committed
77
pipeline.to("cuda")
YiYi Xu's avatar
YiYi Xu committed
78

Steven Liu's avatar
Steven Liu committed
79
80
img_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/sdxl-text2img.png"
mask_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/sdxl-inpaint-mask.png"
YiYi Xu's avatar
YiYi Xu committed
81

Steven Liu's avatar
Steven Liu committed
82
83
84
85
86
87
init_image = load_image(img_url)
mask_image = load_image(mask_url)

prompt = "A deep sea diver floating"
image = pipeline(prompt=prompt, image=init_image, mask_image=mask_image, strength=0.85, output="images")[0]
image.save("moduar_inpaint_out.png")
YiYi Xu's avatar
YiYi Xu committed
88
89
```

Steven Liu's avatar
Steven Liu committed
90
91
</hfoption>
</hfoptions>
YiYi Xu's avatar
YiYi Xu committed
92

Steven Liu's avatar
Steven Liu committed
93
This guide will show you how to create a [`ModularPipeline`] and manage the components in it.
YiYi Xu's avatar
YiYi Xu committed
94

Steven Liu's avatar
Steven Liu committed
95
## Adding blocks
YiYi Xu's avatar
YiYi Xu committed
96

Steven Liu's avatar
Steven Liu committed
97
Blocks are [`InsertableDict`] objects that can be inserted at specific positions, providing a flexible way to mix-and-match blocks.
YiYi Xu's avatar
YiYi Xu committed
98

Steven Liu's avatar
Steven Liu committed
99
Use [`~modular_pipelines.modular_pipeline_utils.InsertableDict.insert`] on either the block class or `sub_blocks` attribute to add a block.
YiYi Xu's avatar
YiYi Xu committed
100
101
102
103
104
105
106
107

```py
# BLOCKS is dict of block classes, you need to add class to it
BLOCKS.insert("block_name", BlockClass, index)
# sub_blocks attribute contains instance, add a block instance to the  attribute
t2i_blocks.sub_blocks.insert("block_name", block_instance, index)
```

Steven Liu's avatar
Steven Liu committed
108
109
Use [`~modular_pipelines.modular_pipeline_utils.InsertableDict.pop`] on either the block class or `sub_blocks` attribute to remove a block.

YiYi Xu's avatar
YiYi Xu committed
110
111
112
113
114
115
116
```py
# remove a block class from preset
BLOCKS.pop("text_encoder")
# split out a block instance on its own
text_encoder_block = t2i_blocks.sub_blocks.pop("text_encoder")
```

Steven Liu's avatar
Steven Liu committed
117
118
Swap blocks by setting the existing block to the new block.

YiYi Xu's avatar
YiYi Xu committed
119
120
121
122
123
124
125
```py
# Replace block class in preset
BLOCKS["prepare_latents"] = CustomPrepareLatents
# Replace in sub_blocks attribute using an block instance
t2i_blocks.sub_blocks["prepare_latents"] = CustomPrepareLatents()
```

Steven Liu's avatar
Steven Liu committed
126
## Creating a pipeline
YiYi Xu's avatar
YiYi Xu committed
127

Steven Liu's avatar
Steven Liu committed
128
There are two ways to create a [`ModularPipeline`]. Assemble and create a pipeline from [`ModularPipelineBlocks`] or load an existing pipeline with [`~ModularPipeline.from_pretrained`].
YiYi Xu's avatar
YiYi Xu committed
129

Steven Liu's avatar
Steven Liu committed
130
You should also initialize a [`ComponentsManager`] to handle device placement and memory and component management.
YiYi Xu's avatar
YiYi Xu committed
131

Steven Liu's avatar
Steven Liu committed
132
133
> [!TIP]
> Refer to the [ComponentsManager](./components_manager) doc for more details about how it can help manage components across different workflows.
YiYi Xu's avatar
YiYi Xu committed
134

Steven Liu's avatar
Steven Liu committed
135
136
<hfoptions id="create">
<hfoption id="ModularPipelineBlocks">
YiYi Xu's avatar
YiYi Xu committed
137

Steven Liu's avatar
Steven Liu committed
138
Use the [`~ModularPipelineBlocks.init_pipeline`] method to create a [`ModularPipeline`] from the component and configuration specifications. This method loads the *specifications* from a `modular_model_index.json` file, but it doesn't load the *models* yet.
YiYi Xu's avatar
YiYi Xu committed
139
140

```py
Steven Liu's avatar
Steven Liu committed
141
142
143
from diffusers import ComponentsManager
from diffusers.modular_pipelines import SequentialPipelineBlocks
from diffusers.modular_pipelines.stable_diffusion_xl import TEXT2IMAGE_BLOCKS
YiYi Xu's avatar
YiYi Xu committed
144
145
146
147
148
149
150
151

t2i_blocks = SequentialPipelineBlocks.from_blocks_dict(TEXT2IMAGE_BLOCKS)

modular_repo_id = "YiYiXu/modular-loader-t2i-0704"
components = ComponentsManager()
t2i_pipeline = t2i_blocks.init_pipeline(modular_repo_id, components_manager=components)
```

Steven Liu's avatar
Steven Liu committed
152
153
</hfoption>
<hfoption id="from_pretrained">
YiYi Xu's avatar
YiYi Xu committed
154

Steven Liu's avatar
Steven Liu committed
155
The [`~ModularPipeline.from_pretrained`] method creates a [`ModularPipeline`] from a modular repository on the Hub.
YiYi Xu's avatar
YiYi Xu committed
156
157
158

```py
from diffusers import ModularPipeline, ComponentsManager
Steven Liu's avatar
Steven Liu committed
159

YiYi Xu's avatar
YiYi Xu committed
160
161
162
163
components = ComponentsManager()
pipeline = ModularPipeline.from_pretrained("YiYiXu/modular-loader-t2i-0704", components_manager=components)
```

Steven Liu's avatar
Steven Liu committed
164
Add the `trust_remote_code` argument to load a custom [`ModularPipeline`].
YiYi Xu's avatar
YiYi Xu committed
165
166
167

```py
from diffusers import ModularPipeline, ComponentsManager
Steven Liu's avatar
Steven Liu committed
168

YiYi Xu's avatar
YiYi Xu committed
169
170
171
172
173
components = ComponentsManager()
modular_repo_id = "YiYiXu/modular-diffdiff-0704"
diffdiff_pipeline = ModularPipeline.from_pretrained(modular_repo_id, trust_remote_code=True, components_manager=components)
```

Steven Liu's avatar
Steven Liu committed
174
175
</hfoption>
</hfoptions>
YiYi Xu's avatar
YiYi Xu committed
176

Steven Liu's avatar
Steven Liu committed
177
## Loading components
YiYi Xu's avatar
YiYi Xu committed
178

179
A [`ModularPipeline`] doesn't automatically instantiate with components. It only loads the configuration and component specifications. You can load all components with [`~ModularPipeline.load_components`] or only load specific components with [`~ModularPipeline.load_components`].
YiYi Xu's avatar
YiYi Xu committed
180

Steven Liu's avatar
Steven Liu committed
181
<hfoptions id="load">
182
<hfoption id="load_components">
YiYi Xu's avatar
YiYi Xu committed
183
184
185

```py
import torch
Steven Liu's avatar
Steven Liu committed
186

187
t2i_pipeline.load_components(torch_dtype=torch.float16)
YiYi Xu's avatar
YiYi Xu committed
188
189
190
t2i_pipeline.to("cuda")
```

Steven Liu's avatar
Steven Liu committed
191
192
</hfoption>
<hfoption id="load_components">
YiYi Xu's avatar
YiYi Xu committed
193

Steven Liu's avatar
Steven Liu committed
194
The example below only loads the UNet and VAE.
YiYi Xu's avatar
YiYi Xu committed
195
196

```py
Steven Liu's avatar
Steven Liu committed
197
import torch
YiYi Xu's avatar
YiYi Xu committed
198

Steven Liu's avatar
Steven Liu committed
199
200
t2i_pipeline.load_components(names=["unet", "vae"], torch_dtype=torch.float16)
```
YiYi Xu's avatar
YiYi Xu committed
201

Steven Liu's avatar
Steven Liu committed
202
203
</hfoption>
</hfoptions>
YiYi Xu's avatar
YiYi Xu committed
204

Steven Liu's avatar
Steven Liu committed
205
Print the pipeline to inspect the loaded pretrained components.
YiYi Xu's avatar
YiYi Xu committed
206
207

```py
Steven Liu's avatar
Steven Liu committed
208
t2i_pipeline
YiYi Xu's avatar
YiYi Xu committed
209
210
```

Steven Liu's avatar
Steven Liu committed
211
This should match the `modular_model_index.json` file from the modular repository a pipeline is initialized from. If a pipeline doesn't need a component, it won't be included even if it exists in the modular repository.
YiYi Xu's avatar
YiYi Xu committed
212

Steven Liu's avatar
Steven Liu committed
213
To modify where components are loaded from, edit the `modular_model_index.json` file in the repository and change it to your desired loading path. The example below loads a UNet from a different repository.
YiYi Xu's avatar
YiYi Xu committed
214

Steven Liu's avatar
Steven Liu committed
215
216
```json
# original
YiYi Xu's avatar
YiYi Xu committed
217
218
219
220
221
222
223
224
225
"unet": [
  null, null,
  {
    "repo": "stabilityai/stable-diffusion-xl-base-1.0",
    "subfolder": "unet",
    "variant": "fp16"
  }
]

Steven Liu's avatar
Steven Liu committed
226
# modified
YiYi Xu's avatar
YiYi Xu committed
227
228
229
230
"unet": [
  null, null,
  {
    "repo": "RunDiffusion/Juggernaut-XL-v9",
Steven Liu's avatar
Steven Liu committed
231
    "subfolder": "unet",
YiYi Xu's avatar
YiYi Xu committed
232
233
234
235
236
    "variant": "fp16"
  }
]
```

Steven Liu's avatar
Steven Liu committed
237
### Component loading status
YiYi Xu's avatar
YiYi Xu committed
238

Steven Liu's avatar
Steven Liu committed
239
The pipeline properties below provide more information about which components are loaded.
YiYi Xu's avatar
YiYi Xu committed
240

Steven Liu's avatar
Steven Liu committed
241
Use `component_names` to return all expected components.
YiYi Xu's avatar
YiYi Xu committed
242
243

```py
Steven Liu's avatar
Steven Liu committed
244
245
t2i_pipeline.component_names
['text_encoder', 'text_encoder_2', 'tokenizer', 'tokenizer_2', 'guider', 'scheduler', 'unet', 'vae', 'image_processor']
YiYi Xu's avatar
YiYi Xu committed
246
247
```

Steven Liu's avatar
Steven Liu committed
248
Use `null_component_names` to return components that aren't loaded yet. Load these components with [`~ModularPipeline.from_pretrained`].
YiYi Xu's avatar
YiYi Xu committed
249
250

```py
Steven Liu's avatar
Steven Liu committed
251
252
t2i_pipeline.null_component_names
['text_encoder', 'text_encoder_2', 'tokenizer', 'tokenizer_2', 'scheduler']
YiYi Xu's avatar
YiYi Xu committed
253
254
```

Steven Liu's avatar
Steven Liu committed
255
Use `pretrained_component_names` to return components that will be loaded from pretrained models.
YiYi Xu's avatar
YiYi Xu committed
256
257

```py
Steven Liu's avatar
Steven Liu committed
258
259
t2i_pipeline.pretrained_component_names
['text_encoder', 'text_encoder_2', 'tokenizer', 'tokenizer_2', 'scheduler', 'unet', 'vae']
YiYi Xu's avatar
YiYi Xu committed
260
261
```

Steven Liu's avatar
Steven Liu committed
262
Use `config_component_names` to return components that are created with the default config (not loaded from a modular repository). Components from a config aren't included because they are already initialized during pipeline creation. This is why they aren't listed in `null_component_names`.
YiYi Xu's avatar
YiYi Xu committed
263
264

```py
Steven Liu's avatar
Steven Liu committed
265
266
t2i_pipeline.config_component_names
['guider', 'image_processor']
YiYi Xu's avatar
YiYi Xu committed
267
268
```

Steven Liu's avatar
Steven Liu committed
269
## Updating components
YiYi Xu's avatar
YiYi Xu committed
270

Steven Liu's avatar
Steven Liu committed
271
Components may be updated depending on whether it is a *pretrained component* or a *config component*.
YiYi Xu's avatar
YiYi Xu committed
272

Steven Liu's avatar
Steven Liu committed
273
274
> [!WARNING]
> A component may change from pretrained to config when updating a component. The component type is initially defined in a block's `expected_components` field.
YiYi Xu's avatar
YiYi Xu committed
275

Steven Liu's avatar
Steven Liu committed
276
A pretrained component is updated with [`ComponentSpec`] whereas a config component is updated by eihter passing the object directly or with [`ComponentSpec`].
YiYi Xu's avatar
YiYi Xu committed
277

Steven Liu's avatar
Steven Liu committed
278
The [`ComponentSpec`] shows `default_creation_method="from_pretrained"` for a pretrained component shows `default_creation_method="from_config` for a config component.
YiYi Xu's avatar
YiYi Xu committed
279

Steven Liu's avatar
Steven Liu committed
280
To update a pretrained component, create a [`ComponentSpec`] with the name of the component and where to load it from. Use the [`~ComponentSpec.load`] method to load the component.
YiYi Xu's avatar
YiYi Xu committed
281
282

```py
Steven Liu's avatar
Steven Liu committed
283
from diffusers import ComponentSpec, UNet2DConditionModel
YiYi Xu's avatar
YiYi Xu committed
284

Steven Liu's avatar
Steven Liu committed
285
286
unet_spec = ComponentSpec(name="unet",type_hint=UNet2DConditionModel, repo="stabilityai/stable-diffusion-xl-base-1.0", subfolder="unet", variant="fp16")
unet = unet_spec.load(torch_dtype=torch.float16)
YiYi Xu's avatar
YiYi Xu committed
287
288
```

Steven Liu's avatar
Steven Liu committed
289
The [`~ModularPipeline.update_components`] method replaces the component with a new one.
YiYi Xu's avatar
YiYi Xu committed
290
291

```py
Steven Liu's avatar
Steven Liu committed
292
t2i_pipeline.update_components(unet=unet2)
YiYi Xu's avatar
YiYi Xu committed
293
294
```

Steven Liu's avatar
Steven Liu committed
295
When a component is updated, the loading specifications are also updated in the pipeline config.
YiYi Xu's avatar
YiYi Xu committed
296

Steven Liu's avatar
Steven Liu committed
297
### Component extraction and modification
YiYi Xu's avatar
YiYi Xu committed
298

Steven Liu's avatar
Steven Liu committed
299
When you use [`~ComponentSpec.load`], the new component maintains its loading specifications. This makes it possible to extract the specification and recreate the component.
YiYi Xu's avatar
YiYi Xu committed
300
301

```py
Steven Liu's avatar
Steven Liu committed
302
303
304
305
spec = ComponentSpec.from_component("unet", unet2)
spec
ComponentSpec(name='unet', type_hint=<class 'diffusers.models.unets.unet_2d_condition.UNet2DConditionModel'>, description=None, config=None, repo='stabilityai/stable-diffusion-xl-base-1.0', subfolder='unet', variant='fp16', revision=None, default_creation_method='from_pretrained')
unet2_recreated = spec.load(torch_dtype=torch.float16)
YiYi Xu's avatar
YiYi Xu committed
306
307
```

Steven Liu's avatar
Steven Liu committed
308
The [`~ModularPipeline.get_component_spec`] method gets a copy of the current component specification to modify or update.
YiYi Xu's avatar
YiYi Xu committed
309
310

```py
Steven Liu's avatar
Steven Liu committed
311
312
313
314
315
316
317
318
319
unet_spec = t2i_pipeline.get_component_spec("unet")
unet_spec
ComponentSpec(
    name='unet',
    type_hint=<class 'diffusers.models.unets.unet_2d_condition.UNet2DConditionModel'>,
    repo='RunDiffusion/Juggernaut-XL-v9',
    subfolder='unet',
    variant='fp16',
    default_creation_method='from_pretrained'
YiYi Xu's avatar
YiYi Xu committed
320
321
)

Steven Liu's avatar
Steven Liu committed
322
323
# modify to load from a different repository
unet_spec.repo = "stabilityai/stable-diffusion-xl-base-1.0"
YiYi Xu's avatar
YiYi Xu committed
324

Steven Liu's avatar
Steven Liu committed
325
326
# load component with modified spec
unet = unet_spec.load(torch_dtype=torch.float16)
YiYi Xu's avatar
YiYi Xu committed
327
328
```

Steven Liu's avatar
Steven Liu committed
329
## Modular repository
YiYi Xu's avatar
YiYi Xu committed
330

Steven Liu's avatar
Steven Liu committed
331
A repository is required if the pipeline blocks use *pretrained components*. The repository supplies loading specifications and metadata.
YiYi Xu's avatar
YiYi Xu committed
332

Steven Liu's avatar
Steven Liu committed
333
[`ModularPipeline`] specifically requires *modular repositories* (see [example repository](https://huggingface.co/YiYiXu/modular-diffdiff)) which are more flexible than a typical repository. It contains a `modular_model_index.json` file containing the following 3 elements.
YiYi Xu's avatar
YiYi Xu committed
334

Steven Liu's avatar
Steven Liu committed
335
336
- `library` and `class` shows which library the component was loaded from and it's class. If `null`, the component hasn't been loaded yet.
- `loading_specs_dict` contains the information required to load the component such as the repository and subfolder it is loaded from.
YiYi Xu's avatar
YiYi Xu committed
337

Steven Liu's avatar
Steven Liu committed
338
Unlike standard repositories, a modular repository can fetch components from different repositories based on the `loading_specs_dict`. Components don't need to exist in the same repository.
YiYi Xu's avatar
YiYi Xu committed
339

Steven Liu's avatar
Steven Liu committed
340
A modular repository may contain custom code for loading a [`ModularPipeline`]. This allows you to use specialized blocks that aren't native to Diffusers.
YiYi Xu's avatar
YiYi Xu committed
341
342

```
Steven Liu's avatar
Steven Liu committed
343
344
345
346
modular-diffdiff-0704/
├── block.py                    # Custom pipeline blocks implementation
├── config.json                 # Pipeline configuration and auto_map
└── modular_model_index.json    # Component loading specifications
YiYi Xu's avatar
YiYi Xu committed
347
348
```

Steven Liu's avatar
Steven Liu committed
349
The [config.json](https://huggingface.co/YiYiXu/modular-diffdiff-0704/blob/main/config.json) file contains an `auto_map` key that points to where a custom block is defined in `block.py`.
YiYi Xu's avatar
YiYi Xu committed
350

Steven Liu's avatar
Steven Liu committed
351
352
353
354
355
356
357
```json
{
  "_class_name": "DiffDiffBlocks",
  "auto_map": {
    "ModularPipelineBlocks": "block.DiffDiffBlocks"
  }
}
358
```