InvokeAI 2.2.4.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- backend/__init__.py +0 -0
- backend/invoke_ai_web_server.py +1724 -0
- backend/modules/__init__.py +0 -0
- backend/modules/create_cmd_parser.py +55 -0
- backend/modules/get_canvas_generation_mode.py +117 -0
- backend/modules/parameters.py +71 -0
- backend/modules/parse_seed_weights.py +47 -0
- configs/INITIAL_MODELS.yaml +80 -0
- configs/models.yaml.example +29 -0
- configs/sd-concepts.txt +803 -0
- configs/stable-diffusion/v1-finetune.yaml +110 -0
- configs/stable-diffusion/v1-finetune_style.yaml +103 -0
- configs/stable-diffusion/v1-inference.yaml +79 -0
- configs/stable-diffusion/v1-inpainting-inference.yaml +79 -0
- configs/stable-diffusion/v1-m1-finetune.yaml +110 -0
- docs/assets/invoke-web-server-1.png +0 -0
- docs/assets/invoke_ai_banner.png +0 -0
- invokeai-2.2.4.5.data/data/assets/caution.png +0 -0
- invokeai-2.2.4.5.data/data/configs/INITIAL_MODELS.yaml +80 -0
- invokeai-2.2.4.5.data/data/configs/models.yaml.example +29 -0
- invokeai-2.2.4.5.data/data/configs/sd-concepts.txt +803 -0
- invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-finetune.yaml +110 -0
- invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-finetune_style.yaml +103 -0
- invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-inference.yaml +79 -0
- invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-inpainting-inference.yaml +79 -0
- invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-m1-finetune.yaml +110 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/Inter-Bold.790c108b.ttf +0 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/Inter.b9a8e5e2.ttf +0 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/favicon.0d253ced.ico +0 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/index-legacy-b741077f.js +48 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/index.0dadf5d0.css +1 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/index.21ff81eb.js +625 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/logo.13003d72.png +0 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/polyfills-legacy-dde3a68a.js +4 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/polyfills.1ff60148.js +1 -0
- invokeai-2.2.4.5.data/data/frontend/dist/index.html +23 -0
- invokeai-2.2.4.5.data/data/scripts/configure_invokeai.py +797 -0
- invokeai-2.2.4.5.data/data/scripts/create_root.py +43 -0
- invokeai-2.2.4.5.data/data/scripts/dream.py +10 -0
- invokeai-2.2.4.5.data/data/scripts/images2prompt.py +30 -0
- invokeai-2.2.4.5.data/data/scripts/invoke.py +5 -0
- invokeai-2.2.4.5.data/data/scripts/legacy_api.py +681 -0
- invokeai-2.2.4.5.data/data/scripts/load_models.py +12 -0
- invokeai-2.2.4.5.data/data/scripts/merge_embeddings.py +124 -0
- invokeai-2.2.4.5.data/data/scripts/preload_models.py +12 -0
- invokeai-2.2.4.5.data/data/scripts/sd-metadata.py +22 -0
- invokeai-2.2.4.5.dist-info/LICENSE +21 -0
- invokeai-2.2.4.5.dist-info/LICENSE-ModelWeights.txt +294 -0
- invokeai-2.2.4.5.dist-info/METADATA +288 -0
- invokeai-2.2.4.5.dist-info/RECORD +169 -0
- invokeai-2.2.4.5.dist-info/WHEEL +5 -0
- invokeai-2.2.4.5.dist-info/entry_points.txt +4 -0
- invokeai-2.2.4.5.dist-info/top_level.txt +6 -0
- invokeai-2.2.4.5.dist-info/zip-safe +1 -0
- ldm/__init__.py +0 -0
- ldm/data/__init__.py +0 -0
- ldm/data/base.py +31 -0
- ldm/data/imagenet.py +466 -0
- ldm/data/lsun.py +126 -0
- ldm/data/personalized.py +202 -0
- ldm/data/personalized_style.py +169 -0
- ldm/generate.py +1159 -0
- ldm/invoke/CLI.py +960 -0
- ldm/invoke/__init__.py +3 -0
- ldm/invoke/args.py +1257 -0
- ldm/invoke/concepts_lib.py +169 -0
- ldm/invoke/conditioning.py +289 -0
- ldm/invoke/devices.py +27 -0
- ldm/invoke/generator/__init__.py +4 -0
- ldm/invoke/generator/base.py +335 -0
- ldm/invoke/generator/embiggen.py +501 -0
- ldm/invoke/generator/img2img.py +97 -0
- ldm/invoke/generator/inpaint.py +358 -0
- ldm/invoke/generator/omnibus.py +175 -0
- ldm/invoke/generator/txt2img.py +88 -0
- ldm/invoke/generator/txt2img2img.py +182 -0
- ldm/invoke/globals.py +31 -0
- ldm/invoke/image_util.py +70 -0
- ldm/invoke/log.py +66 -0
- ldm/invoke/model_cache.py +449 -0
- ldm/invoke/patchmatch.py +44 -0
- ldm/invoke/pngwriter.py +113 -0
- ldm/invoke/prompt_parser.py +655 -0
- ldm/invoke/readline.py +442 -0
- ldm/invoke/restoration/__init__.py +4 -0
- ldm/invoke/restoration/base.py +38 -0
- ldm/invoke/restoration/codeformer.py +103 -0
- ldm/invoke/restoration/codeformer_arch.py +276 -0
- ldm/invoke/restoration/gfpgan.py +84 -0
- ldm/invoke/restoration/outcrop.py +108 -0
- ldm/invoke/restoration/outpaint.py +92 -0
- ldm/invoke/restoration/realesrgan.py +90 -0
- ldm/invoke/restoration/vqgan_arch.py +435 -0
- ldm/invoke/seamless.py +30 -0
- ldm/invoke/server.py +282 -0
- ldm/invoke/server_legacy.py +246 -0
- ldm/invoke/txt2mask.py +136 -0
- ldm/lr_scheduler.py +143 -0
- ldm/models/__init__.py +0 -0
- ldm/models/autoencoder.py +596 -0
- ldm/models/diffusion/__init__.py +0 -0
- ldm/models/diffusion/classifier.py +355 -0
- ldm/models/diffusion/cross_attention_control.py +443 -0
- ldm/models/diffusion/cross_attention_map_saving.py +95 -0
- ldm/models/diffusion/ddim.py +111 -0
- ldm/models/diffusion/ddpm.py +2263 -0
- ldm/models/diffusion/ksampler.py +312 -0
- ldm/models/diffusion/plms.py +146 -0
- ldm/models/diffusion/sampler.py +450 -0
- ldm/models/diffusion/shared_invokeai_diffusion.py +244 -0
- ldm/modules/__init__.py +0 -0
- ldm/modules/attention.py +274 -0
- ldm/modules/diffusionmodules/__init__.py +0 -0
- ldm/modules/diffusionmodules/model.py +894 -0
- ldm/modules/diffusionmodules/openaimodel.py +1036 -0
- ldm/modules/diffusionmodules/util.py +314 -0
- ldm/modules/distributions/__init__.py +0 -0
- ldm/modules/distributions/distributions.py +104 -0
- ldm/modules/ema.py +88 -0
- ldm/modules/embedding_manager.py +413 -0
- ldm/modules/encoders/__init__.py +0 -0
- ldm/modules/encoders/modules.py +772 -0
- ldm/modules/image_degradation/__init__.py +6 -0
- ldm/modules/image_degradation/bsrgan.py +846 -0
- ldm/modules/image_degradation/bsrgan_light.py +751 -0
- ldm/modules/image_degradation/utils_image.py +1024 -0
- ldm/modules/losses/__init__.py +1 -0
- ldm/modules/losses/contperceptual.py +175 -0
- ldm/modules/losses/vqperceptual.py +240 -0
- ldm/modules/x_transformer.py +752 -0
- ldm/simplet2i.py +13 -0
- ldm/util.py +264 -0
- scripts/configure_invokeai.py +797 -0
- scripts/create_root.py +43 -0
- scripts/dream.py +10 -0
- scripts/images2prompt.py +30 -0
- scripts/invoke.py +5 -0
- scripts/legacy_api.py +681 -0
- scripts/load_models.py +12 -0
- scripts/merge_embeddings.py +124 -0
- scripts/orig_scripts/img2img.py +296 -0
- scripts/orig_scripts/inpaint.py +98 -0
- scripts/orig_scripts/knn2img.py +397 -0
- scripts/orig_scripts/sample_diffusion.py +313 -0
- scripts/orig_scripts/train_searcher.py +147 -0
- scripts/orig_scripts/txt2img.py +324 -0
- scripts/preload_models.py +12 -0
- scripts/sd-metadata.py +22 -0
- vendored/clip/clip/__init__.py +1 -0
- vendored/clip/clip/bpe_simple_vocab_16e6.txt.gz +0 -0
- vendored/clip/clip/clip.py +237 -0
- vendored/clip/clip/model.py +436 -0
- vendored/clip/clip/simple_tokenizer.py +132 -0
- vendored/clip/hubconf.py +42 -0
- vendored/clip/setup.py +21 -0
- vendored/clip/tests/test_consistency.py +25 -0
- vendored/clipseg/datasets/coco_wrapper.py +99 -0
- vendored/clipseg/datasets/pascal_zeroshot.py +60 -0
- vendored/clipseg/datasets/pfe_dataset.py +129 -0
- vendored/clipseg/datasets/phrasecut.py +380 -0
- vendored/clipseg/datasets/utils.py +68 -0
- vendored/clipseg/evaluation_utils.py +292 -0
- vendored/clipseg/general_utils.py +272 -0
- vendored/clipseg/metrics.py +271 -0
- vendored/clipseg/models/clipseg.py +538 -0
- vendored/clipseg/models/vitseg.py +286 -0
- vendored/clipseg/score.py +453 -0
- vendored/clipseg/setup.py +30 -0
- vendored/clipseg/training.py +266 -0
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import os.path
|
|
3
|
+
import torch
|
|
4
|
+
import torch.nn as nn
|
|
5
|
+
from functools import partial
|
|
6
|
+
import vendored.clip.clip.clip as clip
|
|
7
|
+
from einops import rearrange, repeat
|
|
8
|
+
from transformers import CLIPTokenizer, CLIPTextModel
|
|
9
|
+
import kornia
|
|
10
|
+
from ldm.invoke.devices import choose_torch_device
|
|
11
|
+
from ldm.invoke.globals import Globals
|
|
12
|
+
|
|
13
|
+
from ldm.modules.x_transformer import (
|
|
14
|
+
Encoder,
|
|
15
|
+
TransformerWrapper,
|
|
16
|
+
) # TODO: can we directly rely on lucidrains code and simply add this as a reuirement? --> test
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _expand_mask(mask, dtype, tgt_len=None):
|
|
20
|
+
"""
|
|
21
|
+
Expands attention_mask from `[bsz, seq_len]` to `[bsz, 1, tgt_seq_len, src_seq_len]`.
|
|
22
|
+
"""
|
|
23
|
+
bsz, src_len = mask.size()
|
|
24
|
+
tgt_len = tgt_len if tgt_len is not None else src_len
|
|
25
|
+
|
|
26
|
+
expanded_mask = (
|
|
27
|
+
mask[:, None, None, :].expand(bsz, 1, tgt_len, src_len).to(dtype)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
inverted_mask = 1.0 - expanded_mask
|
|
31
|
+
|
|
32
|
+
return inverted_mask.masked_fill(
|
|
33
|
+
inverted_mask.to(torch.bool), torch.finfo(dtype).min
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _build_causal_attention_mask(bsz, seq_len, dtype):
|
|
38
|
+
# lazily create causal attention mask, with full attention between the vision tokens
|
|
39
|
+
# pytorch uses additive attention mask; fill with -inf
|
|
40
|
+
mask = torch.empty(bsz, seq_len, seq_len, dtype=dtype)
|
|
41
|
+
mask.fill_(torch.tensor(torch.finfo(dtype).min))
|
|
42
|
+
mask.triu_(1) # zero out the lower diagonal
|
|
43
|
+
mask = mask.unsqueeze(1) # expand mask
|
|
44
|
+
return mask
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AbstractEncoder(nn.Module):
|
|
48
|
+
def __init__(self):
|
|
49
|
+
super().__init__()
|
|
50
|
+
|
|
51
|
+
def encode(self, *args, **kwargs):
|
|
52
|
+
raise NotImplementedError
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ClassEmbedder(nn.Module):
|
|
56
|
+
def __init__(self, embed_dim, n_classes=1000, key='class'):
|
|
57
|
+
super().__init__()
|
|
58
|
+
self.key = key
|
|
59
|
+
self.embedding = nn.Embedding(n_classes, embed_dim)
|
|
60
|
+
|
|
61
|
+
def forward(self, batch, key=None):
|
|
62
|
+
if key is None:
|
|
63
|
+
key = self.key
|
|
64
|
+
# this is for use in crossattn
|
|
65
|
+
c = batch[key][:, None]
|
|
66
|
+
c = self.embedding(c)
|
|
67
|
+
return c
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TransformerEmbedder(AbstractEncoder):
|
|
71
|
+
"""Some transformer encoder layers"""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
n_embed,
|
|
76
|
+
n_layer,
|
|
77
|
+
vocab_size,
|
|
78
|
+
max_seq_len=77,
|
|
79
|
+
device=choose_torch_device(),
|
|
80
|
+
):
|
|
81
|
+
super().__init__()
|
|
82
|
+
self.device = device
|
|
83
|
+
self.transformer = TransformerWrapper(
|
|
84
|
+
num_tokens=vocab_size,
|
|
85
|
+
max_seq_len=max_seq_len,
|
|
86
|
+
attn_layers=Encoder(dim=n_embed, depth=n_layer),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def forward(self, tokens):
|
|
90
|
+
tokens = tokens.to(self.device) # meh
|
|
91
|
+
z = self.transformer(tokens, return_embeddings=True)
|
|
92
|
+
return z
|
|
93
|
+
|
|
94
|
+
def encode(self, x):
|
|
95
|
+
return self(x)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class BERTTokenizer(AbstractEncoder):
|
|
99
|
+
"""Uses a pretrained BERT tokenizer by huggingface. Vocab size: 30522 (?)"""
|
|
100
|
+
|
|
101
|
+
def __init__(
|
|
102
|
+
self, device=choose_torch_device(), vq_interface=True, max_length=77
|
|
103
|
+
):
|
|
104
|
+
super().__init__()
|
|
105
|
+
from transformers import (
|
|
106
|
+
BertTokenizerFast,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
cache = os.path.join(Globals.root,'models/bert-base-uncased')
|
|
110
|
+
try:
|
|
111
|
+
self.tokenizer = BertTokenizerFast.from_pretrained(
|
|
112
|
+
'bert-base-uncased',
|
|
113
|
+
cache_dir=cache,
|
|
114
|
+
local_files_only=True
|
|
115
|
+
)
|
|
116
|
+
except OSError:
|
|
117
|
+
raise SystemExit(
|
|
118
|
+
"* Couldn't load Bert tokenizer files. Try running scripts/preload_models.py from an internet-conected machine."
|
|
119
|
+
)
|
|
120
|
+
self.device = device
|
|
121
|
+
self.vq_interface = vq_interface
|
|
122
|
+
self.max_length = max_length
|
|
123
|
+
|
|
124
|
+
def forward(self, text):
|
|
125
|
+
batch_encoding = self.tokenizer(
|
|
126
|
+
text,
|
|
127
|
+
truncation=True,
|
|
128
|
+
max_length=self.max_length,
|
|
129
|
+
return_length=True,
|
|
130
|
+
return_overflowing_tokens=False,
|
|
131
|
+
padding='max_length',
|
|
132
|
+
return_tensors='pt',
|
|
133
|
+
)
|
|
134
|
+
tokens = batch_encoding['input_ids'].to(self.device)
|
|
135
|
+
return tokens
|
|
136
|
+
|
|
137
|
+
@torch.no_grad()
|
|
138
|
+
def encode(self, text):
|
|
139
|
+
tokens = self(text)
|
|
140
|
+
if not self.vq_interface:
|
|
141
|
+
return tokens
|
|
142
|
+
return None, None, [None, None, tokens]
|
|
143
|
+
|
|
144
|
+
def decode(self, text):
|
|
145
|
+
return text
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class BERTEmbedder(AbstractEncoder):
|
|
149
|
+
"""Uses the BERT tokenizr model and add some transformer encoder layers"""
|
|
150
|
+
|
|
151
|
+
def __init__(
|
|
152
|
+
self,
|
|
153
|
+
n_embed,
|
|
154
|
+
n_layer,
|
|
155
|
+
vocab_size=30522,
|
|
156
|
+
max_seq_len=77,
|
|
157
|
+
device=choose_torch_device(),
|
|
158
|
+
use_tokenizer=True,
|
|
159
|
+
embedding_dropout=0.0,
|
|
160
|
+
):
|
|
161
|
+
super().__init__()
|
|
162
|
+
self.use_tknz_fn = use_tokenizer
|
|
163
|
+
if self.use_tknz_fn:
|
|
164
|
+
self.tknz_fn = BERTTokenizer(
|
|
165
|
+
vq_interface=False, max_length=max_seq_len
|
|
166
|
+
)
|
|
167
|
+
self.device = device
|
|
168
|
+
self.transformer = TransformerWrapper(
|
|
169
|
+
num_tokens=vocab_size,
|
|
170
|
+
max_seq_len=max_seq_len,
|
|
171
|
+
attn_layers=Encoder(dim=n_embed, depth=n_layer),
|
|
172
|
+
emb_dropout=embedding_dropout,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
def forward(self, text, embedding_manager=None):
|
|
176
|
+
if self.use_tknz_fn:
|
|
177
|
+
tokens = self.tknz_fn(text) # .to(self.device)
|
|
178
|
+
else:
|
|
179
|
+
tokens = text
|
|
180
|
+
z = self.transformer(
|
|
181
|
+
tokens, return_embeddings=True, embedding_manager=embedding_manager
|
|
182
|
+
)
|
|
183
|
+
return z
|
|
184
|
+
|
|
185
|
+
def encode(self, text, **kwargs):
|
|
186
|
+
# output of length 77
|
|
187
|
+
return self(text, **kwargs)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class SpatialRescaler(nn.Module):
|
|
191
|
+
def __init__(
|
|
192
|
+
self,
|
|
193
|
+
n_stages=1,
|
|
194
|
+
method='bilinear',
|
|
195
|
+
multiplier=0.5,
|
|
196
|
+
in_channels=3,
|
|
197
|
+
out_channels=None,
|
|
198
|
+
bias=False,
|
|
199
|
+
):
|
|
200
|
+
super().__init__()
|
|
201
|
+
self.n_stages = n_stages
|
|
202
|
+
assert self.n_stages >= 0
|
|
203
|
+
assert method in [
|
|
204
|
+
'nearest',
|
|
205
|
+
'linear',
|
|
206
|
+
'bilinear',
|
|
207
|
+
'trilinear',
|
|
208
|
+
'bicubic',
|
|
209
|
+
'area',
|
|
210
|
+
]
|
|
211
|
+
self.multiplier = multiplier
|
|
212
|
+
self.interpolator = partial(
|
|
213
|
+
torch.nn.functional.interpolate, mode=method
|
|
214
|
+
)
|
|
215
|
+
self.remap_output = out_channels is not None
|
|
216
|
+
if self.remap_output:
|
|
217
|
+
print(
|
|
218
|
+
f'Spatial Rescaler mapping from {in_channels} to {out_channels} channels after resizing.'
|
|
219
|
+
)
|
|
220
|
+
self.channel_mapper = nn.Conv2d(
|
|
221
|
+
in_channels, out_channels, 1, bias=bias
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
def forward(self, x):
|
|
225
|
+
for stage in range(self.n_stages):
|
|
226
|
+
x = self.interpolator(x, scale_factor=self.multiplier)
|
|
227
|
+
|
|
228
|
+
if self.remap_output:
|
|
229
|
+
x = self.channel_mapper(x)
|
|
230
|
+
return x
|
|
231
|
+
|
|
232
|
+
def encode(self, x):
|
|
233
|
+
return self(x)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class FrozenCLIPEmbedder(AbstractEncoder):
|
|
237
|
+
"""Uses the CLIP transformer encoder for text (from Hugging Face)"""
|
|
238
|
+
|
|
239
|
+
def __init__(
|
|
240
|
+
self,
|
|
241
|
+
version='openai/clip-vit-large-patch14',
|
|
242
|
+
device=choose_torch_device(),
|
|
243
|
+
max_length=77,
|
|
244
|
+
):
|
|
245
|
+
super().__init__()
|
|
246
|
+
cache = os.path.join(Globals.root,'models',version)
|
|
247
|
+
self.tokenizer = CLIPTokenizer.from_pretrained(
|
|
248
|
+
version,
|
|
249
|
+
cache_dir=cache,
|
|
250
|
+
local_files_only=True
|
|
251
|
+
)
|
|
252
|
+
self.transformer = CLIPTextModel.from_pretrained(
|
|
253
|
+
version,
|
|
254
|
+
cache_dir=cache,
|
|
255
|
+
local_files_only=True
|
|
256
|
+
)
|
|
257
|
+
self.device = device
|
|
258
|
+
self.max_length = max_length
|
|
259
|
+
self.freeze()
|
|
260
|
+
|
|
261
|
+
def embedding_forward(
|
|
262
|
+
self,
|
|
263
|
+
input_ids=None,
|
|
264
|
+
position_ids=None,
|
|
265
|
+
inputs_embeds=None,
|
|
266
|
+
embedding_manager=None,
|
|
267
|
+
) -> torch.Tensor:
|
|
268
|
+
|
|
269
|
+
seq_length = (
|
|
270
|
+
input_ids.shape[-1]
|
|
271
|
+
if input_ids is not None
|
|
272
|
+
else inputs_embeds.shape[-2]
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
if position_ids is None:
|
|
276
|
+
position_ids = self.position_ids[:, :seq_length]
|
|
277
|
+
|
|
278
|
+
if inputs_embeds is None:
|
|
279
|
+
inputs_embeds = self.token_embedding(input_ids)
|
|
280
|
+
|
|
281
|
+
if embedding_manager is not None:
|
|
282
|
+
inputs_embeds = embedding_manager(input_ids, inputs_embeds)
|
|
283
|
+
|
|
284
|
+
position_embeddings = self.position_embedding(position_ids)
|
|
285
|
+
embeddings = inputs_embeds + position_embeddings
|
|
286
|
+
|
|
287
|
+
return embeddings
|
|
288
|
+
|
|
289
|
+
self.transformer.text_model.embeddings.forward = (
|
|
290
|
+
embedding_forward.__get__(self.transformer.text_model.embeddings)
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def encoder_forward(
|
|
294
|
+
self,
|
|
295
|
+
inputs_embeds,
|
|
296
|
+
attention_mask=None,
|
|
297
|
+
causal_attention_mask=None,
|
|
298
|
+
output_attentions=None,
|
|
299
|
+
output_hidden_states=None,
|
|
300
|
+
return_dict=None,
|
|
301
|
+
):
|
|
302
|
+
output_attentions = (
|
|
303
|
+
output_attentions
|
|
304
|
+
if output_attentions is not None
|
|
305
|
+
else self.config.output_attentions
|
|
306
|
+
)
|
|
307
|
+
output_hidden_states = (
|
|
308
|
+
output_hidden_states
|
|
309
|
+
if output_hidden_states is not None
|
|
310
|
+
else self.config.output_hidden_states
|
|
311
|
+
)
|
|
312
|
+
return_dict = (
|
|
313
|
+
return_dict
|
|
314
|
+
if return_dict is not None
|
|
315
|
+
else self.config.use_return_dict
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
encoder_states = () if output_hidden_states else None
|
|
319
|
+
all_attentions = () if output_attentions else None
|
|
320
|
+
|
|
321
|
+
hidden_states = inputs_embeds
|
|
322
|
+
for idx, encoder_layer in enumerate(self.layers):
|
|
323
|
+
if output_hidden_states:
|
|
324
|
+
encoder_states = encoder_states + (hidden_states,)
|
|
325
|
+
|
|
326
|
+
layer_outputs = encoder_layer(
|
|
327
|
+
hidden_states,
|
|
328
|
+
attention_mask,
|
|
329
|
+
causal_attention_mask,
|
|
330
|
+
output_attentions=output_attentions,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
hidden_states = layer_outputs[0]
|
|
334
|
+
|
|
335
|
+
if output_attentions:
|
|
336
|
+
all_attentions = all_attentions + (layer_outputs[1],)
|
|
337
|
+
|
|
338
|
+
if output_hidden_states:
|
|
339
|
+
encoder_states = encoder_states + (hidden_states,)
|
|
340
|
+
|
|
341
|
+
return hidden_states
|
|
342
|
+
|
|
343
|
+
self.transformer.text_model.encoder.forward = encoder_forward.__get__(
|
|
344
|
+
self.transformer.text_model.encoder
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
def text_encoder_forward(
|
|
348
|
+
self,
|
|
349
|
+
input_ids=None,
|
|
350
|
+
attention_mask=None,
|
|
351
|
+
position_ids=None,
|
|
352
|
+
output_attentions=None,
|
|
353
|
+
output_hidden_states=None,
|
|
354
|
+
return_dict=None,
|
|
355
|
+
embedding_manager=None,
|
|
356
|
+
):
|
|
357
|
+
output_attentions = (
|
|
358
|
+
output_attentions
|
|
359
|
+
if output_attentions is not None
|
|
360
|
+
else self.config.output_attentions
|
|
361
|
+
)
|
|
362
|
+
output_hidden_states = (
|
|
363
|
+
output_hidden_states
|
|
364
|
+
if output_hidden_states is not None
|
|
365
|
+
else self.config.output_hidden_states
|
|
366
|
+
)
|
|
367
|
+
return_dict = (
|
|
368
|
+
return_dict
|
|
369
|
+
if return_dict is not None
|
|
370
|
+
else self.config.use_return_dict
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if input_ids is None:
|
|
374
|
+
raise ValueError('You have to specify either input_ids')
|
|
375
|
+
|
|
376
|
+
input_shape = input_ids.size()
|
|
377
|
+
input_ids = input_ids.view(-1, input_shape[-1])
|
|
378
|
+
|
|
379
|
+
hidden_states = self.embeddings(
|
|
380
|
+
input_ids=input_ids,
|
|
381
|
+
position_ids=position_ids,
|
|
382
|
+
embedding_manager=embedding_manager,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
bsz, seq_len = input_shape
|
|
386
|
+
# CLIP's text model uses causal mask, prepare it here.
|
|
387
|
+
# https://github.com/openai/CLIP/blob/cfcffb90e69f37bf2ff1e988237a0fbe41f33c04/clip/model.py#L324
|
|
388
|
+
causal_attention_mask = _build_causal_attention_mask(
|
|
389
|
+
bsz, seq_len, hidden_states.dtype
|
|
390
|
+
).to(hidden_states.device)
|
|
391
|
+
|
|
392
|
+
# expand attention_mask
|
|
393
|
+
if attention_mask is not None:
|
|
394
|
+
# [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len]
|
|
395
|
+
attention_mask = _expand_mask(
|
|
396
|
+
attention_mask, hidden_states.dtype
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
last_hidden_state = self.encoder(
|
|
400
|
+
inputs_embeds=hidden_states,
|
|
401
|
+
attention_mask=attention_mask,
|
|
402
|
+
causal_attention_mask=causal_attention_mask,
|
|
403
|
+
output_attentions=output_attentions,
|
|
404
|
+
output_hidden_states=output_hidden_states,
|
|
405
|
+
return_dict=return_dict,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
last_hidden_state = self.final_layer_norm(last_hidden_state)
|
|
409
|
+
|
|
410
|
+
return last_hidden_state
|
|
411
|
+
|
|
412
|
+
self.transformer.text_model.forward = text_encoder_forward.__get__(
|
|
413
|
+
self.transformer.text_model
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
def transformer_forward(
|
|
417
|
+
self,
|
|
418
|
+
input_ids=None,
|
|
419
|
+
attention_mask=None,
|
|
420
|
+
position_ids=None,
|
|
421
|
+
output_attentions=None,
|
|
422
|
+
output_hidden_states=None,
|
|
423
|
+
return_dict=None,
|
|
424
|
+
embedding_manager=None,
|
|
425
|
+
):
|
|
426
|
+
return self.text_model(
|
|
427
|
+
input_ids=input_ids,
|
|
428
|
+
attention_mask=attention_mask,
|
|
429
|
+
position_ids=position_ids,
|
|
430
|
+
output_attentions=output_attentions,
|
|
431
|
+
output_hidden_states=output_hidden_states,
|
|
432
|
+
return_dict=return_dict,
|
|
433
|
+
embedding_manager=embedding_manager,
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
self.transformer.forward = transformer_forward.__get__(
|
|
437
|
+
self.transformer
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
def freeze(self):
|
|
441
|
+
self.transformer = self.transformer.eval()
|
|
442
|
+
for param in self.parameters():
|
|
443
|
+
param.requires_grad = False
|
|
444
|
+
|
|
445
|
+
def forward(self, text, **kwargs):
|
|
446
|
+
batch_encoding = self.tokenizer(
|
|
447
|
+
text,
|
|
448
|
+
truncation=True,
|
|
449
|
+
max_length=self.max_length,
|
|
450
|
+
return_length=True,
|
|
451
|
+
return_overflowing_tokens=False,
|
|
452
|
+
padding='max_length',
|
|
453
|
+
return_tensors='pt',
|
|
454
|
+
)
|
|
455
|
+
tokens = batch_encoding['input_ids'].to(self.device)
|
|
456
|
+
z = self.transformer(input_ids=tokens, **kwargs)
|
|
457
|
+
|
|
458
|
+
return z
|
|
459
|
+
|
|
460
|
+
def encode(self, text, **kwargs):
|
|
461
|
+
return self(text, **kwargs)
|
|
462
|
+
|
|
463
|
+
class WeightedFrozenCLIPEmbedder(FrozenCLIPEmbedder):
|
|
464
|
+
|
|
465
|
+
fragment_weights_key = "fragment_weights"
|
|
466
|
+
return_tokens_key = "return_tokens"
|
|
467
|
+
|
|
468
|
+
def forward(self, text: list, **kwargs):
|
|
469
|
+
'''
|
|
470
|
+
|
|
471
|
+
:param text: A batch of prompt strings, or, a batch of lists of fragments of prompt strings to which different
|
|
472
|
+
weights shall be applied.
|
|
473
|
+
:param kwargs: If the keyword arg "fragment_weights" is passed, it shall contain a batch of lists of weights
|
|
474
|
+
for the prompt fragments. In this case text must contain batches of lists of prompt fragments.
|
|
475
|
+
:return: A tensor of shape (B, 77, 768) containing weighted embeddings
|
|
476
|
+
'''
|
|
477
|
+
if self.fragment_weights_key not in kwargs:
|
|
478
|
+
# fallback to base class implementation
|
|
479
|
+
return super().forward(text, **kwargs)
|
|
480
|
+
|
|
481
|
+
fragment_weights = kwargs[self.fragment_weights_key]
|
|
482
|
+
# self.transformer doesn't like receiving "fragment_weights" as an argument
|
|
483
|
+
kwargs.pop(self.fragment_weights_key)
|
|
484
|
+
|
|
485
|
+
should_return_tokens = False
|
|
486
|
+
if self.return_tokens_key in kwargs:
|
|
487
|
+
should_return_tokens = kwargs.get(self.return_tokens_key, False)
|
|
488
|
+
# self.transformer doesn't like having extra kwargs
|
|
489
|
+
kwargs.pop(self.return_tokens_key)
|
|
490
|
+
|
|
491
|
+
batch_z = None
|
|
492
|
+
batch_tokens = None
|
|
493
|
+
for fragments, weights in zip(text, fragment_weights):
|
|
494
|
+
|
|
495
|
+
# First, weight tokens in individual fragments by scaling the feature vectors as requested (effectively
|
|
496
|
+
# applying a multiplier to the CFG scale on a per-token basis).
|
|
497
|
+
# For tokens weighted<1, intuitively we want SD to become not merely *less* interested in the concept
|
|
498
|
+
# captured by the fragment but actually *dis*interested in it (a 0.01 interest in "red" is still an active
|
|
499
|
+
# interest, however small, in redness; what the user probably intends when they attach the number 0.01 to
|
|
500
|
+
# "red" is to tell SD that it should almost completely *ignore* redness).
|
|
501
|
+
# To do this, the embedding is lerped away from base_embedding in the direction of an embedding for a prompt
|
|
502
|
+
# string from which the low-weighted fragment has been simply removed. The closer the weight is to zero, the
|
|
503
|
+
# closer the resulting embedding is to an embedding for a prompt that simply lacks this fragment.
|
|
504
|
+
|
|
505
|
+
# handle weights >=1
|
|
506
|
+
tokens, per_token_weights = self.get_tokens_and_weights(fragments, weights)
|
|
507
|
+
base_embedding = self.build_weighted_embedding_tensor(tokens, per_token_weights, **kwargs)
|
|
508
|
+
|
|
509
|
+
# this is our starting point
|
|
510
|
+
embeddings = base_embedding.unsqueeze(0)
|
|
511
|
+
per_embedding_weights = [1.0]
|
|
512
|
+
|
|
513
|
+
# now handle weights <1
|
|
514
|
+
# Do this by building extra embeddings tensors that lack the words being <1 weighted. These will be lerped
|
|
515
|
+
# with the embeddings tensors that have the words, such that if the weight of a word is 0.5, the resulting
|
|
516
|
+
# embedding will be exactly half-way between the unweighted prompt and the prompt with the <1 weighted words
|
|
517
|
+
# removed.
|
|
518
|
+
# eg for "mountain:1 man:0.5", intuitively the "man" should be "half-gone". therefore, append an embedding
|
|
519
|
+
# for "mountain" (i.e. without "man") to the already-produced embedding for "mountain man", and weight it
|
|
520
|
+
# such that the resulting lerped embedding is exactly half-way between "mountain man" and "mountain".
|
|
521
|
+
for index, fragment_weight in enumerate(weights):
|
|
522
|
+
if fragment_weight < 1:
|
|
523
|
+
fragments_without_this = fragments[:index] + fragments[index+1:]
|
|
524
|
+
weights_without_this = weights[:index] + weights[index+1:]
|
|
525
|
+
tokens, per_token_weights = self.get_tokens_and_weights(fragments_without_this, weights_without_this)
|
|
526
|
+
embedding_without_this = self.build_weighted_embedding_tensor(tokens, per_token_weights, **kwargs)
|
|
527
|
+
|
|
528
|
+
embeddings = torch.cat((embeddings, embedding_without_this.unsqueeze(0)), dim=1)
|
|
529
|
+
# weight of the embedding *without* this fragment gets *stronger* as its weight approaches 0
|
|
530
|
+
# if fragment_weight = 0, basically we want embedding_without_this to completely overwhelm base_embedding
|
|
531
|
+
# therefore:
|
|
532
|
+
# fragment_weight = 1: we are at base_z => lerp weight 0
|
|
533
|
+
# fragment_weight = 0.5: we are halfway between base_z and here => lerp weight 1
|
|
534
|
+
# fragment_weight = 0: we're now entirely overriding base_z ==> lerp weight inf
|
|
535
|
+
# so let's use tan(), because:
|
|
536
|
+
# tan is 0.0 at 0,
|
|
537
|
+
# 1.0 at PI/4, and
|
|
538
|
+
# inf at PI/2
|
|
539
|
+
# -> tan((1-weight)*PI/2) should give us ideal lerp weights
|
|
540
|
+
epsilon = 1e-9
|
|
541
|
+
fragment_weight = max(epsilon, fragment_weight) # inf is bad
|
|
542
|
+
embedding_lerp_weight = math.tan((1.0 - fragment_weight) * math.pi / 2)
|
|
543
|
+
# todo handle negative weight?
|
|
544
|
+
|
|
545
|
+
per_embedding_weights.append(embedding_lerp_weight)
|
|
546
|
+
|
|
547
|
+
lerped_embeddings = self.apply_embedding_weights(embeddings, per_embedding_weights, normalize=True).squeeze(0)
|
|
548
|
+
|
|
549
|
+
#print(f"assembled tokens for '{fragments}' into tensor of shape {lerped_embeddings.shape}")
|
|
550
|
+
|
|
551
|
+
# append to batch
|
|
552
|
+
batch_z = lerped_embeddings.unsqueeze(0) if batch_z is None else torch.cat([batch_z, lerped_embeddings.unsqueeze(0)], dim=1)
|
|
553
|
+
batch_tokens = tokens.unsqueeze(0) if batch_tokens is None else torch.cat([batch_tokens, tokens.unsqueeze(0)], dim=1)
|
|
554
|
+
|
|
555
|
+
# should have shape (B, 77, 768)
|
|
556
|
+
#print(f"assembled all tokens into tensor of shape {batch_z.shape}")
|
|
557
|
+
|
|
558
|
+
if should_return_tokens:
|
|
559
|
+
return batch_z, batch_tokens
|
|
560
|
+
else:
|
|
561
|
+
return batch_z
|
|
562
|
+
|
|
563
|
+
def get_tokens(self, fragments: list[str], include_start_and_end_markers: bool = True) -> list[list[int]]:
|
|
564
|
+
tokens = self.tokenizer(
|
|
565
|
+
fragments,
|
|
566
|
+
truncation=True,
|
|
567
|
+
max_length=self.max_length,
|
|
568
|
+
return_overflowing_tokens=False,
|
|
569
|
+
padding='do_not_pad',
|
|
570
|
+
return_tensors=None, # just give me a list of ints
|
|
571
|
+
)['input_ids']
|
|
572
|
+
if include_start_and_end_markers:
|
|
573
|
+
return tokens
|
|
574
|
+
else:
|
|
575
|
+
return [x[1:-1] for x in tokens]
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
@classmethod
|
|
579
|
+
def apply_embedding_weights(self, embeddings: torch.Tensor, per_embedding_weights: list[float], normalize:bool) -> torch.Tensor:
|
|
580
|
+
per_embedding_weights = torch.tensor(per_embedding_weights, dtype=embeddings.dtype, device=embeddings.device)
|
|
581
|
+
if normalize:
|
|
582
|
+
per_embedding_weights = per_embedding_weights / torch.sum(per_embedding_weights)
|
|
583
|
+
reshaped_weights = per_embedding_weights.reshape(per_embedding_weights.shape + (1, 1,))
|
|
584
|
+
#reshaped_weights = per_embedding_weights.reshape(per_embedding_weights.shape + (1,1,)).expand(embeddings.shape)
|
|
585
|
+
return torch.sum(embeddings * reshaped_weights, dim=1)
|
|
586
|
+
# lerped embeddings has shape (77, 768)
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def get_tokens_and_weights(self, fragments: list[str], weights: list[float]) -> (torch.Tensor, torch.Tensor):
|
|
590
|
+
'''
|
|
591
|
+
|
|
592
|
+
:param fragments:
|
|
593
|
+
:param weights: Per-fragment weights (CFG scaling). No need for these to be normalized. They will not be normalized here and that's fine.
|
|
594
|
+
:return:
|
|
595
|
+
'''
|
|
596
|
+
# empty is meaningful
|
|
597
|
+
if len(fragments) == 0 and len(weights) == 0:
|
|
598
|
+
fragments = ['']
|
|
599
|
+
weights = [1]
|
|
600
|
+
item_encodings = self.tokenizer(
|
|
601
|
+
fragments,
|
|
602
|
+
truncation=True,
|
|
603
|
+
max_length=self.max_length,
|
|
604
|
+
return_overflowing_tokens=True,
|
|
605
|
+
padding='do_not_pad',
|
|
606
|
+
return_tensors=None, # just give me a list of ints
|
|
607
|
+
)['input_ids']
|
|
608
|
+
all_tokens = []
|
|
609
|
+
per_token_weights = []
|
|
610
|
+
#print("all fragments:", fragments, weights)
|
|
611
|
+
for index, fragment in enumerate(item_encodings):
|
|
612
|
+
weight = weights[index]
|
|
613
|
+
#print("processing fragment", fragment, weight)
|
|
614
|
+
fragment_tokens = item_encodings[index]
|
|
615
|
+
#print("fragment", fragment, "processed to", fragment_tokens)
|
|
616
|
+
# trim bos and eos markers before appending
|
|
617
|
+
all_tokens.extend(fragment_tokens[1:-1])
|
|
618
|
+
per_token_weights.extend([weight] * (len(fragment_tokens) - 2))
|
|
619
|
+
|
|
620
|
+
if (len(all_tokens) + 2) > self.max_length:
|
|
621
|
+
excess_token_count = (len(all_tokens) + 2) - self.max_length
|
|
622
|
+
print(f">> Prompt is {excess_token_count} token(s) too long and has been truncated")
|
|
623
|
+
all_tokens = all_tokens[:self.max_length - 2]
|
|
624
|
+
per_token_weights = per_token_weights[:self.max_length - 2]
|
|
625
|
+
|
|
626
|
+
# pad out to a 77-entry array: [eos_token, <prompt tokens>, eos_token, ..., eos_token]
|
|
627
|
+
# (77 = self.max_length)
|
|
628
|
+
pad_length = self.max_length - 1 - len(all_tokens)
|
|
629
|
+
all_tokens.insert(0, self.tokenizer.bos_token_id)
|
|
630
|
+
all_tokens.extend([self.tokenizer.eos_token_id] * pad_length)
|
|
631
|
+
per_token_weights.insert(0, 1)
|
|
632
|
+
per_token_weights.extend([1] * pad_length)
|
|
633
|
+
|
|
634
|
+
all_tokens_tensor = torch.tensor(all_tokens, dtype=torch.long).to(self.device)
|
|
635
|
+
per_token_weights_tensor = torch.tensor(per_token_weights, dtype=torch.float32).to(self.device)
|
|
636
|
+
#print(f"assembled all_tokens_tensor with shape {all_tokens_tensor.shape}")
|
|
637
|
+
return all_tokens_tensor, per_token_weights_tensor
|
|
638
|
+
|
|
639
|
+
def build_weighted_embedding_tensor(self, tokens: torch.Tensor, per_token_weights: torch.Tensor, weight_delta_from_empty=True, **kwargs) -> torch.Tensor:
|
|
640
|
+
'''
|
|
641
|
+
Build a tensor representing the passed-in tokens, each of which has a weight.
|
|
642
|
+
:param tokens: A tensor of shape (77) containing token ids (integers)
|
|
643
|
+
:param per_token_weights: A tensor of shape (77) containing weights (floats)
|
|
644
|
+
:param method: Whether to multiply the whole feature vector for each token or just its distance from an "empty" feature vector
|
|
645
|
+
:param kwargs: passed on to self.transformer()
|
|
646
|
+
:return: A tensor of shape (1, 77, 768) representing the requested weighted embeddings.
|
|
647
|
+
'''
|
|
648
|
+
#print(f"building weighted embedding tensor for {tokens} with weights {per_token_weights}")
|
|
649
|
+
z = self.transformer(input_ids=tokens.unsqueeze(0), **kwargs)
|
|
650
|
+
batch_weights_expanded = per_token_weights.reshape(per_token_weights.shape + (1,)).expand(z.shape)
|
|
651
|
+
|
|
652
|
+
if weight_delta_from_empty:
|
|
653
|
+
empty_tokens = self.tokenizer([''] * z.shape[0],
|
|
654
|
+
truncation=True,
|
|
655
|
+
max_length=self.max_length,
|
|
656
|
+
padding='max_length',
|
|
657
|
+
return_tensors='pt'
|
|
658
|
+
)['input_ids'].to(self.device)
|
|
659
|
+
empty_z = self.transformer(input_ids=empty_tokens, **kwargs)
|
|
660
|
+
z_delta_from_empty = z - empty_z
|
|
661
|
+
weighted_z = empty_z + (z_delta_from_empty * batch_weights_expanded)
|
|
662
|
+
|
|
663
|
+
weighted_z_delta_from_empty = (weighted_z-empty_z)
|
|
664
|
+
#print("weighted z has delta from empty with sum", weighted_z_delta_from_empty.sum().item(), "mean", weighted_z_delta_from_empty.mean().item() )
|
|
665
|
+
|
|
666
|
+
#print("using empty-delta method, first 5 rows:")
|
|
667
|
+
#print(weighted_z[:5])
|
|
668
|
+
|
|
669
|
+
return weighted_z
|
|
670
|
+
|
|
671
|
+
else:
|
|
672
|
+
original_mean = z.mean()
|
|
673
|
+
z *= batch_weights_expanded
|
|
674
|
+
after_weighting_mean = z.mean()
|
|
675
|
+
# correct the mean. not sure if this is right but it's what the automatic1111 fork of SD does
|
|
676
|
+
mean_correction_factor = original_mean/after_weighting_mean
|
|
677
|
+
z *= mean_correction_factor
|
|
678
|
+
return z
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
class FrozenCLIPTextEmbedder(nn.Module):
|
|
682
|
+
"""
|
|
683
|
+
Uses the CLIP transformer encoder for text.
|
|
684
|
+
"""
|
|
685
|
+
|
|
686
|
+
def __init__(
|
|
687
|
+
self,
|
|
688
|
+
version='ViT-L/14',
|
|
689
|
+
device=choose_torch_device(),
|
|
690
|
+
max_length=77,
|
|
691
|
+
n_repeat=1,
|
|
692
|
+
normalize=True,
|
|
693
|
+
):
|
|
694
|
+
super().__init__()
|
|
695
|
+
self.model, _ = clip.load(version, jit=False, device=device)
|
|
696
|
+
self.device = device
|
|
697
|
+
self.max_length = max_length
|
|
698
|
+
self.n_repeat = n_repeat
|
|
699
|
+
self.normalize = normalize
|
|
700
|
+
|
|
701
|
+
def freeze(self):
|
|
702
|
+
self.model = self.model.eval()
|
|
703
|
+
for param in self.parameters():
|
|
704
|
+
param.requires_grad = False
|
|
705
|
+
|
|
706
|
+
def forward(self, text):
|
|
707
|
+
tokens = clip.tokenize(text).to(self.device)
|
|
708
|
+
z = self.model.encode_text(tokens)
|
|
709
|
+
if self.normalize:
|
|
710
|
+
z = z / torch.linalg.norm(z, dim=1, keepdim=True)
|
|
711
|
+
return z
|
|
712
|
+
|
|
713
|
+
def encode(self, text):
|
|
714
|
+
z = self(text)
|
|
715
|
+
if z.ndim == 2:
|
|
716
|
+
z = z[:, None, :]
|
|
717
|
+
z = repeat(z, 'b 1 d -> b k d', k=self.n_repeat)
|
|
718
|
+
return z
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
class FrozenClipImageEmbedder(nn.Module):
|
|
722
|
+
"""
|
|
723
|
+
Uses the CLIP image encoder.
|
|
724
|
+
"""
|
|
725
|
+
|
|
726
|
+
def __init__(
|
|
727
|
+
self,
|
|
728
|
+
model,
|
|
729
|
+
jit=False,
|
|
730
|
+
device=choose_torch_device(),
|
|
731
|
+
antialias=False,
|
|
732
|
+
):
|
|
733
|
+
super().__init__()
|
|
734
|
+
self.model, _ = clip.load(name=model, device=device, jit=jit)
|
|
735
|
+
|
|
736
|
+
self.antialias = antialias
|
|
737
|
+
|
|
738
|
+
self.register_buffer(
|
|
739
|
+
'mean',
|
|
740
|
+
torch.Tensor([0.48145466, 0.4578275, 0.40821073]),
|
|
741
|
+
persistent=False,
|
|
742
|
+
)
|
|
743
|
+
self.register_buffer(
|
|
744
|
+
'std',
|
|
745
|
+
torch.Tensor([0.26862954, 0.26130258, 0.27577711]),
|
|
746
|
+
persistent=False,
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
def preprocess(self, x):
|
|
750
|
+
# normalize to [0,1]
|
|
751
|
+
x = kornia.geometry.resize(
|
|
752
|
+
x,
|
|
753
|
+
(224, 224),
|
|
754
|
+
interpolation='bicubic',
|
|
755
|
+
align_corners=True,
|
|
756
|
+
antialias=self.antialias,
|
|
757
|
+
)
|
|
758
|
+
x = (x + 1.0) / 2.0
|
|
759
|
+
# renormalize according to clip
|
|
760
|
+
x = kornia.enhance.normalize(x, self.mean, self.std)
|
|
761
|
+
return x
|
|
762
|
+
|
|
763
|
+
def forward(self, x):
|
|
764
|
+
# x is assumed to be in range [-1,1]
|
|
765
|
+
return self.model.encode_image(self.preprocess(x))
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
if __name__ == '__main__':
|
|
769
|
+
from ldm.util import count_params
|
|
770
|
+
|
|
771
|
+
model = FrozenCLIPEmbedder()
|
|
772
|
+
count_params(model, verbose=True)
|