0xSoul 发表于 2023-6-13 16:15:13

大型语言模型的低秩适应(LoRA)的深度解读指南

> 目前,LoRA仅支持UNet2DConditionalModel的注意力层(Attention Layer)。我们还以有限的能力支持对DreamBooth的文本编码器进行LoRA微调。一般来说,对DreamBooth的文本编码器进行微调会产生更好的结果,但会增加计算资源的使用。

[大型语言模型的低秩适应](https://arxiv.org/abs/2106.09685) 英文全称:Low-Rank Adaptation of Large Language Models,也就是我们一般所说的“LoRA”。它是一种训练方法,可以加速大型模型的训练过程同时消耗较少的内存。它通过将一对秩分解权重矩阵(称为更新矩阵)添加到现有权重中,并仅对新增的权重进行训练。这样做有几个优点:

1. 先前的预训练权重被冻结,因此模型不容易发生灾难性遗忘。
2. 秩分解矩阵(Rank-decomposition matrices)的参数数量远少于原始模型,这意味着训练得到的LoRA权重易于迁移。
3. LoRA矩阵通常添加到原始模型的注意力层中。🧨 Diffusers提供了(https://huggingface.co/docs/diffusers/v0.17.1/en/api/loaders#diffusers.loaders.UNet2DConditionLoadersMixin.load_attn_procs)方法,用于将LoRA权重加载到模型的注意力层中。您可以通过一个scale参数来控制模型向新的训练图像进行适应的程度。
4. 更高的内存效率使您能够在Tesla T4、RTX 3080甚至RTX 2080 Ti等消费级GPU上进行微调!像T4这样的GPU在Kaggle或Google Colab笔记本中是免费且易于访问的。

> 💡 LoRA不仅限于注意力层。作者发现,修改语言模型的注意力层足以在高效的情况下获得良好的下游性能。这就是为什么通常只需要将LoRA权重添加到模型的注意力层中。请查看关于[如何使用LoRA进行高效稳定扩散微调](https://huggingface.co/blog/zh/lora/)的博客文章,以获取有关LoRA工作原理的更多信息!

(https://github.com/cloneofsimo)是第一个在流行的(https://github.com/cloneofsimo/lora) GitHub存储库中尝试使用LoRA对"Stable Diffusion"进行训练的人。

🧨 Diffusers现在支持使用LoRA进行文本到图像生成和DreamBooth的微调。

本指南将向您展示如何进行这两种操作。

## Text-to-image(文生图)

对于像"Stable Diffusion"这样具有数十亿参数的模型进行微调可能会很慢且困难。而使用LoRA,微调扩散模型变得更加容易和快速。它可以在仅有11GB GPU内存的硬件上运行,而无需使用8位优化器等技巧。

## 训练

让我们对[`stable-diffusion-v1-5`](https://huggingface.co/runwayml/stable-diffusion-v1-5)模型在(https://huggingface.co/datasets/lambdalabs/pokemon-blip-captions)字幕数据集上进行微调,以生成您自己的宝可梦。

请指定MODEL_NAME环境变量(可以是Hub模型存储库ID或包含模型权重的目录路径),并将其传递给[`pretrained_model_name_or_path`](https://huggingface.co/docs/diffusers/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.from_pretrained.pretrained_model_name_or_path)参数。您还需要将DATASET_NAME环境变量设置为您想要进行训练的数据集的名称。如果要使用自己的数据集,请查看[创建用于训练的数据集指南](https://huggingface.co/docs/diffusers/training/create_dataset)。

OUTPUT_DIR和HUB_MODEL_ID变量是可选的,用于指定将模型保存到Hub上的位置:

```python
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仪表板(例如,查看此报告作为[示例](https://wandb.ai/pcuenq/text2image-fine-tune/runs/b4k1w0tn?workspace=user-pcuenq/))。
--learning_rate=1e-04,您可以使用比通常更高的学习率来使用LoRA。

现在您已准备好启动训练了(您可以在此处找到完整的训练脚本)。在具有11GB内存的2080 Ti GPU上,训练大约需要5个小时,它将创建和保存模型检查点以及pytorch_lora_weights文件到您的存储库中。

```python
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)

现在您可以通过在(https://huggingface.co/docs/diffusers/v0.17.1/en/api/pipelines/stable_diffusion/text2img#diffusers.StableDiffusionPipeline)中加载基础模型,然后加载(https://huggingface.co/docs/diffusers/v0.17.1/en/api/schedulers/multistep_dpm_solver#diffusers.DPMSolverMultistepScheduler)来使用模型进行推理:

```python
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` 之间的值会在这两个权重之间进行插值。

```python
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

image = pipe("A pokemon with blue eyes.", num_inference_steps=25, guidance_scale=7.5).images
image.save("blue_pokemon.png")
```

如果您从Hub加载LoRA参数,并且Hub存储库有一个名为`base_model`的标签(例如[这个](https://huggingface.co/sayakpaul/sd-model-finetuned-lora-t4/blob/main/README.md?code=true#L4)),那么您可以执行以下操作:

```python
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

(https://arxiv.org/abs/2208.12242)是一种微调技术,用于个性化文本到图像模型(如Stable Diffusion),以在不同环境下生成一个主题的逼真图像,只需提供该主题的几张图片。然而,DreamBooth对超参数非常敏感,很容易出现过拟合。一些重要的超参数需要考虑,包括影响训练时间的参数(学习率、训练步数)和推理时间的参数(步数、调度器类型)。

> 💡 请阅读 [使用🧨 Diffusers训练Stable Diffusion和DreamBooth](https://huggingface.co/blog/dreambooth) 博客,以获取对DreamBooth实验和推荐设置的深入分析。

### 训练

让我们使用一些🐶[狗的图像](https://drive.google.com/drive/folders/1BO_dyz-p65qhBRRMRA4TbZ8qW4rB99JZ)对[`stable-diffusion-v1-5`](https://huggingface.co/runwayml/stable-diffusion-v1-5)进行 DreamBooth 和 LoRA 的微调。请下载并将这些图像保存到一个目录中。如果要使用自己的数据集,请参阅[创建用于训练的数据集](https://huggingface.co/docs/diffusers/training/create_dataset)指南。

首先,指定 `MODEL_NAME` 环境变量(可以是 Hub 模型存储库 ID 或包含模型权重的目录路径),并将其传递给 [`pretrained_model_name_or_path`](https://huggingface.co/docs/diffusers/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.from_pretrained.pretrained_model_name_or_path) 参数。您还需要将 `INSTANCE_DIR` 设置为包含图像的目录的路径。

`OUTPUT_DIR` 变量是可选的,用于指定将模型保存到 Hub 上的位置:

```python
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 仪表板(例如,查看此[报告](https://wandb.ai/pcuenq/text2image-fine-tune/runs/b4k1w0tn?workspace=user-pcuenq))。
* `--learning_rate=1e-04`,您可以使用比通常使用的 LoRA 更高的学习率。

现在,您可以开始训练了(可以在[此处](https://github.com/huggingface/diffusers/blob/main/examples/dreambooth/train_dreambooth_lora.py)找到完整的训练脚本)。该脚本将创建并保存模型检查点和 `pytorch_lora_weights.bin` 文件到您的存储库中。

此外,还可以使用 LoRA 进行文本编码器的额外微调。在大多数情况下,这会在计算量略有增加的情况下带来更好的结果。要在启动 `train_dreambooth_lora.py` 脚本时允许对文本编码器进行 LoRA 微调,请指定 `--train_text_encoder`。

```python
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
```

### 推理

现在,您可以通过在(https://huggingface.co/docs/diffusers/v0.17.1/en/api/pipelines/stable_diffusion/text2img#diffusers.StableDiffusionPipeline)中加载基础模型来使用模型进行推断:

```python
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之间的值会在两个权重之间进行插值。

```python
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

image = pipe("A picture of a sks dog in a bucket.", num_inference_steps=25, guidance_scale=7.5).images
image.save("bucket-dog.png")
```

如果在训练过程中使用了`--train_text_encoder`,则可以使用`pipe.load_lora_weights()`来加载LoRA权重。例如:

```python
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
```

> 如果您的LoRA参数涉及UNet和文本编码器,那么通过传递`cross_attention_kwargs={"scale": 0.5}`,将会将该比例值应用于UNet和文本编码器的权重。

请注意,使用(https://huggingface.co/docs/diffusers/v0.17.1/en/api/pipelines/stable_diffusion/text2img#diffusers.StableDiffusionPipeline.load_lora_weights)来加载LoRA参数比使用(https://huggingface.co/docs/diffusers/v0.17.1/en/api/loaders#diffusers.loaders.UNet2DConditionLoadersMixin.load_attn_procs)更为推荐。这是因为(https://huggingface.co/docs/diffusers/v0.17.1/en/api/pipelines/stable_diffusion/text2img#diffusers.StableDiffusionPipeline.load_lora_weights)可以处理以下情况:

* LoRA参数没有针对UNet和文本编码器的单独标识符(例如[`"patrickvonplaten/lora_dreambooth_dog_example"`](https://huggingface.co/patrickvonplaten/lora_dreambooth_dog_example))。因此,您可以直接执行以下操作:

```python
pipe.load_lora_weights(lora_model_path)

```


* LoRA参数具有针对UNet和文本编码器的单独标识符,例如:[`"sayakpaul/dreambooth"`](https://huggingface.co/sayakpaul/dreambooth)。

请注意,可以将本地目录路径提供给(https://huggingface.co/docs/diffusers/v0.17.1/en/api/pipelines/stable_diffusion/text2img#diffusers.StableDiffusionPipeline.load_lora_weights)和(https://huggingface.co/docs/diffusers/v0.17.1/en/api/loaders#diffusers.loaders.UNet2DConditionLoadersMixin.load_attn_procs)。有关支持的输入,请参考相应的文档字符串。

## 支持从Diffusers加载A1111主题的LoRA检查点

为了为我们的用户提供与A1111的无缝互操作性,我们在有限的范围内支持使用(https://huggingface.co/docs/diffusers/v0.17.1/en/api/pipelines/stable_diffusion/text2img#diffusers.StableDiffusionPipeline.load_lora_weights)加载A1111格式的LoRA检查点。在本节中,我们将说明如何从(https://civitai.com/)加载A1111格式的LoRA检查点并在Diffusers中进行推理。

首先,下载一个检查点。我们将使用[这个](https://civitai.com/models/13239/light-and-shadow)来进行演示。

```python
wget https://civitai.com/api/download/models/15603 -O light_and_shadow.safetensors
```

接下来,我们初始化一个[~DiffusionPipeline](https://huggingface.co/docs/diffusers/v0.17.1/en/api/diffusion_pipeline#diffusers.DiffusionPipeline):

```python
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下载的检查点:

```python
pipeline.load_lora_weights(".", weight_name="light_and_shadow.safetensors")

```

> 如果您要加载safetensors格式的检查点,请确保已安装safetensors。

然后是运行推理的时候了:

```python
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结果的比较:

!(data/attachment/forum/202306/13/195351e1ro129v1o2sg9sr.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "lora_non_lora_comparison.png")


您可以直接使用(https://huggingface.co/docs/diffusers/v0.17.1/en/api/pipelines/stable_diffusion/text2img#diffusers.StableDiffusionPipeline.load_lora_weights)从Hugging Face Hub加载相似的检查点,方法如下:

```python
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)
```

嗷嗷夜 发表于 2024-6-11 11:24:41

感谢分享!

raosea 发表于 2024-8-1 11:37:20

感谢分享
页: [1]
查看完整版本: 大型语言模型的低秩适应(LoRA)的深度解读指南