Publish pinned visual components with exact source payloads and native numerical validation
Browse files- CODE_LICENSE +21 -0
- LICENSE +204 -0
- OPENCLIP_LICENSE +24 -0
- README.md +40 -0
- code_provenance.json +66 -0
- component.py +17 -0
- config.json +116 -0
- extraction.json +15 -0
- model.safetensors +3 -0
- native/__init__.py +0 -0
- native/models/__init__.py +0 -0
- native/models/quant.py +185 -0
- native/models/unitok.py +185 -0
- native/models/vitamin.py +793 -0
- native/models/vqvae.py +175 -0
- native/open_clip/__init__.py +0 -0
- native/open_clip/source_model.py +599 -0
- native/open_clip/transformer.py +735 -0
- native/open_clip/utils.py +89 -0
- native/utils/__init__.py +0 -0
- native/utils/config.py +243 -0
- native/utils/dist.py +302 -0
- preprocessor_config.json +12 -0
- release_manifest.json +117 -0
- requirements.txt +7 -0
- tensor_mapping.json +0 -0
- tensor_sha256.json +0 -0
- validate.py +48 -0
- validation.json +81 -0
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 |
+
}
|