今更だけど、ちょっと遊んでみた。VAEのエンコーダで低次元の潜在空間に圧縮して、拡散させて、それをUNETで復元する際にCLIPを経由した生成指示情報を各層のAttentionに埋め込み、VAEのデコーダでピクセルに復元するとな。なるほど、完全に理解した。当たり前だけど、よく考えられているなぁ。
Windows 10 Pro 64bit
GeForce RTX2070 8GB
python 3.9.12
torch 1.12.1+cu116
diffusers 0.9.0
1.生成してみる(txt2img)
まず、モデルを読み込む。ここではv2を使っているが、v1-4などにも変更しやすいように変数化。GPUメモリは8GBしかないので、fp16版で、かつなんかメモリを節約してくれるらしいメソッド(最終行のやつ)を呼ぶ。
from diffusers import StableDiffusionPipeline, EulerDiscreteScheduler
import torch
version = "v2-0"
model_id = "stabilityai/stable-diffusion-2"
device = "cuda"
scheduler = EulerDiscreteScheduler.from_pretrained(model_id, subfolder="scheduler")
pipe = StableDiffusionPipeline.from_pretrained(model_id, scheduler=scheduler, revision="fp16", torch_dtype=torch.float16)
pipe = pipe.to(device)
pipe.enable_attention_slicing()
適当に10枚ほど生成。768×768で1枚約22秒。非実在写真を無限に錬成できる。
import os
import time
outdir = f"outputs_{version}"
os.makedirs(outdir, exist_ok=True)
fname = str(int(time.time()))
num = 10
prompt = "a beautiful girl in street. she has blond hair."
with open(f"{outdir}/{fname}.txt", "w") as f:
print(prompt, file=f)
for i in range(num):
image = pipe(prompt)["images"][0]
image.save(f"{outdir}/{fname}_{i:03.0f}.png")
2.変換してみる(img2img)
次に、この画像(写真風)をイラスト風に変換してみる。イラストに強いと言われるanything-v3-0を使ってみる。
import torch
from diffusers import StableDiffusionImg2ImgPipeline
version = "anything-v3-0"
model_id = "Linaqruf/anything-v3.0"
pipe = StableDiffusionImg2ImgPipeline.from_pretrained(model_id, revision="diffusers", torch_dtype=torch.float16)
device = "cuda"
pipe.to(device)
pipe.enable_attention_slicing()
これを使って、先ほど生成した画像をイラスト風に変換してみる。768×768で1枚約25秒。画像がでかいとメモリが足りないみたいなことを言われるので、スケーリング係数をかけられるようにしている。*_000.pngは変換前の画像のコピー、001~が変換画像。
適当にコピペで動かしているんだけど、autocastってなんだろう?
strengthは大きくすると、元画像との差異が大きくなる? guidance_scaleは大きくすると、プロンプトによる指定が強く効くようになる?
import os
from PIL import Image
from torch import autocast
filename = "outputs_v2-0/1670846014_001.png"
outdir = f"outputs_i2i_{version}"
os.makedirs(outdir, exist_ok=True)
fname = os.path.splitext(os.path.basename(filename))[0]
init_image = Image.open(filename).convert("RGB")
scaling = 1.0
init_image = init_image.resize((int(scaling*init_image.width), int(scaling*init_image.height)))
init_image.save(f"{outdir}/{fname}_000.png")
start = 1
num = 10
prompt = "a girl of illust style. "
with open(f"{outdir}/{fname}.txt", "w") as f:
print(prompt, file=f)
for i in range(start, num+start):
with autocast(device):
image = pipe(prompt, init_image=init_image, strength=0.5, guidance_scale=7.5, num_inference_steps=50).images[0]
image.save(f"{outdir}/{fname}_{i:03.0f}.png")
すごいなぁ。画像は一致してないんだけど、印象はほぼそのままにイラスト風になる。