Diffusers documentation

Démarrage rapide

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

Démarrage rapide

Modular Diffusers est un framework permettant de construire rapidement des pipelines flexibles et personnalisables. Ces pipelines peuvent aller au-delà de ce que les DiffusionPipeline standard peuvent faire. Au cœur de Modular Diffusers se trouvent des ModularPipelineBlocks qui peuvent être combinés avec d'autres blocs pour s'adapter à de nouveaux flux de travail. Les blocs sont convertis en ModularPipeline, une interface conviviale destinée aux utilisateurs pour exécuter des tâches de génération.

Ce guide vous montre comment exécuter un pipeline modulaire, comprendre sa structure et le personnaliser en modifiant les blocs qui le composent. Si vous êtes familier avec DiffusionPipeline, lisez d'abord Coming from DiffusionPipeline pour savoir ce qui fonctionne différemment.

Exécuter un pipeline

ModularPipeline est l’interface principale pour charger, exécuter et gérer les pipelines modulaires.

import torch
from diffusers import ModularPipeline, ComponentsManager

# Use ComponentsManager to enable auto CPU offloading for memory efficiency
manager = ComponentsManager()
manager.enable_auto_cpu_offload(device="cuda:0")

pipe = ModularPipeline.from_pretrained("Qwen/Qwen-Image", components_manager=manager)
pipe.load_components(dtype=torch.bfloat16)

image = pipe(
    prompt="cat wizard with red hat, gandalf, lord of the rings, detailed, fantasy, cute, adorable, Pixar, Disney",
).images[0]
image

from_pretrained() utilise un chargement paresseux : il lit la configuration pour savoir où charger chaque composant, mais ne charge réellement les poids du modèle que lorsque vous appelez load_components(). Cela vous donne le contrôle sur le moment et la manière dont les composants sont chargés.

ComponentsManager avec enable_auto_cpu_offload déplace automatiquement les modèles entre le CPU et le GPU selon les besoins, réduisant ainsi l’utilisation de la mémoire pour les grands modèles comme Qwen-Image. En savoir plus dans le guide ComponentsManager.

Si vous n’avez pas besoin de déchargement, supprimez l’argument components_manager et déplacez le pipeline manuellement sur votre appareil avec to("cuda").

En savoir plus sur la création et le chargement de pipelines dans les guides Créer un pipeline et Charger des composants.

Comprendre la structure

Un ModularPipeline comporte deux parties : une définition (les blocs) et un état (les composants et configurations chargés).

Affichez le pipeline pour voir son état — les composants et leur statut de chargement et leur configuration.

print(pipe)
QwenImageModularPipeline {
  "_blocks_class_name": "QwenImageAutoBlocks",
  "_class_name": "QwenImageModularPipeline",
  "_diffusers_version": "0.37.0.dev0",
  "transformer": [
    "diffusers",
    "QwenImageTransformer2DModel",
    {
      "pretrained_model_name_or_path": "Qwen/Qwen-Image",
      "revision": null,
      "subfolder": "transformer",
      "type_hint": [
        "diffusers",
        "QwenImageTransformer2DModel"
      ],
      "variant": null
    }
  ],
  ...
}

Accédez à la définition via pipe.blocks — il s’agit du ModularPipelineBlocks qui définit les flux de travail, les entrées, les sorties et la logique de calcul du pipeline.

print(pipe.blocks)
QwenImageAutoBlocks(
  Class: SequentialPipelineBlocks

  Description: Auto Modular pipeline for text-to-image, image-to-image, inpainting, and controlnet tasks using QwenImage.
      
      Supported workflows:
        - `text2image`: requires `prompt`
        - `image2image`: requires `prompt`, `image`
        - `inpainting`: requires `prompt`, `mask_image`, `image`
        - `controlnet_text2image`: requires `prompt`, `control_image`
        ...

  Components:
      text_encoder (`Qwen2_5_VLForConditionalGeneration`)
      vae (`AutoencoderKLQwenImage`)
      transformer (`QwenImageTransformer2DModel`)
      ...

  Sub-Blocks:
    [0] text_encoder (QwenImageAutoTextEncoderStep)
    [1] vae_encoder (QwenImageAutoVaeEncoderStep)
    [2] controlnet_vae_encoder (QwenImageOptionalControlNetVaeEncoderStep)
    [3] denoise (QwenImageAutoCoreDenoiseStep)
    [4] decode (QwenImageAutoDecodeStep)
)

La sortie renvoie :

  • Les flux de travail pris en charge (text2image, image2image, inpainting, etc.)
  • Les sous-blocs dont il est composé (text_encoder, vae_encoder, denoise, decode)

Flux de travail

Ce pipeline prend en charge plusieurs flux de travail et adapte son comportement en fonction des entrées que vous fournissez. Par exemple, si vous passez image au pipeline, il exécute un flux de travail image-vers-image au lieu de texte-vers-image. En savoir plus sur le fonctionnement interne dans le guide AutoPipelineBlocks.

from diffusers.utils import load_image

input_image = load_image("https://github.com/Trgtuan10/Image_storage/blob/main/cute_cat.png?raw=true")

image = pipe(
    prompt="cat wizard with red hat, gandalf, lord of the rings, detailed, fantasy, cute, adorable, Pixar, Disney",
    image=input_image,
).images[0]

Utilisez get_workflow() pour extraire les blocs d'un workflow spécifique. Transmettez le nom du workflow (par exemple, "image2image", "inpainting", "controlnet_text2image") pour n'obtenir que les blocs pertinents pour ce workflow. Cela est utile lorsque vous souhaitez personnaliser ou déboguer un workflow spécifique. Vous pouvez consulter pipe.blocks.available_workflows pour voir tous les workflows disponibles.

img2img_blocks = pipe.blocks.get_workflow("image2image")

Sous-blocs

Les blocs peuvent contenir d'autres blocs. pipe.blocks vous donne la définition du bloc de premier niveau (ici, QwenImageAutoBlocks), tandis que sub_blocks vous permet d'accéder aux plus petits blocs à l'intérieur.

QwenImageAutoBlocks est composé de : text_encoder, vae_encoder, controlnet_vae_encoder, denoise et decode.

Ces sous-blocs s’exécutent les uns après les autres et les données circulent linéairement d’un bloc à l’autre — les intermediate_outputs de chaque bloc deviennent disponibles en tant que inputs du bloc suivant. C’est ainsi que SequentialPipelineBlocks fonctionne.

Vous pouvez y accéder via la propriété sub_blocks. La propriété doc est utile pour consulter la documentation complète de n’importe quel bloc, y compris ses entrées, ses sorties et ses composants.

vae_encoder_block = pipe.blocks.sub_blocks["vae_encoder"]
print(vae_encoder_block.doc)

Ce bloc peut être converti en pipeline afin qu'il puisse s'exécuter de manière autonome avec init_pipeline().

vae_encoder_pipe = vae_encoder_block.init_pipeline()

# Reuse the VAE we already loaded, we can reuse it with update_components() method
vae_encoder_pipe.update_components(vae=pipe.vae)

# Run just this block
image_latents = vae_encoder_pipe(image=input_image).image_latents
print(image_latents.shape)

Il réutilise le VAE de notre pipeline d’origine au lieu de le recharger, ce qui permet de maintenir une utilisation de la mémoire efficace. En savoir plus dans le guide Chargement des composants.

Les blocs étant composables, vous pouvez modifier la définition du pipeline en ajoutant, supprimant ou échangeant des blocs pour créer de nouveaux flux de travail. Dans la section suivante, nous ajouterons un bloc de détection de contours canny à un pipeline ControlNet, afin que vous puissiez passer une image ordinaire plutôt qu’une carte de contours canny prétraitée.

Composer de nouveaux flux de travail

Ajoutons un bloc de détection de contours canny à un pipeline ControlNet. Chargez d’abord un bloc canny préconstruit depuis le Hub (voir Building Custom Blocks pour en créer un vous-même).

from diffusers.modular_pipelines import ModularPipelineBlocks

# Load a canny block from the Hub
canny_block = ModularPipelineBlocks.from_pretrained(
    "diffusers-internal-dev/canny-filtering",
    trust_remote_code=True,
)

print(canny_block.doc)
class CannyBlock

  Inputs:
      image (`Union[Image, ndarray]`):
          Image to compute canny filter on
      low_threshold (`int`, *optional*, defaults to 50):
          Low threshold for the canny filter.
      high_threshold (`int`, *optional*, defaults to 200):
          High threshold for the canny filter.
      ...

  Outputs:
      control_image (`PIL.Image`):
          Canny map for input image

Utilisez get_workflow pour extraire le workflow ControlNet de QwenImageAutoBlocks.

# Get the controlnet workflow that we want to work with
blocks = pipe.blocks.get_workflow("controlnet_text2image")
print(blocks.doc)
class SequentialPipelineBlocks

  Inputs:
      prompt (`str`):
          The prompt or prompts to guide image generation.
      control_image (`Image`):
          Control image for ControlNet conditioning.
      ...

Le workflow extrait est un SequentialPipelineBlocks et il nécessite actuellement control_image en entrée. Insérez le bloc canny au début afin que le pipeline accepte une image ordinaire à la place.

# Insert canny at the beginning
blocks.sub_blocks.insert("canny", canny_block, 0)

# Check the updated structure: CannyBlock is now listed as first sub-block
print(blocks)
# Check the updated doc
print(blocks.doc)
class SequentialPipelineBlocks

  Inputs:
      image (`Union[Image, ndarray]`):
          Image to compute canny filter on
      low_threshold (`int`, *optional*, defaults to 50):
          Low threshold for the canny filter.
      high_threshold (`int`, *optional*, defaults to 200):
          High threshold for the canny filter.
      prompt (`str`):
          The prompt or prompts to guide image generation.
      ...

Le pipeline prend désormais image en entrée au lieu de control_image. Comme les blocs d’une séquence partagent automatiquement les données, la sortie du bloc canny (control_image) est transmise au bloc de débruitage qui en a besoin, et l’entrée du bloc canny (image) devient une entrée du pipeline, aucun bloc antérieur ne la fournissant.

Créez un pipeline à partir des blocs modifiés et chargez un modèle ControlNet. Le ControlNet ne fait pas partie du dépôt de modèle d’origine, il faut donc le charger séparément et l’ajouter avec update_components().

pipeline = blocks.init_pipeline("Qwen/Qwen-Image", components_manager=manager)

pipeline.load_components(dtype=torch.bfloat16)

# Load the ControlNet model
controlnet_spec = pipeline.get_component_spec("controlnet")
controlnet_spec.pretrained_model_name_or_path = "InstantX/Qwen-Image-ControlNet-Union"
controlnet = controlnet_spec.load(dtype=torch.bfloat16)
pipeline.update_components(controlnet=controlnet)

Exécutez maintenant le pipeline - le bloc canny prétraite l'image pour ControlNet.

from diffusers.utils import load_image

prompt = "cat wizard with red hat, gandalf, lord of the rings, detailed, fantasy, cute, adorable, Pixar, Disney"
image = load_image("https://github.com/Trgtuan10/Image_storage/blob/main/cute_cat.png?raw=true")

output = pipeline(
    prompt=prompt,
    image=image,
).images[0]
output

Étapes suivantes

Apprendre les bases
Créer des blocs personnalisés
Partager des composants
Interface visuelle

Comprendre les briques de base de Modular Diffusers :

Update on GitHub