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)は、元の重みを凍結し、新しいパラメータの小さなセットを学習させることで、事前学習済みモデルを新しいタスクに適応させます。適応はフルファインチューニングよりも高速かつ低コストであるため、新しいスタイルでの画像生成のようなタスクに実用的です。

LoRAのチェックポイントは通常、数百MB程度です。pipeline.load_lora_weightsで読み込み、weight_nameにチェックポイントのファイル名を指定します。共通のパラメータについては、load_lora_weights()を参照してください。

テキストから画像
テキストから動画へ
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]

このページの pipeline LoRA API は、モデルファミリーを問わず同じように動作します。デノイジングモジュールは、Stable DiffusionおよびSDXLではpipeline.unet、Flux、Stable Diffusion 3、AuraFlow、LTXなどの DiT 系モデルではpipeline.transformerです。CogVideoX、HunyuanVideo、Wanなどの動画モデルにも同じパターンが適用されます。モデルレベルの PEFT メソッドでは、この属性と対応するprefix("unet"または"transformer")を使用します。

pipeline.load_lora_weights は、LoRA 重みをデノイザー(UNet または transformer)およびテキストエンコーダーに読み込むための推奨方法です。共通のパラメータについては load_lora_weights() を参照してください。以下のケースに対応できます。

  • チェックポイントは、デノイザーとテキストエンコーダー間でキーを共有します
  • チェックポイントは、個別のデノイザーとテキストエンコーダーのキーを使用しています。

load_lora_adapter() メソッドは、UNet や transformer など、PeftAdapterMixin をサブクラス化する任意の Diffusers モデルにおいて、モデルレベルで LoRA アダプターを読み込みます。アダプターの設定を構築・準備し、そのモジュールに重みをロードします。

prefix を使用して、対象モジュール("unet" または "transformer")の state dict キーをフィルタリングします。デフォルトは "transformer" であるため、Stable Diffusion および SDXL では 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 は、PyTorch モデルをコンパイルして最適化されたカーネルを使用することで、推論を高速化します。コンパイルの前に、LoRA 重みをベースモデルに融合し、その後アンロードする必要があります。

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()

通常、パイプラインの中で最も計算負荷が高いコンポーネントであるため、denoiserをコンパイルします。

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]

コンパイル済みモデルと複数のLoRAを扱う際に再コンパイルを回避する方法については、hotswappingセクションを参照してください。

重みスケール

set_adapters() の adapter_weights パラメータを使用して、適用する LoRA の量を制御します。重みが 0 の場合はベースモデルの重みのみを使用する場合に相当し、1 の場合は LoRA を完全に使用する場合に相当します。

スカラー重み
コンポーネントごとのスケール

adapter_weights にスカラーを渡し、set_adapters() で 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 を主要なパターンとして使用しますが、一部の pipeline は呼び出し時に cross_attention_kwargs={"scale": 1.0} を受け入れ、アテンションのスケーリングを行う副次的な方法として機能します。クロスアテンションを使用しない pipeline は、代わりに attention_kwargs を公開している場合が多いです(Flux と Stable Diffusion 3 では joint_attention_kwargs )。

スケジューリングのスケーリング

サンプリング中にLoRAスケールを動的に調整することで、特定のステップではスケールの増加または減少がより効果的になるため、全体の構成やレイアウトをより適切に制御できます。

以下の例では、character LoRAはより高いスケールから始まり、最初の20ステップにかけて徐々に減衰してキャラクター生成を確立します。その後のステップでは、LoRAが学習されていない画像の他の部分にLoRAの特徴を過度に追加することを避けるため、スケールは0.2のみが適用されます。

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]

ホットスワップ

LoRAのホットスワップは、load_lora_weightsを複数回呼び出すことによるメモリ蓄積を回避し、モデルがコンパイルされている場合によっては再コンパイルも回避できるため、複数のLoRAを扱う効率的な方法です。このワークフローには、既存のロード済み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",
    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"
)

テキストエンコーダーを対象とするLoRAでは、ホットスワップはサポートされていません。

load_lora_weights で hotswap=True を設定すると、2番目の LoRA をスワップできます。スワップする LoRA を指定するには adapter_name パラメータを使用します。adapter_name を省略して読み込む場合、Diffusers はアダプターに default_0、default_1 のような名前を付けます。名前付きアダプターの方が明示的にターゲットを指定しやすくなります。

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

コンパイル済みモデル

コンパイル済みモデルでは、LoRA のホットスワップ時に再コンパイルを回避するために enable_lora_hotswap() を使用します。このメソッドは最初の LoRA をロードする前に呼び出す必要があり、torch.compile は最初の LoRA をロードする後に呼び出します。

2番目のLoRAが1番目のLoRAと同一のLoRAランクとスケールを対象とする場合、enable_lora_hotswap()メソッドは必ずしも必要ではありません。

enable_lora_hotswap() 内で、読み込む予定のアダプターの中で最も高いランクを target_rank に設定します(デフォルトは 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"
)

with torch._dynamo.config.patch(error_on_recompile=True) コンテキストマネージャー内にコードを移動し、モデルが再コンパイルされたかどうかを検出してください。上記の手順をすべて実行したにもかかわらずモデルが再コンパイルされる場合は、再現可能な例を添えてissueを作成してください。

この機能を使用する際に、推論中に解像度が変化する可能性がある場合は、コンパイル時にdynamic=Trueを設定してください。詳細については、動的な形状のコンパイルを参照してください。

再コンパイルが避けられないシナリオがまだ存在します。例えば、ホットスワップされた LoRA が初期アダプターよりも多くのレイヤーを対象としている場合です。最も多くのレイヤーを対象とする LoRA を最初に読み込むようにしてください。この制約の詳細については、PEFT のhotswappingドキュメントを参照してください。

Why `target_rank` and load order matter

enable_lora_hotswap() メソッドは、LoRA のスケーリング係数を float から torch.tensors に変換し、重みのデータが置き換えられた際に属性全体を再割り当てすることを避けるため、重みの形状を必要な最大形状にパディングします。

これがtarget_rank引数が重要である理由です。値をゼロでパディングしても、結果は変わりません。ただし、パディングサイズによっては計算が遅くなる場合があります。

新しい LoRA 属性が追加されないため、後続の各 LoRA は、最初の LoRA が対象とするレイヤー、またはその部分集合のみを対象とすることが許可されます。LoRA の読み込み順序の選択は重要であり、LoRA が互いに異なるレイヤーを対象としている場合、すべての対象レイヤーの和集合を対象とするダミーの LoRA が作成されてしまう可能性があります。

コンパイル済みモデルでの LoRA ホットスワップの詳細については、LoRA fast のブログ記事をご参照ください。また、実装の詳細については、hotswap.py ファイルをご覧ください。

マージ

各LoRAの重みをマージすることで、複数の既存スタイルのブレンドを生成できます。LoRAのマージにはいくつかの方法があり、それぞれが重みをマージする方法が異なります(生成品質に影響する可能性があります)。

set_adapters

set_adapters() メソッドは、重み付き行列を連結することにより LoRA をマージします。set_adapters() に LoRA の名前を渡し、adapter_weights パラメータを使用して各 LoRA のスケーリングを制御します。例えば、adapter_weights=[0.5, 0.5] の場合、出力は両方の LoRA の平均になります。

各マージ済み LoRA を適用する量を制御するには、set_adapters() で adapter_weights を優先的に使用してください。詳細は Weight scale セクションを参照してください。

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

これは実験的なメソッドであり、詳細については PEFT の Model merging を参照してください。この統合の動機や設計について興味がある場合は、この issue をご覧ください。

add_weighted_adapter メソッドにより、TIES や DARE のようなより効率的なマージ手法を有効にできます。これらのマージ手法は、マージ後のモデルから冗長なパラメータや、干渉を引き起こす可能性のあるパラメータを除去します。マージするには、LoRA のランクが同一である必要があります。以下の解説では、SDXL の UNet を使用します。

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",
    dtype=torch.float16,
    use_safetensors=True,
    variant="fp16",
    subfolder="unet",
).to("cuda")  # or "mps", "xpu", "cpu"

パイプラインを読み込み、UNetを渡して、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"
)

最初に読み込んだ UNet と、パイプラインの LoRA UNet を組み合わせることで、LoRA チェックポイントから 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)

このプロセスを繰り返し、2番目の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 pipeline.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",
    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"
)

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",
    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

fuse_lora() メソッドは、LoRA の重みを、基盤モデルの元のデノイザー(UNet または transformer)およびテキストエンコーダーの重みに直接融合します。これにより、各 LoRA ごとに基盤モデルを読み込むオーバーヘッドが削減され、モデルが一度だけ読み込まれるため、メモリ使用量が低減し、推論速度が向上します。

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])

fuse_lora() を呼び出して、それらを融合します。lora_scale パラメータは、LoRA 重みによって出力をどの程度スケーリングするかを制御します。パイプラインでは cross_attention_kwargs に scale を渡しても動作しないため、ここでこの調整を行うことが重要です。

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

LoRA の重みは基盤モデルにすでに融合されているため、アンロードします。融合されたパイプラインは、ローカルに保存するには save_pretrained() を、Hub に保存するには ~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", 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]

lora_scale の値を変更したい場合など、元のモデルの重みを復元するには unfuse_lora() を使用します。アンフュージョンは、フュージョンされた LoRA が 1 つしかない場合のみ可能です。例えば、上記のパイプラインでは複数の 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",
    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

pipeline.unet または pipeline.transformer に読み込んだのと同じモジュールで、save_lora_adapter() を使用してアダプターを保存します。

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

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 を返します。辞書のキーは、unet や transformer、およびテキストエンコーダーなど、読み込まれたモジュールと一致します。

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にアップロードできます。
Update on GitHub