Diffusers 文档
合并 LoRA
并获得增强的文档体验
开始使用
合并 LoRA
一起使用多个 LoRA 来生成全新且独特的内容可能既有趣又富有创造性。其工作原理是将多个 LoRA 权重合并在一起,以生成混合了不同风格的图像。Diffusers 提供了几种合并 LoRA 的方法,具体取决于您希望如何合并它们的权重,这可能会影响图像质量。
本指南将向您展示如何使用 set_adapters() 和 add_weighted_adapter 方法合并 LoRA。为了提高推理速度并减少合并 LoRA 的内存使用量,您还将了解如何使用 fuse_lora()
方法将 LoRA 权重与底层模型的原始权重融合。
对于本指南,请加载 Stable Diffusion XL (SDXL) checkpoint 以及 KappaNeuro/studio-ghibli-style 和 Norod78/sdxl-chalkboarddrawing-lora LoRA,使用 load_lora_weights() 方法。您需要为每个 LoRA 分配一个 adapter_name
以便稍后组合它们。
from diffusers import DiffusionPipeline
import torch
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")
set_adapters
set_adapters() 方法通过连接其加权矩阵来合并 LoRA adapters。使用 adapter 名称来指定要合并的 LoRA,并使用 adapter_weights
参数来控制每个 LoRA 的缩放比例。例如,如果 adapter_weights=[0.5, 0.5]
,则合并后的 LoRA 输出是两个 LoRA 的平均值。尝试调整 adapter 权重,看看它如何影响生成的图像!
pipeline.set_adapters(["ikea", "feng"], adapter_weights=[0.7, 0.8])
generator = torch.manual_seed(0)
prompt = "A bowl of ramen shaped like a cute kawaii bear, by Feng Zikai"
image = pipeline(prompt, generator=generator, cross_attention_kwargs={"scale": 1.0}).images[0]
image

add_weighted_adapter
这是一个实验性方法,它将 PEFT 的 add_weighted_adapter 方法添加到 Diffusers,以实现更高效的合并方法。如果您有兴趣了解有关此集成的动机和设计的更多信息,请查看此 issue。
add_weighted_adapter 方法提供了对更高效的合并方法(如 TIES 和 DARE)的访问。要使用这些合并方法,请确保您已安装最新稳定版本的 Diffusers 和 PEFT。
pip install -U diffusers peft
使用 add_weighted_adapter 方法合并 LoRA 需要三个步骤
- 从底层模型和 LoRA checkpoint 创建 PeftModel。
- 加载基础 UNet 模型和 LoRA adapters。
- 使用 add_weighted_adapter 方法和您选择的合并方法合并 adapters。
让我们更深入地了解这些步骤的含义。
- 加载与 LoRA checkpoint 中的 UNet 相对应的 UNet。在本例中,两个 LoRA 都使用 SDXL UNet 作为其基础模型。
from diffusers import UNet2DConditionModel
import torch
unet = UNet2DConditionModel.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16,
use_safetensors=True,
variant="fp16",
subfolder="unet",
).to("cuda")
加载 SDXL pipeline 和 LoRA checkpoints,从 ostris/ikea-instructions-lora-sdxl LoRA 开始。
from diffusers import DiffusionPipeline
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")
现在,您将通过组合 SDXL UNet 和 pipeline 中的 LoRA UNet,从加载的 LoRA checkpoint 创建一个 PeftModel。
from peft import get_peft_model, LoraConfig
import copy
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.push_to_hub("ikea_peft_model", token=TOKEN)
将 ikea_peft_model 推送到 Hub。
重复此过程,从 lordjia/by-feng-zikai LoRA 创建一个 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 模型,然后将 adapters 加载到其上。
from peft import PeftModel
base_unet = UNet2DConditionModel.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")
- 使用 add_weighted_adapter 方法和您选择的合并方法(在此 博客文章 中了解有关其他合并方法的更多信息)合并 adapters。对于本示例,让我们使用
"dare_linear"
方法来合并 LoRA。
请记住,LoRA 需要具有相同的 rank 才能合并!
model.add_weighted_adapter(
adapters=["ikea", "feng"],
weights=[1.0, 1.0],
combination_type="dare_linear",
adapter_name="ikea-feng"
)
model.set_adapters("ikea-feng")
现在,您可以使用合并后的 LoRA 生成图像。
model = model.to(dtype=torch.float16, device="cuda")
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0", unet=model, variant="fp16", torch_dtype=torch.float16,
).to("cuda")
image = pipeline("A bowl of ramen shaped like a cute kawaii bear, by Feng Zikai", generator=torch.manual_seed(0)).images[0]
image

fuse_lora
set_adapters() 和 add_weighted_adapter 方法都需要分别加载基础模型和 LoRA adapters,这会产生一些开销。fuse_lora() 方法允许您将 LoRA 权重直接与底层模型的原始权重融合。这样,您只需加载一次模型,即可提高推理速度并降低内存使用量。
您可以使用 PEFT 通过 fuse_lora() 方法轻松地将多个 adapters 直接融合/解除融合到模型权重中(UNet 和文本编码器),这可以加快推理速度并降低 VRAM 使用率。
例如,如果您有一个基础模型和 adapters 已加载并设置为活动状态,并且具有以下 adapter 权重
from diffusers import DiffusionPipeline
import torch
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 融合到 UNet 中。lora_scale
参数控制使用 LoRA 权重缩放输出的程度。在 fuse_lora() 方法中进行 lora_scale
调整非常重要,因为如果您尝试将 scale
传递给 pipeline 中的 cross_attention_kwargs
,它将不起作用。
pipeline.fuse_lora(adapter_names=["ikea", "feng"], lora_scale=1.0)
然后,您应该使用 unload_lora_weights() 来卸载 LoRA 权重,因为它们已经与底层基础模型融合。最后,调用 save_pretrained() 以在本地保存融合的 pipeline,或者您可以调用 push_to_hub() 以将融合的 pipeline 推送到 Hub。
pipeline.unload_lora_weights()
# save locally
pipeline.save_pretrained("path/to/fused-pipeline")
# save to the Hub
pipeline.push_to_hub("fused-ikea-feng")
现在,您可以快速加载融合的 pipeline 并将其用于推理,而无需单独加载 LoRA adapters。
pipeline = DiffusionPipeline.from_pretrained(
"username/fused-ikea-feng", torch_dtype=torch.float16,
).to("cuda")
image = pipeline("A bowl of ramen shaped like a cute kawaii bear, by Feng Zikai", generator=torch.manual_seed(0)).images[0]
image
您可以调用 ~~loaders.lora_base.LoraBaseMixin.unfuse_lora
来恢复模型的原始权重(例如,如果您想使用不同的 lora_scale
值)。但是,这仅在您只将一个 LoRA adapter 融合到原始模型时才有效。如果您融合了多个 LoRA,则需要重新加载模型。
pipeline.unfuse_lora()
torch.compile
torch.compile 可以更快地加速您的 pipeline,但必须先融合 LoRA 权重,然后再卸载。通常,UNet 会被编译,因为它是 pipeline 中计算量非常大的组件。
from diffusers import DiffusionPipeline
import torch
# 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")
pipeline.load_lora_weights("lordjia/by-feng-zikai", weight_name="fengzikai_v1.0_XL.safetensors", adapter_name="feng")
# activate both LoRAs and set adapter weights
pipeline.set_adapters(["ikea", "feng"], adapter_weights=[0.7, 0.8])
# fuse LoRAs and unload weights
pipeline.fuse_lora(adapter_names=["ikea", "feng"], lora_scale=1.0)
pipeline.unload_lora_weights()
# torch.compile
pipeline.unet.to(memory_format=torch.channels_last)
pipeline.unet = torch.compile(pipeline.unet, mode="reduce-overhead", fullgraph=True)
image = pipeline("A bowl of ramen shaped like a cute kawaii bear, by Feng Zikai", generator=torch.manual_seed(0)).images[0]
在 加速文本到图像扩散模型的推理 指南中了解有关 torch.compile 的更多信息。
下一步
有关每种合并方法如何工作的更多概念细节,请查看 🤗 PEFT 欢迎新的合并方法 博客文章!
< > 在 GitHub 上更新