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 上更新