从零入门AI生图原理&实践 是 Datawhale 2024 年 AI 夏令营第四期的学习活动(“AIGC”方向),基于魔搭社区“可图Kolors-LoRA风格故事挑战赛”开展的实践学习。
Datawhale官方的速通教程链接:
Datawhale(Task2)
下面是根据教程实践的记录
Part1:AI生图技术能力及局限
①通俗来说,AI生图模型获得图片生成能力主要是通过 学习 图片描述 以及 图片特征,尝试将这两者进行一一对应,存储在自己的记忆里。
模型根据我们输入的文字+凭借它的能力→生成我们需要的图片
②不同模型用于训练的数据有限且不一定相同→匹配的描述和特征也是有限的
所以不同模型在风格和具体事物上会有很大的生成差异,且存在诸多于现实不符的情况
③关于“ai味”辨别方法:
*观察图片的细节(人物面部特征,特别是眼睛和嘴巴)
*检查光线和阴影(光源是否一致,阴影是否与光源相符,光线阴影会不会不正常)
*分析像素(是否有模糊/像素化部分)
*注意背景(是否有不协调eg.边缘是平滑,有无不自然的重复)
Part2:AI生图前沿探索
可以从魔搭社区各种AIGC开始探索
→魔搭社区
通过task1的学习,觉得数据集很重要!可以在魔搭开源数据集中寻找我们要的数据集
Kolors(可图)模型(点击即可跳转魔搭模型介绍页) 是快手开源的文本到图像生成模型,该模型具有对英语和汉语的深刻理解,并能够生成高质量、逼真的图像。以下是一些相关链接:
代码开源链接:https://github.com/Kwai-Kolors/Kolors
模型开源链接:https://modelscope.cn/models/Kwai-Kolors/Kolors
技术报告链接:https://github.com/Kwai-Kolors/Kolors/blob/master/imgs/Kolors_paper.pdf
魔搭研习社最佳实践说明:https://www.modelscope.cn/learn/575?pid=543
可图区别过去SD系列基础模型的一个特点是它支持中文文生图,
魔搭社区还开源了专门的各种风格的可图优质咒语书(点击即可跳转),可以针对600+种不同风格,完善prompt,生成各种风格图片
Part3:通过魔搭社区持续探索AI生图前沿
目前让ai稳定生成相似效果的海报还是比较困难,一些创意海报工具(eg.
创意海报生成 )往往是通过固定文字位置及字体进行相应海报生成,
大致是①ai生成背景②通过代码将对应的文字显示到对应位置,然后渲染合成照片输出给我们
自己想多多探索的话可以往这些方向思考
- 可以用在哪些业务里?有哪些行业在使用?
- 用的是哪个 AI 模型?
- 是自己部署的AI模型,还是用的API?
- 使用到了哪些 AI 能力?
- 如果要复现,需要做哪些额外的设置和开发?
- 还可以有哪些优化点?
可以在刚刚的创意海报生成空间文件里进行相关代码的查看/下载到本地/云服务器复现,验证思考及学习相关的设计开发
一、认识通义千问
通义千问(点击直达)。
它相当于一个AI助手
二、使用这样一个AI助手帮我们精读baseline——①分析代码的主体架构②逐行代码解析
根据教程里图片可以直观感知文生图代码的框架结构
使用AI助手前我们先整理出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
好了,现在请借助AI助手实战!
→①分析代码的主体架构
Ⅰ.向AI助手输入prompt
你是一个优秀的python开发工程师,现在我们需要你帮我们分析这个代码的主体框架,你需要把代码按照工作流分成几部分,用中文回答我的问题。{此处替换前面的代码}
*插个题外话,看见“工作流”的时候我其实不太懂这个词的实际意思,就去csdn上查了一下(
工作流:工作流是一系列有序的任务、活动和决策,以实现特定的业务过程或工作流程。它描述了任务的先后顺序、依赖关系、条件和规则等,用于指导和管理工作的执行,后面第一个附的是两篇文章链接,不清楚工作流的小伙伴也可以看看,很详细奥~)
来自 <【工作流】 工作流相关概念及Activiti基本介绍_activity工作流-CSDN博客>
从程序员的角度来看为什么我们需要工作流_是否有必要使用工作流-CSDN博客
Ⅱ.AI助手返回的结果
→这段代码主要完成了以下几个任务:
1. **环境准备**:
- 安装所需的Python包,包括`simple-aesthetics-predictor`, `data-juicer`, `peft`, `lightning`, `pandas`, `torchvision`, 和 `DiffSynth-Studio`。
- 卸载`pytorch-lightning`并安装一些替代版本。
2. **数据集加载与处理**:
- 从ModelScope加载了一个名为`lowres_anime`的数据集,并将其保存到指定的目录。
- 将数据集中的图片转换为RGB格式,并将每张图片和对应的描述性文本保存到本地文件系统中。同时,这些信息被记录在一个JSON Lines文件`metadata.jsonl`中。
3. **数据筛选与处理配置**:
- 创建了一个配置文件`data_juicer_config.yaml`来定义数据处理的规则,包括过滤图片的尺寸和宽高比。
- 使用`data-juicer`工具根据上述配置文件对数据进行预处理,并输出到`result.jsonl`文件中。
4. **数据分析与准备**:
- 读取经过筛选后的数据集,创建一个Pandas DataFrame来存储图像文件名和对应的文本描述。
- 将DataFrame保存为CSV文件。
- 利用CLIP模型计算图像和文本之间的相似度得分。
5. **自定义数据集类**:
- 定义了一个PyTorch的`Dataset`子类`CustomDataset`,用于将图像和文本配对,并准备好输入给下游模型。
6. **数据加载与模型推理**:
- 创建了`DataLoader`对象来批量加载数据,并通过CLIP模型计算每个批次的图像与文本相似度。
7. **生成图像**:
- 使用`StableDiffusionPipeline`根据不同的提示(prompt)生成了一系列二次元风格的图像,并保存这些图像。
8. **图像展示**:
- 最后,代码尝试将生成的图像拼接在一起并调整大小以方便展示。
总结来说,整个流程涵盖了从数据集的加载、处理、分析到使用预训练模型进行推理以及图像生成等多个环节。这样的流水线非常适合于处理大规模图像数据集,并且可以用来训练或评估图像生成模型的效果。
Ⅲ.使用AI助手例图
→②逐行代码解析
Ⅰ.同样地,向AI助手输入prompt
你是一个优秀的python开发工程师,现在我们需要你帮我们逐行分析这个代码,用中文回答我的问题。{此处替换前面的代码}
Ⅱ.AI助手返回结果
这段代码可以分为几个主要部分来进行逐行分析:
### 1. 环境准备
- `!pip install simple-aesthetics-predictor`: 安装`simple-aesthetics-predictor`包。
- `!pip install -v -e data-juicer`: 安装`data-juicer`包,并使其可编辑运行。
- `!pip uninstall pytorch-lightning -y`: 卸载`pytorch-lightning`包。
- `!pip install peft lightning pandas torchvision`: 安装`peft`、`lightning`、`pandas`和`torchvision`包。
- `!pip install -e DiffSynth-Studio`: 安装`DiffSynth-Studio`包,并使其可编辑运行。
### 2. 数据集加载与处理
- `from modelscope.msdatasets import MsDataset`: 导入ModelScope的数据集模块。
- `ds = MsDataset.load('AI-ModelScope/lowres_anime', subset_name='default', split='train', cache_dir="/mnt/workspace/kolors/data")`: 加载名为`lowres_anime`的数据集,并设置缓存目录。
### 3. 数据预处理
- `import json, os`: 导入`json`和`os`模块。
- `from data_juicer.utils.mm_utils import SpecialTokens`: 导入`SpecialTokens`类。
- `from tqdm import tqdm`: 导入`tqdm`模块以显示进度条。
- `os.makedirs("./data/lora_dataset/train", exist_ok=True)`: 创建目录`./data/lora_dataset/train`,如果已存在则不报错。
- `os.makedirs("./data/data-juicer/input", exist_ok=True)`: 创建目录`./data/data-juicer/input`。
- `with open("./data/data-juicer/input/metadata.jsonl", "w") as f:`: 打开`metadata.jsonl`文件以写入数据。
- `for data_id, data in enumerate(tqdm(ds)):`: 遍历数据集中的每一项数据。
- `image = data["image"].convert("RGB")`: 将图片转换为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")`: 每个元数据项后添加换行符。
### 4. 数据筛选配置
- 定义`data_juicer_config`字符串,包含了数据处理的配置信息。
- `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`: 调用`data-juicer`工具对数据进行处理。
### 5. 数据分析与准备
- `import pandas as pd`: 导入`pandas`模块。
- `os.makedirs("./data/data-juicer/output/images", exist_ok=True)`: 创建输出目录。
- 读取处理后的数据,并将元数据存储为列表。
- `df = pd.DataFrame({"text": texts, "file_name": file_names})`: 创建DataFrame。
- `df.to_csv("./data/data-juicer/output/result.csv", index=False)`: 将DataFrame保存为CSV文件。
- `df`: 显示DataFrame的内容。
### 6. 计算图像与文本相似度
- `from transformers import CLIPProcessor, CLIPModel`: 导入`CLIP`模型和处理器。
- `model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")`: 加载预训练的CLIP模型。
- `processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")`: 加载预训练的CLIP处理器。
- `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)`: 使用CLIP模型进行推理。
- `logits_per_image = outputs.logits_per_image`: 获取图像与文本相似度得分。
- `probs = logits_per_image.softmax(dim=1)`: 计算概率分布。
### 7. 自定义数据集类
- `class CustomDataset(Dataset):`: 定义一个自定义的数据集类。
- 实现`__init__`, `__len__`, `__getitem__`方法。
- `dataset = CustomDataset(df, processor)`: 创建数据集实例。
- `dataloader = DataLoader(dataset, batch_size=8)`: 创建数据加载器。
- `for batch in dataloader:`: 遍历数据加载器中的批次。
- `outputs = model(**batch)`: 对每个批次进行模型推理。
- `print(probs)`: 输出每个批次的概率分布。
### 8. 图像生成
- `import torch`: 导入`torch`模块。
- `from diffusers import StableDiffusionPipeline`: 导入`StableDiffusionPipeline`。
- 设置随机种子以确保结果可复现。
- `pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v-1-4", torch_dtype=torch.float16)`: 加载预训练的图像生成模型。
- `pipe = pipe.to("cuda")`: 将模型移动到GPU上。
- 使用不同的提示和配置生成图像,并保存。
### 9. 图像展示
- `images = [np.array(Image.open(f"{i}.jpg")) for i in range(1, 9)]`: 读取生成的图像。
- `image = np.concatenate([...])`: 将图像拼接起来。
- `image = Image.fromarray(image).resize((1024, 2048))`: 将图像转换为`PIL.Image`对象并调整大小。
- `image`: 展示最终的拼接图像。
以上是对这段代码的逐行分析。这段代码主要涉及了数据集的加载、处理、图像与文本相似度计算、自定义数据集、图像生成和展示等功能。
一些感悟~
通过和Datawhale教程的对比,我们发现返回的结果存在出入,以上面的prompt输入,AI助手返回的结果比较粗略,如果想要更细致,可以选择调整一下提示词(更有指向性AI返回结果会更清晰,也不会省略、跳过一些代码,下面也有更好的办法!)
→②修改成:你是一个优秀的python代码开发工程师,我们现在有一个python文件,需要你来逐行分析,每一行代码都增加上中文语义备注(调整后的提示词AI的返回结果会更加细致)
Ⅲ示例.
想要更细致更好的方法↓(Datawhale教程里面代码分析应该是多次追问的结果,多多探索!)
Ⅳ..向AI追问!
在逐行分析后,继续向你想要更深入了解的部分追问
提示词:
我对其中{替换成你的问题}还是不太理解,给我再详细介绍一下
示例:
觉得一开始AI逐行分析代码粗略仍然可以多多提问不断了解代码。
三、实战演练——基于话剧的连环画制作
1、数据准备(用通义)
Eg.输入下面的提示词
(下面是我实践的一个例子,每个part有空行应该是为了让AI便于思考输出,空行感觉不删除更好,在这一部分大家也可以自己想一些剧情进行尝试~)
你是一个文生图专家,我们现在要做一个实战项目,就是要编排一个文生图话剧 话剧由8张场景图片生成,你需要输出每张图片的生图提示词
具体的场景图片
1、女主是一个大学生,正在大学课堂上课
2、开始睡着了
3、进入梦乡,梦到自己回到了初中
4、初中同学在操场上追逐打闹
5、女主穿着初中校服迎风笑
6、女主和初中同学谈笑风生
7、下课了,梦醒了
8、女主看了一下手机微信列表早已灰暗的初中同学账号
生图提示词要求
1、风格为二次元,日系动漫
2、根据场景确定是使用全身还是上半身
3、人物描述
4、场景描述
5、做什么事情
例子:
二次元、日系动漫、一个黑色长发少女,坐在教室里,盯着黑板,手里拿着笔,深思,上半身,蓝白条纹T恤
询问通义
最后整理出来的表格
结合通义返回的结果就是下面这样了,负向提示词是人为添加到表格上的(不是通义返回的,我暂时还没试通义能不能返回负向提示词结果)一般负向提示词大致就是像下面这么写,有什么还想规避的就往上添加
编号 | 场景 | 正向提示词 | 负向提示词 |
图片1 | 女主是一个大学生,正在大学课堂上课 |
| 丑陋、变形、嘈杂、模糊、低对比度,扭曲手臂,扭曲手指,多余手臂,多余手指 |
图片2 | 开始睡着了 |
| 丑陋、变形、嘈杂、模糊、低对比度,扭曲面部,扭曲手臂,扭曲手指,多余手臂,多余手指 |
图片3 | 进入梦乡,梦到自己回到了初中 |
| 丑陋、变形、嘈杂、模糊、低对比度,扭曲的脑海框,模糊手臂,模糊色块,曲面部,扭曲手臂,扭曲手指,多余手臂,扭曲身体,不规则身体 |
图片4 | 初中同学在操场上追逐打闹 |
| 丑陋、变形、嘈杂、模糊、低对比度,扭曲的手指,多余的手指,扭曲身体,不规则身体 |
图片5 | 女主穿着初中校服迎风笑 |
| 丑陋、变形、嘈杂、模糊、低对比度,扭曲身体,扭曲面部,扭曲模糊画面,扭曲身体,不规则身体 |
图片6 | 女主和初中同学谈笑风生 |
| 丑陋、变形、嘈杂、模糊、低对比度,扭曲手指,多余手指,扭曲面部,扭曲身体,不规则身体 |
图片7 | 下课了,梦醒了 |
| 丑陋、变形、嘈杂、模糊、低对比度,扭曲手指,多余手指,扭曲面部,扭曲身体,不规则身体 |
图片8 | 女主看了一下手机微信列表早已灰暗的初中同学账号 |
| 丑陋、变形、嘈杂、模糊、低对比度,扭曲嘴巴,扭曲面部,扭曲手指,多余手指,扭曲手机屏幕,异常手机页面 |
按照这样再跑一遍task1流程即可!
展示图(我描述出来人物还是有一些不一致,我会再继续尝试的!下面是输出的一张我比较喜欢的画风,明天试试调整成这样的画风,现在不小心出现CUDA内存不够的错误了qaq)
Part4:更多的选择一一浅尝scepter webui
这是教程里面鼓励我们再多尝试的一个part~
1、浅尝功能
魔搭体验网址:https://www.modelscope.cn/studios/iic/scepter_studio
2、核心组件
3、私有部署(挑战型)
魔搭社区提供的体验地址里面缺失了微调模型的功能,如果大家希望能使用secpter完整的功能,我们可以将其私有部署到我们自己的服务器。
github地址:https://github.com/modelscope/scepter
可以通过官方readme文件中的教程进行操作,该模块对基础有一定的要求,适合学有余力的同学