Datawhale AI夏令营第四期魔搭-AIGC文生图方向
Task1
过程记录
按照教程https://linklearner.com/activity/14/10/24
Step0:开通阿里云PAI-DSW试用
https://free.aliyun.com/?productCode=learn申请试用
https://www.modelscope.cn/my/mynotebook/authorization 选个人云账号去授权,一步步跳转到阿里云授权然后跳回来。
Step1:报名赛事!
https://tianchi.aliyun.com/competition/entrance/532254
出来组队忽略即可
Step2:在魔搭社区创建PAI实例!(点击即可跳转)
创建实例
Step3:30 分钟体验一站式 baseline!
-
下载baseline文件**(大约需要2分钟)**
git lfs install git clone https://www.modelscope.cn/datasets/maochase/kolors.git
-
进入文件夹,打开baseline文件
-
安装环境,然后重启kernel
-
启动结果:
下面的代码块按照功能主要分成这几类
- 使用Data-Juicer处理数据,整理训练数据文件
- 使用DiffSynth-Studio在基础模型上,使用前面整理好的数据文件进行训练微调
- 加载训练微调后的模型
- 使用微调后的模型,生成用户指定的prompt提示词的图片
问题解决:
1.报错FileNotFoundError: [Errno 2] No such file or directory: ‘data/data-juicer/data_juicer_config.yaml’
解决:
2.上传结果时选择
解决:
代码阅读
1.安装
!pip install simple-aesthetics-predictor
!pip install -v -e data-juicer
!pip uninstall pytorch-lightning -y
!pip install peft lightning pandas torchvision
!pip install -e DiffSynth-Studio
Data-Juicer是一个专为大型语言模型(LLMs)设计的一站式多模态数据处理系统。
DiffSynth-Studio是一个多功能的创作工具,它可以作为图片和视频处理工具或音乐合成软件使用。
2.下载数据集
from modelscope.msdatasets import MsDataset
ds = MsDataset.load(
'AI-ModelScope/lowres_anime',
subset_name='default',
split='train',
cache_dir="/mnt/workspace/kolors/data"
)
-
MsDataset
是modelscope
平台提供的一个用于加载数据集的类,它简化了从平台下载、加载和管理数据集的过程。 -
加载数据集:
ds = MsDataset.load( 'AI-ModelScope/lowres_anime', subset_name='default', split='train', cache_dir="/mnt/workspace/kolors/data" )
- dataset_name (
'AI-ModelScope/lowres_anime'
): 这是要加载的数据集的名称。数据集lowres_anime
包含了一些低分辨率的动漫图片。 - subset_name (
'default'
): 这个参数指定了数据集的哪个子集应该被加载。有些数据集可能包含多个子集,比如训练集、验证集和测试集,而subset_name
允许你指定要加载哪个子集。 - split (
'train'
): 这个参数进一步指定了要加载的子集中的哪一部分。它被设置为'train'
,意味着加载训练集部分。这通常用于模型训练。 - cache_dir (
"/mnt/workspace/kolors/data"
): 这个参数指定了数据集的缓存目录,即下载后的数据集文件将存储在哪里。
- dataset_name (
保存数据集中的图片及元数据
import json, os
from data_juicer.utils.mm_utils import SpecialTokens
from tqdm import tqdm
os.makedirs("./data/lora_dataset/train", exist_ok=True)
os.makedirs("./data/data-juicer/input", exist_ok=True)
with open("./data/data-juicer/input/metadata.jsonl", "w") as f:
for data_id, data in enumerate(tqdm(ds)):
image = data["image"].convert("RGB")
image.save(f"/mnt/workspace/kolors/data/lora_dataset/train/{data_id}.jpg")
metadata = {"text": "二次元", "image": [f"/mnt/workspace/kolors/data/lora_dataset/train/{data_id}.jpg"]}
f.write(json.dumps(metadata))
f.write("\n")
-
导入必要的库:
from tqdm import tqdm
:导入**tqdm
库,用于在循环中添加进度条**,以便用户了解循环的进度。 -
创建目录:
- 使用
os.makedirs
函数创建两个目录(如果它们尚不存在):"./data/lora_dataset/train"
用于存储训练图像,"./data/data-juicer/input"
用于存储元数据文件。exist_ok=True
参数确保如果目录已存在,则不会抛出错误。
- 使用
-
处理数据集并写入元数据:
- 打开(或创建)一个名为
"./data/data-juicer/input/metadata.jsonl"
的文件用于写入元数据。.jsonl
扩展名通常表示JSON Lines格式,即每行一个JSON对象。 - 使用
enumerate(tqdm(ds))
遍历数据集ds
,其中tqdm
用于添加进度条。enumerate
函数同时返回每个元素的索引(data_id
)和元素本身(data
)。 - 对于数据集中的每个元素,首先将其图像部分转换为RGB格式(假设原始图像可能包含透明度通道),然后保存到
"/mnt/workspace/kolors/data/lora_dataset/train/"
目录下,文件名由data_id
生成。 - 构造一个包含文本标签(“二次元”)和图像路径的元数据字典
metadata
。 - 使用
json.dumps(metadata)
将元数据字典转换为JSON格式的字符串,并写入之前打开的文件中。每个元数据对象后都添加一个换行符"\n"
,以符合JSON Lines格式。
- 打开(或创建)一个名为
3.数据处理
使用 data-juicer 处理数据
data_juicer_config = """
# global parameters
project_name: 'data-process'
dataset_path: './data/data-juicer/input/metadata.jsonl' # path to your dataset directory or file
np: 4 # number of subprocess to process your dataset
text_keys: 'text'
image_key: 'image'
image_special_token: '<__dj__image>'
export_path: './data/data-juicer/output/result.jsonl'
# process schedule
# a list of several process operators with their arguments
process:
- image_shape_filter:
min_width: 1024
min_height: 1024
any_or_all: any
- image_aspect_ratio_filter:
min_ratio: 0.5
max_ratio: 2.0
any_or_all: any
"""
with open("data/data-juicer/data_juicer_config.yaml", "w") as file:
file.write(data_juicer_config.strip())
!dj-process --config data/data-juicer/data_juicer_config.yaml
使用了一个魔术命令(以 !
开头)来调用 dj-process
命令,该命令使用刚刚创建的 YAML 文件作为配置。
保存处理好的数据
import pandas as pd
import os, json
from PIL import Image
from tqdm import tqdm
texts, file_names = [], []
os.makedirs("./data/lora_dataset_processed/train", exist_ok=True)
with open("./data/data-juicer/output/result.jsonl", "r") as file:
for data_id, data in enumerate(tqdm(file.readlines())):
data = json.loads(data)
text = data["text"]
texts.append(text)
image = Image.open(data["image"][0])
image_path = f"./data/lora_dataset_processed/train/{data_id}.jpg"
image.save(image_path)
file_names.append(f"{data_id}.jpg")
data_frame = pd.DataFrame()
data_frame["file_name"] = file_names
data_frame["text"] = texts
data_frame.to_csv("./data/lora_dataset_processed/train/metadata.csv", index=False, encoding="utf-8-sig")
data_frame
这段代码主要执行了几个步骤,用于处理和准备一个数据集,特别是为了机器学习或深度学习模型(如基于Stable Diffusion的LORA模型)的训练。以下是代码的详细解释:
-
导入必要的库:
pandas as pd
:用于数据处理和分析。os
:提供了与操作系统交互的功能,比如文件和目录的管理。json
:用于解析和生成JSON数据。PIL.Image
(来自Pillow库):用于图像的打开、保存和处理。tqdm
:用于在循环中显示进度条,使长时间运行的程序更加用户友好。
-
初始化变量:
texts, file_names = [], []
:分别用于存储文本数据和对应的文件名。
-
创建目录:
- 使用
os.makedirs("./data/lora_dataset_processed/train", exist_ok=True)
确保目标目录存在。如果目录已存在且exist_ok=True
,则不会抛出错误。
- 使用
-
读取和处理JSONL文件:
- 打开一个JSON Lines(JSONL)文件
"./data/data-juicer/output/result.jsonl"
进行读取。JSONL是一种每行一个JSON对象的格式。 - 使用
enumerate(tqdm(file.readlines()))
在读取文件时显示进度条,enumerate
用于获取每行的索引(即data_id
)和内容(即每行的JSON字符串)。 - 使用
json.loads(data)
将每行的JSON字符串解析为Python字典。 - 从解析后的字典中提取
text
和image
信息。 - 使用Pillow库中的
Image.open(data["image"][0])
打开图像文件。注意这里假设data["image"]
是一个包含图像文件路径的列表,且只取第一个元素。 - 将图像保存到
"./data/lora_dataset_processed/train/"
目录下,文件名为{data_id}.jpg
。 - 将文件名添加到
file_names
列表中,将文本添加到texts
列表中。
- 打开一个JSON Lines(JSONL)文件
-
创建DataFrame并保存为CSV文件:
- 使用
pd.DataFrame()
创建一个空的DataFrame。 - 向DataFrame中添加两列:
file_name
(包含处理后的文件名)和text
(包含对应的文本数据)。 - 使用
data_frame.to_csv(...)
将DataFrame保存为CSV文件,文件名为"./data/lora_dataset_processed/train/metadata.csv"
,并设置index=False
以避免将索引作为一列写入文件,encoding="utf-8-sig"
确保文件编码兼容Excel等程序。
- 使用
-
最后的
data_frame
:- 最后一行代码
data_frame
只是简单地引用了前面创建的DataFrame对象,实际上并没有执行任何操作。在脚本的上下文中,这行代码可能是多余的,除非它在后续的代码块中有用。
- 最后一行代码
总的来说,这段代码的目的是从一个JSONL文件中读取文本和图像信息,将图像保存到指定目录,并将文本和对应的文件名保存到CSV文件中,以便后续用于训练机器学习或深度学习模型。
4.训练模型
下载模型
from diffsynth import download_models
download_models(["Kolors", "SDXL-vae-fp16-fix"])
查看训练脚本的输入参数
!python DiffSynth-Studio/examples/train/kolors/train_kolors_lora.py -h
开始训练
提示:
- 在训练命令中填入
--modelscope_model_id xxxxx
以及--modelscope_access_token xxxxx
后,训练程序会在结束时自动上传模型到 ModelScope - 部分参数可根据实际需求调整,例如
lora_rank
可以控制 LoRA 模型的参数量
import os
cmd = """
python DiffSynth-Studio/examples/train/kolors/train_kolors_lora.py \
--pretrained_unet_path models/kolors/Kolors/unet/diffusion_pytorch_model.safetensors \
--pretrained_text_encoder_path models/kolors/Kolors/text_encoder \
--pretrained_fp16_vae_path models/sdxl-vae-fp16-fix/diffusion_pytorch_model.safetensors \
--lora_rank 16 \
--lora_alpha 4.0 \
--dataset_path data/lora_dataset_processed \
--output_path ./models \
--max_epochs 1 \
--center_crop \
--use_gradient_checkpointing \
--precision "16-mixed"
""".strip()
os.system(cmd)
它使用os
模块的system
函数来执行一个字符串命令。**这个命令是一个长字符串,被分成了多行以提高可读性。**这个命令是调用Python脚本来训练一个LoRA(Low-Rank Adaptation)模型,这通常用于微调大型预训练模型,如Stable Diffusion等文本到图像的模型。
让我们分解这个命令及其参数:
-
python DiffSynth-Studio/examples/train/kolors/train_kolors_lora.py
:这是要执行的Python脚本的路径,它负责训练LoRA模型。 -
--pretrained_unet_path models/kolors/Kolors/unet/diffusion_pytorch_model.safetensors
:这个参数指定了预训练的U-Net模型的路径。U-Net是Stable Diffusion等模型中的一部分,用于从潜在空间生成图像。 -
--pretrained_text_encoder_path models/kolors/Kolors/text_encoder
:这个参数指定了预训练的文本编码器的路径,文本编码器负责将文本提示转换为模型可以理解的向量。 -
--pretrained_fp16_vae_path models/sdxl-vae-fp16-fix/diffusion_pytorch_model.safetensors
:这个参数指定了预训练的FP16 VAE(变分自编码器)模型的路径。VAE在这里可能用于潜在空间的压缩或变换。 -
--lora_rank 16
:这个参数设置了LoRA层的秩(rank),秩的大小影响LoRA模型的复杂度和表现能力。 -
--lora_alpha 4.0
:这个参数设置了LoRA层相对于原始预训练模型的缩放因子(alpha),它决定了LoRA层对最终模型输出的影响程度。 -
--dataset_path data/lora_dataset_processed
:这个参数指定了训练数据集的路径,数据集应该是经过处理的,以符合训练脚本的要求。 -
--output_path ./models
:这个参数指定了训练好的模型(包括LoRA层)的保存路径。 -
--max_epochs 1
:这个参数设置了训练的最大轮次(epoch)为1,这意味着整个数据集将被遍历一次以进行训练。 -
--center_crop
:这个参数可能指示训练脚本在预处理图像时使用中心裁剪的方式。 -
--use_gradient_checkpointing
:这个参数启用了梯度检查点(gradient checkpointing),这是一种节省内存的技术,允许训练更大的模型,尽管它会略微增加计算时间。 -
--precision "16-mixed"
:这个参数指定了训练的精度为混合精度(16位),这可以加速训练过程并减少内存使用。
os.system(cmd)
调用实际上会执行这个命令,就好像在命令行中手动输入并运行它一样。
加载模型
from diffsynth import ModelManager, SDXLImagePipeline
from peft import LoraConfig, inject_adapter_in_model
import torch
# 接收秩、权重、权重文件的路径。
def load_lora(model, lora_rank, lora_alpha, lora_path):
# 使用 `LoraConfig` 配置 LoRA 适配器,指定要修改的目标模块(这里为 Transformer 层的查询(`to_q`)、键(`to_k`)、值(`to_v`)和输出(`to_out`)部分)。
lora_config = LoraConfig(
r=lora_rank,
lora_alpha=lora_alpha,
init_lora_weights="gaussian",
target_modules=["to_q", "to_k", "to_v", "to_out"],
)
# 使用 `inject_adapter_in_model` 将配置好的 LoRA 适配器注入到模型中。
model = inject_adapter_in_model(lora_config, model)
# 使用 `torch.load` 加载 LoRA 权重文件,并将这些权重加载到模型中。
state_dict = torch.load(lora_path, map_location="cpu")
model.load_state_dict(state_dict, strict=False)
# 返回加载了 LoRA 权重的模型。
return model
# Load models
# 使用 ModelManager 创建一个模型管理器实例
# 指定数据类型(torch.float16)、设备(cuda)和包含模型文件的路径列表。
model_manager = ModelManager(torch_dtype=torch.float16, device="cuda",
file_path_list=[
"models/kolors/Kolors/text_encoder",
"models/kolors/Kolors/unet/diffusion_pytorch_model.safetensors",
"models/kolors/Kolors/vae/diffusion_pytorch_model.safetensors"
])
# 使用 `SDXLImagePipeline.from_model_manager` 从模型管理器创建图像生成管道(pipe)。
pipe = SDXLImagePipeline.from_model_manager(model_manager)
# Load LoRA
#调用 `load_lora` 函数,将 LoRA 权重加载到 `pipe.unet`(即 U-Net 模型)中。
# 这里指定了 LoRA 的秩(`lora_rank=16`)、LoRA 的权重(`lora_alpha=2.0`)和 LoRA 权重文件的路径(`lora_path`)。
pipe.unet = load_lora(
pipe.unet,
lora_rank=16, # This parameter should be consistent with that in your training script.
lora_alpha=2.0, # lora_alpha can control the weight of LoRA.
lora_path="models/lightning_logs/version_0/checkpoints/epoch=0-step=500.ckpt"
)
基于 Diffusion Models 的图像生成框架(这里使用 diffsynth
)中加载和使用 LoRA(Low-Rank Adaptation)权重。**LoRA 是一种轻量级的方法,用于微调预训练模型,而不需要修改模型的所有权重。**这在计算资源有限的情况下特别有用,因为它只修改模型的一小部分参数。
5.生成图像
torch.manual_seed(0)
image = pipe(
prompt="二次元,一个紫色短发小女孩,在家中沙发上坐着,双手托着腮,很无聊,全身,粉色连衣裙",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("1.jpg")
6.尝试生成其他图片
写一个新的,重新运行
7.上传数据
1.移动结果文件
创建terminal,粘贴如下命令,回车执行
mkdir /mnt/workspace/kolors/output & cd
cp /mnt/workspace/kolors/models/lightning_logs/version_0/checkpoints/epoch\=0-step\=500.ckpt /mnt/workspace/kolors/output/
cp /mnt/workspace/kolors/1.jpg /mnt/workspace/kolors/output/
2.下载结果文件
双击进入output文件夹,分别下载两个文件到本地
3.创建并上传模型所需内容