Primeros pasos con Hugging Face Diffusers
Configura Hugging Face Diffusers con SDXL, genera imágenes, ajusta parámetros, reduce el uso de memoria de la GPU y crea una API FastAPI para tu app JavaScript.
Hugging Face Diffusers es una biblioteca de Python para ejecutar modelos de difusión de imagen, video y audio en tu propio hardware. Agrupa el codificador de texto, el scheduler, la red de eliminación de ruido y el VAE en un único objeto pipeline al que llamas con un prompt.
Las API de imágenes alojadas son fáciles para empezar. Después llegan las facturas por imagen, los límites de tasa ralentizan tu prototipo y quieres saber qué está haciendo realmente el modelo. Ejecutar el modelo en local resuelve los tres problemas, pero la mayoría de los tutoriales dan por hecho que ya conoces PyTorch.
Esta guía está escrita para desarrolladores de JavaScript. Te lleva desde la instalación hasta tu primera imagen, explica los parámetros que importan, muestra cómo hacer que el modelo quepa en GPU más pequeñas y termina con un endpoint de FastAPI al que tu frontend llama con fetch(). Los ejemplos están pensados para Diffusers v0.42.1.
Puntos clave
- Carga
stabilityai/stable-diffusion-xl-base-1.0conDiffusionPipeline.from_pretrained(..., dtype=torch.float16)y muévelo a"cuda"en GPU NVIDIA o a"mps"en Apple Silicon. - SDXL base 1.0 se publica bajo la CreativeML Open RAIL++-M License. Léela antes de distribuir imágenes generadas.
- Un
torch.Generatorcon semilla hace que los resultados sean reproducibles, de modo que puedes cambiar un parámetro cada vez y ver su efecto. - Si el modelo no cabe en la memoria de la GPU, llama primero a
enable_model_cpu_offload(). La documentación de Diffusers describe el offload secuencial a CPU como extremadamente lento. - Carga el pipeline una sola vez al iniciar el servidor y reutilízalo en cada solicitud. Nunca lo cargues dentro de un manejador de solicitudes.
¿Qué es Hugging Face Diffusers?
Diffusers es la biblioteca de Hugging Face para modelos de difusión. Su abstracción central es DiffusionPipeline, que agrupa los componentes que necesita un modelo:
- uno o más codificadores de texto que convierten el prompt en embeddings
- un scheduler que controla el algoritmo de eliminación de ruido
- una UNet o un transformer de difusión que predice el ruido en cada paso
- un VAE que decodifica el resultado en píxeles
Si eres nuevo en el Hub, en Inference Providers o en Spaces, lee primero A Quick Guide to Hugging Face for Developers. Si quieres hacer inferencia sin ningún backend en Python, Transformers.js ejecuta modelos directamente en el navegador, aunque cubre un conjunto de modelos distinto.
¿Qué hardware necesita Diffusers?
Diffusers funciona en tres tipos de máquina. Una GPU NVIDIA es, con diferencia, la opción más rápida; un Mac con Apple Silicon funciona mediante el backend "mps" de PyTorch, y una CPU sin más funciona, pero es muy lenta.
| Hardware | Cadena de dispositivo | dtype | Qué esperar |
|---|---|---|---|
| GPU NVIDIA | "cuda" | torch.float16 | Rápido |
| Mac con Apple Silicon | "mps" | torch.float16 | Funciona, más lento que una GPU NVIDIA dedicada |
| Solo CPU | "cpu" | torch.float32 | Funciona, muy lento |
El Quickstart enumera "cuda", "mps", "xpu" y "cpu" como dispositivos de destino. Diffusers también tiene una guía específica para Apple Silicon (MPS). Este helper elige el dispositivo y el dtype adecuados para la máquina actual:
import torch
if torch.cuda.is_available():
device, dtype = "cuda", torch.float16
elif torch.backends.mps.is_available():
device, dtype = "mps", torch.float16
else:
device, dtype = "cpu", torch.float32
Instalar Diffusers y generar una primera imagen
Generar una primera imagen con Hugging Face Diffusers requiere tres pasos: instalar Diffusers con PyTorch, cargar SDXL y, después, llamar al pipeline con un prompt y guardar .images[0].
Desde la v0.42.0, Diffusers requiere Python 3.11 o superior y se prueba con PyTorch 2.6 o superior. Sigue la guía de instalación para configurar un entorno virtual y, a continuación, instala los paquetes. Los codificadores de texto de SDXL son modelos CLIP de Transformers, así que instala también esa biblioteca:
uv pip install "diffusers[torch]>=0.42.1" transformers
# or: pip install "diffusers[torch]>=0.42.1" transformers
Usando el device y el dtype del helper anterior, genera una imagen:
import torch
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
dtype=dtype,
).to(device)
prompt = "isometric pixel-art coffee shop at dusk, warm window light, rain on the street"
image = pipeline(prompt).images[0]
image.save("first.png")
image es una imagen PIL, por lo que .save() escribe un PNG en disco. El id del modelo es el nombre completo del checkpoint en el Hub, incluido el prefijo de organización stabilityai/.
Licencia: SDXL base 1.0 se publica bajo la CreativeML Open RAIL++-M License. Léela antes de usar imágenes generadas en un producto y revisa la licencia de cualquier otro modelo que utilices en su lugar.
¿Qué parámetros de SDXL son los más importantes?
Cuatro argumentos del pipeline de SDXL controlan la mayor parte de lo que ves en el resultado: num_inference_steps, guidance_scale, generator y height / width. Fija primero la semilla y luego cambia un argumento cada vez.
generator = torch.Generator().manual_seed(42)
image = pipeline(
prompt,
num_inference_steps=30,
guidance_scale=7.0,
height=1024,
width=1024,
generator=generator,
).images[0]
num_inference_stepsestablece cuántas pasadas de eliminación de ruido se ejecutan. Más pasos tardan más y suelen añadir detalle, hasta que las mejoras se estancan.guidance_scaleestablece con qué fidelidad sigue el modelo el prompt. Los valores más altos se ciñen más al texto, y los valores demasiado altos pueden dar un aspecto sobreprocesado.generatorfija el ruido inicial. Con el mismo modelo, los mismos parámetros y el mismo hardware, un generador con semilla vuelve a producir la misma imagen.height/widthestablecen el tamaño de salida en píxeles. Las imágenes más grandes necesitan más memoria y más tiempo.
La referencia del pipeline de SDXL enumera todos los argumentos junto con su valor predeterminado.
Cambiar el scheduler
El scheduler decide cómo se elimina el ruido en cada paso, y los distintos schedulers equilibran velocidad y calidad de forma diferente. Reemplázalo en un pipeline ya cargado con from_config(), pasándole la configuración existente para que los parámetros que corresponden al checkpoint sigan alineados:
from diffusers import DPMSolverMultistepScheduler
pipeline.scheduler = DPMSolverMultistepScheduler.from_config(pipeline.scheduler.config)
generator = torch.Generator().manual_seed(42)
image = pipeline(prompt, num_inference_steps=20, generator=generator).images[0]
La guía de Schedulers usa exactamente este patrón con SDXL. La referencia de DPMSolverMultistepScheduler indica que produce muestras de alta calidad en unos 20 pasos. Mantén la semilla fija al comparar schedulers, para que el scheduler sea lo único que cambie entre las dos imágenes.
¿Cómo hacer que SDXL quepa en GPU más pequeñas?
Si SDXL da errores de falta de memoria (out-of-memory), prueba estas cuatro soluciones de memoria de Diffusers en orden: offload del modelo a CPU, tiling del VAE, slicing del VAE y cuantización a 4 bits. Cada paso ahorra más memoria y cuesta más velocidad.
- Offload del modelo a CPU. Omite
.to(device)y llama apipeline.enable_model_cpu_offload(). Cada modelo completo permanece en la GPU solo mientras se ejecuta. - Tiling del VAE.
pipeline.vae.enable_tiling()decodifica las imágenes grandes por mosaicos (tiles). La guía de memoria señala que esto puede provocar ligeras variaciones de tono entre mosaicos. - Slicing del VAE.
pipeline.vae.enable_slicing()decodifica un lote imagen por imagen. Solo ayuda cuando generas varias imágenes en una única llamada. - Cuantización a 4 bits. Instala
bitsandbytesy añade una configuración breve:
from diffusers.quantizers import PipelineQuantizationConfig
quant_config = PipelineQuantizationConfig(
quant_backend="bitsandbytes_4bit",
quant_kwargs={"load_in_4bit": True, "bnb_4bit_quant_type": "nf4", "bnb_4bit_compute_dtype": torch.float16},
components_to_quantize=["unet"],
)
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
dtype=torch.float16,
quantization_config=quant_config,
)
pipeline.enable_model_cpu_offload()
La descripción general de cuantización documenta PipelineQuantizationConfig y sus backends. Diffusers añadió la cuantización con bitsandbytes en MPS en la v0.39.0, por lo que este enfoque no se limita a CUDA. En Apple Silicon, bitsandbytes necesita PyTorch 2.9 o superior, un requisito mayor que la base de PyTorch 2.6 mencionada antes.
Evita enable_sequential_cpu_offload() salvo que ninguna otra opción funcione. Es la que más memoria ahorra, pero la guía de memoria la califica de extremadamente lenta. Además, un pipeline cargado con device_map debe llamar a reset_device_map() antes de usar .to() o cualquiera de los dos métodos de offload.
Llamar a Hugging Face Diffusers desde una aplicación web
Para usar Diffusers desde un frontend, envuelve el pipeline en un pequeño servidor HTTP. El servidor debe cargar el modelo una sola vez al arrancar y devolver los bytes del PNG. Instala fastapi y uvicorn y, a continuación, guarda esto como main.py:
import io, threading
from contextlib import asynccontextmanager
import torch
from diffusers import DiffusionPipeline
from fastapi import FastAPI, Response
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
state = {}
lock = threading.Lock()
@asynccontextmanager
async def lifespan(app: FastAPI):
state["pipe"] = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16
).to("cuda") # or "mps"
yield
state.clear()
app = FastAPI(lifespan=lifespan)
app.add_middleware(CORSMiddleware, allow_origins=["http://localhost:5173"],
allow_methods=["POST"], allow_headers=["*"])
class GenerateRequest(BaseModel):
prompt: str
seed: int | None = None
@app.post("/generate")
def generate(req: GenerateRequest):
gen = torch.Generator().manual_seed(req.seed) if req.seed is not None else None
with lock:
image = state["pipe"](req.prompt, generator=gen).images[0]
buf = io.BytesIO()
image.save(buf, format="PNG")
return Response(content=buf.getvalue(), media_type="image/png")
Ejecútalo con uvicorn main:app. El código se basa en tres funcionalidades de FastAPI:
- El manejador lifespan carga el modelo una sola vez, antes de que el servidor acepte ninguna solicitud.
from_pretrained()es demasiado lento para llamarlo en cada solicitud. - Como
generatees una funcióndefnormal, FastAPI la ejecuta en un threadpool en lugar de bloquear el event loop. - El lock hace que las solicitudes usen la GPU de una en una.
CORSMiddleware permite que el origen de tu frontend de desarrollo llame al endpoint.
En el frontend, usa fetch() para solicitar la imagen, lee el cuerpo como un Blob y muéstralo con URL.createObjectURL():
async function generate(prompt, seed, img) {
const res = await fetch("http://localhost:8000/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt, seed }),
});
if (!res.ok) throw new Error(`Generation failed: ${res.status}`);
if (img.src.startsWith("blob:")) URL.revokeObjectURL(img.src);
img.src = URL.createObjectURL(await res.blob());
}
La generación tarda segundos, no milisegundos. Muestra un estado de carga, desactiva el botón de envío mientras haya una solicitud en curso y pasa una señal de AbortController si los usuarios pueden cancelar. Antes de servir los PNG generados a visitantes reales, redúcelos con el compresor de imágenes de OpenReplay.
Próximos pasos
Una vez cargado el pipeline en el dispositivo adecuado, Diffusers necesita muy poco código: un id de modelo, un dtype, un prompt y una semilla. Genera una primera imagen con SDXL, fija la semilla y ajusta un parámetro cada vez. Si te quedas sin memoria, aplica los pasos de offload y cuantización en orden. Después, coloca el endpoint de FastAPI detrás de tu frontend. A partir de ahí, la guía de memoria y la guía de Schedulers cubren las concesiones que surgen al optimizar para producción.
Preguntas frecuentes
¿Dónde almacena Diffusers los pesos de los modelos descargados?
from_pretrained() guarda los archivos del modelo en la caché del Hugging Face Hub y los reutiliza en ejecuciones posteriores, por lo que no vuelve a descargarlos. La caché se encuentra por defecto en ~/.cache/huggingface/hub. Define la variable de entorno HF_HUB_CACHE para mover solo la caché de modelos. Define HF_HOME para mover todos los datos de Hugging Face, lo que sitúa la caché en HF_HOME/hub. También puedes pasar un argumento cache_dir para elegir una ubicación en una carga concreta.
¿Por qué la misma semilla produce una imagen distinta en otra máquina?
Una semilla solo reproduce una imagen por completo en el mismo hardware y con la misma versión de PyTorch. Las GPU CUDA usan un generador de números aleatorios distinto al de las CPU, y la multiplicación de matrices en GPU es menos determinista. La guía de reproducibilidad de Diffusers recomienda pasar un generador de CPU cuando los resultados deban coincidir, que es lo que crea torch.Generator() por defecto. Aun así, no garantiza un resultado idéntico entre distintos modelos de GPU o versiones de PyTorch.
¿Funciona la cuantización a 4 bits con bitsandbytes en un Mac con Apple Silicon?
Sí, con versiones recientes. Diffusers añadió la cuantización con bitsandbytes en MPS en la v0.39.0, y bitsandbytes 0.50.0 hizo que todas sus configuraciones de 4 bits y LLM.int8() funcionaran en MPS. La ruta MPS de bitsandbytes necesita PyTorch 2.9 o superior, un requisito mayor que el mínimo de PyTorch 2.6 de Diffusers. En macOS 26 y posteriores, instalar el paquete kernels habilita kernels de Metal más rápidos.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k