参考教程: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,根据赛题规则,图片符合要求。
总结
prompt数据集jsonclipclitpuctogit二次元diffusionstablediffusion提示词pandascsvguikolpytorch自定义amlapp