目前,LoRA仅支持UNet2DConditionalModel的注意力层(Attention Layer)。我们还以有限的能力支持对DreamBooth的文本编码器进行LoRA微调。一般来说,对DreamBooth的文本编码器进行微调会产生更好的结果,但会增加计算资源的使用。
大型语言模型的低秩适应 英文全称:Low-Rank Adaptation of Large Language Models,也就是我们一般所说的“LoRA”。它是一种训练方法,可以加速大型模型的训练过程同时消耗较少的内存。它通过将一对秩分解权重矩阵(称为更新矩阵)添加到现有权重中,并仅对新增的权重进行训练。这样做有几个优点:
- 先前的预训练权重被冻结,因此模型不容易发生灾难性遗忘。
- 秩分解矩阵(Rank-decomposition matrices)的参数数量远少于原始模型,这意味着训练得到的LoRA权重易于迁移。
- LoRA矩阵通常添加到原始模型的注意力层中。🧨 Diffusers提供了load_attn_procs()方法,用于将LoRA权重加载到模型的注意力层中。您可以通过一个scale参数来控制模型向新的训练图像进行适应的程度。
- 更高的内存效率使您能够在Tesla T4、RTX 3080甚至RTX 2080 Ti等消费级GPU上进行微调!像T4这样的GPU在Kaggle或Google Colab笔记本中是免费且易于访问的。
💡 LoRA不仅限于注意力层。作者发现,修改语言模型的注意力层足以在高效的情况下获得良好的下游性能。这就是为什么通常只需要将LoRA权重添加到模型的注意力层中。请查看关于如何使用LoRA进行高效稳定扩散微调的博客文章,以获取有关LoRA工作原理的更多信息!
cloneofsimo是第一个在流行的lora GitHub存储库中尝试使用LoRA对"Stable Diffusion"进行训练的人。
🧨 Diffusers现在支持使用LoRA进行文本到图像生成和DreamBooth的微调。
本指南将向您展示如何进行这两种操作。
Text-to-image(文生图)
对于像"Stable Diffusion"这样具有数十亿参数的模型进行微调可能会很慢且困难。而使用LoRA,微调扩散模型变得更加容易和快速。它可以在仅有11GB GPU内存的硬件上运行,而无需使用8位优化器等技巧。
训练
让我们对stable-diffusion-v1-5
模型在Pokémon BLIP captions字幕数据集上进行微调,以生成您自己的宝可梦。
请指定MODEL_NAME环境变量(可以是Hub模型存储库ID或包含模型权重的目录路径),并将其传递给pretrained_model_name_or_path
参数。您还需要将DATASET_NAME环境变量设置为您想要进行训练的数据集的名称。如果要使用自己的数据集,请查看创建用于训练的数据集指南。
OUTPUT_DIR和HUB_MODEL_ID变量是可选的,用于指定将模型保存到Hub上的位置:
export MODEL_NAME="runwayml/stable-diffusion-v1-5"
export OUTPUT_DIR="/sddata/finetune/lora/pokemon"
export HUB_MODEL_ID="pokemon-lora"
export DATASET_NAME="lambdalabs/pokemon-blip-captions"
在开始训练之前,有一些标志需要注意:
--push_to_hub将训练的LoRA嵌入存储在Hub上。
--report_to=wandb将训练结果报告和日志记录到您的Weights & Biases仪表板(例如,查看此报告作为示例)。
--learning_rate=1e-04,您可以使用比通常更高的学习率来使用LoRA。
现在您已准备好启动训练了(您可以在此处找到完整的训练脚本)。在具有11GB内存的2080 Ti GPU上,训练大约需要5个小时,它将创建和保存模型检查点以及pytorch_lora_weights文件到您的存储库中。
accelerate launch --mixed_precision="fp16" train_text_to_image_lora.py \
--pretrained_model_name_or_path=$MODEL_NAME \
--dataset_name=$DATASET_NAME \
--dataloader_num_workers=8 \
--resolution=512 --center_crop --random_flip \
--train_batch_size=1 \
--gradient_accumulation_steps=4 \
--max_train_steps=15000 \
--learning_rate=1e-04 \
--max_grad_norm=1 \
--lr_scheduler="cosine" --lr_warmup_steps=0 \
--output_dir=${OUTPUT_DIR} \
--push_to_hub \
--hub_model_id=${HUB_MODEL_ID} \
--report_to=wandb \
--checkpointing_steps=500 \
--validation_prompt="A pokemon with blue eyes." \
--seed=1337
推理(Inference)
现在您可以通过在StableDiffusionPipeline中加载基础模型,然后加载DPMSolverMultistepScheduler来使用模型进行推理:
import torch
from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
model_base = "runwayml/stable-diffusion-v1-5"
pipe = StableDiffusionPipeline.from_pretrained(model_base, torch_dtype=torch.float16)
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
从您微调的模型中加载LoRA权重(在基础模型权重之上),然后将管道移动到GPU上以加快推理速度。当您将LoRA权重与冻结的预训练模型权重合并时,您可以通过scale
参数可选地调整要合并的权重比例:
💡 scale
值为 0
相当于不使用您的 LoRA 权重,只使用基础模型权重,而 scale
值为 1
表示仅使用完全微调的 LoRA 权重。在 0
和 1
之间的值会在这两个权重之间进行插值。
pipe.unet.load_attn_procs(lora_model_path)
pipe.to("cuda")
image = pipe(
"A pokemon with blue eyes.", num_inference_steps=25, guidance_scale=7.5, cross_attention_kwargs={"scale": 0.5}
).images[0]
image = pipe("A pokemon with blue eyes.", num_inference_steps=25, guidance_scale=7.5).images[0]
image.save("blue_pokemon.png")
如果您从Hub加载LoRA参数,并且Hub存储库有一个名为base_model
的标签(例如这个),那么您可以执行以下操作:
from huggingface_hub.repocard import RepoCard
lora_model_id = "sayakpaul/sd-model-finetuned-lora-t4"
card = RepoCard.load(lora_model_id)
base_model_id = card.data.to_dict()["base_model"]
pipe = StableDiffusionPipeline.from_pretrained(base_model_id, torch_dtype=torch.float16)
...
DreamBooth
DreamBooth是一种微调技术,用于个性化文本到图像模型(如Stable Diffusion),以在不同环境下生成一个主题的逼真图像,只需提供该主题的几张图片。然而,DreamBooth对超参数非常敏感,很容易出现过拟合。一些重要的超参数需要考虑,包括影响训练时间的参数(学习率、训练步数)和推理时间的参数(步数、调度器类型)。
💡 请阅读 使用🧨 Diffusers训练Stable Diffusion和DreamBooth 博客,以获取对DreamBooth实验和推荐设置的深入分析。
训练
让我们使用一些🐶狗的图像对stable-diffusion-v1-5
进行 DreamBooth 和 LoRA 的微调。请下载并将这些图像保存到一个目录中。如果要使用自己的数据集,请参阅创建用于训练的数据集指南。
首先,指定 MODEL_NAME
环境变量(可以是 Hub 模型存储库 ID 或包含模型权重的目录路径),并将其传递给 pretrained_model_name_or_path
参数。您还需要将 INSTANCE_DIR
设置为包含图像的目录的路径。
OUTPUT_DIR
变量是可选的,用于指定将模型保存到 Hub 上的位置:
export MODEL_NAME="runwayml/stable-diffusion-v1-5"
export INSTANCE_DIR="path-to-instance-images"
export OUTPUT_DIR="path-to-save-model"
在开始训练之前,有一些需要注意的标志:
--push_to_hub
将训练后的 LoRA 嵌入存储到 Hub 上。
--report_to=wandb
将训练结果报告和日志记录到您的 Weights & Biases 仪表板(例如,查看此报告)。
--learning_rate=1e-04
,您可以使用比通常使用的 LoRA 更高的学习率。
现在,您可以开始训练了(可以在此处找到完整的训练脚本)。该脚本将创建并保存模型检查点和 pytorch_lora_weights.bin
文件到您的存储库中。
此外,还可以使用 LoRA 进行文本编码器的额外微调。在大多数情况下,这会在计算量略有增加的情况下带来更好的结果。要在启动 train_dreambooth_lora.py
脚本时允许对文本编码器进行 LoRA 微调,请指定 --train_text_encoder
。
accelerate launch train_dreambooth_lora.py \
--pretrained_model_name_or_path=$MODEL_NAME \
--instance_data_dir=$INSTANCE_DIR \
--output_dir=$OUTPUT_DIR \
--instance_prompt="a photo of sks dog" \
--resolution=512 \
--train_batch_size=1 \
--gradient_accumulation_steps=1 \
--checkpointing_steps=100 \
--learning_rate=1e-4 \
--report_to="wandb" \
--lr_scheduler="constant" \
--lr_warmup_steps=0 \
--max_train_steps=500 \
--validation_prompt="A photo of sks dog in a bucket" \
--validation_epochs=50 \
--seed="0" \
--push_to_hub
推理
现在,您可以通过在StableDiffusionPipeline中加载基础模型来使用模型进行推断:
import torch
from diffusers import StableDiffusionPipeline
model_base = "runwayml/stable-diffusion-v1-5"
pipe = StableDiffusionPipeline.from_pretrained(model_base, torch_dtype=torch.float16)
将您微调的DreamBooth模型的LoRA权重加载到基础模型权重之上,然后将管道移动到GPU上以进行更快的推断。当您将LoRA权重与冻结的预训练模型权重合并时,可以使用scale参数选择要合并的权重比例:
💡 scale
值为0意味着不使用LoRA权重,只使用基础模型权重;而scale
值为1意味着只使用完全微调的LoRA权重。0和1之间的值会在两个权重之间进行插值。
pipe.unet.load_attn_procs(lora_model_path)
pipe.to("cuda")
image = pipe(
"A picture of a sks dog in a bucket.",
num_inference_steps=25,
guidance_scale=7.5,
cross_attention_kwargs={"scale": 0.5},
).images[0]
image = pipe("A picture of a sks dog in a bucket.", num_inference_steps=25, guidance_scale=7.5).images[0]
image.save("bucket-dog.png")
如果在训练过程中使用了--train_text_encoder
,则可以使用pipe.load_lora_weights()
来加载LoRA权重。例如:
from huggingface_hub.repocard import RepoCard
from diffusers import StableDiffusionPipeline
import torch
lora_model_id = "sayakpaul/dreambooth-text-encoder-test"
card = RepoCard.load(lora_model_id)
base_model_id = card.data.to_dict()["base_model"]
pipe = StableDiffusionPipeline.from_pretrained(base_model_id, torch_dtype=torch.float16)
pipe = pipe.to("cuda")
pipe.load_lora_weights(lora_model_id)
image = pipe("A picture of a sks dog in a bucket", num_inference_steps=25).images[0]
如果您的LoRA参数涉及UNet和文本编码器,那么通过传递cross_attention_kwargs={"scale": 0.5}
,将会将该比例值应用于UNet和文本编码器的权重。
请注意,使用load_lora_weights()来加载LoRA参数比使用load_attn_procs()更为推荐。这是因为load_lora_weights()可以处理以下情况:
pipe.load_lora_weights(lora_model_path)
请注意,可以将本地目录路径提供给load_lora_weights()和load_attn_procs()。有关支持的输入,请参考相应的文档字符串。
支持从Diffusers加载A1111主题的LoRA检查点
为了为我们的用户提供与A1111的无缝互操作性,我们在有限的范围内支持使用load_lora_weights()加载A1111格式的LoRA检查点。在本节中,我们将说明如何从CivitAI加载A1111格式的LoRA检查点并在Diffusers中进行推理。
首先,下载一个检查点。我们将使用这个来进行演示。
wget [https://civitai.com/api/download/models/15603](https://civitai.com/api/download/models/15603) -O light_and_shadow.safetensors
接下来,我们初始化一个~DiffusionPipeline:
import torch
from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
pipeline = StableDiffusionPipeline.from_pretrained(
"gsdf/Counterfeit-V2.5", torch_dtype=torch.float16, safety_checker=None
).to("cuda")
pipeline.scheduler = DPMSolverMultistepScheduler.from_config(
pipeline.scheduler.config, use_karras_sigmas=True
)
然后,我们加载从CivitAI下载的检查点:
pipeline.load_lora_weights(".", weight_name="light_and_shadow.safetensors")
如果您要加载safetensors格式的检查点,请确保已安装safetensors。
然后是运行推理的时候了:
prompt = "masterpiece, best quality, 1girl, at dusk"
negative_prompt = ("(low quality, worst quality:1.4), (bad anatomy), (inaccurate limb:1.2), "
"bad composition, inaccurate eyes, extra digit, fewer digits, (extra arms:1.2), large breasts")
images = pipeline(prompt=prompt,
negative_prompt=negative_prompt,
width=512,
height=768,
num_inference_steps=15,
num_images_per_prompt=4,
generator=torch.manual_seed(0)
).images
以下是LoRA和非LoRA结果的比较:
您可以直接使用load_lora_weights()从Hugging Face Hub加载相似的检查点,方法如下:
lora_model_id = "sayakpaul/civitai-light-shadow-lora"
lora_filename = "light_and_shadow.safetensors"
pipeline.load_lora_weights(lora_model_id, weight_name=lora_filename)