Diffusers documentation

ModularPipeline

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.42.1).
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.

  • 파이프라인을 생성하는 두 가지 방법 기존 diffusers 모델 저장소와 함께 from_pretrained()를 사용할 수 있습니다. 이 방법은 기본 파이프라인 블록에 자동으로 매핑한 후 추가 설정 없이 ModularPipeline으로 변환합니다. 현재 지원되는 모델을 확인하려면 modular_pipelines_directory를 살펴보세요. 또한 ModularPipelineBlocks로 직접 파이프라인을 조립한 후 init_pipeline() 메서드로 변환할 수도 있습니다(자세한 내용은 파이프라인 생성을 참고하세요).

  • 파이프라인 실행 방법은 동일합니다. 로드된 후, 익숙한 인수를 그대로 사용하여 파이프라인을 호출합니다. 파이프라인 블록이 AutoPipelineBlocks를 사용하여 입력에 따라 워크플로를 자동으로 선택하는 경우, 단일 ModularPipeline이 여러 워크플로(텍스트-이미지, 이미지-이미지, 인페인팅 등)를 지원할 수 있습니다.

다음은 SDXL을 사용한 텍스트-이미지 생성, 이미지-이미지 변환, 인페인팅의 완전한 예시입니다.

텍스트-이미지
이미지-이미지
인페인팅
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"
}

Blocks는 변경 가능합니다. 파이프라인을 생성하기 전에 자유롭게 블록을 추가, 제거 또는 교체할 수 있습니다. 그러나 파이프라인이 생성된 후에는 pipeline.blocks를 수정해도 복사본을 반환하기 때문에 파이프라인에 영향을 주지 않습니다. 다른 블록 구조가 필요하면 블록을 수정한 후 새로운 파이프라인을 생성하세요.

리포지토리를 지정하지 않고 init_pipeline()을 호출하면, 블록의 ComponentSpec에 정의된 pretrained_model_name_or_path를 사용하여 각 구성 요소를从哪里 로드할지 결정합니다. 파이프라인을 출력하면 구성 요소 로드 설정을 확인할 수 있습니다.

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 파일이 포함되어 있습니다. 구성 요소는 서로 다른 저장소에서 가져올 수 있으며, 모듈형 저장소 자체에는 모델 가중치가 전혀 없을 수도 있습니다. 예를 들어, diffusers/flux2-bnb-4bit-modular는 한 저장소에서 양자화된 transformer를 로드하고 나머지 구성 요소를 다른 저장소에서 로드합니다. 포맷에 대한 자세한 내용은 Modular repository를 참고하세요.

from diffusers import ModularPipeline, ComponentsManager

components = ComponentsManager()
pipeline = ModularPipeline.from_pretrained(
    "diffusers/flux2-bnb-4bit-modular", 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 블록이 워크플로를 정의하는 경우(pipeline.blocks.available_workflows 확인), workflow=를 전달하여 해당 워크플로의 블록만 유지합니다. 이는 get_workflow()와 동일한 가지치기입니다. 그러면 pipeline은 해당 워크플로가 사용하는 컴포넌트만 선언하고, 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()로 전달됩니다. 모든 구성 요소에 적용할 단일 값을 전달하거나, 구성 요소별 값을 설정하기 위해 dict를 전달할 수 있습니다.

# 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에 나열된 대로 config에서 생성됩니다. 이러한 구성 요소에는 load() 대신 create()를 사용하세요.

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)

사용 가능한 guider와 설정 방법에 대한 자세한 내용은 Guiders 가이드를 참고하세요.

파이프라인을 단계로 분할하기

블록은 구성 가능(composable)하므로, 파이프라인을 분해하여 각 단계별로 별도의 파이프라인으로 재구성할 수 있습니다. 아래 예제는 텍스트 인코더 블록을 파이프라인의 나머지 부분에서 분리하는 방법을 보여줍니다. 이를 통해 프롬프트를 독립적으로 인코딩한 후 임베딩을 메인 파이프라인에 전달할 수 있습니다.

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 가이드를 참고하세요.

pipeline 단계가 구성 요소를 공유하는 경우(예: 인코딩과 디코딩에 동일한 VAE 사용), 다시 로드하는 대신 이미 로드된 구성 요소를 다른 pipeline에 전달하기 위해 update_components()를 사용할 수 있습니다.

모듈형 저장소

pretrained components를 사용하는 파이프라인 블록의 경우 저장소가 필요합니다. 저장소는 로딩 사양과 메타데이터를 제공합니다.

ModularPipeline는 일반적인 diffusers 저장소와 바로 함께 작동합니다. 그러나 더 많은 유연성을 위해 모듈형 저장소를 만들 수도 있습니다. 모듈형 저장소에는 다음 3가지 요소를 포함하는 modular_model_index.json 파일이 있습니다.

  • library와 class는 해당 구성 요소가 어떤 라이브러리에서 로드되었는지와 그 클래스를 나타냅니다. null인 경우, 해당 구성 요소는 아직 로드되지 않은 것입니다.
  • loading_specs_dict에는 저장소와 하위 폴더와 같이 구성 요소를 로드하는 데 필요한 정보가 포함되어 있습니다.

모듈형 저장소의 핵심 이점은 컴포넌트를 서로 다른 저장소에서 로드할 수 있다는 것입니다. 예를 들어, diffusers/flux2-bnb-4bit-modular는 diffusers/FLUX.2-dev-bnb-4bit에서 양자화된 transformer를 로드하는 동시에 나머지 컴포넌트는 black-forest-labs/FLUX.2-dev에서 로드합니다.

일반적인 diffusers 저장소를 모듈형 저장소로 변환하려면, 먼저 일반적인 저장소를 사용하여 파이프라인을 생성한 후 Hub에 push합니다. 저장된 저장소에는 모든 로딩 사양이 포함된 modular_model_index.json가 포함됩니다.

from diffusers import ModularPipeline

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

# 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, push_to_hub=True를 전달한 경우에는 repo_id입니다.

huggingface_hub>=1.32.0부터 동일한 Xet 기반 파일은 Hub 저장소 간에 하나의 캐시된 페이로드를 공유할 수 있습니다. 이는 샤드화된 체크포인트의 개별 파일에도 적용됩니다. 다른 샤드와 내용이 달라도 동일한 Xet 해시를 가진 샤드는 재사용될 수 있습니다. 두 다운로드 모두 심볼릭 링크를 지원하는 동일한 Hub 캐시 디렉터리를 사용해야 합니다. dtype, 직렬화 방식 또는 샤드 경계를 변경하면 파일 해시가 달라져 재사용이 불가능해질 수 있습니다. 이를 통해 다운로드 트래픽과 디스크 공간을 절약할 수 있지만, 로드된 구성 요소가 사용하는 메모리는 절약되지 않습니다.

공유 캐싱은 최선 노력(best-effort) 방식입니다. HF_HUB_DISABLE_SHARED_BLOBS=1을 설정해도 로딩은 지원되지만, 개별 사본을 캐시합니다. modular_model_index.json에 있는 기존 구성 요소 참조는 변경되지 않으며, 모듈형 저장소에 저장되지 않은 구성 요소의 외부 참조는 유지해야 합니다.

기존 모듈형 인덱스 업데이트

마이그레이션은 선택 사항입니다: 원본 저장소에 대한 참조는 이미 캐시된 파일을 재사용합니다. 중복 다운로드를 피하기 위해만 사용되었고 해당 구성 요소를 모듈형 저장소에서 로드하도록 변경하려면 참조를 업데이트하세요.

먼저, 모듈형 저장소에 동일한 구성 요소 구성과 모든 필수 가중치 파일이 포함되어 있는지 확인하십시오. 여기에는 가중치 인덱스와 샤드화된 체크포인트의 모든 샤드가 포함됩니다. 일치하는 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")를 호출한 후, 해당 구성 요소가 예상된 매개변수로 로드되는지 확인하세요. 편집된 인덱스만 업로드하세요. 가중치를 다시 저장하면 해시가 변경되어 캐시 재사용이 방지될 수 있습니다.

기존 컴포넌트에 대한 참조 유지

새로운 것만(아니면 아무것도 아닌) 로드합니다. 로드된 컴포넌트만 저장되며, 나머지는 원래 저장소를 가리키는 포인터로 유지됩니다. 한 컴포넌트를 교체하면서 다른 컴포넌트는 원래 저장소에서 계속 로드하기를 원할 때 이 모드를 사용하세요. 예를 들어, 커스텀 transformer를 저장하면서 나머지 컴포넌트는 기본 저장소에서 계속 로드되도록 할 수 있습니다.

pipe = ModularPipeline.from_pretrained("black-forest-labs/FLUX.2-dev")
pipe.update_components(transformer=my_custom_transformer)  # the only component in memory
pipe.save_pretrained("local/path", repo_id="my-username/flux2-custom-transformer", push_to_hub=True)

overwrite_modular_index=False를 전달하면 modular_model_index.json의 로딩 사양을 그대로 유지합니다. 로딩 사양이 비어 있는 저장된 컴포넌트는 보존할 내용이 없으므로 대상 경로로 채워집니다.

파일을 다른 방식으로 이동하면 인덱스가 다시 작성되지 않습니다. hf download --local-dir로 다운로드한 복사본은 여전히 작동합니다. 파이프라인이 로컬 디렉터리에서 로드되면, 해당 디렉터리에 파일이 존재하는 모든 구성 요소는 기록된 저장소가 아닌 해당 디렉터리에서 로드됩니다. hf upload로 업로드한 복사본은 여전히 이전 위치를 가리키므로, 이 경우 인덱스를 수동으로 업데이트해야 합니다.

모듈형 저장소에는 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 파일에는 ModularPipeline가 커스텀 블록을 어디에서 찾을 수 있는지 알려 주는 auto_map 키가 포함되어 있습니다:

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

trust_remote_code=True를 사용하여 from_pretrained에 표시된 대로 사용자 정의 코드 저장소를 로드합니다. 직접 생성하고 공유하는 방법은 Custom blocks를 참조하세요.

Update on GitHub