Skip to content

Commit 3ae3ea5

Browse files
authored
[PPDiffusers] Add lora training scripts (PaddlePaddle#4768)
* add lora training script * update * update * fix mkdir bug * safety checker & ti bug * rerun ci * update text2img readme
1 parent dc5f5d8 commit 3ae3ea5

24 files changed

+3517
-896
lines changed

paddlenlp/utils/downloader.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,7 @@ def _download(url, path, md5sum=None):
174174
url (str): download url
175175
path (str): download to given path
176176
"""
177-
if not osp.exists(path):
178-
os.makedirs(path)
177+
os.makedirs(path, exist_ok=True)
179178

180179
fname = osp.split(url)[-1]
181180
fullname = osp.join(path, fname)

ppdiffusers/examples/dreambooth/README.md

+96-42
Original file line numberDiff line numberDiff line change
@@ -36,70 +36,58 @@ python -u train_dreambooth.py \
3636
--instance_data_dir=$INSTANCE_DIR \
3737
--output_dir=$OUTPUT_DIR \
3838
--instance_prompt="a photo of sks dog" \
39-
--height=512 \
40-
--width=512 \
39+
--resolution=512 \
4140
--train_batch_size=1 \
4241
--gradient_accumulation_steps=1 \
4342
--learning_rate=5e-6 \
4443
--lr_scheduler="constant" \
4544
--lr_warmup_steps=0 \
4645
--max_train_steps=400
4746
```
48-
49-
```bash
50-
bash run_single.sh
51-
```
52-
| ppdiffusers支持的模型名称 | huggingface对应的模型地址 | Tips备注 |
53-
| ---------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------- |
54-
| CompVis/stable-diffusion-v1-4 | https://huggingface.co/CompVis/stable-diffusion-v1-4 | 原版SD模型,模型使用PNDM scheduler。 |
55-
| hakurei/waifu-diffusion | https://huggingface.co/hakurei/waifu-diffusion | Waifu v1-2的模型,模型使用了DDIM scheduler。 |
56-
| hakurei/waifu-diffusion-v1-3 | https://huggingface.co/hakurei/waifu-diffusion | Waifu v1-3的模型,模型使用了PNDM scheduler。 |
57-
| naclbit/trinart_stable_diffusion_v2_60k | https://huggingface.co/naclbit/trinart_stable_diffusion_v2 | trinart 经过60k步数训练得到的模型,模型使用了DDIM scheduler。 |
58-
| naclbit/trinart_stable_diffusion_v2_95k | https://huggingface.co/naclbit/trinart_stable_diffusion_v2 | trinart 经过95k步数训练得到的模型,模型使用了DDIM scheduler。 |
59-
| naclbit/trinart_stable_diffusion_v2_115k | https://huggingface.co/naclbit/trinart_stable_diffusion_v2 | trinart 经过115k步数训练得到的模型,模型使用了DDIM scheduler。 |
60-
| Deltaadams/Hentai-Diffusion | https://huggingface.co/Deltaadams/Hentai-Diffusion | Hentai模型,模型使用了PNDM scheduler。 |
61-
| IDEA-CCNL/Taiyi-Stable-Diffusion-1B-Chinese-v0.1 | https://huggingface.co/IDEA-CCNL/Taiyi-Stable-Diffusion-1B-Chinese-v0.1 | 中文StableDiffusion模型,模型使用了PNDM scheduler。 |
62-
| IDEA-CCNL/Taiyi-Stable-Diffusion-1B-Chinese-EN-v0.1 | https://huggingface.co/IDEA-CCNL/Taiyi-Stable-Diffusion-1B-Chinese-EN-v0.1 | 中文+英文双语言的StableDiffusion模型,模型使用了PNDM scheduler。 |
63-
6447

6548
`train_dreambooth.py`代码可传入的参数解释如下:
6649
> 主要修改的参数
67-
> * `--pretrained_model_name_or_path`: 所使用的Stable Diffusion模型权重名称或者本地下载的模型路径,目前支持了上表中的8种模型权重,我们可直接替换使用。
50+
> * `--pretrained_model_name_or_path`: 所使用的 `Stable Diffusion` 模型权重名称或者本地下载的模型路径,目前支持了上表中的8种模型权重,我们可直接替换使用。
6851
> * `--instance_data_dir`: 实例(物体)图片文件夹地址。
69-
> * `--instance_prompt`: 带有特定实例(物体)的提示词描述文本,例如a photo of sks dog,其中dog代表实例(物体)。
52+
> * `--instance_prompt`: 带有特定实例(物体)的提示词描述文本,例如`a photo of sks dog`,其中dog代表实例(物体)。
7053
> * `--class_data_dir`: 类别(class)图片文件夹地址,主要作为先验知识。
71-
> * `--class_prompt`: 类别(class)提示词文本,该提示器要与实例(物体)是同一种类别,例如a photo of dog,主要作为先验知识。
54+
> * `--class_prompt`: 类别(class)提示词文本,该提示器要与实例(物体)是同一种类别,例如`a photo of dog`,主要作为先验知识。
7255
> * `--num_class_images`: 事先需要从`class_prompt`中生成多少张图片,主要作为先验知识。
7356
> * `--prior_loss_weight`: 先验`loss`占比权重。
7457
> * `--sample_batch_size`: 生成`class_prompt`文本对应的图片所用的批次(batch size),注意,当GPU显卡显存较小的时候需要将这个默认值改成1。
7558
> * `--with_prior_preservation`: 是否将生成的同类图片(先验知识)一同加入训练,当为`True`的时候,`class_prompt``class_data_dir``num_class_images``sample_batch_size``prior_loss_weight`才生效。
7659
> * `--num_train_epochs`: 训练的轮数,默认值为`1`
7760
> * `--max_train_steps`: 最大的训练步数,当我们设置这个值后,它会重新计算所需的`num_train_epochs`轮数。
78-
> * `--save_steps`: 每间隔多少步`(global step步数)`保存学习到的`pipe`
79-
> * `--gradient_accumulation_steps`: 梯度累积的步数,用户可以指定梯度累积的步数,在梯度累积的step中。减少多卡之间梯度的通信,减少更新的次数,扩大训练的batch_size
61+
> * `--checkpointing_steps`: 每间隔多少步`(global step步数)`保存模型权重
62+
> * `--gradient_accumulation_steps`: 梯度累积的步数,用户可以指定梯度累积的步数,在梯度累积的 step 中。减少多卡之间梯度的通信,减少更新的次数,扩大训练的 batch_size
8063
> * `--train_text_encoder`: 是否一同训练文本编码器的部分,默认为`False`
8164
8265
> 可以修改的参数
83-
> * `--height`: 输入给模型的图片`高度`,由于用户输入的并不是固定大小的图片,因此代码中会将原始大小的图片压缩成指定`高度`的图片,默认值为`512`
84-
> * `--width`: 输入给模型的图片`宽度`,由于用户输入的并不是固定大小的图片,因此代码中会将原始大小的图片压缩成指定`宽度`的图片,默认值为`512`
66+
> * `--height`: 输入给模型的图片`高度`,由于用户输入的并不是固定大小的图片,因此代码中会将原始大小的图片压缩成指定`高度`的图片,默认值为`None`
67+
> * `--width`: 输入给模型的图片`宽度`,由于用户输入的并不是固定大小的图片,因此代码中会将原始大小的图片压缩成指定`宽度`的图片,默认值为`None`
68+
> * `--resolution`: 输入给模型图片的`分辨率`,当`高度``宽度``None`时,我们将会使用`resolution`,默认值为`512`
8569
> * `--learning_rate`: 学习率。
8670
> * `--scale_lr`: 是否根据GPU数量,梯度累积步数,以及批量数对学习率进行缩放。缩放公式:`learning_rate * gradient_accumulation_steps * train_batch_size * num_processes`
8771
> * `--lr_scheduler`: 要使用的学习率调度策略。默认为 `constant`
88-
> * `--lr_warmup_steps`: 用于从 0 到 `learning_rate` 的线性 warmup 的步数。
72+
> * `--lr_warmup_steps`: 用于从 0 到 `learning_rate` 的线性 `warmup` 的步数。
8973
> * `--train_batch_size`: 训练时每张显卡所使用的`batch_size批量`,当我们的显存较小的时候,需要将这个值设置的小一点。
9074
> * `--center_crop`: 在调整图片宽和高之前是否将裁剪图像居中,默认值为`False`
75+
> * `--random_flip`: 是否对图片进行随机水平反转,默认值为`False`
9176
> * `--gradient_checkpointing`: 是否开启`gradient_checkpointing`功能,在一定程度上能够更显显存,但是会减慢训练速度。
9277
> * `--output_dir`: 模型训练完所保存的路径,默认设置为`dreambooth-model`文件夹,建议用户每训练一个模型可以修改一下输出路径,防止先前已有的模型被覆盖了。
9378
9479
> 基本无需修改的参数
9580
> * `--seed`: 随机种子,为了可以复现训练结果,Tips:当前paddle设置该随机种子后仍无法完美复现。
96-
> * `--adam_beta1`: AdamW 优化器时的 beta1 超参数。默认为 `0.9`
97-
> * `--adam_beta2`: AdamW 优化器时的 beta2 超参数。默认为 `0.999`
98-
> * `--adam_weight_decay`: AdamW 优化器时的 weight_decay 超参数。 默认为`0.02`
99-
> * `--adam_weight_decay`: AdamW 优化器时的 epsilon 超参数。默认为 1e-8。
100-
> * `--max_grad_norm`: 最大梯度范数(用于梯度裁剪)。默认为 `None`表示不使用。
81+
> * `--adam_beta1`: `AdamW` 优化器时的 `beta1` 超参数。默认为 `0.9`
82+
> * `--adam_beta2`: `AdamW` 优化器时的 `beta2` 超参数。默认为 `0.999`
83+
> * `--adam_weight_decay`: `AdamW` 优化器时的 `weight_decay` 超参数。 默认为`0.02`
84+
> * `--adam_weight_decay`: `AdamW` 优化器时的 `epsilon` 超参数。默认为 `1e-8`
85+
> * `--max_grad_norm`: 最大梯度范数(用于梯度裁剪)。默认为 `-1` 表示不使用。
10186
> * `--logging_dir`: Tensorboard 或 VisualDL 记录日志的地址,注意:该地址会与输出目录进行拼接,即,最终的日志地址为`<output_dir>/<logging_dir>`
102-
> * `--writer_type`: 用于记录日志的工具,可选`["tensorboard", "visualdl"]`,默认为`visualdl`,如果选用`tensorboard`,请使用命令安装`pip install tensorboardX`
87+
> * `--report_to`: 用于记录日志的工具,可选`["tensorboard", "visualdl"]`,默认为`visualdl`,如果选用`tensorboard`,请使用命令安装`pip install tensorboardX`
88+
> * `--push_to_hub`: 是否将模型上传到 `huggingface hub`,默认值为 `False`
89+
> * `--hub_token`: 上传到 `huggingface hub` 所需要使用的 `token`,如果我们已经登录了,那么我们就无需填写。
90+
> * `--hub_model_id`: 上传到 `huggingface hub` 的模型库名称, 如果为 `None` 的话表示我们将使用 `output_dir` 的名称作为模型库名称。
10391
10492

10593
#### 1.2.3 单机多卡训练
@@ -115,19 +103,14 @@ python -u -m paddle.distributed.launch --gpus "0,1,2,3" train_dreambooth.py \
115103
--instance_data_dir=$INSTANCE_DIR \
116104
--output_dir=$OUTPUT_DIR \
117105
--instance_prompt="a photo of sks dog" \
118-
--height=512 \
119-
--width=512 \
106+
--resolution=512 \
120107
--train_batch_size=1 \
121108
--gradient_accumulation_steps=1 \
122109
--learning_rate=5e-6 \
123110
--lr_scheduler="constant" \
124111
--lr_warmup_steps=0 \
125112
--max_train_steps=400
126113
```
127-
128-
```bash
129-
bash run_multi.sh
130-
```
131114

132115
#### 1.2.4 预测生成图片
133116

@@ -142,7 +125,7 @@ bash run_multi.sh
142125
├── model_state.pdparams
143126
├── config.json
144127
├── text_encoder # text_encoder权重文件夹
145-
├── model_config.json
128+
├── config.json
146129
├── model_state.pdparams
147130
├── unet # unet权重文件夹
148131
├── model_state.pdparams
@@ -194,8 +177,7 @@ python -u train_dreambooth.py \
194177
--with_prior_preservation --prior_loss_weight=1.0 \
195178
--instance_prompt="a photo of sks dog" \
196179
--class_prompt="a photo of dog" \
197-
--height=512 \
198-
--width=512 \
180+
--resolution=512 \
199181
--train_batch_size=1 \
200182
--gradient_accumulation_steps=1 \
201183
--learning_rate=5e-6 \
@@ -222,7 +204,79 @@ image.save("sks-dog-with-class.png")
222204
</p>
223205

224206

207+
# 使用 LoRA 和 DreamBooth 技术进行模型训练
208+
209+
[LoRA: Low-Rank Adaptation of Large Language Models](https://arxiv.org/abs/2106.09685) 是微软研究员引入的一项新技术,主要用于处理大模型微调的问题。目前超过数十亿以上参数的具有强能力的大模型 (例如 GPT-3) 通常在为了适应其下游任务的微调中会呈现出巨大开销。LoRA 建议冻结预训练模型的权重并在每个 Transformer 块中注入可训练层 (秩-分解矩阵)。因为不需要为大多数模型权重计算梯度,所以大大减少了需要训练参数的数量并且降低了 GPU 的内存要求。研究人员发现,通过聚焦大模型的 Transformer 注意力块,使用 LoRA 进行的微调质量与全模型微调相当,同时速度更快且需要更少的计算。
210+
211+
简而言之,LoRA允许通过向现有权重添加一对秩分解矩阵,并只训练这些新添加的权重来适应预训练的模型。这有几个优点:
212+
213+
- 保持预训练的权重不变,这样模型就不容易出现灾难性遗忘 [catastrophic forgetting](https://www.pnas.org/doi/10.1073/pnas.1611835114)
214+
- 秩分解矩阵的参数比原始模型少得多,这意味着训练的 LoRA 权重很容易移植;
215+
- LoRA 注意力层允许通过一个 `scale` 参数来控制模型适应新训练图像的程度。
216+
217+
[cloneofsimo](https://github.com/cloneofsimo) 是第一个在 [LoRA GitHub](https://github.com/cloneofsimo/lora) 仓库中尝试使用 LoRA 训练 Stable Diffusion 的人。
218+
219+
## 训练
220+
221+
**___Note: 如果我们使用 [stable-diffusion-2](https://huggingface.co/stabilityai/stable-diffusion-2) 进行训练,那么我们需要将 `resolution` 改成 768 .___**
222+
223+
```bash
224+
export MODEL_NAME="runwayml/stable-diffusion-v1-5"
225+
export INSTANCE_DIR="path-to-instance-images"
226+
export OUTPUT_DIR="path-to-save-model"
227+
228+
python train_dreambooth_lora.py \
229+
--pretrained_model_name_or_path=$MODEL_NAME \
230+
--instance_data_dir=$INSTANCE_DIR \
231+
--output_dir=$OUTPUT_DIR \
232+
--instance_prompt="a photo of sks dog" \
233+
--resolution=512 \
234+
--train_batch_size=1 \
235+
--gradient_accumulation_steps=1 \
236+
--checkpointing_steps=100 \
237+
--learning_rate=1e-4 \
238+
--report_to="visualdl" \
239+
--lr_scheduler="constant" \
240+
--lr_warmup_steps=0 \
241+
--max_train_steps=500 \
242+
--validation_prompt="A photo of sks dog in a bucket" \
243+
--validation_epochs=50 \
244+
--seed=0
245+
```
246+
247+
**___Note: 当我使用 LoRA 训练模型的时候,我们需要使用更大的学习率,因此我们这里使用 *1e-4* 而不是 *2e-6*.___**
248+
249+
最终经过微调后的 LoRA 权重,我们已经上传到了 [junnyu/lora_dreambooth_dog_example](https://huggingface.co/junnyu/lora_dreambooth_dog_example). **___Note: [最终的权重](https://huggingface.co/junnyu/lora_dreambooth_dog_example/blob/main/paddle_lora_weights.pdparams) 只有 3 MB 的大小.___**
250+
251+
## 推理
252+
253+
经过训练, LoRA 权重可以直接加载到原始的 pipeline 中。
254+
255+
首先我们需要加载原始的 pipeline:
256+
257+
```python
258+
from ppdiffusers import DiffusionPipeline, DPMSolverMultistepScheduler
259+
import paddle
260+
261+
pipe = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", paddle_dtype=paddle.float16)
262+
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
263+
```
264+
265+
接下来, 我们需要使用 `load_attn_procs` 方法将 `adapter layers` 添加到 UNet 模型中。
266+
```python
267+
pipe.unet.load_attn_procs("junnyu/lora_dreambooth_dog_example", from_hf_hub=True)
268+
```
269+
270+
最终, 我们可以使用模型进行推理预测.
271+
272+
```python
273+
image = pipe("A picture of a sks dog in a bucket", num_inference_steps=25).images[0]
274+
image.save("demo.png")
275+
```
276+
<p align="center">
277+
<img src="https://user-images.githubusercontent.com/50394665/218384517-b89667f4-b5c9-4ecf-afcb-8b667c5532bb.jpg">
278+
</p>
225279

226-
## 2 参考资料
280+
# 参考资料
227281
- https://github.com/huggingface/diffusers/tree/main/examples/dreambooth
228282
- https://github.com/CompVis/stable-diffusion

ppdiffusers/examples/dreambooth/run_multi.sh

-32
This file was deleted.

ppdiffusers/examples/dreambooth/run_single.sh

-32
This file was deleted.

0 commit comments

Comments
 (0)