Buckets:

hf-doc-build/doc / diffusers /v0.43.0 /es /using-diffusers /write_own_pipeline.md
mishig's picture
|
download
raw
14.3 kB
# Comprensión de pipelines, modelos y programadores
🧨 Diffusers está diseñado para ser una caja de herramientas flexible y fácil de usar para construir sistemas de difusión adaptados a tu caso de uso. En el núcleo de la caja de herramientas se encuentran los modelos y los programadores. Si bien [DiffusionPipeline](/docs/diffusers/v0.43.0/es/api/pipelines/overview#diffusers.DiffusionPipeline) agrupa estos componentes para mayor comodidad, también puedes desagregar el pipeline y utilizar los modelos y los programadores de forma independiente para crear nuevos sistemas de difusión.
En este tutorial, aprenderás a usar modelos y programadores para ensamblar un sistema de difusión para inferencia, comenzando con un pipeline básico y luego avanzando hacia el pipeline de Stable Diffusion.
## Descomponer un pipeline básico
Un pipeline es una forma rápida y sencilla de ejecutar un modelo para inferencia, que requiere no más de cuatro líneas de código para generar una imagen:
```py
>>> from diffusers import DDPMPipeline
>>> ddpm = DDPMPipeline.from_pretrained("google/ddpm-cat-256", use_safetensors=True).to("cuda") # or "mps", "xpu", "cpu"
>>> image = ddpm(num_inference_steps=25).images[0]
>>> image
```
Fue muy fácil, pero ¿cómo lo hizo el pipeline? Desglosemos el pipeline y veamos qué ocurre por debajo.
En el ejemplo anterior, el pipeline contiene un modelo [UNet2DModel](/docs/diffusers/v0.43.0/es/api/models/unet2d#diffusers.UNet2DModel) y un [DDPMScheduler](/docs/diffusers/v0.43.0/es/api/schedulers/ddpm#diffusers.DDPMScheduler). El pipeline desruidiza una imagen tomando ruido aleatorio del tamaño de la salida deseada y pasándolo por el modelo varias veces. En cada paso de tiempo, el modelo predice el *ruido residual* y el scheduler lo utiliza para predecir una imagen con menos ruido. El pipeline repite este proceso hasta alcanzar el final del número de pasos de inferencia especificado.
Para recrear el pipeline con el modelo y el programador por separado, escribamos nuestro propio proceso de desruido.
1. Cargue el modelo y el programador:
```py
>>> from diffusers import DDPMScheduler, UNet2DModel
>>> scheduler = DDPMScheduler.from_pretrained("google/ddpm-cat-256")
>>> model = UNet2DModel.from_pretrained("google/ddpm-cat-256", use_safetensors=True).to("cuda") # or "mps", "xpu", "cpu"
```
2. Establezca el número de pasos de tiempo para ejecutar el proceso de desruido:
```py
>>> scheduler.set_timesteps(50)
```
3. Al establecer los pasos de tiempo del programador, se crea un tensor con elementos equiespaciados, 50 en este ejemplo. Cada elemento corresponde a un paso de tiempo en el que el modelo elimina el ruido de una imagen. Cuando crees el bucle de eliminación de ruido más adelante, iterarás sobre este tensor para eliminar el ruido de una imagen:
```py
>>> scheduler.timesteps
tensor([980, 960, 940, 920, 900, 880, 860, 840, 820, 800, 780, 760, 740, 720,
700, 680, 660, 640, 620, 600, 580, 560, 540, 520, 500, 480, 460, 440,
420, 400, 380, 360, 340, 320, 300, 280, 260, 240, 220, 200, 180, 160,
140, 120, 100, 80, 60, 40, 20, 0])
```
4. Cree algo de ruido aleatorio con la misma forma que la salida deseada:
```py
>>> import torch
>>> sample_size = model.config.sample_size
>>> noise = torch.randn((1, 3, sample_size, sample_size), device="cuda") # or "mps", "xpu", "cpu"
```
5. Ahora escribe un bucle para iterar sobre los pasos de tiempo. En cada paso de tiempo, el modelo realiza una pasada de [UNet2DModel.forward()](/docs/diffusers/v0.43.0/es/api/models/unet2d#diffusers.UNet2DModel.forward) y devuelve el residual ruidoso. El método [step()](/docs/diffusers/v0.43.0/es/api/schedulers/ddpm#diffusers.DDPMScheduler.step) del programador toma el residual ruidoso, el paso de tiempo y la entrada, y predice la imagen en el paso de tiempo anterior. Esta salida se convierte en la siguiente entrada para el modelo en el bucle de eliminación de ruido, y se repite hasta llegar al final del array `timesteps`.
```py
>>> input = noise
>>> for t in scheduler.timesteps:
... with torch.no_grad():
... noisy_residual = model(input, t).sample
... previous_noisy_sample = scheduler.step(noisy_residual, t, input).prev_sample
... input = previous_noisy_sample
```
Este es todo el proceso de eliminación de ruido, y puedes usar este mismo patrón para escribir cualquier sistema de difusión.
6. El último paso es convertir la salida desruidada en una imagen:
```py
>>> from PIL import Image
>>> import numpy as np
>>> image = (input / 2 + 0.5).clamp(0, 1).squeeze()
>>> image = (image.permute(1, 2, 0) * 255).round().to(torch.uint8).cpu().numpy()
>>> image = Image.fromarray(image)
>>> image
```
En la siguiente sección, pondrás a prueba tus habilidades y analizarás el pipeline de Stable Diffusion más complejo. Los pasos son más o menos los mismos. Inicializarás los componentes necesarios y establecerás el número de timesteps para crear un array `timestep`. El array `timestep` se utiliza en el bucle de denoising y, para cada elemento de este array, el modelo predice una imagen con menos ruido. El bucle de denoising itera sobre los `timestep` y, en cada timestep, genera un residual con ruido que el scheduler utiliza para predecir una imagen con menos ruido en el timestep anterior. Este proceso se repite hasta llegar al final del array `timestep`.
¡Pongámoslo a prueba!
## Descomponer el pipeline de Stable Diffusion
Stable Diffusion es un modelo de *difusión latente* de texto a imagen. Se denomina modelo de difusión latente porque trabaja con una representación de menor dimensión de la imagen en lugar del espacio de píxeles real, lo que lo hace más eficiente en memoria. El codificador comprime la imagen en una representación más pequeña y un decodificador convierte la representación comprimida de nuevo en una imagen. Para los modelos de texto a imagen, necesitarás un tokenizador y un codificador para generar incrustaciones de texto. A partir del ejemplo anterior, ya sabes que necesitas un modelo UNet y un programador.
Como puede ver, esto ya es más complejo que el pipeline de DDPM, que solo contiene un modelo UNet. El modelo Stable Diffusion tiene tres modelos preentrenados separados.
> [!TIP]
> 💡 Lee el blog [¿Cómo funciona Stable Diffusion?](https://huggingface.co/blog/stable_diffusion#how-does-stable-diffusion-work) para obtener más detalles sobre cómo funcionan los modelos VAE, UNet y text encoder.
Ahora que sabe qué necesita para el pipeline de Stable Diffusion, cargue todos estos componentes con el método [from_pretrained()](/docs/diffusers/v0.43.0/es/api/models/overview#diffusers.ModelMixin.from_pretrained). Puede encontrarlos en el checkpoint preentrenado [`stable-diffusion-v1-5/stable-diffusion-v1-5`](https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5), y cada componente se almacena en una subcarpeta separada:
```py
>>> from PIL import Image
>>> import torch
>>> from transformers import CLIPTextModel, CLIPTokenizer
>>> from diffusers import AutoencoderKL, UNet2DConditionModel, PNDMScheduler
>>> vae = AutoencoderKL.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="vae", use_safetensors=True)
>>> tokenizer = CLIPTokenizer.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="tokenizer")
>>> text_encoder = CLIPTextModel.from_pretrained(
... "CompVis/stable-diffusion-v1-4", subfolder="text_encoder", use_safetensors=True
... )
>>> unet = UNet2DConditionModel.from_pretrained(
... "CompVis/stable-diffusion-v1-4", subfolder="unet", use_safetensors=True
... )
```
En lugar del [PNDMScheduler](/docs/diffusers/v0.43.0/es/api/schedulers/pndm#diffusers.PNDMScheduler) predeterminado, cámbielo por el [UniPCMultistepScheduler](/docs/diffusers/v0.43.0/es/api/schedulers/unipc#diffusers.UniPCMultistepScheduler) para ver lo fácil que resulta incorporar un programador diferente:
```py
>>> from diffusers import UniPCMultistepScheduler
>>> scheduler = UniPCMultistepScheduler.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="scheduler")
```
Para acelerar la inferencia, mueva los modelos a una GPU ya que, a diferencia del programador, tienen pesos entrenables:
```py
>>> torch_device = "cuda" # or "mps", "xpu", "cpu"
>>> vae.to(torch_device)
>>> text_encoder.to(torch_device)
>>> unet.to(torch_device)
```
### Crear embeddings de texto
El siguiente paso es tokenizar el texto para generar embeddings. El texto se utiliza para condicionar el modelo UNet y guiar el proceso de difusión hacia algo que se asemeje al prompt de entrada.
> [!TIP]
> 💡 El parámetro `guidance_scale` determina el peso que debe otorgarse al prompt al generar una imagen.
¡Siéntete libre de elegir cualquier prompt que te guste si quieres generar algo más!
```py
>>> prompt = ["a photograph of an astronaut riding a horse"]
>>> height = 512 # default height of Stable Diffusion
>>> width = 512 # default width of Stable Diffusion
>>> num_inference_steps = 25 # Number of denoising steps
>>> guidance_scale = 7.5 # Scale for classifier-free guidance
>>> generator = torch.manual_seed(0) # Seed generator to create the initial latent noise
>>> batch_size = len(prompt)
```
Tokeniza el texto y genera los embeddings a partir del prompt:
```py
>>> text_input = tokenizer(
... prompt, padding="max_length", max_length=tokenizer.model_max_length, truncation=True, return_tensors="pt"
... )
>>> with torch.no_grad():
... text_embeddings = text_encoder(text_input.input_ids.to(torch_device))[0]
```
También deberá generar los *embeddings de texto incondicionales*, que son los embeddings para el token de relleno. Estos deben tener la misma forma (`batch_size` y `seq_length`) que los `text_embeddings` condicionales:
```py
>>> max_length = text_input.input_ids.shape[-1]
>>> uncond_input = tokenizer([""] * batch_size, padding="max_length", max_length=max_length, return_tensors="pt")
>>> uncond_embeddings = text_encoder(uncond_input.input_ids.to(torch_device))[0]
```
Concatenemos los embeddings condicionales y no condicionales en un lote para evitar realizar dos pases hacia adelante:
```py
>>> text_embeddings = torch.cat([uncond_embeddings, text_embeddings])
```
### Crear ruido aleatorio
A continuación, genera algo de ruido aleatorio inicial como punto de partida para el proceso de difusión. Esta es la representación latente de la imagen y se irá desruidando gradualmente. En este punto, la imagen `latent` es más pequeña que el tamaño final de la imagen, pero no hay problema, ya que el modelo la transformará en las dimensiones finales de 512x512 más adelante.
> [!TIP]
> 💡 La altura y el ancho se dividen entre 8 porque el modelo `vae` tiene 3 capas de muestreo descendente. Puedes verificarlo ejecutando lo siguiente:
>
> ```py
> 2 ** (len(vae.config.block_out_channels) - 1) == 8
> ```
```py
>>> latents = torch.randn(
... (batch_size, unet.config.in_channels, height // 8, width // 8),
... generator=generator,
... device=torch_device,
... )
```
### Denoise the image
Comience escalando la entrada con la distribución de ruido inicial, *sigma*, el valor de escala del ruido, que es necesario para programadores mejorados como [UniPCMultistepScheduler](/docs/diffusers/v0.43.0/es/api/schedulers/unipc#diffusers.UniPCMultistepScheduler):
```py
>>> latents = latents * scheduler.init_noise_sigma
```
El último paso es crear el bucle de desruido que transformará progresivamente el ruido puro en `latents` en una imagen descrita por tu prompt. Recuerda que el bucle de desruido debe hacer tres cosas:
1. Establece los pasos de tiempo del programador que se usarán durante el desruido.
2. Itera sobre los pasos de tiempo.
3. En cada paso de tiempo, se llama al modelo UNet para predecir el residuo de ruido y se pasa al programador para calcular la muestra ruidosa anterior.
```py
>>> from tqdm.auto import tqdm
>>> scheduler.set_timesteps(num_inference_steps)
>>> for t in tqdm(scheduler.timesteps):
... # expand the latents if we are doing classifier-free guidance to avoid doing two forward passes.
... latent_model_input = torch.cat([latents] * 2)
... latent_model_input = scheduler.scale_model_input(latent_model_input, timestep=t)
... # predict the noise residual
... with torch.no_grad():
... noise_pred = unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample
... # perform guidance
... noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
... noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
... # compute the previous noisy sample x_t -> x_t-1
... latents = scheduler.step(noise_pred, t, latents).prev_sample
```
### Decodificar la imagen
El paso final es usar `vae` para decodificar la representación latente en una imagen y obtener la salida decodificada con `sample`:
```py
# scale and decode the image latents with vae
latents = 1 / 0.18215 * latents
with torch.no_grad():
image = vae.decode(latents).sample
```
Por último, convierte la imagen a `PIL.Image` para ver la imagen generada.
```py
>>> image = (image / 2 + 0.5).clamp(0, 1).squeeze()
>>> image = (image.permute(1, 2, 0) * 255).to(torch.uint8).cpu().numpy()
>>> image = Image.fromarray(image)
>>> image
```
## Próximos pasos
Desde pipelines básicos hasta complejos, has visto que todo lo que realmente necesitas para escribir tu propio sistema de difusión es un bucle de desruido. El bucle debe establecer los pasos de tiempo del programador, iterar sobre ellos y alternar entre llamar al modelo UNet para predecir el residuo de ruido y pasarlo al programador para calcular la muestra ruidosa anterior.
Esto es exactamente para lo que está diseñado 🧨 Diffusers: para que escribir tu propio sistema de difusión utilizando modelos y programadores sea intuitivo y fácil.
Para tus próximos pasos, puedes:
* Aprende a [construir y contribuir con un pipeline](../conceptual/contribution) a 🧨 Diffusers. ¡No podemos esperar a ver en qué te inspiras!
* Explora los [pipelines existentes](../api/pipelines/overview) en la biblioteca y comprueba si puedes descomponerlos y construir un pipeline desde cero utilizando los modelos y los programadores por separado.

Xet Storage Details

Size:
14.3 kB
·
Xet hash:
32c77006d65e6b672f10f336e30278207062971a435edf3ead5df8adf202c03d

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.