gatilin commited on
Commit
7bfa997
·
verified ·
1 Parent(s): 4a3dee4

Publish pinned visual components with exact source payloads and native numerical validation

Browse files
CODE_LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2024 FoundationVision
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
LICENSE ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ UniTok model weights are released under Apache-2.0 in the official model card:
2
+ https://huggingface.co/FoundationVision/unitok_tokenizer/tree/7f6ea34f2f669d8d250a4376468c69fe4bcb6c65
3
+
4
+ Apache License
5
+ Version 2.0, January 2004
6
+ http://www.apache.org/licenses/
7
+
8
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
9
+
10
+ 1. Definitions.
11
+
12
+ "License" shall mean the terms and conditions for use, reproduction,
13
+ and distribution as defined by Sections 1 through 9 of this document.
14
+
15
+ "Licensor" shall mean the copyright owner or entity authorized by
16
+ the copyright owner that is granting the License.
17
+
18
+ "Legal Entity" shall mean the union of the acting entity and all
19
+ other entities that control, are controlled by, or are under common
20
+ control with that entity. For the purposes of this definition,
21
+ "control" means (i) the power, direct or indirect, to cause the
22
+ direction or management of such entity, whether by contract or
23
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
24
+ outstanding shares, or (iii) beneficial ownership of such entity.
25
+
26
+ "You" (or "Your") shall mean an individual or Legal Entity
27
+ exercising permissions granted by this License.
28
+
29
+ "Source" form shall mean the preferred form for making modifications,
30
+ including but not limited to software source code, documentation
31
+ source, and configuration files.
32
+
33
+ "Object" form shall mean any form resulting from mechanical
34
+ transformation or translation of a Source form, including but
35
+ not limited to compiled object code, generated documentation,
36
+ and conversions to other media types.
37
+
38
+ "Work" shall mean the work of authorship, whether in Source or
39
+ Object form, made available under the License, as indicated by a
40
+ copyright notice that is included in or attached to the work
41
+ (an example is provided in the Appendix below).
42
+
43
+ "Derivative Works" shall mean any work, whether in Source or Object
44
+ form, that is based on (or derived from) the Work and for which the
45
+ editorial revisions, annotations, elaborations, or other modifications
46
+ represent, as a whole, an original work of authorship. For the purposes
47
+ of this License, Derivative Works shall not include works that remain
48
+ separable from, or merely link (or bind by name) to the interfaces of,
49
+ the Work and Derivative Works thereof.
50
+
51
+ "Contribution" shall mean any work of authorship, including
52
+ the original version of the Work and any modifications or additions
53
+ to that Work or Derivative Works thereof, that is intentionally
54
+ submitted to Licensor for inclusion in the Work by the copyright owner
55
+ or by an individual or Legal Entity authorized to submit on behalf of
56
+ the copyright owner. For the purposes of this definition, "submitted"
57
+ means any form of electronic, verbal, or written communication sent
58
+ to the Licensor or its representatives, including but not limited to
59
+ communication on electronic mailing lists, source code control systems,
60
+ and issue tracking systems that are managed by, or on behalf of, the
61
+ Licensor for the purpose of discussing and improving the Work, but
62
+ excluding communication that is conspicuously marked or otherwise
63
+ designated in writing by the copyright owner as "Not a Contribution."
64
+
65
+ "Contributor" shall mean Licensor and any individual or Legal Entity
66
+ on behalf of whom a Contribution has been received by Licensor and
67
+ subsequently incorporated within the Work.
68
+
69
+ 2. Grant of Copyright License. Subject to the terms and conditions of
70
+ this License, each Contributor hereby grants to You a perpetual,
71
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
72
+ copyright license to reproduce, prepare Derivative Works of,
73
+ publicly display, publicly perform, sublicense, and distribute the
74
+ Work and such Derivative Works in Source or Object form.
75
+
76
+ 3. Grant of Patent License. Subject to the terms and conditions of
77
+ this License, each Contributor hereby grants to You a perpetual,
78
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
79
+ (except as stated in this section) patent license to make, have made,
80
+ use, offer to sell, sell, import, and otherwise transfer the Work,
81
+ where such license applies only to those patent claims licensable
82
+ by such Contributor that are necessarily infringed by their
83
+ Contribution(s) alone or by combination of their Contribution(s)
84
+ with the Work to which such Contribution(s) was submitted. If You
85
+ institute patent litigation against any entity (including a
86
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
87
+ or a Contribution incorporated within the Work constitutes direct
88
+ or contributory patent infringement, then any patent licenses
89
+ granted to You under this License for that Work shall terminate
90
+ as of the date such litigation is filed.
91
+
92
+ 4. Redistribution. You may reproduce and distribute copies of the
93
+ Work or Derivative Works thereof in any medium, with or without
94
+ modifications, and in Source or Object form, provided that You
95
+ meet the following conditions:
96
+
97
+ (a) You must give any other recipients of the Work or
98
+ Derivative Works a copy of this License; and
99
+
100
+ (b) You must cause any modified files to carry prominent notices
101
+ stating that You changed the files; and
102
+
103
+ (c) You must retain, in the Source form of any Derivative Works
104
+ that You distribute, all copyright, patent, trademark, and
105
+ attribution notices from the Source form of the Work,
106
+ excluding those notices that do not pertain to any part of
107
+ the Derivative Works; and
108
+
109
+ (d) If the Work includes a "NOTICE" text file as part of its
110
+ distribution, then any Derivative Works that You distribute must
111
+ include a readable copy of the attribution notices contained
112
+ within such NOTICE file, excluding those notices that do not
113
+ pertain to any part of the Derivative Works, in at least one
114
+ of the following places: within a NOTICE text file distributed
115
+ as part of the Derivative Works; within the Source form or
116
+ documentation, if provided along with the Derivative Works; or,
117
+ within a display generated by the Derivative Works, if and
118
+ wherever such third-party notices normally appear. The contents
119
+ of the NOTICE file are for informational purposes only and
120
+ do not modify the License. You may add Your own attribution
121
+ notices within Derivative Works that You distribute, alongside
122
+ or as an addendum to the NOTICE text from the Work, provided
123
+ that such additional attribution notices cannot be construed
124
+ as modifying the License.
125
+
126
+ You may add Your own copyright statement to Your modifications and
127
+ may provide additional or different license terms and conditions
128
+ for use, reproduction, or distribution of Your modifications, or
129
+ for any such Derivative Works as a whole, provided Your use,
130
+ reproduction, and distribution of the Work otherwise complies with
131
+ the conditions stated in this License.
132
+
133
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
134
+ any Contribution intentionally submitted for inclusion in the Work
135
+ by You to the Licensor shall be under the terms and conditions of
136
+ this License, without any additional terms or conditions.
137
+ Notwithstanding the above, nothing herein shall supersede or modify
138
+ the terms of any separate license agreement you may have executed
139
+ with Licensor regarding such Contributions.
140
+
141
+ 6. Trademarks. This License does not grant permission to use the trade
142
+ names, trademarks, service marks, or product names of the Licensor,
143
+ except as required for reasonable and customary use in describing the
144
+ origin of the Work and reproducing the content of the NOTICE file.
145
+
146
+ 7. Disclaimer of Warranty. Unless required by applicable law or
147
+ agreed to in writing, Licensor provides the Work (and each
148
+ Contributor provides its Contributions) on an "AS IS" BASIS,
149
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
150
+ implied, including, without limitation, any warranties or conditions
151
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
152
+ PARTICULAR PURPOSE. You are solely responsible for determining the
153
+ appropriateness of using or redistributing the Work and assume any
154
+ risks associated with Your exercise of permissions under this License.
155
+
156
+ 8. Limitation of Liability. In no event and under no legal theory,
157
+ whether in tort (including negligence), contract, or otherwise,
158
+ unless required by applicable law (such as deliberate and grossly
159
+ negligent acts) or agreed to in writing, shall any Contributor be
160
+ liable to You for damages, including any direct, indirect, special,
161
+ incidental, or consequential damages of any character arising as a
162
+ result of this License or out of the use or inability to use the
163
+ Work (including but not limited to damages for loss of goodwill,
164
+ work stoppage, computer failure or malfunction, or any and all
165
+ other commercial damages or losses), even if such Contributor
166
+ has been advised of the possibility of such damages.
167
+
168
+ 9. Accepting Warranty or Additional Liability. While redistributing
169
+ the Work or Derivative Works thereof, You may choose to offer,
170
+ and charge a fee for, acceptance of support, warranty, indemnity,
171
+ or other liability obligations and/or rights consistent with this
172
+ License. However, in accepting such obligations, You may act only
173
+ on Your own behalf and on Your sole responsibility, not on behalf
174
+ of any other Contributor, and only if You agree to indemnify,
175
+ defend, and hold each Contributor harmless for any liability
176
+ incurred by, or claims asserted against, such Contributor by reason
177
+ of your accepting any such warranty or additional liability.
178
+
179
+ END OF TERMS AND CONDITIONS
180
+
181
+ APPENDIX: How to apply the Apache License to your work.
182
+
183
+ To apply the Apache License to your work, attach the following
184
+ boilerplate notice, with the fields enclosed by brackets "[]"
185
+ replaced with your own identifying information. (Don't include
186
+ the brackets!) The text should be enclosed in the appropriate
187
+ comment syntax for the file format. We also recommend that a
188
+ file or class name and description of purpose be included on the
189
+ same "printed page" as the copyright notice for easier
190
+ identification within third-party archives.
191
+
192
+ Copyright [yyyy] [name of copyright owner]
193
+
194
+ Licensed under the Apache License, Version 2.0 (the "License");
195
+ you may not use this file except in compliance with the License.
196
+ You may obtain a copy of the License at
197
+
198
+ http://www.apache.org/licenses/LICENSE-2.0
199
+
200
+ Unless required by applicable law or agreed to in writing, software
201
+ distributed under the License is distributed on an "AS IS" BASIS,
202
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
203
+ See the License for the specific language governing permissions and
204
+ limitations under the License.
OPENCLIP_LICENSE ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2021 OpenAI
4
+
5
+ Attribution from the unchanged official vendored open_clip/model.py header.
6
+ Project modifications are also covered by CODE_LICENSE.
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
README.md ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ library_name: pytorch
4
+ pipeline_tag: image-to-image
5
+ base_model: FoundationVision/unitok_tokenizer
6
+ tags:
7
+ - unitok
8
+ - image-tokenizer
9
+ - unified-multimodal
10
+ - component-extraction
11
+ ---
12
+ # UniTok Large unified visual codec
13
+
14
+ The complete trained `trainer.unitok` mapping from [the official tokenizer checkpoint](https://huggingface.co/FoundationVision/unitok_tokenizer/tree/7f6ea34f2f669d8d250a4376468c69fe4bcb6c65): ViTamin-L encoder, attention projections, eight normalized codebooks, RGB decoder, semantic image projection and trained CLIP text tower. All 1335 tensors preserve their original names, dtypes and bytes. Discriminator, optimizer and trainer state are excluded.
15
+
16
+ Install `requirements.txt` before loading. In particular, the native hybrid embedder requires the official `timm==1.0.8`; newer timm constructors use incompatible arguments. The exact text-transformer implementation vendored by the original UniTok project is bundled here; no external OpenCLIP installation is required.
17
+
18
+ ```python
19
+ from huggingface_hub import snapshot_download
20
+ import sys, torch
21
+ folder = snapshot_download('gatilin/unitok_large_visual_codec')
22
+ sys.path.insert(0, folder)
23
+ from component import load_model
24
+ codec = load_model(folder)
25
+ x = torch.zeros(1, 3, 256, 256) # RGB, NCHW, [-1, 1]
26
+ with torch.inference_mode():
27
+ ids = codec.img_to_idx(x) # [B, 8, 256]
28
+ rgb = codec.idx_to_img(ids)
29
+ semantic_features = codec.encode_image(x, normalize=True) # [B, 768]
30
+ ```
31
+
32
+ Each spatial position uses **eight local IDs in [0, 4095]**, one per 4096-by-8 codebook. The checkpoint's `vocab_size=32768` is the sum of the eight codebook sizes, not the upper bound of every local ID. Code lookup normalizes each sub-codebook before concatenating 64-dimensional vectors. The native decoder assumes a square token grid; this release validates its trained 256-by-256 image path.
33
+
34
+ `preprocessor_config.json` records the deterministic validation transform in the pinned project's `utils/data.py`: resize the shorter edge to 256 with Lanczos, center-crop 256-by-256, convert to RGB, convert to a [0,1] tensor and apply `2*x-1`. Downstream MLLM prompt processors have additional conventions.
35
+
36
+ Native source is pinned to [FoundationVision/UniTok revision bb8012e](https://github.com/FoundationVision/UniTok/tree/bb8012e8e8d7be66928bedd80506c56a55ea6305), with imports relocated into the bundled `native` package. The text tower directly instantiates the same configured default branch of the original vendored builder, using its unchanged `TextTransformer` and utilities; `native/open_clip/source_model.py` retains the original constructor as a reference. The wrapper disables training checkpointing and strictly restores the complete trained mapping. Text features use `codec.encode_text`; CLIP BPE tokenization should follow the original project. No BPE assets or language generator are included.
37
+
38
+ `validation.json` records source tensor payload hashes, strict loading, codebook geometry, encode/ID/decode behavior, direct lookup parity, the native quantizer's small floating point difference, and normalized image/text features. These synthetic checks do not reproduce paper quality or downstream accuracy benchmarks. Re-run with `python validate.py`.
39
+
40
+ Weight terms follow the official model-card Apache-2.0 license (`LICENSE`). Bundled project code follows its MIT license (`CODE_LICENSE`); `OPENCLIP_LICENSE` and the unchanged reference source retain the original OpenAI attribution. Original third-party notices in individual files are retained. Source container integrity, mapping, per-tensor hashes and code provenance are included.
code_provenance.json ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "native/models/unitok.py": {
3
+ "url": "https://raw.githubusercontent.com/FoundationVision/UniTok/bb8012e8e8d7be66928bedd80506c56a55ea6305/models/unitok.py",
4
+ "original_sha256": "27872719d35827715e478556e857efa1236b6575219db2aac8206ae354e04972",
5
+ "modifications": [
6
+ "Package imports relocated into native namespace.",
7
+ "Instantiate the exact configured non-HF default branch of the official vendored _build_text_tower directly with native.open_clip.transformer.TextTransformer; its original constructor remains in native/open_clip/source_model.py."
8
+ ]
9
+ },
10
+ "native/models/quant.py": {
11
+ "url": "https://raw.githubusercontent.com/FoundationVision/UniTok/bb8012e8e8d7be66928bedd80506c56a55ea6305/models/quant.py",
12
+ "original_sha256": "9d121a0d8d3d52127afd118df3c119a30a4dea784433ea6403a3be299945375a",
13
+ "modifications": [
14
+ "Package imports relocated into native namespace."
15
+ ]
16
+ },
17
+ "native/models/vitamin.py": {
18
+ "url": "https://raw.githubusercontent.com/FoundationVision/UniTok/bb8012e8e8d7be66928bedd80506c56a55ea6305/models/vitamin.py",
19
+ "original_sha256": "9c2df37cbad670786a4ac3a5eeece4fe3ab3de57c9c5848b4f6bdd44f76d4600",
20
+ "modifications": [
21
+ "Package imports relocated into native namespace."
22
+ ]
23
+ },
24
+ "native/models/vqvae.py": {
25
+ "url": "https://raw.githubusercontent.com/FoundationVision/UniTok/bb8012e8e8d7be66928bedd80506c56a55ea6305/models/vqvae.py",
26
+ "original_sha256": "9ddcb17ea31936e80bdf7cd749082314aa7a6686c0c60e79e64edc467f75323d",
27
+ "modifications": [
28
+ "Package imports relocated into native namespace."
29
+ ]
30
+ },
31
+ "native/utils/config.py": {
32
+ "url": "https://raw.githubusercontent.com/FoundationVision/UniTok/bb8012e8e8d7be66928bedd80506c56a55ea6305/utils/config.py",
33
+ "original_sha256": "94b0c9637ef1a8dfd710c42b97ad4e8175f56573a3705b912b217b6b4b527037",
34
+ "modifications": [
35
+ "Package imports relocated into native namespace."
36
+ ]
37
+ },
38
+ "native/utils/dist.py": {
39
+ "url": "https://raw.githubusercontent.com/FoundationVision/UniTok/bb8012e8e8d7be66928bedd80506c56a55ea6305/utils/dist.py",
40
+ "original_sha256": "4ad98f72e600a666795eb074329f4e6cfd8c182776fb7f9e4d9d39ae2dd83524",
41
+ "modifications": []
42
+ },
43
+ "CODE_LICENSE": {
44
+ "url": "https://raw.githubusercontent.com/FoundationVision/UniTok/bb8012e8e8d7be66928bedd80506c56a55ea6305/LICENSE",
45
+ "original_sha256": "a318f4da9d043e05f535b65774febde7572908c97562cc14e49a2f3687f5a453",
46
+ "modifications": []
47
+ },
48
+ "native/open_clip/transformer.py": {
49
+ "url": "https://raw.githubusercontent.com/FoundationVision/UniTok/bb8012e8e8d7be66928bedd80506c56a55ea6305/open_clip/transformer.py",
50
+ "original_sha256": "dfaef403435d5766aeea8286fca0f9fc46285ee25fd28e87ba197bf51f1f676e",
51
+ "modifications": [],
52
+ "purpose": "Exact official vendored text computation; no external OpenCLIP dependency."
53
+ },
54
+ "native/open_clip/utils.py": {
55
+ "url": "https://raw.githubusercontent.com/FoundationVision/UniTok/bb8012e8e8d7be66928bedd80506c56a55ea6305/open_clip/utils.py",
56
+ "original_sha256": "f0267bb9d5402b1b5b38a90f96f6cd9002ac21d2db0f3f3fbee8ed48b2f46047",
57
+ "modifications": [],
58
+ "purpose": "Exact official vendored text computation; no external OpenCLIP dependency."
59
+ },
60
+ "native/open_clip/source_model.py": {
61
+ "url": "https://raw.githubusercontent.com/FoundationVision/UniTok/bb8012e8e8d7be66928bedd80506c56a55ea6305/open_clip/model.py",
62
+ "original_sha256": "2a7bc461f7cfe70497e7ce8541f75a8b67dcbd3d93685cfd8671ebf98d337150",
63
+ "modifications": [],
64
+ "purpose": "Original constructor reference only."
65
+ }
66
+ }
component.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Strict loader preserving the complete trained UniTok state, including semantic heads."""
2
+ import json
3
+ from pathlib import Path
4
+ from types import SimpleNamespace
5
+ import torch
6
+ from safetensors.torch import load_file
7
+ from native.models.unitok import UniTok
8
+
9
+
10
+ def load_model(path=None, dtype=torch.float32, device='cpu'):
11
+ root = Path(path or Path(__file__).parent)
12
+ cfg = json.loads((root / 'config.json').read_text())
13
+ cfg.update(device='cpu', grad_ckpt=False)
14
+ # The native hybrid embedder runs a CPU shape probe in its constructor.
15
+ model = UniTok(SimpleNamespace(**cfg))
16
+ model.load_state_dict(load_file(root / 'model.safetensors'), strict=True, assign=True)
17
+ return model.to(device=device, dtype=dtype).eval()
config.json ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "vitamin_large",
3
+ "exp_name": "unitok_large_causal",
4
+ "output_dir": "local_output",
5
+ "resume_from": "",
6
+ "lpips_path": "",
7
+ "dino_path": "",
8
+ "fid_eval_src": "",
9
+ "fid_eval_dst": "",
10
+ "vis_img_dir": "asset/vis_imgs/",
11
+ "fid_feature_extractor": "",
12
+ "clip_pretrain_path": "",
13
+ "fp16": false,
14
+ "bf16": true,
15
+ "tf32": true,
16
+ "compile_model": false,
17
+ "ddp_static": false,
18
+ "grad_ckpt": true,
19
+ "grad_accu": 1,
20
+ "dtype": "torch.bfloat16",
21
+ "train_data": "",
22
+ "val_data": null,
23
+ "dataset_type": "webdataset",
24
+ "imagenet_val": "",
25
+ "imagenet_v2": null,
26
+ "subset_ratio": 1.0,
27
+ "img_size": 256,
28
+ "resize_ratio": 1.125,
29
+ "hflip": false,
30
+ "workers": 16,
31
+ "train_num_samples": 1280000000,
32
+ "train_data_upsampling_factors": null,
33
+ "dataset_resampled": false,
34
+ "use_aug": false,
35
+ "vocab_size": 32768,
36
+ "vocab_width": 64,
37
+ "vocab_norm": true,
38
+ "vq_beta": 0.25,
39
+ "num_codebooks": 8,
40
+ "quant_proj": "attn",
41
+ "embed_dim": 768,
42
+ "num_query": 0,
43
+ "use_clip_pretrain": false,
44
+ "patch_size": 16,
45
+ "drop_path": 0.1,
46
+ "text_width": 768,
47
+ "text_heads": 12,
48
+ "text_layers": 12,
49
+ "text_vocab_size": 49408,
50
+ "text_context_length": 77,
51
+ "local_loss": true,
52
+ "gather_with_grad": true,
53
+ "pretrained_clip": null,
54
+ "pretrained_clip_text": null,
55
+ "lock_text": false,
56
+ "lock_text_unlocked_layers": 0,
57
+ "lock_text_freeze_layer_norm": false,
58
+ "force_custom_text": false,
59
+ "force_custom_vision": false,
60
+ "zeroshot_eval_freq": 1,
61
+ "dino_depth": 12,
62
+ "dino_kernel_size": 9,
63
+ "disc_norm": "gn",
64
+ "disc_aug_prob": 1.0,
65
+ "disc_specnorm": false,
66
+ "step_disc_every": 1,
67
+ "vae_init": -0.5,
68
+ "vocab_init": -1,
69
+ "disc_init": -0.5,
70
+ "epoch": 1,
71
+ "local_bs": 56,
72
+ "vae_local_bs": 56,
73
+ "global_bs": 16384,
74
+ "lr": 0.0005,
75
+ "wd": 0.02,
76
+ "disc_lr": 2e-05,
77
+ "disc_wd": 0.2,
78
+ "grad_clip": 10,
79
+ "ema": 0.9999,
80
+ "warmup_iter": null,
81
+ "warmup_ep": 0.01,
82
+ "disc_start_ep": 0.375,
83
+ "disc_warmup_ep": 0.03,
84
+ "schedule": "cos",
85
+ "lr_start_ratio": 0.0,
86
+ "lr_end_ratio": 0.1,
87
+ "disc_lr_end_ratio": 0.1,
88
+ "custom_lr_multiplier": null,
89
+ "optimizer": "adamw",
90
+ "optim_eps": 1e-06,
91
+ "fuse_opt": false,
92
+ "optim_beta": "0.9_0.95",
93
+ "disc_optim_beta": "0.5_0.9",
94
+ "l1": 0.2,
95
+ "l2": 1.0,
96
+ "lp": 1.0,
97
+ "lpr": 48,
98
+ "ld": 0.4,
99
+ "le": 0.0,
100
+ "lq": 1.0,
101
+ "lc": 1.0,
102
+ "e_temp": 0.01,
103
+ "gada": 1,
104
+ "bcr": 4.0,
105
+ "bcr_cut": 0.2,
106
+ "dcrit": "hg",
107
+ "report_wandb": true,
108
+ "wandb_notes": null,
109
+ "run_id": null,
110
+ "eval_per_epoch": 8,
111
+ "dbg_unused_param": false,
112
+ "dbg_nan": false,
113
+ "seed": null,
114
+ "deterministic": false,
115
+ "same_seed_for_all_ranks": 0
116
+ }
extraction.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "source_repo": "FoundationVision/unitok_tokenizer",
3
+ "source_revision": "7f6ea34f2f669d8d250a4376468c69fe4bcb6c65",
4
+ "source_file": "unitok_tokenizer.pth",
5
+ "prefixes": [],
6
+ "source_tensor_bytes_equal": true,
7
+ "sha256": "364bbd3e3f453f19dba447805f347aabdd7abdc7da20186364f5b2961c541b5c",
8
+ "bytes": 3281477448,
9
+ "verification": "Every HTTP range received at its requested exact offsets and persisted-byte SHA-256 checked; whole original file also matches official LFS SHA-256.",
10
+ "source_file_lfs_sha256": "b1e39e3df707c909c81de0affcbf45075d40d9f156ef0e200b037bcc9b6db81a",
11
+ "source_field": "trainer.unitok",
12
+ "tensor_count": 1335,
13
+ "source_container_sha256": "b1e39e3df707c909c81de0affcbf45075d40d9f156ef0e200b037bcc9b6db81a",
14
+ "transform": "Extract trainer.unitok only; retain trained encoder, quantizer, decoder, semantic projection and text tower. Discriminator/training state excluded; original names, dtypes and bytes preserved."
15
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:364bbd3e3f453f19dba447805f347aabdd7abdc7da20186364f5b2961c541b5c
3
+ size 3281477448
native/__init__.py ADDED
File without changes
native/models/__init__.py ADDED
File without changes
native/models/quant.py ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from typing import List, Tuple
3
+ from torch.nn import functional as F
4
+ from torch import distributed as tdist, nn as nn
5
+
6
+ from native.utils import dist
7
+
8
+
9
+ def get_entropy_loss(latent_embed, codebook_embed, inv_entropy_tau):
10
+ E_dist = latent_embed.square().sum(dim=1, keepdim=True) + codebook_embed.square().sum(dim=1, keepdim=False)
11
+ E_dist.addmm_(latent_embed, codebook_embed.T, alpha=-2, beta=1) # E_dist: (N, vocab_size)
12
+ logits = -E_dist.float().mul_(inv_entropy_tau)
13
+ # calc per_sample_entropy
14
+ prob, log_prob = logits.softmax(dim=-1), logits.log_softmax(dim=-1) # both are (N, vocab_size)
15
+ per_sample_entropy = torch.mean((-prob * log_prob).sum(dim=-1))
16
+ # calc codebook_entropy
17
+ avg_prob = prob.mean(dim=0) # (vocab_size,)
18
+ log_avg_prob = torch.log(avg_prob + 1e-7)
19
+ codebook_entropy = (-avg_prob * log_avg_prob).sum()
20
+ # calc entropy_loss
21
+ entropy_loss = per_sample_entropy - codebook_entropy
22
+ return entropy_loss
23
+
24
+
25
+ class NormalizedEmbedding(nn.Embedding):
26
+ def __init__(self, num_embeddings: int, embedding_dim: int):
27
+ super().__init__(num_embeddings=num_embeddings, embedding_dim=embedding_dim)
28
+ # self.norm_scale = nn.Parameter(torch.tensor(0.0, dtype=torch.float32))
29
+
30
+ def forward(self, idx):
31
+ return F.embedding(
32
+ idx, F.normalize(self.weight, dim=1), self.padding_idx, self.max_norm,
33
+ self.norm_type, self.scale_grad_by_freq, self.sparse
34
+ )
35
+
36
+ def get_norm_weight(self):
37
+ return F.normalize(self.weight, dim=1)
38
+
39
+
40
+ class ResConv(nn.Conv2d):
41
+ def __init__(self, embed_dim, quant_resi):
42
+ ks = 3 if quant_resi < 0 else 1
43
+ super().__init__(in_channels=embed_dim, out_channels=embed_dim, kernel_size=ks, stride=1, padding=ks // 2)
44
+ self.resi_ratio = abs(quant_resi)
45
+
46
+ def forward(self, h_BChw):
47
+ return h_BChw.mul(1 - self.resi_ratio) + super().forward(h_BChw).mul_(self.resi_ratio)
48
+
49
+
50
+ class VectorQuantizer(nn.Module):
51
+ def __init__(
52
+ self,
53
+ vocab_size: int,
54
+ vocab_width: int,
55
+ beta: float = 0.25,
56
+ use_entropy_loss=False,
57
+ entropy_temp=0.01,
58
+ ):
59
+ super().__init__()
60
+ self.beta = beta
61
+ self.vocab_size = vocab_size
62
+ self.vocab_width = vocab_width
63
+ self.vocab_usage_record_times: int = 0
64
+ self.register_buffer('vocab_usage', torch.zeros(self.vocab_size))
65
+ self.codebook = NormalizedEmbedding(self.vocab_size, self.vocab_width)
66
+
67
+ self.use_entropy_loss = use_entropy_loss
68
+ self.inv_entropy_tau = 1 / entropy_temp
69
+
70
+ def init_vocab(self, eini: float):
71
+ if eini > 0:
72
+ nn.init.trunc_normal_(self.codebook.weight.data, std=eini)
73
+ elif eini < 0:
74
+ base = self.vocab_width ** -0.5
75
+ base /= 36
76
+ self.codebook.weight.data.uniform_(-abs(eini) * base, abs(eini) * base)
77
+
78
+ def extra_repr(self) -> str:
79
+ return f'beta={self.beta:g}'
80
+
81
+ def forward(self, features):
82
+ B, L, C = features.shape
83
+ features = features.reshape(-1, C)
84
+ features = F.normalize(features, dim=-1).float()
85
+ codebook_embed = self.codebook.get_norm_weight()
86
+ indices = torch.argmax(features.detach() @ codebook_embed.T, dim=1)
87
+ entropy_loss = get_entropy_loss(features, codebook_embed, self.inv_entropy_tau) if self.use_entropy_loss else 0
88
+ features_hat = self.codebook(indices)
89
+
90
+ # calc loss
91
+ vq_loss = F.mse_loss(features_hat.detach(), features).mul_(self.beta) + F.mse_loss(features_hat,
92
+ features.detach())
93
+ features_hat = (features_hat.detach() - features.detach()).add_(features)
94
+
95
+ # update vocab_usage
96
+ prob_per_class_is_chosen = indices.bincount(minlength=self.vocab_size).float()
97
+ handler = tdist.all_reduce(prob_per_class_is_chosen, async_op=True) if (
98
+ self.training and dist.initialized()) else None
99
+ if handler is not None:
100
+ handler.wait()
101
+ prob_per_class_is_chosen /= prob_per_class_is_chosen.sum()
102
+ vocab_usage = (prob_per_class_is_chosen > 0.01 / self.vocab_size).float().mean().mul_(100)
103
+ if self.vocab_usage_record_times == 0:
104
+ self.vocab_usage.copy_(prob_per_class_is_chosen)
105
+ elif self.vocab_usage_record_times < 100:
106
+ self.vocab_usage.mul_(0.9).add_(prob_per_class_is_chosen, alpha=0.1)
107
+ else:
108
+ self.vocab_usage.mul_(0.99).add_(prob_per_class_is_chosen, alpha=0.01)
109
+ self.vocab_usage_record_times += 1
110
+
111
+ return features_hat.view(B, L, C), vq_loss, entropy_loss, vocab_usage
112
+
113
+ def f_to_idx(self, features):
114
+ B, L, C = features.shape
115
+ features = features.reshape(-1, C)
116
+ features = F.normalize(features, dim=-1).float()
117
+ codebook_embed = self.codebook.get_norm_weight().float()
118
+ indices = torch.argmax(features.detach() @ codebook_embed.T, dim=1)
119
+ return indices.view(B, L)
120
+
121
+
122
+ class VectorQuantizerM(nn.Module):
123
+ def __init__(
124
+ self,
125
+ vocab_size,
126
+ vocab_width,
127
+ beta=0.25,
128
+ use_entropy_loss=False,
129
+ entropy_temp=0.01,
130
+ num_codebooks=16
131
+ ):
132
+ super().__init__()
133
+ self.num_codebooks = num_codebooks
134
+ self.codebooks = nn.ModuleList()
135
+ for _ in range(num_codebooks):
136
+ codebook = VectorQuantizer(
137
+ vocab_size=vocab_size // num_codebooks,
138
+ vocab_width=vocab_width // num_codebooks,
139
+ beta=beta,
140
+ use_entropy_loss=use_entropy_loss,
141
+ entropy_temp=entropy_temp,
142
+ )
143
+ self.codebooks.append(codebook)
144
+
145
+ def init_vocab(self, eini: float):
146
+ for codebook in self.codebooks:
147
+ codebook.init_vocab(eini)
148
+
149
+ def f_to_idx(self, features):
150
+ indices = []
151
+ chunk_size = features.shape[-1] // self.num_codebooks
152
+ splited_features = features.split(chunk_size, dim=-1)
153
+ for i, codebook in enumerate(self.codebooks):
154
+ indices.append(codebook.f_to_idx(splited_features[i]))
155
+ indices = torch.stack(indices, dim=1)
156
+ return indices
157
+
158
+ def idx_to_f(self, indices):
159
+ assert indices.shape[1] == self.num_codebooks
160
+ latent_features = []
161
+ for i, codebook in enumerate(self.codebooks):
162
+ sub_indices = indices[:, i].flatten(start_dim=1)
163
+ latent_feature = codebook.codebook(sub_indices)
164
+ latent_features.append(latent_feature)
165
+ latent_features = torch.cat(latent_features, dim=-1)
166
+ return latent_features
167
+
168
+ def forward(self, features):
169
+ latent_features = []
170
+ global_vq_loss = 0.
171
+ global_entropy_loss = 0.
172
+ global_vocab_usage = 0.
173
+ chunk_size = features.shape[-1] // self.num_codebooks
174
+ splited_features = features.split(chunk_size, dim=-1)
175
+ for i, codebook in enumerate(self.codebooks):
176
+ latent_feature, vq_loss, entropy_loss, vocab_usage = codebook(splited_features[i])
177
+ latent_features.append(latent_feature)
178
+ global_vq_loss += vq_loss
179
+ global_entropy_loss += entropy_loss
180
+ global_vocab_usage += vocab_usage
181
+ latent_features = torch.cat(latent_features, dim=-1)
182
+ global_entropy_loss /= self.num_codebooks
183
+ global_vq_loss /= self.num_codebooks
184
+ global_vocab_usage /= self.num_codebooks
185
+ return latent_features, global_vq_loss, global_entropy_loss, global_vocab_usage
native/models/unitok.py ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import timm
2
+ import torch
3
+ import numpy as np
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+ from contextlib import nullcontext
7
+
8
+ from native.models.vitamin import GeGluMlp, ViTaminDecoder
9
+ from native.models.quant import VectorQuantizerM
10
+ from native.models.vqvae import AttnProjection
11
+
12
+
13
+ class UniTok(nn.Module):
14
+ def __init__(self, args):
15
+ super().__init__()
16
+
17
+ self.num_query = args.num_query
18
+
19
+ self.encoder = timm.create_model(
20
+ args.model,
21
+ patch_size=1,
22
+ fc_norm=False,
23
+ drop_rate=0.0,
24
+ num_classes=0,
25
+ global_pool='',
26
+ pos_embed='none',
27
+ class_token=False,
28
+ mlp_layer=GeGluMlp,
29
+ reg_tokens=args.num_query,
30
+ img_size=args.img_size,
31
+ drop_path_rate=args.drop_path,
32
+ )
33
+ self.encoder.pos_embed = nn.Parameter(torch.zeros(1, 1, self.encoder.embed_dim), requires_grad=False)
34
+
35
+ if args.quant_proj == 'linear':
36
+ self.quant_proj = nn.Linear(self.encoder.embed_dim, args.vocab_width)
37
+ elif args.quant_proj == 'attn':
38
+ self.quant_proj = AttnProjection(self.encoder.embed_dim, args.vocab_width, self.encoder.embed_dim // args.vocab_width)
39
+ else:
40
+ raise NotImplementedError
41
+
42
+ self.quantizer = VectorQuantizerM(
43
+ vocab_size=args.vocab_size,
44
+ vocab_width=args.vocab_width,
45
+ beta=args.vq_beta,
46
+ use_entropy_loss=args.le > 0,
47
+ entropy_temp=args.e_temp,
48
+ num_codebooks=args.num_codebooks,
49
+ )
50
+
51
+ if args.quant_proj == 'linear':
52
+ self.post_quant_proj = nn.Linear(args.vocab_width, self.encoder.embed_dim)
53
+ elif args.quant_proj == 'attn':
54
+ self.post_quant_proj = AttnProjection(args.vocab_width, self.encoder.embed_dim, self.encoder.embed_dim // args.vocab_width)
55
+ else:
56
+ raise NotImplementedError
57
+
58
+ self.decoder = ViTaminDecoder(
59
+ args.model,
60
+ num_query=args.num_query,
61
+ img_size=args.img_size,
62
+ drop_path=args.drop_path,
63
+ grad_ckpt=args.grad_ckpt,
64
+ )
65
+
66
+ text_cfg = {
67
+ "width": args.text_width,
68
+ "heads": args.text_heads,
69
+ "layers": args.text_layers,
70
+ "vocab_size": args.text_vocab_size,
71
+ "context_length": args.text_context_length,
72
+ }
73
+ # Exact non-HF default branch of the pinned project's vendored builder.
74
+ from native.open_clip.transformer import TextTransformer
75
+ self.text_encoder = TextTransformer(output_dim=args.embed_dim, **text_cfg)
76
+
77
+ self.fc_norm = nn.LayerNorm(self.encoder.embed_dim, eps=1e-6)
78
+ self.projection = nn.Linear(self.encoder.embed_dim, args.embed_dim)
79
+ self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07))
80
+
81
+ self.context_length = self.text_encoder.context_length
82
+ self.vocab_size = self.text_encoder.vocab_size
83
+ self.maybe_record_function = nullcontext
84
+
85
+ self.text_no_grad = False
86
+ self.encoder.set_grad_checkpointing(args.grad_ckpt)
87
+ self.text_encoder.set_grad_checkpointing(args.grad_ckpt)
88
+
89
+ def forward(self, img, vae_bs, text=None, ret_usages=False):
90
+ img_tokens = self.encoder(img).float()
91
+ with torch.cuda.amp.autocast(enabled=False):
92
+ img_tokens = torch.utils.checkpoint.checkpoint(self.quant_proj, img_tokens, use_reentrant=False)
93
+ img_tokens, vq_loss, entropy_loss, usages = self.quantizer(img_tokens)
94
+ img_tokens = torch.utils.checkpoint.checkpoint(self.post_quant_proj, img_tokens, use_reentrant=False)
95
+ img_rec = self.decoder(img_tokens[:vae_bs]).float()
96
+
97
+ clip_visual = img_tokens.mean(dim=1)
98
+ clip_visual = self.projection(self.fc_norm(clip_visual))
99
+ clip_visual = F.normalize(clip_visual, dim=-1)
100
+ if text is not None:
101
+ clip_text = self.text_encoder(text)
102
+ clip_text = F.normalize(clip_text, dim=-1)
103
+ else:
104
+ clip_text = None
105
+
106
+ output_dict = {
107
+ "img_rec": img_rec,
108
+ "vq_loss": vq_loss,
109
+ "entropy_loss": entropy_loss,
110
+ "codebook_usages": usages,
111
+ "clip_image_features": clip_visual,
112
+ "clip_text_features": clip_text,
113
+ "logit_scale": self.logit_scale.exp()
114
+ }
115
+ return output_dict
116
+
117
+ def encode_image(self, image, normalize: bool = False):
118
+ img_tokens = self.encoder(image)
119
+ img_tokens = self.quant_proj(img_tokens)
120
+ img_indices = self.quantizer.f_to_idx(img_tokens)
121
+ img_tokens = self.quantizer.idx_to_f(img_indices)
122
+ img_tokens = self.post_quant_proj(img_tokens)
123
+ features = img_tokens.mean(dim=1)
124
+ features = self.projection(self.fc_norm(features))
125
+ return F.normalize(features, dim=-1) if normalize else features
126
+
127
+ def encode_text(self, text, normalize: bool = False):
128
+ features = self.text_encoder(text)
129
+ return F.normalize(features, dim=-1) if normalize else features
130
+
131
+ def img_to_idx(self, img):
132
+ features = self.encoder(img).float()
133
+ features = self.quant_proj(features)
134
+ return self.quantizer.f_to_idx(features)
135
+
136
+ def idx_to_img(self, indices):
137
+ features = self.quantizer.idx_to_f(indices)
138
+ features = self.post_quant_proj(features)
139
+ img = self.decoder(features).clamp_(-1, 1)
140
+ return img
141
+
142
+ def img_to_reconstructed_img(self, image) -> torch.Tensor:
143
+ img_tokens = self.encoder(image)
144
+ img_tokens = self.quant_proj(img_tokens)
145
+ img_tokens, _, _, _ = self.quantizer(img_tokens)
146
+ img_tokens = self.post_quant_proj(img_tokens)
147
+ img_rec = self.decoder(img_tokens).clamp_(-1, 1)
148
+ return img_rec
149
+
150
+ def lock_text_tower(self, unlocked_layers: int = 0, freeze_layer_norm: bool = True, unlock_text_proj=False):
151
+ self.text.lock(unlocked_layers, freeze_layer_norm, unlock_text_proj)
152
+ self.text_no_grad = True
153
+
154
+
155
+ if __name__ == '__main__':
156
+ model = timm.create_model(
157
+ 'vitamin_base',
158
+ patch_size=1,
159
+ fc_norm=True,
160
+ drop_rate=0.0,
161
+ num_classes=0,
162
+ global_pool='',
163
+ pos_embed='none',
164
+ class_token=False,
165
+ mlp_layer=GeGluMlp,
166
+ reg_tokens=0,
167
+ img_size=256,
168
+ drop_path_rate=0.1,
169
+ )
170
+ model.pos_embed = nn.Parameter(torch.zeros(1, 1, model.embed_dim), requires_grad=False)
171
+
172
+ model_dict = model.state_dict()
173
+ ckpt_dict = torch.load('ViTamin-B/pytorch_model.bin')
174
+ visual_dict = dict()
175
+ for k, v in ckpt_dict.items():
176
+ if k.startswith('visual.'):
177
+ if 'head' in k or 'pos_embed' in k:
178
+ continue
179
+ new_k = k.replace('visual.trunk.', '')
180
+ visual_dict[new_k] = v
181
+
182
+ model.load_state_dict(visual_dict, strict=False)
183
+ print(set(model_dict.keys()) - set(visual_dict.keys()))
184
+ print(set(visual_dict.keys() - set(model_dict.keys())))
185
+
native/models/vitamin.py ADDED
@@ -0,0 +1,793 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ TODO: FIXME:
3
+ /usr/local/lib/python3.9/dist-packages/torch/autograd/__init__.py:251: UserWarning: Grad strides do not match bucket view strides. This may indicate grad was not created according to the gradient layout contract, or that the param's strides changed since DDP was constructed. This is not an error, but may impair performance.
4
+ grad.sizes() = [256, 1024, 1, 1], strides() = [1024, 1, 1024, 1024]
5
+ bucket_view.sizes() = [256, 1024, 1, 1], strides() = [1024, 1, 1, 1] (Triggered internally at ../torch/csrc/distributed/c10d/reducer.cpp:334.) Variable._execution_engine.run_backward( # Calls into the C++ engine to run the backward pass
6
+ /usr/local/lib/python3.9/dist-packages/torch/autograd/__init__.py:251: UserWarning: Grad strides do not match bucket view strides. This may indicate grad was not created according to the gradient layout contract, or that the param's strides changed since DDP was constructed. This is not an error, but may impair performance.
7
+ grad.sizes() = [256, 1024, 1, 1], strides() = [1024, 1, 1024, 1024]
8
+
9
+ """
10
+
11
+ """ ViTamin
12
+
13
+ Paper: Designing Scalable Vison Models in the Vision-Language Era
14
+
15
+ @misc{chen2023designing,
16
+ title={Designing Scalable Vison Models in the Vision-Language Era},
17
+ author={Jieneng Chen and Qihang Yu and Xiaohui Shen and Alan Yuille and Liang-Cheih Chen},
18
+ year={2023},
19
+ archivePrefix={arXiv},
20
+ primaryClass={cs.CV}
21
+ }
22
+
23
+ Based on Apache 2.0 licensed code at https://github.com/ViTamin/ViTamin
24
+
25
+ Modifications and timm support by Jieneng Chen 2023
26
+
27
+ Reference:
28
+ https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/vision_transformer.py
29
+ https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/vision_transformer_hybrid.py
30
+ """
31
+
32
+ import math
33
+ from dataclasses import dataclass
34
+ from functools import partial
35
+ import torch.nn.functional as F
36
+ from typing import Optional, Tuple, Union
37
+
38
+ import timm
39
+ import torch
40
+ import torch.nn as nn
41
+ from timm.layers import to_2tuple
42
+ from timm.layers.norm_act import _create_act
43
+ from timm.models._builder import build_model_with_cfg
44
+ from timm.models._manipulate import checkpoint_seq, named_apply
45
+ from timm.models._registry import register_model
46
+ from timm.models.layers import DropPath
47
+ from timm.models.layers import create_conv2d, get_norm_act_layer, get_norm_layer, make_divisible
48
+ from timm.models.vision_transformer import VisionTransformer, checkpoint_filter_fn
49
+ from timm.models.vision_transformer_hybrid import HybridEmbed
50
+ from torch.utils.checkpoint import checkpoint
51
+
52
+ from native.utils.config import Args
53
+ DropPath.__repr__ = lambda self: f'{type(self).__name__}(...)'
54
+
55
+
56
+ @dataclass
57
+ class VitConvCfg:
58
+ expand_ratio: float = 4.0
59
+ expand_output: bool = True # calculate expansion channels from output (vs input chs)
60
+ kernel_size: int = 3
61
+ group_size: int = 1 # 1 == depthwise
62
+ pre_norm_act: bool = False # activation after pre-norm
63
+ stride_mode: str = 'dw' # stride done via one of 'pool', '1x1', 'dw'
64
+ pool_type: str = 'avg2'
65
+ downsample_pool_type: str = 'avg2'
66
+ act_layer: str = 'gelu' # stem & stage 1234
67
+ act_layer1: str = 'gelu' # stage 1234
68
+ act_layer2: str = 'gelu' # stage 1234
69
+ norm_layer: str = ''
70
+ norm_layer_cl: str = ''
71
+ norm_eps: Optional[float] = None
72
+ down_shortcut: Optional[bool] = True
73
+ mlp: str = 'mlp'
74
+
75
+ def __post_init__(self):
76
+ # mbconv vs convnext blocks have different defaults, set in post_init to avoid explicit config args
77
+ use_mbconv = True
78
+ if not self.norm_layer:
79
+ self.norm_layer = 'batchnorm2d' if use_mbconv else 'layernorm2d'
80
+ if not self.norm_layer_cl and not use_mbconv:
81
+ self.norm_layer_cl = 'layernorm'
82
+ if self.norm_eps is None:
83
+ self.norm_eps = 1e-5 if use_mbconv else 1e-6
84
+ self.downsample_pool_type = self.downsample_pool_type or self.pool_type
85
+
86
+
87
+ @dataclass
88
+ class VitCfg:
89
+ # embed_dim: Tuple[int, ...] = (96, 192, 384, 768)
90
+ embed_dim: Tuple[Union[int, Tuple[int, ...]], ...] = (96, 192, 384, 768)
91
+ depths: Tuple[Union[int, Tuple[int, ...]], ...] = (2, 3, 5, 2)
92
+ stem_width: int = 64
93
+ conv_cfg: VitConvCfg = None
94
+ weight_init: str = 'vit_eff'
95
+ head_type: str = ""
96
+ stem_type: str = "stem"
97
+ ln2d_permute: bool = True
98
+ # memory_format: str=""
99
+
100
+
101
+ def _init_conv(module, name, scheme=''):
102
+ if isinstance(module, nn.Conv2d):
103
+ fan_out = module.kernel_size[0] * module.kernel_size[1] * module.out_channels
104
+ fan_out //= module.groups
105
+ nn.init.normal_(module.weight, 0, math.sqrt(2.0 / fan_out))
106
+ if module.bias is not None:
107
+ nn.init.zeros_(module.bias)
108
+
109
+
110
+ class Stem(nn.Module):
111
+ def __init__(
112
+ self,
113
+ in_chs: int,
114
+ out_chs: int,
115
+ act_layer: str = 'gelu',
116
+ norm_layer: str = 'layernorm2d',
117
+ norm_eps: float = 1e-6,
118
+ bias: bool = True,
119
+ ):
120
+ super().__init__()
121
+ self.grad_checkpointing=False
122
+ norm_act_layer = partial(get_norm_act_layer(norm_layer, act_layer), eps=norm_eps)
123
+ self.out_chs = out_chs
124
+ self.conv1 = create_conv2d(in_chs, out_chs, 3, stride=2, bias=bias)
125
+ self.norm1 = norm_act_layer(out_chs)
126
+ self.conv2 = create_conv2d(out_chs, out_chs, 3, stride=1, bias=bias)
127
+ named_apply(_init_conv, self)
128
+
129
+ def forward(self, x):
130
+ if self.grad_checkpointing:
131
+ x = checkpoint(self.conv1, x)
132
+ x = self.norm1(x)
133
+ x = checkpoint(self.conv2, x)
134
+ else:
135
+ x = self.conv1(x)
136
+ x = self.norm1(x)
137
+ x = self.conv2(x)
138
+
139
+ return x
140
+
141
+
142
+ class Downsample2d(nn.Module):
143
+ def __init__(
144
+ self,
145
+ dim: int,
146
+ dim_out: int,
147
+ pool_type: str = 'avg2',
148
+ bias: bool = True,
149
+ ):
150
+ super().__init__()
151
+ self.pool = nn.AvgPool2d(kernel_size=3, stride=2, padding=1, count_include_pad=False)
152
+
153
+ if dim != dim_out:
154
+ self.expand = nn.Conv2d(dim, dim_out, 1, bias=bias) # 1x1 conv
155
+ else:
156
+ self.expand = nn.Identity()
157
+
158
+ def forward(self, x):
159
+ x = self.pool(x) # spatial downsample
160
+ x = self.expand(x) # expand chs
161
+ return x
162
+
163
+
164
+ class StridedConv(nn.Module):
165
+ """ downsample 2d as well
166
+ """
167
+ def __init__(
168
+ self,
169
+ kernel_size=3,
170
+ stride=2,
171
+ padding=1,
172
+ in_chans=3,
173
+ embed_dim=768,
174
+ ln2d_permute=True
175
+ ):
176
+ super().__init__()
177
+ self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=kernel_size, stride=stride, padding=padding)
178
+ self.permute = ln2d_permute # TODO: disable
179
+ norm_layer = partial(get_norm_layer('layernorm2d'), eps=1e-6)
180
+ self.norm = norm_layer(in_chans) # affine over C
181
+
182
+ def forward(self, x):
183
+ x = self.norm(x)
184
+ x = self.proj(x)
185
+ return x
186
+
187
+
188
+ class MbConvLNBlock(nn.Module):
189
+ """ Pre-Norm Conv Block - 1x1 - kxk - 1x1, w/ inverted bottleneck (expand)
190
+ """
191
+ def __init__(
192
+ self,
193
+ in_chs: int,
194
+ out_chs: int,
195
+ stride: int = 1,
196
+ drop_path: float = 0.,
197
+ kernel_size: int = 3,
198
+ norm_layer: str = 'layernorm2d',
199
+ norm_eps: float = 1e-6,
200
+ act_layer: str = 'gelu',
201
+ expand_ratio: float = 4.0,
202
+ ):
203
+ super(MbConvLNBlock, self).__init__()
204
+ self.stride, self.in_chs, self.out_chs = stride, in_chs, out_chs
205
+ mid_chs = make_divisible(out_chs * expand_ratio)
206
+ prenorm_act_layer = partial(get_norm_act_layer(norm_layer, act_layer), eps=norm_eps)
207
+
208
+ if stride == 2:
209
+ self.shortcut = Downsample2d(in_chs, out_chs, pool_type='avg', bias=True)
210
+ elif in_chs != out_chs:
211
+ self.shortcut = nn.Conv2d(in_chs, out_chs, 1, bias=True)
212
+ else:
213
+ self.shortcut = nn.Identity()
214
+
215
+ self.pre_norm = prenorm_act_layer(in_chs, apply_act=False)
216
+ self.down = nn.Identity()
217
+ self.conv1_1x1 = create_conv2d(in_chs, mid_chs, 1, stride=1, bias=True)
218
+ self.act1 = _create_act(act_layer, inplace=True)
219
+ self.act2 = _create_act(act_layer, inplace=True)
220
+
221
+ self.conv2_kxk = create_conv2d(mid_chs, mid_chs, kernel_size, stride=stride, dilation=1, groups=mid_chs, bias=True)
222
+ self.conv3_1x1 = create_conv2d(mid_chs, out_chs, 1, bias=True)
223
+ self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
224
+
225
+ def init_weights(self, scheme=''):
226
+ named_apply(partial(_init_conv, scheme=scheme), self)
227
+
228
+ def forward(self, x):
229
+ shortcut = self.shortcut(x)
230
+
231
+ x = self.pre_norm(x)
232
+ x = self.down(x) # nn.Identity()
233
+
234
+ # 1x1 expansion conv & act
235
+ x = self.conv1_1x1(x)
236
+ x = self.act1(x)
237
+
238
+ # (strided) depthwise 3x3 conv & act
239
+ x = self.conv2_kxk(x)
240
+ x = self.act2(x)
241
+
242
+ # 1x1 linear projection to output width
243
+ x = self.conv3_1x1(x)
244
+ x = self.drop_path(x) + shortcut
245
+
246
+ return x
247
+
248
+
249
+ class MbConvStages(nn.Module):
250
+ """ MobileConv for stage 1 and stage 2 of ViTamin
251
+ """
252
+ def __init__(
253
+ self,
254
+ cfg: VitCfg,
255
+ img_size: Union[int, Tuple[int, int]] = 224, # place holder
256
+ in_chans: int = 3,
257
+ ):
258
+ super().__init__()
259
+ self.grad_checkpointing = False
260
+ self.stem = Stem(
261
+ in_chs=in_chans,
262
+ out_chs=cfg.stem_width,
263
+ )
264
+ stages = []
265
+ self.num_stages = len(cfg.embed_dim)
266
+ for s, dim in enumerate(cfg.embed_dim[:2]): # stage
267
+ blocks = []
268
+ stage_in_chs = cfg.embed_dim[s-1] if s>0 else cfg.stem_width
269
+ for d in range(cfg.depths[s]):
270
+ blocks += [MbConvLNBlock(
271
+ in_chs = stage_in_chs if d==0 else dim,
272
+ out_chs = dim,
273
+ stride = 2 if d == 0 else 1,
274
+ # cfg = cfg.conv_cfg,
275
+ )]
276
+ blocks = nn.Sequential(*blocks)
277
+ stages += [blocks]
278
+
279
+ self.stages = nn.ModuleList(stages)
280
+ self.pool = StridedConv(
281
+ stride=2,
282
+ in_chans=cfg.embed_dim[1],
283
+ embed_dim=cfg.embed_dim[2]
284
+ )
285
+
286
+ def forward(self, x):
287
+ x = self.stem(x)
288
+ if self.grad_checkpointing and not torch.jit.is_scripting():
289
+ for stage in self.stages:
290
+ x = checkpoint_seq(stage, x)
291
+ x = checkpoint(self.pool, x)
292
+ else:
293
+ for stage in self.stages:
294
+ x = stage(x)
295
+ x = self.pool(x)
296
+
297
+ return x
298
+
299
+
300
+ class GeGluMlp(nn.Module):
301
+ def __init__(
302
+ self,
303
+ in_features,
304
+ hidden_features,
305
+ act_layer = None,
306
+ drop = 0.0,
307
+ ):
308
+ super().__init__()
309
+ norm_layer = partial(get_norm_layer('layernorm'), eps=1e-6)
310
+ self.norm = norm_layer(in_features)
311
+ self.act = nn.GELU(approximate='tanh')
312
+ self.w0 = nn.Linear(in_features, hidden_features)
313
+ self.w1 = nn.Linear(in_features, hidden_features)
314
+ self.w2 = nn.Linear(hidden_features, in_features)
315
+
316
+ def forward(self, x):
317
+ x = self.norm(x)
318
+ x = self.act(self.w0(x)) * self.w1(x)
319
+ x = self.w2(x)
320
+ return x
321
+
322
+
323
+ class HybridEmbed(nn.Module):
324
+ """ CNN Feature Map Embedding
325
+ Extract feature map from CNN, flatten, project to embedding dim.
326
+ """
327
+ def __init__(
328
+ self,
329
+ backbone,
330
+ img_size=256,
331
+ patch_size=1,
332
+ feature_size=None,
333
+ in_chans=3,
334
+ embed_dim=1024,
335
+ bias=True,
336
+ dynamic_img_pad=False,
337
+ ):
338
+ super().__init__()
339
+ assert isinstance(backbone, nn.Module)
340
+ img_size = to_2tuple(img_size)
341
+ patch_size = to_2tuple(patch_size)
342
+ self.img_size = img_size
343
+ self.patch_size = patch_size
344
+ self.backbone = backbone
345
+ with torch.no_grad():
346
+ training = backbone.training
347
+ if training:
348
+ backbone.eval()
349
+ o = self.backbone(torch.zeros(1, in_chans, img_size[0], img_size[1]))
350
+ if isinstance(o, (list, tuple)):
351
+ o = o[-1] # last feature if backbone outputs list/tuple of features
352
+ feature_size = o.shape[-2:]
353
+ feature_dim = o.shape[1]
354
+ backbone.train(training)
355
+
356
+ assert feature_size[0] % patch_size[0] == 0 and feature_size[1] % patch_size[1] == 0
357
+ self.grid_size = (feature_size[0] // patch_size[0], feature_size[1] // patch_size[1])
358
+ self.num_patches = self.grid_size[0] * self.grid_size[1]
359
+ self.proj = nn.Identity()
360
+
361
+ def forward(self, x):
362
+ x = self.backbone(x)
363
+ if isinstance(x, (list, tuple)):
364
+ x = x[-1] # last feature if backbone outputs list/tuple of features
365
+ x = self.proj(x)
366
+ x = x.flatten(2).transpose(1, 2)
367
+ return x
368
+
369
+
370
+ class Upsample2d(nn.Module):
371
+ def __init__(self, dim, dim_out):
372
+ super().__init__()
373
+ self.conv = torch.nn.Conv2d(dim, dim_out, kernel_size=3, stride=1, padding=1)
374
+
375
+ def forward(self, x):
376
+ x = F.interpolate(x, scale_factor=2, mode='nearest')
377
+ x = self.conv(x)
378
+ return x
379
+
380
+
381
+ class InvMbConvLNBlock(nn.Module):
382
+ """ Pre-Norm Conv Block - 1x1 - kxk - 1x1, w/ inverted bottleneck (expand)
383
+ """
384
+ def __init__(
385
+ self,
386
+ in_chs: int,
387
+ out_chs: int,
388
+ stride: int = 1,
389
+ drop_path: float = 0.,
390
+ kernel_size: int = 3,
391
+ norm_layer: str = 'layernorm2d',
392
+ norm_eps: float = 1e-6,
393
+ act_layer: str = 'gelu',
394
+ expand_ratio: float = 4.0,
395
+ ):
396
+ super().__init__()
397
+ self.stride, self.in_chs, self.out_chs = stride, in_chs, out_chs
398
+ mid_chs = make_divisible(out_chs * expand_ratio)
399
+ prenorm_act_layer = partial(get_norm_act_layer(norm_layer, act_layer), eps=norm_eps)
400
+
401
+ if stride == 2:
402
+ self.shortcut = Upsample2d(in_chs, out_chs)
403
+ elif in_chs != out_chs:
404
+ self.shortcut = nn.Conv2d(in_chs, out_chs, 1, bias=True)
405
+ else:
406
+ self.shortcut = nn.Identity()
407
+
408
+ self.pre_norm = prenorm_act_layer(in_chs, apply_act=False)
409
+
410
+ self.conv1_1x1 = create_conv2d(in_chs, mid_chs, 1, stride=1, bias=True)
411
+ self.act1 = _create_act(act_layer, inplace=True)
412
+ self.act2 = _create_act(act_layer, inplace=True)
413
+
414
+ self.up = Upsample2d(mid_chs, mid_chs) if stride == 2 else nn.Identity()
415
+ self.conv2_kxk = create_conv2d(mid_chs, mid_chs, kernel_size, stride=1, dilation=1, groups=mid_chs, bias=True)
416
+ self.conv3_1x1 = create_conv2d(mid_chs, out_chs, 1, bias=True)
417
+ self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
418
+
419
+ def init_weights(self, scheme=''):
420
+ named_apply(partial(_init_conv, scheme=scheme), self)
421
+
422
+ def forward(self, x):
423
+ shortcut = self.shortcut(x)
424
+ x = self.pre_norm(x)
425
+
426
+ # 1x1 expansion conv & act
427
+ x = self.conv1_1x1(x)
428
+ x = self.act1(x)
429
+ x = self.up(x)
430
+
431
+ # (strided) depthwise 3x3 conv & act
432
+ x = self.conv2_kxk(x)
433
+ x = self.act2(x)
434
+
435
+ # 1x1 linear projection to output width
436
+ x = self.conv3_1x1(x)
437
+ x = self.drop_path(x) + shortcut
438
+
439
+ return x
440
+
441
+
442
+ class InvStem(nn.Module):
443
+ def __init__(
444
+ self,
445
+ in_chs: int,
446
+ out_chs: int,
447
+ act_layer: str = 'gelu',
448
+ norm_layer: str = 'layernorm2d',
449
+ norm_eps: float = 1e-6,
450
+ bias: bool = True,
451
+ ):
452
+ super().__init__()
453
+ self.grad_checkpointing=False
454
+ norm_act_layer = partial(get_norm_act_layer(norm_layer, act_layer), eps=norm_eps)
455
+ self.out_chs = out_chs
456
+ self.conv1 = Upsample2d(in_chs, in_chs)
457
+ self.norm1 = norm_act_layer(in_chs)
458
+ self.conv2 = create_conv2d(in_chs, out_chs, 3, stride=1, bias=bias)
459
+ named_apply(_init_conv, self)
460
+
461
+ def forward(self, x):
462
+ if self.grad_checkpointing:
463
+ x = checkpoint(self.conv1, x)
464
+ x = self.norm1(x)
465
+ x = checkpoint(self.conv2, x)
466
+ else:
467
+ x = self.conv1(x)
468
+ x = self.norm1(x)
469
+ x = self.conv2(x)
470
+
471
+ return x
472
+
473
+
474
+ class ViTaminDecoder(nn.Module):
475
+ def __init__(
476
+ self,
477
+ model,
478
+ num_query=0,
479
+ img_size=256,
480
+ drop_path=0.,
481
+ depths=(4, 2),
482
+ grad_ckpt=False,
483
+ ):
484
+ super().__init__()
485
+
486
+ self.num_query = num_query
487
+ vit = timm.create_model(
488
+ model,
489
+ fc_norm=False,
490
+ patch_size=1,
491
+ drop_rate=0.0,
492
+ num_classes=0,
493
+ global_pool='',
494
+ pos_embed='none',
495
+ mlp_layer=GeGluMlp,
496
+ class_token=False,
497
+ reg_tokens=num_query,
498
+ img_size=img_size,
499
+ drop_path_rate=drop_path,
500
+ )
501
+ self.blocks = vit.blocks
502
+ self.norm_pre = vit.norm_pre
503
+ self.norm = vit.norm
504
+
505
+ embed_dims = {
506
+ 'vitamin_base': (768, 256, 128),
507
+ 'vitamin_large': (1024, 320, 160)
508
+ }[model]
509
+ self.up_conv1 = Upsample2d(embed_dims[0], embed_dims[1])
510
+ self.up_conv2 = nn.Sequential(*[
511
+ InvMbConvLNBlock(
512
+ in_chs=embed_dims[1],
513
+ out_chs=embed_dims[1],
514
+ stride=2 if d == 0 else 1)
515
+ for d in range(depths[0])]
516
+ )
517
+ self.up_conv3 = nn.Sequential(*[
518
+ InvMbConvLNBlock(
519
+ in_chs=embed_dims[1] if d == 0 else embed_dims[2],
520
+ out_chs=embed_dims[2],
521
+ stride=2 if d == 0 else 1)
522
+ for d in range(depths[1])]
523
+ )
524
+ self.up_conv4 = InvStem(in_chs=embed_dims[2], out_chs=3)
525
+
526
+ self.grad_ckpt = grad_ckpt
527
+
528
+ def get_last_param(self):
529
+ return self.up_conv4.conv2.weight
530
+
531
+ def forward(self, x):
532
+ B, L, C = x.shape
533
+ H = W = int((L-self.num_query) ** 0.5)
534
+ x = self.norm_pre(x)
535
+ if self.grad_ckpt:
536
+ x = checkpoint_seq(self.blocks, x)
537
+ x = x[:, self.num_query:, :]
538
+ x = self.norm(x)
539
+ x = x.view(B, H, W, C).permute(0, 3, 1, 2)
540
+ x = checkpoint(self.up_conv1, x)
541
+ x = checkpoint_seq(self.up_conv2, x)
542
+ x = checkpoint_seq(self.up_conv3, x)
543
+ else:
544
+ x = self.blocks(x)
545
+ x = x[:, self.num_query:, :]
546
+ x = self.norm(x)
547
+ x = x.view(B, H, W, C).permute(0, 3, 1, 2)
548
+ x = self.up_conv1(x)
549
+ x = self.up_conv2(x)
550
+ x = self.up_conv3(x)
551
+ x = self.up_conv4(x)
552
+ return x
553
+
554
+
555
+ def _create_vision_transformer(variant, pretrained=False, grad_ckpt=False, **kwargs) -> VisionTransformer:
556
+ if kwargs.get('features_only', None):
557
+ raise RuntimeError('features_only not implemented for Vision Transformer models.')
558
+
559
+ if 'flexi' in variant:
560
+ # FIXME Google FlexiViT pretrained models have a strong preference for bilinear patch / embed
561
+ # interpolation, other pretrained models resize better w/ anti-aliased bicubic interpolation.
562
+ _filter_fn = partial(checkpoint_filter_fn, interpolation='bilinear', antialias=False)
563
+ else:
564
+ _filter_fn = checkpoint_filter_fn
565
+
566
+ return build_model_with_cfg(
567
+ VisionTransformer,
568
+ variant,
569
+ pretrained,
570
+ pretrained_filter_fn=_filter_fn,
571
+ **kwargs,
572
+ )
573
+
574
+
575
+ def _create_vision_transformer_hybrid(variant, backbone, pretrained=False, **kwargs):
576
+ embed_layer = partial(HybridEmbed, backbone=backbone)
577
+ kwargs.setdefault('patch_size', 1) # default patch size for hybrid models if not set
578
+ return _create_vision_transformer(variant, pretrained=pretrained, embed_layer=embed_layer, **kwargs)
579
+
580
+
581
+ @register_model
582
+ def vitamin_small(pretrained=False, **kwargs) -> VisionTransformer:
583
+ stage_1_2 = MbConvStages(cfg=VitCfg(
584
+ embed_dim=(64, 128, 384),
585
+ depths=(2, 4, 1),
586
+ stem_width=64,
587
+ conv_cfg = VitConvCfg(
588
+ norm_layer='layernorm2d',
589
+ norm_eps=1e-6,
590
+ ),
591
+ head_type='1d',
592
+ ),
593
+ )
594
+ stage3_args = dict(embed_dim=384, depth=14, num_heads=6, mlp_layer=GeGluMlp, mlp_ratio=2., class_token=False, global_pool='avg')
595
+ model = _create_vision_transformer_hybrid('vitamin_small', backbone=stage_1_2, pretrained=pretrained, **dict(stage3_args, **kwargs))
596
+ return model
597
+
598
+
599
+ @register_model
600
+ def vitamin_base(pretrained=False, **kwargs) -> VisionTransformer:
601
+ stage_1_2 = MbConvStages(cfg=VitCfg(
602
+ embed_dim=(128, 256, 768),
603
+ depths=(2, 4, 1),
604
+ stem_width=128,
605
+ conv_cfg = VitConvCfg(
606
+ norm_layer='layernorm2d',
607
+ norm_eps=1e-6,
608
+ ),
609
+ head_type='1d',
610
+ ),
611
+ )
612
+ stage3_args = dict(embed_dim=768, depth=14, num_heads=12, mlp_layer=GeGluMlp, mlp_ratio=2., class_token=False, global_pool='avg')
613
+ model = _create_vision_transformer_hybrid('vitamin_base', backbone=stage_1_2, pretrained=pretrained, **dict(stage3_args, **kwargs))
614
+ return model
615
+
616
+
617
+ @register_model
618
+ def vitamin_base_256(pretrained=False, **kwargs) -> VisionTransformer:
619
+ stage_1_2 = MbConvStages(cfg=VitCfg(
620
+ embed_dim=(128, 256, 768),
621
+ depths=(2, 4, 1),
622
+ stem_width=128,
623
+ conv_cfg = VitConvCfg(
624
+ norm_layer='layernorm2d',
625
+ norm_eps=1e-6,
626
+ ),
627
+ head_type='1d',
628
+ ),
629
+ )
630
+ stage3_args = dict(img_size=256, embed_dim=768, depth=14, num_heads=12, mlp_layer=GeGluMlp, mlp_ratio=2., class_token=False, global_pool='avg')
631
+ model = _create_vision_transformer_hybrid('vitamin_base_256', backbone=stage_1_2, pretrained=pretrained, **dict(stage3_args, **kwargs))
632
+ return model
633
+
634
+
635
+ @register_model
636
+ def vitamin_large(pretrained=False, **kwargs) -> VisionTransformer:
637
+ stage_1_2 = MbConvStages(cfg=VitCfg(
638
+ embed_dim=(160, 320, 1024),
639
+ depths=(2, 4, 1),
640
+ stem_width=160,
641
+ conv_cfg = VitConvCfg(
642
+ norm_layer='layernorm2d',
643
+ norm_eps=1e-6,
644
+ ),
645
+ head_type='1d',
646
+ ),
647
+ )
648
+ stage3_args = dict(embed_dim=1024, depth=31, num_heads=16, mlp_layer=GeGluMlp, mlp_ratio=2., class_token=False, global_pool='avg')
649
+ model = _create_vision_transformer_hybrid(
650
+ 'vitamin_large', backbone=stage_1_2, pretrained=pretrained, **dict(stage3_args, **kwargs))
651
+ return model
652
+
653
+ # @register_model
654
+ # def vitamin_large_256(pretrained=False, **kwargs) -> VisionTransformer:
655
+ # backbone = MbConvStages(cfg=VitCfg(
656
+ # embed_dim=(160, 320, 1024),
657
+ # depths=(2, 4, 1),
658
+ # stem_width=160,
659
+ # conv_cfg = VitConvCfg(
660
+ # norm_layer='layernorm2d',
661
+ # norm_eps=1e-6,
662
+ # ),
663
+ # head_type='1d',
664
+ # ),
665
+ # )
666
+ # model_args = dict(img_size=256, embed_dim=1024, depth=31, num_heads=16, mlp_layer=GeGluMlp, mlp_ratio=2., class_token=False, global_pool='avg')
667
+ # model = _create_vision_transformer_hybrid(
668
+ # 'vitamin_large_256', backbone=backbone, pretrained=pretrained, **dict(model_args, **kwargs))
669
+ # return model
670
+
671
+ # @register_model
672
+ # def vitamin_large_336(pretrained=False, **kwargs) -> VisionTransformer:
673
+ # backbone = MbConvStages(cfg=VitCfg(
674
+ # embed_dim=(160, 320, 1024),
675
+ # depths=(2, 4, 1),
676
+ # stem_width=160,
677
+ # conv_cfg = VitConvCfg(
678
+ # norm_layer='layernorm2d',
679
+ # norm_eps=1e-6,
680
+ # ),
681
+ # head_type='1d',
682
+ # ),
683
+ # )
684
+ # model_args = dict(img_size=336, embed_dim=1024, depth=31, num_heads=16, mlp_layer=GeGluMlp, mlp_ratio=2., class_token=False, global_pool='avg')
685
+ # model = _create_vision_transformer_hybrid(
686
+ # 'vitamin_large_336', backbone=backbone, pretrained=pretrained, **dict(model_args, **kwargs))
687
+ # return model
688
+
689
+ # @register_model
690
+ # def vitamin_large_384(pretrained=False, **kwargs) -> VisionTransformer:
691
+ # backbone = MbConvStages(cfg=VitCfg(
692
+ # embed_dim=(160, 320, 1024),
693
+ # depths=(2, 4, 1),
694
+ # stem_width=160,
695
+ # conv_cfg = VitConvCfg(
696
+ # norm_layer='layernorm2d',
697
+ # norm_eps=1e-6,
698
+ # ),
699
+ # head_type='1d',
700
+ # ),
701
+ # )
702
+ # model_args = dict(img_size=384, embed_dim=1024, depth=31, num_heads=16, mlp_layer=GeGluMlp, mlp_ratio=2., class_token=False, global_pool='avg')
703
+ # model = _create_vision_transformer_hybrid(
704
+ # 'vitamin_large_384', backbone=backbone, pretrained=pretrained, **dict(model_args, **kwargs))
705
+ # return model
706
+
707
+ # @register_model
708
+ # def vitamin_xlarge_256(pretrained=False, **kwargs) -> VisionTransformer:
709
+ # backbone = MbConvStages(cfg=VitCfg(
710
+ # embed_dim=(192, 384, 1152),
711
+ # depths=(2, 4, 1),
712
+ # stem_width=192,
713
+ # conv_cfg = VitConvCfg(
714
+ # norm_layer='layernorm2d',
715
+ # norm_eps=1e-6,
716
+ # ),
717
+ # head_type='1d',
718
+ # ),
719
+ # )
720
+ # model_args = dict(img_size=256, embed_dim=1152, depth=32, num_heads=16, mlp_layer=GeGluMlp, mlp_ratio=2., class_token=False, global_pool='avg')
721
+ # model = _create_vision_transformer_hybrid(
722
+ # 'vitamin_xlarge_256', backbone=backbone, pretrained=pretrained, **dict(model_args, **kwargs))
723
+ # return model
724
+
725
+ # @register_model
726
+ # def vitamin_xlarge_336(pretrained=False, **kwargs) -> VisionTransformer:
727
+ # backbone = MbConvStages(cfg=VitCfg(
728
+ # embed_dim=(192, 384, 1152),
729
+ # depths=(2, 4, 1),
730
+ # stem_width=192,
731
+ # conv_cfg = VitConvCfg(
732
+ # norm_layer='layernorm2d',
733
+ # norm_eps=1e-6,
734
+ # ),
735
+ # head_type='1d',
736
+ # ),
737
+ # )
738
+ # model_args = dict(img_size=336, embed_dim=1152, depth=32, num_heads=16, mlp_layer=GeGluMlp, mlp_ratio=2., class_token=False, global_pool='avg')
739
+ # model = _create_vision_transformer_hybrid(
740
+ # 'vitamin_xlarge_256', backbone=backbone, pretrained=pretrained, **dict(model_args, **kwargs))
741
+ # return model
742
+
743
+ # @register_model
744
+ # def vitamin_xlarge_384(pretrained=False, **kwargs) -> VisionTransformer:
745
+ # backbone = MbConvStages(cfg=VitCfg(
746
+ # embed_dim=(192, 384, 1152),
747
+ # depths=(2, 4, 1),
748
+ # stem_width=192,
749
+ # conv_cfg = VitConvCfg(
750
+ # norm_layer='layernorm2d',
751
+ # norm_eps=1e-6,
752
+ # ),
753
+ # head_type='1d',
754
+ # ),
755
+ # )
756
+ # model_args = dict(img_size=384, embed_dim=1152, depth=32, num_heads=16, mlp_layer=GeGluMlp, mlp_ratio=2., class_token=False, global_pool='avg')
757
+ # model = _create_vision_transformer_hybrid(
758
+ # 'vitamin_xlarge_384', backbone=backbone, pretrained=pretrained, **dict(model_args, **kwargs))
759
+ # return model
760
+
761
+
762
+ def count_params(model: nn.Module):
763
+ return sum([m.numel() for m in model.parameters()])
764
+
765
+
766
+ def count_stage_params(model: nn.Module, prefix='none'):
767
+ collections = []
768
+ for name, m in model.named_parameters():
769
+ print(name)
770
+ if name.startswith(prefix):
771
+ collections.append(m.numel())
772
+ return sum(collections)
773
+
774
+
775
+ if __name__ == "__main__":
776
+ # ViTaminDecoder('vitamin_base', img_size=256, patch_size=16)
777
+ # model = timm.create_model(
778
+ # 'vitamin_base',
779
+ # fc_norm=True,
780
+ # drop_rate=0.0,
781
+ # num_classes=0,
782
+ # global_pool='',
783
+ # mlp_layer=GeGluMlp,
784
+ # class_token=False,
785
+ # reg_tokens=32,
786
+ # img_size=256,
787
+ # patch_size=1,
788
+ # drop_path_rate=0.1,
789
+ # )
790
+ # print(model.has_class_token)
791
+ # print(model.num_prefix_tokens)
792
+ # print(model.pos_embed.shape)
793
+ Stem(64, 64)
native/models/vqvae.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import timm
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from contextlib import nullcontext
6
+ from torch.nn.functional import scaled_dot_product_attention
7
+
8
+ from native.models.quant import VectorQuantizerM
9
+ from native.models.vitamin import ViTaminDecoder, GeGluMlp
10
+
11
+
12
+ class PlainAttention(nn.Module):
13
+ def __init__(self, in_dim, out_dim, num_heads):
14
+ super().__init__()
15
+ if in_dim > out_dim:
16
+ # assert in_dim // num_heads == out_dim
17
+ self.head_dim = in_dim // num_heads
18
+ self.qkv = nn.Linear(in_dim, in_dim * 3, bias=False)
19
+ self.q_bias = nn.Parameter(torch.zeros(in_dim))
20
+ self.v_bias = nn.Parameter(torch.zeros(in_dim))
21
+ self.register_buffer('zero_k_bias', torch.zeros(in_dim))
22
+ else:
23
+ # assert out_dim // num_heads == in_dim
24
+ self.head_dim = out_dim // num_heads
25
+ self.qkv = nn.Linear(in_dim, out_dim * 3, bias=False)
26
+ self.q_bias = nn.Parameter(torch.zeros(out_dim))
27
+ self.v_bias = nn.Parameter(torch.zeros(out_dim))
28
+ self.register_buffer('zero_k_bias', torch.zeros(out_dim))
29
+
30
+ self.in_dim = in_dim
31
+ self.out_dim = out_dim
32
+ self.num_heads = num_heads
33
+ self.scale = self.head_dim ** -0.5
34
+ self.proj = nn.Linear(out_dim, out_dim)
35
+
36
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
37
+ B, N, C = x.shape
38
+ qkv = F.linear(input=x, weight=self.qkv.weight, bias=torch.cat((self.q_bias, self.zero_k_bias, self.v_bias)))
39
+ q, k, v = qkv.reshape(B, N, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4).unbind(0)
40
+
41
+ x = scaled_dot_product_attention(q, k, v)
42
+
43
+ if self.in_dim > self.out_dim:
44
+ x = torch.mean(x, dim=1)
45
+ if self.in_dim // self.num_heads != self.out_dim:
46
+ x = nn.functional.adaptive_avg_pool1d(x, self.out_dim)
47
+ else:
48
+ x = x.transpose(1, 2).reshape(B, N, -1)
49
+ x = self.proj(x)
50
+ return x
51
+
52
+
53
+ class AttnProjection(nn.Module):
54
+ def __init__(self, in_dim, out_dim, num_heads, norm_layer=nn.LayerNorm, mlp_ratio=2):
55
+ super().__init__()
56
+ assert out_dim % in_dim == 0 or in_dim % out_dim == 0
57
+ self.in_dim = in_dim
58
+ self.out_dim = out_dim
59
+ self.norm1 = norm_layer(in_dim)
60
+ self.attn = PlainAttention(in_dim, out_dim, num_heads)
61
+ self.proj = nn.Linear(in_dim, out_dim)
62
+ self.norm3 = norm_layer(in_dim)
63
+
64
+ self.norm2 = norm_layer(out_dim)
65
+ hidden_dim = int(out_dim * mlp_ratio)
66
+ self.mlp = GeGluMlp(
67
+ in_features=out_dim,
68
+ hidden_features=hidden_dim
69
+ )
70
+
71
+ def forward(self, x):
72
+ x = self.proj(self.norm3(x)) + self.attn(self.norm1(x))
73
+ x = x + self.mlp(self.norm2(x))
74
+ return x
75
+
76
+
77
+ class VQVAE(nn.Module):
78
+ def __init__(self, args):
79
+ super().__init__()
80
+
81
+ # 1. build encoder
82
+ self.encoder = timm.create_model(
83
+ args.model,
84
+ patch_size=1,
85
+ fc_norm=True,
86
+ drop_rate=0.0,
87
+ num_classes=0,
88
+ global_pool='',
89
+ pos_embed='none',
90
+ class_token=False,
91
+ mlp_layer=GeGluMlp,
92
+ img_size=args.img_size,
93
+ drop_path_rate=args.drop_path,
94
+ )
95
+ self.encoder.set_grad_checkpointing(args.grad_ckpt)
96
+
97
+ # 2. build conv before quant
98
+ if args.quant_proj == 'linear':
99
+ self.quant_proj = nn.Linear(self.encoder.embed_dim, args.vocab_width)
100
+ elif args.quant_proj == 'attn':
101
+ self.quant_proj = AttnProjection(self.encoder.embed_dim, args.vocab_width, args.num_codebooks)
102
+ else:
103
+ raise NotImplementedError
104
+
105
+ # 3. build quant
106
+ self.quantize = VectorQuantizerM(
107
+ vocab_size=args.vocab_size,
108
+ vocab_width=args.vocab_width,
109
+ beta=args.vq_beta,
110
+ use_entropy_loss=args.le > 0,
111
+ entropy_temp=args.e_temp,
112
+ num_codebooks=args.num_codebooks,
113
+ )
114
+
115
+ # 4. build conv after quant
116
+ if args.quant_proj == 'linear':
117
+ self.post_quant_proj = nn.Linear(args.vocab_width, self.encoder.embed_dim)
118
+ elif args.quant_proj == 'attn':
119
+ self.post_quant_proj = AttnProjection(args.vocab_width, self.encoder.embed_dim, args.num_codebooks)
120
+ else:
121
+ raise NotImplementedError
122
+
123
+ # 5. build decoder
124
+ self.decoder = ViTaminDecoder(
125
+ args.model,
126
+ depths=(4, 2),
127
+ img_size=args.img_size,
128
+ drop_path=args.drop_path,
129
+ grad_ckpt=args.grad_ckpt
130
+ )
131
+
132
+ self.maybe_record_function = nullcontext
133
+
134
+ def forward(self, img):
135
+ features = self.encoder(img).float()
136
+ with torch.cuda.amp.autocast(enabled=False):
137
+ features = self.quant_proj(features)
138
+ quant_out = self.quantize(features)
139
+ features, vq_loss, entropy_loss, usages = quant_out
140
+ features = self.post_quant_proj(features)
141
+ rec_img = self.decoder(features).float()
142
+ return rec_img, vq_loss, entropy_loss, usages
143
+
144
+ def img_to_idx(self, img):
145
+ features = self.encoder(img).float()
146
+ features = self.quant_proj(features)
147
+ return self.quantize.f_to_idx(features)
148
+
149
+ def idx_to_img(self, indices):
150
+ features = self.quantize.idx_to_f(indices)
151
+ features = self.post_quant_proj(features)
152
+ img = self.decoder(features).clamp_(-1, 1)
153
+ return img
154
+
155
+ def img_to_reconstructed_img(self, img) -> torch.Tensor:
156
+ features = self.encoder(img).float()
157
+ with torch.cuda.amp.autocast(enabled=False):
158
+ features = self.quant_proj(features)
159
+ quant_out = self.quantize(features)
160
+ features, _, _, _ = quant_out
161
+ features = self.post_quant_proj(features)
162
+ rec_img = self.decoder(features).float().clamp_(-1, 1)
163
+ return rec_img
164
+
165
+
166
+ if __name__ == '__main__':
167
+ for clz in (nn.Linear, nn.LayerNorm, nn.BatchNorm2d, nn.SyncBatchNorm, nn.Conv1d, nn.Conv2d, nn.ConvTranspose1d,
168
+ nn.ConvTranspose2d):
169
+ setattr(clz, 'reset_parameters', lambda self: None)
170
+
171
+ cnn = VQVAE(channel_num=64, vocab_norm=False)
172
+ from models import init_weights
173
+
174
+ init_weights(cnn, -0.5)
175
+ torch.save(cnn.state_dict(), r'C:\Users\16333\Desktop\PyCharm\vlip\local_output\cnn.pth')
native/open_clip/__init__.py ADDED
File without changes
native/open_clip/source_model.py ADDED
@@ -0,0 +1,599 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ CLIP Model
2
+
3
+ Adapted from https://github.com/openai/CLIP. Originally MIT License, Copyright (c) 2021 OpenAI.
4
+ """
5
+ from dataclasses import dataclass
6
+ import logging
7
+ import math
8
+ from typing import Optional, Tuple, Union
9
+
10
+ import numpy as np
11
+ import torch
12
+ import torch.nn.functional as F
13
+ from torch import nn
14
+ from torch.utils.checkpoint import checkpoint
15
+
16
+ from .hf_model import HFTextEncoder
17
+ from .modified_resnet import ModifiedResNet
18
+ from .transformer import LayerNormFp32, LayerNorm, QuickGELU, Attention, VisionTransformer, TextTransformer
19
+ from .utils import to_2tuple
20
+ from .timm_model import TimmModel
21
+
22
+
23
+ import time
24
+
25
+
26
+ @dataclass
27
+ class CLIPVisionCfg:
28
+ layers: Union[Tuple[int, int, int, int], int] = 12
29
+ width: int = 768
30
+ head_width: int = 64
31
+ mlp_ratio: float = 4.0
32
+ patch_size: int = 16
33
+ image_size: Union[Tuple[int, int], int] = 224
34
+
35
+ ls_init_value: Optional[float] = None # layer scale initial value
36
+ patch_dropout: float = 0. # what fraction of patches to dropout during training (0 would mean disabled and no patches dropped) - 0.5 to 0.75 recommended in the paper for optimal results
37
+ input_patchnorm: bool = False # whether to use dual patchnorm - would only apply the input layernorm on each patch, as post-layernorm already exist in original clip vit design
38
+ global_average_pool: bool = False # whether to global average pool the last embedding layer, instead of using CLS token (https://arxiv.org/abs/2205.01580)
39
+ attentional_pool: bool = False # whether to use attentional pooler in the last embedding layer
40
+ n_queries: int = 256 # n_queries for attentional pooler
41
+ attn_pooler_heads: int = 8 # n heads for attentional_pooling
42
+ output_tokens: bool = False
43
+
44
+ timm_model_name: str = None # a valid model name overrides layers, width, patch_size
45
+ timm_model_pretrained: bool = False # use (imagenet) pretrained weights for named model
46
+ timm_pool: str = 'avg' # feature pooling for timm model ('abs_attn', 'rot_attn', 'avg', '')
47
+ timm_proj: str = 'linear' # linear projection for timm model output ('linear', 'mlp', '')
48
+ timm_proj_bias: bool = False # enable bias final projection
49
+ timm_drop: float = 0. # head dropout
50
+ timm_drop_path: Optional[float] = None # backbone stochastic depth
51
+
52
+
53
+ @dataclass
54
+ class CLIPTextCfg:
55
+ context_length: int = 77
56
+ vocab_size: int = 49408
57
+ width: int = 512
58
+ heads: int = 8
59
+ layers: int = 12
60
+ ls_init_value: Optional[float] = None # layer scale initial value
61
+ hf_model_name: str = None
62
+ hf_tokenizer_name: str = None
63
+ hf_model_pretrained: bool = True
64
+ proj: str = 'mlp'
65
+ pooler_type: str = 'mean_pooler'
66
+ embed_cls: bool = False
67
+ pad_id: int = 0
68
+ output_tokens: bool = False
69
+ text_mask: str = 'first' # default first truncate in bpe_tokenizer
70
+
71
+
72
+ def get_cast_dtype(precision: str):
73
+ cast_dtype = None
74
+ if precision == 'bf16':
75
+ cast_dtype = torch.bfloat16
76
+ elif precision == 'fp16':
77
+ cast_dtype = torch.float16
78
+ return cast_dtype
79
+
80
+
81
+ def get_input_dtype(precision: str):
82
+ input_dtype = None
83
+ if precision in ('bf16', 'pure_bf16'):
84
+ input_dtype = torch.bfloat16
85
+ elif precision in ('fp16', 'pure_fp16'):
86
+ input_dtype = torch.float16
87
+ return input_dtype
88
+
89
+
90
+ def _build_vision_tower(
91
+ embed_dim: int,
92
+ vision_cfg: CLIPVisionCfg,
93
+ quick_gelu: bool = False,
94
+ cast_dtype: Optional[torch.dtype] = None
95
+ ):
96
+ if isinstance(vision_cfg, dict):
97
+ vision_cfg = CLIPVisionCfg(**vision_cfg)
98
+
99
+ # OpenAI models are pretrained w/ QuickGELU but native nn.GELU is both faster and more
100
+ # memory efficient in recent PyTorch releases (>= 1.10).
101
+ # NOTE: timm models always use native GELU regardless of quick_gelu flag.
102
+ act_layer = QuickGELU if quick_gelu else nn.GELU
103
+
104
+ if vision_cfg.timm_model_name:
105
+ visual = TimmModel(
106
+ vision_cfg.timm_model_name,
107
+ pretrained=vision_cfg.timm_model_pretrained,
108
+ pool=vision_cfg.timm_pool,
109
+ proj=vision_cfg.timm_proj,
110
+ proj_bias=vision_cfg.timm_proj_bias,
111
+ drop=vision_cfg.timm_drop,
112
+ drop_path=vision_cfg.timm_drop_path,
113
+ patch_drop=vision_cfg.patch_dropout if vision_cfg.patch_dropout > 0 else None,
114
+ embed_dim=embed_dim,
115
+ image_size=vision_cfg.image_size,
116
+ )
117
+ elif isinstance(vision_cfg.layers, (tuple, list)):
118
+ vision_heads = vision_cfg.width * 32 // vision_cfg.head_width
119
+ visual = ModifiedResNet(
120
+ layers=vision_cfg.layers,
121
+ output_dim=embed_dim,
122
+ heads=vision_heads,
123
+ image_size=vision_cfg.image_size,
124
+ width=vision_cfg.width,
125
+ )
126
+ else:
127
+ vision_heads = vision_cfg.width // vision_cfg.head_width
128
+ norm_layer = LayerNormFp32 if cast_dtype in (torch.float16, torch.bfloat16) else LayerNorm
129
+ visual = VisionTransformer(
130
+ image_size=vision_cfg.image_size,
131
+ patch_size=vision_cfg.patch_size,
132
+ width=vision_cfg.width,
133
+ layers=vision_cfg.layers,
134
+ heads=vision_heads,
135
+ mlp_ratio=vision_cfg.mlp_ratio,
136
+ ls_init_value=vision_cfg.ls_init_value,
137
+ patch_dropout=vision_cfg.patch_dropout,
138
+ input_patchnorm=vision_cfg.input_patchnorm,
139
+ global_average_pool=vision_cfg.global_average_pool,
140
+ attentional_pool=vision_cfg.attentional_pool,
141
+ n_queries=vision_cfg.n_queries,
142
+ attn_pooler_heads=vision_cfg.attn_pooler_heads,
143
+ output_tokens=vision_cfg.output_tokens,
144
+ output_dim=embed_dim,
145
+ act_layer=act_layer,
146
+ norm_layer=norm_layer,
147
+ )
148
+
149
+ return visual
150
+
151
+
152
+ def _build_text_tower(
153
+ embed_dim: int,
154
+ text_cfg: CLIPTextCfg,
155
+ quick_gelu: bool = False,
156
+ cast_dtype: Optional[torch.dtype] = None,
157
+ ):
158
+ if isinstance(text_cfg, dict):
159
+ text_cfg = CLIPTextCfg(**text_cfg)
160
+
161
+ if text_cfg.hf_model_name:
162
+ text = HFTextEncoder(
163
+ text_cfg.hf_model_name,
164
+ output_dim=embed_dim,
165
+ proj=text_cfg.proj,
166
+ pooler_type=text_cfg.pooler_type,
167
+ pretrained=text_cfg.hf_model_pretrained,
168
+ output_tokens=text_cfg.output_tokens,
169
+ )
170
+ else:
171
+ act_layer = QuickGELU if quick_gelu else nn.GELU
172
+ norm_layer = LayerNormFp32 if cast_dtype in (torch.float16, torch.bfloat16) else LayerNorm
173
+
174
+ text = TextTransformer(
175
+ context_length=text_cfg.context_length,
176
+ vocab_size=text_cfg.vocab_size,
177
+ width=text_cfg.width,
178
+ heads=text_cfg.heads,
179
+ layers=text_cfg.layers,
180
+ ls_init_value=text_cfg.ls_init_value,
181
+ output_dim=embed_dim,
182
+ embed_cls=text_cfg.embed_cls,
183
+ output_tokens=text_cfg.output_tokens,
184
+ pad_id=text_cfg.pad_id,
185
+ act_layer=act_layer,
186
+ norm_layer=norm_layer,
187
+ )
188
+ return text
189
+
190
+
191
+ class CLIP(nn.Module):
192
+ output_dict: torch.jit.Final[bool]
193
+
194
+ def __init__(
195
+ self,
196
+ embed_dim: int,
197
+ vision_cfg: CLIPVisionCfg,
198
+ text_cfg: CLIPTextCfg,
199
+ quick_gelu: bool = False,
200
+ cast_dtype: Optional[torch.dtype] = None,
201
+ output_dict: bool = False,
202
+ ):
203
+ super().__init__()
204
+ self.output_dict = output_dict
205
+ self.visual = _build_vision_tower(embed_dim, vision_cfg, quick_gelu, cast_dtype)
206
+
207
+ text = _build_text_tower(embed_dim, text_cfg, quick_gelu, cast_dtype)
208
+ self.transformer = text.transformer
209
+ self.context_length = text.context_length
210
+ self.vocab_size = text.vocab_size
211
+ self.token_embedding = text.token_embedding
212
+ self.positional_embedding = text.positional_embedding
213
+
214
+ self.ln_final = text.ln_final
215
+ self.text_projection = text.text_projection
216
+ self.register_buffer('attn_mask', text.attn_mask, persistent=False)
217
+
218
+ self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07))
219
+
220
+ self.method_lock_text_tower = text.lock
221
+ self.text_no_grad = False
222
+
223
+ def lock_image_tower(self, unlocked_groups=0, freeze_bn_stats=False):
224
+ # lock image tower as per LiT - https://arxiv.org/abs/2111.07991
225
+ self.visual.lock(unlocked_groups=unlocked_groups, freeze_bn_stats=freeze_bn_stats)
226
+
227
+ def lock_text_tower(self, unlocked_layers: int = 0, freeze_layer_norm: bool = True, unlock_text_proj=False):
228
+ # added by jieneng
229
+ self.method_lock_text_tower(unlocked_layers, freeze_layer_norm)
230
+ self.text_no_grad = True
231
+
232
+ @torch.jit.ignore
233
+ def set_grad_checkpointing(self, enable=True, enable_text=True):
234
+ self.visual.set_grad_checkpointing(enable)
235
+ self.transformer.grad_checkpointing = enable_text
236
+
237
+ def encode_image(self, image, normalize: bool = False):
238
+ features = self.visual.trunk.forward_features(image)
239
+ features = self.visual.trunk.forward_head(features)
240
+ features = self.visual.head(features)
241
+ return F.normalize(features, dim=-1) if normalize else features
242
+
243
+ def encode_text(self, text, normalize: bool = False):
244
+ cast_dtype = self.transformer.get_cast_dtype()
245
+
246
+ x = self.token_embedding(text).to(cast_dtype) # [batch_size, n_ctx, d_model]
247
+
248
+ x = x + self.positional_embedding.to(cast_dtype)
249
+ x = x.permute(1, 0, 2) # NLD -> LND
250
+ x = self.transformer(x, attn_mask=self.attn_mask)
251
+ x = x.permute(1, 0, 2) # LND -> NLD
252
+ x = self.ln_final(x) # [batch_size, n_ctx, transformer.width]
253
+ # take features from the eot embedding (eot_token is the highest number in each sequence)
254
+ x = x[torch.arange(x.shape[0]), text.argmax(dim=-1)] @ self.text_projection
255
+ return F.normalize(x, dim=-1) if normalize else x
256
+
257
+ def forward(
258
+ self,
259
+ image: Optional[torch.Tensor] = None,
260
+ text: Optional[torch.Tensor] = None,
261
+ ):
262
+ torch.cuda.synchronize()
263
+ # time_s1 = time.time()
264
+ image_features = self.encode_image(image, normalize=True) if image is not None else None
265
+ # torch.cuda.synchronize()
266
+ # time_e1 = time.time()
267
+ # print("image ", time_e1-time_s1)
268
+
269
+ if self.text_no_grad:
270
+ with torch.no_grad():
271
+ text_features = self.encode_text(text, normalize=True).detach() if text is not None else None
272
+ else:
273
+ text_features = self.encode_text(text, normalize=True) if text is not None else None
274
+
275
+ # torch.cuda.synchronize()
276
+ # time_e2 = time.time()
277
+ # print("text ", time_e2-time_e1)
278
+
279
+ if self.output_dict:
280
+ return {
281
+ "image_features": image_features,
282
+ "text_features": text_features,
283
+ "logit_scale": self.logit_scale.exp()
284
+ }
285
+ return image_features, text_features, self.logit_scale.exp()
286
+
287
+
288
+ class CustomTextCLIP(nn.Module):
289
+ output_dict: torch.jit.Final[bool]
290
+
291
+ def __init__(
292
+ self,
293
+ embed_dim: int,
294
+ vision_cfg: CLIPVisionCfg,
295
+ text_cfg: CLIPTextCfg,
296
+ quick_gelu: bool = False,
297
+ cast_dtype: Optional[torch.dtype] = None,
298
+ output_dict: bool = False,
299
+ ):
300
+ super().__init__()
301
+ self.output_dict = output_dict
302
+ self.visual = _build_vision_tower(embed_dim, vision_cfg, quick_gelu, cast_dtype)
303
+ self.text = _build_text_tower(embed_dim, text_cfg, quick_gelu, cast_dtype)
304
+ self.context_length = self.text.context_length
305
+ self.vocab_size = self.text.vocab_size
306
+ self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07))
307
+ self.text_no_grad = False
308
+ # self.context_length = 0 in t5
309
+ # assert 1==2, (self.text, self.context_length, self.vocab_size)
310
+
311
+
312
+ def lock_image_tower(self, unlocked_groups=0, freeze_bn_stats=False):
313
+ # lock image tower as per LiT - https://arxiv.org/abs/2111.07991
314
+ self.visual.lock(unlocked_groups=unlocked_groups, freeze_bn_stats=freeze_bn_stats)
315
+
316
+ def lock_text_tower(self, unlocked_layers: int = 0, freeze_layer_norm: bool = True, unlock_text_proj = False):
317
+ self.text.lock(unlocked_layers, freeze_layer_norm, unlock_text_proj)
318
+ self.text_no_grad = True
319
+
320
+
321
+ @torch.jit.ignore
322
+ def set_grad_checkpointing(self, enable=True, enable_text=True):
323
+ self.visual.set_grad_checkpointing(enable)
324
+ self.text.set_grad_checkpointing(enable_text)
325
+
326
+
327
+ def encode_image(self, image, normalize: bool = False):
328
+ features = self.visual.trunk.forward_features(image)
329
+ features = self.visual.trunk.forward_head(features)
330
+ features = self.visual.head(features)
331
+ return F.normalize(features, dim=-1) if normalize else features
332
+
333
+ def encode_text(self, text, normalize: bool = False):
334
+ features = self.text(text)
335
+ return F.normalize(features, dim=-1) if normalize else features
336
+
337
+ def forward(
338
+ self,
339
+ image: Optional[torch.Tensor] = None,
340
+ text: Optional[torch.Tensor] = None,
341
+ ):
342
+ image_features = self.encode_image(image, normalize=True) if image is not None else None
343
+ # if self.text_no_grad:
344
+ # with torch.no_grad():
345
+ # text_features = self.encode_text(text, normalize=True).detach() if text is not None else None
346
+ # else:
347
+ text_features = self.encode_text(text, normalize=True) if text is not None else None
348
+
349
+ if self.output_dict:
350
+ return {
351
+ "image_features": image_features,
352
+ "text_features": text_features,
353
+ "logit_scale": self.logit_scale.exp()
354
+ }
355
+ return image_features, text_features, self.logit_scale.exp()
356
+
357
+
358
+ def convert_weights_to_lp(model: nn.Module, dtype=torch.float16):
359
+ """Convert applicable model parameters to low-precision (bf16 or fp16)"""
360
+
361
+ def _convert_weights(l):
362
+ if isinstance(l, (nn.Conv1d, nn.Conv2d, nn.Linear)):
363
+ l.weight.data = l.weight.data.to(dtype)
364
+ if l.bias is not None:
365
+ l.bias.data = l.bias.data.to(dtype)
366
+
367
+ if isinstance(l, (nn.MultiheadAttention, Attention)):
368
+ for attr in [*[f"{s}_proj_weight" for s in ["in", "q", "k", "v"]], "in_proj_bias", "bias_k", "bias_v"]:
369
+ tensor = getattr(l, attr)
370
+ if tensor is not None:
371
+ tensor.data = tensor.data.to(dtype)
372
+
373
+ if isinstance(l, (CLIP, TextTransformer)):
374
+ # convert text nn.Parameter projections
375
+ attr = getattr(l, "text_projection", None)
376
+ if attr is not None:
377
+ attr.data = attr.data.to(dtype)
378
+
379
+ if isinstance(l, VisionTransformer):
380
+ # convert vision nn.Parameter projections
381
+ attr = getattr(l, "proj", None)
382
+ if attr is not None:
383
+ attr.data = attr.data.to(dtype)
384
+
385
+ model.apply(_convert_weights)
386
+
387
+
388
+ convert_weights_to_fp16 = convert_weights_to_lp # backwards compat
389
+
390
+
391
+ # used to maintain checkpoint compatibility
392
+ def convert_to_custom_text_state_dict(state_dict: dict):
393
+ if 'text_projection' in state_dict:
394
+ # old format state_dict, move text tower -> .text
395
+ new_state_dict = {}
396
+ for k, v in state_dict.items():
397
+ if any(k.startswith(p) for p in (
398
+ 'text_projection',
399
+ 'positional_embedding',
400
+ 'token_embedding',
401
+ 'transformer',
402
+ 'ln_final',
403
+ )):
404
+ k = 'text.' + k
405
+ new_state_dict[k] = v
406
+ return new_state_dict
407
+ return state_dict
408
+
409
+
410
+ def build_model_from_openai_state_dict(
411
+ state_dict: dict,
412
+ quick_gelu=True,
413
+ cast_dtype=torch.float16,
414
+ ):
415
+ vit = "visual.proj" in state_dict
416
+
417
+ if vit:
418
+ vision_width = state_dict["visual.conv1.weight"].shape[0]
419
+ vision_layers = len(
420
+ [k for k in state_dict.keys() if k.startswith("visual.") and k.endswith(".attn.in_proj_weight")])
421
+ vision_patch_size = state_dict["visual.conv1.weight"].shape[-1]
422
+ grid_size = round((state_dict["visual.positional_embedding"].shape[0] - 1) ** 0.5)
423
+ image_size = vision_patch_size * grid_size
424
+ else:
425
+ counts: list = [
426
+ len(set(k.split(".")[2] for k in state_dict if k.startswith(f"visual.layer{b}"))) for b in [1, 2, 3, 4]]
427
+ vision_layers = tuple(counts)
428
+ vision_width = state_dict["visual.layer1.0.conv1.weight"].shape[0]
429
+ output_width = round((state_dict["visual.attnpool.positional_embedding"].shape[0] - 1) ** 0.5)
430
+ vision_patch_size = None
431
+ assert output_width ** 2 + 1 == state_dict["visual.attnpool.positional_embedding"].shape[0]
432
+ image_size = output_width * 32
433
+
434
+ embed_dim = state_dict["text_projection"].shape[1]
435
+ context_length = state_dict["positional_embedding"].shape[0]
436
+ vocab_size = state_dict["token_embedding.weight"].shape[0]
437
+ transformer_width = state_dict["ln_final.weight"].shape[0]
438
+ transformer_heads = transformer_width // 64
439
+ transformer_layers = len(set(k.split(".")[2] for k in state_dict if k.startswith(f"transformer.resblocks")))
440
+
441
+ vision_cfg = CLIPVisionCfg(
442
+ layers=vision_layers,
443
+ width=vision_width,
444
+ patch_size=vision_patch_size,
445
+ image_size=image_size,
446
+ )
447
+ text_cfg = CLIPTextCfg(
448
+ context_length=context_length,
449
+ vocab_size=vocab_size,
450
+ width=transformer_width,
451
+ heads=transformer_heads,
452
+ layers=transformer_layers,
453
+ )
454
+ model = CLIP(
455
+ embed_dim,
456
+ vision_cfg=vision_cfg,
457
+ text_cfg=text_cfg,
458
+ quick_gelu=quick_gelu, # OpenAI models were trained with QuickGELU
459
+ cast_dtype=cast_dtype,
460
+ )
461
+
462
+ for key in ["input_resolution", "context_length", "vocab_size"]:
463
+ state_dict.pop(key, None)
464
+
465
+ convert_weights_to_fp16(model) # OpenAI state dicts are partially converted to float16
466
+ model.load_state_dict(state_dict)
467
+ return model.eval()
468
+
469
+
470
+ def trace_model(model, batch_size=256, device=torch.device('cpu')):
471
+ model.eval()
472
+ image_size = model.visual.image_size
473
+ example_images = torch.ones((batch_size, 3, image_size, image_size), device=device)
474
+ example_text = torch.zeros((batch_size, model.context_length), dtype=torch.int, device=device)
475
+ model = torch.jit.trace_module(
476
+ model,
477
+ inputs=dict(
478
+ forward=(example_images, example_text),
479
+ encode_text=(example_text,),
480
+ encode_image=(example_images,)
481
+ ))
482
+ model.visual.image_size = image_size
483
+ return model
484
+
485
+ def resize_pos_embed_timm(state_dict, model, interpolation: str = 'bicubic', antialias: bool = True):
486
+ # Rescale the grid of position embeddings when loading from state_dict
487
+ old_pos_embed = state_dict.get('visual.trunk.pos_embed', None) # 1, 196, 1024]
488
+ if old_pos_embed is None:
489
+ return
490
+
491
+ grid_size = to_2tuple(model.visual.trunk.patch_embed.grid_size)
492
+
493
+
494
+ if hasattr(model.visual.trunk, 'cls_token') and model.visual.trunk.cls_token is not None:
495
+ return
496
+ # extra_tokens?
497
+ raise NotImplementedError
498
+
499
+ new_seq_len = grid_size[0] * grid_size[1]
500
+ if new_seq_len == old_pos_embed.shape[0]:
501
+ return
502
+
503
+ pos_emb_img = old_pos_embed
504
+ old_grid_size = to_2tuple(int(math.sqrt(len(pos_emb_img[0]))))
505
+ old_pos_emb_img = pos_emb_img
506
+ logging.info('Resizing position embedding grid-size from %s to %s', old_grid_size, grid_size) # Resizing position embedding grid-size from (1, 1) to (21, 21)
507
+ pos_emb_img = pos_emb_img.reshape(1, old_grid_size[0], old_grid_size[1], -1).permute(0, 3, 1, 2)
508
+
509
+ pos_emb_img = F.interpolate(
510
+ pos_emb_img,
511
+ size=grid_size,
512
+ mode=interpolation,
513
+ antialias=antialias,
514
+ align_corners=False,
515
+ )
516
+ pos_emb_img = pos_emb_img.permute(0, 2, 3, 1).reshape(1, grid_size[0] * grid_size[1], -1)
517
+ state_dict['visual.trunk.pos_embed'] = pos_emb_img
518
+
519
+ def resize_pos_embed(state_dict, model, interpolation: str = 'bicubic', antialias: bool = True):
520
+ # Rescale the grid of position embeddings when loading from state_dict
521
+ pe_key_name = 'visual.positional_embedding'
522
+ old_pos_embed = state_dict.get('visual.positional_embedding', None)
523
+ if old_pos_embed is None:
524
+ pe_key_name = 'visual.trunk.pos_embed'
525
+ old_pos_embed = state_dict.get('visual.trunk.pos_embed', None) # 1, 196, 1024]
526
+
527
+ if old_pos_embed is None:
528
+ return
529
+
530
+ if hasattr(model.visual, 'grid_size'):
531
+ grid_size = to_2tuple(model.visual.grid_size)
532
+ elif hasattr(model.visual.trunk.patch_embed, 'grid_size'):
533
+ grid_size = to_2tuple(model.visual.trunk.patch_embed.grid_size)
534
+ else:
535
+ return
536
+
537
+ if hasattr(model.visual.trunk, 'cls_token') and model.visual.trunk.cls_token is not None:
538
+ extra_tokens = 1 # FIXME detect different token configs (ie no class token, or more)
539
+ else:
540
+ extra_tokens = 0
541
+ new_seq_len = grid_size[0] * grid_size[1] + extra_tokens
542
+
543
+ if new_seq_len == old_pos_embed.shape[0]:
544
+ return
545
+
546
+ if extra_tokens:
547
+ pos_emb_tok, pos_emb_img = old_pos_embed[:extra_tokens], old_pos_embed[extra_tokens:]
548
+ else:
549
+ pos_emb_tok, pos_emb_img = None, old_pos_embed
550
+ old_grid_size = to_2tuple(int(math.sqrt(len(pos_emb_img))))
551
+ old_pos_emb_img = pos_emb_img
552
+ logging.info('Resizing position embedding grid-size from %s to %s', old_grid_size, grid_size) # Resizing position embedding grid-size from (1, 1) to (21, 21)
553
+ pos_emb_img = pos_emb_img.reshape(1, old_grid_size[0], old_grid_size[1], -1).permute(0, 3, 1, 2)
554
+
555
+
556
+ pos_emb_img = F.interpolate(
557
+ pos_emb_img,
558
+ size=grid_size,
559
+ mode=interpolation,
560
+ antialias=antialias,
561
+ align_corners=False,
562
+ )
563
+ pos_emb_img = pos_emb_img.permute(0, 2, 3, 1).reshape(1, grid_size[0] * grid_size[1], -1)[0]
564
+ if pos_emb_tok is not None:
565
+ new_pos_embed = torch.cat([pos_emb_tok, pos_emb_img], dim=0)
566
+ else:
567
+ new_pos_embed = pos_emb_img
568
+ state_dict[pe_key_name] = new_pos_embed
569
+
570
+ def resize_text_pos_embed(state_dict, model, interpolation: str = 'linear', antialias: bool = False):
571
+ old_pos_embed = state_dict.get('positional_embedding', None)
572
+ if old_pos_embed is None:
573
+ return
574
+ # FIXME add support for text cls_token
575
+ model_pos_embed = getattr(model, 'positional_embedding', None)
576
+ if model_pos_embed is None:
577
+ model_pos_embed = getattr(model.text, 'positional_embedding', None)
578
+
579
+ old_num_pos = old_pos_embed.shape[0]
580
+ old_width = old_pos_embed.shape[1]
581
+ num_pos = model_pos_embed.shape[0]
582
+ width = model_pos_embed.shape[1]
583
+ assert old_width == width, 'text pos_embed width changed!'
584
+ if old_num_pos == num_pos:
585
+ return
586
+
587
+ logging.info('Resizing text position embedding num_pos from %s to %s', old_num_pos, num_pos)
588
+ old_pos_embed = old_pos_embed.reshape(1, old_num_pos, old_width).permute(0, 2, 1)
589
+ old_pos_embed = F.interpolate(
590
+ old_pos_embed,
591
+ size=num_pos,
592
+ mode=interpolation,
593
+ antialias=antialias,
594
+ align_corners=False,
595
+ )
596
+ old_pos_embed = old_pos_embed.permute(0, 2, 1)[0]
597
+ new_pos_embed = old_pos_embed
598
+
599
+ state_dict['positional_embedding'] = new_pos_embed
native/open_clip/transformer.py ADDED
@@ -0,0 +1,735 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections import OrderedDict
2
+ import math
3
+ from typing import Callable, Optional, Sequence, Tuple
4
+
5
+ import torch
6
+ from torch import nn
7
+ from torch.nn import functional as F
8
+ from torch.utils.checkpoint import checkpoint
9
+
10
+ from .utils import to_2tuple
11
+
12
+
13
+ class LayerNormFp32(nn.LayerNorm):
14
+ """Subclass torch's LayerNorm to handle fp16 (by casting to float32 and back)."""
15
+
16
+ def forward(self, x: torch.Tensor):
17
+ orig_type = x.dtype
18
+ x = F.layer_norm(x.to(torch.float32), self.normalized_shape, self.weight, self.bias, self.eps)
19
+ return x.to(orig_type)
20
+
21
+
22
+ class LayerNorm(nn.LayerNorm):
23
+ """Subclass torch's LayerNorm (with cast back to input dtype)."""
24
+
25
+ def forward(self, x: torch.Tensor):
26
+ orig_type = x.dtype
27
+ x = F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
28
+ return x.to(orig_type)
29
+
30
+
31
+ class QuickGELU(nn.Module):
32
+ # NOTE This is slower than nn.GELU or nn.SiLU and uses more GPU memory
33
+ def forward(self, x: torch.Tensor):
34
+ return x * torch.sigmoid(1.702 * x)
35
+
36
+
37
+ class LayerScale(nn.Module):
38
+ def __init__(self, dim, init_values=1e-5, inplace=False):
39
+ super().__init__()
40
+ self.inplace = inplace
41
+ self.gamma = nn.Parameter(init_values * torch.ones(dim))
42
+
43
+ def forward(self, x):
44
+ return x.mul_(self.gamma) if self.inplace else x * self.gamma
45
+
46
+
47
+ class PatchDropout(nn.Module):
48
+ """
49
+ https://arxiv.org/abs/2212.00794
50
+ """
51
+
52
+ def __init__(self, prob, exclude_first_token=True):
53
+ super().__init__()
54
+ assert 0 <= prob < 1.
55
+ self.prob = prob
56
+ self.exclude_first_token = exclude_first_token # exclude CLS token
57
+
58
+ def forward(self, x):
59
+ if not self.training or self.prob == 0.:
60
+ return x
61
+
62
+ if self.exclude_first_token:
63
+ cls_tokens, x = x[:, :1], x[:, 1:]
64
+ else:
65
+ cls_tokens = torch.jit.annotate(torch.Tensor, x[:, :1])
66
+
67
+ batch = x.size()[0]
68
+ num_tokens = x.size()[1]
69
+
70
+ batch_indices = torch.arange(batch)
71
+ batch_indices = batch_indices[..., None]
72
+
73
+ keep_prob = 1 - self.prob
74
+ num_patches_keep = max(1, int(num_tokens * keep_prob))
75
+
76
+ rand = torch.randn(batch, num_tokens)
77
+ patch_indices_keep = rand.topk(num_patches_keep, dim=-1).indices
78
+
79
+ x = x[batch_indices, patch_indices_keep]
80
+
81
+ if self.exclude_first_token:
82
+ x = torch.cat((cls_tokens, x), dim=1)
83
+
84
+ return x
85
+
86
+
87
+ class Attention(nn.Module):
88
+ def __init__(
89
+ self,
90
+ dim,
91
+ num_heads=8,
92
+ qkv_bias=True,
93
+ scaled_cosine=False,
94
+ scale_heads=False,
95
+ logit_scale_max=math.log(1. / 0.01),
96
+ attn_drop=0.,
97
+ proj_drop=0.
98
+ ):
99
+ super().__init__()
100
+ self.scaled_cosine = scaled_cosine
101
+ self.scale_heads = scale_heads
102
+ assert dim % num_heads == 0, 'dim should be divisible by num_heads'
103
+ self.num_heads = num_heads
104
+ self.head_dim = dim // num_heads
105
+ self.scale = self.head_dim ** -0.5
106
+ self.logit_scale_max = logit_scale_max
107
+
108
+ # keeping in_proj in this form (instead of nn.Linear) to match weight scheme of original
109
+ self.in_proj_weight = nn.Parameter(torch.randn((dim * 3, dim)) * self.scale)
110
+ if qkv_bias:
111
+ self.in_proj_bias = nn.Parameter(torch.zeros(dim * 3))
112
+ else:
113
+ self.in_proj_bias = None
114
+
115
+ if self.scaled_cosine:
116
+ self.logit_scale = nn.Parameter(torch.log(10 * torch.ones((num_heads, 1, 1))))
117
+ else:
118
+ self.logit_scale = None
119
+ self.attn_drop = nn.Dropout(attn_drop)
120
+ if self.scale_heads:
121
+ self.head_scale = nn.Parameter(torch.ones((num_heads, 1, 1)))
122
+ else:
123
+ self.head_scale = None
124
+ self.out_proj = nn.Linear(dim, dim)
125
+ self.out_drop = nn.Dropout(proj_drop)
126
+
127
+ def forward(self, x, attn_mask: Optional[torch.Tensor] = None):
128
+ L, N, C = x.shape
129
+ q, k, v = F.linear(x, self.in_proj_weight, self.in_proj_bias).chunk(3, dim=-1)
130
+ q = q.contiguous().view(L, N * self.num_heads, -1).transpose(0, 1)
131
+ k = k.contiguous().view(L, N * self.num_heads, -1).transpose(0, 1)
132
+ v = v.contiguous().view(L, N * self.num_heads, -1).transpose(0, 1)
133
+
134
+ if self.logit_scale is not None:
135
+ attn = torch.bmm(F.normalize(q, dim=-1), F.normalize(k, dim=-1).transpose(-1, -2))
136
+ logit_scale = torch.clamp(self.logit_scale, max=self.logit_scale_max).exp()
137
+ attn = attn.view(N, self.num_heads, L, L) * logit_scale
138
+ attn = attn.view(-1, L, L)
139
+ else:
140
+ q = q * self.scale
141
+ attn = torch.bmm(q, k.transpose(-1, -2))
142
+
143
+ if attn_mask is not None:
144
+ if attn_mask.dtype == torch.bool:
145
+ new_attn_mask = torch.zeros_like(attn_mask, dtype=q.dtype)
146
+ new_attn_mask.masked_fill_(attn_mask, float("-inf"))
147
+ attn_mask = new_attn_mask
148
+ attn += attn_mask
149
+
150
+ attn = attn.softmax(dim=-1)
151
+ attn = self.attn_drop(attn)
152
+
153
+ x = torch.bmm(attn, v)
154
+ if self.head_scale is not None:
155
+ x = x.view(N, self.num_heads, L, C) * self.head_scale
156
+ x = x.view(-1, L, C)
157
+ x = x.transpose(0, 1).reshape(L, N, C)
158
+ x = self.out_proj(x)
159
+ x = self.out_drop(x)
160
+ return x
161
+
162
+
163
+ class AttentionalPooler(nn.Module):
164
+ def __init__(
165
+ self,
166
+ d_model: int,
167
+ context_dim: int,
168
+ n_head: int = 8,
169
+ n_queries: int = 256,
170
+ norm_layer: Callable = LayerNorm
171
+ ):
172
+ super().__init__()
173
+ self.query = nn.Parameter(torch.randn(n_queries, d_model))
174
+ self.attn = nn.MultiheadAttention(d_model, n_head, kdim=context_dim, vdim=context_dim)
175
+ self.ln_q = norm_layer(d_model)
176
+ self.ln_k = norm_layer(context_dim)
177
+
178
+ def forward(self, x: torch.Tensor):
179
+ x = self.ln_k(x).permute(1, 0, 2) # NLD -> LND
180
+ N = x.shape[1]
181
+ q = self.ln_q(self.query)
182
+ out = self.attn(self._repeat(q, N), x, x, need_weights=False)[0]
183
+ return out.permute(1, 0, 2) # LND -> NLD
184
+
185
+ def _repeat(self, query, N: int):
186
+ return query.unsqueeze(1).repeat(1, N, 1)
187
+
188
+
189
+ class ResidualAttentionBlock(nn.Module):
190
+ def __init__(
191
+ self,
192
+ d_model: int,
193
+ n_head: int,
194
+ mlp_ratio: float = 4.0,
195
+ ls_init_value: float = None,
196
+ act_layer: Callable = nn.GELU,
197
+ norm_layer: Callable = LayerNorm,
198
+ is_cross_attention: bool = False,
199
+ ):
200
+ super().__init__()
201
+
202
+ self.ln_1 = norm_layer(d_model)
203
+ self.attn = nn.MultiheadAttention(d_model, n_head)
204
+ self.ls_1 = LayerScale(d_model, ls_init_value) if ls_init_value is not None else nn.Identity()
205
+ if is_cross_attention:
206
+ self.ln_1_kv = norm_layer(d_model)
207
+
208
+ self.ln_2 = norm_layer(d_model)
209
+ mlp_width = int(d_model * mlp_ratio)
210
+ self.mlp = nn.Sequential(OrderedDict([
211
+ ("c_fc", nn.Linear(d_model, mlp_width)),
212
+ ("gelu", act_layer()),
213
+ ("c_proj", nn.Linear(mlp_width, d_model))
214
+ ]))
215
+ self.ls_2 = LayerScale(d_model, ls_init_value) if ls_init_value is not None else nn.Identity()
216
+
217
+ def attention(
218
+ self,
219
+ q_x: torch.Tensor,
220
+ k_x: Optional[torch.Tensor] = None,
221
+ v_x: Optional[torch.Tensor] = None,
222
+ attn_mask: Optional[torch.Tensor] = None,
223
+ ):
224
+ k_x = k_x if k_x is not None else q_x
225
+ v_x = v_x if v_x is not None else q_x
226
+
227
+ attn_mask = attn_mask.to(q_x.dtype) if attn_mask is not None else None
228
+ return self.attn(
229
+ q_x, k_x, v_x, need_weights=False, attn_mask=attn_mask
230
+ )[0]
231
+
232
+ def forward(
233
+ self,
234
+ q_x: torch.Tensor,
235
+ k_x: Optional[torch.Tensor] = None,
236
+ v_x: Optional[torch.Tensor] = None,
237
+ attn_mask: Optional[torch.Tensor] = None,
238
+ ):
239
+ k_x = self.ln_1_kv(k_x) if hasattr(self, "ln_1_kv") and k_x is not None else None
240
+ v_x = self.ln_1_kv(v_x) if hasattr(self, "ln_1_kv") and v_x is not None else None
241
+
242
+ x = q_x + self.ls_1(self.attention(q_x=self.ln_1(q_x), k_x=k_x, v_x=v_x, attn_mask=attn_mask))
243
+ x = x + self.ls_2(self.mlp(self.ln_2(x)))
244
+ return x
245
+
246
+
247
+ class CustomResidualAttentionBlock(nn.Module):
248
+ def __init__(
249
+ self,
250
+ d_model: int,
251
+ n_head: int,
252
+ mlp_ratio: float = 4.0,
253
+ ls_init_value: float = None,
254
+ act_layer: Callable = nn.GELU,
255
+ norm_layer: Callable = LayerNorm,
256
+ scale_cosine_attn: bool = False,
257
+ scale_heads: bool = False,
258
+ scale_attn: bool = False,
259
+ scale_fc: bool = False,
260
+ ):
261
+ super().__init__()
262
+
263
+ self.ln_1 = norm_layer(d_model)
264
+ self.attn = Attention(
265
+ d_model, n_head,
266
+ scaled_cosine=scale_cosine_attn,
267
+ scale_heads=scale_heads,
268
+ )
269
+ self.ln_attn = norm_layer(d_model) if scale_attn else nn.Identity()
270
+ self.ls_1 = LayerScale(d_model, ls_init_value) if ls_init_value is not None else nn.Identity()
271
+
272
+ self.ln_2 = norm_layer(d_model)
273
+ mlp_width = int(d_model * mlp_ratio)
274
+ self.mlp = nn.Sequential(OrderedDict([
275
+ ("c_fc", nn.Linear(d_model, mlp_width)),
276
+ ('ln', norm_layer(mlp_width) if scale_fc else nn.Identity()),
277
+ ("gelu", act_layer()),
278
+ ("c_proj", nn.Linear(mlp_width, d_model))
279
+ ]))
280
+ self.ls_2 = LayerScale(d_model, ls_init_value) if ls_init_value is not None else nn.Identity()
281
+
282
+ def forward(self, x: torch.Tensor, attn_mask: Optional[torch.Tensor] = None):
283
+ x = x + self.ls_1(self.ln_attn(self.attn(self.ln_1(x), attn_mask=attn_mask)))
284
+ x = x + self.ls_2(self.mlp(self.ln_2(x)))
285
+ return x
286
+
287
+
288
+ class Transformer(nn.Module):
289
+ def __init__(
290
+ self,
291
+ width: int,
292
+ layers: int,
293
+ heads: int,
294
+ mlp_ratio: float = 4.0,
295
+ ls_init_value: float = None,
296
+ act_layer: Callable = nn.GELU,
297
+ norm_layer: Callable = LayerNorm,
298
+ ):
299
+ super().__init__()
300
+ self.width = width
301
+ self.layers = layers
302
+ self.grad_checkpointing = False
303
+
304
+ self.resblocks = nn.ModuleList([
305
+ ResidualAttentionBlock(
306
+ width, heads, mlp_ratio, ls_init_value=ls_init_value, act_layer=act_layer, norm_layer=norm_layer)
307
+ for _ in range(layers)
308
+ ])
309
+
310
+ def get_cast_dtype(self) -> torch.dtype:
311
+ if hasattr(self.resblocks[0].mlp.c_fc, 'int8_original_dtype'):
312
+ return self.resblocks[0].mlp.c_fc.int8_original_dtype
313
+ return self.resblocks[0].mlp.c_fc.weight.dtype
314
+
315
+ def forward(self, x: torch.Tensor, attn_mask: Optional[torch.Tensor] = None):
316
+ for r in self.resblocks:
317
+ if self.grad_checkpointing and not torch.jit.is_scripting():
318
+ # TODO: handle kwargs https://github.com/pytorch/pytorch/issues/79887#issuecomment-1161758372
319
+ x = checkpoint(r, x, None, None, attn_mask)
320
+ else:
321
+ x = r(x, attn_mask=attn_mask)
322
+ return x
323
+
324
+
325
+ class VisionTransformer(nn.Module):
326
+ output_tokens: torch.jit.Final[bool]
327
+
328
+ def __init__(
329
+ self,
330
+ image_size: int,
331
+ patch_size: int,
332
+ width: int,
333
+ layers: int,
334
+ heads: int,
335
+ mlp_ratio: float,
336
+ ls_init_value: float = None,
337
+ global_average_pool: bool = False,
338
+ attentional_pool: bool = False,
339
+ n_queries: int = 256,
340
+ attn_pooler_heads: int = 8,
341
+ output_dim: int = 512,
342
+ patch_dropout: float = 0.,
343
+ input_patchnorm: bool = False,
344
+ act_layer: Callable = nn.GELU,
345
+ norm_layer: Callable = LayerNorm,
346
+ output_tokens: bool = False
347
+ ):
348
+ super().__init__()
349
+ self.output_tokens = output_tokens
350
+ image_height, image_width = self.image_size = to_2tuple(image_size)
351
+ patch_height, patch_width = self.patch_size = to_2tuple(patch_size)
352
+ self.grid_size = (image_height // patch_height, image_width // patch_width)
353
+ self.output_dim = output_dim
354
+
355
+ # whether to layernorm each patch, as done in dual patchnorm paper - https://arxiv.org/abs/2302.01327v1
356
+ self.input_patchnorm = input_patchnorm
357
+
358
+ if input_patchnorm:
359
+ patch_input_dim = patch_height * patch_width * 3
360
+ self.patchnorm_pre_ln = LayerNorm(patch_input_dim)
361
+ self.conv1 = nn.Linear(patch_input_dim, width)
362
+ else:
363
+ self.patchnorm_pre_ln = nn.Identity()
364
+ self.conv1 = nn.Conv2d(in_channels=3, out_channels=width, kernel_size=patch_size, stride=patch_size,
365
+ bias=False)
366
+
367
+ # class embeddings and positional embeddings
368
+ scale = width ** -0.5
369
+ self.class_embedding = nn.Parameter(scale * torch.randn(width))
370
+ self.positional_embedding = nn.Parameter(scale * torch.randn(self.grid_size[0] * self.grid_size[1] + 1, width))
371
+
372
+ # setting a patch_dropout of 0. would mean it is disabled and this function would be the identity fn
373
+ self.patch_dropout = PatchDropout(patch_dropout) if patch_dropout > 0. else nn.Identity()
374
+
375
+ self.ln_pre = norm_layer(width)
376
+ self.transformer = Transformer(
377
+ width,
378
+ layers,
379
+ heads,
380
+ mlp_ratio,
381
+ ls_init_value=ls_init_value,
382
+ act_layer=act_layer,
383
+ norm_layer=norm_layer,
384
+ )
385
+
386
+ self.global_average_pool = global_average_pool
387
+ if attentional_pool:
388
+ self.attn_pool = AttentionalPooler(output_dim, width, n_head=attn_pooler_heads, n_queries=n_queries)
389
+ self.ln_post = norm_layer(output_dim)
390
+ self.proj = nn.Parameter(scale * torch.randn(output_dim, output_dim))
391
+ else:
392
+ self.attn_pool = None
393
+ self.ln_post = norm_layer(width)
394
+ self.proj = nn.Parameter(scale * torch.randn(width, output_dim))
395
+
396
+ self.init_parameters()
397
+
398
+ def lock(self, unlocked_groups=0, freeze_bn_stats=False):
399
+ for param in self.parameters():
400
+ param.requires_grad = False
401
+
402
+ if unlocked_groups != 0:
403
+ groups = [
404
+ [
405
+ self.conv1,
406
+ self.class_embedding,
407
+ self.positional_embedding,
408
+ self.ln_pre,
409
+ ],
410
+ *self.transformer.resblocks[:-1],
411
+ [
412
+ self.transformer.resblocks[-1],
413
+ self.ln_post,
414
+ ],
415
+ self.proj,
416
+ ]
417
+
418
+ def _unlock(x):
419
+ if isinstance(x, Sequence):
420
+ for g in x:
421
+ _unlock(g)
422
+ else:
423
+ if isinstance(x, torch.nn.Parameter):
424
+ x.requires_grad = True
425
+ else:
426
+ for p in x.parameters():
427
+ p.requires_grad = True
428
+
429
+ _unlock(groups[-unlocked_groups:])
430
+
431
+ def init_parameters(self):
432
+ # FIXME OpenAI CLIP did not define an init for the VisualTransformer
433
+ # TODO experiment if default PyTorch init, below, or alternate init is best.
434
+
435
+ # nn.init.normal_(self.class_embedding, std=self.scale)
436
+ # nn.init.normal_(self.positional_embedding, std=self.scale)
437
+ #
438
+ # proj_std = (self.transformer.width ** -0.5) * ((2 * self.transformer.layers) ** -0.5)
439
+ # attn_std = self.transformer.width ** -0.5
440
+ # fc_std = (2 * self.transformer.width) ** -0.5
441
+ # for block in self.transformer.resblocks:
442
+ # nn.init.normal_(block.attn.in_proj_weight, std=attn_std)
443
+ # nn.init.normal_(block.attn.out_proj.weight, std=proj_std)
444
+ # nn.init.normal_(block.mlp.c_fc.weight, std=fc_std)
445
+ # nn.init.normal_(block.mlp.c_proj.weight, std=proj_std)
446
+ #
447
+ # if self.text_projection is not None:
448
+ # nn.init.normal_(self.text_projection, std=self.scale)
449
+ pass
450
+
451
+ @torch.jit.ignore
452
+ def set_grad_checkpointing(self, enable=True):
453
+ self.transformer.grad_checkpointing = enable
454
+
455
+ def _global_pool(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
456
+ if self.global_average_pool:
457
+ return x.mean(dim=1), x
458
+ else:
459
+ return x[:, 0], x[:, 1:]
460
+
461
+ def forward(self, x: torch.Tensor):
462
+
463
+ # to patches - whether to use dual patchnorm - https://arxiv.org/abs/2302.01327v1
464
+ if self.input_patchnorm:
465
+ # einops - rearrange(x, 'b c (h p1) (w p2) -> b (h w) (c p1 p2)')
466
+ x = x.reshape(x.shape[0], x.shape[1], self.grid_size[0], self.patch_size[0], self.grid_size[1],
467
+ self.patch_size[1])
468
+ x = x.permute(0, 2, 4, 1, 3, 5)
469
+ x = x.reshape(x.shape[0], self.grid_size[0] * self.grid_size[1], -1)
470
+ x = self.patchnorm_pre_ln(x)
471
+ x = self.conv1(x)
472
+ else:
473
+ x = self.conv1(x) # shape = [*, width, grid, grid]
474
+ x = x.reshape(x.shape[0], x.shape[1], -1) # shape = [*, width, grid ** 2]
475
+ x = x.permute(0, 2, 1) # shape = [*, grid ** 2, width]
476
+
477
+ # class embeddings and positional embeddings
478
+ x = torch.cat(
479
+ [self.class_embedding.to(x.dtype) + torch.zeros(x.shape[0], 1, x.shape[-1], dtype=x.dtype, device=x.device),
480
+ x], dim=1) # shape = [*, grid ** 2 + 1, width]
481
+ x = x + self.positional_embedding.to(x.dtype)
482
+
483
+ # a patch_dropout of 0. would mean it is disabled and this function would do nothing but return what was passed in
484
+ x = self.patch_dropout(x)
485
+ x = self.ln_pre(x)
486
+
487
+ x = x.permute(1, 0, 2) # NLD -> LND
488
+ x = self.transformer(x)
489
+ x = x.permute(1, 0, 2) # LND -> NLD
490
+
491
+ if self.attn_pool is not None:
492
+ x = self.attn_pool(x)
493
+ x = self.ln_post(x)
494
+ pooled, tokens = self._global_pool(x)
495
+ else:
496
+ pooled, tokens = self._global_pool(x)
497
+ pooled = self.ln_post(pooled)
498
+
499
+ if self.proj is not None:
500
+ pooled = pooled @ self.proj
501
+
502
+ if self.output_tokens:
503
+ return pooled, tokens
504
+
505
+ return pooled
506
+
507
+
508
+ class TextTransformer(nn.Module):
509
+ output_tokens: torch.jit.Final[bool]
510
+
511
+ def __init__(
512
+ self,
513
+ context_length: int = 77,
514
+ vocab_size: int = 49408,
515
+ width: int = 512,
516
+ heads: int = 8,
517
+ layers: int = 12,
518
+ ls_init_value: float = None,
519
+ output_dim: int = 512,
520
+ act_layer: Callable = nn.GELU,
521
+ norm_layer: Callable = LayerNorm,
522
+ embed_cls: bool = False,
523
+ pad_id: int = 0,
524
+ output_tokens: bool = False,
525
+ ):
526
+ super().__init__()
527
+ self.output_tokens = output_tokens
528
+ self.num_pos = self.context_length = context_length
529
+ self.vocab_size = vocab_size
530
+ self.width = width
531
+ self.output_dim = output_dim
532
+ self.heads = heads
533
+ self.pad_id = pad_id
534
+
535
+ self.text_projection = nn.Parameter(torch.empty(width, output_dim))
536
+
537
+ if embed_cls:
538
+ self.cls_emb = nn.Parameter(torch.empty(width))
539
+ self.num_pos += 1
540
+ else:
541
+ self.cls_emb = None
542
+
543
+ self.token_embedding = nn.Embedding(vocab_size, width)
544
+ self.positional_embedding = nn.Parameter(torch.empty(self.num_pos, width))
545
+ self.transformer = Transformer(
546
+ width=width,
547
+ layers=layers,
548
+ heads=heads,
549
+ ls_init_value=ls_init_value,
550
+ act_layer=act_layer,
551
+ norm_layer=norm_layer,
552
+ )
553
+ self.ln_final = norm_layer(width)
554
+
555
+ self.register_buffer('attn_mask', self.build_attention_mask(), persistent=False)
556
+
557
+ self.init_parameters()
558
+
559
+ def lock(self, unlocked_layers: int = 0, freeze_layer_norm: bool = True, unlock_text_proj=False):
560
+ for param in self.parameters():
561
+ param.requires_grad = False
562
+
563
+ if unlocked_layers == -1:
564
+ self.text_projection.requires_grad = True
565
+
566
+ def init_parameters(self):
567
+ nn.init.normal_(self.token_embedding.weight, std=0.02)
568
+ nn.init.normal_(self.positional_embedding, std=0.01)
569
+ if self.cls_emb is not None:
570
+ nn.init.normal_(self.cls_emb, std=0.01)
571
+
572
+ proj_std = (self.transformer.width ** -0.5) * ((2 * self.transformer.layers) ** -0.5)
573
+ attn_std = self.transformer.width ** -0.5
574
+ fc_std = (2 * self.transformer.width) ** -0.5
575
+ for block in self.transformer.resblocks:
576
+ nn.init.normal_(block.attn.in_proj_weight, std=attn_std)
577
+ nn.init.normal_(block.attn.out_proj.weight, std=proj_std)
578
+ nn.init.normal_(block.mlp.c_fc.weight, std=fc_std)
579
+ nn.init.normal_(block.mlp.c_proj.weight, std=proj_std)
580
+
581
+ if self.text_projection is not None:
582
+ nn.init.normal_(self.text_projection, std=self.transformer.width ** -0.5)
583
+
584
+ @torch.jit.ignore
585
+ def set_grad_checkpointing(self, enable=True):
586
+ self.transformer.grad_checkpointing = enable
587
+
588
+ def build_attention_mask(self):
589
+ # lazily create causal attention mask, with full attention between the tokens
590
+ # pytorch uses additive attention mask; fill with -inf
591
+ mask = torch.empty(self.num_pos, self.num_pos)
592
+ mask.fill_(float("-inf"))
593
+ mask.triu_(1) # zero out the lower diagonal
594
+ return mask
595
+
596
+ def build_cls_mask(self, text, cast_dtype: torch.dtype):
597
+ cls_mask = (text != self.pad_id).unsqueeze(1)
598
+ cls_mask = F.pad(cls_mask, (1, 0, cls_mask.shape[2], 0), value=1.0)
599
+ additive_mask = torch.empty(cls_mask.shape, dtype=cast_dtype, device=cls_mask.device)
600
+ additive_mask.fill_(0)
601
+ additive_mask.masked_fill_(~cls_mask, float("-inf"))
602
+ additive_mask = torch.repeat_interleave(additive_mask, self.heads, 0)
603
+ return additive_mask
604
+
605
+ def _repeat(self, t, N: int):
606
+ return t.reshape(1, 1, -1).repeat(N, 1, 1)
607
+
608
+ def forward(self, text):
609
+ cast_dtype = self.transformer.get_cast_dtype()
610
+ seq_len = text.shape[1]
611
+
612
+ x = self.token_embedding(text).to(cast_dtype) # [batch_size, n_ctx, d_model]
613
+ attn_mask = self.attn_mask
614
+ if self.cls_emb is not None:
615
+ seq_len += 1
616
+ x = torch.cat([x, self._repeat(self.cls_emb, x.shape[0])], dim=1)
617
+ cls_mask = self.build_cls_mask(text, cast_dtype)
618
+ attn_mask = attn_mask[None, :seq_len, :seq_len] + cls_mask[:, :seq_len, :seq_len]
619
+
620
+ x = x + self.positional_embedding[:seq_len].to(cast_dtype)
621
+ x = x.permute(1, 0, 2) # NLD -> LND
622
+ x = self.transformer(x, attn_mask=attn_mask)
623
+ x = x.permute(1, 0, 2) # LND -> NLD
624
+
625
+ # x.shape = [batch_size, n_ctx, transformer.width]
626
+ # take features from the eot embedding (eot_token is the highest number in each sequence)
627
+ if self.cls_emb is not None:
628
+ pooled, tokens = x[:, -1], x[:, :-1]
629
+ pooled = self.ln_final(pooled)
630
+ else:
631
+ x = self.ln_final(x)
632
+ pooled, tokens = x[torch.arange(x.shape[0]), text.argmax(dim=-1)], x
633
+
634
+ if self.text_projection is not None:
635
+ pooled = pooled @ self.text_projection
636
+
637
+ if self.output_tokens:
638
+ return pooled, tokens
639
+
640
+ return pooled
641
+
642
+
643
+ class MultimodalTransformer(Transformer):
644
+ def __init__(
645
+ self,
646
+ width: int,
647
+ layers: int,
648
+ heads: int,
649
+ context_length: int = 77,
650
+ mlp_ratio: float = 4.0,
651
+ ls_init_value: float = None,
652
+ act_layer: Callable = nn.GELU,
653
+ norm_layer: Callable = LayerNorm,
654
+ output_dim: int = 512,
655
+ ):
656
+
657
+ super().__init__(
658
+ width=width,
659
+ layers=layers,
660
+ heads=heads,
661
+ mlp_ratio=mlp_ratio,
662
+ ls_init_value=ls_init_value,
663
+ act_layer=act_layer,
664
+ norm_layer=norm_layer,
665
+ )
666
+ self.context_length = context_length
667
+ self.cross_attn = nn.ModuleList([
668
+ ResidualAttentionBlock(
669
+ width,
670
+ heads,
671
+ mlp_ratio,
672
+ ls_init_value=ls_init_value,
673
+ act_layer=act_layer,
674
+ norm_layer=norm_layer,
675
+ is_cross_attention=True,
676
+ )
677
+ for _ in range(layers)
678
+ ])
679
+
680
+ self.register_buffer('attn_mask', self.build_attention_mask(), persistent=False)
681
+
682
+ self.ln_final = norm_layer(width)
683
+ self.text_projection = nn.Parameter(torch.empty(width, output_dim))
684
+
685
+ def init_parameters(self):
686
+ proj_std = (self.transformer.width ** -0.5) * ((2 * self.transformer.layers) ** -0.5)
687
+ attn_std = self.transformer.width ** -0.5
688
+ fc_std = (2 * self.transformer.width) ** -0.5
689
+ for block in self.transformer.resblocks:
690
+ nn.init.normal_(block.attn.in_proj_weight, std=attn_std)
691
+ nn.init.normal_(block.attn.out_proj.weight, std=proj_std)
692
+ nn.init.normal_(block.mlp.c_fc.weight, std=fc_std)
693
+ nn.init.normal_(block.mlp.c_proj.weight, std=proj_std)
694
+ for block in self.transformer.cross_attn:
695
+ nn.init.normal_(block.attn.in_proj_weight, std=attn_std)
696
+ nn.init.normal_(block.attn.out_proj.weight, std=proj_std)
697
+ nn.init.normal_(block.mlp.c_fc.weight, std=fc_std)
698
+ nn.init.normal_(block.mlp.c_proj.weight, std=proj_std)
699
+
700
+ if self.text_projection is not None:
701
+ nn.init.normal_(self.text_projection, std=self.transformer.width ** -0.5)
702
+
703
+ def build_attention_mask(self):
704
+ # lazily create causal attention mask, with full attention between the tokens
705
+ # pytorch uses additive attention mask; fill with -inf
706
+ mask = torch.empty(self.context_length, self.context_length)
707
+ mask.fill_(float("-inf"))
708
+ mask.triu_(1) # zero out the lower diagonal
709
+ return mask
710
+
711
+ def forward(self, image_embs, text_embs):
712
+ text_embs = text_embs.permute(1, 0, 2) # NLD -> LNDsq
713
+ image_embs = image_embs.permute(1, 0, 2) # NLD -> LND
714
+ seq_len = text_embs.shape[0]
715
+
716
+ for resblock, cross_attn in zip(self.resblocks, self.cross_attn):
717
+ if self.grad_checkpointing and not torch.jit.is_scripting():
718
+ # TODO: handle kwargs https://github.com/pytorch/pytorch/issues/79887#issuecomment-1161758372
719
+ text_embs = checkpoint(resblock, text_embs, None, None, self.attn_mask[:seq_len, :seq_len])
720
+ text_embs = checkpoint(cross_attn, text_embs, image_embs, image_embs, None)
721
+ else:
722
+ text_embs = resblock(text_embs, attn_mask=self.attn_mask[:seq_len, :seq_len])
723
+ text_embs = cross_attn(text_embs, k_x=image_embs, v_x=image_embs)
724
+
725
+ x = text_embs.permute(1, 0, 2) # LND -> NLD
726
+ x = self.ln_final(x)
727
+
728
+ if self.text_projection is not None:
729
+ x = x @ self.text_projection
730
+
731
+ return x
732
+
733
+ @torch.jit.ignore
734
+ def set_grad_checkpointing(self, enable=True):
735
+ self.grad_checkpointing = enable
native/open_clip/utils.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import repeat
2
+ import collections.abc
3
+
4
+ import torch
5
+ from torch import nn as nn
6
+ from torchvision.ops.misc import FrozenBatchNorm2d
7
+
8
+
9
+ def freeze_batch_norm_2d(module, module_match={}, name=''):
10
+ """
11
+ Converts all `BatchNorm2d` and `SyncBatchNorm` layers of provided module into `FrozenBatchNorm2d`. If `module` is
12
+ itself an instance of either `BatchNorm2d` or `SyncBatchNorm`, it is converted into `FrozenBatchNorm2d` and
13
+ returned. Otherwise, the module is walked recursively and submodules are converted in place.
14
+
15
+ Args:
16
+ module (torch.nn.Module): Any PyTorch module.
17
+ module_match (dict): Dictionary of full module names to freeze (all if empty)
18
+ name (str): Full module name (prefix)
19
+
20
+ Returns:
21
+ torch.nn.Module: Resulting module
22
+
23
+ Inspired by https://github.com/pytorch/pytorch/blob/a5895f85be0f10212791145bfedc0261d364f103/torch/nn/modules/batchnorm.py#L762
24
+ """
25
+ res = module
26
+ is_match = True
27
+ if module_match:
28
+ is_match = name in module_match
29
+ if is_match and isinstance(module, (nn.modules.batchnorm.BatchNorm2d, nn.modules.batchnorm.SyncBatchNorm)):
30
+ res = FrozenBatchNorm2d(module.num_features)
31
+ res.num_features = module.num_features
32
+ res.affine = module.affine
33
+ if module.affine:
34
+ res.weight.data = module.weight.data.clone().detach()
35
+ res.bias.data = module.bias.data.clone().detach()
36
+ res.running_mean.data = module.running_mean.data
37
+ res.running_var.data = module.running_var.data
38
+ res.eps = module.eps
39
+ else:
40
+ for child_name, child in module.named_children():
41
+ full_child_name = '.'.join([name, child_name]) if name else child_name
42
+ new_child = freeze_batch_norm_2d(child, module_match, full_child_name)
43
+ if new_child is not child:
44
+ res.add_module(child_name, new_child)
45
+ return res
46
+
47
+
48
+ # From PyTorch internals
49
+ def _ntuple(n):
50
+ def parse(x):
51
+ if isinstance(x, collections.abc.Iterable):
52
+ return x
53
+ return tuple(repeat(x, n))
54
+ return parse
55
+
56
+
57
+ to_1tuple = _ntuple(1)
58
+ to_2tuple = _ntuple(2)
59
+ to_3tuple = _ntuple(3)
60
+ to_4tuple = _ntuple(4)
61
+ to_ntuple = lambda n, x: _ntuple(n)(x)
62
+
63
+ # Replaces all linear layers with linear_replacement
64
+ # TODO: add int8 support for other linear layers including attn and convnets
65
+ def replace_linear(model, linear_replacement, include_modules=['c_fc', 'c_proj'], copy_weights=True):
66
+ for name, module in model.named_children():
67
+ if len(list(module.children())) > 0:
68
+ replace_linear(module, linear_replacement, include_modules, copy_weights)
69
+
70
+ if isinstance(module, torch.nn.Linear) and name in include_modules:
71
+ old_module = model._modules[name]
72
+ model._modules[name] = linear_replacement(
73
+ module.in_features,
74
+ module.out_features,
75
+ module.bias is not None,
76
+ )
77
+ if copy_weights:
78
+ model._modules[name].weight.data.copy_(old_module.weight.data)
79
+ if model._modules[name].bias is not None:
80
+ model._modules[name].bias.data.copy_(old_module.bias)
81
+
82
+ return model
83
+
84
+ def convert_int8_model_to_inference_mode(model):
85
+ for m in model.modules():
86
+ if hasattr(m, 'prepare_for_eval'):
87
+ int8_original_dtype = m.weight.dtype
88
+ m.prepare_for_eval()
89
+ m.int8_original_dtype = int8_original_dtype
native/utils/__init__.py ADDED
File without changes
native/utils/config.py ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import torch
4
+ import random
5
+ import numpy as np
6
+ from tap import Tap
7
+ from typing import Optional, Union
8
+ from collections import OrderedDict
9
+
10
+ from native.utils import dist
11
+
12
+
13
+ class Args(Tap):
14
+ model: str = 'vitamin_large' # 'vitamin_base', 'vitamin_large', xxx
15
+ exp_name: str = 'unitok_large'
16
+ output_dir: str = 'local_output'
17
+ resume_from: str = '' # if specified, load this checkpoint; if not, load the latest checkpoint in output_dir (if exists)
18
+ lpips_path: str = 'external/lpips_with_vgg.pth'
19
+ dino_path: str = 'external/dinov2_vits14_pretrain.pth'
20
+ fid_eval_src: str = ''
21
+ fid_eval_dst: str = ''
22
+ vis_img_dir: str = 'asset/vis_imgs/'
23
+ fid_feature_extractor: str = 'external/weights-inception-2015-12-05-6726825d.pth'
24
+ clip_pretrain_path: str = ''
25
+
26
+ # speed-up
27
+ fp16: bool = False # whether to use FP16
28
+ bf16: bool = True # whether to use BF16
29
+ tf32: bool = True # whether to use TensorFloat32
30
+ compile_model: bool = False # whether to use torch.compile()
31
+ ddp_static: bool = False # whether to use static graph in DDP
32
+ grad_ckpt: bool = True # gradient checkpointing
33
+ grad_accu: int = 1 # gradient accumulation
34
+ device: str = 'cpu' # will be set automatically
35
+ dtype: torch.dtype = torch.float32 # will be set automatically
36
+
37
+ # data
38
+ train_data: str = None
39
+ val_data: str = None
40
+ dataset_type: str = 'webdataset'
41
+ imagenet_val: str = None
42
+ imagenet_v2: str = None
43
+ subset_ratio: float = 1.0
44
+ img_size: int = 256
45
+ resize_ratio: float = 1.125 # only applicable to 'img' dataset_type
46
+ hflip: bool = False
47
+ workers: int = 8 # num workers; 0: auto, -1: don't use multiprocessing in DataLoader
48
+ train_num_samples: int = 1280_000_000
49
+ train_data_upsampling_factors: str = None
50
+ dataset_resampled: bool = False
51
+ use_aug: bool = False
52
+
53
+ # quantizer
54
+ vocab_size: int = 32768
55
+ vocab_width: int = 64
56
+ vocab_norm: bool = True
57
+ vq_beta: float = 0.25 # commitment loss weight
58
+ num_codebooks: int = 8
59
+ quant_proj: str = 'attn'
60
+
61
+ # model
62
+ embed_dim: int = 768
63
+ num_query: int = 0
64
+ use_clip_pretrain: bool = False
65
+ patch_size: int = 16
66
+ drop_path: float = 0.1
67
+ text_width: int = 768
68
+ text_heads: int = 12
69
+ text_layers: int = 12
70
+ text_vocab_size: int = 49408
71
+ text_context_length: int = 77
72
+
73
+ # CLIP
74
+ local_loss: bool = True
75
+ gather_with_grad: bool = True
76
+ pretrained_clip: str = None
77
+ pretrained_clip_text: str = None
78
+ lock_text: bool = False
79
+ lock_text_unlocked_layers: int = 0
80
+ lock_text_freeze_layer_norm: bool = False
81
+ force_custom_text: bool = False
82
+ force_custom_vision: bool = False
83
+ zeroshot_eval_freq: int = 1
84
+
85
+ # discriminator
86
+ dino_depth: int = 12
87
+ dino_kernel_size: int = 9
88
+ disc_norm: str = 'gn' # gn: group norm, bn: batch norm, sbn: sync batch norm, hbn: hybrid sync batch norm
89
+ disc_aug_prob: float = 1.0
90
+ disc_specnorm: bool = False
91
+ step_disc_every: int = 1
92
+
93
+ # initialization
94
+ vae_init: float = -0.5 # <0: xavier_normal_(gain=abs(init)); >0: trunc_normal_(std=init)
95
+ vocab_init: float = -1 # <0: uniform(-abs(init)*base, abs(init)*base), where base = 20/vocab_size; >0: trunc_normal_(std=init)
96
+ disc_init: float = -0.5 # <0: xavier_normal_(gain=abs(init)); >0: trunc_normal_(std=init)
97
+
98
+ # optimization
99
+ epoch: int = 1 # number of epochs
100
+ local_bs: int = 64 # batch size per device; if this is specified, --global_bs will be ignored
101
+ vae_local_bs: int = 64 # sub-batch size for vae loss calculation
102
+ global_bs: int = 0 # global batch size (exclusive to --local_bs)
103
+ lr: float = 5e-4 # learning rate
104
+ wd: float = 0.02 # weight decay
105
+ disc_lr: float = 2e-5 # disc lr
106
+ disc_wd: float = 0.2
107
+ grad_clip: float = 10 # <=0 for not using grad clip
108
+ ema: float = 0.9999 # ema ratio
109
+ warmup_iter: int = None
110
+ warmup_ep: float = 0.01 # lr warmup: epochs
111
+ disc_start_ep: float = 0.375 # start using disc loss for VAE after xxx epochs;
112
+ disc_warmup_ep: float = 0.03 # disc loss warm up epochs;
113
+ schedule: str = 'cos' # lr schedule type
114
+ lr_start_ratio: float = 0. # lr warmup: initial lr ratio
115
+ lr_end_ratio: float = 0.1 # lr schedule: final lr ratio
116
+ disc_lr_end_ratio: float = 0.1
117
+ custom_lr_multiplier: float = None
118
+ optimizer: str = 'adamw'
119
+ optim_eps: float = 1e-6
120
+ fuse_opt: bool = False # whether to use fused optimizer
121
+ optim_beta: str = '0.9_0.95' # beta1, beta2 of optimizer
122
+ disc_optim_beta: str = '0.5_0.9' # beta1, beta2 of disc optimizer
123
+
124
+ # loss
125
+ l1: float = 0.2 # L1 rec loss weight
126
+ l2: float = 1.0 # L2 rec loss weight
127
+ lp: float = 1.0 # lpips loss weight
128
+ lpr: int = 48 # only calculate lpips >= this image resolution
129
+ ld: float = 0.4 # discriminator loss weight; if <0: NO ADAPTIVE WEIGHT
130
+ le: float = 0.0 # VQ entropy loss weight
131
+ lq: float = 1.0
132
+ lc: float = 1.0 # CLIP loss weight
133
+ e_temp: float = 0.01
134
+ gada: int = 1
135
+ bcr: float = 4. # balanced Consistency Regularization, used on small dataset with low reso, StyleSwin: 10.0
136
+ bcr_cut: float = 0.2 # cutout ratio (0.5: 50% width)
137
+ dcrit: str = 'hg' # hg hinge, sp softplus, ln linear
138
+
139
+ # wandb log
140
+ report_wandb: bool = True
141
+ wandb_notes: str = None
142
+ run_id: str = None
143
+
144
+ # debug
145
+ eval_per_epoch: int = 8
146
+ dbg_unused_param: bool = False
147
+ dbg_nan: bool = False # 'KEVIN_LOCAL' in os.environ
148
+ seed: int = None
149
+ deterministic: bool = False
150
+ same_seed_for_all_ranks: int = 0 # this is only for distributed sampler
151
+
152
+
153
+ def seed_everything(self):
154
+ torch.backends.cudnn.enabled = True
155
+ torch.backends.cudnn.benchmark = True
156
+ torch.backends.cudnn.deterministic = False
157
+ if self.seed is not None:
158
+ if self.deterministic:
159
+ torch.backends.cudnn.benchmark = False
160
+ torch.backends.cudnn.deterministic = True
161
+ torch.use_deterministic_algorithms(True)
162
+ os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':16:8'
163
+ seed = self.seed + dist.get_rank() * 10000
164
+ os.environ['PYTHONHASHSEED'] = str(seed)
165
+ random.seed(seed)
166
+ np.random.seed(seed)
167
+ torch.manual_seed(seed)
168
+ torch.cuda.manual_seed(seed)
169
+ torch.cuda.manual_seed_all(seed)
170
+
171
+ def get_different_generator_for_each_rank(self) -> Optional[torch.Generator]: # for random augmentation
172
+ if self.seed is None:
173
+ return None
174
+ g = torch.Generator()
175
+ g.manual_seed(self.seed * dist.get_world_size() + dist.get_rank())
176
+ return g
177
+
178
+ def state_dict(self, key_ordered=True) -> Union[OrderedDict, dict]:
179
+ d = (OrderedDict if key_ordered else dict)()
180
+ for k in self.class_variables.keys():
181
+ if k not in {'device'}: # these are not serializable
182
+ d[k] = getattr(self, k)
183
+ return d
184
+
185
+ def load_state_dict(self, state_dict):
186
+ for k, v in state_dict.items():
187
+ try:
188
+ setattr(self, k, v)
189
+ except Exception as e:
190
+ print(f'k={k}, v={v}')
191
+ raise e
192
+
193
+ @staticmethod
194
+ def set_tf32(tf32: bool):
195
+ if torch.cuda.is_available():
196
+ torch.backends.cudnn.allow_tf32 = bool(tf32)
197
+ torch.backends.cuda.matmul.allow_tf32 = bool(tf32)
198
+ if hasattr(torch, 'set_float32_matmul_precision'):
199
+ torch.set_float32_matmul_precision('high' if tf32 else 'highest')
200
+ print(f'[tf32] [precis] torch.get_float32_matmul_precision(): {torch.get_float32_matmul_precision()}')
201
+ print(f'[tf32] [ conv ] torch.backends.cudnn.allow_tf32: {torch.backends.cudnn.allow_tf32}')
202
+ print(f'[tf32] [matmul] torch.backends.cuda.matmul.allow_tf32: {torch.backends.cuda.matmul.allow_tf32}')
203
+
204
+ def __str__(self):
205
+ s = []
206
+ for k in self.class_variables.keys():
207
+ if k not in {'device', 'dbg_ks_fp'}: # these are not serializable
208
+ s.append(f' {k:20s}: {getattr(self, k)}')
209
+ s = '\n'.join(s)
210
+ return f'{{\n{s}\n}}\n'
211
+
212
+
213
+ def init_dist_and_get_args():
214
+ for i in range(len(sys.argv)):
215
+ if sys.argv[i].startswith('--local-rank=') or sys.argv[i].startswith('--local_rank='):
216
+ del sys.argv[i]
217
+ break
218
+
219
+ args = Args(explicit_bool=True).parse_args(known_only=True)
220
+ # warn args.extra_args
221
+ if len(args.extra_args) > 0:
222
+ print(f'======================================================================================')
223
+ print(f'=========================== WARNING: UNEXPECTED EXTRA ARGS ===========================\n{args.extra_args}')
224
+ print(f'=========================== WARNING: UNEXPECTED EXTRA ARGS ===========================')
225
+ print(f'======================================================================================\n\n')
226
+
227
+ # init torch distributed
228
+ os.makedirs(args.output_dir, exist_ok=True)
229
+ dist.init_distributed_mode(local_out_path=args.output_dir, timeout_minutes=30)
230
+
231
+ # set env
232
+ args.set_tf32(args.tf32)
233
+ args.seed_everything()
234
+ args.device = dist.get_device()
235
+
236
+ # update args
237
+ if args.local_bs == 0:
238
+ args.local_bs = max(1, round(args.global_bs / args.grad_accu / dist.get_world_size()))
239
+ args.global_bs = args.local_bs * dist.get_world_size()
240
+ if args.fp16 or args.bf16:
241
+ args.dtype = torch.float16 if args.fp16 else torch.bfloat16
242
+
243
+ return args
native/utils/dist.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import datetime
2
+ import functools
3
+ import os
4
+ import sys
5
+ from typing import List
6
+ from typing import Union
7
+
8
+ import pytz
9
+ import torch
10
+ import torch.distributed as tdist
11
+ import torch.multiprocessing as mp
12
+
13
+ __rank, __local_rank, __world_size, __device = 0, 0, 1, 'cuda' if torch.cuda.is_available() else 'cpu'
14
+ __rank_str_zfill = '0'
15
+ __initialized = False
16
+
17
+
18
+ def initialized():
19
+ return __initialized
20
+
21
+
22
+ def __initialize(fork=False, backend='nccl', gpu_id_if_not_distibuted=0, timeout_minutes=30):
23
+ global __device
24
+ if not torch.cuda.is_available():
25
+ print(f'[dist initialize] cuda is not available, use cpu instead', file=sys.stderr)
26
+ return
27
+ elif 'RANK' not in os.environ:
28
+ torch.cuda.set_device(gpu_id_if_not_distibuted)
29
+ __device = torch.empty(1).cuda().device
30
+ print(f'[dist initialize] env variable "RANK" is not set, use {__device} as the device', file=sys.stderr)
31
+ return
32
+ # then 'RANK' must exist
33
+ global_rank, num_gpus = int(os.environ['RANK']), torch.cuda.device_count()
34
+ local_rank = global_rank % num_gpus
35
+ torch.cuda.set_device(local_rank)
36
+
37
+ # ref: https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/dist_utils.py#L29
38
+ if mp.get_start_method(allow_none=True) is None:
39
+ method = 'fork' if fork else 'spawn'
40
+ print(f'[dist initialize] mp method={method}')
41
+ mp.set_start_method(method)
42
+ tdist.init_process_group(backend=backend, timeout=datetime.timedelta(seconds=timeout_minutes * 60))
43
+
44
+ global __rank, __local_rank, __world_size, __initialized, __rank_str_zfill
45
+ __local_rank = local_rank
46
+ __rank, __world_size = tdist.get_rank(), tdist.get_world_size()
47
+ __rank_str_zfill = str(__rank).zfill(len(str(__world_size)))
48
+ __device = torch.empty(1).cuda().device
49
+ __initialized = True
50
+
51
+ assert tdist.is_initialized(), 'torch.distributed is not initialized!'
52
+ print(f'[lrk={get_local_rank()}, rk={get_rank()}]')
53
+
54
+
55
+ def get_rank():
56
+ return __rank
57
+
58
+
59
+ def get_rank_str_zfill():
60
+ return __rank_str_zfill
61
+
62
+
63
+ def get_local_rank():
64
+ return __local_rank
65
+
66
+
67
+ def get_world_size():
68
+ return __world_size
69
+
70
+
71
+ def get_device():
72
+ return __device
73
+
74
+
75
+ def set_gpu_id(gpu_id: int):
76
+ if gpu_id is None: return
77
+ global __device
78
+ if isinstance(gpu_id, (str, int)):
79
+ torch.cuda.set_device(int(gpu_id))
80
+ __device = torch.empty(1).cuda().device
81
+ else:
82
+ raise NotImplementedError
83
+
84
+
85
+ def is_master():
86
+ return __rank == 0
87
+
88
+
89
+ def is_local_master():
90
+ return __local_rank == 0
91
+
92
+
93
+ def new_group(ranks: List[int]):
94
+ if __initialized:
95
+ return tdist.new_group(ranks=ranks)
96
+ return None
97
+
98
+
99
+ def new_local_machine_group():
100
+ if __initialized:
101
+ cur_subgroup, subgroups = tdist.new_subgroups()
102
+ return cur_subgroup
103
+ return None
104
+
105
+
106
+ def barrier():
107
+ if __initialized:
108
+ tdist.barrier()
109
+
110
+
111
+ def allreduce(t: torch.Tensor, async_op=False):
112
+ if __initialized:
113
+ if not t.is_cuda:
114
+ cu = t.detach().cuda()
115
+ ret = tdist.all_reduce(cu, async_op=async_op)
116
+ t.copy_(cu.cpu())
117
+ else:
118
+ ret = tdist.all_reduce(t, async_op=async_op)
119
+ return ret
120
+ return None
121
+
122
+
123
+ def allgather(t: torch.Tensor, cat=True) -> Union[List[torch.Tensor], torch.Tensor]:
124
+ if __initialized:
125
+ if not t.is_cuda:
126
+ t = t.cuda()
127
+ ls = [torch.empty_like(t) for _ in range(__world_size)]
128
+ tdist.all_gather(ls, t)
129
+ else:
130
+ ls = [t]
131
+ if cat:
132
+ ls = torch.cat(ls, dim=0)
133
+ return ls
134
+
135
+
136
+ def allgather_diff_shape(t: torch.Tensor, cat=True) -> Union[List[torch.Tensor], torch.Tensor]:
137
+ if __initialized:
138
+ if not t.is_cuda:
139
+ t = t.cuda()
140
+
141
+ t_size = torch.tensor(t.size(), device=t.device)
142
+ ls_size = [torch.empty_like(t_size) for _ in range(__world_size)]
143
+ tdist.all_gather(ls_size, t_size)
144
+
145
+ max_B = max(size[0].item() for size in ls_size)
146
+ pad = max_B - t_size[0].item()
147
+ if pad:
148
+ pad_size = (pad, *t.size()[1:])
149
+ t = torch.cat((t, t.new_empty(pad_size)), dim=0)
150
+
151
+ ls_padded = [torch.empty_like(t) for _ in range(__world_size)]
152
+ tdist.all_gather(ls_padded, t)
153
+ ls = []
154
+ for t, size in zip(ls_padded, ls_size):
155
+ ls.append(t[:size[0].item()])
156
+ else:
157
+ ls = [t]
158
+ if cat:
159
+ ls = torch.cat(ls, dim=0)
160
+ return ls
161
+
162
+
163
+ def broadcast(t: torch.Tensor, src_rank) -> None:
164
+ if __initialized:
165
+ if not t.is_cuda:
166
+ cu = t.detach().cuda()
167
+ tdist.broadcast(cu, src=src_rank)
168
+ t.copy_(cu.cpu())
169
+ else:
170
+ tdist.broadcast(t, src=src_rank)
171
+
172
+
173
+ def dist_fmt_vals(val: float, fmt: Union[str, None] = '%.2f') -> Union[torch.Tensor, List]:
174
+ if not initialized():
175
+ return torch.tensor([val]) if fmt is None else [fmt % val]
176
+
177
+ ts = torch.zeros(__world_size)
178
+ ts[__rank] = val
179
+ allreduce(ts)
180
+ if fmt is None:
181
+ return ts
182
+ return [fmt % v for v in ts.cpu().numpy().tolist()]
183
+
184
+
185
+ def master_only(func):
186
+ @functools.wraps(func)
187
+ def wrapper(*args, **kwargs):
188
+ force = kwargs.pop('force', False)
189
+ if force or is_master():
190
+ ret = func(*args, **kwargs)
191
+ else:
192
+ ret = None
193
+ barrier()
194
+ return ret
195
+ return wrapper
196
+
197
+
198
+ def local_master_only(func):
199
+ @functools.wraps(func)
200
+ def wrapper(*args, **kwargs):
201
+ force = kwargs.pop('force', False)
202
+ if force or is_local_master():
203
+ ret = func(*args, **kwargs)
204
+ else:
205
+ ret = None
206
+ barrier()
207
+ return ret
208
+ return wrapper
209
+
210
+
211
+ def for_visualize(func):
212
+ @functools.wraps(func)
213
+ def wrapper(*args, **kwargs):
214
+ if is_master():
215
+ # with torch.no_grad():
216
+ ret = func(*args, **kwargs)
217
+ else:
218
+ ret = None
219
+ return ret
220
+ return wrapper
221
+
222
+
223
+ def finalize():
224
+ if __initialized:
225
+ tdist.destroy_process_group()
226
+
227
+
228
+ def init_distributed_mode(local_out_path, only_sync_master=False, timeout_minutes=30):
229
+ try:
230
+ __initialize(fork=False, timeout_minutes=timeout_minutes)
231
+ barrier()
232
+ except RuntimeError as e:
233
+ print(f'{"!"*80} dist init error (NCCL Error?), stopping training! {"!"*80}', flush=True)
234
+ raise e
235
+
236
+ if local_out_path is not None: os.makedirs(local_out_path, exist_ok=True)
237
+ _change_builtin_print(is_local_master())
238
+ if (is_master() if only_sync_master else is_local_master()) and local_out_path is not None and len(local_out_path):
239
+ sys.stdout, sys.stderr = BackupStreamToFile(local_out_path, for_stdout=True), BackupStreamToFile(local_out_path, for_stdout=False)
240
+
241
+
242
+ def _change_builtin_print(is_master):
243
+ import builtins as __builtin__
244
+
245
+ builtin_print = __builtin__.print
246
+ if type(builtin_print) != type(open):
247
+ return
248
+
249
+ def prt(*args, **kwargs):
250
+ force = kwargs.pop('force', False)
251
+ clean = kwargs.pop('clean', False)
252
+ deeper = kwargs.pop('deeper', False)
253
+ if is_master or force:
254
+ if not clean:
255
+ f_back = sys._getframe().f_back
256
+ if deeper and f_back.f_back is not None:
257
+ f_back = f_back.f_back
258
+ file_desc = f'{f_back.f_code.co_filename:24s}'[-24:]
259
+ time_str = datetime.datetime.now(tz=pytz.timezone('Asia/Shanghai')).strftime('[%m-%d %H:%M:%S]')
260
+ builtin_print(f'{time_str} ({file_desc}, line{f_back.f_lineno:-4d})=>', *args, **kwargs)
261
+ else:
262
+ builtin_print(*args, **kwargs)
263
+
264
+ __builtin__.print = prt
265
+
266
+
267
+ class BackupStreamToFile(object):
268
+ def __init__(self, local_output_dir, for_stdout=True):
269
+ self.for_stdout = for_stdout
270
+ self.terminal_stream = sys.stdout if for_stdout else sys.stderr
271
+ fname = os.path.join(local_output_dir, 'backup1_stdout.txt' if for_stdout else 'backup2_stderr.txt')
272
+ existing = os.path.exists(fname)
273
+ self.file_stream = open(fname, 'a')
274
+ if existing:
275
+ time_str = datetime.datetime.now(tz=pytz.timezone('Asia/Shanghai')).strftime('[%m-%d %H:%M:%S]')
276
+ self.file_stream.write('\n'*7 + '='*55 + f' RESTART {time_str} ' + '='*55 + '\n')
277
+ self.file_stream.flush()
278
+ self.enabled = True
279
+
280
+ def write(self, message):
281
+ self.terminal_stream.write(message)
282
+ self.file_stream.write(message)
283
+
284
+ def flush(self):
285
+ self.terminal_stream.flush()
286
+ self.file_stream.flush()
287
+
288
+ def close(self):
289
+ if not self.enabled:
290
+ return
291
+ self.enabled = False
292
+ self.file_stream.flush()
293
+ self.file_stream.close()
294
+ if self.for_stdout:
295
+ sys.stdout = self.terminal_stream
296
+ sys.stdout.flush()
297
+ else:
298
+ sys.stderr = self.terminal_stream
299
+ sys.stderr.flush()
300
+
301
+ def __del__(self):
302
+ self.close()
preprocessor_config.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "reference": "https://github.com/FoundationVision/UniTok/blob/bb8012e8e8d7be66928bedd80506c56a55ea6305/utils/data.py",
3
+ "reference_function": "build_transforms: preprocess_val",
4
+ "resize_shortest_edge": 256,
5
+ "resize_interpolation": "PIL Lanczos",
6
+ "center_crop": [256, 256],
7
+ "convert_to_rgb": true,
8
+ "to_tensor_scale": "uint8 / 255",
9
+ "normalization": "tensor * 2 - 1",
10
+ "layout": "NCHW",
11
+ "notes": "This describes the native deterministic validation transform, not every downstream MLLM prompt processor."
12
+ }
release_manifest.json ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "as_of": "2026-10-21",
3
+ "files": {
4
+ "model.safetensors": {
5
+ "size": 3281477448,
6
+ "sha256": "364bbd3e3f453f19dba447805f347aabdd7abdc7da20186364f5b2961c541b5c"
7
+ },
8
+ "tensor_mapping.json": {
9
+ "size": 247482,
10
+ "sha256": "b1bd76fbe4fd06157d4e6384d619c604b7bb498decf756fb2d76f79226dbffe8"
11
+ },
12
+ "OPENCLIP_LICENSE": {
13
+ "size": 1195,
14
+ "sha256": "d4a357b7ebcb1eec1502d4ee0dc79c73469e126937cf66d5df4c20a9319b5217"
15
+ },
16
+ "code_provenance.json": {
17
+ "size": 3602,
18
+ "sha256": "5592e18e7f3e1dcd5c5844047c5ee80235d1ae78e642cb3cf10f9052601a3c25"
19
+ },
20
+ "LICENSE": {
21
+ "size": 11540,
22
+ "sha256": "76510cc65779339b0a4d3a510200eb18e8dd5c79dc3ad7958530da499160ad2c"
23
+ },
24
+ "requirements.txt": {
25
+ "size": 93,
26
+ "sha256": "6820fdfb476caef1b0553cb1929da16e2ee19caad3c32fa623b632edbe3d29d2"
27
+ },
28
+ "config.json": {
29
+ "size": 2590,
30
+ "sha256": "4713288a6bcc010b0977fa6bd5f95ee5c0a23d283cdc248afcba08dccf8344c2"
31
+ },
32
+ "tensor_sha256.json": {
33
+ "size": 143621,
34
+ "sha256": "4386911017688407b41fb0e295846a3db213a799405256eb56e5b519bf22cf26"
35
+ },
36
+ "README.md": {
37
+ "size": 3631,
38
+ "sha256": "023fbaf021040ff8f7d95ec4a5dabb048df2fbe4e48e5f004893f1faae68da24"
39
+ },
40
+ "validate.py": {
41
+ "size": 3551,
42
+ "sha256": "7ed5f8c436d9fbb602b84a763c4c3d131ca03ca97c5dfa5dc1bceb4156572379"
43
+ },
44
+ "validation.json": {
45
+ "size": 1995,
46
+ "sha256": "c7465f3e73c9ce27928fe9c2dc0e7460ce3c9b30e2d4b08715c599a6e000ff05"
47
+ },
48
+ "CODE_LICENSE": {
49
+ "size": 1072,
50
+ "sha256": "a318f4da9d043e05f535b65774febde7572908c97562cc14e49a2f3687f5a453"
51
+ },
52
+ "extraction.json": {
53
+ "size": 953,
54
+ "sha256": "f689cf9bb9e6f90e65c66fa9306ea15792609d294b2489fe9f5dbeb36204cd2f"
55
+ },
56
+ "component.py": {
57
+ "size": 735,
58
+ "sha256": "abd48d6e68f4f2e1371c793882e540e24ed68c8ca5ce94632976cf7f032e0d88"
59
+ },
60
+ "preprocessor_config.json": {
61
+ "size": 524,
62
+ "sha256": "3a8598551509dff8de647325f146892b2e47ed8017e1e707074df9a3dea9a97d"
63
+ },
64
+ "native/__init__.py": {
65
+ "size": 0,
66
+ "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
67
+ },
68
+ "native/utils/config.py": {
69
+ "size": 9535,
70
+ "sha256": "2776f3ea4baf57708dbaf385568ad26fc10be61b7f1b01e8a9d897b3d4242aed"
71
+ },
72
+ "native/utils/__init__.py": {
73
+ "size": 0,
74
+ "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
75
+ },
76
+ "native/utils/dist.py": {
77
+ "size": 8957,
78
+ "sha256": "4ad98f72e600a666795eb074329f4e6cfd8c182776fb7f9e4d9d39ae2dd83524"
79
+ },
80
+ "native/models/unitok.py": {
81
+ "size": 7009,
82
+ "sha256": "bc18a150c277e85a052bf03ed09cc6c4ecc51745cd926cb680b4d352d03fc0ac"
83
+ },
84
+ "native/models/__init__.py": {
85
+ "size": 0,
86
+ "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
87
+ },
88
+ "native/models/vitamin.py": {
89
+ "size": 27654,
90
+ "sha256": "bd10601ae44739f7c67273c1f2c783e9205ecc23440f63b6dc50fdd4abae896f"
91
+ },
92
+ "native/models/vqvae.py": {
93
+ "size": 6425,
94
+ "sha256": "1afad9ee7f0624c1af6265c245c54d89d9a732f58fa3b2c5c2c8db7d9e00fdc3"
95
+ },
96
+ "native/models/quant.py": {
97
+ "size": 7558,
98
+ "sha256": "fcd9fa6a76a962eab32c33a7deb4205dfaac019f8816be41951f0604e2343b3b"
99
+ },
100
+ "native/open_clip/source_model.py": {
101
+ "size": 23383,
102
+ "sha256": "2a7bc461f7cfe70497e7ce8541f75a8b67dcbd3d93685cfd8671ebf98d337150"
103
+ },
104
+ "native/open_clip/__init__.py": {
105
+ "size": 0,
106
+ "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
107
+ },
108
+ "native/open_clip/utils.py": {
109
+ "size": 3472,
110
+ "sha256": "f0267bb9d5402b1b5b38a90f96f6cd9002ac21d2db0f3f3fbee8ed48b2f46047"
111
+ },
112
+ "native/open_clip/transformer.py": {
113
+ "size": 27556,
114
+ "sha256": "dfaef403435d5766aeea8286fca0f9fc46285ee25fd28e87ba197bf51f1f676e"
115
+ }
116
+ }
117
+ }
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ torch>=2.2
2
+ torchvision
3
+ safetensors>=0.4
4
+ timm==1.0.8
5
+ typed-argument-parser==1.12.0
6
+ pytz
7
+ numpy
tensor_mapping.json ADDED
The diff for this file is too large to render. See raw diff
 
tensor_sha256.json ADDED
The diff for this file is too large to render. See raw diff
 
validate.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib, json, time
2
+ from pathlib import Path
3
+ import torch
4
+ from importlib.metadata import version
5
+ torch.set_num_threads(4)
6
+ from safetensors import safe_open
7
+ from component import load_model
8
+
9
+ root=Path(__file__).parent
10
+ started=time.monotonic(); checks=[]
11
+ def check(name,ok,**details):
12
+ assert ok,(name,details)
13
+ checks.append({'name':name,'passed':True,**details});print(name,details,flush=True)
14
+
15
+ hashes=json.loads((root/'tensor_sha256.json').read_text())
16
+ with safe_open(root/'model.safetensors',framework='pt') as f:
17
+ assert set(f.keys())==set(hashes)
18
+ for k in f.keys():
19
+ t=f.get_tensor(k)
20
+ assert hashlib.sha256(t.contiguous().reshape(-1).view(torch.uint8).numpy().tobytes()).hexdigest()==hashes[k],k
21
+ check('1335_source_tensor_payloads',len(hashes)==1335)
22
+ m=load_model(root)
23
+ check('native_strict_load',len(m.state_dict())==1335)
24
+ assert type(m.text_encoder).__module__=='native.open_clip.transformer'
25
+ check('eight_4096_by_8_codebooks',len(m.quantizer.codebooks)==8 and all(tuple(c.codebook.weight.shape)==(4096,8) for c in m.quantizer.codebooks))
26
+ with torch.inference_mode():
27
+ x=torch.linspace(-1,1,3*256*256).reshape(1,3,256,256)
28
+ ids=m.img_to_idx(x)
29
+ check('token_geometry',tuple(ids.shape)==(1,8,256),shape=list(ids.shape))
30
+ check('per_codebook_ID_bounds',ids.min().item()>=0 and ids.max().item()<4096)
31
+ q=m.quantizer.idx_to_f(ids)
32
+ canonical=torch.cat([c.codebook(ids[:,i]) for i,c in enumerate(m.quantizer.codebooks)],dim=-1)
33
+ check('normalized_lookup_exact',torch.equal(q,canonical))
34
+ rgb=m.idx_to_img(ids)
35
+ direct=m.decoder(m.post_quant_proj(canonical)).clamp(-1,1)
36
+ check('decode_ID_lookup_exact',torch.equal(rgb,direct))
37
+ check('finite_RGB_reconstruction',rgb.shape==x.shape and torch.isfinite(rgb).all().item(),mse=(rgb-x).square().mean().item())
38
+ reconstructed=m.img_to_reconstructed_img(x)
39
+ check('native_quantizer_reconstruction_bound',torch.allclose(reconstructed,rgb,atol=2e-5,rtol=2e-5),max_abs=(rgb-reconstructed).abs().max().item())
40
+ visual=m.encode_image(x,normalize=True)
41
+ text=torch.zeros((1,m.context_length),dtype=torch.long)
42
+ text[0,0]=49406;text[0,1]=49407
43
+ textual=m.encode_text(text,normalize=True)
44
+ check('semantic_image_features',tuple(visual.shape)==(1,768) and torch.isfinite(visual).all().item() and torch.allclose(visual.norm(dim=-1),torch.ones(1),atol=1e-5))
45
+ check('semantic_text_features',tuple(textual.shape)==(1,768) and torch.isfinite(textual).all().item() and torch.allclose(textual.norm(dim=-1),torch.ones(1),atol=1e-5))
46
+ report={'strict_load':True,'source_tensor_bytes_equal':True,'checks':checks,'smoke':{'passed':True,'device':'cpu','dtype':'float32','resolution':[256,256]},'environment':{p:version(p) for p in ['torch','torchvision','timm','safetensors','typed-argument-parser','pytz']},'text_implementation':'Unchanged official project-vendored native.open_clip.transformer.TextTransformer, same configured default constructor branch. External OpenCLIP is not imported.','elapsed_seconds':time.monotonic()-started,'limitations':['Synthetic native codec and semantic feature fixtures; no downstream image quality, retrieval, VQA or text-to-image benchmark.','This is the complete trained tokenizer mapping including its semantic text tower; no language generator is included.','The eight codebooks each have 4096 entries of width 8. The sum of entries is 32768; IDs are local to each codebook.']}
47
+ (root/'validation.json').write_text(json.dumps(report,indent=2)+'\n')
48
+ print('UNITOK VALIDATED',len(checks),flush=True)
validation.json ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "strict_load": true,
3
+ "source_tensor_bytes_equal": true,
4
+ "checks": [
5
+ {
6
+ "name": "1335_source_tensor_payloads",
7
+ "passed": true
8
+ },
9
+ {
10
+ "name": "native_strict_load",
11
+ "passed": true
12
+ },
13
+ {
14
+ "name": "eight_4096_by_8_codebooks",
15
+ "passed": true
16
+ },
17
+ {
18
+ "name": "token_geometry",
19
+ "passed": true,
20
+ "shape": [
21
+ 1,
22
+ 8,
23
+ 256
24
+ ]
25
+ },
26
+ {
27
+ "name": "per_codebook_ID_bounds",
28
+ "passed": true
29
+ },
30
+ {
31
+ "name": "normalized_lookup_exact",
32
+ "passed": true
33
+ },
34
+ {
35
+ "name": "decode_ID_lookup_exact",
36
+ "passed": true
37
+ },
38
+ {
39
+ "name": "finite_RGB_reconstruction",
40
+ "passed": true,
41
+ "mse": 0.0007349226507358253
42
+ },
43
+ {
44
+ "name": "native_quantizer_reconstruction_bound",
45
+ "passed": true,
46
+ "max_abs": 1.0728836059570312e-06
47
+ },
48
+ {
49
+ "name": "semantic_image_features",
50
+ "passed": true
51
+ },
52
+ {
53
+ "name": "semantic_text_features",
54
+ "passed": true
55
+ }
56
+ ],
57
+ "smoke": {
58
+ "passed": true,
59
+ "device": "cpu",
60
+ "dtype": "float32",
61
+ "resolution": [
62
+ 256,
63
+ 256
64
+ ]
65
+ },
66
+ "environment": {
67
+ "torch": "2.14.1",
68
+ "torchvision": "0.29.1",
69
+ "timm": "1.0.8",
70
+ "safetensors": "0.8.0",
71
+ "typed-argument-parser": "1.12.0",
72
+ "pytz": "2026.5"
73
+ },
74
+ "text_implementation": "Unchanged official project-vendored native.open_clip.transformer.TextTransformer, same configured default constructor branch. External OpenCLIP is not imported.",
75
+ "elapsed_seconds": 90.44274949999817,
76
+ "limitations": [
77
+ "Synthetic native codec and semantic feature fixtures; no downstream image quality, retrieval, VQA or text-to-image benchmark.",
78
+ "This is the complete trained tokenizer mapping including its semantic text tower; no language generator is included.",
79
+ "The eight codebooks each have 4096 entries of width 8. The sum of entries is 32768; IDs are local to each codebook."
80
+ ]
81
+ }