01 相关链接
可图Kolors-LoRA风格故事挑战赛_创新应用大赛_天池大赛-阿里云天池的赛制 (aliyun.com)
02 背景知识
一、文生图技术
文生图技术(Text-to-Image Generation)是一种利用人工智能技术将文本描述转化为图像的技术。其核心在于利用GAN等深度学习模型,根据用户输入的文本描述来生成相应的图像。
- 将文本描述进行编码处理,得到文本特征向量;
- 将该特征向量作为生成器的输入,通过生成器生成对应的图像;
- 利用判别器对生成的图像进行评估,以确保其符合文本描述的要求。
二、生成模型
(一)生成对抗网络(GAN)
生成对抗网络(Generative Adversarial Networ)是一类基于深度学习的无监督机器学习模型,它通过两个神经网络——生成器(Generator)和判别器(Discriminator)——的相互对抗训练,来提升生成数据的质量和真实性。
- 生成器(G) :负责接收随机噪声作为输入,并生成尽可能接近真实数据的假数据。
- 判别器(D) :负责区分输入的数据是真实的还是由生成器生成的假数据,并输出一个概率值来表示其判断结果。
在训练过程中,生成器试图欺骗判别器,使其无法区分生成的数据和真实数据;而判别器则努力提高自己的判别能力,以准确区分真实数据和假数据。这种对抗训练的过程不断迭代,直到生成器能够生成足以“欺骗”判别器的逼真数据,而判别器也无法再准确区分数据的真伪。
(二)变分自编码器(VAE)
变分自编码器(Variational Autoencoder)是一种基于概率生成模型的深度学习架构,它通过将输入数据编码为潜在空间的分布参数,然后从这些参数中解码出新的数据样本,生成与训练数据相似的新数据点。
- 编码器:负责将输入数据映射到潜在空间,并输出该空间的分布参数(通常是均值和方差)。这些参数定义了潜在空间中的一个分布,该分布描述了输入数据的潜在表示。
- 解码器:根据编码器输出的分布参数,从中采样得到潜在变量,并将这些潜在变量解码为原始数据的高品质复制。
在训练过程中,主要分为三个步骤:
- 编码:输入数据通过编码器被映射到潜在空间,并输出该空间的分布参数
- 解码:从潜在空间的分布中采样得到潜在变量,并通过解码器将其解码为原始数据的高品质复制
- 重构:计算重构数据与原始数据之间的误差,并通过反向传播算法更新编码器和解码器的参数。
(三)流模型(Flow-based models)
流模型(Flow-based models)是一类基于概率密度函数的深度学习模型,它通过构建可逆的神经网络变换,实现从一种分布到另一种分布的直接且可逆的映射。这类模型的主要特点是能够直接对数据的概率密度函数进行建模,从而在数据生成和推断方面具有显著优势。
(四)Stable Diffusion
Stable Diffusion基于Latent Diffusion Models(LDMs)实现,其工作原理主要基于扩散过程(Diffusion Process)和反向扩散过程(Reverse Diffusion Process)。
- 扩散过程:模型将随机噪声逐渐地向原始数据集的中心值靠近,学习数据的分布特性。
- 反向扩散过程:与扩散过程相反,该过程将噪声逐渐变换为用户所需的图像。通过参数化的马尔可夫链(Markov Chain)实现,具有较高的灵活性和可定制性。
使用VAE压缩图像,在压缩后的latents域上建模扩散过程
03 代码实践
第一步:安装
安装 Data-Juicer 和 DiffSynth-Studio
Data-Juicer 数据处理和转换工具
将复杂的数据原料转化为易于“吸收”的精华。该系统强大之处在于它针对多模态数据的处理,覆盖了文本、图像、音频甚至视频,为当今和未来多模态模型的发展提供了强有力的支持。
DiffSynth-Studio 高效微调训练大模型工具
Diffutoon渲染技术通过结合深度学习和计算机图形学,实现了高质量的图像和视频渲染。Diffutoon技术主要利用了生成对抗网络(GANs)和扩散模型(Diffusion Models),能够生成具有高度真实感和艺术效果的图像和视频。
!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
重启 Notebook kernel
第二步:下载数据集
from modelscope.msdatasets import MsDataset
ds = MsDataset.load(
'AI-ModelScope/lowres_anime',
subset_name='default',
split='train',
cache_dir="/mnt/workspace/kolors/data"
)
保存数据集中的图片及元数据
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")
第三步:数据处理
使用 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
保存处理好的数据
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
第四步:训练模型
下载模型
from diffsynth import download_models
download_models(["Kolors", "SDXL-vae-fp16-fix"])
查看训练脚本的输入参数
!python DiffSynth-Studio/examples/train/kolors/train_kolors_lora.py -h
开始训练
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)
参数解释:
--pretrained_unet_path:指定预训练的U-Net模型路径。U-Net是一种常用于图像分割和生成的神经网络结构。这里的模型用于图像生成过程的一部分。--pretrained_text_encoder_path:指定预训练的文本编码器路径。这个编码器用于将文本描述转换为模型可以理解的嵌入向量,以指导图像的生成。--pretrained_fp16_vae_path:指定预训练的FP16 VAE(变分自编码器)模型路径。这里的FP16指的是模型使用了半精度浮点数,这有助于减少内存占用和加速训练。--lora_rank 16:设置LORA的秩为16。LORA是一种低秩适应技术,用于微调大型预训练模型,而不需要修改原始模型的全部参数。--lora_alpha 4.0:设置LORA的alpha值为4.0。--dataset_path:指定训练数据集的路径。--output_path:指定训练后模型输出的路径。--max_epochs 1:设置训练的最大轮次为1。--center_crop:在训练过程中使用中心裁剪作为数据增强的一部分。--use_gradient_checkpointing:启用梯度检查点。--precision "16-mixed":设置训练过程中的精度为混合精度16位。
加载模型
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):
lora_config = LoraConfig(
r=lora_rank,
lora_alpha=lora_alpha,
init_lora_weights="gaussian",
target_modules=["to_q", "to_k", "to_v", "to_out"],
)
model = inject_adapter_in_model(lora_config, model)
state_dict = torch.load(lora_path, map_location="cpu")
model.load_state_dict(state_dict, strict=False)
return model
# Load models
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"
])
pipe = SDXLImagePipeline.from_model_manager(model_manager)
# Load LoRA
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"
)
生成图像
torch.manual_seed(0)
image = pipe(
prompt="二次元,一个紫色短发小女孩,在家中沙发上坐着,双手托着腮,很无聊,全身,粉色连衣裙",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("1.jpg")
torch.manual_seed(1)
image = pipe(
prompt="二次元,日系动漫,演唱会的观众席,人山人海,一个紫色短发小女孩穿着粉色吊带漏肩连衣裙坐在演唱会的观众席,舞台上衣着华丽的歌星们在唱歌",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("2.jpg")
torch.manual_seed(2)
image = pipe(
prompt="二次元,一个紫色短发小女孩穿着粉色吊带漏肩连衣裙坐在演唱会的观众席,露出憧憬的神情",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度,色情擦边",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("3.jpg")
torch.manual_seed(5)
image = pipe(
prompt="二次元,一个紫色短发小女孩穿着粉色吊带漏肩连衣裙,对着流星许愿,闭着眼睛,十指交叉,侧面",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度,扭曲的手指,多余的手指",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("4.jpg")
torch.manual_seed(0)
image = pipe(
prompt="二次元,一个紫色中等长度头发小女孩穿着粉色吊带漏肩连衣裙,在练习室练习唱歌",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("5.jpg")
torch.manual_seed(1)
image = pipe(
prompt="二次元,一个紫色长发小女孩穿着粉色吊带漏肩连衣裙,在练习室练习唱歌,手持话筒",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("6.jpg")
torch.manual_seed(7)
image = pipe(
prompt="二次元,紫色长发少女,穿着黑色连衣裙,试衣间,心情忐忑",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("7.jpg")
torch.manual_seed(0)
image = pipe(
prompt="二次元,紫色长发少女,穿着黑色礼服,连衣裙,在台上唱歌",
negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("8.jpg")
import numpy as np
from PIL import Image
images = [np.array(Image.open(f"{i}.jpg")) for i in range(1, 9)]
image = np.concatenate([
np.concatenate(images[0:2], axis=1),
np.concatenate(images[2:4], axis=1),
np.concatenate(images[4:6], axis=1),
np.concatenate(images[6:8], axis=1),
], axis=0)
image = Image.fromarray(image).resize((1024, 2048))
image
04 Ending
修改了一下图片元素描述: