Image-Text-to-Text
Transformers
Safetensors
English
visionpsynano
feature-extraction
vision-language-model
nanovlm
chart-understanding
ocr
crypto
launchpad
stable-mainnet
fefer
pegd-fun
conversational
custom_code
Instructions to use feferai/FEFER-AI-460M with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use feferai/FEFER-AI-460M with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="feferai/FEFER-AI-460M", trust_remote_code=True) messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("feferai/FEFER-AI-460M", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use feferai/FEFER-AI-460M with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "feferai/FEFER-AI-460M" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "feferai/FEFER-AI-460M", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/feferai/FEFER-AI-460M
- SGLang
How to use feferai/FEFER-AI-460M with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "feferai/FEFER-AI-460M" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "feferai/FEFER-AI-460M", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "feferai/FEFER-AI-460M" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "feferai/FEFER-AI-460M", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use feferai/FEFER-AI-460M with Docker Model Runner:
docker model run hf.co/feferai/FEFER-AI-460M
FEFER AI 🦖 — VisionPsy-Nano-460M for pegd.fun / Stable 988
Browse files- .gitattributes +3 -0
- ATTRIBUTIONS.md +0 -0
- LICENSE +201 -0
- README.md +106 -0
- assets/category_performance.png +3 -0
- assets/category_summary.png +3 -0
- chat_template.jinja +4 -0
- config.json +125 -0
- configuration_visionpsynano.py +317 -0
- custom_transforms.py +115 -0
- fefer-512.png +3 -0
- language_model.py +557 -0
- modality_projector.py +39 -0
- model.safetensors +3 -0
- model_utils.py +21 -0
- modeling_visionpsynano.py +301 -0
- preprocessor_config.json +84 -0
- processing_visionpsynano.py +411 -0
- processor_config.json +6 -0
- processors.py +118 -0
- runtime_profile.py +37 -0
- tokenizer.json +0 -0
- tokenizer_config.json +150 -0
- vision_language_model.py +346 -0
- vision_transformer.py +217 -0
- vlm_config.py +62 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
assets/category_performance.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
assets/category_summary.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
fefer-512.png filter=lfs diff=lfs merge=lfs -text
|
ATTRIBUTIONS.md
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
base_model: qvac/VisionPsy-Nano-460M
|
| 4 |
+
pipeline_tag: image-text-to-text
|
| 5 |
+
library_name: transformers
|
| 6 |
+
tags:
|
| 7 |
+
- vision-language-model
|
| 8 |
+
- nanovlm
|
| 9 |
+
- chart-understanding
|
| 10 |
+
- ocr
|
| 11 |
+
- crypto
|
| 12 |
+
- launchpad
|
| 13 |
+
- stable-mainnet
|
| 14 |
+
- fefer
|
| 15 |
+
- pegd-fun
|
| 16 |
+
language:
|
| 17 |
+
- en
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
<p align="center">
|
| 21 |
+
<img src="fefer-512.png" width="180" alt="FEFER AI" />
|
| 22 |
+
</p>
|
| 23 |
+
|
| 24 |
+
# FEFER-AI-460M 🦖
|
| 25 |
+
|
| 26 |
+
**FEFER** is the resident chart analyst of [pegd.fun](https://pegd.fun) — the stablecoin
|
| 27 |
+
launchpad on **Stable mainnet (chain id 988)**. This model is a FEFER-tuned build of
|
| 28 |
+
[`qvac/VisionPsy-Nano-460M`](https://huggingface.co/qvac/VisionPsy-Nano-460M)
|
| 29 |
+
(Tether AI Research), a ~460M-parameter vision-language model (SigLIP2 vision encoder +
|
| 30 |
+
SmolLM2-360M LM, nanoVLM architecture) that punches far above its size on chart
|
| 31 |
+
understanding, OCR, and visual instruction following.
|
| 32 |
+
|
| 33 |
+
FEFER reads **one thing and one thing only**: terminal-style candlestick charts rendered by
|
| 34 |
+
the pegd.fun indexer for Stable-988 launchpad tokens. Each chart has verified indexer facts
|
| 35 |
+
(market cap, 24h volume, graduation progress) printed directly on the image, so the model
|
| 36 |
+
cross-checks pixels against ground truth instead of inventing numbers.
|
| 37 |
+
|
| 38 |
+
## What it's for
|
| 39 |
+
|
| 40 |
+
- Reading pegd.fun 1024×512 candle charts: trend, momentum, volume behavior, drawdowns
|
| 41 |
+
- Answering holder questions in the FEFER persona (plain language, risk note always included)
|
| 42 |
+
- Explaining launchpad mechanics it was trained on: fixed 1B supply, permanently locked
|
| 43 |
+
Uniswap V3 1% USDT0 pools, 3,000 USDT0 opening FDV, 9,000 USDT0 graduation, 80/20
|
| 44 |
+
creator/platform fee split
|
| 45 |
+
|
| 46 |
+
## What it's NOT for
|
| 47 |
+
|
| 48 |
+
- Price prediction or financial advice — FEFER always says so, by training and by prompt
|
| 49 |
+
- Multi-image reasoning (single-image by architecture)
|
| 50 |
+
- Anything outside Stable mainnet 988 — other chains are out of scope on purpose
|
| 51 |
+
|
| 52 |
+
## Quickstart (transformers)
|
| 53 |
+
|
| 54 |
+
```python
|
| 55 |
+
from transformers import AutoModelForImageTextToText, AutoProcessor
|
| 56 |
+
from PIL import Image
|
| 57 |
+
|
| 58 |
+
repo = "feferai/FEFER-AI-460M" # replace after publishing
|
| 59 |
+
model = AutoModelForImageTextToText.from_pretrained(repo, trust_remote_code=True)
|
| 60 |
+
processor = AutoProcessor.from_pretrained(repo, trust_remote_code=True)
|
| 61 |
+
|
| 62 |
+
chart = Image.open("fefer_chart.png") # a pegd.fun /api/fefer/chart/<token>.png render
|
| 63 |
+
messages = [{"role": "user", "content": [
|
| 64 |
+
{"type": "image", "image": chart},
|
| 65 |
+
{"type": "text", "text": "Read this chart. Trend, volume, and graduation status?"},
|
| 66 |
+
]}]
|
| 67 |
+
inputs = processor.apply_chat_template(messages, add_generation_prompt=True,
|
| 68 |
+
tokenize=True, return_dict=True, return_tensors="pt")
|
| 69 |
+
out = model.generate(**inputs, max_new_tokens=320)
|
| 70 |
+
print(processor.batch_decode(out[:, inputs["input_ids"].shape[1]:], skip_special_tokens=True)[0])
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
## Serving for pegd.fun
|
| 74 |
+
|
| 75 |
+
Any OpenAI-compatible endpoint works. Then point the launchpad at it:
|
| 76 |
+
|
| 77 |
+
```bash
|
| 78 |
+
# GPU
|
| 79 |
+
vllm serve feferai/FEFER-AI-460M --trust-remote-code --port 8000
|
| 80 |
+
# or CPU (no GPU needed at 460M)
|
| 81 |
+
python fefer-ai/serve_cpu.py --model feferai/FEFER-AI-460M --port 8008
|
| 82 |
+
|
| 83 |
+
# pegd.fun server env
|
| 84 |
+
FEFER_AI_URL=http://127.0.0.1:8000/v1
|
| 85 |
+
FEFER_AI_MODEL=feferai/FEFER-AI-460M
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
## Training
|
| 89 |
+
|
| 90 |
+
LoRA fine-tune on the language-model attention projections (vision tower frozen), on
|
| 91 |
+
chart/answer pairs harvested from live pegd.fun launches by `fefer-ai/build_dataset.mjs`
|
| 92 |
+
— every sample is a real indexer-rendered chart paired with facts computed from on-chain
|
| 93 |
+
trade history, so supervision is grounded, not synthetic guesswork. Merged with
|
| 94 |
+
`merge_and_unload` for standalone serving.
|
| 95 |
+
|
| 96 |
+
## Limitations & bias
|
| 97 |
+
|
| 98 |
+
460M parameters is small: FEFER is a sharp chart *reader*, not an oracle. It can misread
|
| 99 |
+
dense wicks, and it inherits any biases of the base model. Launchpad tokens are volatile;
|
| 100 |
+
nothing this model outputs is financial advice, and it is trained to say exactly that.
|
| 101 |
+
|
| 102 |
+
## License & attribution
|
| 103 |
+
|
| 104 |
+
Apache 2.0, same as the base model. Built on **VisionPsy-Nano-460M by Tether AI Research
|
| 105 |
+
(qvac)** — all credit for the base architecture and pretraining to them. FEFER branding,
|
| 106 |
+
chart pipeline, dataset tooling, and fine-tune by the pegd.fun team.
|
assets/category_performance.png
ADDED
|
Git LFS Details
|
assets/category_summary.png
ADDED
|
Git LFS Details
|
chat_template.jinja
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% for message in messages %}{{'<|im_start|>' + message['role'] + '
|
| 2 |
+
' + message['content'] + '<|im_end|>' + '
|
| 3 |
+
'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant
|
| 4 |
+
' }}{% endif %}
|
config.json
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"VisionPsyNanoForConditionalGeneration"
|
| 4 |
+
],
|
| 5 |
+
"auto_map": {
|
| 6 |
+
"AutoConfig": "configuration_visionpsynano.VisionPsyNanoConfig",
|
| 7 |
+
"AutoModel": "modeling_visionpsynano.VisionPsyNanoForConditionalGeneration",
|
| 8 |
+
"AutoModelForImageTextToText": "modeling_visionpsynano.VisionPsyNanoForConditionalGeneration",
|
| 9 |
+
"AutoProcessor": "processing_visionpsynano.VisionPsyNanoProcessor"
|
| 10 |
+
},
|
| 11 |
+
"compile_inference": true,
|
| 12 |
+
"compile_inference_mode": "default",
|
| 13 |
+
"cuda_graphs_cache_quantum": 128,
|
| 14 |
+
"eos_check_interval": 16,
|
| 15 |
+
"extra_token_amount": 66,
|
| 16 |
+
"hf_repo_name": "qvac/VisionPsy-Nano-460M",
|
| 17 |
+
"inference_max_img_size": null,
|
| 18 |
+
"is_flash": false,
|
| 19 |
+
"lm_attn_scaling": 1.0,
|
| 20 |
+
"lm_base_vocab_size": 49152,
|
| 21 |
+
"lm_chat_template": "{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}",
|
| 22 |
+
"lm_dropout": 0.0,
|
| 23 |
+
"lm_hidden_dim": 960,
|
| 24 |
+
"lm_inter_dim": 2560,
|
| 25 |
+
"lm_max_length": 8192,
|
| 26 |
+
"lm_max_position_embeddings": 8192,
|
| 27 |
+
"lm_model_type": "HuggingFaceTB/SmolLM2-360M-Instruct",
|
| 28 |
+
"lm_n_blocks": 32,
|
| 29 |
+
"lm_n_heads": 15,
|
| 30 |
+
"lm_n_kv_heads": 5,
|
| 31 |
+
"lm_re_base": 100000,
|
| 32 |
+
"lm_rms_eps": 1e-05,
|
| 33 |
+
"lm_tie_weights": true,
|
| 34 |
+
"lm_tokenizer": "HuggingFaceTB/SmolLM2-360M-Instruct",
|
| 35 |
+
"lm_use_tokens": false,
|
| 36 |
+
"lm_vocab_size": 49218,
|
| 37 |
+
"max_img_size": 2048,
|
| 38 |
+
"model_type": "visionpsynano",
|
| 39 |
+
"mp_image_token_length": 64,
|
| 40 |
+
"mp_pixel_shuffle_factor": 4,
|
| 41 |
+
"resize_min_side_len": null,
|
| 42 |
+
"resize_to_max_side_len": true,
|
| 43 |
+
"transformers_version": "5.14.1",
|
| 44 |
+
"vit_cls_flag": false,
|
| 45 |
+
"vit_dropout": 0.0,
|
| 46 |
+
"vit_hidden_dim": 768,
|
| 47 |
+
"vit_img_size": 512,
|
| 48 |
+
"vit_inter_dim": 3072,
|
| 49 |
+
"vit_ln_eps": 1e-06,
|
| 50 |
+
"vit_model_type": "google/siglip2-base-patch16-512",
|
| 51 |
+
"vit_n_blocks": 12,
|
| 52 |
+
"vit_n_heads": 12,
|
| 53 |
+
"vit_patch_size": 16,
|
| 54 |
+
"vlm_checkpoint_path": null,
|
| 55 |
+
"vlm_extra_tokens": {
|
| 56 |
+
"global_image_token": "<|global_image|>",
|
| 57 |
+
"image_token": "<|image|>",
|
| 58 |
+
"r1c1": "<row_1_col_1>",
|
| 59 |
+
"r1c2": "<row_1_col_2>",
|
| 60 |
+
"r1c3": "<row_1_col_3>",
|
| 61 |
+
"r1c4": "<row_1_col_4>",
|
| 62 |
+
"r1c5": "<row_1_col_5>",
|
| 63 |
+
"r1c6": "<row_1_col_6>",
|
| 64 |
+
"r1c7": "<row_1_col_7>",
|
| 65 |
+
"r1c8": "<row_1_col_8>",
|
| 66 |
+
"r2c1": "<row_2_col_1>",
|
| 67 |
+
"r2c2": "<row_2_col_2>",
|
| 68 |
+
"r2c3": "<row_2_col_3>",
|
| 69 |
+
"r2c4": "<row_2_col_4>",
|
| 70 |
+
"r2c5": "<row_2_col_5>",
|
| 71 |
+
"r2c6": "<row_2_col_6>",
|
| 72 |
+
"r2c7": "<row_2_col_7>",
|
| 73 |
+
"r2c8": "<row_2_col_8>",
|
| 74 |
+
"r3c1": "<row_3_col_1>",
|
| 75 |
+
"r3c2": "<row_3_col_2>",
|
| 76 |
+
"r3c3": "<row_3_col_3>",
|
| 77 |
+
"r3c4": "<row_3_col_4>",
|
| 78 |
+
"r3c5": "<row_3_col_5>",
|
| 79 |
+
"r3c6": "<row_3_col_6>",
|
| 80 |
+
"r3c7": "<row_3_col_7>",
|
| 81 |
+
"r3c8": "<row_3_col_8>",
|
| 82 |
+
"r4c1": "<row_4_col_1>",
|
| 83 |
+
"r4c2": "<row_4_col_2>",
|
| 84 |
+
"r4c3": "<row_4_col_3>",
|
| 85 |
+
"r4c4": "<row_4_col_4>",
|
| 86 |
+
"r4c5": "<row_4_col_5>",
|
| 87 |
+
"r4c6": "<row_4_col_6>",
|
| 88 |
+
"r4c7": "<row_4_col_7>",
|
| 89 |
+
"r4c8": "<row_4_col_8>",
|
| 90 |
+
"r5c1": "<row_5_col_1>",
|
| 91 |
+
"r5c2": "<row_5_col_2>",
|
| 92 |
+
"r5c3": "<row_5_col_3>",
|
| 93 |
+
"r5c4": "<row_5_col_4>",
|
| 94 |
+
"r5c5": "<row_5_col_5>",
|
| 95 |
+
"r5c6": "<row_5_col_6>",
|
| 96 |
+
"r5c7": "<row_5_col_7>",
|
| 97 |
+
"r5c8": "<row_5_col_8>",
|
| 98 |
+
"r6c1": "<row_6_col_1>",
|
| 99 |
+
"r6c2": "<row_6_col_2>",
|
| 100 |
+
"r6c3": "<row_6_col_3>",
|
| 101 |
+
"r6c4": "<row_6_col_4>",
|
| 102 |
+
"r6c5": "<row_6_col_5>",
|
| 103 |
+
"r6c6": "<row_6_col_6>",
|
| 104 |
+
"r6c7": "<row_6_col_7>",
|
| 105 |
+
"r6c8": "<row_6_col_8>",
|
| 106 |
+
"r7c1": "<row_7_col_1>",
|
| 107 |
+
"r7c2": "<row_7_col_2>",
|
| 108 |
+
"r7c3": "<row_7_col_3>",
|
| 109 |
+
"r7c4": "<row_7_col_4>",
|
| 110 |
+
"r7c5": "<row_7_col_5>",
|
| 111 |
+
"r7c6": "<row_7_col_6>",
|
| 112 |
+
"r7c7": "<row_7_col_7>",
|
| 113 |
+
"r7c8": "<row_7_col_8>",
|
| 114 |
+
"r8c1": "<row_8_col_1>",
|
| 115 |
+
"r8c2": "<row_8_col_2>",
|
| 116 |
+
"r8c3": "<row_8_col_3>",
|
| 117 |
+
"r8c4": "<row_8_col_4>",
|
| 118 |
+
"r8c5": "<row_8_col_5>",
|
| 119 |
+
"r8c6": "<row_8_col_6>",
|
| 120 |
+
"r8c7": "<row_8_col_7>",
|
| 121 |
+
"r8c8": "<row_8_col_8>"
|
| 122 |
+
},
|
| 123 |
+
"vlm_load_backbone_weights": false,
|
| 124 |
+
"torch_dtype": "float32"
|
| 125 |
+
}
|
configuration_visionpsynano.py
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""HuggingFace PretrainedConfig for VisionPsyNano."""
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import os
|
| 5 |
+
from typing import Any, Optional
|
| 6 |
+
|
| 7 |
+
from transformers import PretrainedConfig
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
FLASH_MIN_SIDE_LEN = 512
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def resolve_is_flash(
|
| 14 |
+
*,
|
| 15 |
+
is_flash: Optional[bool] = None,
|
| 16 |
+
variant: Optional[str] = None,
|
| 17 |
+
resize_to_max_side_len: Optional[bool] = None,
|
| 18 |
+
) -> bool:
|
| 19 |
+
"""Resolve Flash preprocess mode from explicit flag, legacy variant, or resize policy."""
|
| 20 |
+
if is_flash is not None:
|
| 21 |
+
return bool(is_flash)
|
| 22 |
+
if variant is not None:
|
| 23 |
+
v = str(variant).lower().strip()
|
| 24 |
+
if v in ("flash", "nano-flash", "visionpsy-nano-flash"):
|
| 25 |
+
return True
|
| 26 |
+
if v in ("nano", "plain", "nano-plain", "visionpsy-nano"):
|
| 27 |
+
return False
|
| 28 |
+
raise ValueError(
|
| 29 |
+
"legacy variant must be 'nano' or 'flash' "
|
| 30 |
+
f"(or aliases); got {variant!r}. Prefer is_flash=True/False."
|
| 31 |
+
)
|
| 32 |
+
if resize_to_max_side_len is not None:
|
| 33 |
+
return not bool(resize_to_max_side_len)
|
| 34 |
+
return False
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def apply_flash_preprocess(
|
| 38 |
+
*,
|
| 39 |
+
is_flash: bool,
|
| 40 |
+
resize_to_max_side_len: Optional[bool] = None,
|
| 41 |
+
resize_min_side_len: Optional[int] = None,
|
| 42 |
+
) -> tuple[bool, Optional[int]]:
|
| 43 |
+
"""Return (resize_to_max_side_len, resize_min_side_len) for the given Flash mode."""
|
| 44 |
+
if resize_to_max_side_len is None:
|
| 45 |
+
resize_to_max_side_len = not is_flash
|
| 46 |
+
resize_to_max_side_len = bool(resize_to_max_side_len)
|
| 47 |
+
if is_flash:
|
| 48 |
+
resize_min_side_len = max(int(resize_min_side_len or 0), FLASH_MIN_SIDE_LEN)
|
| 49 |
+
elif resize_to_max_side_len:
|
| 50 |
+
resize_min_side_len = None
|
| 51 |
+
return resize_to_max_side_len, resize_min_side_len
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
_DEFAULT_CHAT_TEMPLATE = (
|
| 55 |
+
"{% for message in messages %}"
|
| 56 |
+
"{{'<|im_start|>' + message['role'] + '\\n' + message['content'] + '<|im_end|>' + '\\n'}}"
|
| 57 |
+
"{% endfor %}"
|
| 58 |
+
"{% if add_generation_prompt %}{{ '<|im_start|>assistant\\n' }}{% endif %}"
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
_DEFAULT_EXTRA_TOKENS = {
|
| 62 |
+
"image_token": "<|image|>",
|
| 63 |
+
"global_image_token": "<|global_image|>",
|
| 64 |
+
"r1c1": "<row_1_col_1>",
|
| 65 |
+
"r1c2": "<row_1_col_2>",
|
| 66 |
+
"r1c3": "<row_1_col_3>",
|
| 67 |
+
"r1c4": "<row_1_col_4>",
|
| 68 |
+
"r1c5": "<row_1_col_5>",
|
| 69 |
+
"r1c6": "<row_1_col_6>",
|
| 70 |
+
"r1c7": "<row_1_col_7>",
|
| 71 |
+
"r1c8": "<row_1_col_8>",
|
| 72 |
+
"r2c1": "<row_2_col_1>",
|
| 73 |
+
"r2c2": "<row_2_col_2>",
|
| 74 |
+
"r2c3": "<row_2_col_3>",
|
| 75 |
+
"r2c4": "<row_2_col_4>",
|
| 76 |
+
"r2c5": "<row_2_col_5>",
|
| 77 |
+
"r2c6": "<row_2_col_6>",
|
| 78 |
+
"r2c7": "<row_2_col_7>",
|
| 79 |
+
"r2c8": "<row_2_col_8>",
|
| 80 |
+
"r3c1": "<row_3_col_1>",
|
| 81 |
+
"r3c2": "<row_3_col_2>",
|
| 82 |
+
"r3c3": "<row_3_col_3>",
|
| 83 |
+
"r3c4": "<row_3_col_4>",
|
| 84 |
+
"r3c5": "<row_3_col_5>",
|
| 85 |
+
"r3c6": "<row_3_col_6>",
|
| 86 |
+
"r3c7": "<row_3_col_7>",
|
| 87 |
+
"r3c8": "<row_3_col_8>",
|
| 88 |
+
"r4c1": "<row_4_col_1>",
|
| 89 |
+
"r4c2": "<row_4_col_2>",
|
| 90 |
+
"r4c3": "<row_4_col_3>",
|
| 91 |
+
"r4c4": "<row_4_col_4>",
|
| 92 |
+
"r4c5": "<row_4_col_5>",
|
| 93 |
+
"r4c6": "<row_4_col_6>",
|
| 94 |
+
"r4c7": "<row_4_col_7>",
|
| 95 |
+
"r4c8": "<row_4_col_8>",
|
| 96 |
+
"r5c1": "<row_5_col_1>",
|
| 97 |
+
"r5c2": "<row_5_col_2>",
|
| 98 |
+
"r5c3": "<row_5_col_3>",
|
| 99 |
+
"r5c4": "<row_5_col_4>",
|
| 100 |
+
"r5c5": "<row_5_col_5>",
|
| 101 |
+
"r5c6": "<row_5_col_6>",
|
| 102 |
+
"r5c7": "<row_5_col_7>",
|
| 103 |
+
"r5c8": "<row_5_col_8>",
|
| 104 |
+
"r6c1": "<row_6_col_1>",
|
| 105 |
+
"r6c2": "<row_6_col_2>",
|
| 106 |
+
"r6c3": "<row_6_col_3>",
|
| 107 |
+
"r6c4": "<row_6_col_4>",
|
| 108 |
+
"r6c5": "<row_6_col_5>",
|
| 109 |
+
"r6c6": "<row_6_col_6>",
|
| 110 |
+
"r6c7": "<row_6_col_7>",
|
| 111 |
+
"r6c8": "<row_6_col_8>",
|
| 112 |
+
"r7c1": "<row_7_col_1>",
|
| 113 |
+
"r7c2": "<row_7_col_2>",
|
| 114 |
+
"r7c3": "<row_7_col_3>",
|
| 115 |
+
"r7c4": "<row_7_col_4>",
|
| 116 |
+
"r7c5": "<row_7_col_5>",
|
| 117 |
+
"r7c6": "<row_7_col_6>",
|
| 118 |
+
"r7c7": "<row_7_col_7>",
|
| 119 |
+
"r7c8": "<row_7_col_8>",
|
| 120 |
+
"r8c1": "<row_8_col_1>",
|
| 121 |
+
"r8c2": "<row_8_col_2>",
|
| 122 |
+
"r8c3": "<row_8_col_3>",
|
| 123 |
+
"r8c4": "<row_8_col_4>",
|
| 124 |
+
"r8c5": "<row_8_col_5>",
|
| 125 |
+
"r8c6": "<row_8_col_6>",
|
| 126 |
+
"r8c7": "<row_8_col_7>",
|
| 127 |
+
"r8c8": "<row_8_col_8>",
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
class VisionPsyNanoConfig(PretrainedConfig):
|
| 132 |
+
"""Config for VisionPsyNano (``is_flash`` selects preprocess mode)."""
|
| 133 |
+
|
| 134 |
+
model_type = "visionpsynano"
|
| 135 |
+
keys_to_ignore_at_inference = ["past_key_values"]
|
| 136 |
+
|
| 137 |
+
def __init__(
|
| 138 |
+
self,
|
| 139 |
+
is_flash: Optional[bool] = None,
|
| 140 |
+
variant: Optional[str] = None,
|
| 141 |
+
vit_hidden_dim: int = 768,
|
| 142 |
+
vit_inter_dim: int = 3072,
|
| 143 |
+
vit_patch_size: int = 16,
|
| 144 |
+
vit_img_size: int = 512,
|
| 145 |
+
vit_n_heads: int = 12,
|
| 146 |
+
vit_dropout: float = 0.0,
|
| 147 |
+
vit_n_blocks: int = 12,
|
| 148 |
+
vit_ln_eps: float = 1e-6,
|
| 149 |
+
vit_cls_flag: bool = False,
|
| 150 |
+
vit_model_type: str = "google/siglip2-base-patch16-512",
|
| 151 |
+
lm_hidden_dim: int = 960,
|
| 152 |
+
lm_inter_dim: int = 2560,
|
| 153 |
+
lm_rms_eps: float = 1e-5,
|
| 154 |
+
lm_re_base: int = 100000,
|
| 155 |
+
lm_max_position_embeddings: int = 8192,
|
| 156 |
+
lm_base_vocab_size: int = 49152,
|
| 157 |
+
extra_token_amount: int = 66,
|
| 158 |
+
lm_vocab_size: Optional[int] = None,
|
| 159 |
+
lm_n_heads: int = 15,
|
| 160 |
+
lm_n_kv_heads: int = 5,
|
| 161 |
+
lm_dropout: float = 0.0,
|
| 162 |
+
lm_n_blocks: int = 32,
|
| 163 |
+
lm_attn_scaling: float = 1.0,
|
| 164 |
+
lm_max_length: int = 4096,
|
| 165 |
+
lm_use_tokens: bool = False,
|
| 166 |
+
lm_tie_weights: bool = True,
|
| 167 |
+
lm_model_type: str = "HuggingFaceTB/SmolLM2-360M-Instruct",
|
| 168 |
+
lm_tokenizer: str = "HuggingFaceTB/SmolLM2-360M-Instruct",
|
| 169 |
+
lm_chat_template: str = _DEFAULT_CHAT_TEMPLATE,
|
| 170 |
+
mp_pixel_shuffle_factor: int = 4,
|
| 171 |
+
mp_image_token_length: int = 64,
|
| 172 |
+
max_img_size: int = 2048,
|
| 173 |
+
resize_to_max_side_len: Optional[bool] = None,
|
| 174 |
+
resize_min_side_len: Optional[int] = None,
|
| 175 |
+
inference_max_img_size: Optional[int] = None,
|
| 176 |
+
vlm_extra_tokens: Optional[dict] = None,
|
| 177 |
+
vlm_load_backbone_weights: bool = True,
|
| 178 |
+
vlm_checkpoint_path: str = "checkpoints",
|
| 179 |
+
hf_repo_name: str = "qvac/VisionPsy-Nano-460M",
|
| 180 |
+
compile_inference: bool = True,
|
| 181 |
+
compile_inference_mode: str = "reduce-overhead",
|
| 182 |
+
cuda_graphs_cache_quantum: int = 128,
|
| 183 |
+
eos_check_interval: int = 16,
|
| 184 |
+
**kwargs: Any,
|
| 185 |
+
):
|
| 186 |
+
is_flash = resolve_is_flash(
|
| 187 |
+
is_flash=is_flash,
|
| 188 |
+
variant=variant,
|
| 189 |
+
resize_to_max_side_len=resize_to_max_side_len,
|
| 190 |
+
)
|
| 191 |
+
resize_to_max_side_len, resize_min_side_len = apply_flash_preprocess(
|
| 192 |
+
is_flash=is_flash,
|
| 193 |
+
resize_to_max_side_len=resize_to_max_side_len,
|
| 194 |
+
resize_min_side_len=resize_min_side_len,
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
if lm_vocab_size is None:
|
| 198 |
+
lm_vocab_size = lm_base_vocab_size + extra_token_amount
|
| 199 |
+
|
| 200 |
+
self.is_flash = bool(is_flash)
|
| 201 |
+
self.vit_hidden_dim = vit_hidden_dim
|
| 202 |
+
self.vit_inter_dim = vit_inter_dim
|
| 203 |
+
self.vit_patch_size = vit_patch_size
|
| 204 |
+
self.vit_img_size = vit_img_size
|
| 205 |
+
self.vit_n_heads = vit_n_heads
|
| 206 |
+
self.vit_dropout = vit_dropout
|
| 207 |
+
self.vit_n_blocks = vit_n_blocks
|
| 208 |
+
self.vit_ln_eps = vit_ln_eps
|
| 209 |
+
self.vit_cls_flag = vit_cls_flag
|
| 210 |
+
self.vit_model_type = vit_model_type
|
| 211 |
+
|
| 212 |
+
self.lm_hidden_dim = lm_hidden_dim
|
| 213 |
+
self.lm_inter_dim = lm_inter_dim
|
| 214 |
+
self.lm_rms_eps = lm_rms_eps
|
| 215 |
+
self.lm_re_base = lm_re_base
|
| 216 |
+
self.lm_max_position_embeddings = lm_max_position_embeddings
|
| 217 |
+
self.lm_base_vocab_size = lm_base_vocab_size
|
| 218 |
+
self.extra_token_amount = extra_token_amount
|
| 219 |
+
self.lm_vocab_size = lm_vocab_size
|
| 220 |
+
self.lm_n_heads = lm_n_heads
|
| 221 |
+
self.lm_n_kv_heads = lm_n_kv_heads
|
| 222 |
+
self.lm_dropout = lm_dropout
|
| 223 |
+
self.lm_n_blocks = lm_n_blocks
|
| 224 |
+
self.lm_attn_scaling = lm_attn_scaling
|
| 225 |
+
self.lm_max_length = lm_max_length
|
| 226 |
+
self.lm_use_tokens = lm_use_tokens
|
| 227 |
+
self.lm_tie_weights = lm_tie_weights
|
| 228 |
+
self.lm_model_type = lm_model_type
|
| 229 |
+
self.lm_tokenizer = lm_tokenizer
|
| 230 |
+
self.lm_chat_template = lm_chat_template
|
| 231 |
+
|
| 232 |
+
self.mp_pixel_shuffle_factor = mp_pixel_shuffle_factor
|
| 233 |
+
self.mp_image_token_length = mp_image_token_length
|
| 234 |
+
self.max_img_size = max_img_size
|
| 235 |
+
self.resize_to_max_side_len = bool(resize_to_max_side_len)
|
| 236 |
+
self.resize_min_side_len = resize_min_side_len
|
| 237 |
+
self.inference_max_img_size = inference_max_img_size
|
| 238 |
+
self.vlm_extra_tokens = dict(vlm_extra_tokens or _DEFAULT_EXTRA_TOKENS)
|
| 239 |
+
self.vlm_load_backbone_weights = vlm_load_backbone_weights
|
| 240 |
+
self.vlm_checkpoint_path = vlm_checkpoint_path
|
| 241 |
+
if self.is_flash and hf_repo_name == "qvac/VisionPsy-Nano-460M":
|
| 242 |
+
self.hf_repo_name = "qvac/VisionPsy-Nano-460M-Flash"
|
| 243 |
+
else:
|
| 244 |
+
self.hf_repo_name = hf_repo_name
|
| 245 |
+
|
| 246 |
+
self.compile_inference = compile_inference
|
| 247 |
+
self.compile_inference_mode = compile_inference_mode
|
| 248 |
+
self.cuda_graphs_cache_quantum = cuda_graphs_cache_quantum
|
| 249 |
+
self.eos_check_interval = eos_check_interval
|
| 250 |
+
|
| 251 |
+
kwargs.pop("text_config", None)
|
| 252 |
+
kwargs.pop("vision_config", None)
|
| 253 |
+
|
| 254 |
+
super().__init__(**kwargs)
|
| 255 |
+
|
| 256 |
+
def get_text_config(self, decoder: bool = False, **kwargs):
|
| 257 |
+
"""VisionPsyNano is a single flat config (not text+vision composite)."""
|
| 258 |
+
return self
|
| 259 |
+
|
| 260 |
+
@property
|
| 261 |
+
def variant(self) -> str:
|
| 262 |
+
return "flash" if self.is_flash else "nano"
|
| 263 |
+
|
| 264 |
+
def to_vlm_config(self):
|
| 265 |
+
"""Convert to the internal VLMConfig dataclass used by the core modules."""
|
| 266 |
+
from dataclasses import fields as dc_fields
|
| 267 |
+
|
| 268 |
+
try:
|
| 269 |
+
from .vlm_config import VLMConfig
|
| 270 |
+
except ImportError:
|
| 271 |
+
from vlm_config import VLMConfig
|
| 272 |
+
|
| 273 |
+
valid = {f.name for f in dc_fields(VLMConfig)}
|
| 274 |
+
payload = {k: getattr(self, k) for k in valid if hasattr(self, k)}
|
| 275 |
+
return VLMConfig(**payload)
|
| 276 |
+
|
| 277 |
+
@classmethod
|
| 278 |
+
def from_vlm_config(
|
| 279 |
+
cls, cfg, *, is_flash: Optional[bool] = None, variant: Optional[str] = None
|
| 280 |
+
) -> "VisionPsyNanoConfig":
|
| 281 |
+
from dataclasses import asdict
|
| 282 |
+
|
| 283 |
+
data = asdict(cfg)
|
| 284 |
+
data["is_flash"] = resolve_is_flash(
|
| 285 |
+
is_flash=is_flash,
|
| 286 |
+
variant=variant,
|
| 287 |
+
resize_to_max_side_len=data.get("resize_to_max_side_len"),
|
| 288 |
+
)
|
| 289 |
+
data.pop("variant", None)
|
| 290 |
+
return cls(**data)
|
| 291 |
+
|
| 292 |
+
@classmethod
|
| 293 |
+
def from_legacy_dict(
|
| 294 |
+
cls,
|
| 295 |
+
raw: dict,
|
| 296 |
+
*,
|
| 297 |
+
is_flash: Optional[bool] = None,
|
| 298 |
+
variant: Optional[str] = None,
|
| 299 |
+
) -> "VisionPsyNanoConfig":
|
| 300 |
+
"""Load an existing VisionPsyNano / nanoVLM config.json (without model_type)."""
|
| 301 |
+
raw = dict(raw)
|
| 302 |
+
for key in (
|
| 303 |
+
"model_type",
|
| 304 |
+
"architectures",
|
| 305 |
+
"auto_map",
|
| 306 |
+
"transformers_version",
|
| 307 |
+
"text_config",
|
| 308 |
+
"vision_config",
|
| 309 |
+
):
|
| 310 |
+
raw.pop(key, None)
|
| 311 |
+
raw["is_flash"] = resolve_is_flash(
|
| 312 |
+
is_flash=is_flash if is_flash is not None else raw.get("is_flash"),
|
| 313 |
+
variant=variant if variant is not None else raw.get("variant"),
|
| 314 |
+
resize_to_max_side_len=raw.get("resize_to_max_side_len"),
|
| 315 |
+
)
|
| 316 |
+
raw.pop("variant", None)
|
| 317 |
+
return cls(**raw)
|
custom_transforms.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import torch
|
| 3 |
+
from torchvision.transforms.functional import resize, InterpolationMode
|
| 4 |
+
from einops import rearrange
|
| 5 |
+
from typing import Tuple, Union
|
| 6 |
+
from PIL import Image
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class DynamicResize(torch.nn.Module):
|
| 10 |
+
"""Resize H/W to patch-aligned sizes within max_side_len."""
|
| 11 |
+
def __init__(
|
| 12 |
+
self,
|
| 13 |
+
patch_size: int,
|
| 14 |
+
max_side_len: int,
|
| 15 |
+
resize_to_max_side_len: bool = False,
|
| 16 |
+
min_side_len: int | None = None,
|
| 17 |
+
interpolation: InterpolationMode = InterpolationMode.BICUBIC,
|
| 18 |
+
) -> None:
|
| 19 |
+
super().__init__()
|
| 20 |
+
self.p = int(patch_size)
|
| 21 |
+
self.m = int(max_side_len)
|
| 22 |
+
self.interpolation = interpolation
|
| 23 |
+
self.resize_to_max_side_len = resize_to_max_side_len
|
| 24 |
+
self.min_side_len = int(min_side_len) if min_side_len else None
|
| 25 |
+
|
| 26 |
+
def _get_new_hw(self, h: int, w: int) -> Tuple[int, int]:
|
| 27 |
+
"""Compute target (h, w) divisible by patch_size."""
|
| 28 |
+
long, short = (w, h) if w >= h else (h, w)
|
| 29 |
+
|
| 30 |
+
if (
|
| 31 |
+
self.min_side_len
|
| 32 |
+
and not self.resize_to_max_side_len
|
| 33 |
+
and short < self.min_side_len
|
| 34 |
+
):
|
| 35 |
+
den = short * self.p
|
| 36 |
+
target_long = min(self.m, -(-(long * self.min_side_len) // den) * self.p)
|
| 37 |
+
target_short = max(-(-(short * self.min_side_len) // den) * self.p, self.p)
|
| 38 |
+
return (target_short, target_long) if w >= h else (target_long, target_short)
|
| 39 |
+
|
| 40 |
+
target_long = self.m if self.resize_to_max_side_len else min(self.m, math.ceil(long / self.p) * self.p)
|
| 41 |
+
scale = target_long / long
|
| 42 |
+
target_short = math.ceil(short * scale / self.p) * self.p
|
| 43 |
+
target_short = max(target_short, self.p)
|
| 44 |
+
|
| 45 |
+
return (target_short, target_long) if w >= h else (target_long, target_short)
|
| 46 |
+
|
| 47 |
+
def forward(self, img: Union[Image.Image, torch.Tensor]):
|
| 48 |
+
if isinstance(img, Image.Image):
|
| 49 |
+
w, h = img.size
|
| 50 |
+
new_h, new_w = self._get_new_hw(h, w)
|
| 51 |
+
return resize(img, [new_h, new_w], interpolation=self.interpolation)
|
| 52 |
+
|
| 53 |
+
if not torch.is_tensor(img):
|
| 54 |
+
raise TypeError(
|
| 55 |
+
"DynamicResize expects a PIL Image or a torch.Tensor; "
|
| 56 |
+
f"got {type(img)}"
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
batched = img.ndim == 4
|
| 60 |
+
if img.ndim not in (3, 4):
|
| 61 |
+
raise ValueError(
|
| 62 |
+
"Tensor input must have shape (C,H,W) or (B,C,H,W); "
|
| 63 |
+
f"got {img.shape}"
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
imgs = img if batched else img.unsqueeze(0)
|
| 67 |
+
_, _, h, w = imgs.shape
|
| 68 |
+
new_h, new_w = self._get_new_hw(h, w)
|
| 69 |
+
out = resize(imgs, [new_h, new_w], interpolation=self.interpolation)
|
| 70 |
+
|
| 71 |
+
return out if batched else out.squeeze(0)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
class SplitImage(torch.nn.Module):
|
| 75 |
+
"""Split (B, C, H, W) image tensor into square patches.
|
| 76 |
+
|
| 77 |
+
Returns:
|
| 78 |
+
patches: (B·n_h·n_w, C, patch_size, patch_size)
|
| 79 |
+
grid: (n_h, n_w) - number of patches along H and W
|
| 80 |
+
"""
|
| 81 |
+
def __init__(self, patch_size: int) -> None:
|
| 82 |
+
super().__init__()
|
| 83 |
+
self.p = patch_size
|
| 84 |
+
|
| 85 |
+
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, Tuple[int, int]]:
|
| 86 |
+
if x.ndim == 3:
|
| 87 |
+
x = x.unsqueeze(0)
|
| 88 |
+
|
| 89 |
+
b, c, h, w = x.shape
|
| 90 |
+
if h % self.p or w % self.p:
|
| 91 |
+
raise ValueError(f'Image size {(h,w)} not divisible by patch_size {self.p}')
|
| 92 |
+
|
| 93 |
+
n_h, n_w = h // self.p, w // self.p
|
| 94 |
+
patches = rearrange(x, 'b c (nh ph) (nw pw) -> (b nh nw) c ph pw',
|
| 95 |
+
ph=self.p, pw=self.p)
|
| 96 |
+
return patches, (n_h, n_w)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class GlobalAndSplitImages(torch.nn.Module):
|
| 100 |
+
def __init__(self, patch_size: int):
|
| 101 |
+
super().__init__()
|
| 102 |
+
self.p = patch_size
|
| 103 |
+
self.splitter = SplitImage(patch_size)
|
| 104 |
+
|
| 105 |
+
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, Tuple[int, int]]:
|
| 106 |
+
if x.ndim == 3:
|
| 107 |
+
x = x.unsqueeze(0)
|
| 108 |
+
|
| 109 |
+
patches, grid = self.splitter(x)
|
| 110 |
+
|
| 111 |
+
if grid == (1, 1):
|
| 112 |
+
return patches, grid
|
| 113 |
+
|
| 114 |
+
global_patch = resize(x, [self.p, self.p])
|
| 115 |
+
return torch.cat([global_patch, patches], dim=0), grid
|
fefer-512.png
ADDED
|
Git LFS Details
|
language_model.py
ADDED
|
@@ -0,0 +1,557 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import inspect
|
| 2 |
+
import math
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
import torch.nn.functional as F
|
| 6 |
+
|
| 7 |
+
def _sdpa_supports_native_gqa() -> bool:
|
| 8 |
+
|
| 9 |
+
try:
|
| 10 |
+
params = inspect.signature(F.scaled_dot_product_attention).parameters
|
| 11 |
+
return "enable_gqa" in params
|
| 12 |
+
except (TypeError, ValueError):
|
| 13 |
+
try:
|
| 14 |
+
q = torch.zeros(1, 2, 1, 4)
|
| 15 |
+
k = torch.zeros(1, 1, 1, 4)
|
| 16 |
+
v = torch.zeros(1, 1, 1, 4)
|
| 17 |
+
F.scaled_dot_product_attention(q, k, v, enable_gqa=True)
|
| 18 |
+
return True
|
| 19 |
+
except TypeError:
|
| 20 |
+
return False
|
| 21 |
+
except Exception:
|
| 22 |
+
return True
|
| 23 |
+
|
| 24 |
+
_SDPA_HAS_GQA = _sdpa_supports_native_gqa()
|
| 25 |
+
|
| 26 |
+
class RMSNorm(nn.Module):
|
| 27 |
+
|
| 28 |
+
def __init__(self, cfg):
|
| 29 |
+
super().__init__()
|
| 30 |
+
self.weight = nn.Parameter(torch.ones(cfg.lm_hidden_dim))
|
| 31 |
+
self.eps = cfg.lm_rms_eps
|
| 32 |
+
self._normalized_shape = (cfg.lm_hidden_dim,)
|
| 33 |
+
|
| 34 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 35 |
+
|
| 36 |
+
return F.rms_norm(x, self._normalized_shape, self.weight, self.eps)
|
| 37 |
+
|
| 38 |
+
class RotaryEmbedding(nn.Module):
|
| 39 |
+
|
| 40 |
+
def __init__(self, cfg):
|
| 41 |
+
super().__init__()
|
| 42 |
+
assert cfg.lm_hidden_dim % cfg.lm_n_heads == 0, "Hidden dimension must be divisible by number of heads"
|
| 43 |
+
|
| 44 |
+
self.dim = cfg.lm_hidden_dim // cfg.lm_n_heads
|
| 45 |
+
self.base = cfg.lm_re_base
|
| 46 |
+
self.max_seq_len = cfg.lm_max_position_embeddings
|
| 47 |
+
inv_freq = 1.0 / (self.base ** (torch.arange(0, self.dim, 2).float() / self.dim))
|
| 48 |
+
self.register_buffer("inv_freq", inv_freq)
|
| 49 |
+
self.original_max_seq_len = cfg.lm_max_position_embeddings
|
| 50 |
+
self.attention_scaling = cfg.lm_attn_scaling
|
| 51 |
+
|
| 52 |
+
positions = torch.arange(self.max_seq_len, dtype=torch.float)
|
| 53 |
+
freqs = positions.unsqueeze(-1) * inv_freq.unsqueeze(0)
|
| 54 |
+
emb = torch.cat([freqs, freqs], dim=-1)
|
| 55 |
+
cos_cached = emb.cos() * self.attention_scaling
|
| 56 |
+
sin_cached = emb.sin() * self.attention_scaling
|
| 57 |
+
self.register_buffer("cos_cached", cos_cached, persistent=False)
|
| 58 |
+
self.register_buffer("sin_cached", sin_cached, persistent=False)
|
| 59 |
+
|
| 60 |
+
@torch.no_grad()
|
| 61 |
+
def forward(self, position_ids: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
| 62 |
+
|
| 63 |
+
if position_ids.dtype != torch.long:
|
| 64 |
+
position_ids = position_ids.long()
|
| 65 |
+
cos = self.cos_cached[position_ids]
|
| 66 |
+
sin = self.sin_cached[position_ids]
|
| 67 |
+
return cos, sin
|
| 68 |
+
|
| 69 |
+
def extend_cache(self, new_max_seq_len: int) -> None:
|
| 70 |
+
|
| 71 |
+
positions = torch.arange(
|
| 72 |
+
new_max_seq_len, dtype=torch.float, device=self.cos_cached.device,
|
| 73 |
+
)
|
| 74 |
+
freqs = positions.unsqueeze(-1) * self.inv_freq.unsqueeze(0)
|
| 75 |
+
emb = torch.cat([freqs, freqs], dim=-1)
|
| 76 |
+
self.cos_cached = emb.cos() * self.attention_scaling
|
| 77 |
+
self.sin_cached = emb.sin() * self.attention_scaling
|
| 78 |
+
self.original_max_seq_len = new_max_seq_len
|
| 79 |
+
|
| 80 |
+
def apply_rotary_pos_embd(
|
| 81 |
+
q: torch.Tensor,
|
| 82 |
+
k: torch.Tensor,
|
| 83 |
+
cos: torch.Tensor,
|
| 84 |
+
sin: torch.Tensor,
|
| 85 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 86 |
+
|
| 87 |
+
half = q.shape[-1] // 2
|
| 88 |
+
cos_h = cos[..., :half]
|
| 89 |
+
sin_h = sin[..., :half]
|
| 90 |
+
|
| 91 |
+
q1 = q[..., :half]
|
| 92 |
+
q2 = q[..., half:]
|
| 93 |
+
k1 = k[..., :half]
|
| 94 |
+
k2 = k[..., half:]
|
| 95 |
+
|
| 96 |
+
q_embed = torch.cat(
|
| 97 |
+
(q1 * cos_h - q2 * sin_h, q2 * cos_h + q1 * sin_h),
|
| 98 |
+
dim=-1,
|
| 99 |
+
)
|
| 100 |
+
k_embed = torch.cat(
|
| 101 |
+
(k1 * cos_h - k2 * sin_h, k2 * cos_h + k1 * sin_h),
|
| 102 |
+
dim=-1,
|
| 103 |
+
)
|
| 104 |
+
return q_embed, k_embed
|
| 105 |
+
|
| 106 |
+
class LanguageModelGroupedQueryAttention(nn.Module):
|
| 107 |
+
|
| 108 |
+
def __init__(self, cfg):
|
| 109 |
+
super().__init__()
|
| 110 |
+
|
| 111 |
+
self.n_heads = cfg.lm_n_heads
|
| 112 |
+
self.n_kv_heads = cfg.lm_n_kv_heads
|
| 113 |
+
self.embd_dim = cfg.lm_hidden_dim
|
| 114 |
+
self.dropout = cfg.lm_dropout
|
| 115 |
+
|
| 116 |
+
assert self.n_heads % self.n_kv_heads == 0, "n_heads must be divisible by n_kv_heads"
|
| 117 |
+
assert self.embd_dim % self.n_heads == 0, "embd_dim must be divisible by num_heads"
|
| 118 |
+
|
| 119 |
+
self.n_kv_groups = self.n_heads // self.n_kv_heads
|
| 120 |
+
self.head_dim = self.embd_dim // self.n_heads
|
| 121 |
+
|
| 122 |
+
self.q_proj = nn.Linear(self.embd_dim, self.embd_dim, bias=False)
|
| 123 |
+
self.k_proj = nn.Linear(self.embd_dim, self.head_dim * self.n_kv_heads, bias=False)
|
| 124 |
+
self.v_proj = nn.Linear(self.embd_dim, self.head_dim * self.n_kv_heads, bias=False)
|
| 125 |
+
self.out_proj = nn.Linear(self.embd_dim, self.embd_dim, bias=False)
|
| 126 |
+
|
| 127 |
+
self.attn_dropout = nn.Dropout(self.dropout)
|
| 128 |
+
self.resid_dropout = nn.Dropout(self.dropout)
|
| 129 |
+
|
| 130 |
+
self.sdpa = hasattr(torch.nn.functional, 'scaled_dot_product_attention')
|
| 131 |
+
if not self.sdpa:
|
| 132 |
+
print("Warning: scaled dot product attention not available, using standard attention in LM.")
|
| 133 |
+
|
| 134 |
+
def forward(self, x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, attention_mask=None, block_kv_cache=None, start_pos: int = 0) -> tuple[torch.Tensor, dict]:
|
| 135 |
+
|
| 136 |
+
B, T_curr, C = x.size()
|
| 137 |
+
|
| 138 |
+
q_curr = self.q_proj(x).view(B, T_curr, self.n_heads, self.head_dim).transpose(1, 2)
|
| 139 |
+
k_curr = self.k_proj(x).view(B, T_curr, self.n_kv_heads, self.head_dim).transpose(1, 2)
|
| 140 |
+
v_curr = self.v_proj(x).view(B, T_curr, self.n_kv_heads, self.head_dim).transpose(1, 2)
|
| 141 |
+
|
| 142 |
+
q, k_rotated = apply_rotary_pos_embd(q_curr, k_curr, cos, sin)
|
| 143 |
+
|
| 144 |
+
use_static_cache = (
|
| 145 |
+
block_kv_cache is not None
|
| 146 |
+
and block_kv_cache.get('cache_max_length') is not None
|
| 147 |
+
)
|
| 148 |
+
|
| 149 |
+
static_compile_decode = (
|
| 150 |
+
use_static_cache
|
| 151 |
+
and T_curr == 1
|
| 152 |
+
and isinstance(start_pos, torch.Tensor)
|
| 153 |
+
)
|
| 154 |
+
sdpa_attn_mask_static: torch.Tensor | None = None
|
| 155 |
+
|
| 156 |
+
if static_compile_decode:
|
| 157 |
+
cache_max_length = block_kv_cache['cache_max_length']
|
| 158 |
+
idx = start_pos.unsqueeze(0)
|
| 159 |
+
block_kv_cache['key_cache'].index_copy_(2, idx, k_rotated)
|
| 160 |
+
block_kv_cache['value_cache'].index_copy_(2, idx, v_curr)
|
| 161 |
+
|
| 162 |
+
k = block_kv_cache['key_cache']
|
| 163 |
+
v = block_kv_cache['value_cache']
|
| 164 |
+
T_kv = cache_max_length
|
| 165 |
+
|
| 166 |
+
arange = torch.arange(
|
| 167 |
+
cache_max_length, device=q.device, dtype=torch.long,
|
| 168 |
+
)
|
| 169 |
+
sdpa_attn_mask_static = (arange <= start_pos).view(1, 1, 1, -1)
|
| 170 |
+
elif use_static_cache:
|
| 171 |
+
cache_max_length = block_kv_cache['cache_max_length']
|
| 172 |
+
if block_kv_cache.get('key_cache') is None:
|
| 173 |
+
cache_shape = (B, self.n_kv_heads, cache_max_length, self.head_dim)
|
| 174 |
+
block_kv_cache['key_cache'] = torch.zeros(
|
| 175 |
+
cache_shape, dtype=k_rotated.dtype, device=k_rotated.device,
|
| 176 |
+
)
|
| 177 |
+
block_kv_cache['value_cache'] = torch.zeros(
|
| 178 |
+
cache_shape, dtype=v_curr.dtype, device=v_curr.device,
|
| 179 |
+
)
|
| 180 |
+
new_pos = start_pos + T_curr
|
| 181 |
+
block_kv_cache['key_cache'][:, :, start_pos:new_pos] = k_rotated
|
| 182 |
+
block_kv_cache['value_cache'][:, :, start_pos:new_pos] = v_curr
|
| 183 |
+
k = block_kv_cache['key_cache'][:, :, :new_pos]
|
| 184 |
+
v = block_kv_cache['value_cache'][:, :, :new_pos]
|
| 185 |
+
T_kv = new_pos
|
| 186 |
+
else:
|
| 187 |
+
is_prefill = block_kv_cache is None
|
| 188 |
+
if not is_prefill and block_kv_cache.get('key') is not None:
|
| 189 |
+
k = block_kv_cache['key']
|
| 190 |
+
v = block_kv_cache['value']
|
| 191 |
+
k = torch.cat([k, k_rotated], dim=2)
|
| 192 |
+
v = torch.cat([v, v_curr], dim=2)
|
| 193 |
+
block_kv_cache['key'] = k
|
| 194 |
+
block_kv_cache['value'] = v
|
| 195 |
+
else:
|
| 196 |
+
k = k_rotated
|
| 197 |
+
v = v_curr
|
| 198 |
+
block_kv_cache = {'key': k, 'value': v}
|
| 199 |
+
|
| 200 |
+
if _SDPA_HAS_GQA and self.sdpa and x.device.type != 'mps':
|
| 201 |
+
k_exp = k
|
| 202 |
+
v_exp = v
|
| 203 |
+
else:
|
| 204 |
+
k_exp = k.repeat_interleave(self.n_kv_groups, dim=1)
|
| 205 |
+
v_exp = v.repeat_interleave(self.n_kv_groups, dim=1)
|
| 206 |
+
|
| 207 |
+
T_kv = k_exp.size(2)
|
| 208 |
+
|
| 209 |
+
if self.sdpa and x.device.type != 'mps':
|
| 210 |
+
if sdpa_attn_mask_static is not None:
|
| 211 |
+
sdpa_attn_mask = sdpa_attn_mask_static
|
| 212 |
+
sdpa_is_causal = False
|
| 213 |
+
elif attention_mask is None:
|
| 214 |
+
sdpa_attn_mask = None
|
| 215 |
+
sdpa_is_causal = (T_curr == T_kv and T_curr > 1)
|
| 216 |
+
else:
|
| 217 |
+
if attention_mask.size(-1) < T_kv:
|
| 218 |
+
pad = attention_mask.new_ones(
|
| 219 |
+
attention_mask.size(0), T_kv - attention_mask.size(-1)
|
| 220 |
+
)
|
| 221 |
+
key_padding = torch.cat([attention_mask, pad], dim=-1).bool()
|
| 222 |
+
else:
|
| 223 |
+
key_padding = attention_mask[:, :T_kv].bool()
|
| 224 |
+
if T_curr == T_kv and T_curr > 1:
|
| 225 |
+
causal = torch.ones(
|
| 226 |
+
T_curr, T_kv, dtype=torch.bool, device=q.device,
|
| 227 |
+
).tril()
|
| 228 |
+
else:
|
| 229 |
+
causal = torch.ones(
|
| 230 |
+
T_curr, T_kv, dtype=torch.bool, device=q.device,
|
| 231 |
+
)
|
| 232 |
+
sdpa_attn_mask = (
|
| 233 |
+
causal.unsqueeze(0).unsqueeze(0)
|
| 234 |
+
& key_padding.unsqueeze(1).unsqueeze(2)
|
| 235 |
+
)
|
| 236 |
+
sdpa_is_causal = False
|
| 237 |
+
|
| 238 |
+
sdpa_kwargs = {
|
| 239 |
+
"attn_mask": sdpa_attn_mask,
|
| 240 |
+
"dropout_p": self.dropout if self.training else 0.0,
|
| 241 |
+
"is_causal": sdpa_is_causal,
|
| 242 |
+
}
|
| 243 |
+
if _SDPA_HAS_GQA and k_exp.size(1) != q.size(1):
|
| 244 |
+
sdpa_kwargs["enable_gqa"] = True
|
| 245 |
+
|
| 246 |
+
y = torch.nn.functional.scaled_dot_product_attention(
|
| 247 |
+
q, k_exp, v_exp,
|
| 248 |
+
**sdpa_kwargs,
|
| 249 |
+
)
|
| 250 |
+
else:
|
| 251 |
+
attn = torch.matmul(q, k_exp.transpose(2, 3)) / math.sqrt(self.head_dim)
|
| 252 |
+
if T_curr == T_kv and T_curr > 1:
|
| 253 |
+
causal_mask_val = torch.tril(
|
| 254 |
+
torch.ones(T_curr, T_curr, device=x.device, dtype=torch.bool),
|
| 255 |
+
).view(1, 1, T_curr, T_curr)
|
| 256 |
+
attn = attn.masked_fill(~causal_mask_val, float('-inf'))
|
| 257 |
+
|
| 258 |
+
if attention_mask is not None:
|
| 259 |
+
additive = (
|
| 260 |
+
1.0 - attention_mask[:, :T_kv].unsqueeze(1).unsqueeze(2).float()
|
| 261 |
+
) * torch.finfo(q.dtype).min
|
| 262 |
+
attn = attn + additive
|
| 263 |
+
|
| 264 |
+
attn = F.softmax(attn, dim=-1)
|
| 265 |
+
attn = self.attn_dropout(attn)
|
| 266 |
+
y = attn @ v_exp
|
| 267 |
+
y = y.transpose(1, 2).contiguous().view(B, T_curr, C)
|
| 268 |
+
y = self.out_proj(y)
|
| 269 |
+
y = self.resid_dropout(y)
|
| 270 |
+
|
| 271 |
+
return y, block_kv_cache
|
| 272 |
+
|
| 273 |
+
class LanguageModelMLP(nn.Module):
|
| 274 |
+
|
| 275 |
+
def __init__(self, cfg):
|
| 276 |
+
super().__init__()
|
| 277 |
+
self.embd_dim = cfg.lm_hidden_dim
|
| 278 |
+
self.inter_dim = cfg.lm_inter_dim
|
| 279 |
+
|
| 280 |
+
self.activation_fn = F.silu
|
| 281 |
+
self.gate_up_proj = nn.Linear(self.embd_dim, 2 * self.inter_dim, bias=False)
|
| 282 |
+
self.down_proj = nn.Linear(self.inter_dim, self.embd_dim, bias=False)
|
| 283 |
+
|
| 284 |
+
self._register_load_state_dict_pre_hook(self._fuse_legacy_gate_up_hook)
|
| 285 |
+
|
| 286 |
+
@staticmethod
|
| 287 |
+
def _fuse_legacy_gate_up_hook(
|
| 288 |
+
state_dict, prefix, local_metadata, strict,
|
| 289 |
+
missing_keys, unexpected_keys, error_msgs,
|
| 290 |
+
):
|
| 291 |
+
|
| 292 |
+
gate_key = f"{prefix}gate_proj.weight"
|
| 293 |
+
up_key = f"{prefix}up_proj.weight"
|
| 294 |
+
fused_key = f"{prefix}gate_up_proj.weight"
|
| 295 |
+
if (
|
| 296 |
+
gate_key in state_dict
|
| 297 |
+
and up_key in state_dict
|
| 298 |
+
and fused_key not in state_dict
|
| 299 |
+
):
|
| 300 |
+
gate_w = state_dict.pop(gate_key)
|
| 301 |
+
up_w = state_dict.pop(up_key)
|
| 302 |
+
state_dict[fused_key] = torch.cat([gate_w, up_w], dim=0)
|
| 303 |
+
|
| 304 |
+
def forward(self, x):
|
| 305 |
+
|
| 306 |
+
gate, up = self.gate_up_proj(x).chunk(2, dim=-1)
|
| 307 |
+
return self.down_proj(self.activation_fn(gate) * up)
|
| 308 |
+
|
| 309 |
+
class LanguageModelBlock(nn.Module):
|
| 310 |
+
def __init__(self, cfg):
|
| 311 |
+
super().__init__()
|
| 312 |
+
self.mlp = LanguageModelMLP(cfg)
|
| 313 |
+
self.attn = LanguageModelGroupedQueryAttention(cfg)
|
| 314 |
+
self.norm1 = RMSNorm(cfg)
|
| 315 |
+
self.norm2 = RMSNorm(cfg)
|
| 316 |
+
def forward(self, x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, attention_mask: torch.Tensor=None, block_kv_cache: dict=None, start_pos: int = 0):
|
| 317 |
+
|
| 318 |
+
res = x
|
| 319 |
+
x = self.norm1(x)
|
| 320 |
+
x, block_kv_cache = self.attn(x, cos, sin, attention_mask, block_kv_cache, start_pos=start_pos)
|
| 321 |
+
x = res + x
|
| 322 |
+
|
| 323 |
+
res = x
|
| 324 |
+
x = self.norm2(x)
|
| 325 |
+
x = self.mlp(x)
|
| 326 |
+
x = res + x
|
| 327 |
+
|
| 328 |
+
return x, block_kv_cache
|
| 329 |
+
|
| 330 |
+
class LanguageModel(nn.Module):
|
| 331 |
+
def __init__(self, cfg):
|
| 332 |
+
super().__init__()
|
| 333 |
+
self.cfg = cfg
|
| 334 |
+
self.lm_use_tokens = cfg.lm_use_tokens
|
| 335 |
+
self.lm_tie_weights = cfg.lm_tie_weights
|
| 336 |
+
|
| 337 |
+
self.token_embedding = nn.Embedding(cfg.lm_vocab_size, cfg.lm_hidden_dim)
|
| 338 |
+
self.rotary_embd = RotaryEmbedding(cfg)
|
| 339 |
+
self.blocks = nn.ModuleList([
|
| 340 |
+
LanguageModelBlock(cfg) for _ in range(cfg.lm_n_blocks)
|
| 341 |
+
])
|
| 342 |
+
self.norm = RMSNorm(cfg)
|
| 343 |
+
self.head = nn.Linear(cfg.lm_hidden_dim, cfg.lm_vocab_size, bias=False)
|
| 344 |
+
if self.lm_tie_weights:
|
| 345 |
+
self.head.weight = self.token_embedding.weight
|
| 346 |
+
|
| 347 |
+
self.apply(self._init_weights)
|
| 348 |
+
|
| 349 |
+
def _init_weights(self, module):
|
| 350 |
+
if isinstance(module, nn.Linear):
|
| 351 |
+
torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
|
| 352 |
+
if module.bias is not None:
|
| 353 |
+
torch.nn.init.zeros_(module.bias)
|
| 354 |
+
elif isinstance(module, nn.Embedding):
|
| 355 |
+
torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
|
| 356 |
+
elif isinstance(module, RMSNorm):
|
| 357 |
+
module.weight.data.fill_(1.0)
|
| 358 |
+
|
| 359 |
+
def forward(self, x: torch.Tensor, attention_mask: torch.Tensor=None, kv_cache: list[dict]=None, start_pos: int=0):
|
| 360 |
+
|
| 361 |
+
if self.lm_use_tokens:
|
| 362 |
+
x = self.token_embedding(x)
|
| 363 |
+
|
| 364 |
+
B, T_curr, _ = x.size()
|
| 365 |
+
|
| 366 |
+
if isinstance(start_pos, torch.Tensor):
|
| 367 |
+
offsets = torch.arange(0, T_curr, device=x.device, dtype=torch.long)
|
| 368 |
+
current_position_ids = (offsets + start_pos).unsqueeze(0).expand(B, -1)
|
| 369 |
+
else:
|
| 370 |
+
current_position_ids = torch.arange(start_pos, start_pos + T_curr, device=x.device).unsqueeze(0).expand(B, -1)
|
| 371 |
+
cos, sin = self.rotary_embd(current_position_ids)
|
| 372 |
+
cos = cos.unsqueeze(1)
|
| 373 |
+
sin = sin.unsqueeze(1)
|
| 374 |
+
|
| 375 |
+
if kv_cache is None:
|
| 376 |
+
kv_cache = [None] * len(self.blocks)
|
| 377 |
+
|
| 378 |
+
for i, block in enumerate(self.blocks):
|
| 379 |
+
x, kv_cache[i] = block(x, cos, sin, attention_mask, kv_cache[i], start_pos=start_pos)
|
| 380 |
+
|
| 381 |
+
x = self.norm(x)
|
| 382 |
+
|
| 383 |
+
if self.lm_use_tokens:
|
| 384 |
+
x = self.head(x)
|
| 385 |
+
|
| 386 |
+
return x, kv_cache
|
| 387 |
+
|
| 388 |
+
@torch.inference_mode()
|
| 389 |
+
def generate(self, inputs: torch.Tensor, max_new_tokens: int=20):
|
| 390 |
+
|
| 391 |
+
if inputs.dim() == 1:
|
| 392 |
+
inputs = inputs.unsqueeze(0)
|
| 393 |
+
generated_outputs = inputs.clone()
|
| 394 |
+
|
| 395 |
+
prompt_output, kv_cache_list = self.forward(
|
| 396 |
+
generated_outputs,
|
| 397 |
+
attention_mask=None,
|
| 398 |
+
kv_cache=None,
|
| 399 |
+
start_pos=0
|
| 400 |
+
)
|
| 401 |
+
last_output = prompt_output[:, -1, :]
|
| 402 |
+
|
| 403 |
+
for i in range(max_new_tokens):
|
| 404 |
+
if self.lm_use_tokens:
|
| 405 |
+
next_output = torch.argmax(last_output, dim=-1, keepdim=True)
|
| 406 |
+
else:
|
| 407 |
+
next_output = last_output.unsqueeze(1)
|
| 408 |
+
|
| 409 |
+
generated_outputs = torch.cat((generated_outputs, next_output), dim=1)
|
| 410 |
+
current_token_start_pos = generated_outputs.size(1) - 1
|
| 411 |
+
|
| 412 |
+
if i == max_new_tokens - 1:
|
| 413 |
+
break
|
| 414 |
+
|
| 415 |
+
decode_step_output, kv_cache_list = self.forward(
|
| 416 |
+
next_output,
|
| 417 |
+
attention_mask=None,
|
| 418 |
+
kv_cache=kv_cache_list,
|
| 419 |
+
start_pos=current_token_start_pos
|
| 420 |
+
)
|
| 421 |
+
last_output = decode_step_output[:, -1, :]
|
| 422 |
+
return generated_outputs
|
| 423 |
+
|
| 424 |
+
@classmethod
|
| 425 |
+
def from_pretrained(cls, cfg):
|
| 426 |
+
from transformers import AutoConfig
|
| 427 |
+
from huggingface_hub import hf_hub_download
|
| 428 |
+
import safetensors
|
| 429 |
+
import torch.nn.init as init
|
| 430 |
+
import json
|
| 431 |
+
from huggingface_hub.utils import EntryNotFoundError
|
| 432 |
+
hf_config = AutoConfig.from_pretrained(cfg.lm_model_type)
|
| 433 |
+
original_vocab_size = hf_config.vocab_size
|
| 434 |
+
cfg.lm_hidden_dim = hf_config.hidden_size
|
| 435 |
+
cfg.lm_inter_dim = hf_config.intermediate_size
|
| 436 |
+
cfg.lm_rms_eps = hf_config.rms_norm_eps
|
| 437 |
+
cfg.lm_re_base = hf_config.rope_theta
|
| 438 |
+
cfg.lm_max_position_embeddings = hf_config.max_position_embeddings
|
| 439 |
+
if hasattr(cfg, 'lm_vocab_size'):
|
| 440 |
+
if cfg.lm_vocab_size < original_vocab_size:
|
| 441 |
+
raise ValueError(f"Config vocab size ({cfg.lm_vocab_size}) is smaller than pretrained model vocab size ({original_vocab_size})")
|
| 442 |
+
else:
|
| 443 |
+
cfg.lm_vocab_size = original_vocab_size
|
| 444 |
+
cfg.lm_n_heads = hf_config.num_attention_heads
|
| 445 |
+
cfg.lm_n_kv_heads = hf_config.num_key_value_heads
|
| 446 |
+
cfg.lm_dropout = hf_config.attention_dropout
|
| 447 |
+
cfg.lm_n_blocks = hf_config.num_hidden_layers
|
| 448 |
+
model = cls(cfg)
|
| 449 |
+
try:
|
| 450 |
+
index_path = hf_hub_download(repo_id=cfg.lm_model_type, filename="model.safetensors.index.json")
|
| 451 |
+
with open(index_path, 'r') as f:
|
| 452 |
+
index = json.load(f)
|
| 453 |
+
safetensors_filenames = sorted(list(set(index['weight_map'].values())))
|
| 454 |
+
safetensors_files = [hf_hub_download(repo_id=cfg.lm_model_type, filename=fn) for fn in safetensors_filenames]
|
| 455 |
+
except EntryNotFoundError:
|
| 456 |
+
safetensors_files = [hf_hub_download(repo_id=cfg.lm_model_type, filename="model.safetensors")]
|
| 457 |
+
|
| 458 |
+
sd = model.state_dict()
|
| 459 |
+
mapping = {
|
| 460 |
+
'model.embed_tokens.weight': 'token_embedding.weight',
|
| 461 |
+
'model.norm.weight': 'norm.weight'
|
| 462 |
+
}
|
| 463 |
+
for i in range(cfg.lm_n_blocks):
|
| 464 |
+
layer_prefix = f'model.layers.{i}.'
|
| 465 |
+
block_prefix = f'blocks.{i}.'
|
| 466 |
+
mapping.update({
|
| 467 |
+
f"{layer_prefix}self_attn.q_proj.weight": f"{block_prefix}attn.q_proj.weight",
|
| 468 |
+
f"{layer_prefix}self_attn.k_proj.weight": f"{block_prefix}attn.k_proj.weight",
|
| 469 |
+
f"{layer_prefix}self_attn.v_proj.weight": f"{block_prefix}attn.v_proj.weight",
|
| 470 |
+
f"{layer_prefix}self_attn.o_proj.weight": f"{block_prefix}attn.out_proj.weight",
|
| 471 |
+
f"{layer_prefix}mlp.down_proj.weight": f"{block_prefix}mlp.down_proj.weight",
|
| 472 |
+
f"{layer_prefix}input_layernorm.weight": f"{block_prefix}norm1.weight",
|
| 473 |
+
f"{layer_prefix}post_attention_layernorm.weight": f"{block_prefix}norm2.weight"
|
| 474 |
+
})
|
| 475 |
+
has_extended_embeddings = False
|
| 476 |
+
loaded_keys = set()
|
| 477 |
+
for safetensors_file in safetensors_files:
|
| 478 |
+
with safetensors.safe_open(filename=safetensors_file, framework="pt", device="cpu") as f:
|
| 479 |
+
for hf_key, our_key in mapping.items():
|
| 480 |
+
if our_key in loaded_keys:
|
| 481 |
+
continue
|
| 482 |
+
if hf_key in f.keys() and our_key in sd:
|
| 483 |
+
tensor = f.get_tensor(hf_key)
|
| 484 |
+
if hf_key == 'model.embed_tokens.weight' and tensor.shape[0] != sd[our_key].shape[0]:
|
| 485 |
+
has_extended_embeddings = True
|
| 486 |
+
print(f"Extending token embeddings from {tensor.shape} to {sd[our_key].shape}")
|
| 487 |
+
sd[our_key][:tensor.shape[0]].copy_(tensor)
|
| 488 |
+
std = 0.02
|
| 489 |
+
init.normal_(sd[our_key][tensor.shape[0]:], mean=0.0, std=std)
|
| 490 |
+
print(f"Initialized {sd[our_key].shape[0] - tensor.shape[0]} new token embeddings")
|
| 491 |
+
sd['head.weight'].copy_(sd[our_key])
|
| 492 |
+
elif tensor.shape == sd[our_key].shape:
|
| 493 |
+
sd[our_key].copy_(tensor)
|
| 494 |
+
else:
|
| 495 |
+
print(f"Shape mismatch for {hf_key} -> {our_key}: {tensor.shape} vs {sd[our_key].shape}")
|
| 496 |
+
loaded_keys.add(our_key)
|
| 497 |
+
|
| 498 |
+
for hf_key, our_key in mapping.items():
|
| 499 |
+
if our_key not in loaded_keys:
|
| 500 |
+
if our_key in sd:
|
| 501 |
+
print(f"Warning: Key {our_key} not found in any safetensors file (HF key: {hf_key})")
|
| 502 |
+
|
| 503 |
+
for i in range(cfg.lm_n_blocks):
|
| 504 |
+
layer_prefix = f"model.layers.{i}."
|
| 505 |
+
block_prefix = f"blocks.{i}."
|
| 506 |
+
fused_param_key = f"{block_prefix}mlp.gate_up_proj.weight"
|
| 507 |
+
if fused_param_key not in sd or fused_param_key in loaded_keys:
|
| 508 |
+
continue
|
| 509 |
+
gate_hf_key = f"{layer_prefix}mlp.gate_proj.weight"
|
| 510 |
+
up_hf_key = f"{layer_prefix}mlp.up_proj.weight"
|
| 511 |
+
gate_w = None
|
| 512 |
+
up_w = None
|
| 513 |
+
for safetensors_file in safetensors_files:
|
| 514 |
+
with safetensors.safe_open(filename=safetensors_file, framework="pt", device="cpu") as f:
|
| 515 |
+
keys = f.keys()
|
| 516 |
+
if gate_w is None and gate_hf_key in keys:
|
| 517 |
+
gate_w = f.get_tensor(gate_hf_key)
|
| 518 |
+
if up_w is None and up_hf_key in keys:
|
| 519 |
+
up_w = f.get_tensor(up_hf_key)
|
| 520 |
+
if gate_w is not None and up_w is not None:
|
| 521 |
+
break
|
| 522 |
+
if gate_w is None or up_w is None:
|
| 523 |
+
print(
|
| 524 |
+
f"Warning: gate_proj or up_proj missing for layer {i} "
|
| 525 |
+
f"(gate={gate_w is not None}, up={up_w is not None}); "
|
| 526 |
+
f"{fused_param_key} left at init values."
|
| 527 |
+
)
|
| 528 |
+
continue
|
| 529 |
+
fused = torch.cat([gate_w, up_w], dim=0)
|
| 530 |
+
if fused.shape != sd[fused_param_key].shape:
|
| 531 |
+
print(
|
| 532 |
+
f"Shape mismatch for fused gate_up at layer {i}: "
|
| 533 |
+
f"{fused.shape} vs {sd[fused_param_key].shape}"
|
| 534 |
+
)
|
| 535 |
+
continue
|
| 536 |
+
sd[fused_param_key].copy_(fused)
|
| 537 |
+
loaded_keys.add(fused_param_key)
|
| 538 |
+
|
| 539 |
+
model.load_state_dict(sd)
|
| 540 |
+
if has_extended_embeddings and hasattr(model, 'head') and 'head.weight' in sd:
|
| 541 |
+
lm_head_loaded = False
|
| 542 |
+
for safetensors_file in safetensors_files:
|
| 543 |
+
with safetensors.safe_open(filename=safetensors_file, framework="pt", device="cpu") as f:
|
| 544 |
+
if 'lm_head.weight' in f.keys():
|
| 545 |
+
lm_head = f.get_tensor('lm_head.weight')
|
| 546 |
+
if lm_head.shape[0] != sd['head.weight'].shape[0]:
|
| 547 |
+
print(f"Extending LM head from {lm_head.shape} to {sd['head.weight'].shape}")
|
| 548 |
+
sd['head.weight'][:lm_head.shape[0]].copy_(lm_head)
|
| 549 |
+
std = 0.02
|
| 550 |
+
init.normal_(sd['head.weight'][lm_head.shape[0]:], mean=0.0, std=std)
|
| 551 |
+
model.load_state_dict(sd)
|
| 552 |
+
lm_head_loaded = True
|
| 553 |
+
break
|
| 554 |
+
if cfg.lm_tie_weights and hasattr(model, 'head') and hasattr(model, 'token_embedding'):
|
| 555 |
+
model.head.weight = model.token_embedding.weight
|
| 556 |
+
print(f"Successfully loaded {cfg.lm_model_type} weights from safetensors. Model has {sum(p.numel() for p in model.parameters()):,} parameters.")
|
| 557 |
+
return model
|
modality_projector.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch.nn as nn
|
| 2 |
+
|
| 3 |
+
class ModalityProjector(nn.Module):
|
| 4 |
+
def __init__(self, cfg):
|
| 5 |
+
super().__init__()
|
| 6 |
+
self.cfg = cfg
|
| 7 |
+
self.input_dim = cfg.vit_hidden_dim * (cfg.mp_pixel_shuffle_factor**2)
|
| 8 |
+
self.output_dim = cfg.lm_hidden_dim
|
| 9 |
+
self.scale_factor = cfg.mp_pixel_shuffle_factor
|
| 10 |
+
|
| 11 |
+
self.proj = nn.Linear(self.input_dim, self.output_dim, bias=False)
|
| 12 |
+
self.apply(self._init_weights)
|
| 13 |
+
|
| 14 |
+
def _init_weights(self, module):
|
| 15 |
+
if isinstance(module, nn.Linear):
|
| 16 |
+
nn.init.normal_(self.proj.weight, mean=0.0, std=0.02)
|
| 17 |
+
if module.bias is not None:
|
| 18 |
+
nn.init.zeros_(module.bias)
|
| 19 |
+
|
| 20 |
+
def pixel_shuffle(self, x):
|
| 21 |
+
bsz, seq, embed_dim = x.size()
|
| 22 |
+
seq_root = int(seq**0.5)
|
| 23 |
+
assert seq_root**2 == seq
|
| 24 |
+
assert seq_root % self.scale_factor == 0
|
| 25 |
+
|
| 26 |
+
height = width = seq_root
|
| 27 |
+
x = x.view(bsz, height, width, embed_dim)
|
| 28 |
+
h_out = height // self.scale_factor
|
| 29 |
+
w_out = width // self.scale_factor
|
| 30 |
+
x = x.reshape(bsz, h_out, self.scale_factor, w_out, self.scale_factor, embed_dim)
|
| 31 |
+
x = x.permute(0, 1, 3, 2, 4, 5).contiguous()
|
| 32 |
+
x = x.reshape(bsz, h_out * w_out, embed_dim * self.scale_factor**2)
|
| 33 |
+
return x
|
| 34 |
+
|
| 35 |
+
def forward(self, x):
|
| 36 |
+
x = self.pixel_shuffle(x)
|
| 37 |
+
x = self.proj(x)
|
| 38 |
+
|
| 39 |
+
return x
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5b5ddf088789717db7d7bc25d8ddd88ee4bf678ff36dfe28ca5d760f79d1e3fa
|
| 3 |
+
size 2029498200
|
model_utils.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def top_k_top_p_filtering(logits, top_k=0, top_p=1.0, filter_value=-float('Inf')):
|
| 5 |
+
top_k = min(top_k, logits.size(-1))
|
| 6 |
+
|
| 7 |
+
if top_k > 0:
|
| 8 |
+
indices_to_remove = logits < torch.topk(logits, top_k)[0][..., -1, None]
|
| 9 |
+
logits = logits.masked_fill(indices_to_remove, filter_value)
|
| 10 |
+
|
| 11 |
+
if top_p < 1.0:
|
| 12 |
+
sorted_logits, sorted_indices = torch.sort(logits, descending=True)
|
| 13 |
+
cumulative_probs = torch.softmax(sorted_logits, dim=-1).cumsum(dim=-1)
|
| 14 |
+
|
| 15 |
+
sorted_indices_to_remove = cumulative_probs > top_p
|
| 16 |
+
|
| 17 |
+
sorted_indices_to_remove[..., 0] = False
|
| 18 |
+
indices_to_remove = sorted_indices_to_remove.scatter(1, sorted_indices, sorted_indices_to_remove)
|
| 19 |
+
logits = logits.masked_fill(indices_to_remove, filter_value)
|
| 20 |
+
|
| 21 |
+
return logits
|
modeling_visionpsynano.py
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""HuggingFace PreTrainedModel wrapper for VisionPsyNano."""
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import os
|
| 5 |
+
from typing import Optional
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
from transformers import PreTrainedModel
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
try:
|
| 12 |
+
from .configuration_visionpsynano import VisionPsyNanoConfig
|
| 13 |
+
except ImportError:
|
| 14 |
+
from configuration_visionpsynano import VisionPsyNanoConfig
|
| 15 |
+
|
| 16 |
+
try:
|
| 17 |
+
from .processors import get_tokenizer
|
| 18 |
+
from .language_model import LanguageModel
|
| 19 |
+
from .modality_projector import ModalityProjector
|
| 20 |
+
from .vision_language_model import VisionLanguageModel
|
| 21 |
+
from .vision_transformer import ViT
|
| 22 |
+
except ImportError as exc:
|
| 23 |
+
raise ImportError(
|
| 24 |
+
"Failed to import VisionPsyNano core modules (models/, data/). "
|
| 25 |
+
"For Hub packages use scripts/package_hub_repo.py so imports are flattened."
|
| 26 |
+
) from exc
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class VisionPsyNanoForConditionalGeneration(PreTrainedModel):
|
| 30 |
+
config_class = VisionPsyNanoConfig
|
| 31 |
+
base_model_prefix = ""
|
| 32 |
+
_no_split_modules = ["ViTBlock", "LanguageModelBlock", "ModalityProjector"]
|
| 33 |
+
main_input_name = "input_ids"
|
| 34 |
+
supports_gradient_checkpointing = False
|
| 35 |
+
|
| 36 |
+
def __init__(self, config: VisionPsyNanoConfig):
|
| 37 |
+
super().__init__(config)
|
| 38 |
+
self.cfg = config.to_vlm_config()
|
| 39 |
+
self.vision_encoder = ViT(self.cfg)
|
| 40 |
+
self.decoder = LanguageModel(self.cfg)
|
| 41 |
+
self.MP = ModalityProjector(self.cfg)
|
| 42 |
+
self.tokenizer = get_tokenizer(
|
| 43 |
+
self.cfg.lm_tokenizer,
|
| 44 |
+
self.cfg.vlm_extra_tokens,
|
| 45 |
+
self.cfg.lm_chat_template,
|
| 46 |
+
)
|
| 47 |
+
self._sync_image_token_id()
|
| 48 |
+
self._compiled_decoder = None
|
| 49 |
+
self._compiled_vision_encoder = None
|
| 50 |
+
self.load_backbone = False
|
| 51 |
+
self.post_init()
|
| 52 |
+
self.tie_weights()
|
| 53 |
+
|
| 54 |
+
def _sync_image_token_id(self) -> None:
|
| 55 |
+
tok = self.tokenizer
|
| 56 |
+
if hasattr(tok, "image_token"):
|
| 57 |
+
tok.image_token_id = tok.convert_tokens_to_ids(tok.image_token)
|
| 58 |
+
elif "<|image|>" in getattr(tok, "get_vocab", lambda: {})():
|
| 59 |
+
tok.image_token = "<|image|>"
|
| 60 |
+
tok.image_token_id = tok.convert_tokens_to_ids("<|image|>")
|
| 61 |
+
|
| 62 |
+
def set_tokenizer(self, tokenizer) -> None:
|
| 63 |
+
self.tokenizer = tokenizer
|
| 64 |
+
self._sync_image_token_id()
|
| 65 |
+
|
| 66 |
+
def _reinit_rotary_cache(self) -> None:
|
| 67 |
+
re = getattr(self.decoder, "rotary_embd", None)
|
| 68 |
+
if re is None or not hasattr(re, "extend_cache"):
|
| 69 |
+
return
|
| 70 |
+
cos = getattr(re, "cos_cached", None)
|
| 71 |
+
if cos is None or getattr(cos, "is_meta", False) or cos.device.type == "meta":
|
| 72 |
+
return
|
| 73 |
+
length = int(getattr(re, "original_max_seq_len", None) or re.max_seq_len)
|
| 74 |
+
re.extend_cache(max(length, 1))
|
| 75 |
+
if bool(torch.isnan(re.cos_cached).any()) or bool(torch.isnan(re.sin_cached).any()):
|
| 76 |
+
device = re.inv_freq.device
|
| 77 |
+
positions = torch.arange(length, dtype=torch.float, device=device)
|
| 78 |
+
freqs = positions.unsqueeze(-1) * re.inv_freq.unsqueeze(0)
|
| 79 |
+
emb = torch.cat([freqs, freqs], dim=-1)
|
| 80 |
+
re.cos_cached = emb.cos() * re.attention_scaling
|
| 81 |
+
re.sin_cached = emb.sin() * re.attention_scaling
|
| 82 |
+
re.original_max_seq_len = length
|
| 83 |
+
|
| 84 |
+
def tie_weights(self, recompute_mapping: bool = True, **kwargs):
|
| 85 |
+
_ = recompute_mapping, kwargs
|
| 86 |
+
if not getattr(self.cfg, "lm_tie_weights", True):
|
| 87 |
+
return
|
| 88 |
+
head_w = self.decoder.head.weight
|
| 89 |
+
emb_w = self.decoder.token_embedding.weight
|
| 90 |
+
if head_w.data_ptr() != emb_w.data_ptr():
|
| 91 |
+
with torch.no_grad():
|
| 92 |
+
emb_w.copy_(head_w)
|
| 93 |
+
self.decoder.head.weight = self.decoder.token_embedding.weight
|
| 94 |
+
|
| 95 |
+
def _tie_weights(self):
|
| 96 |
+
self.tie_weights()
|
| 97 |
+
|
| 98 |
+
def _repair_legacy_checkpoint_weights(
|
| 99 |
+
self, pretrained_model_name_or_path, *, revision: Optional[str] = None
|
| 100 |
+
) -> None:
|
| 101 |
+
weights_path = None
|
| 102 |
+
if os.path.isdir(pretrained_model_name_or_path):
|
| 103 |
+
candidate = os.path.join(pretrained_model_name_or_path, "model.safetensors")
|
| 104 |
+
if os.path.isfile(candidate):
|
| 105 |
+
weights_path = candidate
|
| 106 |
+
else:
|
| 107 |
+
try:
|
| 108 |
+
from huggingface_hub import hf_hub_download
|
| 109 |
+
|
| 110 |
+
weights_path = hf_hub_download(
|
| 111 |
+
pretrained_model_name_or_path,
|
| 112 |
+
"model.safetensors",
|
| 113 |
+
revision=revision,
|
| 114 |
+
)
|
| 115 |
+
except Exception:
|
| 116 |
+
return
|
| 117 |
+
if not weights_path:
|
| 118 |
+
return
|
| 119 |
+
|
| 120 |
+
from safetensors import safe_open
|
| 121 |
+
|
| 122 |
+
with safe_open(weights_path, framework="pt", device="cpu") as f:
|
| 123 |
+
keys = set(f.keys())
|
| 124 |
+
for i, block in enumerate(self.decoder.blocks):
|
| 125 |
+
gate_key = f"decoder.blocks.{i}.mlp.gate_proj.weight"
|
| 126 |
+
up_key = f"decoder.blocks.{i}.mlp.up_proj.weight"
|
| 127 |
+
fused_key = f"decoder.blocks.{i}.mlp.gate_up_proj.weight"
|
| 128 |
+
if gate_key in keys and up_key in keys and fused_key not in keys:
|
| 129 |
+
fused = torch.cat([f.get_tensor(gate_key), f.get_tensor(up_key)], dim=0)
|
| 130 |
+
param = block.mlp.gate_up_proj.weight
|
| 131 |
+
param.data.copy_(fused.to(device=param.device, dtype=param.dtype))
|
| 132 |
+
if (
|
| 133 |
+
"decoder.token_embedding.weight" not in keys
|
| 134 |
+
and "decoder.head.weight" in keys
|
| 135 |
+
):
|
| 136 |
+
head = f.get_tensor("decoder.head.weight")
|
| 137 |
+
emb = self.decoder.token_embedding.weight
|
| 138 |
+
emb.data.copy_(head.to(device=emb.device, dtype=emb.dtype))
|
| 139 |
+
|
| 140 |
+
def _vision_encoder_for_prefill(self):
|
| 141 |
+
return VisionLanguageModel._vision_encoder_for_prefill(self)
|
| 142 |
+
|
| 143 |
+
def _decoder_for_decode(self):
|
| 144 |
+
return VisionLanguageModel._decoder_for_decode(self)
|
| 145 |
+
|
| 146 |
+
def _replace_img_tokens_with_embd(self, input_ids, token_embd, image_embd):
|
| 147 |
+
return VisionLanguageModel._replace_img_tokens_with_embd(
|
| 148 |
+
self, input_ids, token_embd, image_embd
|
| 149 |
+
)
|
| 150 |
+
|
| 151 |
+
def _process_images(self, images, device):
|
| 152 |
+
return VisionLanguageModel._process_images(self, images, device)
|
| 153 |
+
|
| 154 |
+
def apply_deploy_profile(self, device: Optional[torch.device] = None) -> None:
|
| 155 |
+
try:
|
| 156 |
+
from .runtime_profile import apply_deploy_profile
|
| 157 |
+
except ImportError:
|
| 158 |
+
from runtime_profile import apply_deploy_profile
|
| 159 |
+
|
| 160 |
+
apply_deploy_profile(self, device or next(self.parameters()).device)
|
| 161 |
+
|
| 162 |
+
def apply_eager_profile(self) -> None:
|
| 163 |
+
try:
|
| 164 |
+
from .runtime_profile import apply_eager_profile
|
| 165 |
+
except ImportError:
|
| 166 |
+
from runtime_profile import apply_eager_profile
|
| 167 |
+
|
| 168 |
+
apply_eager_profile(self)
|
| 169 |
+
|
| 170 |
+
def forward(
|
| 171 |
+
self,
|
| 172 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 173 |
+
images=None,
|
| 174 |
+
pixel_values=None,
|
| 175 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 176 |
+
labels: Optional[torch.LongTensor] = None,
|
| 177 |
+
targets: Optional[torch.LongTensor] = None,
|
| 178 |
+
**kwargs,
|
| 179 |
+
):
|
| 180 |
+
images = images if images is not None else pixel_values
|
| 181 |
+
targets = targets if targets is not None else labels
|
| 182 |
+
return VisionLanguageModel.forward(
|
| 183 |
+
self, input_ids, images, attention_mask=attention_mask, targets=targets
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
@torch.inference_mode()
|
| 187 |
+
def generate(
|
| 188 |
+
self,
|
| 189 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 190 |
+
images=None,
|
| 191 |
+
pixel_values=None,
|
| 192 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 193 |
+
max_new_tokens: int = 5,
|
| 194 |
+
top_k: int = 50,
|
| 195 |
+
top_p: float = 0.9,
|
| 196 |
+
temperature: float = 0.5,
|
| 197 |
+
greedy: bool = False,
|
| 198 |
+
**kwargs,
|
| 199 |
+
):
|
| 200 |
+
images = images if images is not None else pixel_values
|
| 201 |
+
_ = kwargs
|
| 202 |
+
return VisionLanguageModel.generate(
|
| 203 |
+
self,
|
| 204 |
+
input_ids,
|
| 205 |
+
images,
|
| 206 |
+
attention_mask=attention_mask,
|
| 207 |
+
max_new_tokens=max_new_tokens,
|
| 208 |
+
top_k=top_k,
|
| 209 |
+
top_p=top_p,
|
| 210 |
+
temperature=temperature,
|
| 211 |
+
greedy=greedy,
|
| 212 |
+
)
|
| 213 |
+
|
| 214 |
+
@classmethod
|
| 215 |
+
def from_pretrained(cls, pretrained_model_name_or_path, *model_args, **kwargs):
|
| 216 |
+
model = super().from_pretrained(pretrained_model_name_or_path, *model_args, **kwargs)
|
| 217 |
+
model._repair_legacy_checkpoint_weights(
|
| 218 |
+
pretrained_model_name_or_path, revision=kwargs.get("revision")
|
| 219 |
+
)
|
| 220 |
+
model.tie_weights()
|
| 221 |
+
model._reinit_rotary_cache()
|
| 222 |
+
try:
|
| 223 |
+
from transformers import AutoTokenizer
|
| 224 |
+
|
| 225 |
+
try:
|
| 226 |
+
from .processing_visionpsynano import VisionPsyNanoProcessor
|
| 227 |
+
except ImportError:
|
| 228 |
+
from processing_visionpsynano import VisionPsyNanoProcessor
|
| 229 |
+
|
| 230 |
+
tok = AutoTokenizer.from_pretrained(
|
| 231 |
+
pretrained_model_name_or_path,
|
| 232 |
+
trust_remote_code=kwargs.get("trust_remote_code", False),
|
| 233 |
+
)
|
| 234 |
+
VisionPsyNanoProcessor._attach_extra_token_attrs(tok, model.config.vlm_extra_tokens)
|
| 235 |
+
model.set_tokenizer(tok)
|
| 236 |
+
except Exception:
|
| 237 |
+
model._sync_image_token_id()
|
| 238 |
+
return model
|
| 239 |
+
|
| 240 |
+
@classmethod
|
| 241 |
+
def from_legacy_pretrained(
|
| 242 |
+
cls,
|
| 243 |
+
repo_id_or_path: str,
|
| 244 |
+
*,
|
| 245 |
+
is_flash: Optional[bool] = None,
|
| 246 |
+
variant: Optional[str] = None,
|
| 247 |
+
revision: Optional[str] = None,
|
| 248 |
+
**kwargs,
|
| 249 |
+
) -> "VisionPsyNanoForConditionalGeneration":
|
| 250 |
+
import json
|
| 251 |
+
|
| 252 |
+
from huggingface_hub import hf_hub_download
|
| 253 |
+
from safetensors.torch import load_model
|
| 254 |
+
|
| 255 |
+
if os.path.isdir(repo_id_or_path):
|
| 256 |
+
config_path = os.path.join(repo_id_or_path, "config.json")
|
| 257 |
+
weights_path = os.path.join(repo_id_or_path, "model.safetensors")
|
| 258 |
+
else:
|
| 259 |
+
config_path = hf_hub_download(
|
| 260 |
+
repo_id=repo_id_or_path, filename="config.json", revision=revision
|
| 261 |
+
)
|
| 262 |
+
weights_path = hf_hub_download(
|
| 263 |
+
repo_id=repo_id_or_path, filename="model.safetensors", revision=revision
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
with open(config_path, "r") as f:
|
| 267 |
+
raw = json.load(f)
|
| 268 |
+
|
| 269 |
+
config = VisionPsyNanoConfig.from_legacy_dict(
|
| 270 |
+
raw, is_flash=is_flash, variant=variant
|
| 271 |
+
)
|
| 272 |
+
model = cls(config)
|
| 273 |
+
load_model(model, weights_path)
|
| 274 |
+
model.tie_weights()
|
| 275 |
+
model._reinit_rotary_cache()
|
| 276 |
+
tok_dir = repo_id_or_path if os.path.isdir(repo_id_or_path) else os.path.dirname(config_path)
|
| 277 |
+
if any(
|
| 278 |
+
os.path.exists(os.path.join(tok_dir, n))
|
| 279 |
+
for n in ("tokenizer.json", "tokenizer_config.json")
|
| 280 |
+
):
|
| 281 |
+
from transformers import AutoTokenizer
|
| 282 |
+
|
| 283 |
+
try:
|
| 284 |
+
from .processing_visionpsynano import VisionPsyNanoProcessor
|
| 285 |
+
except ImportError:
|
| 286 |
+
from processing_visionpsynano import VisionPsyNanoProcessor
|
| 287 |
+
|
| 288 |
+
tok = AutoTokenizer.from_pretrained(tok_dir)
|
| 289 |
+
VisionPsyNanoProcessor._attach_extra_token_attrs(tok, config.vlm_extra_tokens)
|
| 290 |
+
model.set_tokenizer(tok)
|
| 291 |
+
return model
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
try:
|
| 295 |
+
from transformers import AutoConfig, AutoModel, AutoModelForImageTextToText
|
| 296 |
+
|
| 297 |
+
AutoConfig.register("visionpsynano", VisionPsyNanoConfig)
|
| 298 |
+
AutoModel.register(VisionPsyNanoConfig, VisionPsyNanoForConditionalGeneration)
|
| 299 |
+
AutoModelForImageTextToText.register(VisionPsyNanoConfig, VisionPsyNanoForConditionalGeneration)
|
| 300 |
+
except Exception:
|
| 301 |
+
pass
|
preprocessor_config.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"processor_class": "VisionPsyNanoProcessor",
|
| 3 |
+
"auto_map": {
|
| 4 |
+
"AutoProcessor": "processing_visionpsynano.VisionPsyNanoProcessor"
|
| 5 |
+
},
|
| 6 |
+
"is_flash": false,
|
| 7 |
+
"vit_img_size": 512,
|
| 8 |
+
"max_img_size": 2048,
|
| 9 |
+
"inference_max_img_size": null,
|
| 10 |
+
"resize_to_max_side_len": true,
|
| 11 |
+
"resize_min_side_len": null,
|
| 12 |
+
"mp_image_token_length": 64,
|
| 13 |
+
"lm_max_position_embeddings": 8192,
|
| 14 |
+
"lm_tokenizer": "HuggingFaceTB/SmolLM2-360M-Instruct",
|
| 15 |
+
"lm_chat_template": "{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}",
|
| 16 |
+
"vlm_extra_tokens": {
|
| 17 |
+
"image_token": "<|image|>",
|
| 18 |
+
"global_image_token": "<|global_image|>",
|
| 19 |
+
"r1c1": "<row_1_col_1>",
|
| 20 |
+
"r1c2": "<row_1_col_2>",
|
| 21 |
+
"r1c3": "<row_1_col_3>",
|
| 22 |
+
"r1c4": "<row_1_col_4>",
|
| 23 |
+
"r1c5": "<row_1_col_5>",
|
| 24 |
+
"r1c6": "<row_1_col_6>",
|
| 25 |
+
"r1c7": "<row_1_col_7>",
|
| 26 |
+
"r1c8": "<row_1_col_8>",
|
| 27 |
+
"r2c1": "<row_2_col_1>",
|
| 28 |
+
"r2c2": "<row_2_col_2>",
|
| 29 |
+
"r2c3": "<row_2_col_3>",
|
| 30 |
+
"r2c4": "<row_2_col_4>",
|
| 31 |
+
"r2c5": "<row_2_col_5>",
|
| 32 |
+
"r2c6": "<row_2_col_6>",
|
| 33 |
+
"r2c7": "<row_2_col_7>",
|
| 34 |
+
"r2c8": "<row_2_col_8>",
|
| 35 |
+
"r3c1": "<row_3_col_1>",
|
| 36 |
+
"r3c2": "<row_3_col_2>",
|
| 37 |
+
"r3c3": "<row_3_col_3>",
|
| 38 |
+
"r3c4": "<row_3_col_4>",
|
| 39 |
+
"r3c5": "<row_3_col_5>",
|
| 40 |
+
"r3c6": "<row_3_col_6>",
|
| 41 |
+
"r3c7": "<row_3_col_7>",
|
| 42 |
+
"r3c8": "<row_3_col_8>",
|
| 43 |
+
"r4c1": "<row_4_col_1>",
|
| 44 |
+
"r4c2": "<row_4_col_2>",
|
| 45 |
+
"r4c3": "<row_4_col_3>",
|
| 46 |
+
"r4c4": "<row_4_col_4>",
|
| 47 |
+
"r4c5": "<row_4_col_5>",
|
| 48 |
+
"r4c6": "<row_4_col_6>",
|
| 49 |
+
"r4c7": "<row_4_col_7>",
|
| 50 |
+
"r4c8": "<row_4_col_8>",
|
| 51 |
+
"r5c1": "<row_5_col_1>",
|
| 52 |
+
"r5c2": "<row_5_col_2>",
|
| 53 |
+
"r5c3": "<row_5_col_3>",
|
| 54 |
+
"r5c4": "<row_5_col_4>",
|
| 55 |
+
"r5c5": "<row_5_col_5>",
|
| 56 |
+
"r5c6": "<row_5_col_6>",
|
| 57 |
+
"r5c7": "<row_5_col_7>",
|
| 58 |
+
"r5c8": "<row_5_col_8>",
|
| 59 |
+
"r6c1": "<row_6_col_1>",
|
| 60 |
+
"r6c2": "<row_6_col_2>",
|
| 61 |
+
"r6c3": "<row_6_col_3>",
|
| 62 |
+
"r6c4": "<row_6_col_4>",
|
| 63 |
+
"r6c5": "<row_6_col_5>",
|
| 64 |
+
"r6c6": "<row_6_col_6>",
|
| 65 |
+
"r6c7": "<row_6_col_7>",
|
| 66 |
+
"r6c8": "<row_6_col_8>",
|
| 67 |
+
"r7c1": "<row_7_col_1>",
|
| 68 |
+
"r7c2": "<row_7_col_2>",
|
| 69 |
+
"r7c3": "<row_7_col_3>",
|
| 70 |
+
"r7c4": "<row_7_col_4>",
|
| 71 |
+
"r7c5": "<row_7_col_5>",
|
| 72 |
+
"r7c6": "<row_7_col_6>",
|
| 73 |
+
"r7c7": "<row_7_col_7>",
|
| 74 |
+
"r7c8": "<row_7_col_8>",
|
| 75 |
+
"r8c1": "<row_8_col_1>",
|
| 76 |
+
"r8c2": "<row_8_col_2>",
|
| 77 |
+
"r8c3": "<row_8_col_3>",
|
| 78 |
+
"r8c4": "<row_8_col_4>",
|
| 79 |
+
"r8c5": "<row_8_col_5>",
|
| 80 |
+
"r8c6": "<row_8_col_6>",
|
| 81 |
+
"r8c7": "<row_8_col_7>",
|
| 82 |
+
"r8c8": "<row_8_col_8>"
|
| 83 |
+
}
|
| 84 |
+
}
|
processing_visionpsynano.py
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""HuggingFace processor for VisionPsyNano."""
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import json
|
| 5 |
+
import os
|
| 6 |
+
from typing import Any, List, Optional, Union
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from PIL import Image
|
| 10 |
+
from transformers import AutoTokenizer, BatchFeature, ProcessorMixin
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
try:
|
| 15 |
+
from .configuration_visionpsynano import (
|
| 16 |
+
VisionPsyNanoConfig,
|
| 17 |
+
apply_flash_preprocess,
|
| 18 |
+
resolve_is_flash,
|
| 19 |
+
)
|
| 20 |
+
except ImportError:
|
| 21 |
+
from configuration_visionpsynano import (
|
| 22 |
+
VisionPsyNanoConfig,
|
| 23 |
+
apply_flash_preprocess,
|
| 24 |
+
resolve_is_flash,
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
try:
|
| 28 |
+
from .processors import get_image_processor, get_image_string, get_tokenizer
|
| 29 |
+
except ImportError as exc:
|
| 30 |
+
raise ImportError(
|
| 31 |
+
"Failed to import VisionPsyNano data processors. "
|
| 32 |
+
"For Hub packages use scripts/package_hub_repo.py so imports are flattened."
|
| 33 |
+
) from exc
|
| 34 |
+
|
| 35 |
+
logger = logging.get_logger(__name__)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _as_pil_list(images) -> List[Image.Image]:
|
| 39 |
+
if images is None:
|
| 40 |
+
return []
|
| 41 |
+
if isinstance(images, Image.Image):
|
| 42 |
+
return [images.convert("RGB")]
|
| 43 |
+
if isinstance(images, (list, tuple)):
|
| 44 |
+
out = []
|
| 45 |
+
for im in images:
|
| 46 |
+
if isinstance(im, Image.Image):
|
| 47 |
+
out.append(im.convert("RGB"))
|
| 48 |
+
else:
|
| 49 |
+
raise TypeError(f"Expected PIL.Image, got {type(im)}")
|
| 50 |
+
return out
|
| 51 |
+
raise TypeError(f"Unsupported images type: {type(images)}")
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def _extract_user_text(text: Any) -> str:
|
| 55 |
+
"""Normalize text / chat messages into a single user prompt string."""
|
| 56 |
+
if text is None:
|
| 57 |
+
return ""
|
| 58 |
+
if isinstance(text, str):
|
| 59 |
+
return text
|
| 60 |
+
if isinstance(text, list) and text and isinstance(text[0], dict) and "role" in text[0]:
|
| 61 |
+
parts = []
|
| 62 |
+
for msg in text:
|
| 63 |
+
content = msg.get("content", "")
|
| 64 |
+
if isinstance(content, str):
|
| 65 |
+
parts.append(content)
|
| 66 |
+
elif isinstance(content, list):
|
| 67 |
+
for block in content:
|
| 68 |
+
if isinstance(block, str):
|
| 69 |
+
parts.append(block)
|
| 70 |
+
elif isinstance(block, dict) and block.get("type") == "text":
|
| 71 |
+
parts.append(block.get("text", ""))
|
| 72 |
+
return " ".join(p for p in parts if p).strip()
|
| 73 |
+
if isinstance(text, list) and all(isinstance(t, str) for t in text):
|
| 74 |
+
if len(text) == 1:
|
| 75 |
+
return text[0]
|
| 76 |
+
raise ValueError("Batch text list with >1 items is not supported; call per sample.")
|
| 77 |
+
raise TypeError(f"Unsupported text type: {type(text)}")
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class VisionPsyNanoProcessor(ProcessorMixin):
|
| 81 |
+
"""Tokenizer + VisionPsyNano image preprocessing (default or Flash)."""
|
| 82 |
+
|
| 83 |
+
attributes = ["tokenizer"]
|
| 84 |
+
tokenizer_class = "AutoTokenizer"
|
| 85 |
+
|
| 86 |
+
def __init__(self, tokenizer, chat_template=None, **kwargs):
|
| 87 |
+
image_keys = {
|
| 88 |
+
"is_flash",
|
| 89 |
+
"variant",
|
| 90 |
+
"vit_img_size",
|
| 91 |
+
"max_img_size",
|
| 92 |
+
"inference_max_img_size",
|
| 93 |
+
"resize_to_max_side_len",
|
| 94 |
+
"resize_min_side_len",
|
| 95 |
+
"mp_image_token_length",
|
| 96 |
+
"lm_max_position_embeddings",
|
| 97 |
+
"vlm_extra_tokens",
|
| 98 |
+
"lm_tokenizer",
|
| 99 |
+
"lm_chat_template",
|
| 100 |
+
}
|
| 101 |
+
image_cfg = {k: kwargs.pop(k) for k in list(kwargs) if k in image_keys}
|
| 102 |
+
super().__init__(tokenizer, chat_template=chat_template, **kwargs)
|
| 103 |
+
|
| 104 |
+
flash = resolve_is_flash(
|
| 105 |
+
is_flash=image_cfg.get("is_flash"),
|
| 106 |
+
variant=image_cfg.get("variant"),
|
| 107 |
+
resize_to_max_side_len=image_cfg.get("resize_to_max_side_len"),
|
| 108 |
+
)
|
| 109 |
+
resize_to_max, resize_min = apply_flash_preprocess(
|
| 110 |
+
is_flash=flash,
|
| 111 |
+
resize_to_max_side_len=image_cfg.get("resize_to_max_side_len"),
|
| 112 |
+
resize_min_side_len=image_cfg.get("resize_min_side_len"),
|
| 113 |
+
)
|
| 114 |
+
self.is_flash = flash
|
| 115 |
+
self.vit_img_size = int(image_cfg.get("vit_img_size", 512))
|
| 116 |
+
self.max_img_size = int(image_cfg.get("max_img_size", 2048))
|
| 117 |
+
self.inference_max_img_size = image_cfg.get("inference_max_img_size")
|
| 118 |
+
self.resize_to_max_side_len = resize_to_max
|
| 119 |
+
self.resize_min_side_len = resize_min
|
| 120 |
+
self.mp_image_token_length = int(image_cfg.get("mp_image_token_length", 64))
|
| 121 |
+
self.lm_max_position_embeddings = int(
|
| 122 |
+
image_cfg.get("lm_max_position_embeddings", 8192)
|
| 123 |
+
)
|
| 124 |
+
self.vlm_extra_tokens = image_cfg.get("vlm_extra_tokens")
|
| 125 |
+
self.lm_tokenizer = image_cfg.get("lm_tokenizer")
|
| 126 |
+
self.lm_chat_template = image_cfg.get("lm_chat_template")
|
| 127 |
+
|
| 128 |
+
def _effective_max_img_size(self) -> int:
|
| 129 |
+
return int(self.inference_max_img_size or self.max_img_size)
|
| 130 |
+
|
| 131 |
+
def _build_image_processor(self):
|
| 132 |
+
return get_image_processor(
|
| 133 |
+
self._effective_max_img_size(),
|
| 134 |
+
self.vit_img_size,
|
| 135 |
+
self.resize_to_max_side_len,
|
| 136 |
+
self.resize_min_side_len,
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
def set_flash(self, enabled: bool = True) -> None:
|
| 140 |
+
"""Enable/disable Flash optimized image preprocessing in-place."""
|
| 141 |
+
self.is_flash = bool(enabled)
|
| 142 |
+
self.resize_to_max_side_len, self.resize_min_side_len = apply_flash_preprocess(
|
| 143 |
+
is_flash=self.is_flash,
|
| 144 |
+
resize_to_max_side_len=None,
|
| 145 |
+
resize_min_side_len=self.resize_min_side_len if self.is_flash else None,
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
def __call__(
|
| 149 |
+
self,
|
| 150 |
+
images: Optional[Union[Image.Image, List[Image.Image]]] = None,
|
| 151 |
+
text: Optional[Any] = None,
|
| 152 |
+
return_tensors: Optional[str] = "pt",
|
| 153 |
+
is_flash: Optional[bool] = None,
|
| 154 |
+
**kwargs,
|
| 155 |
+
) -> BatchFeature:
|
| 156 |
+
if is_flash is not None:
|
| 157 |
+
self.set_flash(bool(is_flash))
|
| 158 |
+
|
| 159 |
+
prompt = _extract_user_text(text)
|
| 160 |
+
pil_images = _as_pil_list(images)
|
| 161 |
+
|
| 162 |
+
image_processor = self._build_image_processor()
|
| 163 |
+
processed_tensors = []
|
| 164 |
+
ratios = []
|
| 165 |
+
for img in pil_images:
|
| 166 |
+
processed_image, splitted_image_ratio = image_processor(img)
|
| 167 |
+
if (
|
| 168 |
+
not hasattr(self.tokenizer, "global_image_token")
|
| 169 |
+
and splitted_image_ratio[0] * splitted_image_ratio[1]
|
| 170 |
+
== len(processed_image) - 1
|
| 171 |
+
):
|
| 172 |
+
processed_image = processed_image[1:]
|
| 173 |
+
processed_tensors.append(processed_image)
|
| 174 |
+
ratios.append(splitted_image_ratio)
|
| 175 |
+
|
| 176 |
+
if processed_tensors:
|
| 177 |
+
image_string = get_image_string(
|
| 178 |
+
self.tokenizer, ratios, self.mp_image_token_length
|
| 179 |
+
)
|
| 180 |
+
images_tensor = torch.cat(processed_tensors, dim=0)
|
| 181 |
+
else:
|
| 182 |
+
image_string = ""
|
| 183 |
+
images_tensor = None
|
| 184 |
+
|
| 185 |
+
messages = [{"role": "user", "content": image_string + prompt}]
|
| 186 |
+
full_prompt = self.tokenizer.apply_chat_template(
|
| 187 |
+
[messages], tokenize=False, add_generation_prompt=True
|
| 188 |
+
)
|
| 189 |
+
if isinstance(full_prompt, list):
|
| 190 |
+
full_prompt = full_prompt[0]
|
| 191 |
+
|
| 192 |
+
encoded = self.tokenizer(
|
| 193 |
+
[full_prompt],
|
| 194 |
+
return_tensors=return_tensors,
|
| 195 |
+
padding=False,
|
| 196 |
+
truncation=True,
|
| 197 |
+
max_length=self.lm_max_position_embeddings,
|
| 198 |
+
**{k: v for k, v in kwargs.items() if k in ("padding", "truncation", "max_length")},
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
data = {
|
| 202 |
+
"input_ids": encoded["input_ids"],
|
| 203 |
+
"attention_mask": encoded.get("attention_mask"),
|
| 204 |
+
}
|
| 205 |
+
if images_tensor is not None:
|
| 206 |
+
data["images"] = images_tensor
|
| 207 |
+
data["pixel_values"] = images_tensor # HF-friendly alias
|
| 208 |
+
return BatchFeature(data=data, tensor_type=return_tensors)
|
| 209 |
+
|
| 210 |
+
def batch_decode(self, *args, **kwargs):
|
| 211 |
+
return self.tokenizer.batch_decode(*args, **kwargs)
|
| 212 |
+
|
| 213 |
+
def decode(self, *args, **kwargs):
|
| 214 |
+
return self.tokenizer.decode(*args, **kwargs)
|
| 215 |
+
|
| 216 |
+
def image_config_dict(self) -> dict:
|
| 217 |
+
return {
|
| 218 |
+
"processor_class": "VisionPsyNanoProcessor",
|
| 219 |
+
"auto_map": {
|
| 220 |
+
"AutoProcessor": "processing_visionpsynano.VisionPsyNanoProcessor",
|
| 221 |
+
},
|
| 222 |
+
"is_flash": self.is_flash,
|
| 223 |
+
"vit_img_size": self.vit_img_size,
|
| 224 |
+
"max_img_size": self.max_img_size,
|
| 225 |
+
"inference_max_img_size": self.inference_max_img_size,
|
| 226 |
+
"resize_to_max_side_len": self.resize_to_max_side_len,
|
| 227 |
+
"resize_min_side_len": self.resize_min_side_len,
|
| 228 |
+
"mp_image_token_length": self.mp_image_token_length,
|
| 229 |
+
"lm_max_position_embeddings": self.lm_max_position_embeddings,
|
| 230 |
+
"lm_tokenizer": self.lm_tokenizer,
|
| 231 |
+
"lm_chat_template": self.lm_chat_template,
|
| 232 |
+
"vlm_extra_tokens": self.vlm_extra_tokens,
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
def save_pretrained(self, save_directory: str, **kwargs):
|
| 236 |
+
os.makedirs(save_directory, exist_ok=True)
|
| 237 |
+
super().save_pretrained(save_directory, **kwargs)
|
| 238 |
+
|
| 239 |
+
proc_cfg_path = os.path.join(save_directory, "processor_config.json")
|
| 240 |
+
proc_cfg = {}
|
| 241 |
+
if os.path.exists(proc_cfg_path):
|
| 242 |
+
with open(proc_cfg_path) as f:
|
| 243 |
+
proc_cfg = json.load(f)
|
| 244 |
+
proc_cfg["processor_class"] = "VisionPsyNanoProcessor"
|
| 245 |
+
proc_cfg["auto_map"] = {
|
| 246 |
+
"AutoProcessor": "processing_visionpsynano.VisionPsyNanoProcessor",
|
| 247 |
+
}
|
| 248 |
+
with open(proc_cfg_path, "w") as f:
|
| 249 |
+
json.dump(proc_cfg, f, indent=2)
|
| 250 |
+
f.write("\n")
|
| 251 |
+
|
| 252 |
+
with open(os.path.join(save_directory, "preprocessor_config.json"), "w") as f:
|
| 253 |
+
json.dump(self.image_config_dict(), f, indent=2)
|
| 254 |
+
f.write("\n")
|
| 255 |
+
|
| 256 |
+
@staticmethod
|
| 257 |
+
def _attach_extra_token_attrs(tokenizer, vlm_extra_tokens: Optional[dict]):
|
| 258 |
+
"""Restore named attrs (``image_token``, ``r1c1``, ...) used by image strings."""
|
| 259 |
+
if not vlm_extra_tokens:
|
| 260 |
+
return tokenizer
|
| 261 |
+
for name, token in vlm_extra_tokens.items():
|
| 262 |
+
if not hasattr(tokenizer, name):
|
| 263 |
+
setattr(tokenizer, name, token)
|
| 264 |
+
if hasattr(tokenizer, "image_token"):
|
| 265 |
+
try:
|
| 266 |
+
tokenizer.image_token_id = tokenizer.convert_tokens_to_ids(tokenizer.image_token)
|
| 267 |
+
except Exception:
|
| 268 |
+
pass
|
| 269 |
+
return tokenizer
|
| 270 |
+
|
| 271 |
+
@classmethod
|
| 272 |
+
def from_pretrained(cls, pretrained_model_name_or_path, **kwargs):
|
| 273 |
+
trust_remote_code = kwargs.pop("trust_remote_code", False)
|
| 274 |
+
flash_override = kwargs.pop("is_flash", None)
|
| 275 |
+
|
| 276 |
+
tokenizer = None
|
| 277 |
+
prep = {}
|
| 278 |
+
|
| 279 |
+
def _prep_from_cfg(cfg: VisionPsyNanoConfig) -> dict:
|
| 280 |
+
return {
|
| 281 |
+
"is_flash": cfg.is_flash,
|
| 282 |
+
"vit_img_size": cfg.vit_img_size,
|
| 283 |
+
"max_img_size": cfg.max_img_size,
|
| 284 |
+
"inference_max_img_size": cfg.inference_max_img_size,
|
| 285 |
+
"resize_to_max_side_len": cfg.resize_to_max_side_len,
|
| 286 |
+
"resize_min_side_len": cfg.resize_min_side_len,
|
| 287 |
+
"mp_image_token_length": cfg.mp_image_token_length,
|
| 288 |
+
"lm_max_position_embeddings": cfg.lm_max_position_embeddings,
|
| 289 |
+
"lm_tokenizer": cfg.lm_tokenizer,
|
| 290 |
+
"lm_chat_template": cfg.lm_chat_template,
|
| 291 |
+
"vlm_extra_tokens": cfg.vlm_extra_tokens,
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
resolved = pretrained_model_name_or_path
|
| 295 |
+
prep_path = None
|
| 296 |
+
if os.path.isdir(resolved):
|
| 297 |
+
prep_path = os.path.join(resolved, "preprocessor_config.json")
|
| 298 |
+
tok_files_present = any(
|
| 299 |
+
os.path.exists(os.path.join(resolved, name))
|
| 300 |
+
for name in ("tokenizer.json", "tokenizer_config.json", "vocab.json")
|
| 301 |
+
)
|
| 302 |
+
if tok_files_present:
|
| 303 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 304 |
+
resolved, trust_remote_code=trust_remote_code, **kwargs
|
| 305 |
+
)
|
| 306 |
+
if prep_path and os.path.exists(prep_path):
|
| 307 |
+
with open(prep_path) as f:
|
| 308 |
+
prep = json.load(f)
|
| 309 |
+
else:
|
| 310 |
+
from huggingface_hub import hf_hub_download, list_repo_files
|
| 311 |
+
|
| 312 |
+
try:
|
| 313 |
+
files = set(list_repo_files(resolved))
|
| 314 |
+
except Exception:
|
| 315 |
+
files = set()
|
| 316 |
+
if "preprocessor_config.json" in files:
|
| 317 |
+
prep_path = hf_hub_download(resolved, "preprocessor_config.json")
|
| 318 |
+
with open(prep_path) as f:
|
| 319 |
+
prep = json.load(f)
|
| 320 |
+
if any(n in files for n in ("tokenizer.json", "tokenizer_config.json", "vocab.json")):
|
| 321 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 322 |
+
resolved, trust_remote_code=trust_remote_code, **kwargs
|
| 323 |
+
)
|
| 324 |
+
|
| 325 |
+
if tokenizer is None:
|
| 326 |
+
lm_tok = prep.get("lm_tokenizer")
|
| 327 |
+
extra = prep.get("vlm_extra_tokens")
|
| 328 |
+
chat = prep.get("lm_chat_template")
|
| 329 |
+
if lm_tok is None:
|
| 330 |
+
cfg = VisionPsyNanoConfig.from_pretrained(
|
| 331 |
+
pretrained_model_name_or_path, trust_remote_code=trust_remote_code
|
| 332 |
+
)
|
| 333 |
+
lm_tok = cfg.lm_tokenizer
|
| 334 |
+
extra = cfg.vlm_extra_tokens
|
| 335 |
+
chat = cfg.lm_chat_template
|
| 336 |
+
if not prep:
|
| 337 |
+
prep = _prep_from_cfg(cfg)
|
| 338 |
+
tokenizer = get_tokenizer(lm_tok, extra, chat)
|
| 339 |
+
|
| 340 |
+
if not prep:
|
| 341 |
+
try:
|
| 342 |
+
cfg = VisionPsyNanoConfig.from_pretrained(
|
| 343 |
+
pretrained_model_name_or_path, trust_remote_code=trust_remote_code
|
| 344 |
+
)
|
| 345 |
+
prep = _prep_from_cfg(cfg)
|
| 346 |
+
except Exception as e:
|
| 347 |
+
logger.warning("Could not load VisionPsyNanoConfig for processor defaults: %s", e)
|
| 348 |
+
|
| 349 |
+
if flash_override is not None:
|
| 350 |
+
prep["is_flash"] = bool(flash_override)
|
| 351 |
+
|
| 352 |
+
extra = prep.get("vlm_extra_tokens")
|
| 353 |
+
if extra is None:
|
| 354 |
+
try:
|
| 355 |
+
cfg = VisionPsyNanoConfig.from_pretrained(
|
| 356 |
+
pretrained_model_name_or_path, trust_remote_code=trust_remote_code
|
| 357 |
+
)
|
| 358 |
+
extra = cfg.vlm_extra_tokens
|
| 359 |
+
prep.setdefault("vlm_extra_tokens", extra)
|
| 360 |
+
except Exception:
|
| 361 |
+
pass
|
| 362 |
+
cls._attach_extra_token_attrs(tokenizer, extra)
|
| 363 |
+
|
| 364 |
+
chat_template = getattr(tokenizer, "chat_template", None) or prep.get("lm_chat_template")
|
| 365 |
+
return cls(tokenizer, chat_template=chat_template, **{
|
| 366 |
+
k: prep[k]
|
| 367 |
+
for k in (
|
| 368 |
+
"is_flash",
|
| 369 |
+
"vit_img_size",
|
| 370 |
+
"max_img_size",
|
| 371 |
+
"inference_max_img_size",
|
| 372 |
+
"resize_to_max_side_len",
|
| 373 |
+
"resize_min_side_len",
|
| 374 |
+
"mp_image_token_length",
|
| 375 |
+
"lm_max_position_embeddings",
|
| 376 |
+
"vlm_extra_tokens",
|
| 377 |
+
"lm_tokenizer",
|
| 378 |
+
"lm_chat_template",
|
| 379 |
+
)
|
| 380 |
+
if k in prep
|
| 381 |
+
})
|
| 382 |
+
|
| 383 |
+
@classmethod
|
| 384 |
+
def from_config(cls, config: VisionPsyNanoConfig, tokenizer=None) -> "VisionPsyNanoProcessor":
|
| 385 |
+
if tokenizer is None:
|
| 386 |
+
tokenizer = get_tokenizer(
|
| 387 |
+
config.lm_tokenizer, config.vlm_extra_tokens, config.lm_chat_template
|
| 388 |
+
)
|
| 389 |
+
return cls(
|
| 390 |
+
tokenizer,
|
| 391 |
+
chat_template=config.lm_chat_template,
|
| 392 |
+
is_flash=config.is_flash,
|
| 393 |
+
vit_img_size=config.vit_img_size,
|
| 394 |
+
max_img_size=config.max_img_size,
|
| 395 |
+
inference_max_img_size=config.inference_max_img_size,
|
| 396 |
+
resize_to_max_side_len=config.resize_to_max_side_len,
|
| 397 |
+
resize_min_side_len=config.resize_min_side_len,
|
| 398 |
+
mp_image_token_length=config.mp_image_token_length,
|
| 399 |
+
lm_max_position_embeddings=config.lm_max_position_embeddings,
|
| 400 |
+
vlm_extra_tokens=config.vlm_extra_tokens,
|
| 401 |
+
lm_tokenizer=config.lm_tokenizer,
|
| 402 |
+
lm_chat_template=config.lm_chat_template,
|
| 403 |
+
)
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
try:
|
| 407 |
+
from transformers import AutoProcessor
|
| 408 |
+
|
| 409 |
+
AutoProcessor.register(VisionPsyNanoConfig, VisionPsyNanoProcessor)
|
| 410 |
+
except Exception:
|
| 411 |
+
pass
|
processor_config.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"processor_class": "VisionPsyNanoProcessor",
|
| 3 |
+
"auto_map": {
|
| 4 |
+
"AutoProcessor": "processing_visionpsynano.VisionPsyNanoProcessor"
|
| 5 |
+
}
|
| 6 |
+
}
|
processors.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
|
| 3 |
+
from transformers import AutoTokenizer
|
| 4 |
+
import torchvision.transforms as transforms
|
| 5 |
+
|
| 6 |
+
from .custom_transforms import DynamicResize, GlobalAndSplitImages
|
| 7 |
+
|
| 8 |
+
TOKENIZERS_CACHE = {}
|
| 9 |
+
FLASH_MIN_SIDE_LEN = 512
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
# Local copies of configuration_visionpsynano.{resolve_is_flash,apply_flash_preprocess}:
|
| 13 |
+
# the data/ tree is vendored standalone (vLLM plugin), so it must not import from
|
| 14 |
+
# the HF package layout. Keep in sync with configuration_visionpsynano.py.
|
| 15 |
+
def resolve_is_flash(
|
| 16 |
+
*,
|
| 17 |
+
is_flash: Optional[bool] = None,
|
| 18 |
+
variant: Optional[str] = None,
|
| 19 |
+
resize_to_max_side_len: Optional[bool] = None,
|
| 20 |
+
) -> bool:
|
| 21 |
+
"""Resolve Flash preprocess mode from explicit flag, legacy variant, or resize policy."""
|
| 22 |
+
if is_flash is not None:
|
| 23 |
+
return bool(is_flash)
|
| 24 |
+
if variant is not None:
|
| 25 |
+
v = str(variant).lower().strip()
|
| 26 |
+
if v in ("flash", "nano-flash", "visionpsy-nano-flash"):
|
| 27 |
+
return True
|
| 28 |
+
if v in ("nano", "plain", "nano-plain", "visionpsy-nano"):
|
| 29 |
+
return False
|
| 30 |
+
raise ValueError(
|
| 31 |
+
"legacy variant must be 'nano' or 'flash' "
|
| 32 |
+
f"(or aliases); got {variant!r}. Prefer is_flash=True/False."
|
| 33 |
+
)
|
| 34 |
+
if resize_to_max_side_len is not None:
|
| 35 |
+
return not bool(resize_to_max_side_len)
|
| 36 |
+
return False
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def apply_flash_preprocess(
|
| 40 |
+
*,
|
| 41 |
+
is_flash: bool,
|
| 42 |
+
resize_to_max_side_len: Optional[bool] = None,
|
| 43 |
+
resize_min_side_len: Optional[int] = None,
|
| 44 |
+
) -> tuple[bool, Optional[int]]:
|
| 45 |
+
"""Return (resize_to_max_side_len, resize_min_side_len) for the given Flash mode."""
|
| 46 |
+
if resize_to_max_side_len is None:
|
| 47 |
+
resize_to_max_side_len = not is_flash
|
| 48 |
+
resize_to_max_side_len = bool(resize_to_max_side_len)
|
| 49 |
+
if is_flash:
|
| 50 |
+
resize_min_side_len = max(int(resize_min_side_len or 0), FLASH_MIN_SIDE_LEN)
|
| 51 |
+
elif resize_to_max_side_len:
|
| 52 |
+
resize_min_side_len = None
|
| 53 |
+
return resize_to_max_side_len, resize_min_side_len
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def apply_model_preprocess(cfg, *, flash: bool = False) -> bool:
|
| 57 |
+
is_flash = resolve_is_flash(
|
| 58 |
+
is_flash=True if flash else getattr(cfg, "is_flash", None),
|
| 59 |
+
variant=getattr(cfg, "variant", None),
|
| 60 |
+
resize_to_max_side_len=(
|
| 61 |
+
False if flash else getattr(cfg, "resize_to_max_side_len", None)
|
| 62 |
+
),
|
| 63 |
+
)
|
| 64 |
+
resize_to_max, resize_min = apply_flash_preprocess(
|
| 65 |
+
is_flash=is_flash,
|
| 66 |
+
resize_to_max_side_len=None if flash else getattr(cfg, "resize_to_max_side_len", None),
|
| 67 |
+
resize_min_side_len=getattr(cfg, "resize_min_side_len", None),
|
| 68 |
+
)
|
| 69 |
+
cfg.resize_to_max_side_len = resize_to_max
|
| 70 |
+
cfg.resize_min_side_len = resize_min
|
| 71 |
+
if hasattr(cfg, "is_flash"):
|
| 72 |
+
try:
|
| 73 |
+
cfg.is_flash = is_flash
|
| 74 |
+
except AttributeError:
|
| 75 |
+
pass
|
| 76 |
+
return is_flash
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def get_tokenizer(name, extra_special_tokens=None, chat_template=None):
|
| 80 |
+
cache_key = (
|
| 81 |
+
name,
|
| 82 |
+
tuple(sorted((extra_special_tokens or {}).items())),
|
| 83 |
+
chat_template,
|
| 84 |
+
)
|
| 85 |
+
if cache_key not in TOKENIZERS_CACHE:
|
| 86 |
+
tokenizer_init_kwargs = {"use_fast": True}
|
| 87 |
+
if extra_special_tokens is not None:
|
| 88 |
+
tokenizer_init_kwargs["extra_special_tokens"] = extra_special_tokens
|
| 89 |
+
if chat_template is not None:
|
| 90 |
+
tokenizer_init_kwargs["chat_template"] = chat_template
|
| 91 |
+
tokenizer = AutoTokenizer.from_pretrained(name, **tokenizer_init_kwargs,)
|
| 92 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 93 |
+
TOKENIZERS_CACHE[cache_key] = tokenizer
|
| 94 |
+
return TOKENIZERS_CACHE[cache_key]
|
| 95 |
+
|
| 96 |
+
def get_image_processor(max_img_size, splitted_image_size, resize_to_max_side_len=False, min_side_len=None):
|
| 97 |
+
return transforms.Compose([
|
| 98 |
+
DynamicResize(splitted_image_size, max_img_size, resize_to_max_side_len, min_side_len),
|
| 99 |
+
transforms.ToTensor(),
|
| 100 |
+
GlobalAndSplitImages(splitted_image_size),
|
| 101 |
+
])
|
| 102 |
+
|
| 103 |
+
def get_image_string(tokenizer, splitted_image_counts, mp_image_token_length):
|
| 104 |
+
image_string = ""
|
| 105 |
+
|
| 106 |
+
for idx, (n_h, n_w) in enumerate(splitted_image_counts):
|
| 107 |
+
if len(splitted_image_counts) > 1:
|
| 108 |
+
image_string += f"<image: {idx}>"
|
| 109 |
+
if hasattr(tokenizer, "global_image_token"):
|
| 110 |
+
image_string += tokenizer.global_image_token
|
| 111 |
+
image_string += tokenizer.image_token * mp_image_token_length
|
| 112 |
+
if n_h == 1 and n_w == 1:
|
| 113 |
+
continue
|
| 114 |
+
for i in range(n_h):
|
| 115 |
+
for j in range(n_w):
|
| 116 |
+
image_string += getattr(tokenizer, f'r{i+1}c{j+1}')
|
| 117 |
+
image_string += tokenizer.image_token * mp_image_token_length
|
| 118 |
+
return image_string
|
runtime_profile.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Inference runtime profiles (eager vs torch.compile + CUDA graphs)."""
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def apply_eager_profile(model) -> None:
|
| 8 |
+
for cfg in (getattr(model, "cfg", None), getattr(model, "config", None)):
|
| 9 |
+
if cfg is not None and hasattr(cfg, "compile_inference"):
|
| 10 |
+
cfg.compile_inference = False
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def apply_deploy_profile(model, device: torch.device) -> None:
|
| 14 |
+
if device.type != "cuda":
|
| 15 |
+
apply_eager_profile(model)
|
| 16 |
+
return
|
| 17 |
+
for cfg in (getattr(model, "cfg", None), getattr(model, "config", None)):
|
| 18 |
+
if cfg is None:
|
| 19 |
+
continue
|
| 20 |
+
if hasattr(cfg, "compile_inference"):
|
| 21 |
+
cfg.compile_inference = True
|
| 22 |
+
if hasattr(cfg, "compile_inference_mode"):
|
| 23 |
+
cfg.compile_inference_mode = "reduce-overhead"
|
| 24 |
+
if hasattr(cfg, "cuda_graphs_cache_quantum"):
|
| 25 |
+
cfg.cuda_graphs_cache_quantum = 128
|
| 26 |
+
if hasattr(cfg, "eos_check_interval"):
|
| 27 |
+
cfg.eos_check_interval = 16
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def profile_label(model, device: torch.device) -> str:
|
| 31 |
+
cfg = getattr(model, "cfg", None) or getattr(model, "config", None)
|
| 32 |
+
if device.type != "cuda":
|
| 33 |
+
return "cpu-eager"
|
| 34 |
+
if cfg is not None and getattr(cfg, "compile_inference", False):
|
| 35 |
+
mode = getattr(cfg, "compile_inference_mode", "default")
|
| 36 |
+
return f"cuda-compile-{mode}"
|
| 37 |
+
return "cuda-eager"
|
tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_prefix_space": false,
|
| 3 |
+
"backend": "tokenizers",
|
| 4 |
+
"bos_token": "<|im_start|>",
|
| 5 |
+
"clean_up_tokenization_spaces": false,
|
| 6 |
+
"eos_token": "<|im_end|>",
|
| 7 |
+
"errors": "replace",
|
| 8 |
+
"global_image_token": "<|global_image|>",
|
| 9 |
+
"image_token": "<|image|>",
|
| 10 |
+
"is_local": false,
|
| 11 |
+
"local_files_only": false,
|
| 12 |
+
"model_max_length": 8192,
|
| 13 |
+
"model_specific_special_tokens": {
|
| 14 |
+
"global_image_token": "<|global_image|>",
|
| 15 |
+
"image_token": "<|image|>",
|
| 16 |
+
"r1c1": "<row_1_col_1>",
|
| 17 |
+
"r1c2": "<row_1_col_2>",
|
| 18 |
+
"r1c3": "<row_1_col_3>",
|
| 19 |
+
"r1c4": "<row_1_col_4>",
|
| 20 |
+
"r1c5": "<row_1_col_5>",
|
| 21 |
+
"r1c6": "<row_1_col_6>",
|
| 22 |
+
"r1c7": "<row_1_col_7>",
|
| 23 |
+
"r1c8": "<row_1_col_8>",
|
| 24 |
+
"r2c1": "<row_2_col_1>",
|
| 25 |
+
"r2c2": "<row_2_col_2>",
|
| 26 |
+
"r2c3": "<row_2_col_3>",
|
| 27 |
+
"r2c4": "<row_2_col_4>",
|
| 28 |
+
"r2c5": "<row_2_col_5>",
|
| 29 |
+
"r2c6": "<row_2_col_6>",
|
| 30 |
+
"r2c7": "<row_2_col_7>",
|
| 31 |
+
"r2c8": "<row_2_col_8>",
|
| 32 |
+
"r3c1": "<row_3_col_1>",
|
| 33 |
+
"r3c2": "<row_3_col_2>",
|
| 34 |
+
"r3c3": "<row_3_col_3>",
|
| 35 |
+
"r3c4": "<row_3_col_4>",
|
| 36 |
+
"r3c5": "<row_3_col_5>",
|
| 37 |
+
"r3c6": "<row_3_col_6>",
|
| 38 |
+
"r3c7": "<row_3_col_7>",
|
| 39 |
+
"r3c8": "<row_3_col_8>",
|
| 40 |
+
"r4c1": "<row_4_col_1>",
|
| 41 |
+
"r4c2": "<row_4_col_2>",
|
| 42 |
+
"r4c3": "<row_4_col_3>",
|
| 43 |
+
"r4c4": "<row_4_col_4>",
|
| 44 |
+
"r4c5": "<row_4_col_5>",
|
| 45 |
+
"r4c6": "<row_4_col_6>",
|
| 46 |
+
"r4c7": "<row_4_col_7>",
|
| 47 |
+
"r4c8": "<row_4_col_8>",
|
| 48 |
+
"r5c1": "<row_5_col_1>",
|
| 49 |
+
"r5c2": "<row_5_col_2>",
|
| 50 |
+
"r5c3": "<row_5_col_3>",
|
| 51 |
+
"r5c4": "<row_5_col_4>",
|
| 52 |
+
"r5c5": "<row_5_col_5>",
|
| 53 |
+
"r5c6": "<row_5_col_6>",
|
| 54 |
+
"r5c7": "<row_5_col_7>",
|
| 55 |
+
"r5c8": "<row_5_col_8>",
|
| 56 |
+
"r6c1": "<row_6_col_1>",
|
| 57 |
+
"r6c2": "<row_6_col_2>",
|
| 58 |
+
"r6c3": "<row_6_col_3>",
|
| 59 |
+
"r6c4": "<row_6_col_4>",
|
| 60 |
+
"r6c5": "<row_6_col_5>",
|
| 61 |
+
"r6c6": "<row_6_col_6>",
|
| 62 |
+
"r6c7": "<row_6_col_7>",
|
| 63 |
+
"r6c8": "<row_6_col_8>",
|
| 64 |
+
"r7c1": "<row_7_col_1>",
|
| 65 |
+
"r7c2": "<row_7_col_2>",
|
| 66 |
+
"r7c3": "<row_7_col_3>",
|
| 67 |
+
"r7c4": "<row_7_col_4>",
|
| 68 |
+
"r7c5": "<row_7_col_5>",
|
| 69 |
+
"r7c6": "<row_7_col_6>",
|
| 70 |
+
"r7c7": "<row_7_col_7>",
|
| 71 |
+
"r7c8": "<row_7_col_8>",
|
| 72 |
+
"r8c1": "<row_8_col_1>",
|
| 73 |
+
"r8c2": "<row_8_col_2>",
|
| 74 |
+
"r8c3": "<row_8_col_3>",
|
| 75 |
+
"r8c4": "<row_8_col_4>",
|
| 76 |
+
"r8c5": "<row_8_col_5>",
|
| 77 |
+
"r8c6": "<row_8_col_6>",
|
| 78 |
+
"r8c7": "<row_8_col_7>",
|
| 79 |
+
"r8c8": "<row_8_col_8>"
|
| 80 |
+
},
|
| 81 |
+
"pad_token": "<|im_end|>",
|
| 82 |
+
"processor_class": "VisionPsyNanoProcessor",
|
| 83 |
+
"r1c1": "<row_1_col_1>",
|
| 84 |
+
"r1c2": "<row_1_col_2>",
|
| 85 |
+
"r1c3": "<row_1_col_3>",
|
| 86 |
+
"r1c4": "<row_1_col_4>",
|
| 87 |
+
"r1c5": "<row_1_col_5>",
|
| 88 |
+
"r1c6": "<row_1_col_6>",
|
| 89 |
+
"r1c7": "<row_1_col_7>",
|
| 90 |
+
"r1c8": "<row_1_col_8>",
|
| 91 |
+
"r2c1": "<row_2_col_1>",
|
| 92 |
+
"r2c2": "<row_2_col_2>",
|
| 93 |
+
"r2c3": "<row_2_col_3>",
|
| 94 |
+
"r2c4": "<row_2_col_4>",
|
| 95 |
+
"r2c5": "<row_2_col_5>",
|
| 96 |
+
"r2c6": "<row_2_col_6>",
|
| 97 |
+
"r2c7": "<row_2_col_7>",
|
| 98 |
+
"r2c8": "<row_2_col_8>",
|
| 99 |
+
"r3c1": "<row_3_col_1>",
|
| 100 |
+
"r3c2": "<row_3_col_2>",
|
| 101 |
+
"r3c3": "<row_3_col_3>",
|
| 102 |
+
"r3c4": "<row_3_col_4>",
|
| 103 |
+
"r3c5": "<row_3_col_5>",
|
| 104 |
+
"r3c6": "<row_3_col_6>",
|
| 105 |
+
"r3c7": "<row_3_col_7>",
|
| 106 |
+
"r3c8": "<row_3_col_8>",
|
| 107 |
+
"r4c1": "<row_4_col_1>",
|
| 108 |
+
"r4c2": "<row_4_col_2>",
|
| 109 |
+
"r4c3": "<row_4_col_3>",
|
| 110 |
+
"r4c4": "<row_4_col_4>",
|
| 111 |
+
"r4c5": "<row_4_col_5>",
|
| 112 |
+
"r4c6": "<row_4_col_6>",
|
| 113 |
+
"r4c7": "<row_4_col_7>",
|
| 114 |
+
"r4c8": "<row_4_col_8>",
|
| 115 |
+
"r5c1": "<row_5_col_1>",
|
| 116 |
+
"r5c2": "<row_5_col_2>",
|
| 117 |
+
"r5c3": "<row_5_col_3>",
|
| 118 |
+
"r5c4": "<row_5_col_4>",
|
| 119 |
+
"r5c5": "<row_5_col_5>",
|
| 120 |
+
"r5c6": "<row_5_col_6>",
|
| 121 |
+
"r5c7": "<row_5_col_7>",
|
| 122 |
+
"r5c8": "<row_5_col_8>",
|
| 123 |
+
"r6c1": "<row_6_col_1>",
|
| 124 |
+
"r6c2": "<row_6_col_2>",
|
| 125 |
+
"r6c3": "<row_6_col_3>",
|
| 126 |
+
"r6c4": "<row_6_col_4>",
|
| 127 |
+
"r6c5": "<row_6_col_5>",
|
| 128 |
+
"r6c6": "<row_6_col_6>",
|
| 129 |
+
"r6c7": "<row_6_col_7>",
|
| 130 |
+
"r6c8": "<row_6_col_8>",
|
| 131 |
+
"r7c1": "<row_7_col_1>",
|
| 132 |
+
"r7c2": "<row_7_col_2>",
|
| 133 |
+
"r7c3": "<row_7_col_3>",
|
| 134 |
+
"r7c4": "<row_7_col_4>",
|
| 135 |
+
"r7c5": "<row_7_col_5>",
|
| 136 |
+
"r7c6": "<row_7_col_6>",
|
| 137 |
+
"r7c7": "<row_7_col_7>",
|
| 138 |
+
"r7c8": "<row_7_col_8>",
|
| 139 |
+
"r8c1": "<row_8_col_1>",
|
| 140 |
+
"r8c2": "<row_8_col_2>",
|
| 141 |
+
"r8c3": "<row_8_col_3>",
|
| 142 |
+
"r8c4": "<row_8_col_4>",
|
| 143 |
+
"r8c5": "<row_8_col_5>",
|
| 144 |
+
"r8c6": "<row_8_col_6>",
|
| 145 |
+
"r8c7": "<row_8_col_7>",
|
| 146 |
+
"r8c8": "<row_8_col_8>",
|
| 147 |
+
"tokenizer_class": "GPT2Tokenizer",
|
| 148 |
+
"unk_token": "<|endoftext|>",
|
| 149 |
+
"vocab_size": 49152
|
| 150 |
+
}
|
vision_language_model.py
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
import tempfile
|
| 4 |
+
from dataclasses import asdict
|
| 5 |
+
from typing import Optional
|
| 6 |
+
|
| 7 |
+
from .model_utils import top_k_top_p_filtering
|
| 8 |
+
from .vision_transformer import ViT
|
| 9 |
+
from .language_model import LanguageModel
|
| 10 |
+
from .modality_projector import ModalityProjector
|
| 11 |
+
from .vlm_config import VLMConfig
|
| 12 |
+
|
| 13 |
+
from .processors import get_tokenizer
|
| 14 |
+
|
| 15 |
+
import torch
|
| 16 |
+
import torch.nn as nn
|
| 17 |
+
import torch.nn.functional as F
|
| 18 |
+
from safetensors.torch import load_model, save_model
|
| 19 |
+
|
| 20 |
+
def _drop_if_all_attend(attention_mask: Optional[torch.Tensor]) -> Optional[torch.Tensor]:
|
| 21 |
+
"""Drop all-ones masks (avoid decode length mismatch with a fixed prefill mask)."""
|
| 22 |
+
if attention_mask is None:
|
| 23 |
+
return None
|
| 24 |
+
if bool(attention_mask.all()):
|
| 25 |
+
return None
|
| 26 |
+
return attention_mask
|
| 27 |
+
|
| 28 |
+
class VisionLanguageModel(nn.Module):
|
| 29 |
+
def __init__(self, cfg: VLMConfig, load_backbone=True):
|
| 30 |
+
super().__init__()
|
| 31 |
+
self.cfg = cfg
|
| 32 |
+
if load_backbone:
|
| 33 |
+
print("Loading from backbone weights")
|
| 34 |
+
self.vision_encoder = ViT.from_pretrained(cfg)
|
| 35 |
+
self.decoder = LanguageModel.from_pretrained(cfg)
|
| 36 |
+
else:
|
| 37 |
+
self.vision_encoder = ViT(cfg)
|
| 38 |
+
self.decoder = LanguageModel(cfg)
|
| 39 |
+
self.MP = ModalityProjector(cfg)
|
| 40 |
+
self.load_backbone = load_backbone
|
| 41 |
+
self.tokenizer = get_tokenizer(cfg.lm_tokenizer, cfg.vlm_extra_tokens, cfg.lm_chat_template)
|
| 42 |
+
|
| 43 |
+
self._compiled_decoder = None
|
| 44 |
+
self._compiled_vision_encoder = None
|
| 45 |
+
|
| 46 |
+
def _vision_encoder_for_prefill(self):
|
| 47 |
+
if not getattr(self.cfg, "compile_inference", False):
|
| 48 |
+
return self.vision_encoder
|
| 49 |
+
if self._compiled_vision_encoder is None:
|
| 50 |
+
self._compiled_vision_encoder = torch.compile(
|
| 51 |
+
self.vision_encoder,
|
| 52 |
+
mode=getattr(self.cfg, "compile_inference_mode", "default"),
|
| 53 |
+
dynamic=True,
|
| 54 |
+
fullgraph=False,
|
| 55 |
+
)
|
| 56 |
+
return self._compiled_vision_encoder
|
| 57 |
+
|
| 58 |
+
def _decoder_for_decode(self):
|
| 59 |
+
|
| 60 |
+
if not getattr(self.cfg, "compile_inference", False):
|
| 61 |
+
return self.decoder
|
| 62 |
+
if self._compiled_decoder is None:
|
| 63 |
+
mode = getattr(self.cfg, "compile_inference_mode", "default")
|
| 64 |
+
self._compiled_decoder = torch.compile(
|
| 65 |
+
self.decoder,
|
| 66 |
+
mode=mode,
|
| 67 |
+
dynamic=(False if mode == "reduce-overhead" else True),
|
| 68 |
+
fullgraph=False,
|
| 69 |
+
)
|
| 70 |
+
return self._compiled_decoder
|
| 71 |
+
|
| 72 |
+
def _replace_img_tokens_with_embd(self, input_ids, token_embd, image_embd):
|
| 73 |
+
image_token_id = getattr(self.tokenizer, "image_token_id", None)
|
| 74 |
+
if image_token_id is None and hasattr(self.tokenizer, "image_token"):
|
| 75 |
+
image_token_id = self.tokenizer.convert_tokens_to_ids(self.tokenizer.image_token)
|
| 76 |
+
if image_token_id is None:
|
| 77 |
+
image_token_id = self.tokenizer.convert_tokens_to_ids("<|image|>")
|
| 78 |
+
mask = (input_ids == image_token_id).unsqueeze(-1)
|
| 79 |
+
flat = image_embd.view(-1, image_embd.size(-1)).to(token_embd.dtype)
|
| 80 |
+
token_embd.masked_scatter_(mask, flat)
|
| 81 |
+
return token_embd
|
| 82 |
+
|
| 83 |
+
def _process_images(self, images, device):
|
| 84 |
+
if isinstance(images, list):
|
| 85 |
+
if images and isinstance(images[0], list):
|
| 86 |
+
images = [img for sublist in images for img in sublist]
|
| 87 |
+
|
| 88 |
+
if not images:
|
| 89 |
+
return None
|
| 90 |
+
else:
|
| 91 |
+
return torch.cat(images, dim=0).to(device)
|
| 92 |
+
return images
|
| 93 |
+
|
| 94 |
+
def forward(self, input_ids, images, attention_mask=None, targets=None):
|
| 95 |
+
images_tensor = self._process_images(images, input_ids.device)
|
| 96 |
+
token_embd = self.decoder.token_embedding(input_ids)
|
| 97 |
+
|
| 98 |
+
if images_tensor is not None:
|
| 99 |
+
image_embd = self.vision_encoder(images_tensor)
|
| 100 |
+
image_embd = self.MP(image_embd)
|
| 101 |
+
token_embd = self._replace_img_tokens_with_embd(input_ids, token_embd, image_embd)
|
| 102 |
+
|
| 103 |
+
attention_mask = _drop_if_all_attend(attention_mask)
|
| 104 |
+
|
| 105 |
+
logits, _ = self.decoder(token_embd, attention_mask=attention_mask)
|
| 106 |
+
|
| 107 |
+
loss = None
|
| 108 |
+
if targets is not None:
|
| 109 |
+
logits = self.decoder.head(logits)
|
| 110 |
+
loss = F.cross_entropy(logits.reshape(-1, logits.size(-1)), targets.reshape(-1), ignore_index=-100)
|
| 111 |
+
|
| 112 |
+
return logits, loss
|
| 113 |
+
|
| 114 |
+
@torch.inference_mode()
|
| 115 |
+
def generate(self, input_ids, images, attention_mask=None, max_new_tokens=5, top_k=50, top_p=0.9, temperature=0.5, greedy=False):
|
| 116 |
+
images_tensor = self._process_images(images, input_ids.device)
|
| 117 |
+
token_embd = self.decoder.token_embedding(input_ids)
|
| 118 |
+
|
| 119 |
+
if images_tensor is not None:
|
| 120 |
+
vision_encoder_fn = self._vision_encoder_for_prefill()
|
| 121 |
+
image_embd = vision_encoder_fn(images_tensor)
|
| 122 |
+
image_embd = self.MP(image_embd)
|
| 123 |
+
token_embd = self._replace_img_tokens_with_embd(input_ids, token_embd, image_embd)
|
| 124 |
+
|
| 125 |
+
current_total_seq_len = token_embd.size(1)
|
| 126 |
+
batch_size = input_ids.size(0)
|
| 127 |
+
device = input_ids.device
|
| 128 |
+
|
| 129 |
+
cache_max_length = current_total_seq_len + max(int(max_new_tokens), 1)
|
| 130 |
+
head_dim = self.cfg.lm_hidden_dim // self.cfg.lm_n_heads
|
| 131 |
+
use_cuda_graphs_pre = (
|
| 132 |
+
getattr(self.cfg, "compile_inference", False)
|
| 133 |
+
and getattr(self.cfg, "compile_inference_mode", "default") == "reduce-overhead"
|
| 134 |
+
and device.type == "cuda"
|
| 135 |
+
)
|
| 136 |
+
if use_cuda_graphs_pre:
|
| 137 |
+
quantum = int(getattr(self.cfg, "cuda_graphs_cache_quantum", 128))
|
| 138 |
+
if quantum > 1:
|
| 139 |
+
cache_max_length = ((cache_max_length + quantum - 1) // quantum) * quantum
|
| 140 |
+
cache_shape = (batch_size, self.cfg.lm_n_kv_heads, cache_max_length, head_dim)
|
| 141 |
+
cache_dtype = next(self.decoder.parameters()).dtype
|
| 142 |
+
use_cuda_graphs = use_cuda_graphs_pre
|
| 143 |
+
kv_cache_list = []
|
| 144 |
+
for _ in range(self.cfg.lm_n_blocks):
|
| 145 |
+
key_cache = torch.zeros(cache_shape, dtype=cache_dtype, device=device)
|
| 146 |
+
value_cache = torch.zeros(cache_shape, dtype=cache_dtype, device=device)
|
| 147 |
+
if use_cuda_graphs:
|
| 148 |
+
torch._dynamo.mark_static_address(key_cache)
|
| 149 |
+
torch._dynamo.mark_static_address(value_cache)
|
| 150 |
+
kv_cache_list.append({
|
| 151 |
+
"cache_max_length": cache_max_length,
|
| 152 |
+
"key_cache": key_cache,
|
| 153 |
+
"value_cache": value_cache,
|
| 154 |
+
})
|
| 155 |
+
|
| 156 |
+
attention_mask = _drop_if_all_attend(attention_mask)
|
| 157 |
+
|
| 158 |
+
prefill_output, kv_cache_list = self.decoder(
|
| 159 |
+
token_embd,
|
| 160 |
+
attention_mask=attention_mask,
|
| 161 |
+
kv_cache=kv_cache_list,
|
| 162 |
+
start_pos=0,
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
last_token_output_from_prefill = prefill_output[:, -1, :]
|
| 166 |
+
|
| 167 |
+
if not self.decoder.lm_use_tokens:
|
| 168 |
+
current_logits = self.decoder.head(last_token_output_from_prefill)
|
| 169 |
+
else:
|
| 170 |
+
current_logits = last_token_output_from_prefill
|
| 171 |
+
|
| 172 |
+
newly_generated_ids_list = []
|
| 173 |
+
|
| 174 |
+
decode_decoder = self._decoder_for_decode()
|
| 175 |
+
|
| 176 |
+
if use_cuda_graphs:
|
| 177 |
+
start_pos_buf = torch.zeros((), dtype=torch.long, device=device)
|
| 178 |
+
torch._dynamo.mark_static_address(start_pos_buf)
|
| 179 |
+
else:
|
| 180 |
+
start_pos_buf = None
|
| 181 |
+
|
| 182 |
+
eos_id = self.tokenizer.eos_token_id
|
| 183 |
+
eos_check_interval = int(getattr(self.cfg, "eos_check_interval", 16))
|
| 184 |
+
if eos_id is None or eos_check_interval <= 0:
|
| 185 |
+
eos_check_interval = 0
|
| 186 |
+
|
| 187 |
+
for step_idx in range(max_new_tokens):
|
| 188 |
+
if greedy:
|
| 189 |
+
next_token_id = torch.argmax(current_logits, dim=-1, keepdim=True)
|
| 190 |
+
else:
|
| 191 |
+
filtered_logits = top_k_top_p_filtering(current_logits, top_k=top_k, top_p=top_p)
|
| 192 |
+
probs = torch.softmax(filtered_logits / temperature, dim=-1)
|
| 193 |
+
next_token_id = torch.multinomial(probs, num_samples=1)
|
| 194 |
+
|
| 195 |
+
newly_generated_ids_list.append(next_token_id)
|
| 196 |
+
|
| 197 |
+
if (
|
| 198 |
+
eos_check_interval > 0
|
| 199 |
+
and (step_idx + 1) % eos_check_interval == 0
|
| 200 |
+
and bool((next_token_id == eos_id).all().item())
|
| 201 |
+
):
|
| 202 |
+
break
|
| 203 |
+
|
| 204 |
+
next_token_embed = self.decoder.token_embedding(next_token_id)
|
| 205 |
+
|
| 206 |
+
current_token_start_pos = current_total_seq_len
|
| 207 |
+
current_total_seq_len += 1
|
| 208 |
+
|
| 209 |
+
if start_pos_buf is not None:
|
| 210 |
+
start_pos_buf.fill_(current_token_start_pos)
|
| 211 |
+
start_pos_to_pass = start_pos_buf
|
| 212 |
+
else:
|
| 213 |
+
start_pos_to_pass = current_token_start_pos
|
| 214 |
+
|
| 215 |
+
decode_step_output, kv_cache_list = decode_decoder(
|
| 216 |
+
next_token_embed,
|
| 217 |
+
attention_mask=attention_mask,
|
| 218 |
+
kv_cache=kv_cache_list,
|
| 219 |
+
start_pos=start_pos_to_pass,
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
last_token_output = decode_step_output[:, -1, :]
|
| 223 |
+
|
| 224 |
+
if not self.decoder.lm_use_tokens:
|
| 225 |
+
current_logits = self.decoder.head(last_token_output)
|
| 226 |
+
else:
|
| 227 |
+
current_logits = last_token_output
|
| 228 |
+
if not newly_generated_ids_list:
|
| 229 |
+
return torch.empty((batch_size,0), dtype=torch.long, device=input_ids.device)
|
| 230 |
+
|
| 231 |
+
generated_ids = torch.cat(newly_generated_ids_list, dim=1)
|
| 232 |
+
|
| 233 |
+
if self.tokenizer.eos_token_id is not None and generated_ids.numel() > 0:
|
| 234 |
+
seq_len = generated_ids.size(1)
|
| 235 |
+
device = generated_ids.device
|
| 236 |
+
|
| 237 |
+
eos_mask = (generated_ids == self.tokenizer.eos_token_id)
|
| 238 |
+
|
| 239 |
+
col_indices_for_min = torch.arange(seq_len, device=device)
|
| 240 |
+
masked_col_indices = torch.where(eos_mask, col_indices_for_min.unsqueeze(0).expand_as(generated_ids), seq_len + 1)
|
| 241 |
+
|
| 242 |
+
first_eos_indices_values = torch.min(masked_col_indices, dim=1).values
|
| 243 |
+
actual_first_eos_indices = torch.clamp(first_eos_indices_values, max=seq_len)
|
| 244 |
+
|
| 245 |
+
col_indices_for_comparison = torch.arange(seq_len, device=device).unsqueeze(0).expand_as(generated_ids)
|
| 246 |
+
replace_mask = col_indices_for_comparison > actual_first_eos_indices.unsqueeze(1)
|
| 247 |
+
generated_ids[replace_mask] = self.tokenizer.eos_token_id
|
| 248 |
+
return generated_ids
|
| 249 |
+
|
| 250 |
+
@classmethod
|
| 251 |
+
def from_pretrained(
|
| 252 |
+
cls, repo_id_or_path: str, *, revision: Optional[str] = None
|
| 253 |
+
) -> "VisionLanguageModel":
|
| 254 |
+
|
| 255 |
+
if os.path.exists(repo_id_or_path):
|
| 256 |
+
config_path = os.path.join(repo_id_or_path, "config.json")
|
| 257 |
+
weights_path = os.path.join(repo_id_or_path, "model.safetensors")
|
| 258 |
+
|
| 259 |
+
if not os.path.exists(config_path):
|
| 260 |
+
raise ValueError(
|
| 261 |
+
f"Config file not found at {config_path}. Please provide a valid path."
|
| 262 |
+
)
|
| 263 |
+
if not os.path.exists(weights_path):
|
| 264 |
+
raise ValueError(
|
| 265 |
+
f"Weights file not found at {weights_path}. Please provide a valid path."
|
| 266 |
+
)
|
| 267 |
+
else:
|
| 268 |
+
from huggingface_hub import hf_hub_download
|
| 269 |
+
|
| 270 |
+
config_path = hf_hub_download(
|
| 271 |
+
repo_id=repo_id_or_path, filename="config.json", revision=revision
|
| 272 |
+
)
|
| 273 |
+
weights_path = hf_hub_download(
|
| 274 |
+
repo_id=repo_id_or_path, filename="model.safetensors", revision=revision
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
with open(config_path, "r") as f:
|
| 278 |
+
raw_cfg = json.load(f)
|
| 279 |
+
from dataclasses import fields as dc_fields
|
| 280 |
+
valid_keys = {fld.name for fld in dc_fields(VLMConfig)}
|
| 281 |
+
cfg = VLMConfig(**{k: v for k, v in raw_cfg.items() if k in valid_keys})
|
| 282 |
+
|
| 283 |
+
model = cls(cfg, load_backbone=False)
|
| 284 |
+
model._saved_config_keys = set(raw_cfg.keys())
|
| 285 |
+
|
| 286 |
+
load_model(model, weights_path)
|
| 287 |
+
|
| 288 |
+
return model
|
| 289 |
+
|
| 290 |
+
def save_pretrained(self, save_directory: str) -> None:
|
| 291 |
+
|
| 292 |
+
os.makedirs(save_directory, exist_ok=True)
|
| 293 |
+
|
| 294 |
+
with open(os.path.join(save_directory, "config.json"), "w") as f:
|
| 295 |
+
f.write(json.dumps(asdict(self.cfg), indent=4))
|
| 296 |
+
|
| 297 |
+
save_model(self, os.path.join(save_directory, "model.safetensors"))
|
| 298 |
+
|
| 299 |
+
def push_to_hub(self, repo_id: str, private: bool = False) -> None:
|
| 300 |
+
|
| 301 |
+
from huggingface_hub import create_repo, upload_folder
|
| 302 |
+
|
| 303 |
+
repo_url = create_repo(repo_id=repo_id, private=private, exist_ok=True)
|
| 304 |
+
repo_id = repo_url.repo_id
|
| 305 |
+
print("Created repo: ", repo_url)
|
| 306 |
+
|
| 307 |
+
with tempfile.TemporaryDirectory() as save_path:
|
| 308 |
+
self.save_pretrained(save_path)
|
| 309 |
+
|
| 310 |
+
with open(os.path.join(save_path, "README.md"), "w") as f:
|
| 311 |
+
f.write(MODEL_CARD_TEMPLATE.format(repo_id=repo_id))
|
| 312 |
+
|
| 313 |
+
return upload_folder(
|
| 314 |
+
repo_id=repo_id,
|
| 315 |
+
repo_type="model",
|
| 316 |
+
folder_path=save_path,
|
| 317 |
+
commit_message="Upload VisionPsyNano using push_to_hub",
|
| 318 |
+
)
|
| 319 |
+
|
| 320 |
+
MODEL_CARD_TEMPLATE = """
|
| 321 |
+
---
|
| 322 |
+
library_name: transformers
|
| 323 |
+
pipeline_tag: image-text-to-text
|
| 324 |
+
tags:
|
| 325 |
+
- vision-language
|
| 326 |
+
- multimodal
|
| 327 |
+
---
|
| 328 |
+
|
| 329 |
+
```python
|
| 330 |
+
from transformers import AutoModelForImageTextToText, AutoProcessor
|
| 331 |
+
from PIL import Image
|
| 332 |
+
|
| 333 |
+
repo = "{repo_id}"
|
| 334 |
+
model = AutoModelForImageTextToText.from_pretrained(
|
| 335 |
+
repo, trust_remote_code=True, dtype="auto"
|
| 336 |
+
).cuda().eval()
|
| 337 |
+
model.apply_deploy_profile(model.device)
|
| 338 |
+
processor = AutoProcessor.from_pretrained(repo, trust_remote_code=True)
|
| 339 |
+
|
| 340 |
+
inputs = processor(images=Image.open("image.jpg"), text="Describe the image.", return_tensors="pt")
|
| 341 |
+
inputs = {{k: v.cuda() if hasattr(v, "cuda") else v for k, v in inputs.items() if v is not None}}
|
| 342 |
+
inputs.pop("pixel_values", None)
|
| 343 |
+
out = model.generate(**inputs, max_new_tokens=64, greedy=True)
|
| 344 |
+
print(processor.batch_decode(out, skip_special_tokens=True)[0])
|
| 345 |
+
```
|
| 346 |
+
"""
|
vision_transformer.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
|
| 6 |
+
class ViTPatchEmbeddings(nn.Module):
|
| 7 |
+
def __init__(self, cfg):
|
| 8 |
+
super().__init__()
|
| 9 |
+
|
| 10 |
+
self.img_size = cfg.vit_img_size
|
| 11 |
+
self.patch_size = cfg.vit_patch_size
|
| 12 |
+
self.num_patches = (self.img_size // self.patch_size) ** 2
|
| 13 |
+
self.cls_flag = cfg.vit_cls_flag
|
| 14 |
+
self.embd_dim = cfg.vit_hidden_dim
|
| 15 |
+
|
| 16 |
+
self.conv = nn.Conv2d(
|
| 17 |
+
in_channels=3,
|
| 18 |
+
out_channels=self.embd_dim,
|
| 19 |
+
kernel_size=self.patch_size,
|
| 20 |
+
stride=self.patch_size,
|
| 21 |
+
padding="valid",
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
if self.cls_flag:
|
| 25 |
+
self.cls_token = nn.Parameter(torch.zeros(1, 1, self.embd_dim))
|
| 26 |
+
self.position_embedding = nn.Parameter(torch.rand(1, self.num_patches + 1, self.embd_dim))
|
| 27 |
+
else:
|
| 28 |
+
self.position_embedding = nn.Parameter(torch.rand(1, self.num_patches, self.embd_dim))
|
| 29 |
+
|
| 30 |
+
def forward(self, x):
|
| 31 |
+
x = self.conv(x)
|
| 32 |
+
x = x.flatten(2)
|
| 33 |
+
x = x.transpose(1, 2)
|
| 34 |
+
|
| 35 |
+
if self.cls_flag:
|
| 36 |
+
cls_token = self.cls_token.expand(x.shape[0], -1, -1)
|
| 37 |
+
x = torch.cat((cls_token, x), dim=1)
|
| 38 |
+
x = x + self.position_embedding
|
| 39 |
+
return x
|
| 40 |
+
|
| 41 |
+
class ViTMultiHeadAttention(nn.Module):
|
| 42 |
+
def __init__(self, cfg):
|
| 43 |
+
super().__init__()
|
| 44 |
+
|
| 45 |
+
self.n_heads = cfg.vit_n_heads
|
| 46 |
+
self.embd_dim = cfg.vit_hidden_dim
|
| 47 |
+
assert self.embd_dim % self.n_heads == 0, "embd_dim must be divisible by num_heads"
|
| 48 |
+
self.head_dim = self.embd_dim // self.n_heads
|
| 49 |
+
self.dropout = cfg.vit_dropout
|
| 50 |
+
|
| 51 |
+
self.qkv_proj = nn.Linear(self.embd_dim, 3 * self.embd_dim, bias=True)
|
| 52 |
+
self.out_proj = nn.Linear(self.embd_dim, self.embd_dim, bias=True)
|
| 53 |
+
|
| 54 |
+
self.attn_dropout = nn.Dropout(self.dropout)
|
| 55 |
+
self.resid_dropout = nn.Dropout(self.dropout)
|
| 56 |
+
|
| 57 |
+
self.sdpa = hasattr(torch.nn.functional, 'scaled_dot_product_attention')
|
| 58 |
+
if not self.sdpa:
|
| 59 |
+
print("Warning: scaled dot product attention not available. Using standard attention in ViT.")
|
| 60 |
+
|
| 61 |
+
def forward(self, x):
|
| 62 |
+
B, T, C = x.size()
|
| 63 |
+
|
| 64 |
+
qkv = self.qkv_proj(x)
|
| 65 |
+
q, k, v = qkv.split(C, dim=2)
|
| 66 |
+
q = q.view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
|
| 67 |
+
k = k.view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
|
| 68 |
+
v = v.view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
|
| 69 |
+
|
| 70 |
+
if self.sdpa:
|
| 71 |
+
y = torch.nn.functional.scaled_dot_product_attention(
|
| 72 |
+
q, k, v,
|
| 73 |
+
attn_mask=None,
|
| 74 |
+
dropout_p=self.dropout if self.training else 0.0,
|
| 75 |
+
is_causal=False
|
| 76 |
+
)
|
| 77 |
+
else:
|
| 78 |
+
attn = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1)))
|
| 79 |
+
attn = F.softmax(attn, dim=-1)
|
| 80 |
+
attn = self.attn_dropout(attn)
|
| 81 |
+
y = attn @ v
|
| 82 |
+
y = y.transpose(1, 2).contiguous().view(B, T, C)
|
| 83 |
+
y = self.out_proj(y)
|
| 84 |
+
y = self.resid_dropout(y)
|
| 85 |
+
|
| 86 |
+
return y
|
| 87 |
+
|
| 88 |
+
class ViTMLP(nn.Module):
|
| 89 |
+
def __init__(self, cfg):
|
| 90 |
+
super().__init__()
|
| 91 |
+
self.activation_fn = nn.GELU(approximate='tanh')
|
| 92 |
+
self.fc1 = nn.Linear(cfg.vit_hidden_dim, cfg.vit_inter_dim)
|
| 93 |
+
self.fc2 = nn.Linear(cfg.vit_inter_dim, cfg.vit_hidden_dim)
|
| 94 |
+
self.dropout = nn.Dropout(cfg.vit_dropout)
|
| 95 |
+
|
| 96 |
+
def forward(self, x):
|
| 97 |
+
x = self.fc1(x)
|
| 98 |
+
x = self.activation_fn(x)
|
| 99 |
+
x = self.fc2(x)
|
| 100 |
+
x = self.dropout(x)
|
| 101 |
+
return x
|
| 102 |
+
|
| 103 |
+
class ViTBlock(nn.Module):
|
| 104 |
+
def __init__(self, cfg):
|
| 105 |
+
super().__init__()
|
| 106 |
+
self.ln1 = nn.LayerNorm(cfg.vit_hidden_dim, eps=cfg.vit_ln_eps)
|
| 107 |
+
self.attn = ViTMultiHeadAttention(cfg)
|
| 108 |
+
self.ln2 = nn.LayerNorm(cfg.vit_hidden_dim, eps=cfg.vit_ln_eps)
|
| 109 |
+
self.mlp = ViTMLP(cfg)
|
| 110 |
+
def forward(self, x):
|
| 111 |
+
x = x + self.attn(self.ln1(x))
|
| 112 |
+
x = x + self.mlp(self.ln2(x))
|
| 113 |
+
return x
|
| 114 |
+
|
| 115 |
+
class ViT(nn.Module):
|
| 116 |
+
def __init__(self, cfg):
|
| 117 |
+
super().__init__()
|
| 118 |
+
self.cfg = cfg
|
| 119 |
+
self.patch_embedding = ViTPatchEmbeddings(cfg)
|
| 120 |
+
self.cls_flag = cfg.vit_cls_flag
|
| 121 |
+
self.dropout = nn.Dropout(cfg.vit_dropout)
|
| 122 |
+
self.blocks = nn.ModuleList([ViTBlock(cfg) for _ in range(cfg.vit_n_blocks)])
|
| 123 |
+
self.layer_norm = nn.LayerNorm(cfg.vit_hidden_dim, eps=cfg.vit_ln_eps)
|
| 124 |
+
|
| 125 |
+
self.apply(self._init_weights)
|
| 126 |
+
|
| 127 |
+
def _init_weights(self, module):
|
| 128 |
+
if isinstance(module, nn.Linear):
|
| 129 |
+
torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
|
| 130 |
+
if module.bias is not None:
|
| 131 |
+
torch.nn.init.zeros_(module.bias)
|
| 132 |
+
elif isinstance(module, nn.LayerNorm):
|
| 133 |
+
module.bias.data.zero_()
|
| 134 |
+
module.weight.data.fill_(1.0)
|
| 135 |
+
elif isinstance(module, nn.Conv2d):
|
| 136 |
+
torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
|
| 137 |
+
if module.bias is not None:
|
| 138 |
+
torch.nn.init.zeros_(module.bias)
|
| 139 |
+
|
| 140 |
+
def forward(self, x):
|
| 141 |
+
x = self.patch_embedding(x)
|
| 142 |
+
x = self.dropout(x)
|
| 143 |
+
for block in self.blocks:
|
| 144 |
+
x = block(x)
|
| 145 |
+
|
| 146 |
+
if self.cls_flag:
|
| 147 |
+
x = self.layer_norm(x[:, 0])
|
| 148 |
+
else:
|
| 149 |
+
x = self.layer_norm(x)
|
| 150 |
+
return x
|
| 151 |
+
@classmethod
|
| 152 |
+
def from_pretrained(cls, cfg):
|
| 153 |
+
from transformers import SiglipVisionConfig
|
| 154 |
+
from huggingface_hub import hf_hub_download
|
| 155 |
+
import safetensors
|
| 156 |
+
|
| 157 |
+
hf_config = SiglipVisionConfig.from_pretrained(cfg.vit_model_type)
|
| 158 |
+
cfg.vit_dropout=hf_config.attention_dropout
|
| 159 |
+
cfg.vit_hidden_dim=hf_config.hidden_size
|
| 160 |
+
cfg.vit_img_size=hf_config.image_size
|
| 161 |
+
cfg.vit_inter_dim=hf_config.intermediate_size
|
| 162 |
+
cfg.vit_ln_eps=hf_config.layer_norm_eps
|
| 163 |
+
cfg.vit_n_heads=hf_config.num_attention_heads
|
| 164 |
+
cfg.vit_n_blocks=hf_config.num_hidden_layers
|
| 165 |
+
cfg.vit_patch_size=hf_config.patch_size
|
| 166 |
+
model = cls(cfg)
|
| 167 |
+
safetensors_file = hf_hub_download(repo_id=cfg.vit_model_type, filename="model.safetensors")
|
| 168 |
+
|
| 169 |
+
sd = model.state_dict()
|
| 170 |
+
mapping = {
|
| 171 |
+
'vision_model.embeddings.patch_embedding.weight': 'patch_embedding.conv.weight',
|
| 172 |
+
'vision_model.embeddings.patch_embedding.bias': 'patch_embedding.conv.bias',
|
| 173 |
+
'vision_model.embeddings.position_embedding.weight': 'patch_embedding.position_embedding',
|
| 174 |
+
'vision_model.post_layernorm.weight': 'layer_norm.weight',
|
| 175 |
+
'vision_model.post_layernorm.bias': 'layer_norm.bias',
|
| 176 |
+
}
|
| 177 |
+
for i in range(cfg.vit_n_blocks):
|
| 178 |
+
mapping[f'vision_model.encoder.layers.{i}.layer_norm1.weight'] = f'blocks.{i}.ln1.weight'
|
| 179 |
+
mapping[f'vision_model.encoder.layers.{i}.layer_norm1.bias'] = f'blocks.{i}.ln1.bias'
|
| 180 |
+
mapping[f'vision_model.encoder.layers.{i}.layer_norm2.weight'] = f'blocks.{i}.ln2.weight'
|
| 181 |
+
mapping[f'vision_model.encoder.layers.{i}.layer_norm2.bias'] = f'blocks.{i}.ln2.bias'
|
| 182 |
+
mapping[f'vision_model.encoder.layers.{i}.mlp.fc1.weight'] = f'blocks.{i}.mlp.fc1.weight'
|
| 183 |
+
mapping[f'vision_model.encoder.layers.{i}.mlp.fc1.bias'] = f'blocks.{i}.mlp.fc1.bias'
|
| 184 |
+
mapping[f'vision_model.encoder.layers.{i}.mlp.fc2.weight'] = f'blocks.{i}.mlp.fc2.weight'
|
| 185 |
+
mapping[f'vision_model.encoder.layers.{i}.mlp.fc2.bias'] = f'blocks.{i}.mlp.fc2.bias'
|
| 186 |
+
mapping[f'vision_model.encoder.layers.{i}.self_attn.out_proj.weight'] = f'blocks.{i}.attn.out_proj.weight'
|
| 187 |
+
mapping[f'vision_model.encoder.layers.{i}.self_attn.out_proj.bias'] = f'blocks.{i}.attn.out_proj.bias'
|
| 188 |
+
with safetensors.safe_open(filename=safetensors_file, framework="pt", device="cpu") as f:
|
| 189 |
+
for hf_key, our_key in mapping.items():
|
| 190 |
+
if hf_key in f.keys() and our_key in sd:
|
| 191 |
+
tensor = f.get_tensor(hf_key)
|
| 192 |
+
if tensor.shape == sd[our_key].shape:
|
| 193 |
+
sd[our_key].copy_(tensor)
|
| 194 |
+
else:
|
| 195 |
+
if 'position_embedding' in hf_key:
|
| 196 |
+
sd[our_key].copy_(tensor.unsqueeze(0))
|
| 197 |
+
else:
|
| 198 |
+
print(f"Shape mismatch for {hf_key} -> {our_key}: {tensor.shape} vs {sd[our_key].shape}")
|
| 199 |
+
else:
|
| 200 |
+
if hf_key not in f.keys():
|
| 201 |
+
print(f"Warning: Key {hf_key} not found in safetensors file")
|
| 202 |
+
if our_key not in sd:
|
| 203 |
+
print(f"Warning: Key {our_key} not found in model state dict")
|
| 204 |
+
for i in range(model.cfg.vit_n_blocks):
|
| 205 |
+
q_weight = f.get_tensor(f'vision_model.encoder.layers.{i}.self_attn.q_proj.weight')
|
| 206 |
+
k_weight = f.get_tensor(f'vision_model.encoder.layers.{i}.self_attn.k_proj.weight')
|
| 207 |
+
v_weight = f.get_tensor(f'vision_model.encoder.layers.{i}.self_attn.v_proj.weight')
|
| 208 |
+
qkv_weight = torch.cat((q_weight, k_weight, v_weight), dim=0)
|
| 209 |
+
sd[f'blocks.{i}.attn.qkv_proj.weight'].copy_(qkv_weight)
|
| 210 |
+
q_bias = f.get_tensor(f'vision_model.encoder.layers.{i}.self_attn.q_proj.bias')
|
| 211 |
+
k_bias = f.get_tensor(f'vision_model.encoder.layers.{i}.self_attn.k_proj.bias')
|
| 212 |
+
v_bias = f.get_tensor(f'vision_model.encoder.layers.{i}.self_attn.v_proj.bias')
|
| 213 |
+
qkv_bias = torch.cat((q_bias, k_bias, v_bias), dim=0)
|
| 214 |
+
sd[f'blocks.{i}.attn.qkv_proj.bias'].copy_(qkv_bias)
|
| 215 |
+
model.load_state_dict(sd)
|
| 216 |
+
print(f"Successfully loaded {cfg.vit_model_type} weights from safetensors. Model has {sum(p.numel() for p in model.parameters()):,} parameters.")
|
| 217 |
+
return model
|
vlm_config.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass, field
|
| 2 |
+
|
| 3 |
+
@dataclass
|
| 4 |
+
class VLMConfig:
|
| 5 |
+
vit_hidden_dim: int = 768
|
| 6 |
+
vit_inter_dim: int = 4 * vit_hidden_dim
|
| 7 |
+
vit_patch_size: int = 16
|
| 8 |
+
vit_img_size: int = 512
|
| 9 |
+
vit_n_heads: int = 12
|
| 10 |
+
vit_dropout: float = 0.0
|
| 11 |
+
vit_n_blocks: int = 12
|
| 12 |
+
vit_ln_eps: float = 1e-6
|
| 13 |
+
vit_cls_flag: bool = False
|
| 14 |
+
vit_model_type: str = 'google/siglip2-base-patch16-512'
|
| 15 |
+
|
| 16 |
+
lm_hidden_dim: int = 960
|
| 17 |
+
lm_inter_dim: int = 2560
|
| 18 |
+
lm_rms_eps: float = 1e-5
|
| 19 |
+
lm_re_base: int = 100000
|
| 20 |
+
lm_max_position_embeddings: int = 8192
|
| 21 |
+
lm_base_vocab_size: int = 49152
|
| 22 |
+
extra_token_amount: int = 66
|
| 23 |
+
lm_vocab_size: int = lm_base_vocab_size + extra_token_amount
|
| 24 |
+
lm_n_heads: int = 15
|
| 25 |
+
lm_n_kv_heads: int = 5
|
| 26 |
+
lm_dropout: float = 0.0
|
| 27 |
+
lm_n_blocks: int = 32
|
| 28 |
+
lm_attn_scaling: float = 1.0
|
| 29 |
+
lm_max_length: int = 4096
|
| 30 |
+
lm_use_tokens: bool = False
|
| 31 |
+
lm_tie_weights: bool = True
|
| 32 |
+
lm_model_type: str = 'HuggingFaceTB/SmolLM2-360M-Instruct'
|
| 33 |
+
lm_tokenizer: str = 'HuggingFaceTB/SmolLM2-360M-Instruct'
|
| 34 |
+
lm_chat_template: str = "{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}"
|
| 35 |
+
|
| 36 |
+
mp_pixel_shuffle_factor: int = 4
|
| 37 |
+
mp_image_token_length: int = 64
|
| 38 |
+
|
| 39 |
+
max_img_size: int = 2048
|
| 40 |
+
resize_to_max_side_len: bool = True
|
| 41 |
+
resize_min_side_len: int | None = None
|
| 42 |
+
|
| 43 |
+
inference_max_img_size: int | None = None
|
| 44 |
+
|
| 45 |
+
vlm_extra_tokens: dict[str, str] = field(default_factory=lambda: {"image_token": "<|image|>", "global_image_token": "<|global_image|>",
|
| 46 |
+
"r1c1": "<row_1_col_1>", "r1c2": "<row_1_col_2>", "r1c3": "<row_1_col_3>", "r1c4": "<row_1_col_4>", "r1c5": "<row_1_col_5>", "r1c6": "<row_1_col_6>", "r1c7": "<row_1_col_7>", "r1c8": "<row_1_col_8>",
|
| 47 |
+
"r2c1": "<row_2_col_1>", "r2c2": "<row_2_col_2>", "r2c3": "<row_2_col_3>", "r2c4": "<row_2_col_4>", "r2c5": "<row_2_col_5>", "r2c6": "<row_2_col_6>", "r2c7": "<row_2_col_7>", "r2c8": "<row_2_col_8>",
|
| 48 |
+
"r3c1": "<row_3_col_1>", "r3c2": "<row_3_col_2>", "r3c3": "<row_3_col_3>", "r3c4": "<row_3_col_4>", "r3c5": "<row_3_col_5>", "r3c6": "<row_3_col_6>", "r3c7": "<row_3_col_7>", "r3c8": "<row_3_col_8>",
|
| 49 |
+
"r4c1": "<row_4_col_1>", "r4c2": "<row_4_col_2>", "r4c3": "<row_4_col_3>", "r4c4": "<row_4_col_4>", "r4c5": "<row_4_col_5>", "r4c6": "<row_4_col_6>", "r4c7": "<row_4_col_7>", "r4c8": "<row_4_col_8>",
|
| 50 |
+
"r5c1": "<row_5_col_1>", "r5c2": "<row_5_col_2>", "r5c3": "<row_5_col_3>", "r5c4": "<row_5_col_4>", "r5c5": "<row_5_col_5>", "r5c6": "<row_5_col_6>", "r5c7": "<row_5_col_7>", "r5c8": "<row_5_col_8>",
|
| 51 |
+
"r6c1": "<row_6_col_1>", "r6c2": "<row_6_col_2>", "r6c3": "<row_6_col_3>", "r6c4": "<row_6_col_4>", "r6c5": "<row_6_col_5>", "r6c6": "<row_6_col_6>", "r6c7": "<row_6_col_7>", "r6c8": "<row_6_col_8>",
|
| 52 |
+
"r7c1": "<row_7_col_1>", "r7c2": "<row_7_col_2>", "r7c3": "<row_7_col_3>", "r7c4": "<row_7_col_4>", "r7c5": "<row_7_col_5>", "r7c6": "<row_7_col_6>", "r7c7": "<row_7_col_7>", "r7c8": "<row_7_col_8>",
|
| 53 |
+
"r8c1": "<row_8_col_1>", "r8c2": "<row_8_col_2>", "r8c3": "<row_8_col_3>", "r8c4": "<row_8_col_4>", "r8c5": "<row_8_col_5>", "r8c6": "<row_8_col_6>", "r8c7": "<row_8_col_7>", "r8c8": "<row_8_col_8>"})
|
| 54 |
+
vlm_load_backbone_weights: bool = True
|
| 55 |
+
vlm_checkpoint_path: str = 'checkpoints'
|
| 56 |
+
hf_repo_name: str = 'qvac/VisionPsy-Nano-460M'
|
| 57 |
+
|
| 58 |
+
compile_inference: bool = True
|
| 59 |
+
compile_inference_mode: str = 'reduce-overhead'
|
| 60 |
+
cuda_graphs_cache_quantum: int = 128
|
| 61 |
+
|
| 62 |
+
eos_check_interval: int = 16
|