# Stable Diffusion XL

> [!WARNING]
> 此脚本仍处于实验阶段，很容易过拟合，并出现灾难性遗忘等问题。请尝试探索不同的超参数，以在您的数据集上获得最佳效果。

[Stable Diffusion XL (SDXL)](https://hf.co/papers/2307.01952) 是 Stable Diffusion 模型更大、更强大的迭代版本，能够生成更高分辨率的图像。

SDXL 的 UNet 体积是原来的 3 倍，并且该模型在架构中增加了第二个文本编码器。根据您可用的硬件，这可能非常消耗计算资源，并且可能无法在 Tesla T4 等消费级 GPU 上运行。为了帮助将更大的模型装入内存并加速训练，请尝试启用 `gradient_checkpointing`、`mixed_precision` 和 `gradient_accumulation_steps`。通过启用 [xFormers](../optimization/attention_backends) 的内存高效注意力机制并使用 [bitsandbytes'](https://github.com/TimDettmers/bitsandbytes) 的 8 位优化器，您可以进一步降低内存使用量。

This guide will explore the [train_text_to_image_sdxl.py](https://github.com/huggingface/diffusers/blob/main/examples/text_to_image/train_text_to_image_sdxl.py) training script to help you become more familiar with it, and how you can adapt it for your own use-case.

在运行脚本之前，请确保从源代码安装该库：

```bash
git clone https://github.com/huggingface/diffusers
cd diffusers
pip install .
```

然后导航到包含训练脚本的示例文件夹，并安装您正在使用的脚本所需的依赖项：

```bash
cd examples/text_to_image
pip install -r requirements_sdxl.txt
```

> [!TIP]
> 🤗 Accelerate 是一个用于在多个 GPU/TPU 上训练或使用混合精度训练的库。它会根据你的硬件和环境自动配置训练设置。请查看 🤗 Accelerate 的 [快速入门](https://huggingface.co/docs/accelerate/quicktour) 以了解更多信息。

初始化一个 🤗 Accelerate 环境：

```bash
accelerate config
```

要设置一个默认的 🤗 Accelerate 环境，而不选择任何配置：

```bash
accelerate config default
```

或者，如果你的环境不支持交互式 shell（例如 notebook），你可以使用：

```py
from accelerate.utils import write_basic_config

write_basic_config()
```

最后，如果您想在自己的数据集上训练模型，请查看[创建训练数据集](create_dataset)指南，了解如何创建可与训练脚本配合使用的数据集。

## 脚本参数

> [!TIP]
> 以下各节重点介绍了训练脚本中对于理解如何修改脚本至关重要的部分，但并未详细涵盖脚本的每个方面。如果您希望了解更多，请随时阅读[脚本](https://github.com/huggingface/diffusers/blob/main/examples/text_to_image/train_text_to_image_sdxl.py)，并告诉我们您是否有任何疑问或顾虑。

训练脚本提供了许多参数，帮助你自定义训练过程。所有参数及其说明都可以在 [`parse_args()`](https://github.com/huggingface/diffusers/blob/aab6de22c33cc01fb7bc81c0807d6109e2c998c9/examples/text_to_image/train_text_to_image_sdxl.py#L129) 函数中找到。该函数为每个参数提供了默认值，例如训练批次大小和学习率，但你也可以在训练命令中设置自己的值。

例如，若要使用 bf16 格式通过混合精度加速训练，请在训练命令中添加 `--mixed_precision` 参数：

```bash
accelerate launch train_text_to_image_sdxl.py \
  --mixed_precision="bf16"
```

大多数参数与[文本到图像](text2image#script-parameters)训练指南中的参数相同，因此本指南将重点介绍与训练 SDXL 相关的参数。

- `--pretrained_vae_model_name_or_path`：预训练 VAE 的路径；SDXL 的 VAE 存在数值不稳定的问题，因此该参数允许你指定一个更优的 [VAE](https://huggingface.co/madebyollin/sdxl-vae-fp16-fix)
- `--proportion_empty_prompts`：将图像提示替换为空字符串的比例
- `--timestep_bias_strategy`：在时间步的哪个阶段（早期或后期）应用偏差，这可以促使模型学习低频或高频细节
- `--timestep_bias_multiplier`：应用于时间步的偏差权重
- `--timestep_bias_begin`：开始应用偏差的时间步
- `--timestep_bias_end`：结束应用偏差的时间步
- `--timestep_bias_portion`：应用偏差的时间步比例

### Min-SNR 加权

[Min-SNR](https://huggingface.co/papers/2303.09556) 加权策略可以通过重新平衡损失来帮助训练，从而实现更快的收敛。训练脚本支持预测 `epsilon`（噪声）或 `v_prediction`，而 Min-SNR 与这两种预测类型均兼容。此加权策略仅支持 PyTorch。

添加 `--snr_gamma` 参数并将其设置为推荐值 5.0：

```bash
accelerate launch train_text_to_image_sdxl.py \
  --snr_gamma=5.0
```

## 训练脚本

训练脚本与[文本到图像](text2image#training-script)训练指南中的脚本类似，但已修改以支持 SDXL 训练。本指南将重点介绍 SDXL 训练脚本中特有的代码。

首先，创建用于[对提示词进行分词](https://github.com/huggingface/diffusers/blob/aab6de22c33cc01fb7bc81c0807d6109e2c998c9/examples/text_to_image/train_text_to_image_sdxl.py#L478)以计算提示词嵌入的函数，以及使用[VAE](https://github.com/huggingface/diffusers/blob/aab6de22c33cc01fb7bc81c0807d6109e2c998c9/examples/text_to_image/train_text_to_image_sdxl.py#L519)计算图像嵌入的函数。接下来，创建一个函数，根据时间步数量以及要应用的时间步偏差策略来[生成时间步权重](https://github.com/huggingface/diffusers/blob/aab6de22c33cc01fb7bc81c0807d6109e2c998c9/examples/text_to_image/train_text_to_image_sdxl.py#L531)。

在 [`main()`](https://github.com/huggingface/diffusers/blob/aab6de22c33cc01fb7bc81c0807d6109e2c998c9/examples/text_to_image/train_text_to_image_sdxl.py#L572) 函数中，除了加载一个分词器外，脚本还会加载第二个分词器和文本编码器，因为 SDXL 架构每种各使用两个：

```py
tokenizer_one = AutoTokenizer.from_pretrained(
    args.pretrained_model_name_or_path, subfolder="tokenizer", revision=args.revision, use_fast=False
)
tokenizer_two = AutoTokenizer.from_pretrained(
    args.pretrained_model_name_or_path, subfolder="tokenizer_2", revision=args.revision, use_fast=False
)

text_encoder_cls_one = import_model_class_from_model_name_or_path(
    args.pretrained_model_name_or_path, args.revision
)
text_encoder_cls_two = import_model_class_from_model_name_or_path(
    args.pretrained_model_name_or_path, args.revision, subfolder="text_encoder_2"
)
```

[提示词和图像嵌入](https://github.com/huggingface/diffusers/blob/aab6de22c33cc01fb7bc81c0807d6109e2c998c9/examples/text_to_image/train_text_to_image_sdxl.py#L857)会首先被计算并保留在内存中，对于较小的数据集这通常不是问题，但对于较大的数据集则可能导致内存问题。如果出现这种情况，您应该将预计算的嵌入单独保存到磁盘，并在训练过程中将其加载到内存中（有关此主题的更多讨论，请参阅此 [PR](https://github.com/huggingface/diffusers/pull/4505)）。

```py
text_encoders = [text_encoder_one, text_encoder_two]
tokenizers = [tokenizer_one, tokenizer_two]
compute_embeddings_fn = functools.partial(
    encode_prompt,
    text_encoders=text_encoders,
    tokenizers=tokenizers,
    proportion_empty_prompts=args.proportion_empty_prompts,
    caption_column=args.caption_column,
)

train_dataset = train_dataset.map(compute_embeddings_fn, batched=True, new_fingerprint=new_fingerprint)
train_dataset = train_dataset.map(
    compute_vae_encodings_fn,
    batched=True,
    batch_size=args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps,
    new_fingerprint=new_fingerprint_for_vae,
)
```

在计算完嵌入后，文本编码器、VAE 和分词器会被删除，以释放部分内存：

```py
del text_encoders, tokenizers, vae
gc.collect()
torch.cuda.empty_cache()
```

最后，[训练循环](https://github.com/huggingface/diffusers/blob/aab6de22c33cc01fb7bc81c0807d6109e2c998c9/examples/text_to_image/train_text_to_image_sdxl.py#L943)会处理其余部分。如果你选择应用时间步偏差策略，你会看到时间步权重被计算并作为噪声添加：

```py
weights = generate_timestep_weights(args, noise_scheduler.config.num_train_timesteps).to(
        model_input.device
    )
    timesteps = torch.multinomial(weights, bsz, replacement=True).long()

noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps)
```

如果想进一步了解训练循环的工作原理，请查看[理解流水线、模型和调度器](../using-diffusers/write_own_pipeline)教程，其中详细拆解了去噪过程的基本模式。

## 运行脚本

完成所有更改或对默认配置满意后，即可启动训练脚本！🚀

让我们使用 [Naruto BLIP captions](https://huggingface.co/datasets/lambdalabs/naruto-blip-captions) 数据集进行训练，以生成你自己的 Naruto 角色。将环境变量 `MODEL_NAME` 和 `DATASET_NAME` 设置为模型和数据集（来自 Hub 或本地路径）。你还应使用 `VAE_NAME` 指定一个 SDXL VAE 以外的 VAE（来自 Hub 或本地路径），以避免数值不稳定。

> [!TIP]
> 若要使用 Weights & Biases 监控训练进度，请在训练命令中添加 `--report_to=wandb` 参数。同时，还需要在训练命令中添加 `--validation_prompt` 和 `--validation_epochs` 以跟踪结果。这对于调试模型和查看中间结果非常有用。

```bash
export MODEL_NAME="stabilityai/stable-diffusion-xl-base-1.0"
export VAE_NAME="madebyollin/sdxl-vae-fp16-fix"
export DATASET_NAME="lambdalabs/naruto-blip-captions"

accelerate launch train_text_to_image_sdxl.py \
  --pretrained_model_name_or_path=$MODEL_NAME \
  --pretrained_vae_model_name_or_path=$VAE_NAME \
  --dataset_name=$DATASET_NAME \
  --enable_xformers_memory_efficient_attention \
  --resolution=512 \
  --center_crop \
  --random_flip \
  --proportion_empty_prompts=0.2 \
  --train_batch_size=1 \
  --gradient_accumulation_steps=4 \
  --gradient_checkpointing \
  --max_train_steps=10000 \
  --use_8bit_adam \
  --learning_rate=1e-06 \
  --lr_scheduler="constant" \
  --lr_warmup_steps=0 \
  --mixed_precision="fp16" \
  --report_to="wandb" \
  --validation_prompt="a cute Sundar Pichai creature" \
  --validation_epochs 5 \
  --checkpointing_steps=5000 \
  --output_dir="sdxl-naruto-model" \
  --push_to_hub
```

完成训练后，您就可以使用新训练的 SDXL 模型进行推理了！

```py
from diffusers import DiffusionPipeline
import torch

pipeline = DiffusionPipeline.from_pretrained("path/to/your/model", dtype=torch.float16).to("cuda")  # or "mps", "xpu", "cpu"

prompt = "A naruto with green eyes and red legs."
image = pipeline(prompt, num_inference_steps=30, guidance_scale=7.5).images[0]
image.save("naruto.png")
```

[PyTorch XLA](https://pytorch.org/xla) 允许你在 TPU 等 XLA 设备上运行 PyTorch，速度可能更快。初始预热步骤耗时较长，因为模型需要编译和优化。但是，后续对 pipeline 的调用，如果输入长度与原始提示**相同**，则会快得多，因为可以复用优化后的计算图。

```py
from diffusers import DiffusionPipeline
import torch
import torch_xla.core.xla_model as xm

device = xm.xla_device()
pipeline = DiffusionPipeline.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0").to(device)

prompt = "A naruto with green eyes and red legs."
start = time()
image = pipeline(prompt, num_inference_steps=inference_steps).images[0]
print(f'Compilation time is {time()-start} sec')
image.save("naruto.png")

start = time()
image = pipeline(prompt, num_inference_steps=inference_steps).images[0]
print(f'Inference time is {time()-start} sec after compilation')
```

## 后续步骤

恭喜您训练了一个 SDXL 模型！如需了解如何使用您的新模型，以下指南可能对您有所帮助：

- 阅读 [Stable Diffusion XL](../api/pipelines/stable_diffusion/stable_diffusion_xl) 指南，了解如何将其用于多种不同任务（文本生成图像、图像生成图像、图像修复），如何使用其 refiner 模型，以及不同类型的微条件。
- 请参阅 [DreamBooth](dreambooth) 和 [LoRA](lora) 训练指南，了解如何仅使用少量示例图像来训练个性化的 SDXL 模型。这两种训练技术甚至可以组合使用！

