找回密码
 立即注册
查看: 962|回复: 0

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

[复制链接]

3868

主题

36

回帖

4268

积分

管理员

积分
4268
发表于 2023-6-13 16:15:13 | 显示全部楼层 |阅读模式

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

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

  1. 先前的预训练权重被冻结,因此模型不容易发生灾难性遗忘。
  2. 秩分解矩阵(Rank-decomposition matrices)的参数数量远少于原始模型,这意味着训练得到的LoRA权重易于迁移。
  3. LoRA矩阵通常添加到原始模型的注意力层中。🧨 Diffusers提供了load_attn_procs()方法,用于将LoRA权重加载到模型的注意力层中。您可以通过一个scale参数来控制模型向新的训练图像进行适应的程度。
  4. 更高的内存效率使您能够在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 权重。在 01 之间的值会在这两个权重之间进行插值。

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

lora_non_lora_comparison.png

您可以直接使用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)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|金房子

GMT+8, 2024-5-20 01:33 , Processed in 0.040255 second(s), 20 queries .

© 2023 金房子|AI发烧友社区

快速回复 返回顶部 返回列表