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,237 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import os
|
|
3
|
+
import urllib
|
|
4
|
+
import warnings
|
|
5
|
+
from typing import Any, Union, List
|
|
6
|
+
from pkg_resources import packaging
|
|
7
|
+
|
|
8
|
+
import torch
|
|
9
|
+
from PIL import Image
|
|
10
|
+
from torchvision.transforms import Compose, Resize, CenterCrop, ToTensor, Normalize
|
|
11
|
+
from tqdm import tqdm
|
|
12
|
+
|
|
13
|
+
from .model import build_model
|
|
14
|
+
from .simple_tokenizer import SimpleTokenizer as _Tokenizer
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
from torchvision.transforms import InterpolationMode
|
|
18
|
+
BICUBIC = InterpolationMode.BICUBIC
|
|
19
|
+
except ImportError:
|
|
20
|
+
BICUBIC = Image.BICUBIC
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if packaging.version.parse(torch.__version__) < packaging.version.parse("1.7.1"):
|
|
24
|
+
warnings.warn("PyTorch version 1.7.1 or higher is recommended")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
__all__ = ["available_models", "load", "tokenize"]
|
|
28
|
+
_tokenizer = _Tokenizer()
|
|
29
|
+
|
|
30
|
+
_MODELS = {
|
|
31
|
+
"RN50": "https://openaipublic.azureedge.net/clip/models/afeb0e10f9e5a86da6080e35cf09123aca3b358a0c3e3b6c78a7b63bc04b6762/RN50.pt",
|
|
32
|
+
"RN101": "https://openaipublic.azureedge.net/clip/models/8fa8567bab74a42d41c5915025a8e4538c3bdbe8804a470a72f30b0d94fab599/RN101.pt",
|
|
33
|
+
"RN50x4": "https://openaipublic.azureedge.net/clip/models/7e526bd135e493cef0776de27d5f42653e6b4c8bf9e0f653bb11773263205fdd/RN50x4.pt",
|
|
34
|
+
"RN50x16": "https://openaipublic.azureedge.net/clip/models/52378b407f34354e150460fe41077663dd5b39c54cd0bfd2b27167a4a06ec9aa/RN50x16.pt",
|
|
35
|
+
"RN50x64": "https://openaipublic.azureedge.net/clip/models/be1cfb55d75a9666199fb2206c106743da0f6468c9d327f3e0d0a543a9919d9c/RN50x64.pt",
|
|
36
|
+
"ViT-B/32": "https://openaipublic.azureedge.net/clip/models/40d365715913c9da98579312b702a82c18be219cc2a73407c4526f58eba950af/ViT-B-32.pt",
|
|
37
|
+
"ViT-B/16": "https://openaipublic.azureedge.net/clip/models/5806e77cd80f8b59890b7e101eabd078d9fb84e6937f9e85e4ecb61988df416f/ViT-B-16.pt",
|
|
38
|
+
"ViT-L/14": "https://openaipublic.azureedge.net/clip/models/b8cca3fd41ae0c99ba7e8951adf17d267cdb84cd88be6f7c2e0eca1737a03836/ViT-L-14.pt",
|
|
39
|
+
"ViT-L/14@336px": "https://openaipublic.azureedge.net/clip/models/3035c92b350959924f9f00213499208652fc7ea050643e8b385c2dac08641f02/ViT-L-14-336px.pt",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _download(url: str, root: str):
|
|
44
|
+
os.makedirs(root, exist_ok=True)
|
|
45
|
+
filename = os.path.basename(url)
|
|
46
|
+
|
|
47
|
+
expected_sha256 = url.split("/")[-2]
|
|
48
|
+
download_target = os.path.join(root, filename)
|
|
49
|
+
|
|
50
|
+
if os.path.exists(download_target) and not os.path.isfile(download_target):
|
|
51
|
+
raise RuntimeError(f"{download_target} exists and is not a regular file")
|
|
52
|
+
|
|
53
|
+
if os.path.isfile(download_target):
|
|
54
|
+
if hashlib.sha256(open(download_target, "rb").read()).hexdigest() == expected_sha256:
|
|
55
|
+
return download_target
|
|
56
|
+
else:
|
|
57
|
+
warnings.warn(f"{download_target} exists, but the SHA256 checksum does not match; re-downloading the file")
|
|
58
|
+
|
|
59
|
+
with urllib.request.urlopen(url) as source, open(download_target, "wb") as output:
|
|
60
|
+
with tqdm(total=int(source.info().get("Content-Length")), ncols=80, unit='iB', unit_scale=True, unit_divisor=1024) as loop:
|
|
61
|
+
while True:
|
|
62
|
+
buffer = source.read(8192)
|
|
63
|
+
if not buffer:
|
|
64
|
+
break
|
|
65
|
+
|
|
66
|
+
output.write(buffer)
|
|
67
|
+
loop.update(len(buffer))
|
|
68
|
+
|
|
69
|
+
if hashlib.sha256(open(download_target, "rb").read()).hexdigest() != expected_sha256:
|
|
70
|
+
raise RuntimeError("Model has been downloaded but the SHA256 checksum does not not match")
|
|
71
|
+
|
|
72
|
+
return download_target
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _convert_image_to_rgb(image):
|
|
76
|
+
return image.convert("RGB")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _transform(n_px):
|
|
80
|
+
return Compose([
|
|
81
|
+
Resize(n_px, interpolation=BICUBIC),
|
|
82
|
+
CenterCrop(n_px),
|
|
83
|
+
_convert_image_to_rgb,
|
|
84
|
+
ToTensor(),
|
|
85
|
+
Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711)),
|
|
86
|
+
])
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def available_models() -> List[str]:
|
|
90
|
+
"""Returns the names of available CLIP models"""
|
|
91
|
+
return list(_MODELS.keys())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def load(name: str, device: Union[str, torch.device] = "cuda" if torch.cuda.is_available() else "cpu", jit: bool = False, download_root: str = None):
|
|
95
|
+
"""Load a CLIP model
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
name : str
|
|
100
|
+
A model name listed by `clip.available_models()`, or the path to a model checkpoint containing the state_dict
|
|
101
|
+
|
|
102
|
+
device : Union[str, torch.device]
|
|
103
|
+
The device to put the loaded model
|
|
104
|
+
|
|
105
|
+
jit : bool
|
|
106
|
+
Whether to load the optimized JIT model or more hackable non-JIT model (default).
|
|
107
|
+
|
|
108
|
+
download_root: str
|
|
109
|
+
path to download the model files; by default, it uses "~/.cache/clip"
|
|
110
|
+
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
model : torch.nn.Module
|
|
114
|
+
The CLIP model
|
|
115
|
+
|
|
116
|
+
preprocess : Callable[[PIL.Image], torch.Tensor]
|
|
117
|
+
A torchvision transform that converts a PIL image into a tensor that the returned model can take as its input
|
|
118
|
+
"""
|
|
119
|
+
if name in _MODELS:
|
|
120
|
+
model_path = _download(_MODELS[name], download_root or os.path.expanduser("~/.cache/clip"))
|
|
121
|
+
elif os.path.isfile(name):
|
|
122
|
+
model_path = name
|
|
123
|
+
else:
|
|
124
|
+
raise RuntimeError(f"Model {name} not found; available models = {available_models()}")
|
|
125
|
+
|
|
126
|
+
with open(model_path, 'rb') as opened_file:
|
|
127
|
+
try:
|
|
128
|
+
# loading JIT archive
|
|
129
|
+
model = torch.jit.load(opened_file, map_location=device if jit else "cpu").eval()
|
|
130
|
+
state_dict = None
|
|
131
|
+
except RuntimeError:
|
|
132
|
+
# loading saved state dict
|
|
133
|
+
if jit:
|
|
134
|
+
warnings.warn(f"File {model_path} is not a JIT archive. Loading as a state dict instead")
|
|
135
|
+
jit = False
|
|
136
|
+
state_dict = torch.load(opened_file, map_location="cpu")
|
|
137
|
+
|
|
138
|
+
if not jit:
|
|
139
|
+
model = build_model(state_dict or model.state_dict()).to(device)
|
|
140
|
+
if str(device) == "cpu":
|
|
141
|
+
model.float()
|
|
142
|
+
return model, _transform(model.visual.input_resolution)
|
|
143
|
+
|
|
144
|
+
# patch the device names
|
|
145
|
+
device_holder = torch.jit.trace(lambda: torch.ones([]).to(torch.device(device)), example_inputs=[])
|
|
146
|
+
device_node = [n for n in device_holder.graph.findAllNodes("prim::Constant") if "Device" in repr(n)][-1]
|
|
147
|
+
|
|
148
|
+
def patch_device(module):
|
|
149
|
+
try:
|
|
150
|
+
graphs = [module.graph] if hasattr(module, "graph") else []
|
|
151
|
+
except RuntimeError:
|
|
152
|
+
graphs = []
|
|
153
|
+
|
|
154
|
+
if hasattr(module, "forward1"):
|
|
155
|
+
graphs.append(module.forward1.graph)
|
|
156
|
+
|
|
157
|
+
for graph in graphs:
|
|
158
|
+
for node in graph.findAllNodes("prim::Constant"):
|
|
159
|
+
if "value" in node.attributeNames() and str(node["value"]).startswith("cuda"):
|
|
160
|
+
node.copyAttributes(device_node)
|
|
161
|
+
|
|
162
|
+
model.apply(patch_device)
|
|
163
|
+
patch_device(model.encode_image)
|
|
164
|
+
patch_device(model.encode_text)
|
|
165
|
+
|
|
166
|
+
# patch dtype to float32 on CPU
|
|
167
|
+
if str(device) == "cpu":
|
|
168
|
+
float_holder = torch.jit.trace(lambda: torch.ones([]).float(), example_inputs=[])
|
|
169
|
+
float_input = list(float_holder.graph.findNode("aten::to").inputs())[1]
|
|
170
|
+
float_node = float_input.node()
|
|
171
|
+
|
|
172
|
+
def patch_float(module):
|
|
173
|
+
try:
|
|
174
|
+
graphs = [module.graph] if hasattr(module, "graph") else []
|
|
175
|
+
except RuntimeError:
|
|
176
|
+
graphs = []
|
|
177
|
+
|
|
178
|
+
if hasattr(module, "forward1"):
|
|
179
|
+
graphs.append(module.forward1.graph)
|
|
180
|
+
|
|
181
|
+
for graph in graphs:
|
|
182
|
+
for node in graph.findAllNodes("aten::to"):
|
|
183
|
+
inputs = list(node.inputs())
|
|
184
|
+
for i in [1, 2]: # dtype can be the second or third argument to aten::to()
|
|
185
|
+
if inputs[i].node()["value"] == 5:
|
|
186
|
+
inputs[i].node().copyAttributes(float_node)
|
|
187
|
+
|
|
188
|
+
model.apply(patch_float)
|
|
189
|
+
patch_float(model.encode_image)
|
|
190
|
+
patch_float(model.encode_text)
|
|
191
|
+
|
|
192
|
+
model.float()
|
|
193
|
+
|
|
194
|
+
return model, _transform(model.input_resolution.item())
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def tokenize(texts: Union[str, List[str]], context_length: int = 77, truncate: bool = False) -> Union[torch.IntTensor, torch.LongTensor]:
|
|
198
|
+
"""
|
|
199
|
+
Returns the tokenized representation of given input string(s)
|
|
200
|
+
|
|
201
|
+
Parameters
|
|
202
|
+
----------
|
|
203
|
+
texts : Union[str, List[str]]
|
|
204
|
+
An input string or a list of input strings to tokenize
|
|
205
|
+
|
|
206
|
+
context_length : int
|
|
207
|
+
The context length to use; all CLIP models use 77 as the context length
|
|
208
|
+
|
|
209
|
+
truncate: bool
|
|
210
|
+
Whether to truncate the text in case its encoding is longer than the context length
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
A two-dimensional tensor containing the resulting tokens, shape = [number of input strings, context_length].
|
|
215
|
+
We return LongTensor when torch version is <1.8.0, since older index_select requires indices to be long.
|
|
216
|
+
"""
|
|
217
|
+
if isinstance(texts, str):
|
|
218
|
+
texts = [texts]
|
|
219
|
+
|
|
220
|
+
sot_token = _tokenizer.encoder["<|startoftext|>"]
|
|
221
|
+
eot_token = _tokenizer.encoder["<|endoftext|>"]
|
|
222
|
+
all_tokens = [[sot_token] + _tokenizer.encode(text) + [eot_token] for text in texts]
|
|
223
|
+
if packaging.version.parse(torch.__version__) < packaging.version.parse("1.8.0"):
|
|
224
|
+
result = torch.zeros(len(all_tokens), context_length, dtype=torch.long)
|
|
225
|
+
else:
|
|
226
|
+
result = torch.zeros(len(all_tokens), context_length, dtype=torch.int)
|
|
227
|
+
|
|
228
|
+
for i, tokens in enumerate(all_tokens):
|
|
229
|
+
if len(tokens) > context_length:
|
|
230
|
+
if truncate:
|
|
231
|
+
tokens = tokens[:context_length]
|
|
232
|
+
tokens[-1] = eot_token
|
|
233
|
+
else:
|
|
234
|
+
raise RuntimeError(f"Input {texts[i]} is too long for context length {context_length}")
|
|
235
|
+
result[i, :len(tokens)] = torch.tensor(tokens)
|
|
236
|
+
|
|
237
|
+
return result
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
from typing import Tuple, Union
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import torch
|
|
6
|
+
import torch.nn.functional as F
|
|
7
|
+
from torch import nn
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Bottleneck(nn.Module):
|
|
11
|
+
expansion = 4
|
|
12
|
+
|
|
13
|
+
def __init__(self, inplanes, planes, stride=1):
|
|
14
|
+
super().__init__()
|
|
15
|
+
|
|
16
|
+
# all conv layers have stride 1. an avgpool is performed after the second convolution when stride > 1
|
|
17
|
+
self.conv1 = nn.Conv2d(inplanes, planes, 1, bias=False)
|
|
18
|
+
self.bn1 = nn.BatchNorm2d(planes)
|
|
19
|
+
self.relu1 = nn.ReLU(inplace=True)
|
|
20
|
+
|
|
21
|
+
self.conv2 = nn.Conv2d(planes, planes, 3, padding=1, bias=False)
|
|
22
|
+
self.bn2 = nn.BatchNorm2d(planes)
|
|
23
|
+
self.relu2 = nn.ReLU(inplace=True)
|
|
24
|
+
|
|
25
|
+
self.avgpool = nn.AvgPool2d(stride) if stride > 1 else nn.Identity()
|
|
26
|
+
|
|
27
|
+
self.conv3 = nn.Conv2d(planes, planes * self.expansion, 1, bias=False)
|
|
28
|
+
self.bn3 = nn.BatchNorm2d(planes * self.expansion)
|
|
29
|
+
self.relu3 = nn.ReLU(inplace=True)
|
|
30
|
+
|
|
31
|
+
self.downsample = None
|
|
32
|
+
self.stride = stride
|
|
33
|
+
|
|
34
|
+
if stride > 1 or inplanes != planes * Bottleneck.expansion:
|
|
35
|
+
# downsampling layer is prepended with an avgpool, and the subsequent convolution has stride 1
|
|
36
|
+
self.downsample = nn.Sequential(OrderedDict([
|
|
37
|
+
("-1", nn.AvgPool2d(stride)),
|
|
38
|
+
("0", nn.Conv2d(inplanes, planes * self.expansion, 1, stride=1, bias=False)),
|
|
39
|
+
("1", nn.BatchNorm2d(planes * self.expansion))
|
|
40
|
+
]))
|
|
41
|
+
|
|
42
|
+
def forward(self, x: torch.Tensor):
|
|
43
|
+
identity = x
|
|
44
|
+
|
|
45
|
+
out = self.relu1(self.bn1(self.conv1(x)))
|
|
46
|
+
out = self.relu2(self.bn2(self.conv2(out)))
|
|
47
|
+
out = self.avgpool(out)
|
|
48
|
+
out = self.bn3(self.conv3(out))
|
|
49
|
+
|
|
50
|
+
if self.downsample is not None:
|
|
51
|
+
identity = self.downsample(x)
|
|
52
|
+
|
|
53
|
+
out += identity
|
|
54
|
+
out = self.relu3(out)
|
|
55
|
+
return out
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class AttentionPool2d(nn.Module):
|
|
59
|
+
def __init__(self, spacial_dim: int, embed_dim: int, num_heads: int, output_dim: int = None):
|
|
60
|
+
super().__init__()
|
|
61
|
+
self.positional_embedding = nn.Parameter(torch.randn(spacial_dim ** 2 + 1, embed_dim) / embed_dim ** 0.5)
|
|
62
|
+
self.k_proj = nn.Linear(embed_dim, embed_dim)
|
|
63
|
+
self.q_proj = nn.Linear(embed_dim, embed_dim)
|
|
64
|
+
self.v_proj = nn.Linear(embed_dim, embed_dim)
|
|
65
|
+
self.c_proj = nn.Linear(embed_dim, output_dim or embed_dim)
|
|
66
|
+
self.num_heads = num_heads
|
|
67
|
+
|
|
68
|
+
def forward(self, x):
|
|
69
|
+
x = x.flatten(start_dim=2).permute(2, 0, 1) # NCHW -> (HW)NC
|
|
70
|
+
x = torch.cat([x.mean(dim=0, keepdim=True), x], dim=0) # (HW+1)NC
|
|
71
|
+
x = x + self.positional_embedding[:, None, :].to(x.dtype) # (HW+1)NC
|
|
72
|
+
x, _ = F.multi_head_attention_forward(
|
|
73
|
+
query=x[:1], key=x, value=x,
|
|
74
|
+
embed_dim_to_check=x.shape[-1],
|
|
75
|
+
num_heads=self.num_heads,
|
|
76
|
+
q_proj_weight=self.q_proj.weight,
|
|
77
|
+
k_proj_weight=self.k_proj.weight,
|
|
78
|
+
v_proj_weight=self.v_proj.weight,
|
|
79
|
+
in_proj_weight=None,
|
|
80
|
+
in_proj_bias=torch.cat([self.q_proj.bias, self.k_proj.bias, self.v_proj.bias]),
|
|
81
|
+
bias_k=None,
|
|
82
|
+
bias_v=None,
|
|
83
|
+
add_zero_attn=False,
|
|
84
|
+
dropout_p=0,
|
|
85
|
+
out_proj_weight=self.c_proj.weight,
|
|
86
|
+
out_proj_bias=self.c_proj.bias,
|
|
87
|
+
use_separate_proj_weight=True,
|
|
88
|
+
training=self.training,
|
|
89
|
+
need_weights=False
|
|
90
|
+
)
|
|
91
|
+
return x.squeeze(0)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class ModifiedResNet(nn.Module):
|
|
95
|
+
"""
|
|
96
|
+
A ResNet class that is similar to torchvision's but contains the following changes:
|
|
97
|
+
- There are now 3 "stem" convolutions as opposed to 1, with an average pool instead of a max pool.
|
|
98
|
+
- Performs anti-aliasing strided convolutions, where an avgpool is prepended to convolutions with stride > 1
|
|
99
|
+
- The final pooling layer is a QKV attention instead of an average pool
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def __init__(self, layers, output_dim, heads, input_resolution=224, width=64):
|
|
103
|
+
super().__init__()
|
|
104
|
+
self.output_dim = output_dim
|
|
105
|
+
self.input_resolution = input_resolution
|
|
106
|
+
|
|
107
|
+
# the 3-layer stem
|
|
108
|
+
self.conv1 = nn.Conv2d(3, width // 2, kernel_size=3, stride=2, padding=1, bias=False)
|
|
109
|
+
self.bn1 = nn.BatchNorm2d(width // 2)
|
|
110
|
+
self.relu1 = nn.ReLU(inplace=True)
|
|
111
|
+
self.conv2 = nn.Conv2d(width // 2, width // 2, kernel_size=3, padding=1, bias=False)
|
|
112
|
+
self.bn2 = nn.BatchNorm2d(width // 2)
|
|
113
|
+
self.relu2 = nn.ReLU(inplace=True)
|
|
114
|
+
self.conv3 = nn.Conv2d(width // 2, width, kernel_size=3, padding=1, bias=False)
|
|
115
|
+
self.bn3 = nn.BatchNorm2d(width)
|
|
116
|
+
self.relu3 = nn.ReLU(inplace=True)
|
|
117
|
+
self.avgpool = nn.AvgPool2d(2)
|
|
118
|
+
|
|
119
|
+
# residual layers
|
|
120
|
+
self._inplanes = width # this is a *mutable* variable used during construction
|
|
121
|
+
self.layer1 = self._make_layer(width, layers[0])
|
|
122
|
+
self.layer2 = self._make_layer(width * 2, layers[1], stride=2)
|
|
123
|
+
self.layer3 = self._make_layer(width * 4, layers[2], stride=2)
|
|
124
|
+
self.layer4 = self._make_layer(width * 8, layers[3], stride=2)
|
|
125
|
+
|
|
126
|
+
embed_dim = width * 32 # the ResNet feature dimension
|
|
127
|
+
self.attnpool = AttentionPool2d(input_resolution // 32, embed_dim, heads, output_dim)
|
|
128
|
+
|
|
129
|
+
def _make_layer(self, planes, blocks, stride=1):
|
|
130
|
+
layers = [Bottleneck(self._inplanes, planes, stride)]
|
|
131
|
+
|
|
132
|
+
self._inplanes = planes * Bottleneck.expansion
|
|
133
|
+
for _ in range(1, blocks):
|
|
134
|
+
layers.append(Bottleneck(self._inplanes, planes))
|
|
135
|
+
|
|
136
|
+
return nn.Sequential(*layers)
|
|
137
|
+
|
|
138
|
+
def forward(self, x):
|
|
139
|
+
def stem(x):
|
|
140
|
+
x = self.relu1(self.bn1(self.conv1(x)))
|
|
141
|
+
x = self.relu2(self.bn2(self.conv2(x)))
|
|
142
|
+
x = self.relu3(self.bn3(self.conv3(x)))
|
|
143
|
+
x = self.avgpool(x)
|
|
144
|
+
return x
|
|
145
|
+
|
|
146
|
+
x = x.type(self.conv1.weight.dtype)
|
|
147
|
+
x = stem(x)
|
|
148
|
+
x = self.layer1(x)
|
|
149
|
+
x = self.layer2(x)
|
|
150
|
+
x = self.layer3(x)
|
|
151
|
+
x = self.layer4(x)
|
|
152
|
+
x = self.attnpool(x)
|
|
153
|
+
|
|
154
|
+
return x
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class LayerNorm(nn.LayerNorm):
|
|
158
|
+
"""Subclass torch's LayerNorm to handle fp16."""
|
|
159
|
+
|
|
160
|
+
def forward(self, x: torch.Tensor):
|
|
161
|
+
orig_type = x.dtype
|
|
162
|
+
ret = super().forward(x.type(torch.float32))
|
|
163
|
+
return ret.type(orig_type)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class QuickGELU(nn.Module):
|
|
167
|
+
def forward(self, x: torch.Tensor):
|
|
168
|
+
return x * torch.sigmoid(1.702 * x)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class ResidualAttentionBlock(nn.Module):
|
|
172
|
+
def __init__(self, d_model: int, n_head: int, attn_mask: torch.Tensor = None):
|
|
173
|
+
super().__init__()
|
|
174
|
+
|
|
175
|
+
self.attn = nn.MultiheadAttention(d_model, n_head)
|
|
176
|
+
self.ln_1 = LayerNorm(d_model)
|
|
177
|
+
self.mlp = nn.Sequential(OrderedDict([
|
|
178
|
+
("c_fc", nn.Linear(d_model, d_model * 4)),
|
|
179
|
+
("gelu", QuickGELU()),
|
|
180
|
+
("c_proj", nn.Linear(d_model * 4, d_model))
|
|
181
|
+
]))
|
|
182
|
+
self.ln_2 = LayerNorm(d_model)
|
|
183
|
+
self.attn_mask = attn_mask
|
|
184
|
+
|
|
185
|
+
def attention(self, x: torch.Tensor):
|
|
186
|
+
self.attn_mask = self.attn_mask.to(dtype=x.dtype, device=x.device) if self.attn_mask is not None else None
|
|
187
|
+
return self.attn(x, x, x, need_weights=False, attn_mask=self.attn_mask)[0]
|
|
188
|
+
|
|
189
|
+
def forward(self, x: torch.Tensor):
|
|
190
|
+
x = x + self.attention(self.ln_1(x))
|
|
191
|
+
x = x + self.mlp(self.ln_2(x))
|
|
192
|
+
return x
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class Transformer(nn.Module):
|
|
196
|
+
def __init__(self, width: int, layers: int, heads: int, attn_mask: torch.Tensor = None):
|
|
197
|
+
super().__init__()
|
|
198
|
+
self.width = width
|
|
199
|
+
self.layers = layers
|
|
200
|
+
self.resblocks = nn.Sequential(*[ResidualAttentionBlock(width, heads, attn_mask) for _ in range(layers)])
|
|
201
|
+
|
|
202
|
+
def forward(self, x: torch.Tensor):
|
|
203
|
+
return self.resblocks(x)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class VisionTransformer(nn.Module):
|
|
207
|
+
def __init__(self, input_resolution: int, patch_size: int, width: int, layers: int, heads: int, output_dim: int):
|
|
208
|
+
super().__init__()
|
|
209
|
+
self.input_resolution = input_resolution
|
|
210
|
+
self.output_dim = output_dim
|
|
211
|
+
self.conv1 = nn.Conv2d(in_channels=3, out_channels=width, kernel_size=patch_size, stride=patch_size, bias=False)
|
|
212
|
+
|
|
213
|
+
scale = width ** -0.5
|
|
214
|
+
self.class_embedding = nn.Parameter(scale * torch.randn(width))
|
|
215
|
+
self.positional_embedding = nn.Parameter(scale * torch.randn((input_resolution // patch_size) ** 2 + 1, width))
|
|
216
|
+
self.ln_pre = LayerNorm(width)
|
|
217
|
+
|
|
218
|
+
self.transformer = Transformer(width, layers, heads)
|
|
219
|
+
|
|
220
|
+
self.ln_post = LayerNorm(width)
|
|
221
|
+
self.proj = nn.Parameter(scale * torch.randn(width, output_dim))
|
|
222
|
+
|
|
223
|
+
def forward(self, x: torch.Tensor):
|
|
224
|
+
x = self.conv1(x) # shape = [*, width, grid, grid]
|
|
225
|
+
x = x.reshape(x.shape[0], x.shape[1], -1) # shape = [*, width, grid ** 2]
|
|
226
|
+
x = x.permute(0, 2, 1) # shape = [*, grid ** 2, width]
|
|
227
|
+
x = torch.cat([self.class_embedding.to(x.dtype) + torch.zeros(x.shape[0], 1, x.shape[-1], dtype=x.dtype, device=x.device), x], dim=1) # shape = [*, grid ** 2 + 1, width]
|
|
228
|
+
x = x + self.positional_embedding.to(x.dtype)
|
|
229
|
+
x = self.ln_pre(x)
|
|
230
|
+
|
|
231
|
+
x = x.permute(1, 0, 2) # NLD -> LND
|
|
232
|
+
x = self.transformer(x)
|
|
233
|
+
x = x.permute(1, 0, 2) # LND -> NLD
|
|
234
|
+
|
|
235
|
+
x = self.ln_post(x[:, 0, :])
|
|
236
|
+
|
|
237
|
+
if self.proj is not None:
|
|
238
|
+
x = x @ self.proj
|
|
239
|
+
|
|
240
|
+
return x
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class CLIP(nn.Module):
|
|
244
|
+
def __init__(self,
|
|
245
|
+
embed_dim: int,
|
|
246
|
+
# vision
|
|
247
|
+
image_resolution: int,
|
|
248
|
+
vision_layers: Union[Tuple[int, int, int, int], int],
|
|
249
|
+
vision_width: int,
|
|
250
|
+
vision_patch_size: int,
|
|
251
|
+
# text
|
|
252
|
+
context_length: int,
|
|
253
|
+
vocab_size: int,
|
|
254
|
+
transformer_width: int,
|
|
255
|
+
transformer_heads: int,
|
|
256
|
+
transformer_layers: int
|
|
257
|
+
):
|
|
258
|
+
super().__init__()
|
|
259
|
+
|
|
260
|
+
self.context_length = context_length
|
|
261
|
+
|
|
262
|
+
if isinstance(vision_layers, (tuple, list)):
|
|
263
|
+
vision_heads = vision_width * 32 // 64
|
|
264
|
+
self.visual = ModifiedResNet(
|
|
265
|
+
layers=vision_layers,
|
|
266
|
+
output_dim=embed_dim,
|
|
267
|
+
heads=vision_heads,
|
|
268
|
+
input_resolution=image_resolution,
|
|
269
|
+
width=vision_width
|
|
270
|
+
)
|
|
271
|
+
else:
|
|
272
|
+
vision_heads = vision_width // 64
|
|
273
|
+
self.visual = VisionTransformer(
|
|
274
|
+
input_resolution=image_resolution,
|
|
275
|
+
patch_size=vision_patch_size,
|
|
276
|
+
width=vision_width,
|
|
277
|
+
layers=vision_layers,
|
|
278
|
+
heads=vision_heads,
|
|
279
|
+
output_dim=embed_dim
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
self.transformer = Transformer(
|
|
283
|
+
width=transformer_width,
|
|
284
|
+
layers=transformer_layers,
|
|
285
|
+
heads=transformer_heads,
|
|
286
|
+
attn_mask=self.build_attention_mask()
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
self.vocab_size = vocab_size
|
|
290
|
+
self.token_embedding = nn.Embedding(vocab_size, transformer_width)
|
|
291
|
+
self.positional_embedding = nn.Parameter(torch.empty(self.context_length, transformer_width))
|
|
292
|
+
self.ln_final = LayerNorm(transformer_width)
|
|
293
|
+
|
|
294
|
+
self.text_projection = nn.Parameter(torch.empty(transformer_width, embed_dim))
|
|
295
|
+
self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07))
|
|
296
|
+
|
|
297
|
+
self.initialize_parameters()
|
|
298
|
+
|
|
299
|
+
def initialize_parameters(self):
|
|
300
|
+
nn.init.normal_(self.token_embedding.weight, std=0.02)
|
|
301
|
+
nn.init.normal_(self.positional_embedding, std=0.01)
|
|
302
|
+
|
|
303
|
+
if isinstance(self.visual, ModifiedResNet):
|
|
304
|
+
if self.visual.attnpool is not None:
|
|
305
|
+
std = self.visual.attnpool.c_proj.in_features ** -0.5
|
|
306
|
+
nn.init.normal_(self.visual.attnpool.q_proj.weight, std=std)
|
|
307
|
+
nn.init.normal_(self.visual.attnpool.k_proj.weight, std=std)
|
|
308
|
+
nn.init.normal_(self.visual.attnpool.v_proj.weight, std=std)
|
|
309
|
+
nn.init.normal_(self.visual.attnpool.c_proj.weight, std=std)
|
|
310
|
+
|
|
311
|
+
for resnet_block in [self.visual.layer1, self.visual.layer2, self.visual.layer3, self.visual.layer4]:
|
|
312
|
+
for name, param in resnet_block.named_parameters():
|
|
313
|
+
if name.endswith("bn3.weight"):
|
|
314
|
+
nn.init.zeros_(param)
|
|
315
|
+
|
|
316
|
+
proj_std = (self.transformer.width ** -0.5) * ((2 * self.transformer.layers) ** -0.5)
|
|
317
|
+
attn_std = self.transformer.width ** -0.5
|
|
318
|
+
fc_std = (2 * self.transformer.width) ** -0.5
|
|
319
|
+
for block in self.transformer.resblocks:
|
|
320
|
+
nn.init.normal_(block.attn.in_proj_weight, std=attn_std)
|
|
321
|
+
nn.init.normal_(block.attn.out_proj.weight, std=proj_std)
|
|
322
|
+
nn.init.normal_(block.mlp.c_fc.weight, std=fc_std)
|
|
323
|
+
nn.init.normal_(block.mlp.c_proj.weight, std=proj_std)
|
|
324
|
+
|
|
325
|
+
if self.text_projection is not None:
|
|
326
|
+
nn.init.normal_(self.text_projection, std=self.transformer.width ** -0.5)
|
|
327
|
+
|
|
328
|
+
def build_attention_mask(self):
|
|
329
|
+
# lazily create causal attention mask, with full attention between the vision tokens
|
|
330
|
+
# pytorch uses additive attention mask; fill with -inf
|
|
331
|
+
mask = torch.empty(self.context_length, self.context_length)
|
|
332
|
+
mask.fill_(float("-inf"))
|
|
333
|
+
mask.triu_(1) # zero out the lower diagonal
|
|
334
|
+
return mask
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def dtype(self):
|
|
338
|
+
return self.visual.conv1.weight.dtype
|
|
339
|
+
|
|
340
|
+
def encode_image(self, image):
|
|
341
|
+
return self.visual(image.type(self.dtype))
|
|
342
|
+
|
|
343
|
+
def encode_text(self, text):
|
|
344
|
+
x = self.token_embedding(text).type(self.dtype) # [batch_size, n_ctx, d_model]
|
|
345
|
+
|
|
346
|
+
x = x + self.positional_embedding.type(self.dtype)
|
|
347
|
+
x = x.permute(1, 0, 2) # NLD -> LND
|
|
348
|
+
x = self.transformer(x)
|
|
349
|
+
x = x.permute(1, 0, 2) # LND -> NLD
|
|
350
|
+
x = self.ln_final(x).type(self.dtype)
|
|
351
|
+
|
|
352
|
+
# x.shape = [batch_size, n_ctx, transformer.width]
|
|
353
|
+
# take features from the eot embedding (eot_token is the highest number in each sequence)
|
|
354
|
+
x = x[torch.arange(x.shape[0]), text.argmax(dim=-1)] @ self.text_projection
|
|
355
|
+
|
|
356
|
+
return x
|
|
357
|
+
|
|
358
|
+
def forward(self, image, text):
|
|
359
|
+
image_features = self.encode_image(image)
|
|
360
|
+
text_features = self.encode_text(text)
|
|
361
|
+
|
|
362
|
+
# normalized features
|
|
363
|
+
image_features = image_features / image_features.norm(dim=1, keepdim=True)
|
|
364
|
+
text_features = text_features / text_features.norm(dim=1, keepdim=True)
|
|
365
|
+
|
|
366
|
+
# cosine similarity as logits
|
|
367
|
+
logit_scale = self.logit_scale.exp()
|
|
368
|
+
logits_per_image = logit_scale * image_features @ text_features.t()
|
|
369
|
+
logits_per_text = logits_per_image.t()
|
|
370
|
+
|
|
371
|
+
# shape = [global_batch_size, global_batch_size]
|
|
372
|
+
return logits_per_image, logits_per_text
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def convert_weights(model: nn.Module):
|
|
376
|
+
"""Convert applicable model parameters to fp16"""
|
|
377
|
+
|
|
378
|
+
def _convert_weights_to_fp16(l):
|
|
379
|
+
if isinstance(l, (nn.Conv1d, nn.Conv2d, nn.Linear)):
|
|
380
|
+
l.weight.data = l.weight.data.half()
|
|
381
|
+
if l.bias is not None:
|
|
382
|
+
l.bias.data = l.bias.data.half()
|
|
383
|
+
|
|
384
|
+
if isinstance(l, nn.MultiheadAttention):
|
|
385
|
+
for attr in [*[f"{s}_proj_weight" for s in ["in", "q", "k", "v"]], "in_proj_bias", "bias_k", "bias_v"]:
|
|
386
|
+
tensor = getattr(l, attr)
|
|
387
|
+
if tensor is not None:
|
|
388
|
+
tensor.data = tensor.data.half()
|
|
389
|
+
|
|
390
|
+
for name in ["text_projection", "proj"]:
|
|
391
|
+
if hasattr(l, name):
|
|
392
|
+
attr = getattr(l, name)
|
|
393
|
+
if attr is not None:
|
|
394
|
+
attr.data = attr.data.half()
|
|
395
|
+
|
|
396
|
+
model.apply(_convert_weights_to_fp16)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def build_model(state_dict: dict):
|
|
400
|
+
vit = "visual.proj" in state_dict
|
|
401
|
+
|
|
402
|
+
if vit:
|
|
403
|
+
vision_width = state_dict["visual.conv1.weight"].shape[0]
|
|
404
|
+
vision_layers = len([k for k in state_dict.keys() if k.startswith("visual.") and k.endswith(".attn.in_proj_weight")])
|
|
405
|
+
vision_patch_size = state_dict["visual.conv1.weight"].shape[-1]
|
|
406
|
+
grid_size = round((state_dict["visual.positional_embedding"].shape[0] - 1) ** 0.5)
|
|
407
|
+
image_resolution = vision_patch_size * grid_size
|
|
408
|
+
else:
|
|
409
|
+
counts: list = [len(set(k.split(".")[2] for k in state_dict if k.startswith(f"visual.layer{b}"))) for b in [1, 2, 3, 4]]
|
|
410
|
+
vision_layers = tuple(counts)
|
|
411
|
+
vision_width = state_dict["visual.layer1.0.conv1.weight"].shape[0]
|
|
412
|
+
output_width = round((state_dict["visual.attnpool.positional_embedding"].shape[0] - 1) ** 0.5)
|
|
413
|
+
vision_patch_size = None
|
|
414
|
+
assert output_width ** 2 + 1 == state_dict["visual.attnpool.positional_embedding"].shape[0]
|
|
415
|
+
image_resolution = output_width * 32
|
|
416
|
+
|
|
417
|
+
embed_dim = state_dict["text_projection"].shape[1]
|
|
418
|
+
context_length = state_dict["positional_embedding"].shape[0]
|
|
419
|
+
vocab_size = state_dict["token_embedding.weight"].shape[0]
|
|
420
|
+
transformer_width = state_dict["ln_final.weight"].shape[0]
|
|
421
|
+
transformer_heads = transformer_width // 64
|
|
422
|
+
transformer_layers = len(set(k.split(".")[2] for k in state_dict if k.startswith("transformer.resblocks")))
|
|
423
|
+
|
|
424
|
+
model = CLIP(
|
|
425
|
+
embed_dim,
|
|
426
|
+
image_resolution, vision_layers, vision_width, vision_patch_size,
|
|
427
|
+
context_length, vocab_size, transformer_width, transformer_heads, transformer_layers
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
for key in ["input_resolution", "context_length", "vocab_size"]:
|
|
431
|
+
if key in state_dict:
|
|
432
|
+
del state_dict[key]
|
|
433
|
+
|
|
434
|
+
convert_weights(model)
|
|
435
|
+
model.load_state_dict(state_dict)
|
|
436
|
+
return model.eval()
|