Diffusers documentation

LoRA

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

LoRA

LoRA (Low-Rank Adaptation) adapta un modelo preentrenado a una nueva tarea congelando los pesos originales y entrenando un pequeño conjunto de nuevos parámetros. La adaptación es más rápida y económica que el ajuste fino completo, lo que la hace práctica para tareas como generar imágenes en un nuevo estilo.

Los puntos de control de LoRA suelen ser de solo unos cientos de MB. Cargalos con pipeline.load_lora_weights y establece weight_name como el nombre del archivo del punto de control. Consulta load_lora_weights() para los parámetros compartidos.

texto a imagen
texto a video
import torch
from diffusers import AutoPipelineForText2Image

pipeline = AutoPipelineForText2Image.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.load_lora_weights(
    "ostris/super-cereal-sdxl-lora",
    weight_name="cereal_box_sdxl_v1.safetensors",
    adapter_name="cereal"
)
pipeline("bears, pizza bites").images[0]

Las APIs de pipeline LoRA de esta página funcionan de la misma manera en todas las familias de modelos. El módulo de desruido es pipeline.unet en Stable Diffusion y SDXL, y pipeline.transformer en modelos de estilo DiT como Flux, Stable Diffusion 3, AuraFlow y LTX. El mismo patrón se aplica a modelos de video como CogVideoX, HunyuanVideo y Wan. Los métodos PEFT a nivel de modelo utilizan ese atributo y un prefix correspondiente ("unet" o "transformer").

pipeline.load_lora_weights es la forma preferida para cargar los pesos de LoRA en el denoiser (UNet o transformer) y en los codificadores de texto. Consulte load_lora_weights() para los parámetros compartidos. Puede manejar casos en los que:

  • el checkpoint comparte claves entre el denoiser y los codificadores de texto
  • el checkpoint utiliza claves separadas para el denoiser y el text-encoder

El método load_lora_adapter() carga un adaptador LoRA a nivel de modelo en cualquier modelo de Diffusers que herede de PeftAdapterMixin, como un UNet o un transformer. Construye y prepara la configuración del adaptador y carga los pesos en ese módulo.

Use prefix para filtrar las claves del state dict del módulo objetivo ("unet" o "transformer"). El valor predeterminado es "transformer", por lo que Stable Diffusion y SDXL deben pasar prefix="unet".

import torch
from diffusers import AutoPipelineForText2Image

pipeline = AutoPipelineForText2Image.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.unet.load_lora_adapter(
    "jbilcke-hf/sdxl-cinematic-1",
    weight_name="pytorch_lora_weights.safetensors",
    adapter_name="cinematic",
    prefix="unet"
)
# use cnmt in the prompt to trigger the LoRA
pipeline("A cute cnmt eating a slice of pizza, stunning color scheme, masterpiece, illustration").images[0]
import torch
from diffusers import FluxPipeline

pipeline = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev", dtype=torch.bfloat16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.transformer.load_lora_adapter(
    "alvarobartt/ghibli-characters-flux-lora",
    adapter_name="lora",
    prefix="transformer",
)

torch.compile

torch.compile acelera la inferencia al compilar el modelo de PyTorch para usar kernels optimizados. Antes de compilar, es necesario fusionar los pesos de LoRA en el modelo base y descargarlos primero.

import torch
from diffusers import DiffusionPipeline

# load base model and LoRA
pipeline = DiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.load_lora_weights(
    "ostris/ikea-instructions-lora-sdxl",
    weight_name="ikea_instructions_xl_v1_5.safetensors",
    adapter_name="ikea"
)

# activate LoRA and set adapter weight
pipeline.set_adapters("ikea", adapter_weights=0.7)

# fuse LoRAs and unload weights
pipeline.fuse_lora(adapter_names=["ikea"], lora_scale=1.0)
pipeline.unload_lora_weights()

Por lo general, se compila el denoiser, ya que es el componente más intensivo en cómputo del pipeline.

unet
transformer
pipeline.unet.to(memory_format=torch.channels_last)
pipeline.unet = torch.compile(pipeline.unet, mode="reduce-overhead", fullgraph=True)

pipeline("A bowl of ramen shaped like a cute kawaii bear").images[0]

Consulte la sección hotswapping para aprender a evitar la recompilación al trabajar con modelos compilados y múltiples LoRAs.

Escala de pesos

Use the parameter adapter_weights en set_adapters() para controlar cuánto de un LoRA se aplica. Un peso de 0 equivale a usar únicamente los pesos del modelo base y un peso de 1 equivale a usar completamente el LoRA.

peso escalar
escalas por componente

Pasa un escalar a adapter_weights en set_adapters() para controlar la intensidad con la que se aplica LoRA.

import torch
from diffusers import AutoPipelineForText2Image

pipeline = AutoPipelineForText2Image.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.load_lora_weights(
    "ostris/super-cereal-sdxl-lora",
    weight_name="cereal_box_sdxl_v1.safetensors",
    adapter_name="cereal"
)
pipeline.set_adapters("cereal", adapter_weights=1.0)
pipeline("bears, pizza bites").images[0]

set_adapters como patrón principal, aunque algunas tuberías también aceptan cross_attention_kwargs={"scale": 1.0} en la llamada como forma secundaria de escalar la atención. Las tuberías que no utilizan atención cruzada suelen exponer attention_kwargs en su lugar (o joint_attention_kwargs en Flux y Stable Diffusion 3).

Escalar la programación

Ajustar dinámicamente la escala de LoRA durante el muestreo te da un mejor control sobre la composición y el diseño generales, ya que ciertos pasos pueden beneficiarse más de una escala aumentada o reducida.

El LoRA de personaje en el ejemplo siguiente comienza con una escala más alta que decae gradualmente durante los primeros 20 pasos para establecer la generación del personaje. En los pasos posteriores, solo se aplica una escala de 0.2 para evitar añadir demasiadas características del LoRA a otras partes de la imagen en las que el LoRA no fue entrenado.

import torch
from diffusers import FluxPipeline

pipeline = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev", dtype=torch.bfloat16
).to("cuda")  # or "mps", "xpu", "cpu"

pipeline.load_lora_weights("alvarobartt/ghibli-characters-flux-lora", adapter_name="lora")

num_inference_steps = 30
lora_steps = 20
lora_scales = torch.linspace(1.5, 0.7, lora_steps).tolist()
lora_scales += [0.2] * (num_inference_steps - lora_steps + 1)

pipeline.set_adapters("lora", lora_scales[0])

def callback(pipeline: FluxPipeline, step: int, timestep: torch.LongTensor, callback_kwargs: dict):
    pipeline.set_adapters("lora", lora_scales[step + 1])
    return callback_kwargs

prompt = """
Ghibli style The Grinch, a mischievous green creature with a sly grin, peeking out from behind a snow-covered tree while plotting his antics, 
in a quaint snowy village decorated for the holidays, warm light glowing from cozy homes, with playful snowflakes dancing in the air
"""
pipeline(
    prompt=prompt,
    guidance_scale=3.0,
    num_inference_steps=num_inference_steps,
    generator=torch.Generator().manual_seed(42),
    callback_on_step_end=callback,
).images[0]

Hotswapping

El intercambio de LoRAs (hotswapping) es una forma eficiente de trabajar con varios LoRAs mientras se evita la acumulación de memoria derivada de múltiples llamadas a load_lora_weights y, en algunos casos, la recompilación, si el modelo está compilado. Este flujo de trabajo requiere un LoRA cargado, ya que los nuevos pesos del LoRA se intercambian en el lugar del LoRA existente que ya está cargado.

import torch
from diffusers import DiffusionPipeline

# load base model and LoRAs
pipeline = DiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.load_lora_weights(
    "ostris/ikea-instructions-lora-sdxl",
    weight_name="ikea_instructions_xl_v1_5.safetensors",
    adapter_name="ikea"
)

El intercambio en caliente no es compatible con los LoRA que apuntan al codificador de texto.

Establezca hotswap=True en load_lora_weights para intercambiar el segundo LoRA. Use el parámetro adapter_name para indicar qué LoRA intercambiar. Si omite adapter_name al cargar, Diffusers nombra los adaptadores como default_0, default_1, y así sucesivamente. Los adaptadores con nombre son más fáciles de especificar explícitamente.

pipeline.load_lora_weights(
    "lordjia/by-feng-zikai",
    hotswap=True,
    adapter_name="ikea"
)

Modelos compilados

Para los modelos compilados, use enable_lora_hotswap() para evitar la recompilación al intercambiar LoRAs en caliente. Este método debe llamarse antes de cargar el primer LoRA y torch.compile debe llamarse después de cargar el primer LoRA.

El método enable_lora_hotswap() no siempre es necesario si el segundo LoRA apunta a los mismos rangos y escalas de LoRA que el primer LoRA.

Dentro de enable_lora_hotswap(), establece target_rank en el rango más alto entre los adaptadores que planeas cargar (predeterminado 128).

import torch
from diffusers import DiffusionPipeline

# load base model and LoRAs
pipeline = DiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
# 1. enable_lora_hotswap
pipeline.enable_lora_hotswap(target_rank=128)
pipeline.load_lora_weights(
    "ostris/ikea-instructions-lora-sdxl",
    weight_name="ikea_instructions_xl_v1_5.safetensors",
    adapter_name="ikea"
)
# 2. torch.compile
pipeline.unet = torch.compile(pipeline.unet, mode="reduce-overhead", fullgraph=True)

# 3. hotswap
pipeline.load_lora_weights(
    "lordjia/by-feng-zikai",
    hotswap=True,
    adapter_name="ikea"
)

Mueve tu código dentro del gestor de contexto with torch._dynamo.config.patch(error_on_recompile=True) para detectar si un modelo se recompiló. Si un modelo se recompila a pesar de seguir todos los pasos anteriores, abre un issue con un ejemplo reproducible.

Si espera resoluciones variadas durante la inferencia con esta función, asegúrese de establecer dynamic=True durante la compilación. Consulte compilación de formas dinámicas para más detalles.

Aún existen escenarios en los que la recompilación es inevitable, como cuando el LoRA intercambiado en caliente apunta a más capas que el adaptador inicial. Intente cargar primero el LoRA que apunta a la mayor cantidad de capas. Para más detalles sobre esta limitación, consulte la documentación de hotswapping de PEFT.

Why `target_rank` and load order matter

El método enable_lora_hotswap() convierte el factor de escalado de LoRA de flotantes a torch.tensors y rellena la forma de los pesos hasta la forma más grande requerida para evitar reasignar todo el atributo cuando se reemplaza los datos en los pesos.

Por eso el argumento target_rank es importante. Los resultados no cambian incluso cuando los valores se rellenan con ceros. Sin embargo, el cálculo puede ser más lento dependiendo del tamaño del relleno.

Dado que no se añaden nuevos atributos de LoRA, cada LoRA posterior solo puede apuntar a las mismas capas, o a un subconjunto de las capas, a las que apunta el primer LoRA. La elección del orden de carga de los LoRA es importante, ya que si los LoRA apuntan a capas disjuntas, es posible que termine creando un LoRA ficticio que apunte a la unión de todas las capas objetivo.

Consulte la publicación de blog LoRA fast para obtener más información sobre el intercambio de LoRA con modelos compilados, y para más detalles de implementación, eche un vistazo al archivo hotswap.py.

Fusionar

Los pesos de cada LoRA pueden combinarse para producir una mezcla de varios estilos existentes. Existen varios métodos para combinar LoRAs, cada uno de los cuales difiere en cómo se combinan los pesos (lo cual puede afectar la calidad de la generación).

set_adapters

El método set_adapters() fusiona LoRAs concatenando sus matrices ponderadas. Pasa los nombres de los LoRA a set_adapters() y usa el parámetro adapter_weights para controlar la escala de cada LoRA. Por ejemplo, si adapter_weights=[0.5, 0.5], la salida es un promedio de ambos LoRAs.

Prefer adapter_weights en set_adapters() para controlar cuánto de cada LoRA fusionado se aplica. Consulte la sección Escala de pesos para más detalles.

import torch
from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.load_lora_weights(
    "ostris/ikea-instructions-lora-sdxl",
    weight_name="ikea_instructions_xl_v1_5.safetensors",
    adapter_name="ikea"
)
pipeline.load_lora_weights(
    "lordjia/by-feng-zikai",
    weight_name="fengzikai_v1.0_XL.safetensors",
    adapter_name="feng"
)
pipeline.set_adapters(["ikea", "feng"], adapter_weights=[0.7, 0.8])
# use by Feng Zikai to activate the lordjia/by-feng-zikai LoRA
pipeline("A bowl of ramen shaped like a cute kawaii bear, by Feng Zikai").images[0]

add_weighted_adapter

Este es un método experimental y puedes consultar la fusión de modelos de PEFT para obtener más detalles. Echa un vistazo a esta incidencia si te interesa la motivación y el diseño detrás de esta integración.

El método add_weighted_adapter permite métodos de fusión más eficientes como TIES o DARE. Estos métodos de fusión eliminan parámetros redundantes y potencialmente interferentes de los modelos fusionados. Tenga en cuenta que los rangos de LoRA deben tener rangos idénticos para poder fusionarse. El recorrido a continuación utiliza el UNet de SDXL.

Asegúrate de que la última versión estable de Diffusers y PEFT esté instalada.

pip install -U -q diffusers peft

Cargue un UNet que corresponda al UNet de LoRA.

import copy
import torch
from diffusers import AutoModel, DiffusionPipeline
from peft import get_peft_model, LoraConfig, PeftModel

unet = AutoModel.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16,
    use_safetensors=True,
    variant="fp16",
    subfolder="unet",
).to("cuda")  # or "mps", "xpu", "cpu"

Carga un pipeline, pásale el UNet y carga un LoRA.

pipeline = DiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    variant="fp16",
    dtype=torch.float16,
    unet=unet
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.load_lora_weights(
    "ostris/ikea-instructions-lora-sdxl",
    weight_name="ikea_instructions_xl_v1_5.safetensors",
    adapter_name="ikea"
)

Cree un PeftModel a partir del checkpoint de LoRA combinando el primer UNet que cargó con el UNet de LoRA del pipeline.

sdxl_unet = copy.deepcopy(unet)
ikea_peft_model = get_peft_model(
    sdxl_unet,
    pipeline.unet.peft_config["ikea"],
    adapter_name="ikea"
)

original_state_dict = {f"base_model.model.{k}": v for k, v in pipeline.unet.state_dict().items()}
ikea_peft_model.load_state_dict(original_state_dict, strict=True)

Puedes guardar y reutilizar el ikea_peft_model subiéndolo al Hub, como se muestra a continuación.

ikea_peft_model.push_to_hub("ikea_peft_model", token=TOKEN)

Repite este proceso y crea un PeftModel para el segundo LoRA.

pipeline.delete_adapters("ikea")
sdxl_unet.delete_adapters("ikea")

pipeline.load_lora_weights(
    "lordjia/by-feng-zikai",
    weight_name="fengzikai_v1.0_XL.safetensors",
    adapter_name="feng"
)
pipeline.set_adapters(adapter_names="feng")

feng_peft_model = get_peft_model(
    sdxl_unet,
    pipeline.unet.peft_config["feng"],
    adapter_name="feng"
)

original_state_dict = {f"base_model.model.{k}": v for k, v in pipeline.unet.state_dict().items()}
feng_peft_model.load_state_dict(original_state_dict, strict=True)

Carga un modelo UNet base y carga los adaptadores.

base_unet = AutoModel.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16,
    use_safetensors=True,
    variant="fp16",
    subfolder="unet",
).to("cuda")  # or "mps", "xpu", "cpu"

model = PeftModel.from_pretrained(
    base_unet,
    "stevhliu/ikea_peft_model",
    use_safetensors=True,
    subfolder="ikea",
    adapter_name="ikea"
)
model.load_adapter(
    "stevhliu/feng_peft_model",
    use_safetensors=True,
    subfolder="feng",
    adapter_name="feng"
)

Fusiona los LoRAs con add_weighted_adapter y especifica cómo deseas fusionarlos con combination_type. El ejemplo de abajo utiliza el método "dare_linear" (consulta esta publicación en el blog para obtener más información sobre estos métodos de fusión), que recorta aleatoriamente algunos pesos y luego realiza una suma ponderada de los tensores en función del peso asignado a cada LoRA en weights.

Active los LoRAs fusionados con set_adapters().

model.add_weighted_adapter(
    adapters=["ikea", "feng"],
    combination_type="dare_linear",
    weights=[1.0, 1.0],
    adapter_name="ikea-feng"
)
model.set_adapters("ikea-feng")

pipeline = DiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    unet=model,
    variant="fp16",
    dtype=torch.float16,
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline("A bowl of ramen shaped like a cute kawaii bear, by Feng Zikai").images[0]

fuse_lora

El método fuse_lora() fusiona los pesos de LoRA directamente con los pesos del denoiser original (UNet o transformer) y del codificador de texto del modelo subyacente. Esto reduce la sobrecarga de cargar el modelo subyacente para cada LoRA, ya que solo carga el modelo una vez, lo que disminuye el uso de memoria y aumenta la velocidad de inferencia.

import torch
from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.load_lora_weights(
    "ostris/ikea-instructions-lora-sdxl",
    weight_name="ikea_instructions_xl_v1_5.safetensors",
    adapter_name="ikea"
)
pipeline.load_lora_weights(
    "lordjia/by-feng-zikai",
    weight_name="fengzikai_v1.0_XL.safetensors",
    adapter_name="feng"
)
pipeline.set_adapters(["ikea", "feng"], adapter_weights=[0.7, 0.8])

Llama a fuse_lora() para fusionarlos. El parámetro lora_scale controla cuánto se escala la salida con los pesos de LoRA. Es importante realizar este ajuste ahora porque pasar scale a cross_attention_kwargs no funcionará en el pipeline.

pipeline.fuse_lora(adapter_names=["ikea", "feng"], lora_scale=1.0)

Descarga los pesos de LoRA, ya que ya están fusionados con el modelo subyacente. Guarda el pipeline fusionado con save_pretrained() para guardarlo localmente o con ~PushToHubMixin.push_to_hub para guardarlo en el Hub.

guardar localmente
guardar en Hub
pipeline.unload_lora_weights()
pipeline.save_pretrained("path/to/fused-pipeline")

Ahora, el pipeline fusionado se puede cargar rápidamente para inferencia sin necesidad de cargar cada LoRA por separado.

pipeline = DiffusionPipeline.from_pretrained(
    "username/fused-ikea-feng", dtype=torch.float16,
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline("A bowl of ramen shaped like a cute kawaii bear, by Feng Zikai").images[0]

Usa unfuse_lora() para restaurar los pesos del modelo subyacente, por ejemplo, si deseas usar un valor diferente de lora_scale. Solo puedes desfusionar si hay un único LoRA fusionado. Por ejemplo, no funcionará con el pipeline de arriba porque hay múltiples LoRAs fusionados. En estos casos, necesitarás recargar el modelo completo.

pipeline.unfuse_lora()

Gestionar

Diffusers ofrece varios métodos para ayudarte a gestionar el trabajo con LoRAs. Estos métodos pueden ser especialmente útiles si trabajas con varios LoRAs.

set_adapters

set_adapters() también activa el LoRA actual para usarlo si hay varios LoRAs activos. Esto permite cambiar entre diferentes LoRAs especificando su nombre.

import torch
from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.load_lora_weights(
    "ostris/ikea-instructions-lora-sdxl",
    weight_name="ikea_instructions_xl_v1_5.safetensors",
    adapter_name="ikea"
)
pipeline.load_lora_weights(
    "lordjia/by-feng-zikai",
    weight_name="fengzikai_v1.0_XL.safetensors",
    adapter_name="feng"
)
# activates the feng LoRA instead of the ikea LoRA
pipeline.set_adapters("feng")

save_lora_adapter

Guarda un adaptador con save_lora_adapter() en el mismo módulo que cargaste en (pipeline.unet o pipeline.transformer).

import torch
from diffusers import AutoPipelineForText2Image

pipeline = AutoPipelineForText2Image.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    dtype=torch.float16
).to("cuda")  # or "mps", "xpu", "cpu"
pipeline.unet.load_lora_adapter(
    "jbilcke-hf/sdxl-cinematic-1",
    weight_name="pytorch_lora_weights.safetensors",
    adapter_name="cinematic",
    prefix="unet"
)
pipeline.unet.save_lora_adapter("path/to/save", adapter_name="cinematic")

unload_lora_weights

El método unload_lora_weights() descarga los pesos de LoRA en el pipeline para restaurar los pesos del modelo subyacente.

pipeline.unload_lora_weights()

disable_lora

El método disable_lora() desactiva todos los LoRA (aunque se mantienen en el pipeline) y restaura el pipeline a los pesos del modelo subyacente.

pipeline.disable_lora()

get_active_adapters

El método get_active_adapters() devuelve una lista de los LoRAs activos adjuntos a un pipeline.

pipeline.get_active_adapters()
# ["cereal", "ikea"]

get_list_adapters

El método get_list_adapters() devuelve todos los LoRAs cargados para cada componente del pipeline. Las claves del diccionario coinciden con los módulos cargados, como unet o transformer y cualquier codificador de texto.

pipeline.get_list_adapters()
# {"unet": ["cereal", "ikea"], "text_encoder_2": ["cereal"]}

delete_adapters

El método delete_adapters() elimina por completo un LoRA y sus capas de un modelo.

pipeline.delete_adapters("ikea")

Próximos pasos

  • Explora el LoRA Studio para ver los diferentes LoRAs disponibles o puedes subir tus LoRAs favoritos de Civitai al Hub con el Space de abajo.
Update on GitHub