参考教程:Docshttps://datawhaler.feishu.cn/wiki/KxI2wIeAJiRQWhkRt9dcqlNVnFR?from=from_copylink
精读baseline
代码整理
使用通义千问辅助理解baseline代码
首先将baseline中的代码全部整理在一起:
!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
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_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/data-juicer/output/images", exist_ok=True)
with open("./data/data-juicer/output/result.jsonl", "r") as f:
for line in tqdm(f):
metadata = json.loads(line)
texts.append(metadata["text"])
file_names.append(metadata["image"][0])
df = pd.DataFrame({"text": texts, "file_name": file_names})
df.to_csv("./data/data-juicer/output/result.csv", index=False)
df
from transformers import CLIPProcessor, CLIPModel
import torch
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
images = [Image.open(img_path) for img_path in df["file_name"]]
inputs = processor(text=df["text"].tolist(), images=images, return_tensors="pt", padding=True)
outputs = model(**inputs)
logits_per_image = outputs.logits_per_image # this is the image-text similarity score
probs = logits_per_image.softmax(dim=1) # we can take the softmax to get the probabilities
probs
from torch.utils.data import Dataset, DataLoader
class CustomDataset(Dataset):
def __init__(self, df, processor):
self.texts = df["text"].tolist()
self.images = [Image.open(img_path) for img_path in df["file_name"]]
self.processor = processor
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
inputs = self.processor(text=self.texts[idx], images=self.images[idx], return_tensors="pt", padding=True)
return inputs
dataset = CustomDataset(df, processor)
dataloader = DataLoader(dataset, batch_size=8)
for batch in dataloader:
outputs = model(**batch)
logits_per_image = outputs.logits_per_image
probs = logits_per_image.softmax(dim=1)
print(probs)
import torch
from diffusers import StableDiffusionPipeline
torch.manual_seed(1)
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v-1-4", torch_dtype=torch.float16)
pipe = pipe.to("cuda")
prompt = "二次元,一个紫色长发小女孩穿着粉色吊带漏肩连衣裙,在练习室练习唱歌,手持话筒"
negative_prompt = "丑陋、变形、嘈杂、模糊、低对比度"
guidance_scale = 4
num_inference_steps = 50
image = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
guidance_scale=guidance_scale,
num_inference_steps=num_inference_steps,
height=1024,
width=1024,
).images[0]
image.save("example_image.png")
image
from PIL import Image
torch.manual_seed(1)
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
分析主体架构
输入prompt:
你是一个优秀的python开发工程师,现在我们需要你帮我们分析这个代码的主体框架,你需要把代码按照工作流分成几部分,用中文回答我的问题。
{此处替换成baseline代码}
返回的结果:
这段代码可以分为几个主要的工作流程部分来理解:
1. 环境准备和依赖安装
首先是一系列的包安装与卸载命令:
- 安装 `simple-aesthetics-predictor` 和 `data-juicer`。
- 卸载 `pytorch-lightning` 并安装 `peft`, `lightning`, `pandas`, `torchvision`。
- 安装 `DiffSynth-Studio`。
这部分主要是为了确保所需的库已经安装并且版本正确。
2. 数据集加载与处理
接着是从 ModelScope 加载了一个名为 `lowres_anime` 的数据集,并将其保存到指定目录下。这部分包括了对数据集中的每张图片进行转换为 RGB 模式并保存,同时将图片路径和对应的标签文本写入 JSONL 文件中。
3. 数据过滤与预处理
通过配置文件 `data_juicer_config.yaml` 来定义如何使用 `data-juicer` 过滤数据集。这里定义了图片的尺寸和宽高比的过滤条件。然后运行 `dj-process` 命令来执行数据过滤,并输出过滤后的结果到新的 JSONL 文件中。
4. 数据整理与特征提取
这部分代码将过滤后的数据集整理成 DataFrame,并保存为 CSV 文件。接着使用 CLIP 模型来计算图像和文本之间的相似度分数。
5. 自定义数据集与数据加载器
定义了一个 PyTorch 的 `Dataset` 类 `CustomDataset` 用于读取 CSV 文件中的数据,并创建了一个 `DataLoader` 用于批量加载数据。然后使用 CLIP 模型来进一步处理这些数据。
6. 图像生成
这部分代码使用 `StableDiffusionPipeline` 来根据不同的提示(prompts)生成一系列二次元风格的图像,并保存到文件中。
7. 图像合并展示
最后一步是将生成的多个图像合并成一个大图以便于展示和比较。
总结起来,整个代码流程包括了环境配置、数据集加载、数据预处理、特征提取、模型训练数据准备以及图像生成和展示等多个步骤。每个步骤都是前一步骤的基础,逐步构建起一个完整的图像生成和分析系统。
返回结果的截图如下:
逐行解释代码
输入prompt:
你是一个优秀的python开发工程师,现在我们需要你帮我们逐行分析这个代码,用中文回答我的问题。
{此处替换成baseline代码}
返回的结果:
这段代码主要分为几个部分:安装和卸载一些Python包,加载数据集并处理图像数据,使用Data-Juicer进行数据清洗,计算图像与文本的相似度,创建自定义数据集,以及使用StableDiffusionPipeline生成图像。
下面是逐行的注释:
安装和卸载Python包
# 安装simple-aesthetics-predictor包
!pip install simple-aesthetics-predictor
# 安装data-juicer包的开发版本
!pip install -v -e data-juicer
# 卸载pytorch-lightning包
!pip uninstall pytorch-lightning -y
# 安装peft, lightning, pandas 和 torchvision包
!pip install peft lightning pandas torchvision
# 安装DiffSynth-Studio包的开发版本
!pip install -e DiffSynth-Studio
加载数据集
# 导入MsDataset类,用于加载数据集
from modelscope.msdatasets import MsDataset
# 加载名为'AI-ModelScope/lowres_anime'的数据集,subset_name为'default',split为'train',cache_dir指定缓存目录
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)
# 创建data-juicer输入数据目录,如果不存在则创建
os.makedirs("./data/data-juicer/input", exist_ok=True)
# 打开文件以写入metadata.jsonl文件
with open("./data/data-juicer/input/metadata.jsonl", "w") as f:
# 遍历数据集
for data_id, data in enumerate(tqdm(ds)):
# 将图像转换为RGB格式并保存到指定路径
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配置字符串
data_juicer_config = """
# 全局参数
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:
- 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
"""
# 写入Data-Juicer配置文件
with open("data/data-juicer/data_juicer_config.yaml", "w") as file:
file.write(data_juicer_config.strip())
# 运行Data-Juicer进行数据处理
!dj-process --config data/data-juicer/data_juicer_config.yaml
处理Data-Juicer输出
# 导入pandas和其他必要库
import pandas as pd
import os, json
from PIL import Image
from tqdm import tqdm
# 创建输出图像目录,如果不存在则创建
os.makedirs("./data/data-juicer/output/images", exist_ok=True)
# 读取Data-Juicer处理后的结果
with open("./data/data-juicer/output/result.jsonl", "r") as f:
texts, file_names = [], []
for line in tqdm(f):
# 解析每行JSON数据
metadata = json.loads(line)
texts.append(metadata["text"])
file_names.append(metadata["image"][0])
# 将文本和文件名转换为DataFrame并保存为CSV文件
df = pd.DataFrame({"text": texts, "file_name": file_names})
df.to_csv("./data/data-juicer/output/result.csv", index=False)
# 显示DataFrame内容
df
使用CLIP模型计算相似度
# 导入CLIP模型和处理器
from transformers import CLIPProcessor, CLIPModel
import torch
# 加载预训练的CLIP模型和处理器
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
# 加载图像数据
images = [Image.open(img_path) for img_path in df["file_name"]]
# 对图像和文本进行预处理
inputs = processor(text=df["text"].tolist(), images=images, return_tensors="pt", padding=True)
# 计算模型输出
outputs = model(**inputs)
# 获取图像-文本相似度分数
logits_per_image = outputs.logits_per_image
# 计算概率
probs = logits_per_image.softmax(dim=1)
# 显示概率
probs
创建自定义数据集
# 导入PyTorch相关库
from torch.utils.data import Dataset, DataLoader
# 定义自定义数据集类
class CustomDataset(Dataset):
def __init__(self, df, processor):
self.texts = df["text"].tolist()
self.images = [Image.open(img_path) for img_path in df["file_name"]]
self.processor = processor
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
inputs = self.processor(text=self.texts[idx], images=self.images[idx], return_tensors="pt", padding=True)
return inputs
# 创建自定义数据集实例
dataset = CustomDataset(df, processor)
# 创建数据加载器
dataloader = DataLoader(dataset, batch_size=8)
# 遍历数据加载器
for batch in dataloader:
outputs = model(**batch)
logits_per_image = outputs.logits_per_image
probs = logits_per_image.softmax(dim=1)
print(probs)
使用StableDiffusionPipeline生成图像
# 导入StableDiffusionPipeline
import torch
from diffusers import StableDiffusionPipeline
# 设置随机种子以确保结果可重复
torch.manual_seed(1)
# 加载预训练的StableDiffusion模型
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v-1-4", torch_dtype=torch.float16)
pipe = pipe.to("cuda")
# 设置提示和负向提示
prompt = "二次元,一个紫色长发小女孩穿着粉色吊带漏肩连衣裙,在练习室练习唱歌,手持话筒"
negative_prompt = "丑陋、变形、嘈杂、模糊、低对比度"
guidance_scale = 4
num_inference_steps = 50
# 生成图像并保存
image = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
guidance_scale=guidance_scale,
num_inference_steps=num_inference_steps,
height=1024,
width=1024,
).images[0]
image.save("example_image.png")
# 使用不同提示生成多个图像
# (此处省略了重复的代码,每个调用都生成一张图像并保存)
合并生成的图像
# 导入PIL库
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
这段代码执行了一系列复杂的操作,从加载数据集开始,经过图像处理、数据清洗、计算相似度、创建数据集,最后使用StableDiffusionPipeline生成图像,并将它们合并成一个大图。
返回结果的截图如下:
实战演练:基于话剧的连环画制作
数据准备
使用通义千问生成提示词。
prompt准备:
你是一个文生图专家,我们现在要做一个实战项目,就是要编排一个文生图话剧
话剧由8张场景图片生成,你需要输出每张图片的生图提示词
具体的场景图片
1、女主正在上课
2、开始睡着了
3、进入梦乡,梦到自己站在路
4、王子骑马而来
5、两人相谈甚欢
6、一起坐在马背上
7、下课了,梦醒了
8、又回到了学习生活中
生图提示词要求
1、风格为古风
2、根据场景确定是使用全身还是上半身
3、人物描述
4、场景描述
5、做啥事情
例子: 古风,水墨画,一个黑色长发少女,坐在教室里,盯着黑板,深思,上半身,红色长裙
返回结果:
好的,根据您的要求,我会为每个场景创作一组生图提示词。我们来逐一制定这些提示词:
第一张图片 - 女主正在上课
风格: 古风
描述: 水墨画,一个穿着蓝色长裙的女子,黑色长发束起,上半身特写,坐在古色古香的教室里,专注地听着老师讲解,手中握着毛笔。
场景: 木质结构的教室,墙壁挂着书法作品,桌案上摆放着笔墨纸砚。
正在做的事情: 聚精会神听讲,偶尔低头在纸上记录。
第二张图片 - 开始睡着了
风格: 古风
描述: 水墨画,同一女子,黑色长发披散,身穿淡紫色长袍,上半身,头轻轻靠在桌上,嘴角挂着微笑,似乎正在做梦。
场景: 教室背景,周围的同学都在认真听课,只有她沉浸在梦乡之中。
正在做的事情: 在课桌上轻眠。
第三张图片 - 进入梦乡,梦到自己站在路上
风格: 古风
描述: 水墨画,女子全身像,穿着华丽的衣裳,站在一条青石小径上,四周是朦胧的云雾和古树。
场景: 一条通往远方的小径,两旁是古树参天,远处山峦叠嶂。
正在做的事情: 微微闭目,仿佛在感受自然的气息。
第四张图片 - 王子骑马而来
风格: 古风
描述: 水墨画,一位英俊的青年骑着一匹白马缓缓而来,身穿金色镶边的长袍,眼神温柔,望向女子的方向。
场景: 同样的小径,但此刻阳光洒落,王子与马的身影映照在小径上。
正在做的事情: 骑马接近女子,准备邀请她一同前行。
第五张图片 - 两人相谈甚欢
风格: 古风
描述: 水墨画,女子与王子并肩而立,两人面带笑容,眼神交流,身着华美的服饰。
场景: 小径旁的一片开阔地带,花丛中设有石桌石凳,四周环境宁静优美。
正在做的事情: 相互交谈,分享彼此的故事。
第六张图片 - 一起坐在马背上
风格: 古风
描述: 水墨画,女子与王子并排坐在马上,女子穿着轻盈的绿色长裙,王子穿着蓝色长袍,两人相视而笑,马儿在林间小道上缓缓前行。
场景: 林间小道,绿树成荫,鸟语花香。
正在做的事情: 共骑一马,欣赏沿途风景。
第七张图片 - 下课了,梦醒了
风格: 古风
描述: 水墨画,女子睁开眼,表情从惊讶变为微笑,黑色长发散落,身穿淡紫色长袍,上半身特写。
场景: 依旧是在教室里,同学们陆续离开。
正在做的事情: 从梦中醒来,发现自己仍然坐在教室里。
第八张图片 - 又回到了学习生活中
风格: 古风
描述: 水墨画,女子重新投入学习状态,黑色长发束起,身穿蓝色长裙,上半身特写,专心致志地书写。
场景: 教室内,其他同学也在埋头苦读,墙上挂着书画。
正在做的事情: 专心学习,埋头读书写字。
根据返回的结果适当调整,整理成如下表格:
图片编号
场景描述
正向提示词
反向提示词
图片1女主正在上课
古风,水墨画,一个黑色长发少女,坐在教室里,盯着黑板,深思,上半身,红色长裙
丑陋,变形,嘈杂,模糊,低对比度
图片2开始睡着了
古风,水墨画,一个黑色长发少女,坐在教室里,趴在桌子上睡着了,上半身,红色长裙
丑陋,变形,嘈杂,模糊,低对比度
图片3进入梦乡,梦到自己站在路旁
古风,水墨画,一个黑色长发少女,站在路边,上半身,红色长裙
丑陋,变形,嘈杂,模糊,低对比度
图片4王子骑马而来
古风,水墨画,一个英俊少年,骑着白马,上半身,白色衬衫
丑陋,变形,嘈杂,模糊,低对比度
图片5两人相谈甚欢
古风,水墨画,一个英俊少年,白色衬衫,一个黑色长发少女,红色长裙,两个人一起聊天,开心,上半身
丑陋,变形,嘈杂,模糊,低对比度
图片6一起坐在马背上
古风,水墨画,一个英俊少年,白色衬衫,一个黑色长发少女,红色长裙,两个人一起骑着马,全身
丑陋,变形,嘈杂,模糊,低对比度
图片7下课了,梦醒了
古风,水墨画,一个黑色长发少女,坐在教室里,下课铃声响了,同学们开始走动,从睡梦中醒来,深思,上半身,红色长裙
丑陋,变形,嘈杂,模糊,低对比度
图片8又回到了学习生活中
古风,水墨画,一个黑色长发少女,坐在教室里,盯着黑板,认真上课,上半身,红色长裙
丑陋,变形,嘈杂,模糊,低对比度
修改提示词并生成图片
修改提示词并运行baseline代码,可以从“加载模型”这一步开始运行,加载Task01中训练的LoRA权重。
结果展示
测试美学分数
运行以下代码获得美学分数:
import torch, os
from PIL import Image
from transformers import CLIPProcessor
from aesthetics_predictor import AestheticsPredictorV2Linear
from modelscope import snapshot_download
model_id = snapshot_download('AI-ModelScope/aesthetics-predictor-v2-sac-logos-ava1-l14-linearMSE', cache_dir="models/")
predictor = AestheticsPredictorV2Linear.from_pretrained(model_id)
processor = CLIPProcessor.from_pretrained(model_id)
device = "cuda"
predictor = predictor.to(device)
def get_aesthetics_score(image):
inputs = processor(images=image, return_tensors="pt")
inputs = {k: v.to(device) for k, v in inputs.items()}
with torch.no_grad():
outputs = predictor(**inputs)
prediction = outputs.logits
return prediction.tolist()[0][0]
def evaluate(folder):
scores = []
for file_name in os.listdir(folder):
if os.path.isfile(os.path.join(folder, file_name)):
image = Image.open(os.path.join(folder, file_name))
scores.append(get_aesthetics_score(image))
if len(scores) == 0:
return 0
else:
return sum(scores) / len(scores)
score = evaluate("./test1") # 换成自己的结果文件夹
print(score)
输出结果:
得到结果大于6,根据赛题规则,图片符合要求。
总结