目录
1. DataWhale AI夏令营学习活动介绍
Datawhale AI夏令营是一个在暑期举办的大规模AI学习活动,旨在汇聚产学研资源和开源社区力量,为学习者提供丰富的学习机会和实践机会,提升专业能力和就业竞争力。Datawhale社区成立于2018年,是国内AI领域最大的开源学习组织,汇聚了众多有开源精神和探索精神的开源贡献者,给我们提供了一些丰富的资源,详细信息可以到Datawhale查看。目前第四期夏令营正在进行,针对不同基础的同学,这一期有以下四个方向可以报名:
一个人最多可以同时报名其中的两个方向,我这次是报名了方向一(AIGC方向)和方向三(大模型技术方向)。按照方向一的学习规划,首先完成task1(Datawhale)的任务,对给出的baseline进行跑通,并初步阅读并理解代码流程与内容。
2. 可图Kolors-LoRA风格故事挑战赛
这次的AIGC方向的培训主要是基于魔搭社区“可图Kolors-LoRA风格故事挑战赛”开展的实践学习,比赛详情见官网。需要在可图Kolors模型的基础上训练LoRA模型,生成各种风格的图像,要基于LoRA模型生成8张图片组成连贯故事。baseline中提供的样例跑通之后可以生成“偶像少女养成日记”的连贯故事图像组。
3. AIGC与文生图
3.1 AIGC
AIGC,即人工智能生成内容(Artificial Intelligence Generated Content),是一种利用人工智能技术自动生成各种形式内容的技术,包括文本、图片、音频、视频和3D模型等。其核心思想是利用人工智能算法生成具有一定创意和质量的内容。通过训练模型和大量数据的学习,AIGC可以根据输入的条件或指导,生成与之相关的内容。例如,通过输入关键词、描述或样本,AIGC可以生成与之相匹配的文章、图像、音频等。
AIGC技术在多个领域有广泛应用,包括但不限于软件开发、游戏开发、广告营销、教育和培训等。在软件开发领域,AIGC可以自动生成软件文档、API文档以及自动化测试代码,提高开发效率 。在游戏领域,AIGC技术可以用于生成游戏内的内容,如角色、场景等,增强游戏的丰富性和互动性。我们熟悉的ChatGPT就是AIGC的典型应用。
3.2 文生图
文生图是AIGC技术中的一项关键应用,它通过文字描述将文字转化为图像。使用AIGC技术通过大量数据训练,学习语言和视觉内容之间的关系,进而根据文本描述生成相应的图像内容。文生图技术的发展得益于深度学习模型的快速发展,特别是生成对抗网络(GAN)、自回归模型和扩散模型等。其中,扩散模型作为当前主流的生成模型,在图像质量、参数量、生成速度和训练成本等多个指标上表现优异。它主要以SD系列基础模型为主,以及在其基础上微调的lora模型和人物基础模型等,其一般过程如下图(源自Datawhale)
下面简单的介绍一下一些相关的知识点
3.2.1 提示词(prompt)
在文生图中,"提示词"(Prompt)是用于引导AI生成特定图像的文本描述,它对生成的图像内容起着至关重要的作用,一般写法:主体描述,细节描述,修饰词,艺术风格,艺术家。以下是一些撰写有效提示词的要点:
- 具体性:提示词应该尽可能的具体,详细描述想要生成图像的内容,包括场景、对象、动作等元素;
- 风格指定:如果希望图像具有特定的艺术风格或来自某位艺术家,可以在提示词中加入艺术家的名字或风格描述;
- 使用标签语法:利用关键词和标签来增强提示词的效果,例如使用"masterpiece, best quality"等标签来提升图像质量;
- 情感和氛围:在提示词中加入情感或氛围的描述,如"peaceful"、"mysterious"等,可以帮助生成具有特定情感色彩的图像;
- 光照和色彩:描述图像中的光照条件和色彩,例如"soft lighting"、"vibrant colors"等,可以影响图像的最终视觉效果;
- 分辨率和细节:使用如"high resolution"、"highly detailed"等词汇来指导AI生成高清晰度和细节丰富的图像;
- 负面提示词:使用负面提示词来排除不想要的元素或特征,例如"no people"、"not blurry"等,有助于优化生成的图像;
- 关键词权重:通过"(keyword: factor)"的格式来调整关键词的权重,控制其在图像生成中的重要性;
- 利用现有资源:参考一些专门的AI提示词生成器或网站来获取灵感和建议,比如"Ai 画廊 - AI 关键词生成器";
- 实验和迭代:生成图像是一个实验和迭代的过程,可能需要多次尝试和调整提示词来获得最佳结果。
3.2.2 LoRA
LoRA(Low-Rank Adaptation)是一种针对大型预训练模型(如大型语言模型)的微调技术。它的主要目的是在不显著增加计算和存储成本的情况下,通过引入低秩矩阵来近似表示模型权重的更新,从而实现模型的快速调整和适应新任务。具体来说,LoRA通过在模型的权重矩阵中添加一个低秩矩阵来进行调整,而不是直接更新原始的高维权重矩阵,这样可以显著减少训练参数的数量,降低计算和存储成本。
Lora不是指单一的具体模型,而是指一类通过特定微调技术应用于基础模型的扩展应用。在Stable Diffusion这一文本到图像合成模型的框架下,Lora被用来对预训练好的大模型进行针对性优化,以实现对特定主题、风格或任务的精细化控制。
4. baseline的跑通与一些理解
根据Datawhale AI夏令营给出的task1的教程(Datawhale),可以非常顺利流畅的跑通baseline,关于开通阿里云PAI-DSW、“可图Kolors-LoRA风格故事挑战赛”的报名以及魔搭社区创建PAI实例等一些操作,在教程里面已经非常具体详细了,我在这里也不过多的介绍,以下主要对baseline中的一些流程与代码疏通做一些介绍。
4.1 下载baseline
启动在魔搭平台创建好的实例(启动需要一些时间,慢慢等待!)打开之后,在终端输入以下命令克隆baseline.
git lfs install
git clone https://www.modelscope.cn/datasets/maochase/kolors.git
4.2 环境安装与相关库介绍
进入下载好的baseline文件中,点开baseline.ipynb文件。
进入之后第一个代码块就是安装Data-Juicer和DiffSynth-Studio,点击运行,正常情况下等待大概25分钟左右就可以安装好这两个库。
- Data-Juicer 是一个为大语言模型(LLMs)设计的一站式多模态数据处理系统,它提供超过80个高度系统化且可复用的算子,支持文本、图像、音频和视频等多种数据类型的处理,简单来说它就是数据处理和转换的工具,主要用于简化数据的提取、转换和加载过程。
- DiffSynth-Studio 是一个创新的扩散引擎,专门设计用于实现图片和视频的风格转换,它支持多种先进的扩散模型,如 Stable Diffusion、ControlNet、AnimateDiff 等,并提供了Diffutoon渲染技术,用于高质量的图像和视频渲染,简单来说就是一个高效微调训练大模型的工具。
4.3 数据集下载与介绍
注意安装好相关库后需要重启一下kernel!不然可能会占用资源,导致后续代码运行出错。baseline中第二个代码块点击运行加载数据集。
#导入MsDataset类,从 ModelScope 平台加载数据集
from modelscope.msdatasets import MsDataset
#加载数据集
ds = MsDataset.load(
'AI-ModelScope/lowres_anime', #AI-ModelScope仓库中的lowres_anime数据集。
subset_name='default',
split='train', #加载数据集的训练部分
cache_dir="/mnt/workspace/kolors/data" #数据被缓存的路径
)
baseline第三个代码块用与保存数据集中的图片以及元数据,其步骤主要是:
- 创建目录:首先创建两个目录,用于存储处理后的图像和元数据文件;
- 处理数据:遍历数据集中每个数据项,将图像转换为 RGB 格式,并保存到指定的目录中;
- 生成元数据:为每个图像生成一个元数据文件,包含图像路径和文本信息,并将其保存到一个 JSONL 文件中。
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)
#将数据集中的图像转换为 RGB 格式并保存到指定目录,同时生成包含图像路径和文本的元数据文件。
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")
lowres_anime数据集是一个动漫/漫画/2D 角色数据集,以下为该数据集中的数据示例。
4.4 数据处理
第四个代码块的作用是创建一个Data-Juicer的配置文件,定义了数据处理的参数和流程,然后使用这个配置文件来执行数据处理任务。
#定义一个多行字符串,用与存储Data-Juicer的配置信息
data_juicer_config = """
# global parameters
project_name: 'data-process' #项目名称
dataset_path: './data/data-juicer/input/metadata.jsonl' #数据集文件的路径
np: 4 #设置处理数据集时使用的子进程的数量
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())
#调用Data-Juicer的数据处理工具dj-process
!dj-process --config data/data-juicer/data_juicer_config.yaml
第五个代码块用于从JSONL文件中提取图像和文本数据,然后将它们保存到Pandas DataFrame中,并最终导出为CSV文件。处理完之后的训练集剩下129张图像。
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
4.5 模型训练
1、下载模型
from diffsynth import download_models
download_models(["Kolors", "SDXL-vae-fp16-fix"])
2、查看训练脚本的输入参数
!python DiffSynth-Studio/examples/train/kolors/train_kolors_lora.py -h
3、开始训练模型
执行命令行脚本训练一个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)
4.6 图片生成
1、 加载模型
以下加载模型的代码主要作用是配置和加载一个带有LoRA适配器的图像处理管道,特别是针对U-Net模型部分。LoRA技术允许模型在不显著增加参数数量的情况下,通过微调少量参数来适应新任务或数据集。这个管道可以用于图像生成或编辑任务,例如风格转换或超分辨率等。通过这种方式,可以在保持模型大小和计算效率的同时,提高模型的性能和适应性。其具体步骤如下:
- 导入用于模型管理、配置和深度学习操作的模块;
- 定义一个函数,用于加载和注入 LoRA 模型;
- 使用ModelManager和SDXLImagePipeline加载预训练模型;
- 使用load_lora函数加载 LoRA 模型,并将其注入到预训练模型中。
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"
)
2、生成图像
使用训练好的图像生成模型,根据给定的正向和反向描述词分别生成八张图像。
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")
使用basseline中生成的这八张图像故事性非常连贯,而且非常美观,但是在一些地方还是具有一点不合理性。比如第一张图和第六张图中的手指看着非常奇怪,还有第二张图中,其背影并不是二次元人物背景,而是现实人物背景,看着有点违和。
5. 更换Prompt生成自己的图像
我本来想自己写Prompt生成八张具有连贯故事性的图像,但是在试了很多很多次之后发现关于Prompt的编写并没有想象中的那么简单,限定性很强,可能是因为训练的数据集太小,这个模型的局限性太大,有些生成的图像看上去非常奇怪。
下面是我生成的第一个图像,本来想写一个小男孩减肥成功的励志小故事,但是一直卡在肥胖这里,只有第一张图像生成的跟我想的差不多,但是身体结构比例上看着还是有点奇怪。
torch.manual_seed(0)
image = pipe(
prompt="二次元,一个粉色短发的小男孩,在家中的沙发上坐着吃着零食,他的正前方有一个桌子,桌子上放满了零食,有薯片、奶茶、可乐、辣条等,他的两只手里都拿着零食,他穿着白色短袖上衣与黑色短裤,他非常胖", #正向描述词:想要生成的图片应该包含的内容
negative_prompt="露出肚子,变形、模糊,丑陋,低对比度", #反向描述词:不希望生成的图片的内容
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("9.jpg")
在生成第二张图像时,不管怎么修改Prompt还是一直没有“胖”的体现,而且背影也总是展现为侧身。
torch.manual_seed(1)
image = pipe(
prompt="二次元,一个粉色短发很胖很胖的男孩,他低着头走在大街上,他非常胖,被大家小声嘀咕,他穿着白色短袖上衣与黑色短裤,背面,全身,站在路中央,他非常胖,身躯庞大",
negative_prompt="丑陋、变形、半个身子,瘦",
cfg_scale=4,
num_inference_steps=50, height=1024, width=1024,
)
image.save("10.jpg")
后续还是要多学习多尝试尝试!