成果展示
最近一直看奥运会乒乓球比赛,很是热血,想到了以前爱看的热血运动番,所以选定了日漫风格,想画一组主题为:乒乓女将养成记的迷你漫画,致敬奋战在奥运赛场的运动小将们!
图一:女生孙莎莎从上初中起就开始对乒乓球感兴趣,在家里看乒乓球比赛直播时,她也会拿着球拍有样学样地笔画着。
图二: 报社团时,孙莎莎报名了乒乓球社,刚开始学习乒乓球的时候,莎莎还不太适应,拿着球拍试了很久。
图三:学习乒乓球之路并不好走,遇到了很多困难,但这些困难反而激起里孙莎莎的斗志,让她更加想要挑战下去。下午的乒乓球馆里,阳光温暖明媚,其他同学都回家了,莎莎暗下决心,一定要练好乒乓球,在一个月后的校赛上拿到好成绩。
图四:莎莎第一次比赛出师不利,输了比赛,她沮丧地坐在地上休息。
图五:莎莎闷闷不乐,晚上独自来到乒乓球馆,寂静的球馆只有她一人,外面的星空灿烂,而莎莎陷入了沉思和自我怀疑,自己真的适合打乒乓球吗?练习付出了很多汗水,但并没有收获,还要坚持下去吗?
图六、经过了内心激烈的挣扎,孙莎莎还是决定跟自己赌一把,继续打下去。幸运的是,她的教练、朋友都支持、鼓励她,孙莎莎的斗志又回来了。
图七:孙莎莎更加投入地参与到训练当中,专注地打磨自己的球季(怎么这一张这么像越前龙马,网球王子乱入乒乓世界?)
图八:功夫不负有心人,孙莎莎在一次比赛中拿到了冠军奖杯,成了学校的传奇!
下面是技术实现的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
最开始肯定是导入各种包了。简要认识一下这些包的用途:
-
simple-aesthetics-predictor
: 这是一个用于图像美学预测的库。它可能用于评估图像的美观程度,例如通过计算美学分数或其它相关指标。这种工具通常用于图像处理、计算机视觉和相关应用。 -
data-juicer
: 是一个数据处理工具,支持配置化数据清洗、转换和增强,通过 YAML 配置文件定义处理步骤,适用于批量处理和数据格式转换。 -
peft
:用于大模型的微调,PEFT(Parameter-Efficient Fine-Tuning) -
lightning
:这是 PyTorch Lightning 的新版本(可能是pytorch-lightning
的替代),用于简化 PyTorch 的训练过程。 -
pandas
:用于处理数据表格(如 CSV 文件)和dataframe。 -
torchvision
:这个库提供常用的计算机视觉数据集和预训练模型。 -
DiffSynth-Studio
:基于扩散模型生成图像的包。
参数解析
!pip install 用来安装 Python 的库或包,这些库包提供了特定的功能。
!pip uninstall 是用来卸载已经安装的包。
-v 和 -e 是安装选项,前者是启用详细模式,后者表示以“开发模式”安装。这部分代码的作用是安装和配置程序运行所需的库和工具。(-v:控制输出的详细程度,让你看到更多的安装信息。
-e:使包安装在开发模式下,支持源代码的即时修改和调试。两者的区别在于,-v 是为了调试和获得更多信息,而 -e 是为了在开发过程中方便修改和测试代码。)
二、 加载数据集
from modelscope.msdatasets import MsDataset
ds = MsDataset.load(
'AI-ModelScope/lowres_anime',
subset_name='default',
split='train',
cache_dir="/mnt/workspace/kolors/data"
)
这里我们从魔搭 ModelScope 平台加载了一个名为 lowres_anime 的数据集。把它存储在 “/mnt/workspace/kolors/data” 路径下,作为训练数据使用。
三、处理数据并保存图片
将图像转换为 RGB 格式并保存到指定目录,同时创建包含图像路径和文本描述的元数据(metadata),将这些元数据保存到 metadata.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)
with open("./data/data-juicer/input/metadata.jsonl", "w") as f:
for data_id, data in enumerate(tqdm(ds)):
image = data["image"].convert("RGB")
#将图像保存到指定目录,文件名基于 data_id。
image.save(f"/mnt/workspace/kolors/data/lora_dataset/train/{data_id}.jpg")
#metadata是一个包含图像元数据的字典
metadata = {"text": "二次元", "image": [f"/mnt/workspace/kolors/data/lora_dataset/train/{data_id}.jpg"]}
#将 metadata 字典转换为 JSON 格式的字符串,并写入文件
f.write(json.dumps(metadata))
#在文件中写入换行符,以确保每个 JSON 对象在新的一行。
f.write("\n")
os.makedirs
创建文件夹。如果文件夹已经存在,exist_ok=True
会避免报错。创建 ./data/lora_dataset/train 目录,用于保存处理后的图像。创建 ./data/data-juicer/input 目录,用于保存数据处理所需的输入文件。- with open(…) as f:以写模式打开 metadata.jsonl 文件。with 语句会在操作完成后自动关闭文件。metadata.jsonl 文件用于存储每个图像的元数据,每行一个 JSON 对象。
- for data_id, data in enumerate(tqdm(ds)):遍历训练数据集 ds,使用
enumerate
为每个数据项分配一个唯一的 data_id。tqdm
显示进度条,方便跟踪处理进度。for 循环遍历数据集中的每一条数据,将图片转换为 RGB 格式并保存。
四、配置数据处理流程yaml文件
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())
-
这里创建的
data_juicer_config.yaml
配置文件定义了data-juicer这个数据处理工具需要遵循的规则和参数。这个配置文件指定了数据集路径、处理进程的数量以及一系列图像过滤规则(如宽高、宽高比等)。配置文件说明:
全局参数 (global parameters):
1. project_name: 指定项目名称。 2. dataset_path: 数据集文件的路径,这里是 metadata.jsonl 文件。 3. np: 设置用于处理数据的子进程数量。 4. text_keys 和 image_key: 定义数据集中用于文本和图片的字段名称。 5. image_special_token: 用于图像的特殊标记。 6. export_path: 处理完的数据结果的存储地址,文件名为result.jsonl
处理计划 (process schedule):
process:这是一个包含处理操作的列表,每个操作都有特定的参数。 1. image_shape_filter:过滤图像尺寸,设置最小宽度和高度。 2. image_aspect_ratio_filter:过滤图像的宽高比,设置最小和最大比率。
-
dj-process
是 data-juicer 提供的命令行接口工具。它用于根据配置文件中的设置执行数据处理任务。这个工具允许用户通过命令行来处理数据,而无需编写复杂的 Python 代码。data-juicer通过dj-process
工具读取这个配置文件,能够执行相应的数据处理操作。
五、数据处理
!dj-process --config data/data-juicer/data_juicer_config.yaml
- data-juicer通过 dj-process 工具读取配置文件,一键丝滑处理一系列数据处理。
六、保存数据处理结果到 CSV 文件
从 JSONL 文件中读取图像的元数据,提取每个图像的文本描述和文件名,并将这些信息保存到 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):
#将每行的 JSON 字符串解析为 Python 字典
metadata = json.loads(line)
#从字典中提取 text 字段的值,并添加到 texts 列表中。
texts.append(metadata["text"])
#从字典中提取 image 字段的第一个元素(通常是图像路径),并添加到 file_names 列表中。
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)
result.jsonl
文件是之前data-juicer处理完的数据结果。- 读取处理后的数据结果,将文本和图片文件名保存到 CSV 文件中。
七、 使用 CLIP 模型计算图像与文本的相似度
CLIP(Contrastive Language-Image Pretraining) 模型是由 OpenAI 开发的,用于理解图像和文本之间的关系。它的工作原理是把图像和文字都转换成一种共同的表示方式,使得它们在同一个“语义空间”中可以进行比较。这个模型在大量的图像和文本对上进行训练,让它学会如何将图像和相应的文字描述联系起来。
简单来说,CLIP 能够通过分析图像和文字的相似性来完成多种任务。例如,如果你给它一张图片和几段文字描述,它能判断哪段文字最能准确描述这张图片。它还可以根据给定的描述从图像库中找到匹配的图片。
CLIP 的强大之处在于它可以在没有额外训练的情况下处理很多不同的任务,比如图像检索、文本生成和图像分类等,主要因为它在训练时已经学会了如何处理各种图像和文字的关系。
下面这段代码使用了一个预训练好的 CLIP 模型,来计算图片和文本的相似度分数。这个分数表示图片和对应文本描述的匹配程度。
from transformers import CLIPProcessor, CLIPModel
import torch
##-------加载模型部分-------------
#加载预训练的 CLIP 模型 clip-vit-base-patch32。这个模型用于计算文本和图像的相似度。
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
#加载与模型配套的预处理器,用于将输入数据转换为模型所需的格式。
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
##-------数据转换部分---------
#遍历 df["file_name"] 列中的每个图像路径 img_path。使用 Image.open(...) 打开每个图像文件,并将图像对象存储在 images 列表中。
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
-
CLIPModel.from_pretrained(...)
:加载预训练的 CLIP 模型clip-vit-base-patch32
。这个模型可以计算图像和文本之间的相似度。 -
CLIPProcessor.from_pretrained(...)
:加载与模型配套的预处理器,用于将文本和图像转换为模型可以接受的输入格式。 -
[Image.open(img_path) for img_path in df["file_name"]]
:- 遍历 DataFrame 中
file_name
列的每个图像路径img_path
。 - 使用
Image.open(...)
打开每个图像文件,将图像对象存储在images
列表中。
- 遍历 DataFrame 中
-
processor(...)
:
将文本和图像处理成模型输入所需的格式。text=df["text"].tolist()
:将 DataFrame 中的text
列转换为文本列表。images=images
:传入图像列表。return_tensors="pt"
:将处理后的数据转换为 PyTorch 张量。padding=True
:对文本进行填充,确保所有输入具有相同的长度。
-
model(**inputs)
:将处理后的输入数据传递给 CLIP 模型。outputs
包含模型的输出结果,包括图像和文本的相似度分数。 -
outputs.logits_per_image
: 从模型输出中提取每个图像和文本对的相似度分数。 这是一个表示每对图像和文本相似度的分数矩阵。 -
logits_per_image.softmax(dim=1)
: 对相似度分数应用 Softmax 函数,将分数转换为概率。
dim=1
指定在每一行(每个图像的所有文本)上计算 Softmax,以获得相对概率。 -
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"]]
#CLIP 处理器
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)
#遍历 DataLoader 中的每个批次,得到每个批次的图像-文本匹配相似度分数
for batch in dataloader:
outputs = model(**batch)
logits_per_image = outputs.logits_per_image
probs = logits_per_image.softmax(dim=1)
print(probs)
- 这里定义了一个自定义的数据集类 CustomDataset,并使用 DataLoader 来批量加载数据进行处理。CustomDataset(df, processor) 实际上返回的是一个数据集对象,它能够通过 getitem 方法提供数据项。 DataLoader 使用这个数据集对象来批量处理数据。
- 每个批次都会计算图像和文本的相似度分数,并打印出结果。
九、使用 Stable Diffusion 生成图片
使用 Stable Diffusion 模型生成一幅符合描述的图像,并将其保存。最终生成的图片被保存到 example_image.png 文件中
import torch
from diffusers import StableDiffusionPipeline
#设置 PyTorch 的随机种子为 1,确保每次运行代码时生成的结果是相同的,有助于实验的可重复性。
torch.manual_seed(1)
#加载预训练的 Stable Diffusion 模型
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v-1-4", torch_dtype=torch.float16)
#将模型移动到 GPU
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
-
torch.manual_seed(1)
:设置 PyTorch 的随机种子为 1,确保每次运行代码时生成的结果是相同的,有助于实验的可重复性。 -
StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v-1-4", torch_dtype=torch.float16)
:加载预训练的 Stable Diffusion 模型。指定使用 16 位浮点数(torch.float16
),以节省内存并加速计算。 -
pipe = pipe.to("cuda")
:将模型移动到 GPU以加速计算。 -
prompt
:描述生成图像的文本提示。这是图像生成的主要输入。 -
negative_prompt
:描述要避免的特征。模型会尽量避免生成这些不想要的图像特征。 -
guidance_scale
:引导尺度,用于平衡生成图像与文本提示的一致性。值越高,图像更接近提示描述。 -
num_inference_steps
:推理步骤的数量。步骤越多,生成的图像质量通常越高,但也会增加计算时间。 -
pipe(...)
:调用模型生成图像。 -
.images[0]
:模型返回的图像集合中取第一个图像(通常只生成一个图像)。
十、合并生成的图片
将生成的一组图像(8张)拼接成一个大图像,并对结果进行调整和显示。迷你漫画就生成啦!
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