Tarin Clanuwat commited on
Commit
86c9e2c
·
0 Parent(s):

initial commit

Browse files
Files changed (10) hide show
  1. .gitattributes +39 -0
  2. README.md +12 -0
  3. app.py +162 -0
  4. evo_nishikie_v1.py +187 -0
  5. requirements.txt +10 -0
  6. safety_checker.py +137 -0
  7. sample1.jpg +3 -0
  8. sample2.jpg +3 -0
  9. sample3.jpg +3 -0
  10. sample4.jpg +3 -0
.gitattributes ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
36
+ sample1.jpg filter=lfs diff=lfs merge=lfs -text
37
+ sample2.jpg filter=lfs diff=lfs merge=lfs -text
38
+ sample3.jpg filter=lfs diff=lfs merge=lfs -text
39
+ sample4.jpg filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Evo-Nishikie
3
+ emoji: 🐠
4
+ colorFrom: red
5
+ colorTo: indigo
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,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+
3
+ from PIL import Image, ImageFilter
4
+ from controlnet_aux import LineartDetector
5
+ from diffusers import EulerDiscreteScheduler
6
+ import gradio as gr
7
+ import numpy as np
8
+ import spaces
9
+ import torch
10
+
11
+ # torch._inductor.config.conv_1x1_as_mm = True
12
+ # torch._inductor.config.coordinate_descent_tuning = True
13
+ # torch._inductor.config.epilogue_fusion = False
14
+ # torch._inductor.config.coordinate_descent_check_all_directions = True
15
+ from evo_nishikie_v1 import load_evo_nishikie
16
+
17
+
18
+ DESCRIPTION = """# 🐟 Evo-Nishikie
19
+ 🤗 [モデル一覧](https://huggingface.co/SakanaAI) | 📝 [ブログ](https://sakana.ai/evo-ukiyoe/) | 🐦 [Twitter](https://twitter.com/SakanaAILabs)
20
+
21
+ [Evo-Nishikie](https://huggingface.co/SakanaAI/Evo-Nishikie-v1)は[Sakana AI](https://sakana.ai/)が教育目的で開発した浮世絵に特化した画像生成モデルです。
22
+ 入力した単色摺の浮世絵(墨摺絵等)を日本語プロンプトに沿って多色摺の浮世絵(錦絵)風に変換した画像を生成することができます。より詳しくは、上記のブログをご参照ください。
23
+ """
24
+ if not torch.cuda.is_available():
25
+ DESCRIPTION += "\n<p>Running on CPU 🥶 This demo may not work on CPU.</p>"
26
+
27
+ MAX_SEED = np.iinfo(np.int32).max
28
+
29
+ device = "cuda" if torch.cuda.is_available() else "cpu"
30
+
31
+ NUM_IMAGES_PER_PROMPT = 1
32
+ SAFETY_CHECKER = True
33
+ if SAFETY_CHECKER:
34
+ from safety_checker import StableDiffusionSafetyChecker
35
+ from transformers import CLIPFeatureExtractor
36
+
37
+ safety_checker = StableDiffusionSafetyChecker.from_pretrained(
38
+ "CompVis/stable-diffusion-safety-checker"
39
+ ).to(device)
40
+ feature_extractor = CLIPFeatureExtractor.from_pretrained(
41
+ "openai/clip-vit-base-patch32"
42
+ )
43
+
44
+ def check_nsfw_images(
45
+ images: list[Image.Image],
46
+ ) -> tuple[list[Image.Image], list[bool]]:
47
+ safety_checker_input = feature_extractor(images, return_tensors="pt").to(device)
48
+ has_nsfw_concepts = safety_checker(
49
+ images=[images], clip_input=safety_checker_input.pixel_values.to(device)
50
+ )
51
+
52
+ return images, has_nsfw_concepts
53
+
54
+
55
+ pipe = load_evo_nishikie(device)
56
+ pipe.scheduler = EulerDiscreteScheduler.from_config(
57
+ pipe.scheduler.config, use_karras_sigmas=True,
58
+ )
59
+ # pipe.unet.to(memory_format=torch.channels_last)
60
+ # pipe.controlnet.to(memory_format=torch.channels_last)
61
+ # pipe.vae.to(memory_format=torch.channels_last)
62
+ # # Compile the UNet, ControlNet and VAE.
63
+ # pipe.unet = torch.compile(pipe.unet, mode="max-autotune", fullgraph=True)
64
+ # pipe.controlnet = torch.compile(pipe.controlnet, mode="max-autotune", fullgraph=True)
65
+ # pipe.vae.decode = torch.compile(pipe.vae.decode, mode="max-autotune", fullgraph=True)
66
+
67
+ lineart_detector = LineartDetector.from_pretrained("lllyasviel/Annotators")
68
+ image_filter = ImageFilter.MedianFilter(size=3)
69
+ BINARY_THRESHOLD = 40
70
+
71
+
72
+ def randomize_seed_fn(seed: int, randomize_seed: bool) -> int:
73
+ if randomize_seed:
74
+ seed = random.randint(0, MAX_SEED)
75
+ return seed
76
+
77
+
78
+ @spaces.GPU
79
+ @torch.inference_mode()
80
+ def generate(
81
+ input_image: Image.Image,
82
+ prompt: str,
83
+ seed: int = 0,
84
+ randomize_seed: bool = False,
85
+ progress=gr.Progress(track_tqdm=True),
86
+ ):
87
+ pipe.to(device)
88
+
89
+ lineart_image = lineart_detector(input_image, coarse=False, image_resolution=1024)
90
+ lineart_image_filtered = lineart_image.filter(image_filter)
91
+ conditioning_image = lineart_image_filtered.point(lambda p: 255 if p > BINARY_THRESHOLD else 0).convert("L")
92
+
93
+ seed = int(randomize_seed_fn(seed, randomize_seed))
94
+ generator = torch.Generator().manual_seed(seed)
95
+
96
+ images = pipe(
97
+ prompt=prompt + "最高品質の輻の浮世絵。超詳細。",
98
+ negative_prompt="暗い",
99
+ image=conditioning_image,
100
+ guidance_scale=7.0,
101
+ controlnet_conditioning_scale=0.8,
102
+ num_inference_steps=50,
103
+ generator=generator,
104
+ num_images_per_prompt=NUM_IMAGES_PER_PROMPT,
105
+ output_type="pil",
106
+ ).images
107
+
108
+ if SAFETY_CHECKER:
109
+ images, has_nsfw_concepts = check_nsfw_images(images)
110
+ if any(has_nsfw_concepts):
111
+ gr.Warning("NSFW content detected.")
112
+ return Image.new("RGB", (512, 512), "WHITE"), seed
113
+ return images[0], seed
114
+
115
+
116
+ examples = [
117
+ ["./sample1.jpg", "女性がやかんと鍋を持ち、小屋の前に立っています。背景には室内で会話する人々がいます。"],
118
+ ["./sample2.jpg", "着物を着た女性が、赤ん坊を抱え、もう一人の子どもが手押し車を引いています。背景には木があります。"],
119
+ ["./sample3.jpg", "女性が花柄の着物を着ており、他の人物たちが座りながら会話しています。背景には家の内部があります。"],
120
+ ["./sample4.jpg", "花柄や模様入りの着物を着た男女が室内で集まり、煎茶の準備をしています。背景に木材の装飾があります。"],
121
+ ]
122
+
123
+ css = """
124
+ .gradio-container{max-width: 1380px !important}
125
+ h1{text-align:center}
126
+ """
127
+ with gr.Blocks(css=css) as demo:
128
+ gr.Markdown(DESCRIPTION)
129
+ with gr.Row():
130
+ with gr.Column():
131
+ input_image = gr.Image(image_mode="RGB", type="pil", show_label=False)
132
+ prompt = gr.Textbox(placeholder="日本語でプロンプトを入力してください。", show_label=False)
133
+ submit = gr.Button()
134
+ with gr.Accordion("詳細設定", open=False):
135
+ seed = gr.Slider(label="シード値", minimum=0, maximum=MAX_SEED, step=1, value=0)
136
+ randomize_seed = gr.Checkbox(label="ランダムにシード値を決定", value=True)
137
+ with gr.Column():
138
+ result = gr.Image(label="Evo-Nishikieからの生成結果", type="pil", show_label=False)
139
+ gr.Examples(examples=examples, inputs=[input_image, prompt], outputs=[result, seed], fn=generate)
140
+ gr.on(
141
+ triggers=[
142
+ submit.click,
143
+ ],
144
+ fn=generate,
145
+ inputs=[
146
+ input_image,
147
+ prompt,
148
+ seed,
149
+ randomize_seed,
150
+ ],
151
+ outputs=[result, seed],
152
+ api_name="run",
153
+ )
154
+ gr.Markdown("""⚠️ 本モデルは実験段階のプロトタイプであり、教育および研究開発の目的でのみ提供されています。商用利用や、障害が重大な影響を及ぼす可能性のある環境(ミッションクリティカルな環境)での使用には適していません。
155
+ 本モデルの使用は、利用者の自己責任で行われ、その性能や結果については何ら保証されません。
156
+ Sakana AIは、本モデルの使用によって生じた直接的または間接的な損失に対して、結果に関わらず、一切の責任を負いません。
157
+ 利用者は、本モデルの使用に伴うリスクを十分に理解し、自身の判断で使用することが必要です。
158
+ アップロードされた画像は画像生成のみに使用され、サーバー上に保存されることはありません。
159
+
160
+ 出典:サンプル画像はすべて[日本古典籍データセット(国文学研究資料館蔵)『絵本玉かつら』](http://codh.rois.ac.jp/pmjt/book/200013861/)から引用しました。""")
161
+
162
+ demo.queue().launch()
evo_nishikie_v1.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gc
2
+ import os
3
+ from typing import Dict, List, Union
4
+
5
+ from diffusers import (
6
+ ControlNetModel,
7
+ StableDiffusionXLControlNetPipeline,
8
+ UNet2DConditionModel,
9
+ )
10
+ from huggingface_hub import hf_hub_download
11
+ import safetensors
12
+ import torch
13
+ from tqdm import tqdm
14
+ from transformers import AutoTokenizer, CLIPTextModelWithProjection
15
+
16
+
17
+ # Base models
18
+ SDXL_REPO = "stabilityai/stable-diffusion-xl-base-1.0"
19
+ DPO_REPO = "mhdang/dpo-sdxl-text2image-v1"
20
+ JN_REPO = "RunDiffusion/Juggernaut-XL-v9"
21
+ JSDXL_REPO = "stabilityai/japanese-stable-diffusion-xl"
22
+
23
+ # Evo-Ukiyoe
24
+ UKIYOE_REPO = "SakanaAI/Evo-Ukiyoe-v1"
25
+
26
+ # Evo-Nishikie
27
+ NISHIKIE_REPO = "SakanaAI/Evo-Nishikie-v1"
28
+
29
+
30
+ def load_state_dict(checkpoint_file: Union[str, os.PathLike], device: str = "cpu"):
31
+ file_extension = os.path.basename(checkpoint_file).split(".")[-1]
32
+ if file_extension == "safetensors":
33
+ return safetensors.torch.load_file(checkpoint_file, device=device)
34
+ else:
35
+ return torch.load(checkpoint_file, map_location=device)
36
+
37
+
38
+ def load_from_pretrained(
39
+ repo_id,
40
+ filename="diffusion_pytorch_model.fp16.safetensors",
41
+ subfolder="unet",
42
+ device="cuda",
43
+ ) -> Dict[str, torch.Tensor]:
44
+ return load_state_dict(
45
+ hf_hub_download(
46
+ repo_id=repo_id,
47
+ filename=filename,
48
+ subfolder=subfolder,
49
+ ),
50
+ device=device,
51
+ )
52
+
53
+
54
+ def reshape_weight_task_tensors(task_tensors, weights):
55
+ """
56
+ Reshapes `weights` to match the shape of `task_tensors` by unsqueezing in the remaining dimensions.
57
+
58
+ Args:
59
+ task_tensors (`torch.Tensor`): The tensors that will be used to reshape `weights`.
60
+ weights (`torch.Tensor`): The tensor to be reshaped.
61
+
62
+ Returns:
63
+ `torch.Tensor`: The reshaped tensor.
64
+ """
65
+ new_shape = weights.shape + (1,) * (task_tensors.dim() - weights.dim())
66
+ weights = weights.view(new_shape)
67
+ return weights
68
+
69
+
70
+ def linear(task_tensors: List[torch.Tensor], weights: torch.Tensor) -> torch.Tensor:
71
+ """
72
+ Merge the task tensors using `linear`.
73
+
74
+ Args:
75
+ task_tensors(`List[torch.Tensor]`):The task tensors to merge.
76
+ weights (`torch.Tensor`):The weights of the task tensors.
77
+
78
+ Returns:
79
+ `torch.Tensor`: The merged tensor.
80
+ """
81
+ task_tensors = torch.stack(task_tensors, dim=0)
82
+ # weighted task tensors
83
+ weights = reshape_weight_task_tensors(task_tensors, weights)
84
+ weighted_task_tensors = task_tensors * weights
85
+ mixed_task_tensors = weighted_task_tensors.sum(dim=0)
86
+ return mixed_task_tensors
87
+
88
+
89
+ def merge_models(task_tensors, weights):
90
+ keys = list(task_tensors[0].keys())
91
+ weights = torch.tensor(weights, device=task_tensors[0][keys[0]].device)
92
+ state_dict = {}
93
+ for key in tqdm(keys, desc="Merging"):
94
+ w_list = []
95
+ for i, sd in enumerate(task_tensors):
96
+ w = sd.pop(key)
97
+ w_list.append(w)
98
+ new_w = linear(task_tensors=w_list, weights=weights)
99
+ state_dict[key] = new_w
100
+ return state_dict
101
+
102
+
103
+ def split_conv_attn(weights):
104
+ attn_tensors = {}
105
+ conv_tensors = {}
106
+ for key in list(weights.keys()):
107
+ if any(k in key for k in ["to_k", "to_q", "to_v", "to_out.0"]):
108
+ attn_tensors[key] = weights.pop(key)
109
+ else:
110
+ conv_tensors[key] = weights.pop(key)
111
+ return {"conv": conv_tensors, "attn": attn_tensors}
112
+
113
+
114
+ def load_evo_nishikie(device="cuda") -> StableDiffusionXLControlNetPipeline:
115
+ # Load base models
116
+ sdxl_weights = split_conv_attn(load_from_pretrained(SDXL_REPO, device=device))
117
+ dpo_weights = split_conv_attn(
118
+ load_from_pretrained(
119
+ DPO_REPO, "diffusion_pytorch_model.safetensors", device=device
120
+ )
121
+ )
122
+ jn_weights = split_conv_attn(load_from_pretrained(JN_REPO, device=device))
123
+ jsdxl_weights = split_conv_attn(load_from_pretrained(JSDXL_REPO, device=device))
124
+
125
+ # Merge base models
126
+ tensors = [sdxl_weights, dpo_weights, jn_weights, jsdxl_weights]
127
+ new_conv = merge_models(
128
+ [sd["conv"] for sd in tensors],
129
+ [
130
+ 0.15928833971605916,
131
+ 0.1032449268871776,
132
+ 0.6503217149752791,
133
+ 0.08714501842148402,
134
+ ],
135
+ )
136
+ new_attn = merge_models(
137
+ [sd["attn"] for sd in tensors],
138
+ [
139
+ 0.1877279276437178,
140
+ 0.20014114603909822,
141
+ 0.3922685507065275,
142
+ 0.2198623756106564,
143
+ ],
144
+ )
145
+
146
+ # Delete no longer needed variables to free
147
+ del sdxl_weights, dpo_weights, jn_weights, jsdxl_weights
148
+ gc.collect()
149
+ if "cuda" in device:
150
+ torch.cuda.empty_cache()
151
+
152
+ # Instantiate UNet
153
+ unet_config = UNet2DConditionModel.load_config(SDXL_REPO, subfolder="unet")
154
+ unet = UNet2DConditionModel.from_config(unet_config).to(device=device)
155
+ unet.load_state_dict({**new_conv, **new_attn})
156
+
157
+ # Load other modules
158
+ text_encoder = CLIPTextModelWithProjection.from_pretrained(
159
+ JSDXL_REPO, subfolder="text_encoder", torch_dtype=torch.float16, variant="fp16",
160
+ )
161
+ tokenizer = AutoTokenizer.from_pretrained(
162
+ JSDXL_REPO, subfolder="tokenizer", use_fast=False,
163
+ )
164
+
165
+ # Load Evo-Nishikie weights
166
+ controlnet = ControlNetModel.from_pretrained(
167
+ NISHIKIE_REPO, torch_dtype=torch.float16, device=device,
168
+ )
169
+
170
+ # Load pipeline
171
+ pipe = StableDiffusionXLControlNetPipeline.from_pretrained(
172
+ SDXL_REPO,
173
+ unet=unet,
174
+ text_encoder=text_encoder,
175
+ tokenizer=tokenizer,
176
+ controlnet=controlnet,
177
+ torch_dtype=torch.float16,
178
+ variant="fp16",
179
+ )
180
+
181
+ # Load Evo-Ukiyoe weights
182
+ pipe.load_lora_weights(UKIYOE_REPO)
183
+ pipe.fuse_lora(lora_scale=1.0)
184
+
185
+ pipe = pipe.to(device, dtype=torch.float16)
186
+
187
+ return pipe
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ torch
2
+ torchvision
3
+
4
+ accelerate==0.32.0
5
+ controlnet-aux==0.0.9
6
+ diffusers==0.29.2
7
+ gradio==4.38.1
8
+ sentencepiece==0.2.0
9
+ transformers==4.42.3
10
+ 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
sample1.jpg ADDED

Git LFS Details

  • SHA256: b9fe5a98203730cb506e915c42a1031b3cebe28f5fa9b5489d635a494ee26be8
  • Pointer size: 132 Bytes
  • Size of remote file: 2.8 MB
sample2.jpg ADDED

Git LFS Details

  • SHA256: 52803116a60fd53f8ed5f621f4296c66a3c761dce9589b6cdfb6acff6b269ab2
  • Pointer size: 132 Bytes
  • Size of remote file: 1.33 MB
sample3.jpg ADDED

Git LFS Details

  • SHA256: 7f7fc9aad3df3400a774527faf821b1e815f5a47fc1e53f0ed59a5a4aa305bd7
  • Pointer size: 132 Bytes
  • Size of remote file: 2.48 MB
sample4.jpg ADDED

Git LFS Details

  • SHA256: c27553237814625692eb344dc28d0e2cceb1b9176a95ab96a2c5ba64313ac162
  • Pointer size: 132 Bytes
  • Size of remote file: 2.67 MB