Datawhale AI夏令营第四期 Task2: 精读代码,实战进阶

Datawhale AI夏令营第四期 Task2: 精读代码,实战进阶

探探前沿:了解一下 AI生图技术 的能力&局限

了解AI生图的重要性

AI生图使用得当可以产生精美的图片
在这里插入图片描述
但也有不法分子用图片制造虚假新闻,造成社会动荡
在这里插入图片描述
对所有人来说,定期关注AI生图的最新能力情况都十分重要:

  • 对于普通人来说,可以避免被常见的AI生图场景欺骗,偶尔也可以通过相关工具绘图
  • 对于创作者来说,通过AI生图的工具可以提效,快速制作自己所需要的内容
  • 对于技术人来说,了解AI生图的能力的玩法,可以更好地针对自己的业务进行开发和使用,甚至攻克难题开发更实用的工具

回顾AI生图的历史

最早的AI生图可追溯到20世纪70年代,当时由艺术家哈罗德·科恩(Harold Cohen)发明AARON,可通过机械臂输出作画。

在这里插入图片描述

现代的AI生图模型大多基于深度神经网络基础上训练,最早可追溯到2012年吴恩达训练出的能生成“猫脸”的模型。

它使用卷积神经网络(CNN)训练,证明了深度学习模型能够学习到图像的复杂特征。

2015年,谷歌推出了“深梦”(Deep Dream)图像生成工具,类似一个高级滤镜,可以基于给定的图片生成梦幻版图片——
在这里插入图片描述
2021 年 1 月 OpenAI 推出DALL-E模型(一个深度学习算法模型,是GPT-3 语言处理模型的一个衍生版本),能直接从文本提示“按需创造”风格多样的图形设计——
在这里插入图片描述
在当时,就已经被一些媒体评价为:“ 秒杀50%的设计行业打工人应该是没有问题的,而且是质量和速度双重意义上的“秒杀” ”。
通过学习大量画家的作品,AI生图模型 往往可以照猫画虎绘制出类似的画作,在2022年8月,AI生图真正走进了大众的视野,让各个领域无法忽视。
当时让AI生图破圈的是AI绘画作品《太空歌剧院》,该作品在美国科罗拉多州举办的新兴数字艺术家竞赛中获得了比赛“数字艺术/数字修饰照片”类别一等奖,引起了当时“艺术家们 Not Happy”的社会舆论。
在这里插入图片描述

AI 画图的挑战

稍微接触过AI画图的朋友们都知道AI画不好手,现在也有了一些优化方案,训练时增加手部细节的标签,或者在生成时使用ControlNet等技术。
在这里插入图片描述

Part1:磨刀准备一一认识通义千问

通义千问链接
在这里插入图片描述
在接下来的代码解读环节,你有任何不理解的代码块,都可以粘贴到对话框中去询问通义千问,将他当作你的私人助教进行提问。

Part2:精读baseline——从零入门AI生图

我们的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. 分析代码的主体架构

我们在通义千问对话框中输入:

你是一个优秀的python开发工程师,现在我们需要你帮我们分析这个代码的主体框架,你需要把代码按照工作流分成几部分,用中文回答我的问题。代码如下:

{此处把上面baseline的完整代码粘贴进来}

下面是通义千问的回答:

这段代码可以分为几个主要的部分来理解:

第一部分:环境准备和依赖安装
这部分代码负责安装或卸载一些必要的Python包。

!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

第二部分:数据集加载与处理
这部分代码从ModelScope中加载了一个名为lowres_anime的数据集,并将其保存到指定目录。接着创建了一个包含图像路径和标签的JSONL文件。

from modelscope.msdatasets import MsDataset

ds = MsDataset.load(
    'AI-ModelScope/lowres_anime',
    subset_name='default',
    split='train',
    cache_dir="/mnt/workspace/kolors/data"
)

# ... (后续代码省略)

第三部分:使用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
"""

# ... (后续代码省略)

第四部分:构建DataFrame并进行CLIP相似性计算
这里使用Pandas创建了一个DataFrame,并利用CLIP模型计算了图像和文本之间的相似度。

import pandas as pd
import os, json
from PIL import Image
from tqdm import tqdm

# ... (后续代码省略)

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")

# ... (后续代码省略)

第五部分:创建自定义数据集和数据加载器
这部分定义了一个自定义的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

    # ... (后续代码省略)

dataset = CustomDataset(df, processor)
dataloader = DataLoader(dataset, batch_size=8)

# ... (后续代码省略)

第六部分:生成图像
这部分代码使用StableDiffusionPipeline根据不同的提示(prompts)生成了一系列二次元风格的图像。

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")

# ... (后续代码省略)

第七部分:图像拼接
最后,将生成的图像拼接到一起形成一张大图。

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

以上就是代码的主要组成部分及其功能概述。如果有任何具体细节或问题需要进一步解释,请随时告诉我。

2. 逐行解释代码

继续在对话框向与通义千问提问,输入:

你是一个优秀的python开发工程师,现在我们需要你帮我们逐行分析这个代码,用中文回答我的问题。
{这些粘贴进你不明白的代码}

在这里插入图片描述
在这里插入图片描述

3. 代码还有疑问?继续追问

遵循一个“哪里不明白问哪里”的原则,像你跟一个不厌其烦的老师请教问题一样。

在这里插入图片描述
在这里插入图片描述

Part3:实战演练一一基于话剧的连环画制作

让通义千问帮我们生成提示词

在对话框中输入下面的文本(可以根据自己的需求进行修改):

你是一个文生图专家,我们现在要做一个实战项目,就是要编排一个文生图话剧
话剧由8张场景图片生成,你需要输出每张图片的生图提示词

具体的场景图片
1、女主是一名刺客,故意接近男主
2、与男主嬉笑玩乐
3、男主深深地爱上了女主
4、男主发誓要保护女主一辈子
5、女主被坏人抓走了
6、男主孤身一人前去救女主
7、男主中了圈套
8、女主并不爱男主,男主悲痛欲绝

生图提示词要求
1、风格为古风
2、根据场景确定是使用全身还是上半身
3、人物描述
4、场景描述
5、做啥事情

例子:
古风,水墨画,一个黑色长发少女,坐在小溪边,抚摸着秀发,深思,上半身,红色长裙

结合通义千问的回答,并自己进行了一些调整,得到场景表格

图片编号场景描述正向提示词反向提示词
图片1女主是一名刺客,故意接近男主古风,细腻笔触,一个黑色长发少女,在一座古典园林,竹林轻摇,眼神锐利,全身,红色长裙丑陋,变形,嘈杂,模糊,低对比度
图片2女主与男主嬉笑玩乐古风,细腻笔触.黑发少女,坐在花园里,全身,穿着红色长裙丑陋,变形,嘈杂,模糊,低对比度
图片3男主深深地爱上了女主二次元,古风,柔和色调,男主角深情地望着女主角,月下凉亭,微风吹过,男主角握着女主角的手,眼中满是柔情,上半身丑陋,变形,嘈杂,模糊,低对比度
图片4男主发誓要保护女主一辈子二次元,古风,细腻笔触,庄严色彩,男主角单膝跪地,女主,红色长裙,上半身,特写丑陋,变形,嘈杂,模糊,低对比度
图片5女主被坏人抓走了二次元,古风,紧张氛围,在一条昏暗的小巷中,女主角试图挣脱,表情惊恐 ,全身丑陋,变形,嘈杂,模糊,低对比度
图片6男主孤身一人前去救女主二次元,古风,阴沉色调,男主角手持长剑,目光坚定,夜晚的荒郊野外,月光如水,男主角疾步前行,准备营救,全身丑陋,变形,嘈杂,模糊,低对比度
图片7男主中了圈套二次元,古风,暗黑背景,男主角受伤,表情疼痛,全身,黑暗的森林,全身丑陋,变形,嘈杂,模糊,低对比度
图片8女主并不爱男主,男主悲痛欲绝二次元,古风,悲伤色彩,男主角泪流满面,雨夜中的破败小屋,男主角跪倒在地,手扶着门框,上半身丑陋,变形,嘈杂,模糊,低对比度

使用baseline代码生成图片

请添加图片描述
女主是一名刺客,故意接近男主
请添加图片描述
女主与男主嬉笑玩乐
请添加图片描述
男主深深地爱上了女主
请添加图片描述
男主发誓要保护女主一辈子
请添加图片描述
女主被坏人抓走了
请添加图片描述
男主孤身一人前去救女主
请添加图片描述
男主中了圈套
请添加图片描述
女主并不爱男主,男主悲痛欲绝

个人感觉画的比较好的是图1、2、6,图1、2单体照片比较精致,图6非常符合意境,生成代码如下:

torch.manual_seed(0)
image = pipe(
    prompt="古风,细腻笔触,一个黑色长发少女,在一座古典园林,竹林轻摇,眼神锐利,全身,红色长裙",
    negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
    cfg_scale=4,
    num_inference_steps=50, height=1024, width=1024,
)
image.save("1.jpg")

torch.manual_seed(112)
image = pipe(
    prompt="古风,细腻笔触.黑发少女,坐在花园里,全身,穿着红色长裙",
    negative_prompt="丑陋、变形、嘈杂、模糊、低对比度",
    cfg_scale=4,
    num_inference_steps=50, height=1024, width=1024,
)
image.save("2.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")

Part4:更多的选择一一浅尝scepter webui

魔搭体验网址
在这里插入图片描述
我切换了模型为SD1.5,大家可以换用不同的模型,使用相同的参数生成图片,选择自己喜欢的模型。
在这里插入图片描述

我的提示词和参数设置
在这里插入图片描述
生成图片如下:
在这里插入图片描述
哈哈似乎效果不是很理想,大家可以调整prompt和其他参数,或者换用其他模型多多进行尝试,好的图片是需要不断试错的!

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值