全世界的 LoRA 训练脚本,联合起来!
一份由社区贡献的指南,介绍了一些用于 SD-XL Dreambooth LoRA 微调的 SOTA 实践
摘要
我们将 Replicate 的 SDXL Cog 训练器中使用的 Pivotal Tuning 技术与 Kohya 训练器中使用的 Prodigy 优化器(以及一系列其他优化)相结合,在训练用于 SDXL 的 Dreambooth LoRA 上取得了非常好的效果。 在 diffusers 上查看训练脚本🧨。 在 Colab 上尝试。
如果你想跳过技术讨论,你可以使用本博客中的所有技术,并在 Hugging Face Spaces 上通过简单的 UI 和精选的参数(你也可以调整)进行训练。
概述
使用 LoRA dreambooth 微调的 Stable Diffusion XL (SDXL) 模型,仅用少量图片就能捕捉新概念,取得了令人难以置信的效果,同时保持了 SDXL 的美学和图像质量,并且需要的计算和资源相对较少。点击此处查看一些出色的 SDXL LoRA。
在本博客中,我们将回顾一些流行的实践和技巧,让你的 LoRA 微调飞速提升,并展示如何立即使用 diffusers 运行或训练你的模型!
回顾:LoRA (Low-Rank Adaptation) 是一种用于 Stable Diffusion 模型的微调技术,它对图像和提示相交的关键交叉注意力层进行微小调整。它能达到与完整微调模型相当的质量,同时速度更快,计算需求更少。要了解更多关于 LoRA 工作原理的信息,请参阅我们之前的文章 - 使用 LoRA 进行高效的 Stable Diffusion 微调。
目录
- 技术/技巧
- Pivotal tuning
- 自适应优化器
- 推荐实践 - 文本编码器学习率、自定义描述、数据集重复、min snr gamma、训练集创建
- 实验设置与结果
- 推理
致谢 ❤️:本指南中展示的技术——算法、训练脚本、实验和探索——的灵感和构建都基于 Nataniel Ruiz 的贡献:Dreambooth,Rinon Gal 的贡献:Textual Inversion,Ron Mokady 的贡献:Pivotal Tuning,Simo Ryu 的贡献:cog-sdxl,Kohya 的贡献:sd-scripts,The Last Ben 的贡献:fast-stable-diffusion。我们对他们以及社区其他成员表示最诚挚的感谢!🙌
Pivotal Tuning
Pivotal Tuning 是一种将 Textual Inversion 与常规扩散模型微调相结合的方法。对于 Dreambooth,通常你需要提供一个稀有词元作为触发词,比如 "an sks dog"。然而,这些词元通常有其他语义含义,可能会影响你的结果。例如,在社区中很流行的 sks,实际上与一个武器品牌有关。
为了解决这个问题,我们在模型的文本编码器中插入新的词元,而不是重用现有的词元。然后,我们优化新插入的词元嵌入来表示新概念:这就是 Textual Inversion——我们学习通过嵌入空间中的新“词”来表示概念。一旦我们获得了代表它的新词元及其嵌入,我们就可以用这些词元嵌入来训练我们的 Dreambooth LoRA,从而两全其美。
训练
在我们的新训练脚本中,你可以通过提供以下参数来进行文本反演训练
--train_text_encoder_ti
--train_text_encoder_ti_frac=0.5
--token_abstraction="TOK"
--num_new_tokens_per_abstraction=2
--adam_weight_decay_text_encoder
train_text_encoder_ti
启用新概念嵌入的训练train_text_encoder_ti_frac
指定何时停止文本反演(即停止文本嵌入的优化,继续只优化 UNet)。在训练周期的一半进行切换(即前一半训练周期执行文本反演)是 cog sdxl 示例中的默认值,我们的实验也验证了这一点。我们鼓励大家在这里进行实验。token_abstraction
这是指概念标识符,即在图像描述中用来描述我们希望训练的概念的词。你选择的词元抽象应该用在你的实例提示、验证提示或自定义描述中。这里我们选择了 TOK,所以,例如,“a photo of a TOK” 可以是实例提示。由于--token_abstraction
是一个占位符,在训练前,我们会用新词元替换TOK
并优化它们(意味着在训练期间,“a photo ofTOK
” 会变成 “a photo of<s0><s1>
”,其中<s0><s1>
是新词元)。因此,token_abstraction
与实例提示、验证提示和自定义提示(如果使用)中使用的标识符相对应也至关重要。num_new_tokens_per_abstraction
为每个token_abstraction
初始化的新词元数量——即为模型的每个文本编码器插入和训练多少个新词元。默认设置为 2,我们鼓励你对此进行实验并分享你的结果!
adam_weight_decay_text_encoder
这用于为文本编码器参数设置不同的权重衰减值(不同于用于 unet 参数的值)。`
自适应优化器

在训练/微调扩散模型(或任何机器学习模型)时,我们使用优化器来引导我们走向收敛的优化路径——即我们选择的损失函数的最小值点,该点表示模型已经学会了我们试图教给它的东西。深度学习任务的标准(也是最先进的)选择是 Adam 和 AdamW 优化器。
然而,它们需要用户大量调整通往收敛之路的超参数(如学习率、权重衰减等)。这可能导致耗时的实验,结果却不尽如人意,而且即使你找到了一个理想的学习率,如果在训练过程中学习率是恒定的,也可能导致收敛问题。一些参数可能需要更频繁的更新以加速收敛,而其他参数可能需要更小的调整以避免超过最佳值。为了应对这一挑战,引入了具有自适应学习率的算法,如 Adafactor 和 Prodigy。这些方法通过根据每个参数过去梯度的动态调整学习率来优化算法在优化空间中的寻路过程。
我们选择更多地关注 Prodigy,因为我们认为它对 Dreambooth LoRA 训练特别有益!
训练
--optimizer="prodigy"
当使用 prodigy 时,通常好的做法是设置-
--learning_rate=1.0
对于扩散模型和特别是 LoRA 训练,被认为有益的其他设置是
--prodigy_safeguard_warmup=True
--prodigy_use_bias_correction=True
--adam_beta1=0.9
# Note these are set to values different than the default:
--adam_beta2=0.99
--adam_weight_decay=0.01
使用 prodigy 训练时,你还可以调整其他超参数(比如 --prodigy_beta3
、prodigy_decouple
、prodigy_safeguard_warmup
),我们在这篇文章中不会深入探讨,但你可以在这里了解更多。
其他最佳实践
除了 pivotal tuning 和自适应优化器,这里还有一些可以影响你训练的 LoRA 质量的其他技术,它们都已被整合到新的 diffusers 训练脚本中。
文本编码器和 UNet 的独立学习率
在优化文本编码器时,社区普遍认为,为其设置不同的学习率(相对于 UNet 的学习率)可以带来更好的质量结果——特别是为文本编码器设置一个**更低**的学习率,因为它倾向于*更快*过拟合。 * 在进行 pivotal tuning 时,不同 unet 和文本编码器学习率的重要性也很明显——在这种情况下,为文本编码器设置更高的学习率被认为更好。 * 然而,请注意,当使用 Prodigy(或通常的自适应优化器)时,我们为所有训练的参数设置一个相同的初始学习率,然后让优化器发挥其魔力 ✨
训练
--train_text_encoder
--learning_rate=1e-4 #unet
--text_encoder_lr=5e-5
--train_text_encoder
启用完整的文本编码器训练(即文本编码器的权重被完全优化,而不是像我们在文本反演(--train_text_encoder_ti
)中看到的那样只优化插入的嵌入)。如果你希望文本编码器的学习率始终与 --learning_rate
匹配,请设置 --text_encoder_lr=None
。
自定义图像描述
虽然通过在一组都使用相同实例提示(例如,“photo of a

训练 要使用自定义描述,首先确保你已安装 datasets 库,否则可以通过以下方式安装 -
!pip install datasets
为了加载自定义描述,我们需要我们的训练集目录遵循 datasets ImageFolder
的结构,包含图像和每个图像对应的描述。
- 选项 1:你从 Hub 中选择一个已经包含图像和提示的数据集——例如 LinoyTsaban/3d_icon。现在你所要做的就是在你的训练参数中指定数据集的名称和描述列的名称(在这种情况下是“prompt”)
--dataset_name=LinoyTsaban/3d_icon
--caption_column=prompt
- 选项 2:你希望使用自己的图像并为它们添加描述。在这种情况下,你可以使用这个 colab notebook 使用 BLIP 自动为图像添加描述,或者你可以在元数据文件中手动创建描述。然后你以同样的方式继续,通过指定
--dataset_name
为你的文件夹路径,以及--caption_column
为描述的列名。
Min-SNR Gamma 加权
训练扩散模型通常会遇到收敛缓慢的问题,部分原因是不同时间步之间的优化方向存在冲突。Hang 等人 发现了一种通过引入简单的 Min-SNR-gamma 方法来缓解这个问题的方法。该方法根据截断的信噪比来调整时间步的损失权重,从而有效平衡时间步之间的冲突。* 对于小数据集,Min-SNR 加权策略的效果可能不明显,但对于大数据集,效果可能会更显著。* snr vis
在 Weights and Biases 上找到这个项目,它比较了以下设置的损失曲面:snr_gamma 设置为 5.0、1.0 和 None。

训练
要使用 Min-SNR gamma,请为以下参数设置一个值
--snr_gamma=5.0
默认情况下 --snr_gamma=None
,即不使用。启用 --snr_gamma
时,推荐值为 5.0。
重复
此参数指的是数据集中每张图片在训练集中重复的次数。这与 epochs 不同,因为这里是先重复图片,然后再进行洗牌。
训练
要启用重复,只需将一个大于 1 的整数值设置为你的重复次数-
--repeats
默认情况下,--repeats=1,即训练集不重复
训练集创建
俗话说,“垃圾进,垃圾出”。训练一个好的 Dreambooth LoRA 只需要少量图片即可轻松完成,但这些图片的质量对微调后的模型有很大影响。
通常,在对物体/主题进行微调时,我们希望确保训练集包含以尽可能多的不同方式描绘该物体/主题的图像,以便我们能够以这些方式进行提示。
例如,如果我的概念是这个红色背包:(可在 google/dreambooth 数据集中找到)
我很可能也想提示它被人背着的样子,所以有像这样的例子
特别是在训练人脸时,你可能需要注意关于数据集的以下几点
如果可能,总是选择高分辨率、高质量的图像。模糊或低分辨率的图像会损害微调过程。
在训练人脸时,建议训练集中不要出现其他面孔,因为我们不想对我们正在训练的人脸产生模糊的概念。
特写照片对于实现真实感很重要,但好的全身照也应该包含在内,以提高对不同姿势/构图的泛化能力。
我们建议避免主体距离较远的照片,因为这类图像中的大多数像素与我们希望优化的概念无关,模型从中能学到的东西不多。
避免重复的背景/服装/姿势——力求在光线、姿势、背景和面部表情方面实现多样性。多样性越大,LoRA 的灵活性和泛化能力就越强。
先验保留损失 -
先验保留损失是一种利用模型自身生成的样本来帮助其学习如何生成更多样化图像的方法。因为这些样本图像与你提供的图像属于同一类别,它们帮助模型保留其已学到的关于该类别以及如何利用其已知的类别知识来创造新构图的知识。用于正则化的真实图像 VS 模型生成的图像 在选择类别图像时,你可以决定使用合成图像(即由扩散模型生成的图像)还是真实图像。支持使用真实图像的观点是,它们可以提高微调后模型的真实感。另一方面,一些人会认为使用模型生成的图像更好地服务于保留模型对该类别和整体美学的知识的目的。名人相似 - 这更多是关于用于训练的描述/实例提示的评论。一些微调者在用一个词元标识符 + 基础模型认识的、与他们训练的人相似的公众人物进行提示时,发现结果有所改善。
使用先验保留损失进行训练
--with_prior_preservation
--class_data_dir
--num_class_images
--class_prompt
--with_prior_preservation
- 启用先验保留训练--class_data_dir
- 包含类别图像的文件夹路径--num_class_images
- 先验保留损失所需的最小类别图像数。如果 --class_data_dir
中已有的图像不足,将使用 --class_prompt
采样额外的图像。
实验设置与结果
为了探索所描述的方法,我们对不同技术组合在不同目标(风格调整、人脸和物体)上进行了实验。
为了缩小无限的超参数值范围,我们使用了一些更流行和常见的配置作为起点,并在此基础上进行调整。
Huggy Dreambooth LoRA 首先,我们有兴趣微调一个 Huggy LoRA,这意味着同时教授一种艺术风格和一个特定角色。为此,我们策划了一个高质量的 Huggy 吉祥物数据集(使用 Chunte-Lee 的惊人艺术作品),其中包含 31 张带有自定义描述的图像。

配置
--train_batch_size = 1, 2,3, 4
-repeats = 1,2
-learning_rate = 1.0 (Prodigy), 1e-4 (AdamW)
-text_encoder_lr = 1.0 (Prodigy), 3e-4, 5e-5 (AdamW)
-snr_gamma = None, 5.0
-max_train_steps = 1000, 1500, 1800
-text_encoder_training = regular finetuning, pivotal tuning (textual inversion)
- 全文本编码器微调 VS Pivotal Tuning - 我们注意到 pivotal tuning 的效果与全文本编码器训练相当甚至更好,而且还不需要优化 text_encoder 的权重。
- Min SNR Gamma
--pretrained_model_name_or_path="stabilityai/stable-diffusion-xl-base-1.0" \
--pretrained_vae_model_name_or_path="madebyollin/sdxl-vae-fp16-fix" \
--dataset_name="./huggy_clean" \
--instance_prompt="a TOK emoji"\
--validation_prompt="a TOK emoji dressed as Yoda"\
--caption_column="prompt" \
--mixed_precision="bf16" \
--resolution=1024 \
--train_batch_size=4 \
--repeats=1\
--report_to="wandb"\
--gradient_accumulation_steps=1 \
--gradient_checkpointing \
--learning_rate=1e-4 \
--text_encoder_lr=3e-4 \
--optimizer="adamw"\
--train_text_encoder_ti\
--lr_scheduler="constant" \
--lr_warmup_steps=0 \
--rank=32 \
--max_train_steps=1000 \
--checkpointing_steps=2000 \
--seed="0" \

- AdamW vs Prodigy 优化器
- 我们比较了使用
optimizer=prodigy
训练的版本1,和使用optimizer=adamW
训练的[版本2](https://wandb.ai/linoy/dreambooth-lora-sd-xl/runs/cws7nfzg? workspace=user-linoy)。两个版本都使用了 pivotal tuning 进行训练。 - 在使用
optimizer=prodigy
训练时,我们将初始学习率设置为 1。对于 adamW,我们使用了 cog-sdxl 中用于 pivotal tuning 的默认学习率(分别为1e-4
和3e-4
用于learning_rate
和text_encoder_lr
),因为我们能够用这些设置复现出好的结果。 - 所有其他训练参数和设置都相同。具体来说
- 我们比较了使用
--pretrained_model_name_or_path="stabilityai/stable-diffusion-xl-base-1.0" \
--pretrained_vae_model_name_or_path="madebyollin/sdxl-vae-fp16-fix" \
--dataset_name="./huggy_clean" \
--instance_prompt="a TOK emoji"\
--validation_prompt="a TOK emoji dressed as Yoda"\
--output_dir="huggy_v11" \
--caption_column="prompt" \
--mixed_precision="bf16" \
--resolution=1024 \
--train_batch_size=4 \
--repeats=1\
--report_to="wandb"\
--gradient_accumulation_steps=1 \
--gradient_checkpointing \
--train_text_encoder_ti\
--lr_scheduler="constant" \
--snr_gamma=5.0 \
--lr_warmup_steps=0 \
--rank=32 \
--max_train_steps=1000 \
--checkpointing_steps=2000 \
--seed="0" \
Y2K 网页 LoRA 让我们探索另一个例子,这次是在一个由 27 张 1990 年代和 2000 年代初网页截图组成的数据集上进行训练,这些截图是我们(怀旧地 🥲)从互联网上抓取的

配置
–rank = 4,16,32
-optimizer = prodigy, adamW
-repeats = 1,2,3
-learning_rate = 1.0 (Prodigy), 1e-4 (AdamW)
-text_encoder_lr = 1.0 (Prodigy), 3e-4, 5e-5 (AdamW)
-snr_gamma = None, 5.0
-train_batch_size = 1, 2, 3, 4
-max_train_steps = 500, 1000, 1500
-text_encoder_training = regular finetuning, pivotal tuning
这个例子展示了与前一个略有不同的行为。虽然在这两种情况下我们都使用了大约相同数量的图片(即约 30 张),但我们注意到,对于这种风格的 LoRA,那些为 Huggy LoRA 带来良好效果的相同设置,对于网页风格来说却导致了过拟合。

对于 v1,我们选择在训练 Huggy LoRA 时效果最好的设置作为起点——结果明显过拟合,所以我们尝试在接下来的版本中通过调整 --max_train_steps
、--repeats
、--train_batch_size
和 --snr_gamma
来解决这个问题。更具体地说,这些是我们在每个版本之间更改的设置(其余都保持不变)
参数 | v1 | v2 | v3 | v4 | v5 | v6 | v7 | v8 |
---|---|---|---|---|---|---|---|---|
max_train_steps |
1500 | 1500 | 1500 | 1000 | 1000 | 1000 | 1000 | 1000 |
repeats |
1 | 1 | 2 | 2 | 1 | 1 | 2 | 1 |
train_batch_size |
4 | 4 | 4 | 4 | 2 | 1 | 1 | 1 |
instance_data_dir |
web_y2k |
从 web_y2k 中随机抽样的 14 张图片 |
web_y2k |
web_y2k |
web_y2k |
web_y2k |
web_y2k |
web_y2k |
snr_gamma |
5.0 | 5.0 | 5.0 | 5.0 | - | - | 5.0 | 5.0 |
我们发现 v4、v5 和 v6 达到了最佳平衡

人脸 LoRA 在训练人脸图像时,我们的目标是让 LoRA 生成尽可能真实且与原始人物相似的图像,同时也能很好地泛化到训练集中未见过的背景和构图。对于这个用例,我们使用了由 6-10 张 Linoy 的脸部图像组成的不同数据集,包括一组在同一时间拍摄的特写照片,以及一组在不同场合(改变背景、光线和服装)拍摄的照片以及全身照。我们发现,在光线/分辨率/主体对焦方面,如果图像质量中等到低,那么经过更好筛选的少量图像比更多图像效果更好——少即是多:挑选你最好的照片来训练模型!配置
rank = 4,16,32, 64
optimizer = prodigy, adamW
repeats = 1,2,3,4
learning_rate = 1.0 , 1e-4
text_encoder_lr = 1.0, 3e-4
snr_gamma = None, 5.0
num_class_images = 100, 150
max_train_steps = 75 * num_images, 100 * num_images, 120 * num_images
text_encoder_training = regular finetuning, pivotal tuning
先验保留损失
- 与普遍做法相反,我们发现使用生成的类别图像会降低与主体的相似度和真实感。
- 我们创建了一个真实肖像图像的数据集,使用了从 unsplash 下载的免费授权图片。你现在也可以在新的训练空间中自动使用它!
- 在使用真实图像数据集时,我们确实注意到语言漂移减少了(即模型不再将女人/男人这个词与训练过的人脸联系起来,也可以生成不同的人),同时在提示训练过的人脸时保持了真实感和整体质量。
排序
- 我们比较了 rank 为 4、16、32 和 64 的 LoRA。我们观察到,在我们探索的设置中,使用 rank 为 64 的 LoRA 生成的图像往往具有更强的磨皮效果,皮肤纹理看起来不太真实。
- 因此,对于下面详述的实验以及 LoRA ease space,我们使用默认的 rank 为 32。
训练步数
尽管少量高质量的图片(在我们的例子中是 6 张)效果很好,我们仍然需要确定一个理想的训练步数。
我们用图片数量的几个不同乘数进行了实验:6 x 75 = 450 步 / 6 x 100 = 600 步 / 6 x 120 = 720 步。
正如你在下面看到的,我们的初步结果显示,使用 120 倍的乘数可以取得良好的效果(如果数据集足够多样化以避免过拟合,最好不要使用相同的拍摄场景)
推理
使用上述技术训练的模型的推理过程应该与任何训练器相同,只是当我们进行 pivotal tuning 时,除了你的 LoRA 的
*.safetensors
权重之外,还有一个与模型一起为新词元训练的*.safetensors
文本嵌入。为了用这些进行推理,我们在通常加载 LoRA 的方式上增加了 2 个步骤- 从 hub 下载我们训练好的嵌入(你的嵌入文件名默认设置为
{model_name}_emb.safetensors
)
import torch from huggingface_hub import hf_hub_download from diffusers import DiffusionPipeline from safetensors.torch import load_file pipe = DiffusionPipeline.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float16, variant="fp16", ).to("cuda") # download embeddings embedding_path = hf_hub_download(repo_id="LinoyTsaban/web_y2k_lora", filename="web_y2k_emb.safetensors", repo_type="model")
- 将嵌入加载到文本编码器中
# load embeddings to the text encoders state_dict = load_file(embedding_path) # notice we load the tokens <s0><s1>, as "TOK" as only a place-holder and training was performed using the new initialized tokens - <s0><s1> # load embeddings of text_encoder 1 (CLIP ViT-L/14) pipe.load_textual_inversion(state_dict["clip_l"], token=["<s0>", "<s1>"], text_encoder=pipe.text_encoder, tokenizer=pipe.tokenizer) # load embeddings of text_encoder 2 (CLIP ViT-G/14) pipe.load_textual_inversion(state_dict["clip_g"], token=["<s0>", "<s1>"], text_encoder=pipe.text_encoder_2, tokenizer=pipe.tokenizer_2)
- 加载你的 LoRA 并进行提示!
# normal LoRA loading pipe.load_lora_weights("LinoyTsaban/web_y2k_lora", weight_name="pytorch_lora_weights.safetensors") prompt="a <s0><s1> webpage about an astronaut riding a horse" images = pipe( prompt, cross_attention_kwargs={"scale": 0.8}, ).images # your output image images[0]
Comfy UI / AUTOMATIC1111 推理
新脚本完全支持 Comfy UI 和 AUTOMATIC1111 格式的文本反演加载!
AUTOMATIC1111 / SD.Next
在 AUTOMATIC1111/SD.Next 中,我们将同时加载一个 LoRA 和一个文本嵌入。- LoRA:除了 diffusers 格式,该脚本还将训练一个与 WebUI 兼容的 LoRA。它生成为
{your_lora_name}.safetensors
。然后你可以将其包含在你的models/Lora
目录中。 - 嵌入:嵌入对于 diffusers 和 WebUI 是相同的。你可以从训练好的模型中下载你的
{lora_name}_emb.safetensors
文件,并将其包含在你的embeddings
目录中。
然后你可以通过提示
a y2k_emb webpage about the movie Mean Girls <lora:y2k:0.9>
来运行推理。你可以正常使用y2k_emb
词元,包括通过(y2k_emb:1.2)
来增加其权重。ComfyUI
在 ComfyUI 中,我们将同时加载一个 LoRA 和一个文本嵌入。- LoRA:除了 diffusers 格式,该脚本还将训练一个与 ComfyUI 兼容的 LoRA。它生成为
{your_lora_name}.safetensors
。然后你可以将其包含在你的models/Lora
目录中。然后你将加载 LoRALoader 节点,并将其与你的模型和 CLIP 连接起来。加载 LoRA 的官方指南 - Embedding:diffusers 和 WebUI 的 embedding 是相同的。你可以从训练好的模型下载你的
{lora_name}_emb.safetensors
文件,并将其包含在你的models/embeddings
目录中,然后在你的提示中使用它,例如embedding:y2k_emb
。加载 embedding 的官方指南。
下一步是什么?
🚀 更多功能即将推出!我们正在努力为我们的高级训练脚本增加更多的控制和灵活性。让我们知道你觉得哪些功能最有用!
🤹 多概念 LoRA 最近,Shah 等人的一篇论文介绍了 ZipLoRA——一种合并独立训练的风格和主体 LoRA 的方法,以实现生成任何用户提供的主体,并采用任何用户提供的风格。mkshing 实现了一个开源复现,可在此处获取,它使用了新的和改进的脚本。
- 从 hub 下载我们训练好的嵌入(你的嵌入文件名默认设置为