Spaces:
Running
on
Zero
Running
on
Zero
Tarin Clanuwat
commited on
Commit
•
dd4e3ab
0
Parent(s):
initial commit
Browse files- .gitattributes +35 -0
- README.md +12 -0
- app.py +146 -0
- evo_ukiyoe_v1.py +189 -0
- requirements.txt +9 -0
- safety_checker.py +137 -0
.gitattributes
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Evo-Ukiyoe
|
3 |
+
emoji: 🐠
|
4 |
+
colorFrom: purple
|
5 |
+
colorTo: blue
|
6 |
+
sdk: gradio
|
7 |
+
sdk_version: 4.26.0
|
8 |
+
app_file: app.py
|
9 |
+
pinned: false
|
10 |
+
---
|
11 |
+
|
12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import random
|
2 |
+
|
3 |
+
from PIL import Image
|
4 |
+
from diffusers import EulerDiscreteScheduler
|
5 |
+
import gradio as gr
|
6 |
+
import numpy as np
|
7 |
+
import spaces
|
8 |
+
import torch
|
9 |
+
|
10 |
+
# torch._inductor.config.conv_1x1_as_mm = True
|
11 |
+
# torch._inductor.config.coordinate_descent_tuning = True
|
12 |
+
# torch._inductor.config.epilogue_fusion = False
|
13 |
+
# torch._inductor.config.coordinate_descent_check_all_directions = True
|
14 |
+
from evo_ukiyoe_v1 import load_evo_ukiyoe
|
15 |
+
|
16 |
+
|
17 |
+
DESCRIPTION = """# 🐟 Evo-Ukiyoe
|
18 |
+
🤗 [モデル一覧](https://huggingface.co/SakanaAI) | 📝 [ブログ](https://sakana.ai/evo-ukiyoe/) | 🐦 [Twitter](https://twitter.com/SakanaAILabs)
|
19 |
+
|
20 |
+
[Evo-Ukiyoe](https://huggingface.co/SakanaAI/Evo-Ukiyoe-v1)は[Sakana AI](https://sakana.ai/)が教育目的で開発した浮世絵に特化した画像生成モデルです。
|
21 |
+
入力した日本語プロンプトに沿った浮世絵風の画像を生成することができます。より詳しくは、上記のブログをご参照ください。
|
22 |
+
"""
|
23 |
+
if not torch.cuda.is_available():
|
24 |
+
DESCRIPTION += "\n<p>Running on CPU 🥶 This demo may not work on CPU.</p>"
|
25 |
+
|
26 |
+
MAX_SEED = np.iinfo(np.int32).max
|
27 |
+
|
28 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
29 |
+
|
30 |
+
NUM_IMAGES_PER_PROMPT = 1
|
31 |
+
SAFETY_CHECKER = True
|
32 |
+
if SAFETY_CHECKER:
|
33 |
+
from safety_checker import StableDiffusionSafetyChecker
|
34 |
+
from transformers import CLIPFeatureExtractor
|
35 |
+
|
36 |
+
safety_checker = StableDiffusionSafetyChecker.from_pretrained(
|
37 |
+
"CompVis/stable-diffusion-safety-checker"
|
38 |
+
).to(device)
|
39 |
+
feature_extractor = CLIPFeatureExtractor.from_pretrained(
|
40 |
+
"openai/clip-vit-base-patch32"
|
41 |
+
)
|
42 |
+
|
43 |
+
def check_nsfw_images(
|
44 |
+
images: list[Image.Image],
|
45 |
+
) -> tuple[list[Image.Image], list[bool]]:
|
46 |
+
safety_checker_input = feature_extractor(images, return_tensors="pt").to(device)
|
47 |
+
has_nsfw_concepts = safety_checker(
|
48 |
+
images=[images], clip_input=safety_checker_input.pixel_values.to(device)
|
49 |
+
)
|
50 |
+
|
51 |
+
return images, has_nsfw_concepts
|
52 |
+
|
53 |
+
|
54 |
+
pipe = load_evo_ukiyoe(device)
|
55 |
+
pipe.scheduler = EulerDiscreteScheduler.from_config(
|
56 |
+
pipe.scheduler.config, use_karras_sigmas=True,
|
57 |
+
)
|
58 |
+
# pipe.unet.to(memory_format=torch.channels_last)
|
59 |
+
# pipe.vae.to(memory_format=torch.channels_last)
|
60 |
+
# # Compile the UNet and VAE.
|
61 |
+
# pipe.unet = torch.compile(pipe.unet, mode="max-autotune", fullgraph=True)
|
62 |
+
# pipe.vae.decode = torch.compile(pipe.vae.decode, mode="max-autotune", fullgraph=True)
|
63 |
+
|
64 |
+
|
65 |
+
def randomize_seed_fn(seed: int, randomize_seed: bool) -> int:
|
66 |
+
if randomize_seed:
|
67 |
+
seed = random.randint(0, MAX_SEED)
|
68 |
+
return seed
|
69 |
+
|
70 |
+
|
71 |
+
@spaces.GPU
|
72 |
+
@torch.inference_mode()
|
73 |
+
def generate(
|
74 |
+
prompt: str,
|
75 |
+
negative_prompt: str,
|
76 |
+
seed: int = 0,
|
77 |
+
randomize_seed: bool = False,
|
78 |
+
progress=gr.Progress(track_tqdm=True),
|
79 |
+
):
|
80 |
+
pipe.to(device)
|
81 |
+
seed = int(randomize_seed_fn(seed, randomize_seed))
|
82 |
+
generator = torch.Generator().manual_seed(seed)
|
83 |
+
|
84 |
+
images = pipe(
|
85 |
+
prompt=prompt + "最高品質の輻の浮世絵。超詳細。",
|
86 |
+
negative_prompt=negative_prompt,
|
87 |
+
width=1024,
|
88 |
+
height=1024,
|
89 |
+
guidance_scale=8.0,
|
90 |
+
num_inference_steps=40,
|
91 |
+
generator=generator,
|
92 |
+
num_images_per_prompt=NUM_IMAGES_PER_PROMPT,
|
93 |
+
output_type="pil",
|
94 |
+
).images
|
95 |
+
|
96 |
+
if SAFETY_CHECKER:
|
97 |
+
images, has_nsfw_concepts = check_nsfw_images(images)
|
98 |
+
if any(has_nsfw_concepts):
|
99 |
+
gr.Warning("NSFW content detected.")
|
100 |
+
return Image.new("RGB", (512, 512), "WHITE"), seed
|
101 |
+
return images[0], seed
|
102 |
+
|
103 |
+
|
104 |
+
examples = [
|
105 |
+
"植物と花があります。蝶が飛んでいます。",
|
106 |
+
"鶴が庭に立っています。雪が降っています。",
|
107 |
+
"着物を着ている猫が庭でお茶を飲んでいます。",
|
108 |
+
]
|
109 |
+
|
110 |
+
css = """
|
111 |
+
.gradio-container{max-width: 690px !important}
|
112 |
+
h1{text-align:center}
|
113 |
+
"""
|
114 |
+
with gr.Blocks(css=css) as demo:
|
115 |
+
gr.Markdown(DESCRIPTION)
|
116 |
+
with gr.Group():
|
117 |
+
with gr.Row():
|
118 |
+
prompt = gr.Textbox(placeholder="日本語でプロンプトを入力してください。", show_label=False, scale=8)
|
119 |
+
submit = gr.Button(scale=0)
|
120 |
+
result = gr.Image(label="Evo-Ukiyoeからの生成結果", type="pil", show_label=False)
|
121 |
+
with gr.Accordion("詳細設定", open=False):
|
122 |
+
negative_prompt = gr.Textbox(placeholder="日本語でネガティブプロンプトを入力してください。(空白可)", show_label=False)
|
123 |
+
seed = gr.Slider(label="シード値", minimum=0, maximum=MAX_SEED, step=1, value=0)
|
124 |
+
randomize_seed = gr.Checkbox(label="ランダムにシード値を決定", value=True)
|
125 |
+
gr.Examples(examples=examples, inputs=[prompt], outputs=[result, seed], fn=generate)
|
126 |
+
gr.on(
|
127 |
+
triggers=[
|
128 |
+
prompt.submit,
|
129 |
+
submit.click,
|
130 |
+
],
|
131 |
+
fn=generate,
|
132 |
+
inputs=[
|
133 |
+
prompt,
|
134 |
+
negative_prompt,
|
135 |
+
seed,
|
136 |
+
randomize_seed,
|
137 |
+
],
|
138 |
+
outputs=[result, seed],
|
139 |
+
api_name="run",
|
140 |
+
)
|
141 |
+
gr.Markdown("""⚠️ 本モデルは実験段階のプロトタイプであり、教育および研究開発の目的でのみ提供されています。商用利用や、障害が重大な影響を及ぼす可能性のある環境(ミッションクリティカルな環境)での使用には適していません。
|
142 |
+
本モデルの使用は、利用者の自己責任で行われ、その性能や結果については何ら保証されません。
|
143 |
+
Sakana AIは、本モデルの使用によって生じた直接的または間接的な損失に対して、結果に関わらず、一切の責任を負いません。
|
144 |
+
利用者は、本モデルの使用に伴うリスクを十分に理解し、自身の判断で使用することが必要です。""")
|
145 |
+
|
146 |
+
demo.queue().launch()
|
evo_ukiyoe_v1.py
ADDED
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gc
|
2 |
+
import os
|
3 |
+
from typing import Dict, List, Union
|
4 |
+
|
5 |
+
from diffusers import (
|
6 |
+
StableDiffusionXLPipeline,
|
7 |
+
UNet2DConditionModel,
|
8 |
+
)
|
9 |
+
from huggingface_hub import hf_hub_download
|
10 |
+
import safetensors
|
11 |
+
import torch
|
12 |
+
from tqdm import tqdm
|
13 |
+
from transformers import AutoTokenizer, CLIPTextModelWithProjection
|
14 |
+
|
15 |
+
|
16 |
+
# Base models
|
17 |
+
SDXL_REPO = "stabilityai/stable-diffusion-xl-base-1.0"
|
18 |
+
DPO_REPO = "mhdang/dpo-sdxl-text2image-v1"
|
19 |
+
JN_REPO = "RunDiffusion/Juggernaut-XL-v9"
|
20 |
+
JSDXL_REPO = "stabilityai/japanese-stable-diffusion-xl"
|
21 |
+
|
22 |
+
# Evo-Ukiyoe
|
23 |
+
UKIYOE_REPO = "SakanaAI/Evo-Ukiyoe-v1"
|
24 |
+
|
25 |
+
|
26 |
+
def load_state_dict(checkpoint_file: Union[str, os.PathLike], device: str = "cpu"):
|
27 |
+
file_extension = os.path.basename(checkpoint_file).split(".")[-1]
|
28 |
+
if file_extension == "safetensors":
|
29 |
+
return safetensors.torch.load_file(checkpoint_file, device=device)
|
30 |
+
else:
|
31 |
+
return torch.load(checkpoint_file, map_location=device)
|
32 |
+
|
33 |
+
|
34 |
+
def load_from_pretrained(
|
35 |
+
repo_id,
|
36 |
+
filename="diffusion_pytorch_model.fp16.safetensors",
|
37 |
+
subfolder="unet",
|
38 |
+
device="cuda",
|
39 |
+
) -> Dict[str, torch.Tensor]:
|
40 |
+
return load_state_dict(
|
41 |
+
hf_hub_download(
|
42 |
+
repo_id=repo_id,
|
43 |
+
filename=filename,
|
44 |
+
subfolder=subfolder,
|
45 |
+
),
|
46 |
+
device=device,
|
47 |
+
)
|
48 |
+
|
49 |
+
|
50 |
+
def reshape_weight_task_tensors(task_tensors, weights):
|
51 |
+
"""
|
52 |
+
Reshapes `weights` to match the shape of `task_tensors` by unsqeezing in the remaining dimenions.
|
53 |
+
|
54 |
+
Args:
|
55 |
+
task_tensors (`torch.Tensor`): The tensors that will be used to reshape `weights`.
|
56 |
+
weights (`torch.Tensor`): The tensor to be reshaped.
|
57 |
+
|
58 |
+
Returns:
|
59 |
+
`torch.Tensor`: The reshaped tensor.
|
60 |
+
"""
|
61 |
+
new_shape = weights.shape + (1,) * (task_tensors.dim() - weights.dim())
|
62 |
+
weights = weights.view(new_shape)
|
63 |
+
return weights
|
64 |
+
|
65 |
+
|
66 |
+
def linear(task_tensors: List[torch.Tensor], weights: torch.Tensor) -> torch.Tensor:
|
67 |
+
"""
|
68 |
+
Merge the task tensors using `linear`.
|
69 |
+
|
70 |
+
Args:
|
71 |
+
task_tensors(`List[torch.Tensor]`):The task tensors to merge.
|
72 |
+
weights (`torch.Tensor`):The weights of the task tensors.
|
73 |
+
|
74 |
+
Returns:
|
75 |
+
`torch.Tensor`: The merged tensor.
|
76 |
+
"""
|
77 |
+
task_tensors = torch.stack(task_tensors, dim=0)
|
78 |
+
# weighted task tensors
|
79 |
+
weights = reshape_weight_task_tensors(task_tensors, weights)
|
80 |
+
weighted_task_tensors = task_tensors * weights
|
81 |
+
mixed_task_tensors = weighted_task_tensors.sum(dim=0)
|
82 |
+
return mixed_task_tensors
|
83 |
+
|
84 |
+
|
85 |
+
def merge_models(task_tensors, weights):
|
86 |
+
keys = list(task_tensors[0].keys())
|
87 |
+
weights = torch.tensor(weights, device=task_tensors[0][keys[0]].device)
|
88 |
+
state_dict = {}
|
89 |
+
for key in tqdm(keys, desc="Merging"):
|
90 |
+
w_list = []
|
91 |
+
for i, sd in enumerate(task_tensors):
|
92 |
+
w = sd.pop(key)
|
93 |
+
w_list.append(w)
|
94 |
+
new_w = linear(task_tensors=w_list, weights=weights)
|
95 |
+
state_dict[key] = new_w
|
96 |
+
return state_dict
|
97 |
+
|
98 |
+
|
99 |
+
def split_conv_attn(weights):
|
100 |
+
attn_tensors = {}
|
101 |
+
conv_tensors = {}
|
102 |
+
for key in list(weights.keys()):
|
103 |
+
if any(k in key for k in ["to_k", "to_q", "to_v", "to_out.0"]):
|
104 |
+
attn_tensors[key] = weights.pop(key)
|
105 |
+
else:
|
106 |
+
conv_tensors[key] = weights.pop(key)
|
107 |
+
return {"conv": conv_tensors, "attn": attn_tensors}
|
108 |
+
|
109 |
+
|
110 |
+
def load_evo_ukiyoe(device="cuda") -> StableDiffusionXLPipeline:
|
111 |
+
# Load base models
|
112 |
+
sdxl_weights = split_conv_attn(load_from_pretrained(SDXL_REPO, device=device))
|
113 |
+
dpo_weights = split_conv_attn(
|
114 |
+
load_from_pretrained(
|
115 |
+
DPO_REPO, "diffusion_pytorch_model.safetensors", device=device
|
116 |
+
)
|
117 |
+
)
|
118 |
+
jn_weights = split_conv_attn(load_from_pretrained(JN_REPO, device=device))
|
119 |
+
jsdxl_weights = split_conv_attn(load_from_pretrained(JSDXL_REPO, device=device))
|
120 |
+
|
121 |
+
# Merge base models
|
122 |
+
tensors = [sdxl_weights, dpo_weights, jn_weights, jsdxl_weights]
|
123 |
+
new_conv = merge_models(
|
124 |
+
[sd["conv"] for sd in tensors],
|
125 |
+
[
|
126 |
+
0.15928833971605916,
|
127 |
+
0.1032449268871776,
|
128 |
+
0.6503217149752791,
|
129 |
+
0.08714501842148402,
|
130 |
+
],
|
131 |
+
)
|
132 |
+
new_attn = merge_models(
|
133 |
+
[sd["attn"] for sd in tensors],
|
134 |
+
[
|
135 |
+
0.1877279276437178,
|
136 |
+
0.20014114603909822,
|
137 |
+
0.3922685507065275,
|
138 |
+
0.2198623756106564,
|
139 |
+
],
|
140 |
+
)
|
141 |
+
|
142 |
+
# Delete no longer needed variables to free
|
143 |
+
del sdxl_weights, dpo_weights, jn_weights, jsdxl_weights
|
144 |
+
gc.collect()
|
145 |
+
if "cuda" in device:
|
146 |
+
torch.cuda.empty_cache()
|
147 |
+
|
148 |
+
# Instantiate UNet
|
149 |
+
unet_config = UNet2DConditionModel.load_config(SDXL_REPO, subfolder="unet")
|
150 |
+
unet = UNet2DConditionModel.from_config(unet_config).to(device=device)
|
151 |
+
unet.load_state_dict({**new_conv, **new_attn})
|
152 |
+
|
153 |
+
# Load other modules
|
154 |
+
text_encoder = CLIPTextModelWithProjection.from_pretrained(
|
155 |
+
JSDXL_REPO, subfolder="text_encoder", torch_dtype=torch.float16, variant="fp16",
|
156 |
+
)
|
157 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
158 |
+
JSDXL_REPO, subfolder="tokenizer", use_fast=False,
|
159 |
+
)
|
160 |
+
|
161 |
+
# Load pipeline
|
162 |
+
pipe = StableDiffusionXLPipeline.from_pretrained(
|
163 |
+
SDXL_REPO,
|
164 |
+
unet=unet,
|
165 |
+
text_encoder=text_encoder,
|
166 |
+
tokenizer=tokenizer,
|
167 |
+
torch_dtype=torch.float16,
|
168 |
+
variant="fp16",
|
169 |
+
)
|
170 |
+
# Load Evo-Ukiyoe weights
|
171 |
+
pipe.load_lora_weights(UKIYOE_REPO)
|
172 |
+
pipe.fuse_lora(lora_scale=1.0)
|
173 |
+
pipe = pipe.to(device=torch.device(device), dtype=torch.float16)
|
174 |
+
|
175 |
+
return pipe
|
176 |
+
|
177 |
+
|
178 |
+
if __name__ == "__main__":
|
179 |
+
pipe = load_evo_ukiyoe()
|
180 |
+
images = pipe(
|
181 |
+
prompt="着物を着ている猫が庭でお茶を飲んでいる。最高品質の輻の浮世絵。超詳細。",
|
182 |
+
negative_prompt="",
|
183 |
+
guidance_scale=8.0,
|
184 |
+
num_inference_steps=50,
|
185 |
+
generator=torch.Generator().manual_seed(0),
|
186 |
+
num_images_per_prompt=1,
|
187 |
+
output_type="pil",
|
188 |
+
).images
|
189 |
+
images[0].save("out.png")
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
torch
|
2 |
+
torchvision
|
3 |
+
|
4 |
+
accelerate==0.32.0
|
5 |
+
diffusers==0.29.2
|
6 |
+
gradio==4.38.1
|
7 |
+
sentencepiece==0.2.0
|
8 |
+
transformers==4.42.3
|
9 |
+
peft==0.11.1
|
safety_checker.py
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Copyright 2023 The HuggingFace Team. All rights reserved.
|
2 |
+
#
|
3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4 |
+
# you may not use this file except in compliance with the License.
|
5 |
+
# You may obtain a copy of the License at
|
6 |
+
#
|
7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8 |
+
#
|
9 |
+
# Unless required by applicable law or agreed to in writing, software
|
10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12 |
+
# See the License for the specific language governing permissions and
|
13 |
+
# limitations under the License.
|
14 |
+
|
15 |
+
import numpy as np
|
16 |
+
import torch
|
17 |
+
import torch.nn as nn
|
18 |
+
from transformers import CLIPConfig, CLIPVisionModel, PreTrainedModel
|
19 |
+
|
20 |
+
|
21 |
+
def cosine_distance(image_embeds, text_embeds):
|
22 |
+
normalized_image_embeds = nn.functional.normalize(image_embeds)
|
23 |
+
normalized_text_embeds = nn.functional.normalize(text_embeds)
|
24 |
+
return torch.mm(normalized_image_embeds, normalized_text_embeds.t())
|
25 |
+
|
26 |
+
|
27 |
+
class StableDiffusionSafetyChecker(PreTrainedModel):
|
28 |
+
config_class = CLIPConfig
|
29 |
+
|
30 |
+
_no_split_modules = ["CLIPEncoderLayer"]
|
31 |
+
|
32 |
+
def __init__(self, config: CLIPConfig):
|
33 |
+
super().__init__(config)
|
34 |
+
|
35 |
+
self.vision_model = CLIPVisionModel(config.vision_config)
|
36 |
+
self.visual_projection = nn.Linear(
|
37 |
+
config.vision_config.hidden_size, config.projection_dim, bias=False
|
38 |
+
)
|
39 |
+
|
40 |
+
self.concept_embeds = nn.Parameter(
|
41 |
+
torch.ones(17, config.projection_dim), requires_grad=False
|
42 |
+
)
|
43 |
+
self.special_care_embeds = nn.Parameter(
|
44 |
+
torch.ones(3, config.projection_dim), requires_grad=False
|
45 |
+
)
|
46 |
+
|
47 |
+
self.concept_embeds_weights = nn.Parameter(torch.ones(17), requires_grad=False)
|
48 |
+
self.special_care_embeds_weights = nn.Parameter(
|
49 |
+
torch.ones(3), requires_grad=False
|
50 |
+
)
|
51 |
+
|
52 |
+
@torch.no_grad()
|
53 |
+
def forward(self, clip_input, images):
|
54 |
+
pooled_output = self.vision_model(clip_input)[1] # pooled_output
|
55 |
+
image_embeds = self.visual_projection(pooled_output)
|
56 |
+
|
57 |
+
# we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
|
58 |
+
special_cos_dist = (
|
59 |
+
cosine_distance(image_embeds, self.special_care_embeds)
|
60 |
+
.cpu()
|
61 |
+
.float()
|
62 |
+
.numpy()
|
63 |
+
)
|
64 |
+
cos_dist = (
|
65 |
+
cosine_distance(image_embeds, self.concept_embeds).cpu().float().numpy()
|
66 |
+
)
|
67 |
+
|
68 |
+
result = []
|
69 |
+
batch_size = image_embeds.shape[0]
|
70 |
+
for i in range(batch_size):
|
71 |
+
result_img = {
|
72 |
+
"special_scores": {},
|
73 |
+
"special_care": [],
|
74 |
+
"concept_scores": {},
|
75 |
+
"bad_concepts": [],
|
76 |
+
}
|
77 |
+
|
78 |
+
# increase this value to create a stronger `nfsw` filter
|
79 |
+
# at the cost of increasing the possibility of filtering benign images
|
80 |
+
adjustment = 0.0
|
81 |
+
|
82 |
+
for concept_idx in range(len(special_cos_dist[0])):
|
83 |
+
concept_cos = special_cos_dist[i][concept_idx]
|
84 |
+
concept_threshold = self.special_care_embeds_weights[concept_idx].item()
|
85 |
+
result_img["special_scores"][concept_idx] = round(
|
86 |
+
concept_cos - concept_threshold + adjustment, 3
|
87 |
+
)
|
88 |
+
if result_img["special_scores"][concept_idx] > 0:
|
89 |
+
result_img["special_care"].append(
|
90 |
+
{concept_idx, result_img["special_scores"][concept_idx]}
|
91 |
+
)
|
92 |
+
adjustment = 0.01
|
93 |
+
|
94 |
+
for concept_idx in range(len(cos_dist[0])):
|
95 |
+
concept_cos = cos_dist[i][concept_idx]
|
96 |
+
concept_threshold = self.concept_embeds_weights[concept_idx].item()
|
97 |
+
result_img["concept_scores"][concept_idx] = round(
|
98 |
+
concept_cos - concept_threshold + adjustment, 3
|
99 |
+
)
|
100 |
+
if result_img["concept_scores"][concept_idx] > 0:
|
101 |
+
result_img["bad_concepts"].append(concept_idx)
|
102 |
+
|
103 |
+
result.append(result_img)
|
104 |
+
|
105 |
+
has_nsfw_concepts = [len(res["bad_concepts"]) > 0 for res in result]
|
106 |
+
|
107 |
+
return has_nsfw_concepts
|
108 |
+
|
109 |
+
@torch.no_grad()
|
110 |
+
def forward_onnx(self, clip_input: torch.FloatTensor, images: torch.FloatTensor):
|
111 |
+
pooled_output = self.vision_model(clip_input)[1] # pooled_output
|
112 |
+
image_embeds = self.visual_projection(pooled_output)
|
113 |
+
|
114 |
+
special_cos_dist = cosine_distance(image_embeds, self.special_care_embeds)
|
115 |
+
cos_dist = cosine_distance(image_embeds, self.concept_embeds)
|
116 |
+
|
117 |
+
# increase this value to create a stronger `nsfw` filter
|
118 |
+
# at the cost of increasing the possibility of filtering benign images
|
119 |
+
adjustment = 0.0
|
120 |
+
|
121 |
+
special_scores = (
|
122 |
+
special_cos_dist - self.special_care_embeds_weights + adjustment
|
123 |
+
)
|
124 |
+
# special_scores = special_scores.round(decimals=3)
|
125 |
+
special_care = torch.any(special_scores > 0, dim=1)
|
126 |
+
special_adjustment = special_care * 0.01
|
127 |
+
special_adjustment = special_adjustment.unsqueeze(1).expand(
|
128 |
+
-1, cos_dist.shape[1]
|
129 |
+
)
|
130 |
+
|
131 |
+
concept_scores = (cos_dist - self.concept_embeds_weights) + special_adjustment
|
132 |
+
# concept_scores = concept_scores.round(decimals=3)
|
133 |
+
has_nsfw_concepts = torch.any(concept_scores > 0, dim=1)
|
134 |
+
|
135 |
+
images[has_nsfw_concepts] = 0.0 # black image
|
136 |
+
|
137 |
+
return images, has_nsfw_concepts
|