目录
概览
目标:进一步理解baseline代码,并学习如何利用AI辅助提升自学能力,以应对未来挑战
策略:掌握提问技巧,快速学习
技术挑战
AI生图的难点:画手挑战、“恐怖谷效应”……
准备——通义千问
通义千问是阿里云开发的一款超大规模语言模型,被命名为Qwen。被设计用来理解和生成人类语言,能够帮助用户完成各种任务,比如回答问题、创作文字、提供建议、翻译语言、编写代码等等。知识涵盖了多种领域,能够基于已有的知识和数据,以对话的形式与用户进行交流,力求提供准确、安全且有帮助的信息。
精读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
1.分析代码的主体结构
这段代码可以分为以下几个主要的工作流程部分:
1. 环境准备与依赖安装:
这部分通过使用`!pip install`命令安装了一系列Python包,包括`simple-aesthetics-predictor`、`data-juicer`、`peft`、`lightning`、`pandas`、`torchvision`以及`DiffSynth-Studio`。
2. 数据集加载和预处理:
- 加载名为`lowres_anime`的数据集,并将其保存到指定的缓存目录中。
- 遍历数据集中的每一项,将图像转换为RGB格式并保存到磁盘,同时构建元数据(metadata),包含文本描述和图像路径,写入JSONL文件。
3. 数据过滤与处理配置:
- 创建`data_juicer`的配置文件,用于定义数据处理的参数,如项目名称、数据集路径、过滤条件等。
- 使用`data_juicer`对数据进行预处理,包括过滤图像尺寸和长宽比。
4. 数据整理与模型评估:
- 将处理后的数据集读取出来,整理成DataFrame格式并保存为CSV文件。
- 利用CLIP模型对图像和文本进行编码,计算图像-文本相似性得分。
5. 自定义数据集类与数据加载器创建:
- 定义了一个`CustomDataset`类,用于将DataFrame中的数据转化为PyTorch DataLoader可接受的格式,便于后续训练或评估。
6. StableDiffusion模型生成图像:
- 加载`StableDiffusionPipeline`模型,设置随机种子以确保生成结果的一致性。
- 根据不同的提示(prompt)和负面提示(negative prompt),生成一系列二次元风格的图像,并保存到本地。
7. 图像拼接展示:
- 最后,代码将生成的多个图像拼接到一起形成一个大的图像,并调整大小以方便查看。
整体来看,这段代码涉及了从数据加载、预处理、模型评估到图像生成的完整流程,旨在利用深度学习模型对二次元风格的图像进行理解和生成。
2.逐行解释代码
这段代码主要涵盖了几个部分:安装依赖包、处理数据集、使用CLIP模型进行文本与图像匹配、构建自定义数据集、使用Stable Diffusion模型生成图像,以及最后将生成的图像进行合并。下面是对每一部分的逐行分析:
1.安装依赖包
安装`simple-aesthetics-predictor`包,用于评估图像的美学质量。
!pip install simple-aesthetics-predictor
以可编辑模式安装`data-juicer`包,这通常意味着该包将从源代码目录安装,允许对代码进行实时修改而不必重新安装。
!pip install -v -e data-juicer
卸载`pytorch-lightning`包,使用`-y`标志确认卸载操作。
!pip uninstall pytorch-lightning -y
安装`peft`(用于模型微调)、`lightning`(可能是指`pytorch-lightning`的新版本或分支)、`pandas`(用于数据处理)、和`torchvision`(用于计算机视觉任务)。
!pip install peft lightning pandas torchvision
以可编辑模式安装`DiffSynth-Studio`包,可能与音频合成相关。
!pip install -e DiffSynth-Studio
2.处理数据集
导入`MsDataset`类,用于加载ModelScope提供的数据集。
from modelscope.msdatasets import MsDataset
加载名为`AI-ModelScope/lowres_anime`的数据集,选择`default`子集,使用训练集,缓存目录为`/mnt/workspace/kolors/data`。
ds = MsDataset.load(
'AI-ModelScope/lowres_anime',
subset_name='default',
split='train',
cache_dir="/mnt/workspace/kolors/data"
)
创建两个目录用于存储处理过的图像和`data-juicer`的输入数据。
import json, os
from data_juicer.utils.mm_utils import SpecialTokens
遍历数据集,将每个图像转换为RGB模式,保存到指定目录,并构建元数据字典,包含文本标签和图像路径,写入到`metadata.jsonl`文件中。
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")
3.使用`data-juicer`处理数据
定义`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
"""
将配置字符串写入到`data_juicer_config.yaml`文件中。
with open("data/data-juicer/data_juicer_config.yaml", "w") as file:
file.write(data_juicer_config.strip())
调用`dj-process`命令,传入配置文件路径,处理数据集。
!dj-process --config data/data-juicer/data_juicer_config.yaml
4.数据处理和分析
读取处理后的结果,构建Pandas DataFrame,将文本和文件名列保存为CSV文件,并加载数据。
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)
5.使用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)
使用CLIP模型计算图像和文本之间的相似度得分,并应用softmax函数得到概率分布。
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
6.构建自定义数据集和DataLoader
定义`CustomDataset`类,继承自`torch.utils.data.Dataset`,用于构建自定义数据集,实现`__init__`, `__len__`, 和 `__getitem__`方法。
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
实例化`CustomDataset`,并创建`DataLoader`以批量加载数据。
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)
7.使用Stable Diffusion模型生成图像
加载`StableDiffusionPipeline`模型,设置随机种子以确保生成结果的一致性,并将模型移动到GPU上。
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")
8.合并生成的图像
将之前生成的8幅图像加载为NumPy数组,按照特定布局拼接成一个大图像,然后调整大小为`(1024, 2048)`。
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
这段代码涉及了深度学习模型的使用、数据处理和图像生成等多个方面,展示了如何从数据集开始,经过一系列处理步骤,最终生成符合特定要求的图像。值得注意的是,代码中涉及的生成图像的提示包含了性别和年龄特征描述,以及一些情感色彩,这需要确保遵循适当的伦理标准和版权规定。