Diffusers documentation

ModularPipeline

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

ModularPipeline

ModularPipeline преобразует ModularPipelineBlocks в исполняемый конвейер, который загружает модели и выполняет вычислительные шаги, определённые в блоках. Это основной интерфейс для запуска конвейера, и его API очень похож на DiffusionPipeline, но с несколькими ключевыми отличиями.

  • Loading is lazy. With DiffusionPipeline, from_pretrained() creates the pipeline and loads all models at the same time. With ModularPipeline, creating and loading are two separate steps: from_pretrained() reads the configuration and knows where to load each component from, but doesn’t actually load the model weights. You load the models later with load_components(), which is where you pass loading arguments like dtype and quantization_config.

  • Два способа создания пайплайна. Можно использовать from_pretrained() с существующим репозиторием модели diffusers — он автоматически сопоставляется с блоками пайплайна по умолчанию, а затем преобразуется в ModularPipeline без дополнительной настройки. Список поддерживаемых моделей можно найти в modular_pipelines_directory. Также можно собрать собственный пайплайн из ModularPipelineBlocks и преобразовать его с помощью метода init_pipeline() (подробнее см. Создание пайплайна).

  • Запуск пайплайна остаётся прежним. После загрузки вы вызываете пайплайн с теми же аргументами, к которым привыкли. Один ModularPipeline может поддерживать несколько рабочих процессов (генерация изображений из текста, трансформация изображений, inpainting и т. д.), если блоки пайплайна используют AutoPipelineBlocks для автоматического выбора рабочего процесса на основе ваших входных данных.

Ниже приведены полные примеры для генерации изображений по тексту, преобразования изображения в изображение и инпейнтинга с SDXL.

text-to-image
image-to-image
inpainting
import torch
from diffusers import ModularPipeline

pipeline = ModularPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0")
pipeline.load_components(dtype=torch.float16)
pipeline.to("cuda")  # or "mps", "xpu", "cpu"

image = pipeline(prompt="Astronaut in a jungle, cold color palette, muted colors, detailed, 8k").images[0]
image.save("modular_t2i_out.png")

В этом руководстве показано, как создать ModularPipeline, управлять его компонентами и запустить конвейер.

Создание конвейера

There are two ways to create a ModularPipeline. Assemble and create a pipeline from ModularPipelineBlocks with init_pipeline(), or load an existing pipeline with from_pretrained().

Вы также можете инициализировать ComponentsManager для управления размещением на устройствах и управлением памятью. Если автоматическое выгружение не требуется, вы можете пропустить этот шаг и вручную переместить конвейер на ваше устройство с помощью pipeline.to("cuda").

См. документацию ComponentsManager для получения подробной информации о том, как он помогает управлять компонентами в разных рабочих процессах.

init_pipeline

init_pipeline() преобразует любой ModularPipelineBlocks в ModularPipeline.

Определим минимальный блок, чтобы посмотреть, как он работает:

from transformers import CLIPTextModel
from diffusers.modular_pipelines import (
    ComponentSpec,
    ModularPipelineBlocks,
    PipelineState,
)

class MyBlock(ModularPipelineBlocks):
    @property
    def expected_components(self):
        return [
            ComponentSpec(
                name="text_encoder",
                type_hint=CLIPTextModel,
                pretrained_model_name_or_path="openai/clip-vit-large-patch14",
            ),
        ]

    def __call__(self, components, state: PipelineState) -> PipelineState:
        return components, state

Вызовите init_pipeline(), чтобы преобразовать его в пайплайн. Атрибут blocks пайплайна содержит блоки, из которых он был создан, — они определяют ожидаемые входные и выходные данные, а также логику вычислений.

block = MyBlock()
pipe = block.init_pipeline()
pipe.blocks
MyBlock {
  "_class_name": "MyBlock",
  "_diffusers_version": "0.37.0.dev0"
}

Блоки изменяемы — вы можете свободно добавлять, удалять или заменять блоки до создания конвейера. Однако, как только конвейер создан, изменение pipeline.blocks не повлияет на конвейер, так как возвращается копия. Если вам нужна другая структура блоков, создайте новый конвейер после изменения блоков.

При вызове init_pipeline() без указания репозитория используется pretrained_model_name_or_path, определённое в ComponentSpec блока, чтобы определить, откуда загружать каждый компонент. Вывод пайплайна показывает конфигурацию загрузки компонентов.

pipe
ModularPipeline {
  "_blocks_class_name": "MyBlock",
  "_class_name": "ModularPipeline",
  "_diffusers_version": "0.37.0.dev0",
  "text_encoder": [
    null,
    null,
    {
      "pretrained_model_name_or_path": "openai/clip-vit-large-patch14",
      "revision": null,
      "subfolder": "",
      "type_hint": [
        "transformers",
        "CLIPTextModel"
      ],
      "variant": null
    }
  ]
}

Если вы передадите репозиторий в init_pipeline(), он переопределит путь загрузки, сопоставляя компоненты вашего блока с конфигурацией пайплайна в этом репозитории (model_index.json или modular_model_index.json). См. Modular repository, чтобы узнать, как спецификации загрузки записываются и сохраняются.

В примере ниже pretrained_model_name_or_path будет обновлено до "stabilityai/stable-diffusion-xl-base-1.0".

pipe = block.init_pipeline("stabilityai/stable-diffusion-xl-base-1.0")
pipe
ModularPipeline {
  "_blocks_class_name": "MyBlock",
  "_class_name": "ModularPipeline",
  "_diffusers_version": "0.37.0.dev0",
  "text_encoder": [
    null,
    null,
    {
      "pretrained_model_name_or_path": "stabilityai/stable-diffusion-xl-base-1.0",
      "revision": null,
      "subfolder": "text_encoder",
      "type_hint": [
        "transformers",
        "CLIPTextModel"
      ],
      "variant": null
    }
  ]
}

Если компонент в вашем блоке отсутствует в репозитории, он остаётся null и пропускается при вызове load_components().

from_pretrained

from_pretrained() — удобный способ создать ModularPipeline, не определяя блоки самостоятельно.

Он работает с тремя типами репозиториев.

Обычный репозиторий diffusers. Передайте любой поддерживаемый репозиторий модели, и он автоматически сопоставится с блоками стандартного пайплайна. В настоящее время поддерживаются модели SDXL, Wan, Qwen, Z-Image, Flux и Flux2.

from diffusers import ModularPipeline, ComponentsManager

components = ComponentsManager()
pipeline = ModularPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0", components_manager=components
)

Модульный репозиторий. Эти репозитории содержат файл modular_model_index.json, в котором указаны классы каждого компонента и источники их загрузки, при этом сами компоненты хранятся рядом с этим файлом. См. Модульный репозиторий, чтобы узнать, как его создать.

from diffusers import ModularPipeline, ComponentsManager

components = ComponentsManager()
pipeline = ModularPipeline.from_pretrained(
    "MiniMaxAI/MiniMax-H3", components_manager=components
)

Модульный репозиторий с собственным кодом. Некоторые репозитории содержат наряду с конфигурацией загрузки собственные блоки конвейера. Добавьте trust_remote_code=True, чтобы загрузить их. О том, как создать собственные блоки, см. в разделе Собственные блоки.

from diffusers import ModularPipeline, ComponentsManager

components = ComponentsManager()
pipeline = ModularPipeline.from_pretrained(
    "diffusers/Florence2-image-Annotator", trust_remote_code=True, components_manager=components
)

Когда блоки пайплайна определяют рабочие процессы (см. pipeline.blocks.available_workflows), передайте workflow=, чтобы оставить только блоки этого рабочего процесса — это та же обрезка, что и в get_workflow(). Тогда пайплайн объявляет только те компоненты, которые использует данный рабочий процесс, а его docstring описывает именно его входные данные.

pipeline = ModularPipeline.from_pretrained("Qwen/Qwen-Image", workflow="inpainting")

Загрузка компонентов

ModularPipeline не инициализирует компоненты автоматически. Он загружает только конфигурацию и спецификации компонентов. Загрузить компоненты можно с помощью load_components().

Это загрузит все компоненты, для которых определена корректная спецификация загрузки.

import torch

pipeline.load_components(dtype=torch.float16)

Вы также можете загрузить конкретные компоненты по имени. В примере ниже загружается только text_encoder.

pipeline.load_components(names=["text_encoder"], dtype=torch.float16)

В конвейере, блоки которого определяют рабочие процессы, параметр workflow= загружает только те компоненты, которые используются этим рабочим процессом. Конвейер сохраняет все свои блоки, поэтому это удобный способ запускать один конвейер для разных рабочих процессов: каждый вызов добавляет лишь то, чего ещё не хватает новому рабочему процессу.

pipeline.load_components(workflow="inpainting", dtype=torch.float16)

После загрузки вывод пайплайна показывает, какие компоненты загружены — первые два поля меняются с null на библиотеку и класс компонента.

pipeline
# text_encoder is loaded - shows library and class
"text_encoder": [
  "transformers",
  "CLIPTextModel",
  { ... }
]

# unet is not loaded yet - still null
"unet": [
  null,
  null,
  { ... }
]

Параметры, передаваемые по имени, такие как dtype, variant, revision и quantization_config, передаются в from_pretrained() для каждого компонента. Можно передать одно значение, чтобы применить его ко всем компонентам, или словарь, чтобы задать значения для каждого компонента отдельно.

# apply bfloat16 to all components
pipeline.load_components(dtype=torch.bfloat16)

# different dtypes per component
pipeline.load_components(dtype={"transformer": torch.bfloat16, "default": torch.float32})

load_components() загружает только те компоненты, которые ещё не были загружены и для которых задано корректное описание загрузки. Это означает, что если вы уже установили компонент в конвейере, повторный вызов load_components() не перезагрузит его.

Обновление компонентов

update_components() заменяет компонент в конвейере новым. При обновлении компонента спецификации загрузки также обновляются в конфигурации конвейера, и load_components() будет пропускать его при последующих вызовах.

Из AutoModel

Вы можете передать объект модели, загруженный с помощью AutoModel.from_pretrained(). Модели, загруженные таким образом, автоматически помечаются информацией о загрузке.

from diffusers import AutoModel

unet = AutoModel.from_pretrained(
    "RunDiffusion/Juggernaut-XL-v9", subfolder="unet", variant="fp16", dtype=torch.float16
)
pipeline.update_components(unet=unet)

Из ComponentSpec

Используйте get_component_spec(), чтобы получить копию текущей спецификации компонента, изменить её и загрузить новый компонент.

unet_spec = pipeline.get_component_spec("unet")

# modify to load from a different repository
unet_spec.pretrained_model_name_or_path = "RunDiffusion/Juggernaut-XL-v9"

# load and update
unet = unet_spec.load(dtype=torch.float16)
pipeline.update_components(unet=unet)

Вы также можете создать ComponentSpec с нуля.

Не все компоненты загружаются из предобученных весов — некоторые создаются на основе конфигурации (перечислены в pipeline.config_component_names). Для них используйте create() вместо load().

guider_spec = pipeline.get_component_spec("guider")
guider_spec.config = {"guidance_scale": 5.0}
guider = guider_spec.create()
pipeline.update_components(guider=guider)

Или просто передайте объект напрямую.

from diffusers.guiders import ClassifierFreeGuidance

guider = ClassifierFreeGuidance(guidance_scale=5.0)
pipeline.update_components(guider=guider)

См. руководство Guiders для получения подробной информации о доступных guiders и способах их настройки.

Разделение конвейера на этапы

Поскольку блоки комбинируемы, вы можете разобрать пайплайн и собрать из него отдельные пайплайны для каждого этапа. В примере ниже показано, как можно выделить блок text encoder из остальной части пайплайна, чтобы независимо закодировать промпт и передать эмбеддинги в основной пайплайн.

from diffusers import ModularPipeline, ComponentsManager
import torch

device = "cuda"  # or "mps", "xpu", "cpu"
dtype = torch.bfloat16
repo_id = "black-forest-labs/FLUX.2-klein-4B"

# get the blocks and separate out the text encoder
blocks = ModularPipeline.from_pretrained(repo_id).blocks
text_block = blocks.sub_blocks.pop("text_encoder")

# use ComponentsManager to handle offloading across multiple pipelines
manager = ComponentsManager()
manager.enable_auto_cpu_offload(device=device)

# create separate pipelines for each stage
text_encoder_pipeline = text_block.init_pipeline(repo_id, components_manager=manager)
pipeline = blocks.init_pipeline(repo_id, components_manager=manager)

# encode text
text_encoder_pipeline.load_components(dtype=dtype)
text_embeddings = text_encoder_pipeline(prompt="a cat").get_by_kwargs("denoiser_input_fields")

# denoise and decode
pipeline.load_components(dtype=dtype)
output = pipeline(
    **text_embeddings,
    num_inference_steps=4,
).images[0]

ComponentsManager управляет памятью в нескольких конвейерах. В отличие от стратегий выгрузки в DiffusionPipeline, которые следуют фиксированному порядку, ComponentsManager принимает решения о выгрузке динамически при каждом проходе модели вперёд, исходя из текущего состояния памяти. Это означает, что он работает независимо от количества созданных конвейеров и порядка их выполнения. Подробнее см. в руководстве по ComponentsManager.

Если этапы конвейера используют общие компоненты (например, одна и та же VAE для кодирования и декодирования), вы можете использовать update_components(), чтобы передать уже загруженный компонент в другой конвейер, а не загружать его заново.

Модульный репозиторий

Репозиторий требуется, если блоки конвейера используют предобученные компоненты. Репозиторий предоставляет спецификации загрузки и метаданные.

ModularPipeline работает с обычными репозиториями diffusers без дополнительной настройки. Однако для большей гибкости можно создать модульный репозиторий. Модульный репозиторий содержит файл modular_model_index.json с тремя элементами.

  • library и class показывают, из какой библиотеки был загружен компонент и его класс. Если null, компонент ещё не был загружен.
  • loading_specs_dict содержит информацию, необходимую для загрузки компонента, например, репозиторий и подпапку, из которых он загружается.
from diffusers import ModularPipeline

# load from a regular repo
pipeline = ModularPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0")
pipeline.load_components()

# push as a modular repository
pipeline.save_pretrained("local/path", repo_id="my-username/sdxl-modular", push_to_hub=True)

По умолчанию save_pretrained() записывает каждый загруженный в данный момент компонент, который Diffusers может сериализовать. Компоненты, которые не загружены или не могут быть сериализованы, не записываются и сохраняют свои существующие спецификации загрузки.

Сохранить автономную копию

Загрузите все компоненты, а затем сохраните. Каждая спецификация указывает на результат, поэтому он полностью перезагружается из одного места, включая офлайн-режим.

pipe = ModularPipeline.from_pretrained("MiniMaxAI/MiniMax-H3")
pipe.load_components()
pipe.save_pretrained("path/to/local-copy")

Путь назначения, записанный в modular_model_index.json, — это то место, куда save_pretrained() записывает данные: save_directory при сохранении локально или repo_id, если вы передаёте push_to_hub=True.

Перемещение файлов любым другим способом не приводит к переписыванию индекса. Копия, загруженная с помощью hf download --local-dir, по-прежнему работает: при загрузке пайплайна из локального каталога каждый компонент, файлы которого присутствуют в этом каталоге, загружается из него, а не из указанного в индексе репозитория. Копия, загруженная с помощью hf upload, по-прежнему указывает на старое расположение, поэтому в этом случае индекс нужно обновить вручную.

Начиная с huggingface_hub>=1.32.0, идентичные файлы, использующие Xet, могут разделять один кэшированный payload между любыми репозиториями Hub. Это касается отдельных файлов в шардированном чекпоинте: шард с одинаковым Xet-хэшем может быть переиспользован, даже если другие шарды отличаются. Оба процесса загрузки должны использовать один и тот же каталог кэша Hub с поддержкой символьных ссылок. Изменение dtype, способа сериализации или границ шардов может изменить хэши файлов и предотвратить переиспользование. Это экономит трафик загрузки и место на диске, но не память, используемую загруженными компонентами.

Общее кэширование работает в режиме best-effort. Установка переменной HF_HUB_DISABLE_SHARED_BLOBS=1 по-прежнему поддерживает загрузку, но кэширует отдельные копии. Существующие ссылки на компоненты в modular_model_index.json остаются без изменений; сохраняйте внешние ссылки для компонентов, которые не хранятся в модульном репозитории.

Ссылки на компоненты в других репозиториях

Спецификация загрузки компонента в modular_model_index.json может указывать на другой репозиторий. Например, diffusers/flux2-bnb-4bit-modular загружает свой квантованный трансформер из diffusers/FLUX.2-dev-bnb-4bit, а остальные компоненты — из black-forest-labs/FLUX.2-dev.

Мы настоятельно рекомендуем использовать самодостаточные репозитории: объединение всех компонентов в одном месте упрощает загрузку, обмен и использование пайплайна в автономном режиме без зависимости от других репозиториев. Используйте ссылки на другие репозитории только в том случае, если вы намеренно хотите разместить компонент в другом месте.

Передайте overwrite_modular_index=False, чтобы сохранить спецификации загрузки в modular_model_index.json без изменений. Сохранённый компонент с пустой спецификацией загрузки всё равно будет дополнен целевым значением, так как сохранять нечего.

Начиная с версии huggingface_hub>=1.32.0, идентичные файлы, использующие Xet, могут разделять один кэшированный payload между репозиториями через символические ссылки, что позволяет избежать дублирования загрузок и использования дискового пространства. Для этого не требуется создавать ссылки между репозиториями. Требования и ограничения для общего кэша описаны в разделе Сохранение автономной копии.

Обновление существующего модульного индекса

Существующие ссылки на исходный репозиторий продолжают работать. Если ссылка использовалась только для предотвращения дублирования загрузок, рекомендуется обновить её для загрузки из модульного репозитория, как только файлы компонентов станут там доступны.

Сначала убедитесь, что модульный репозиторий содержит ту же конфигурацию компонентов и все необходимые файлы весов, включая индекс весов и каждый шард для шардированного чекпоинта. Совпадение хешей Xet определяет, какие файлы весов можно повторно использовать из общего кэша. Сохраняйте ссылки на компоненты, которые отсутствуют или намеренно загружаются из другого чекпоинта.

Измените только соответствующую спецификацию загрузки в локальной копии modular_model_index.json. Например, чтобы использовать VAE, хранящийся в vae/ в модульном репозитории, с тем же вариантом весов:

import json
from pathlib import Path

index_path = Path("path/to/local-copy/modular_model_index.json")
index = json.loads(index_path.read_text())
spec = index["vae"][2]
spec["pretrained_model_name_or_path"] = "my-username/my-modular-repo"
spec.pop("repo", None)
spec["subfolder"] = "vae"
spec["revision"] = None
index_path.write_text(json.dumps(index, indent=2) + "\n")

Это также удаляет устаревшее поле repo. Ревизия из исходного репозитория не определяет ревизию в целевом: используйте None для ветки по умолчанию целевого репозитория или зафиксируйте ревизию целевого репозитория, содержащую компонент. При необходимости скорректируйте subfolder и variant, если имена файлов различаются.

Перед публикацией обновленного индекса загрузите его с помощью ModularPipeline.from_pretrained("path/to/local-copy"), вызовите load_components(names="vae") и убедитесь, что компонент загружается с ожидаемыми параметрами. Загружайте только отредактированный индекс; повторное сохранение весов может изменить их хеши и помешать повторному использованию кэша.

Пользовательский код

Модульный репозиторий также может включать пользовательские блоки конвейера в виде Python-кода. Это позволяет делиться специализированными блоками, которые не являются нативными для Diffusers. Например, diffusers/Florence2-image-Annotator содержит пользовательские блоки наряду с конфигурацией загрузки:

Florence2-image-Annotator/
├── block.py                    # Custom pipeline blocks implementation
├── config.json                 # Pipeline configuration and auto_map
├── mellon_config.json          # UI configuration for Mellon
└── modular_model_index.json    # Component loading specifications

Файл config.json содержит ключ auto_map, который указывает ModularPipeline, где искать пользовательские блоки:

{
  "_class_name": "Florence2AnnotatorBlocks",
  "auto_map": {
    "ModularPipelineBlocks": "block.Florence2AnnotatorBlocks"
  }
}

Загружайте репозитории с пользовательским кодом с помощью trust_remote_code=True, как показано в from_pretrained. О том, как создавать и делиться собственными блоками, см. в разделе Custom blocks.

Update on GitHub