Diffusers 文档
LoRA
并获得增强的文档体验
开始使用
LoRA
LoRA (低秩适应) 是一种快速训练模型以适应新任务的方法。它通过冻结原始模型权重并添加少量*新的*可训练参数来实现。这意味着将现有模型适应新任务(例如以新样式生成图像)的速度和成本都大大降低。
LoRA 检查点通常只有几百 MB 大小,因此它们非常轻便且易于存储。使用 load_lora_weights() 将这些较小的权重集加载到现有基础模型中,并指定文件名。
import torch
from diffusers import AutoPipelineForText2Image
pipeline = AutoPipelineForText2Image.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16
).to("cuda")
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]
load_lora_weights() 方法是加载 LoRA 权重到 UNet 和文本编码器的首选方法,因为它能够处理以下情况:
- LoRA 权重没有单独的 UNet 和文本编码器标识符
- LoRA 权重有单独的 UNet 和文本编码器标识符
load_lora_adapter() 方法用于直接在**模型级别**加载 LoRA 适配器,只要该模型是 `PeftAdapterMixin` 的子类 Diffusers 模型。它会构建并准备适配器所需的模型配置。此方法还会将 LoRA 适配器加载到 UNet 中。
例如,如果您只将 LoRA 加载到 UNet 中,load_lora_adapter() 会忽略文本编码器键。使用 `prefix` 参数过滤并加载适当的状态字典,例如设置为 `"unet"`。
import torch
from diffusers import AutoPipelineForText2Image
pipeline = AutoPipelineForText2Image.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16
).to("cuda")
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]
torch.compile
torch.compile 通过编译 PyTorch 模型以使用优化内核来加速推理。在编译之前,LoRA 权重需要先融合到基础模型中并卸载。
import torch
from diffusers import DiffusionPipeline
# load base model and LoRA
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16
).to("cuda")
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()
通常,UNet 会被编译,因为它是管道中计算量最大的组件。
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]
请参阅热插拔部分,了解如何在处理已编译模型和多个 LoRA 时避免重新编译。
权重缩放
`scale` 参数用于控制 LoRA 的应用程度。值为 `0` 等同于仅使用基础模型权重,值为 `1` 等同于完全使用 LoRA。
对于简单用例,您可以将 `cross_attention_kwargs={"scale": 1.0}` 传递给管道。
import torch
from diffusers import AutoPipelineForText2Image
pipeline = AutoPipelineForText2Image.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16
).to("cuda")
pipeline.load_lora_weights(
"ostris/super-cereal-sdxl-lora",
weight_name="cereal_box_sdxl_v1.safetensors",
adapter_name="cereal"
)
pipeline("bears, pizza bites", cross_attention_kwargs={"scale": 1.0}).images[0]
缩放调度
在采样过程中动态调整 LoRA 比例可以更好地控制整体构图和布局,因为某些步骤可能更受益于增加或减少的比例。
下面示例中的角色 LoRA 以较高的比例开始,并在前 20 步逐渐衰减以建立角色生成。在后续步骤中,仅应用 0.2 的比例以避免向 LoRA 未训练的图像其他部分添加过多的 LoRA 特征。
import torch
from diffusers import FluxPipeline
pipeline = FluxPipeline.from_pretrained(
"black-forest-labs/FLUX.1-dev", torch_dtype=torch.bfloat16
).to("cuda")
pipelne.load_lora_weights("alvarobartt/ghibli-characters-flux-lora", "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]
热插拔
热插拔 LoRA 是一种高效处理多个 LoRA 的方式,同时避免了多次调用 load_lora_weights() 造成的内存累积,在某些情况下,如果模型已编译,还能避免重新编译。此工作流程需要加载一个 LoRA,因为新的 LoRA 权重会原地替换现有的已加载 LoRA。
import torch
from diffusers import DiffusionPipeline
# load base model and LoRAs
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16
).to("cuda")
pipeline.load_lora_weights(
"ostris/ikea-instructions-lora-sdxl",
weight_name="ikea_instructions_xl_v1_5.safetensors",
adapter_name="ikea"
)
热插拔不支持以文本编码器为目标的 LoRA。
在 load_lora_weights() 中设置 `hotswap=True` 以交换第二个 LoRA。使用 `adapter_name` 参数指示要交换哪个 LoRA (默认名称为 `default_0`)。
pipeline.load_lora_weights(
"lordjia/by-feng-zikai",
hotswap=True,
adapter_name="ikea"
)
编译模型
对于已编译的模型,使用 enable_lora_hotswap() 以避免在热插拔 LoRA 时重新编译。此方法应在加载第一个 LoRA *之前* 调用,并且 `torch.compile` 应在加载第一个 LoRA *之后* 调用。
如果第二个 LoRA 的目标与第一个 LoRA 的 LoRA 等级和比例相同,则 enable_lora_hotswap() 方法并不总是必需的。
在 enable_lora_hotswap() 中,`target_rank` 参数对于设置所有 LoRA 适配器的秩非常重要。将其设置为 `max_rank` 会将其设置为最高值。对于具有不同秩的 LoRA,您应将其设置为更高的秩值。默认秩值为 128。
import torch
from diffusers import DiffusionPipeline
# load base model and LoRAs
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16
).to("cuda")
# 1. enable_lora_hotswap
pipeline.enable_lora_hotswap(target_rank=max_rank)
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"
)
将您的代码移动到 `with torch._dynamo.config.patch(error_on_recompile=True)` 上下文管理器中,以检测模型是否已重新编译。如果模型尽管遵循上述所有步骤仍被重新编译,请提供可复现的示例并提出issue。
在某些情况下,重新编译仍然不可避免,例如当热插拔的 LoRA 目标层多于初始适配器时。尝试*首先*加载目标层最多的 LoRA。有关此限制的更多详细信息,请参阅 PEFT 热插拔文档。
合并
每个 LoRA 的权重可以合并在一起,以产生多种现有风格的混合。有几种合并 LoRA 的方法,每种方法在权重合并方式上有所不同(可能会影响生成质量)。
set_adapters
set_adapters() 方法通过连接加权矩阵来合并 LoRA。将 LoRA 名称传递给 set_adapters() 并使用 `adapter_weights` 参数控制每个 LoRA 的缩放。例如,如果 `adapter_weights=[0.5, 0.5]`,则输出是两个 LoRA 的平均值。
`"scale"` 参数决定了合并后的 LoRA 应用的程度。有关更多详细信息,请参阅权重缩放部分。
import torch
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16
).to("cuda")
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", cross_attention_kwargs={"scale": 1.0}).images[0]

add_weighted_adapter
`~peft.LoraModel.add_weighted_adapter` 方法支持更高效的合并方法,例如 TIES 或 DARE。这些合并方法会从合并模型中移除冗余和可能相互干扰的参数。请记住,LoRA 秩需要相同才能合并。
确保安装了最新稳定版本的 Diffusers 和 PEFT。
pip install -U -q diffusers peft
加载与 LoRA UNet 对应的 UNET。
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",
torch_dtype=torch.float16,
use_safetensors=True,
variant="fp16",
subfolder="unet",
).to("cuda")
加载一个管道,将 UNet 传递给它,然后加载一个 LoRA。
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
variant="fp16",
torch_dtype=torch.float16,
unet=unet
).to("cuda")
pipeline.load_lora_weights(
"ostris/ikea-instructions-lora-sdxl",
weight_name="ikea_instructions_xl_v1_5.safetensors",
adapter_name="ikea"
)
通过组合您加载的第一个 UNet 和管道中的 LoRA UNet,从 LoRA 检查点创建 `~peft.PeftModel`。
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)
您可以保存并重新使用 `ikea_peft_model`,如下所示将其推送到 Hub。
ikea_peft_model.push_to_hub("ikea_peft_model", token=TOKEN)
重复此过程并为第二个 LoRA 创建 `~peft.PeftModel`。
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 pipe.unet.state_dict().items()}
feng_peft_model.load_state_dict(original_state_dict, strict=True)
加载基础 UNet 模型并加载适配器。
base_unet = AutoModel.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16,
use_safetensors=True,
variant="fp16",
subfolder="unet",
).to("cuda")
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"
)
使用 `~peft.LoraModel.add_weighted_adapter` 合并 LoRA,并使用 `combination_type` 指定合并方式。以下示例使用 `"dare_linear"` 方法(请参阅此博客文章了解更多关于这些合并方法的信息),它会随机剪枝一些权重,然后根据 `weights` 中每个 LoRA 的设定权重对张量执行加权和。
使用 set_adapters() 激活合并后的 LoRA。
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",
torch_dtype=torch.float16,
).to("cuda")
pipeline("A bowl of ramen shaped like a cute kawaii bear, by Feng Zikai").images[0]

fuse_lora
fuse_lora() 方法将 LoRA 权重直接与底层模型的原始 UNet 和文本编码器权重融合。这减少了每次 LoRA 加载底层模型的开销,因为它只加载模型一次,从而降低了内存使用并提高了推理速度。
import torch
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16
).to("cuda")
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])
调用 fuse_lora() 将它们融合。`lora_scale` 参数控制 LoRA 权重对输出的缩放程度。现在进行此调整很重要,因为将 `scale` 传递给 `cross_attention_kwargs` 在管道中不起作用。
pipeline.fuse_lora(adapter_names=["ikea", "feng"], lora_scale=1.0)
由于 LoRA 权重已与底层模型融合,因此卸载它们。使用 save_pretrained() 将融合后的管道保存到本地,或使用 `~PushToHubMixin.push_to_hub` 将其保存到 Hub。
pipeline.unload_lora_weights()
pipeline.save_pretrained("path/to/fused-pipeline")
融合后的管道现在可以快速加载进行推理,而无需单独加载每个 LoRA。
pipeline = DiffusionPipeline.from_pretrained(
"username/fused-ikea-feng", torch_dtype=torch.float16,
).to("cuda")
pipeline("A bowl of ramen shaped like a cute kawaii bear, by Feng Zikai").images[0]
使用 `unfuse_lora()` 恢复底层模型权重,例如,如果您想使用不同的 `lora_scale` 值。只有在融合了单个 LoRA 时才能取消融合。例如,它不适用于上述管道,因为存在多个融合的 LoRA。在这种情况下,您需要重新加载整个模型。
pipeline.unfuse_lora()

管理
Diffusers 提供了多种方法来帮助您管理 LoRA 的使用。这些方法在您处理多个 LoRA 时特别有用。
set_adapters
set_adapters() 也激活当前要使用的 LoRA,如果存在多个活动 LoRA。这允许您通过指定其名称在不同的 LoRA 之间切换。
import torch
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16
).to("cuda")
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
使用 save_lora_adapter() 保存适配器。
import torch
from diffusers import AutoPipelineForText2Image
pipeline = AutoPipelineForText2Image.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16
).to("cuda")
pipeline.unet.load_lora_adapter(
"jbilcke-hf/sdxl-cinematic-1",
weight_name="pytorch_lora_weights.safetensors",
adapter_name="cinematic"
prefix="unet"
)
pipeline.save_lora_adapter("path/to/save", adapter_name="cinematic")
unload_lora_weights
unload_lora_weights() 方法会卸载管道中的所有 LoRA 权重,以恢复底层模型权重。
pipeline.unload_lora_weights()
disable_lora
disable_lora() 方法会禁用所有 LoRA(但它们仍保留在管道中),并将管道恢复为底层模型权重。
pipeline.disable_lora()
get_active_adapters
get_active_adapters() 方法返回附加到管道的活动 LoRA 列表。
pipeline.get_active_adapters()
["cereal", "ikea"]
get_list_adapters
get_list_adapters() 方法返回管道中每个组件的活动 LoRA。
pipeline.get_list_adapters()
{"unet": ["cereal", "ikea"], "text_encoder_2": ["cereal"]}
delete_adapters
delete_adapters() 方法将 LoRA 及其层从模型中完全移除。
pipeline.delete_adapters("ikea")
资源
浏览 LoRA Studio 以使用不同的 LoRA,或者您可以通过下面的 Space 将您最喜欢的 Civitai LoRA 上传到 Hub。
您可以在 FLUX LoRA the Explorer 和 LoRA the Explorer Space 中找到其他 LoRA。
< > 在 GitHub 上更新