目录
0 媒介
1 媒介
2 开始动手
3 安装须要的库
4 导入
5 准备数据
6 设置项目路径
7 导入数据
7.1 怎么扩充数据集?
7.2 怎么让模子明白文本?
具体解释:
7.3 自界说数据集
8 界说微调相干的函数
8.1 加载 LoRA
8.2 准备优化器
8.3 界说 collate_fn 函数
9 设置相干参数
9.1 装备设置
9.2 模子与训练参数设置
10 微调前的准备
10.1 准备数据集
10.2 准备模子和优化器
11 开始微调
12 生成图像和评估
12.1 什么是 pipeline?
12.2 推理相干的参数
12.3 加载用于验证的 prompts
12.4 界说生成图像的函数
12.5 界说评估函数
13 拓展作业
14 用脚本微调 SD(可选)
14.1 克隆仓库
14.2 执行脚本
15 参考链接
0 媒介
本文为李宏毅学习条记——2024春《GENERATIVE AI》篇——作业条记HW10。
如果你还没获取到LLM API,请查看我的另一篇条记:
HW1~2:LLM API获取步调及LLM API使用演示:环境设置与多轮对话演示-CSDN博客
完整内容参见:
李宏毅学习条记——2024春《GENERATIVE AI》篇
总得拆开炼丹炉看看是什么样的。这篇文章将带你从代码层面一步步实现 AI 文本生成图像(Text-to-Image)中的 LoRA 微调过程,你将:
- 相识 Trigger Words(触发词)到底是什么,以及它们如何影响生成结果。
- 把握 LoRA 微调的根本原理。
- 学习数据集的准备与结构,并知道如何根据需求定制自己的数据集。
- 明白 Stable Diffusion 模子的微调步调。
- 明白在画图界面(UI)下到底发生了什么。
- 使用代码实现 AI 绘画。
如果你想制作属于自己的数据集,最好遵循以下建议:
- 至少准备 20 张图片:想学到的概念越复杂就需要越多的图片。你可以尝试将样例数据集的图片数量淘汰到 20 张,看看效果会有什么变革。
- 裁剪图片:建议对图片进行裁剪,当然你也可以不裁剪,如果你不追求效果的话。这里会主动 resize 到自界说的分辨率。
与其耗费大量时间去调参,更优的选择是处置惩罚好你的数据集和 Prompts。当然,这两件事变可以同步进行。
注意,当前文章使用的是天然语言标注(而非 Tag),你也可以使用 Tag,这两种方式本质上是一致的。
同时,如果你对深度学习有所相识,那么代码中的一切,都将是你曾经见过的内容翻版,没有什么新的,除了 LoRA。我们将同步使用演员 Brad Pitt(布拉德·皮特)的图片作为训练集,共计一百张。
1 媒介
下面是使用 prompt:"A man in a graphic tee and sport coat.",在默认设置下训练 2000 个步调后模子生成的图像,训练时长约为 18 分钟。乍一看,是不是还挺不错的?
你可能会注意到,我们的 prompt 中并没有提到 Brad Pitt(布拉德·皮特)这个演员(只管我们的数据集完全来自于他),但模子却可以或许绘制长得像 Brad Pitt 的人。
这是因为,如果我们在 prompt 中直接指定 "Brad Pitt",模子可能无法完全学习到他的特性风格。举个例子:
- "A man in a graphic tee and sport coat. Brad Pitt."
- "A man in a graphic tee and sport coat."
第一条 prompt 显然更精准,但精准并不意味着模子训练得更好。如果你用一系列包含 "Brad Pitt" 的 prompt 来训练,模子更有可能学到的是:只有在加上 "Brad Pitt" 时才进行风格转变。你可能会说:“我就是想要这个效果”,那么很好,"Brad Pitt" 就是你模子的 Trigger Word(触发词)。但有可能还有同学:“我希望模子只为 Brad Pitt 服务,我要把全部的 'man' 都酿成 Brad Pitt”,那么在训练时就不要在 prompt 中增加 "Brad Pitt"。简而言之:反着来。
这实际上并没有反直觉,跳出来想一想:
- 想象一下你是一位画家,生活在一个从不变暗的天下里,整个天下永久是白天,你已经习惯画出白天背景下的各种景象,但你不知道白天是什么,这就是你所熟知的「日常」。
- 有一天,有人给你看了一些照片,说:“Hey,实际上天下可以是黑的,叫做夜晚”,这时间你就会明白到,日常是有另一种状态的,叫做夜晚,即便你以前从来没有过概念,但现在,你将认知到它,你将这部分新的概念聚焦到了「夜晚」。于是,今后以后,你的画作被分为了「日常」和「日常,夜晚」。
- 同时,在另一个平行天下,有人告诉你:“你眼中看到的天下是不对的”,他们“治”好了你的眼睛,向你展示了一个完全陌生的漆黑天下,并答应只要你学会画出这种风格的画作,将会获得丰厚的回报,否则将无人问津你的画摊。于是你开始画“夜晚”风格的「日常」。
这是杜攥的三个小片段,希望你喜欢。
你可以分别将它明白为:
- 原始模子:活在自己天下的画家。
- LoRA 微调:当新标签(Tag)“夜晚”被引入,画家学会了夜晚的概念。Prompt:夜晚,日常。
- 另一个 LoRA 微调:迁移风格,画家将“夜晚”视为真正的日常风格。Prompt:日常。
因此,训练模子就像教小朋侪认知天下。如果你将天下分解为差别的概念并逐一传授,孩子会学到差别的知识。这就类似于模子学习差别的标签和风格。如果你不明白区分概念,并将新概念稠浊在已有的认知中,孩子的认知会被重塑,大概会将鹿“误”以为马。这是合理的,模子也是云云,取决于你如何辅导(prompt)它。
Prompt 小本事:
- 明白你的目标:在训练前,思索你是希望模子学习特定的风格、特定的人物,还是希望模子在特定的场景下才生成特定的效果。到底是希望全部的 man 都是 Brad Pitt,还是希望模子知道 Brad Pitt 是一个 man。
- 保持一致性:如果你希望将某个概念拆分出来,应该为它创建一个特定的标签(tag),并应用于具有相同概念的图像上。
大模子很智慧,它会主动将图像中的共性归因于共用的标签上。因此,如果不给它新的标签,它会将新学到的内容融入到已有的标签中。
这些是关于 AI 绘画 Prompt + 微调背后逻辑的明白话。扯远了,让我们回到代码部分 
2 开始动手
下面,我将带你从代码层面一步步实现 LoRA 微调 Stable Diffusion 模子。注意,这里的知识是通用的,你完全可以推广至任何需要 LoRA 微调的领域。
3 安装须要的库
起首,确保安装以下须要的 Python 库:
- pip install timm
- pip install fairscale
- pip install transformers
- pip install requests
- pip install accelerate
- pip install diffusers
- pip install einop
- pip install safetensors
- pip install voluptuous
- pip install jax
- pip install jaxlib
- pip install peft
- pip install deepface==0.0.92
- pip install tensorflow==2.9.0 # 为了避免最后评估阶段使用deepface时的错误,这里选择降级版本
- pip install keras
- pip install opencv-python
复制代码 4 导入
- # ========== 标准库模块 ==========
- import os
- import math
- import glob
- import shutil
- import subprocess
- # ========== 第三方库 ==========
- import numpy as np
- import torch
- import torch.nn.functional as F
- from PIL import Image
- from tqdm.auto import tqdm
- # ========== 深度学习相关库 ==========
- from torchvision import transforms
- # Transformers (Hugging Face)
- from transformers import CLIPTextModel, CLIPTokenizer, CLIPModel, CLIPProcessor
- # Diffusers (Hugging Face)
- from diffusers import (
- AutoencoderKL,
- DDPMScheduler,
- UNet2DConditionModel,
- DiffusionPipeline
- )
- from diffusers.optimization import get_scheduler
- from diffusers.training_utils import compute_snr
- # ========== LoRA 模型库 ==========
- from peft import LoraConfig, get_peft_model, PeftModel
- # ========== 面部检测库 ==========
- from deepface import DeepFace
- import cv2
复制代码 5 准备数据
当前演示使用的是 Brad Pitt(布拉德·皮特),我们的目标是让模子绘制的 man 是 Brad Pitt,粗略地换个表述:AI 换脸。
那根据我们之前的描述,标注应该长什么样呢?
答:都带 “man”,下面是我们当前数据集的标注示例:
- a man with a beard and a suit jacket
- a man in a suit and tie standing in front of a crowd
- a man with long hair and a tie
- ...
信赖你发现了,全部的标注,都不会含有 “Brad Pitt”,那这篇文章训练出的 LoRA 模子的 Trigger Words(触发词)是什么?
答:“a man”。
是不是很风趣,看似简朴的 Prompt 中也有一些真实有用的小本事和逻辑。别急着去炼丹,我们继续往下看。
在这里,我们使用 Brad Pitt 的 100 张图片进行演示,数据集已经上传到了Demos/data/14,你可以下载后放到当前目录下的 ./data/14 下。这个路径没有什么说法,单纯是为了对齐示例代码,你也可以修改代码关于数据的路径,这里不会有限定,你乃至可以直接用其他的数据集,只要它的文件组织如下:
-- 图片1
-- 图片1.txt
-- 图片2
-- 图片2.txt
...
注意:图片和对应的文本标注需要同名,且位于同一文件夹中。
值得一提的是,样例数据集的裁剪大小和比例都是不一致的,只是靠近正方形,但这没有太大的关系,因为在数据预处置惩罚的时间会主动放缩(resize),所以在这里不消担心你的数据集无法训练。
6 设置项目路径
很好!现在你已经知道这篇文章数据集相干的全部前置知识,直接复制下面的代码运行,不消在意此中的任何代码细节,你只需要知道会创建一个文件夹SD,之后的全部结果都会被存放在此中:
[code]# 项目名称和数据集名称
project_name = "Brad"
dataset_name = "Brad"
# 根目录和主要目录
root_dir = "./" # 当前目录
main_dir = os.path.join(root_dir, "SD") # 主目录
# 项目目录
project_dir = os.path.join(main_dir, project_name) # 项目目录
# 数据集和模型路径
images_folder = os.path.join(main_dir, "Datasets", dataset_name)
prompts_folder = os.path.join(main_dir, "Datasets", "prompts")
captions_folder = images_folder # 与原始代码一致
output_folder = os.path.join(project_dir, "logs") # 存放 model checkpoints 和 validation 的文件夹
# prompt 文件路径
validation_prompt_name = "validation_prompt.txt"
validation_prompt_path = os.path.join(prompts_folder, validation_prompt_name)
# 模型检查点路径
model_path = os.path.join(project_dir, "logs", "checkpoint-last")
# 其他路径设置
zip_file = os.path.join("./", "data/14/Datasets.zip")
inference_path = os.path.join(project_dir, "inference") # 保存推理结果的文件夹
os.makedirs(images_folder, exist_ok=True)
os.makedirs(prompts_folder, exist_ok=True)
os.makedirs(output_folder, exist_ok=True)
os.makedirs(inference_path, exist_ok=True)
# 检查并解压数据集
print(" |