Diffusers 中的 SDXL DreamBooth LoRA 微调:从环境搭建、显存优化到 EDM/DoRA 训练实战
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
DreamBooth 是一种仅凭少量(约 3~5 张)主题图片即可对文本到图像(text-to-image)模型进行个性化定制的方法;本仓库中的examples/dreambooth/train_dreambooth_lora_sdxl.py脚本将其完整落地到 Stable Diffusion XL(SDXL),并采用 LoRA 参数高效微调技术,使普通消费级显卡也能完成 SDXL 的个性化训练。读完本文,你将掌握 SDXL LoRA 微调的全流程——从环境安装、数据集准备、训练命令逐项解读,到 16GB 以下显存优化、文本编码器联合训练、EDM 风格训练与 DoRA 训练,以及训练产物的推理与格式转换。
DreamBooth 与 SDXL 结合的技术背景
DreamBooth 的核心思想是:给定一个特定主题(例如一只特定的狗、一个特定的 logo)的少量图片,通过微调扩散模型,让模型学会生成"该主题"在任意场景、姿态下的图像。它通过引入一个稀有标识符(identifier,如sks)来绑定主题,配合描述性提示词(如a photo of sks dog)实现个性化生成。
而 SDXL 是参数量更大的双文本编码器架构模型,直接全量微调代价高昂。因此本脚本采用LoRA(Low-Rank Adaptation)——一种参数高效微调技术,仅训练注入到注意力层中的低秩矩阵,从而大幅降低可训练参数量与显存占用。按文档所述,当前实现只允许通过 LoRA 方式对 SDXL 的 UNet 进行 DreamBooth 微调,这是出于训练成本与稳定性的务实取舍。
从源码可以看到,脚本在main()中通过 PEFT 库的LoraConfig为 UNet 注入 LoRA 适配器(train_dreambooth_lora_sdxl.py):
unet_target_modules = ["to_k", "to_q", "to_v", "to_out.0"] unet_lora_config = get_lora_config( rank=args.rank, dropout=args.lora_dropout, use_dora=args.use_dora, target_modules=unet_target_modules, ) unet.add_adapter(unet_lora_config)其中rank默认值为 4(见脚本 参数定义处),lora_alpha默认与 rank 相同,lora_dropout默认为 0.0。同时脚本将 VAE 与两个文本编码器全部冻结(requires_grad_(False)),只有 LoRA 参数参与优化。
环境准备:安装依赖与配置 Accelerate
官方文档强调:由于示例脚本更新频繁且带有示例专属依赖,强烈建议从源码安装 diffusers,并在新的虚拟环境中执行:
git clone https://github.com/huggingface/diffusers cd diffusers pip install -e .然后进入examples/dreambooth目录安装 SDXL 示例的专属依赖:
pip install -r requirements_sdxl.txt该依赖文件(requirements_sdxl.txt)内容如下:
accelerate>=0.16.0 torchvision transformers>=4.25.1 ftfy tensorboard Jinja2 peft==0.7.0文档同时特别提醒:脚本使用 PEFT 库作为 LoRA 训练的后端,务必确保环境中的peft>=0.6.0(注意:若后续要使用 DoRA 训练,还需升级到peft0.9.0 以上,详见后文)。
最后初始化 🤗 Accelerate 训练环境,三种方式任选其一:
# 交互式配置(按提示回答 GPU、混合精度等问题) accelerate config# 使用默认配置,无需回答任何问题 accelerate config default# 适用于不支持交互式 shell 的环境(例如 Jupyter Notebook) from accelerate.utils import write_basic_config write_basic_config()文档指出:在accelerate config中若开启 torch compile 模式(torch.compile),可以带来显著的训练加速。
第一个实战:Dog Toy 示例
下载数据集
示例使用 Hugging Face 上的diffusers/dog-example数据集(包含若干张特定小狗的图片)。先将其下载到本地:
from huggingface_hub import snapshot_download local_dir = "./dog" snapshot_download( "diffusers/dog-example", local_dir=local_dir, repo_type="dataset", ignore_patterns=".gitattributes", )将数据保存在本地目录./dog,后续训练用--instance_data_dir指向它;文档提示,这样组织数据也有利于训练后将 LoRA 权重推送回 Hugging Face Hub。
启动训练
使用accelerate launch启动训练脚本,核心命令如下(环境变量便于复用):
export MODEL_NAME="stabilityai/stable-diffusion-xl-base-1.0" export INSTANCE_DIR="dog" export OUTPUT_DIR="lora-trained-xl" export VAE_PATH="madebyollin/sdxl-vae-fp16-fix" accelerate launch train_dreambooth_lora_sdxl.py \ --pretrained_model_name_or_path=$MODEL_NAME \ --instance_data_dir=$INSTANCE_DIR \ --pretrained_vae_model_name_or_path=$VAE_PATH \ --output_dir=$OUTPUT_DIR \ --mixed_precision="fp16" \ --instance_prompt="a photo of sks dog" \ --resolution=1024 \ --train_batch_size=1 \ --gradient_accumulation_steps=4 \ --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=25 \ --seed="0" \ --push_to_hub文档对其中两个实验跟踪相关的参数做了专门说明:
report_to="wandb":将训练过程记录到 Weights and Biases。使用前需pip install wandb,且首次使用前执行wandb login <your_api_key>完成登录。validation_prompt与validation_epochs:让脚本在训练期间周期性执行验证推理(默认每validation_epochs个 epoch 运行一次,每次生成num_validation_images张图,默认 4 张),便于定性观察训练是否朝正确方向收敛。
结合脚本源码,还可以补充以下关键参数的底层含义:
| 参数 | 默认值 | 源码说明 |
|---|---|---|
--resolution | 1024 | 输入图像统一缩放并裁剪到的分辨率,SDXL 推荐 1024×1024 |
--train_batch_size | 4 | 每设备训练 batch 大小 |
--gradient_accumulation_steps | 1 | 梯度累积步数,等效放大 batch 并降低显存压力 |
--learning_rate | 1e-4 | 学习率(warmup 之后的目标值) |
--lr_scheduler | constant | 可选linear/cosine/cosine_with_restarts/polynomial/constant/constant_with_warmup |
--lr_warmup_steps | 500 | 学习率 warmup 步数 |
--max_train_steps | None | 总训练步数;若提供则覆盖num_train_epochs |
--rank | 4 | LoRA 更新矩阵的秩 |
--seed | None | 随机种子,保证可复现 |
--mixed_precision | None | 可选no/fp16/bf16,bf16 需要 Ampere 及以上 GPU |
以上参数均可直接通过python train_dreambooth_lora_sdxl.py --help查看完整定义;参数合法性校验(如--instance_data_dir与--dataset_name二选一、prior preservation 相关约束)见脚本 parse_args 末尾。
文档记录的实验环境为单张 40GB A100 GPU,在上述超参数下即可获得良好效果。
16GB 以下显存训练:三件套组合拳
SDXL 模型体积较大,默认配置在低显存显卡上难以运行。文档给出的方案是组合使用gradient checkpointing(Diffusers 原生支持)、xformers与bitsandbytes三个库,在accelerate launch命令中追加以下参数:
+ --enable_xformers_memory_efficient_attention \ + --gradient_checkpointing \ + --use_8bit_adam \ + --mixed_precision="fp16" \并确保安装了对应版本的依赖库:
bitsandbytes>=0.40.0 xformers>=0.0.20这些标志在源码中的实现路径为:
--gradient_checkpointing:调用unet.enable_gradient_checkpointing(),若同时开启文本编码器训练,还会对两个文本编码器启用梯度检查点(脚本实现);--enable_xformers_memory_efficient_attention:调用unet.enable_xformers_memory_efficient_attention(),并对 xformers 0.0.16 版本在部分 GPU 上的已知问题给出警告(脚本实现);--use_8bit_adam:将优化器替换为 bitsandbytes 的AdamW8bit(脚本实现),通过 8 位优化器状态显著降低显存;--mixed_precision="fp16":以半精度运行非训练权重,脚本还会将可训练 LoRA 参数单独 upcast 回 fp32 以保证数值稳定(脚本实现)。
推理:加载 LoRA 权重与使用 Refiner
训练完成后,产物为 LoRA 权重文件(pytorch_lora_weights.safetensors等)。加载方式与普通 SDXL LoRA 一致:
from huggingface_hub.repocard import RepoCard from diffusers import DiffusionPipeline import torch lora_model_id = <"lora-sdxl-dreambooth-id"> card = RepoCard.load(lora_model_id) base_model_id = card.data.to_dict()["base_model"] pipe = DiffusionPipeline.from_pretrained(base_model_id, 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] image.save("sks_dog.png")注意:这里通过 LoRA 模型的 model card 中的base_model字段自动还原基础模型 ID,因此训练时若使用了--push_to_hub,推理侧可以免去手动指定基础模型的麻烦。
使用 SDXL Refiner 进一步精修
SDXL 官方提供了配套的 Refiner(stabilityai/stable-diffusion-xl-refiner-1.0),用于对 base 模型输出做精修。流程为:先用加载了 LoRA 的 base pipeline 生成**潜空间(latent)**输出,再交给 Refiner 以 img2img 方式精修:
from huggingface_hub.repocard import RepoCard from diffusers import DiffusionPipeline, StableDiffusionXLImg2ImgPipeline import torch lora_model_id = <"lora-sdxl-dreambooth-id"> card = RepoCard.load(lora_model_id) base_model_id = card.data.to_dict()["base_model"] # Load the base pipeline and load the LoRA parameters into it. pipe = DiffusionPipeline.from_pretrained(base_model_id, dtype=torch.float16) pipe = pipe.to("cuda") pipe.load_lora_weights(lora_model_id) # Load the refiner. refiner = StableDiffusionXLImg2ImgPipeline.from_pretrained( "stabilityai/stable-diffusion-xl-refiner-1.0", dtype=torch.float16, use_safetensors=True, variant="fp16" ) refiner.to("cuda") prompt = "A picture of a sks dog in a bucket" generator = torch.Generator("cuda").manual_seed(0) # Run inference. image = pipe(prompt=prompt, output_type="latent", generator=generator).images[0] image = refiner(prompt=prompt, image=image[None, :], generator=generator).images[0] image.save("refined_sks_dog.png")关键点在于 base pipeline 推理时设置output_type="latent",将中间结果以潜变量形式直接传给 Refiner,避免了先解码再编码的信息损耗;使用固定generator种子可保证两次生成的一致性。
进阶一:文本编码器(Text Encoder)LoRA 微调
SDXL 拥有两个文本编码器(text_encoder与text_encoder_2,分别对应 CLIPTextModel 与 CLIPTextModelWithProjection)。脚本支持同时对二者进行 LoRA 微调,只需在启动命令中追加:
+ --train_text_encoder文档特别提醒两点:
- 由于 SDXL 有双文本编码器,开启该标志后两个编码器都会被 LoRA 微调;
- 不微调文本编码器时,脚本总会预计算(precompute)文本嵌入以节省显存——对应源码中
compute_text_embeddings的提前编码逻辑(脚本实现),并在编码完成后释放 tokenizer/文本编码器并清理 GPU 缓存。
文本编码器来自 🤗 Transformers,无法直接修改,脚本通过 monkey-patch 其注意力层 forward 的方式注入 LoRA,目标模块为["q_proj", "k_proj", "v_proj", "out_proj"](脚本实现)。此外,开启该选项后脚本会为文本编码器使用独立的学习率(--text_encoder_lr,默认 5e-6)与权重衰减(--adam_weight_decay_text_encoder,默认 1e-3),见 脚本实现。
进阶二:指定更稳定的 VAE
SDXL 自带的 VAE 存在已知的数值不稳定问题,可能导致生成图像出现伪影。因此脚本暴露了--pretrained_vae_model_name_or_path参数,允许指定一个数值更稳定的 VAE(例如madebyollin/sdxl-vae-fp16-fix):
+ --pretrained_vae_model_name_or_path="madebyollin/sdxl-vae-fp16-fix"从源码看,当指定该参数时,VAE 会直接从该仓库根目录加载(subfolder=None),否则从基础模型仓库的vae子目录加载(脚本实现);并且脚本始终将 VAE 保持为 float32 精度以避免 NaN 损失(脚本实现)。
进阶三:EDM 风格训练
脚本支持 EDM(Elucidating the Design Space of Diffusion-Based Generative Models)风格训练,只需在命令中加入:
+ --do_edm_style_trainingEDM 风格训练的核心差异在于:不再用离散 timestep 加噪,而是基于连续噪声水平 sigma 对输入与输出进行预条件化(preconditioning)。对应源码中,脚本会从噪声调度器按索引采样 sigma,并对模型输入做noisy_model_input / ((sigmas**2 + 1) ** 0.5)的预条件化,同时对预测结果做相应反预条件化(脚本实现)。值得一提的是,脚本会根据基础模型的model_index.json自动检测调度器类型(determine_scheduler_type,脚本实现),若基础模型本身已使用 EDM 公式(如 Playground 系列),即使不显式传--do_edm_style_training也会自动启用。
采用 EDM 公式的其他 SDXL 类模型(如playgroundai/playground-v2.5-1024px-aesthetic)同样可以用本脚本做 DreamBooth 微调,示例命令:
accelerate launch train_dreambooth_lora_sdxl.py \ --pretrained_model_name_or_path="playgroundai/playground-v2.5-1024px-aesthetic" \ --instance_data_dir="dog" \ --output_dir="dog-playground-lora" \ --mixed_precision="fp16" \ --instance_prompt="a photo of sks dog" \ --resolution=1024 \ --train_batch_size=1 \ --gradient_accumulation_steps=4 \ --learning_rate=1e-4 \ --use_8bit_adam \ --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=25 \ --seed="0" \ --push_to_hub⚠️注意:Min-SNR gamma(
--snr_gamma)暂不支持与 EDM 风格训练同时使用——脚本会在二者同时启用时直接抛出ValueError(脚本实现)。另外,使用 PlaygroundAI 模型训练时,建议不要传入任何variant。
仓库中 test_dreambooth_lora_edm.py 提供了 EDM 训练与 Playground 训练的冒烟测试用例:使用tiny-stable-diffusion-xl-pipe/tiny-playground-v2-5-pipe迷你模型验证训练流程可跑通,并断言产出的pytorch_lora_weights.safetensors中所有键均包含lora、且在不训练文本编码器时全部以unet开头——这从侧面印证了权重命名规范与输出结构。
进阶四:DoRA 训练
脚本已支持DoRA(Weight-Decomposed Low-Rank Adaptation)训练。DoRA 与 LoRA 的最大区别在于:它将预训练权重分解为**幅度(magnitude)与方向(direction)**两个分量,仅对方向分量使用 LoRA 做低秩更新,从而在几乎不增加推理开销的前提下提升学习能力与训练稳定性。
使用步骤:
- 升级 PEFT 库:
pip install -U peft需要说明的是:训练脚本 参数定义处 同时注明可使用
pip install git+https://github.com/huggingface/peft.git安装最新版;而脚本实际运行时要求peft>=0.9.0(否则在构造LoraConfig时抛出ValueError,见 get_lora_config)。
- 启用 DoRA 训练标志:
--use_dora- 推理方式与普通 LoRA 完全一致,无需任何额外改动。
文档基于实验给出了两点经验性提示:
- LoRA 比 DoRA 收敛更快——一套在 LoRA 上可能导致过拟合的超参数,用在 DoRA 上可能刚刚好;
- 低秩下 DoRA 质量优势更明显——rank 8 时 DoRA 与 LoRA 的差异显著大于 rank 32 或 64 时的差异,这与论文中的定量分析结论一致。
同时文档强调 DoRA 训练目前仍属**实验性(experimental)**功能,很可能需要单独调参才能达到最佳效果。
格式兼容:导出 Kohya 格式权重
脚本支持通过--output_kohya_format额外导出一份与其他平台/工具(Automatic 1111、ComfyUI、Kohya 等)兼容的状态字典:
+ --output_kohya_format启用后,output_dir中会额外生成一个名为pytorch_lora_weights_kohya.safetensors的文件。从源码看,导出流程为:加载标准 diffusers 格式的pytorch_lora_weights.safetensors,先通过convert_all_state_dict_to_peft转成 PEFT 格式,再用convert_state_dict_to_kohya转换为 Kohya 兼容格式并保存(脚本实现)。
训练经验与实验结论
文档总结了官方在实验中的经验:
- SDXL 在不做大量超参数调优的情况下即可获得不错的初始效果;
- 例如**不微调文本编码器、不使用 prior-preservation(先验保持)**也能得到可观的结果;
- 官方未进一步探索超参数调优,但鼓励社区继续尝试并分享结果。
文档记录的实验结果来自以下数据集的 DreamBooth 微调(使用完全相同的脚本与超参数):
- Dogs(
diffusers/dog-example) - Starbucks logo(
diffusers/starbucks-example) - Mr. Potato Head(
diffusers/potato-head-example) - Keramer face(
diffusers/keramer-face-example)
另外,文档提到该训练流程也可以在免费版 Colab Notebook 上运行(对应 Hugging Face notebooks 仓库中的SDXL_DreamBooth_LoRA_.ipynb),适合作为入门体验路径。
小结
examples/dreambooth/train_dreambooth_lora_sdxl.py是 diffusers 仓库中面向 SDXL 的 DreamBooth LoRA 一站式训练脚本,覆盖了从基础训练、低显存优化、双文本编码器微调、VAE 替换,到 EDM 风格训练、DoRA 训练与 Kohya 格式导出的完整能力矩阵。搭配 requirements_sdxl.txt 的依赖清单、test_dreambooth_lora_edm.py 的冒烟测试验证,以及脚本源码中对参数与训练循环的精细控制,开发者可以低成本地将这一流程复用到任意 SDXL/Playground 系模型的个性化微调场景中。
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考