Feature Extraction
Transformers
Safetensors
qwen3_5
image-text-to-text
multimodal-embedding
retrieval
evaluation
custom_code
Instructions to use woooooz/WeMM-Embedding-9B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use woooooz/WeMM-Embedding-9B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="woooooz/WeMM-Embedding-9B", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("woooooz/WeMM-Embedding-9B", trust_remote_code=True) model = AutoModelForMultimodalLM.from_pretrained("woooooz/WeMM-Embedding-9B", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Package upstream model with reproducible local MMEB evaluation
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- LICENSE +434 -0
- README.md +22 -0
- UPSTREAM_README.md +228 -0
- additional_chat_templates/sentence_transformers.jinja +32 -0
- chat_template.jinja +154 -0
- config.json +122 -0
- config_sentence_transformers.json +11 -0
- embedding_chat_template.jinja +28 -0
- evaluation/README.md +31 -0
- evaluation/configs/image_retrieval.yaml +75 -0
- evaluation/configs/moment_retrieval.yaml +35 -0
- evaluation/configs/video_ret_5.yaml +45 -0
- evaluation/configs/visdoc_full.yaml +144 -0
- evaluation/nexus/LICENSE +22 -0
- evaluation/nexus/Nexus/__init__.py +24 -0
- evaluation/nexus/Nexus/abc/__init__.py +10 -0
- evaluation/nexus/Nexus/abc/arguments.py +197 -0
- evaluation/nexus/Nexus/abc/evaluation/__init__.py +13 -0
- evaluation/nexus/Nexus/abc/evaluation/arguments.py +31 -0
- evaluation/nexus/Nexus/abc/evaluation/data_loader.py +11 -0
- evaluation/nexus/Nexus/abc/evaluation/evaluator.py +24 -0
- evaluation/nexus/Nexus/abc/evaluation/runner.py +41 -0
- evaluation/nexus/Nexus/abc/inference/__init__.py +24 -0
- evaluation/nexus/Nexus/abc/inference/arguments.py +72 -0
- evaluation/nexus/Nexus/abc/inference/embedder/AbsEmbedder.py +264 -0
- evaluation/nexus/Nexus/abc/inference/embedder/__init__.py +1 -0
- evaluation/nexus/Nexus/abc/inference/inference_engine.py +87 -0
- evaluation/nexus/Nexus/abc/inference/reranker/AbsReranker.py +188 -0
- evaluation/nexus/Nexus/abc/inference/reranker/__init__.py +1 -0
- evaluation/nexus/Nexus/abc/training/__init__.py +0 -0
- evaluation/nexus/Nexus/abc/training/arguments.py +36 -0
- evaluation/nexus/Nexus/abc/training/dataset.py +8 -0
- evaluation/nexus/Nexus/abc/training/embedder/AbsArguments.py +46 -0
- evaluation/nexus/Nexus/abc/training/embedder/AbsDataset.py +34 -0
- evaluation/nexus/Nexus/abc/training/embedder/AbsModeling.py +85 -0
- evaluation/nexus/Nexus/abc/training/embedder/AbsRunner.py +79 -0
- evaluation/nexus/Nexus/abc/training/embedder/AbsTrainer.py +39 -0
- evaluation/nexus/Nexus/abc/training/embedder/__init__.py +25 -0
- evaluation/nexus/Nexus/abc/training/modeling.py +38 -0
- evaluation/nexus/Nexus/abc/training/reranker/AbsArguments.py +39 -0
- evaluation/nexus/Nexus/abc/training/reranker/AbsDataset.py +25 -0
- evaluation/nexus/Nexus/abc/training/reranker/AbsModeling.py +77 -0
- evaluation/nexus/Nexus/abc/training/reranker/AbsRunner.py +75 -0
- evaluation/nexus/Nexus/abc/training/reranker/AbsTrainer.py +41 -0
- evaluation/nexus/Nexus/abc/training/reranker/__init__.py +19 -0
- evaluation/nexus/Nexus/abc/training/runner.py +34 -0
- evaluation/nexus/Nexus/abc/training/trainer.py +44 -0
- evaluation/nexus/Nexus/data_mining/__init__.py +2 -0
- evaluation/nexus/Nexus/data_mining/multimodal_hard_negative/__init__.py +6 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ 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 |
+
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
LICENSE
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Tencent is pleased to support the open source community by making WeMM-Embedding-9B available.
|
| 2 |
+
|
| 3 |
+
Copyright (C) 2026 Tencent. All rights reserved.
|
| 4 |
+
|
| 5 |
+
The open-source software and/or models included in this distribution may have been modified by Tencent (“Tencent Modifications”). All Tencent Modifications are Copyright (C) Tencent.
|
| 6 |
+
|
| 7 |
+
WeMM-Embedding-9B is licensed under Apache-2.0, except for the third-party components listed below, which remain licensed under their respective original terms. WeMM-Embedding-9B does not impose any additional restrictions beyond those specified in the original licenses of these third-party components. Users are required to comply with all applicable terms and conditions of the original licenses and to ensure that the use of these third-party components conforms to all relevant laws and regulations.
|
| 8 |
+
|
| 9 |
+
For the avoidance of doubt, WeMM-Embedding-9B refers to code, parameters, and weights made publicly available by Tencent in accordance with Apache-2.0.
|
| 10 |
+
|
| 11 |
+
Terms of Apache-2.0:
|
| 12 |
+
--------------------------------------------------------------------
|
| 13 |
+
Apache License
|
| 14 |
+
Version 2.0, January 2004
|
| 15 |
+
http://www.apache.org/licenses/
|
| 16 |
+
|
| 17 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 18 |
+
|
| 19 |
+
1. Definitions.
|
| 20 |
+
|
| 21 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 22 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 23 |
+
|
| 24 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 25 |
+
the copyright owner that is granting the License.
|
| 26 |
+
|
| 27 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 28 |
+
other entities that control, are controlled by, or are under common
|
| 29 |
+
control with that entity. For the purposes of this definition,
|
| 30 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 31 |
+
direction or management of such entity, whether by contract or
|
| 32 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 33 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 34 |
+
|
| 35 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 36 |
+
exercising permissions granted by this License.
|
| 37 |
+
|
| 38 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 39 |
+
including but not limited to software source code, documentation
|
| 40 |
+
source, and configuration files.
|
| 41 |
+
|
| 42 |
+
"Object" form shall mean any form resulting from mechanical
|
| 43 |
+
transformation or translation of a Source form, including but
|
| 44 |
+
not limited to compiled object code, generated documentation,
|
| 45 |
+
and conversions to other media types.
|
| 46 |
+
|
| 47 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 48 |
+
Object form, made available under the License, as indicated by a
|
| 49 |
+
copyright notice that is included in or attached to the work
|
| 50 |
+
(an example is provided in the Appendix below).
|
| 51 |
+
|
| 52 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 53 |
+
form, that is based on (or derived from) the Work and for which the
|
| 54 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 55 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 56 |
+
of this License, Derivative Works shall not include works that remain
|
| 57 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 58 |
+
the Work and Derivative Works thereof.
|
| 59 |
+
|
| 60 |
+
"Contribution" shall mean any work of authorship, including
|
| 61 |
+
the original version of the Work and any modifications or additions
|
| 62 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 63 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 64 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 65 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 66 |
+
means any form of electronic, verbal, or written communication sent
|
| 67 |
+
to the Licensor or its representatives, including but not limited to
|
| 68 |
+
communication on electronic mailing lists, source code control systems,
|
| 69 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 70 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 71 |
+
excluding communication that is conspicuously marked or otherwise
|
| 72 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 73 |
+
|
| 74 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 75 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 76 |
+
subsequently incorporated within the Work.
|
| 77 |
+
|
| 78 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 79 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 80 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 81 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 82 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 83 |
+
Work and such Derivative Works in Source or Object form.
|
| 84 |
+
|
| 85 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 86 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 87 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 88 |
+
(except as stated in this section) patent license to make, have made,
|
| 89 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 90 |
+
where such license applies only to those patent claims licensable
|
| 91 |
+
by such Contributor that are necessarily infringed by their
|
| 92 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 93 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 94 |
+
institute patent litigation against any entity (including a
|
| 95 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 96 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 97 |
+
or contributory patent infringement, then any patent licenses
|
| 98 |
+
granted to You under this License for that Work shall terminate
|
| 99 |
+
as of the date such litigation is filed.
|
| 100 |
+
|
| 101 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 102 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 103 |
+
modifications, and in Source or Object form, provided that You
|
| 104 |
+
meet the following conditions:
|
| 105 |
+
|
| 106 |
+
(a) You must give any other recipients of the Work or
|
| 107 |
+
Derivative Works a copy of this License; and
|
| 108 |
+
|
| 109 |
+
(b) You must cause any modified files to carry prominent notices
|
| 110 |
+
stating that You changed the files; and
|
| 111 |
+
|
| 112 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 113 |
+
that You distribute, all copyright, patent, trademark, and
|
| 114 |
+
attribution notices from the Source form of the Work,
|
| 115 |
+
excluding those notices that do not pertain to any part of
|
| 116 |
+
the Derivative Works; and
|
| 117 |
+
|
| 118 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 119 |
+
distribution, then any Derivative Works that You distribute must
|
| 120 |
+
include a readable copy of the attribution notices contained
|
| 121 |
+
within such NOTICE file, excluding those notices that do not
|
| 122 |
+
pertain to any part of the Derivative Works, in at least one
|
| 123 |
+
of the following places: within a NOTICE text file distributed
|
| 124 |
+
as part of the Derivative Works; within the Source form or
|
| 125 |
+
documentation, if provided along with the Derivative Works; or,
|
| 126 |
+
within a display generated by the Derivative Works, if and
|
| 127 |
+
wherever such third-party notices normally appear. The contents
|
| 128 |
+
of the NOTICE file are for informational purposes only and
|
| 129 |
+
do not modify the License. You may add Your own attribution
|
| 130 |
+
notices within Derivative Works that You distribute, alongside
|
| 131 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 132 |
+
that such additional attribution notices cannot be construed
|
| 133 |
+
as modifying the License.
|
| 134 |
+
|
| 135 |
+
You may add Your own copyright statement to Your modifications and
|
| 136 |
+
may provide additional or different license terms and conditions
|
| 137 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 138 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 139 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 140 |
+
the conditions stated in this License.
|
| 141 |
+
|
| 142 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 143 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 144 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 145 |
+
this License, without any additional terms or conditions.
|
| 146 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 147 |
+
the terms of any separate license agreement you may have executed
|
| 148 |
+
with Licensor regarding such Contributions.
|
| 149 |
+
|
| 150 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 151 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 152 |
+
except as required for reasonable and customary use in describing the
|
| 153 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 154 |
+
|
| 155 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 156 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 157 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 158 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 159 |
+
implied, including, without limitation, any warranties or conditions
|
| 160 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 161 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 162 |
+
appropriateness of using or redistributing the Work and assume any
|
| 163 |
+
risks associated with Your exercise of permissions under this License.
|
| 164 |
+
|
| 165 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 166 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 167 |
+
unless required by applicable law (such as deliberate and grossly
|
| 168 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 169 |
+
liable to You for damages, including any direct, indirect, special,
|
| 170 |
+
incidental, or consequential damages of any character arising as a
|
| 171 |
+
result of this License or out of the use or inability to use the
|
| 172 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 173 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 174 |
+
other commercial damages or losses), even if such Contributor
|
| 175 |
+
has been advised of the possibility of such damages.
|
| 176 |
+
|
| 177 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 178 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 179 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 180 |
+
or other liability obligations and/or rights consistent with this
|
| 181 |
+
License. However, in accepting such obligations, You may act only
|
| 182 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 183 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 184 |
+
defend, and hold each Contributor harmless for any liability
|
| 185 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 186 |
+
of your accepting any such warranty or additional liability.
|
| 187 |
+
|
| 188 |
+
END OF TERMS AND CONDITIONS
|
| 189 |
+
|
| 190 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 191 |
+
|
| 192 |
+
To apply the Apache License to your work, attach the following
|
| 193 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 194 |
+
replaced with your own identifying information. (Don't include
|
| 195 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 196 |
+
comment syntax for the file format. We also recommend that a
|
| 197 |
+
file or class name and description of purpose be included on the
|
| 198 |
+
same "printed page" as the copyright notice for easier
|
| 199 |
+
identification within third-party archives.
|
| 200 |
+
|
| 201 |
+
Copyright [yyyy] [name of copyright owner]
|
| 202 |
+
|
| 203 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 204 |
+
you may not use this file except in compliance with the License.
|
| 205 |
+
You may obtain a copy of the License at
|
| 206 |
+
|
| 207 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 208 |
+
|
| 209 |
+
Unless required by applicable law or agreed to in writing, software
|
| 210 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 211 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 212 |
+
See the License for the specific language governing permissions and
|
| 213 |
+
limitations under the License.
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
Dependencies and Licenses:
|
| 217 |
+
|
| 218 |
+
This open-source project, WeMM-Embedding-9B, builds upon the following open-source models and/or software components, each of which remains licensed under its original license. Certain models or software may include modifications made by Tencent (“Tencent Modifications”), which are Copyright (C) Tencent.
|
| 219 |
+
|
| 220 |
+
In case you believe there have been errors in the attribution below, you may submit the concerns to us for review and correction.
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
Open Source Model Licensed under the Apache-2.0:
|
| 224 |
+
--------------------------------------------------------------------
|
| 225 |
+
1. Qwen3.5-9B
|
| 226 |
+
Copyright 2026 Alibaba Cloud
|
| 227 |
+
Please note this model has been modified by Tencent in this distribution.
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
Terms of the Apache-2.0:
|
| 231 |
+
--------------------------------------------------------------------
|
| 232 |
+
Apache License
|
| 233 |
+
Version 2.0, January 2004
|
| 234 |
+
http://www.apache.org/licenses/
|
| 235 |
+
|
| 236 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 237 |
+
|
| 238 |
+
1. Definitions.
|
| 239 |
+
|
| 240 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 241 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 242 |
+
|
| 243 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 244 |
+
the copyright owner that is granting the License.
|
| 245 |
+
|
| 246 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 247 |
+
other entities that control, are controlled by, or are under common
|
| 248 |
+
control with that entity. For the purposes of this definition,
|
| 249 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 250 |
+
direction or management of such entity, whether by contract or
|
| 251 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 252 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 253 |
+
|
| 254 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 255 |
+
exercising permissions granted by this License.
|
| 256 |
+
|
| 257 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 258 |
+
including but not limited to software source code, documentation
|
| 259 |
+
source, and configuration files.
|
| 260 |
+
|
| 261 |
+
"Object" form shall mean any form resulting from mechanical
|
| 262 |
+
transformation or translation of a Source form, including but
|
| 263 |
+
not limited to compiled object code, generated documentation,
|
| 264 |
+
and conversions to other media types.
|
| 265 |
+
|
| 266 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 267 |
+
Object form, made available under the License, as indicated by a
|
| 268 |
+
copyright notice that is included in or attached to the work
|
| 269 |
+
(an example is provided in the Appendix below).
|
| 270 |
+
|
| 271 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 272 |
+
form, that is based on (or derived from) the Work and for which the
|
| 273 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 274 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 275 |
+
of this License, Derivative Works shall not include works that remain
|
| 276 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 277 |
+
the Work and Derivative Works thereof.
|
| 278 |
+
|
| 279 |
+
"Contribution" shall mean any work of authorship, including
|
| 280 |
+
the original version of the Work and any modifications or additions
|
| 281 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 282 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 283 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 284 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 285 |
+
means any form of electronic, verbal, or written communication sent
|
| 286 |
+
to the Licensor or its representatives, including but not limited to
|
| 287 |
+
communication on electronic mailing lists, source code control systems,
|
| 288 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 289 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 290 |
+
excluding communication that is conspicuously marked or otherwise
|
| 291 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 292 |
+
|
| 293 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 294 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 295 |
+
subsequently incorporated within the Work.
|
| 296 |
+
|
| 297 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 298 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 299 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 300 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 301 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 302 |
+
Work and such Derivative Works in Source or Object form.
|
| 303 |
+
|
| 304 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 305 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 306 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 307 |
+
(except as stated in this section) patent license to make, have made,
|
| 308 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 309 |
+
where such license applies only to those patent claims licensable
|
| 310 |
+
by such Contributor that are necessarily infringed by their
|
| 311 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 312 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 313 |
+
institute patent litigation against any entity (including a
|
| 314 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 315 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 316 |
+
or contributory patent infringement, then any patent licenses
|
| 317 |
+
granted to You under this License for that Work shall terminate
|
| 318 |
+
as of the date such litigation is filed.
|
| 319 |
+
|
| 320 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 321 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 322 |
+
modifications, and in Source or Object form, provided that You
|
| 323 |
+
meet the following conditions:
|
| 324 |
+
|
| 325 |
+
(a) You must give any other recipients of the Work or
|
| 326 |
+
Derivative Works a copy of this License; and
|
| 327 |
+
|
| 328 |
+
(b) You must cause any modified files to carry prominent notices
|
| 329 |
+
stating that You changed the files; and
|
| 330 |
+
|
| 331 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 332 |
+
that You distribute, all copyright, patent, trademark, and
|
| 333 |
+
attribution notices from the Source form of the Work,
|
| 334 |
+
excluding those notices that do not pertain to any part of
|
| 335 |
+
the Derivative Works; and
|
| 336 |
+
|
| 337 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 338 |
+
distribution, then any Derivative Works that You distribute must
|
| 339 |
+
include a readable copy of the attribution notices contained
|
| 340 |
+
within such NOTICE file, excluding those notices that do not
|
| 341 |
+
pertain to any part of the Derivative Works, in at least one
|
| 342 |
+
of the following places: within a NOTICE text file distributed
|
| 343 |
+
as part of the Derivative Works; within the Source form or
|
| 344 |
+
documentation, if provided along with the Derivative Works; or,
|
| 345 |
+
within a display generated by the Derivative Works, if and
|
| 346 |
+
wherever such third-party notices normally appear. The contents
|
| 347 |
+
of the NOTICE file are for informational purposes only and
|
| 348 |
+
do not modify the License. You may add Your own attribution
|
| 349 |
+
notices within Derivative Works that You distribute, alongside
|
| 350 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 351 |
+
that such additional attribution notices cannot be construed
|
| 352 |
+
as modifying the License.
|
| 353 |
+
|
| 354 |
+
You may add Your own copyright statement to Your modifications and
|
| 355 |
+
may provide additional or different license terms and conditions
|
| 356 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 357 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 358 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 359 |
+
the conditions stated in this License.
|
| 360 |
+
|
| 361 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 362 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 363 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 364 |
+
this License, without any additional terms or conditions.
|
| 365 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 366 |
+
the terms of any separate license agreement you may have executed
|
| 367 |
+
with Licensor regarding such Contributions.
|
| 368 |
+
|
| 369 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 370 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 371 |
+
except as required for reasonable and customary use in describing the
|
| 372 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 373 |
+
|
| 374 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 375 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 376 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 377 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 378 |
+
implied, including, without limitation, any warranties or conditions
|
| 379 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 380 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 381 |
+
appropriateness of using or redistributing the Work and assume any
|
| 382 |
+
risks associated with Your exercise of permissions under this License.
|
| 383 |
+
|
| 384 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 385 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 386 |
+
unless required by applicable law (such as deliberate and grossly
|
| 387 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 388 |
+
liable to You for damages, including any direct, indirect, special,
|
| 389 |
+
incidental, or consequential damages of any character arising as a
|
| 390 |
+
result of this License or out of the use or inability to use the
|
| 391 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 392 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 393 |
+
other commercial damages or losses), even if such Contributor
|
| 394 |
+
has been advised of the possibility of such damages.
|
| 395 |
+
|
| 396 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 397 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 398 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 399 |
+
or other liability obligations and/or rights consistent with this
|
| 400 |
+
License. However, in accepting such obligations, You may act only
|
| 401 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 402 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 403 |
+
defend, and hold each Contributor harmless for any liability
|
| 404 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 405 |
+
of your accepting any such warranty or additional liability.
|
| 406 |
+
|
| 407 |
+
END OF TERMS AND CONDITIONS
|
| 408 |
+
|
| 409 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 410 |
+
|
| 411 |
+
To apply the Apache License to your work, attach the following
|
| 412 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 413 |
+
replaced with your own identifying information. (Don't include
|
| 414 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 415 |
+
comment syntax for the file format. We also recommend that a
|
| 416 |
+
file or class name and description of purpose be included on the
|
| 417 |
+
same "printed page" as the copyright notice for easier
|
| 418 |
+
identification within third-party archives.
|
| 419 |
+
|
| 420 |
+
Copyright [yyyy] [name of copyright owner]
|
| 421 |
+
|
| 422 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 423 |
+
you may not use this file except in compliance with the License.
|
| 424 |
+
You may obtain a copy of the License at
|
| 425 |
+
|
| 426 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 427 |
+
|
| 428 |
+
Unless required by applicable law or agreed to in writing, software
|
| 429 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 430 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 431 |
+
See the License for the specific language governing permissions and
|
| 432 |
+
limitations under the License.
|
| 433 |
+
|
| 434 |
+
|
README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: other
|
| 3 |
+
license_name: apache-2.0
|
| 4 |
+
license_link: LICENSE
|
| 5 |
+
library_name: transformers
|
| 6 |
+
pipeline_tag: feature-extraction
|
| 7 |
+
base_model: tencent/WeMM-Embedding-9B
|
| 8 |
+
tags:
|
| 9 |
+
- multimodal-embedding
|
| 10 |
+
- retrieval
|
| 11 |
+
- evaluation
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# WeMM-Embedding-9B with MMEB evaluation
|
| 15 |
+
|
| 16 |
+
Mirror of [tencent/WeMM-Embedding-9B](https://huggingface.co/tencent/WeMM-Embedding-9B) with the local Nexus MMEB evaluation source, configuration and runtime snapshot, prepared on 2026-10-15. The weights retain their upstream provenance and license. This is not a new fine-tuned model.
|
| 17 |
+
|
| 18 |
+
- Original model documentation and inference examples: [UPSTREAM_README.md](UPSTREAM_README.md).
|
| 19 |
+
- Evaluation setup and runnable commands: [evaluation/README.md](evaluation/README.md).
|
| 20 |
+
- Integrity and source information: [package_manifest.json](package_manifest.json).
|
| 21 |
+
|
| 22 |
+
The evaluation package includes image, video, moment and visual-document retrieval configurations. Evaluation data must be obtained separately. No new benchmark scores are asserted by this mirror.
|
UPSTREAM_README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
library_name: transformers
|
| 3 |
+
license: other
|
| 4 |
+
license_name: apache-2.0
|
| 5 |
+
license_link: https://huggingface.co/tencent/WeMM-Embedding-9B/blob/main/LICENSE
|
| 6 |
+
|
| 7 |
+
base_model:
|
| 8 |
+
- Qwen/Qwen3.5-9B
|
| 9 |
+
pipeline_tag: feature-extraction
|
| 10 |
+
|
| 11 |
+
tags:
|
| 12 |
+
- sentence-transformers
|
| 13 |
+
- multimodal-embedding
|
| 14 |
+
- text-embedding
|
| 15 |
+
- image-embedding
|
| 16 |
+
- video-embedding
|
| 17 |
+
- mrl
|
| 18 |
+
|
| 19 |
+
language:
|
| 20 |
+
- zh
|
| 21 |
+
- en
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
# WeMM-Embedding-9B
|
| 25 |
+
|
| 26 |
+
[](https://huggingface.co/collections/tencent/wemm-embedding)
|
| 27 |
+
[](https://arxiv.org/abs/2608.24053)
|
| 28 |
+
[](https://github.com/Tencent/WeMM-Embedding)
|
| 29 |
+
|
| 30 |
+
WeMM-Embedding-9B is a universal multimodal embedding model built on Qwen3.5. It accepts text, images, videos, visual documents, and interleaved multimodal inputs, and returns a 4,096-dimensional L2-normalized embedding. Audio input is not supported.
|
| 31 |
+
|
| 32 |
+
## Installation
|
| 33 |
+
|
| 34 |
+
```bash
|
| 35 |
+
pip install torch transformers==5.2.0 "qwen-vl-utils[decord]==0.0.14" \
|
| 36 |
+
"sentence-transformers>=5.7.0" "accelerate>=1.1.0"
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
## Transformers
|
| 40 |
+
|
| 41 |
+
```python
|
| 42 |
+
import torch
|
| 43 |
+
from qwen_vl_utils import process_vision_info
|
| 44 |
+
from transformers import AutoModel, AutoProcessor
|
| 45 |
+
|
| 46 |
+
model_id = "tencent/WeMM-Embedding-9B"
|
| 47 |
+
processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
|
| 48 |
+
model = AutoModel.from_pretrained(
|
| 49 |
+
model_id, trust_remote_code=True, dtype=torch.bfloat16
|
| 50 |
+
).cuda().eval()
|
| 51 |
+
|
| 52 |
+
messages = [{"role": "user", "content": [
|
| 53 |
+
{"type": "image", "image": "/path/to/image.jpg"},
|
| 54 |
+
{"type": "video", "video": "/path/to/video.mp4"},
|
| 55 |
+
{"type": "text", "text": "This can be any text input."},
|
| 56 |
+
]}]
|
| 57 |
+
text = processor.apply_chat_template(
|
| 58 |
+
messages, tokenize=False, add_generation_prompt=False
|
| 59 |
+
)
|
| 60 |
+
images, videos, video_kwargs = process_vision_info(
|
| 61 |
+
messages,
|
| 62 |
+
image_patch_size=16,
|
| 63 |
+
return_video_kwargs=True,
|
| 64 |
+
return_video_metadata=True,
|
| 65 |
+
)
|
| 66 |
+
if videos is not None:
|
| 67 |
+
videos, video_metadata = zip(*videos)
|
| 68 |
+
videos, video_metadata = list(videos), list(video_metadata)
|
| 69 |
+
else:
|
| 70 |
+
video_metadata = None
|
| 71 |
+
inputs = processor(
|
| 72 |
+
text=text,
|
| 73 |
+
images=images,
|
| 74 |
+
videos=videos,
|
| 75 |
+
video_metadata=video_metadata,
|
| 76 |
+
return_tensors="pt",
|
| 77 |
+
**video_kwargs,
|
| 78 |
+
).to("cuda")
|
| 79 |
+
|
| 80 |
+
with torch.inference_mode():
|
| 81 |
+
embedding = model.embedding(**inputs)
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
Use any subset of the content items to encode text, image, or video independently.
|
| 85 |
+
|
| 86 |
+
## Sentence Transformers
|
| 87 |
+
|
| 88 |
+
```python
|
| 89 |
+
from sentence_transformers import SentenceTransformer
|
| 90 |
+
|
| 91 |
+
model_id = "tencent/WeMM-Embedding-9B"
|
| 92 |
+
model = SentenceTransformer(model_id, trust_remote_code=True)
|
| 93 |
+
|
| 94 |
+
queries = [
|
| 95 |
+
"Which Llama 4 model variants are available?",
|
| 96 |
+
"How is mapo tofu prepared?",
|
| 97 |
+
]
|
| 98 |
+
documents = [
|
| 99 |
+
"Mapo tofu is a Sichuan dish of soft tofu simmered in a spicy, numbing sauce of chili bean paste and Sichuan peppercorn.",
|
| 100 |
+
{
|
| 101 |
+
"image": "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/llama4_hgf.png",
|
| 102 |
+
"text": "Represent this image.",
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"video": "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/mapo_tofu.mp4",
|
| 106 |
+
"text": "Represent this video.",
|
| 107 |
+
},
|
| 108 |
+
]
|
| 109 |
+
|
| 110 |
+
query_embeddings = model.encode_query(queries)
|
| 111 |
+
document_embeddings = model.encode_document(documents)
|
| 112 |
+
print(query_embeddings.shape, document_embeddings.shape)
|
| 113 |
+
# (2, 4096) (3, 4096)
|
| 114 |
+
|
| 115 |
+
similarities = model.similarity(query_embeddings, document_embeddings)
|
| 116 |
+
print(similarities)
|
| 117 |
+
# tensor([[0.2153, 0.5843, 0.1221],
|
| 118 |
+
# [0.7665, 0.2604, 0.5366]])
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
Each input is a string, a URL or path, a `PIL.Image`, or a dict combining `image`,
|
| 122 |
+
`video`, and `text` keys. Put `image` or `video` before `text` so the prompt matches
|
| 123 |
+
the ordering used above. Chat messages such as
|
| 124 |
+
`{"role": "user", "content": [{"type": "image", "image": ...}, {"type": "text", "text": ...}]}`
|
| 125 |
+
are also accepted, which is the way to interleave several images or videos in one input.
|
| 126 |
+
|
| 127 |
+
## Matryoshka Embeddings
|
| 128 |
+
|
| 129 |
+
```python
|
| 130 |
+
d = 256
|
| 131 |
+
embedding_d = torch.nn.functional.normalize(embedding[..., :d], dim=-1)
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
With Sentence Transformers, pass `truncate_dim` and let it renormalize:
|
| 135 |
+
|
| 136 |
+
```python
|
| 137 |
+
embeddings_d = model.encode_document(documents, truncate_dim=d, normalize_embeddings=True)
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
Use a dimension listed in `model.config.matryoshka_dimensions`.
|
| 141 |
+
|
| 142 |
+
## Serving
|
| 143 |
+
|
| 144 |
+
vLLM `0.27.0`:
|
| 145 |
+
|
| 146 |
+
```bash
|
| 147 |
+
MODEL_PATH=/path/to/WeMM-Embedding-9B
|
| 148 |
+
vllm serve "$MODEL_PATH" \
|
| 149 |
+
--runner pooling \
|
| 150 |
+
--chat-template "$MODEL_PATH/embedding_chat_template.jinja"
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
SGLang `0.5.9`:
|
| 154 |
+
|
| 155 |
+
```bash
|
| 156 |
+
MODEL_PATH=/path/to/WeMM-Embedding-9B
|
| 157 |
+
python patch_sglang_video.py
|
| 158 |
+
python -m sglang.launch_server \
|
| 159 |
+
--model-path "$MODEL_PATH" \
|
| 160 |
+
--is-embedding \
|
| 161 |
+
--enable-precise-embedding-interpolation
|
| 162 |
+
```
|
| 163 |
+
|
| 164 |
+
## Evaluation
|
| 165 |
+
|
| 166 |
+
### MMEB-v2
|
| 167 |
+
|
| 168 |
+
Results on 78 datasets from Table 1 of the [technical report](https://github.com/Tencent/WeMM-Embedding/blob/main/assets/WeMM_Embedding_tech_report.pdf). Image and video tasks use Hit@1, while visual-document tasks use NDCG@5. Higher is better.
|
| 169 |
+
|
| 170 |
+
| Model | Size | AVG | Image | Video | VisDoc |
|
| 171 |
+
| --- | ---: | ---: | ---: | ---: | ---: |
|
| 172 |
+
| VLM2Vec | 2B | 47.8 | 59.7 | 29.0 | 44.0 |
|
| 173 |
+
| GME | 2B | 55.4 | 51.9 | 33.9 | 76.8 |
|
| 174 |
+
| VLM2Vec-V2 | 2B | 59.3 | 64.9 | 34.9 | 69.2 |
|
| 175 |
+
| Qwen3-VL-Embedding | 2B | 73.2 | 75.0 | 61.9 | 79.2 |
|
| 176 |
+
| DME-Small† | 2B | 74.8 | 75.9 | 65.6 | 79.9 |
|
| 177 |
+
| **WeMM-Embedding** | **2B** | **77.9** | **79.6** | **70.8** | **80.7** |
|
| 178 |
+
| **WeMM-Embedding** | **4B** | **79.2** | **80.8** | **72.1** | **82.0** |
|
| 179 |
+
| VLM2Vec | 8B | 53.2 | 65.5 | 34.0 | 49.1 |
|
| 180 |
+
| GME | 8B | 59.2 | 56.0 | 38.6 | 79.3 |
|
| 181 |
+
| Qwen3-VL-Embedding | 8B | 77.8 | 80.1 | 67.1 | 82.4 |
|
| 182 |
+
| DME-Medium† | 9B | 78.4 | 79.8 | 70.8 | 82.0 |
|
| 183 |
+
| **WeMM-Embedding** | **9B** | **80.6** | **81.9** | **74.3** | **83.3** |
|
| 184 |
+
|
| 185 |
+
† Closed-source leaderboard submission without publicly released model weights or a public inference endpoint.
|
| 186 |
+
|
| 187 |
+
### MMEB-v3
|
| 188 |
+
|
| 189 |
+
Results on all 190 tasks from Table 2 of the [technical report](https://github.com/Tencent/WeMM-Embedding/blob/main/assets/WeMM_Embedding_tech_report.pdf). V3-All includes the 78 MMEB-v2 tasks, 53 text tasks, 47 agent tasks, 11 audio tasks, and MCMR. Unsupported tasks are assigned a score of zero.
|
| 190 |
+
|
| 191 |
+
| Model | Size | V3-All | Text | Agent | MCMR | Audio |
|
| 192 |
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
| 193 |
+
| VLM2Vec-V2 | 2B | 38.3 | 24.5 | 28.7 | 4.1 | 0.0 |
|
| 194 |
+
| Omni-Embed-Nemotron | 3B | 43.5 | 39.2 | 36.5 | 26.1 | 36.5 |
|
| 195 |
+
| E5-Omni | 3B | 44.6 | 26.7 | 36.9 | 31.9 | 30.8 |
|
| 196 |
+
| Qwen3-VL-Embedding | 2B | 50.9 | 39.2 | 39.3 | 42.0 | 0.0 |
|
| 197 |
+
| **WeMM-Embedding** | **2B** | **56.0** | **45.3** | **45.1** | **42.5** | **0.0** |
|
| 198 |
+
| **WeMM-Embedding** | **4B** | **58.2** | **47.9** | **49.0** | **41.9** | **0.0** |
|
| 199 |
+
| WAVE | 7B | 26.3 | 13.7 | 11.3 | 8.9 | 31.8 |
|
| 200 |
+
| VLM2Vec | 8B | 32.9 | 22.2 | 19.7 | 0.9 | 0.0 |
|
| 201 |
+
| LCO-Embedding-Omni | 7B | 40.6 | 32.4 | 27.8 | 20.0 | 43.2 |
|
| 202 |
+
| GME | 8B | 43.6 | 37.1 | 35.6 | 27.3 | 0.0 |
|
| 203 |
+
| E5-Omni | 7B | 47.1 | 26.9 | 36.7 | 41.1 | 43.0 |
|
| 204 |
+
| Tianmu-Emb-Uni | 8B | 53.3 | 43.6 | 39.4 | 38.8 | 38.9 |
|
| 205 |
+
| Qwen3-VL-Embedding | 8B | 53.5 | 42.5 | 38.4 | 38.0 | 0.0 |
|
| 206 |
+
| **WeMM-Embedding** | **9B** | **59.5** | **48.8** | **51.0** | **49.3** | **0.0** |
|
| 207 |
+
|
| 208 |
+
Text results use NDCG@5; agent, MCMR, and audio results use Hit@1.
|
| 209 |
+
|
| 210 |
+
## Citation
|
| 211 |
+
If you find this repository useful, please consider giving a star ⭐ and citation
|
| 212 |
+
```bibtex
|
| 213 |
+
@article{wemm-embedding,
|
| 214 |
+
title={WeMM-Embedding: WeChat Multi-Modal Embedding Technical Report},
|
| 215 |
+
author={Junjie Zhou and Ke Mei and Lei Li and Tianyi Wang and Fengyun Rao and Jing Lyu},
|
| 216 |
+
year={2026},
|
| 217 |
+
eprint={2608.24053},
|
| 218 |
+
archivePrefix={arXiv},
|
| 219 |
+
primaryClass={cs.CV},
|
| 220 |
+
url={https://arxiv.org/abs/2608.24053},
|
| 221 |
+
}
|
| 222 |
+
```
|
| 223 |
+
|
| 224 |
+
## License
|
| 225 |
+
|
| 226 |
+
WeMM-Embedding-9B, including the code, model parameters, and weights made publicly
|
| 227 |
+
available by Tencent, is licensed under the [Apache License 2.0](https://huggingface.co/tencent/WeMM-Embedding-9B/blob/main/LICENSE).
|
| 228 |
+
Third-party components remain subject to their respective original licenses.
|
additional_chat_templates/sentence_transformers.jinja
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{#- Embedding template used by Sentence Transformers.
|
| 2 |
+
|
| 3 |
+
Renders the same surface form as chat_template.jinja for text and images. Video
|
| 4 |
+
is the exception: it renders as a bare <|video_pad|>, because the processor
|
| 5 |
+
expands that token into one <seconds><|vision_start|>...<|vision_end|> block per
|
| 6 |
+
frame and, from transformers 5.3, no longer consumes a surrounding pair. The
|
| 7 |
+
wrapped form therefore gains a second, unwanted pair of vision boundaries there.
|
| 8 |
+
This matches embedding_chat_template.jinja, which is bare for the same reason.
|
| 9 |
+
|
| 10 |
+
The tokenizer normalizer drops the role newline before a leading image and the
|
| 11 |
+
newline after the final <|im_end|>, then the post-processor appends <embedding>. #}
|
| 12 |
+
{%- for message in messages %}
|
| 13 |
+
{{- '<|im_start|>' + message.role + '\n' }}
|
| 14 |
+
{%- if message.content is string %}
|
| 15 |
+
{{- message.content }}
|
| 16 |
+
{%- elif message.content is iterable and message.content is not mapping %}
|
| 17 |
+
{%- for item in message.content %}
|
| 18 |
+
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
| 19 |
+
{{- '<|vision_start|><|image_pad|><|vision_end|>' }}
|
| 20 |
+
{%- elif 'video' in item or item.type == 'video' %}
|
| 21 |
+
{{- '<|video_pad|>' }}
|
| 22 |
+
{%- elif 'text' in item %}
|
| 23 |
+
{{- item.text }}
|
| 24 |
+
{%- else %}
|
| 25 |
+
{{- raise_exception('Unexpected item type in content.') }}
|
| 26 |
+
{%- endif %}
|
| 27 |
+
{%- endfor %}
|
| 28 |
+
{%- elif message.content is not none %}
|
| 29 |
+
{{- raise_exception('Unexpected content type.') }}
|
| 30 |
+
{%- endif %}
|
| 31 |
+
{{- '<|im_end|>\n' }}
|
| 32 |
+
{%- endfor %}
|
chat_template.jinja
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- set image_count = namespace(value=0) %}
|
| 2 |
+
{%- set video_count = namespace(value=0) %}
|
| 3 |
+
{%- macro render_content(content, do_vision_count, is_system_content=false) %}
|
| 4 |
+
{%- if content is string %}
|
| 5 |
+
{{- content }}
|
| 6 |
+
{%- elif content is iterable and content is not mapping %}
|
| 7 |
+
{%- for item in content %}
|
| 8 |
+
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
| 9 |
+
{%- if is_system_content %}
|
| 10 |
+
{{- raise_exception('System message cannot contain images.') }}
|
| 11 |
+
{%- endif %}
|
| 12 |
+
{%- if do_vision_count %}
|
| 13 |
+
{%- set image_count.value = image_count.value + 1 %}
|
| 14 |
+
{%- endif %}
|
| 15 |
+
{%- if add_vision_id %}
|
| 16 |
+
{{- 'Picture ' ~ image_count.value ~ ': ' }}
|
| 17 |
+
{%- endif %}
|
| 18 |
+
{{- '<|vision_start|><|image_pad|><|vision_end|>' }}
|
| 19 |
+
{%- elif 'video' in item or item.type == 'video' %}
|
| 20 |
+
{%- if is_system_content %}
|
| 21 |
+
{{- raise_exception('System message cannot contain videos.') }}
|
| 22 |
+
{%- endif %}
|
| 23 |
+
{%- if do_vision_count %}
|
| 24 |
+
{%- set video_count.value = video_count.value + 1 %}
|
| 25 |
+
{%- endif %}
|
| 26 |
+
{%- if add_vision_id %}
|
| 27 |
+
{{- 'Video ' ~ video_count.value ~ ': ' }}
|
| 28 |
+
{%- endif %}
|
| 29 |
+
{{- '<|vision_start|><|video_pad|><|vision_end|>' }}
|
| 30 |
+
{%- elif 'text' in item %}
|
| 31 |
+
{{- item.text }}
|
| 32 |
+
{%- else %}
|
| 33 |
+
{{- raise_exception('Unexpected item type in content.') }}
|
| 34 |
+
{%- endif %}
|
| 35 |
+
{%- endfor %}
|
| 36 |
+
{%- elif content is none or content is undefined %}
|
| 37 |
+
{{- '' }}
|
| 38 |
+
{%- else %}
|
| 39 |
+
{{- raise_exception('Unexpected content type.') }}
|
| 40 |
+
{%- endif %}
|
| 41 |
+
{%- endmacro %}
|
| 42 |
+
{%- if not messages %}
|
| 43 |
+
{{- raise_exception('No messages provided.') }}
|
| 44 |
+
{%- endif %}
|
| 45 |
+
{%- if tools and tools is iterable and tools is not mapping %}
|
| 46 |
+
{{- '<|im_start|>system\n' }}
|
| 47 |
+
{{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
|
| 48 |
+
{%- for tool in tools %}
|
| 49 |
+
{{- "\n" }}
|
| 50 |
+
{{- tool | tojson }}
|
| 51 |
+
{%- endfor %}
|
| 52 |
+
{{- "\n</tools>" }}
|
| 53 |
+
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
| 54 |
+
{%- if messages[0].role == 'system' %}
|
| 55 |
+
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
| 56 |
+
{%- if content %}
|
| 57 |
+
{{- '\n\n' + content }}
|
| 58 |
+
{%- endif %}
|
| 59 |
+
{%- endif %}
|
| 60 |
+
{{- '<|im_end|>\n' }}
|
| 61 |
+
{%- else %}
|
| 62 |
+
{%- if messages[0].role == 'system' %}
|
| 63 |
+
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
| 64 |
+
{{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
|
| 65 |
+
{%- endif %}
|
| 66 |
+
{%- endif %}
|
| 67 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 68 |
+
{%- for message in messages[::-1] %}
|
| 69 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 70 |
+
{%- if ns.multi_step_tool and message.role == "user" %}
|
| 71 |
+
{%- set content = render_content(message.content, false)|trim %}
|
| 72 |
+
{%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
|
| 73 |
+
{%- set ns.multi_step_tool = false %}
|
| 74 |
+
{%- set ns.last_query_index = index %}
|
| 75 |
+
{%- endif %}
|
| 76 |
+
{%- endif %}
|
| 77 |
+
{%- endfor %}
|
| 78 |
+
{%- if ns.multi_step_tool %}
|
| 79 |
+
{{- raise_exception('No user query found in messages.') }}
|
| 80 |
+
{%- endif %}
|
| 81 |
+
{%- for message in messages %}
|
| 82 |
+
{%- set content = render_content(message.content, true)|trim %}
|
| 83 |
+
{%- if message.role == "system" %}
|
| 84 |
+
{%- if not loop.first %}
|
| 85 |
+
{{- raise_exception('System message must be at the beginning.') }}
|
| 86 |
+
{%- endif %}
|
| 87 |
+
{%- elif message.role == "user" %}
|
| 88 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 89 |
+
{%- elif message.role == "assistant" %}
|
| 90 |
+
{%- set reasoning_content = '' %}
|
| 91 |
+
{%- if message.reasoning_content is string %}
|
| 92 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 93 |
+
{%- else %}
|
| 94 |
+
{%- if '</think>' in content %}
|
| 95 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 96 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 97 |
+
{%- endif %}
|
| 98 |
+
{%- endif %}
|
| 99 |
+
{%- set reasoning_content = reasoning_content|trim %}
|
| 100 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 101 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
|
| 102 |
+
{%- else %}
|
| 103 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 104 |
+
{%- endif %}
|
| 105 |
+
{%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
|
| 106 |
+
{%- for tool_call in message.tool_calls %}
|
| 107 |
+
{%- if tool_call.function is defined %}
|
| 108 |
+
{%- set tool_call = tool_call.function %}
|
| 109 |
+
{%- endif %}
|
| 110 |
+
{%- if loop.first %}
|
| 111 |
+
{%- if content|trim %}
|
| 112 |
+
{{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 113 |
+
{%- else %}
|
| 114 |
+
{{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 115 |
+
{%- endif %}
|
| 116 |
+
{%- else %}
|
| 117 |
+
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 118 |
+
{%- endif %}
|
| 119 |
+
{%- if tool_call.arguments is defined %}
|
| 120 |
+
{%- for args_name, args_value in tool_call.arguments|items %}
|
| 121 |
+
{{- '<parameter=' + args_name + '>\n' }}
|
| 122 |
+
{%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
|
| 123 |
+
{{- args_value }}
|
| 124 |
+
{{- '\n</parameter>\n' }}
|
| 125 |
+
{%- endfor %}
|
| 126 |
+
{%- endif %}
|
| 127 |
+
{{- '</function>\n</tool_call>' }}
|
| 128 |
+
{%- endfor %}
|
| 129 |
+
{%- endif %}
|
| 130 |
+
{{- '<|im_end|>\n' }}
|
| 131 |
+
{%- elif message.role == "tool" %}
|
| 132 |
+
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
| 133 |
+
{{- '<|im_start|>user' }}
|
| 134 |
+
{%- endif %}
|
| 135 |
+
{{- '\n<tool_response>\n' }}
|
| 136 |
+
{{- content }}
|
| 137 |
+
{{- '\n</tool_response>' }}
|
| 138 |
+
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
| 139 |
+
{{- '<|im_end|>\n' }}
|
| 140 |
+
{%- elif loop.last %}
|
| 141 |
+
{{- '<|im_end|>\n' }}
|
| 142 |
+
{%- endif %}
|
| 143 |
+
{%- else %}
|
| 144 |
+
{{- raise_exception('Unexpected message role.') }}
|
| 145 |
+
{%- endif %}
|
| 146 |
+
{%- endfor %}
|
| 147 |
+
{%- if add_generation_prompt %}
|
| 148 |
+
{{- '<|im_start|>assistant\n' }}
|
| 149 |
+
{%- if enable_thinking is defined and enable_thinking is true %}
|
| 150 |
+
{{- '<think>\n' }}
|
| 151 |
+
{%- else %}
|
| 152 |
+
{{- '<think>\n\n</think>\n\n' }}
|
| 153 |
+
{%- endif %}
|
| 154 |
+
{%- endif %}
|
config.json
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Qwen3_5ForConditionalGeneration"
|
| 4 |
+
],
|
| 5 |
+
"dtype": "bfloat16",
|
| 6 |
+
"image_token_id": 248056,
|
| 7 |
+
"model_type": "qwen3_5",
|
| 8 |
+
"text_config": {
|
| 9 |
+
"attention_bias": false,
|
| 10 |
+
"attention_dropout": 0.0,
|
| 11 |
+
"attn_output_gate": true,
|
| 12 |
+
"bos_token_id": null,
|
| 13 |
+
"dtype": "bfloat16",
|
| 14 |
+
"eos_token_id": 248044,
|
| 15 |
+
"full_attention_interval": 4,
|
| 16 |
+
"head_dim": 256,
|
| 17 |
+
"hidden_act": "silu",
|
| 18 |
+
"hidden_size": 4096,
|
| 19 |
+
"initializer_range": 0.02,
|
| 20 |
+
"intermediate_size": 12288,
|
| 21 |
+
"layer_types": [
|
| 22 |
+
"linear_attention",
|
| 23 |
+
"linear_attention",
|
| 24 |
+
"linear_attention",
|
| 25 |
+
"full_attention",
|
| 26 |
+
"linear_attention",
|
| 27 |
+
"linear_attention",
|
| 28 |
+
"linear_attention",
|
| 29 |
+
"full_attention",
|
| 30 |
+
"linear_attention",
|
| 31 |
+
"linear_attention",
|
| 32 |
+
"linear_attention",
|
| 33 |
+
"full_attention",
|
| 34 |
+
"linear_attention",
|
| 35 |
+
"linear_attention",
|
| 36 |
+
"linear_attention",
|
| 37 |
+
"full_attention",
|
| 38 |
+
"linear_attention",
|
| 39 |
+
"linear_attention",
|
| 40 |
+
"linear_attention",
|
| 41 |
+
"full_attention",
|
| 42 |
+
"linear_attention",
|
| 43 |
+
"linear_attention",
|
| 44 |
+
"linear_attention",
|
| 45 |
+
"full_attention",
|
| 46 |
+
"linear_attention",
|
| 47 |
+
"linear_attention",
|
| 48 |
+
"linear_attention",
|
| 49 |
+
"full_attention",
|
| 50 |
+
"linear_attention",
|
| 51 |
+
"linear_attention",
|
| 52 |
+
"linear_attention",
|
| 53 |
+
"full_attention"
|
| 54 |
+
],
|
| 55 |
+
"linear_conv_kernel_dim": 4,
|
| 56 |
+
"linear_key_head_dim": 128,
|
| 57 |
+
"linear_num_key_heads": 16,
|
| 58 |
+
"linear_num_value_heads": 32,
|
| 59 |
+
"linear_value_head_dim": 128,
|
| 60 |
+
"mamba_ssm_dtype": "float32",
|
| 61 |
+
"max_position_embeddings": 262144,
|
| 62 |
+
"mlp_only_layers": [],
|
| 63 |
+
"model_type": "qwen3_5_text",
|
| 64 |
+
"mtp_num_hidden_layers": 1,
|
| 65 |
+
"mtp_use_dedicated_embeddings": false,
|
| 66 |
+
"num_attention_heads": 16,
|
| 67 |
+
"num_hidden_layers": 32,
|
| 68 |
+
"num_key_value_heads": 4,
|
| 69 |
+
"pad_token_id": null,
|
| 70 |
+
"partial_rotary_factor": 0.25,
|
| 71 |
+
"rms_norm_eps": 1e-06,
|
| 72 |
+
"rope_parameters": {
|
| 73 |
+
"mrope_interleaved": true,
|
| 74 |
+
"mrope_section": [
|
| 75 |
+
11,
|
| 76 |
+
11,
|
| 77 |
+
10
|
| 78 |
+
],
|
| 79 |
+
"partial_rotary_factor": 0.25,
|
| 80 |
+
"rope_theta": 10000000,
|
| 81 |
+
"rope_type": "default"
|
| 82 |
+
},
|
| 83 |
+
"tie_word_embeddings": false,
|
| 84 |
+
"use_cache": true,
|
| 85 |
+
"vocab_size": 248078
|
| 86 |
+
},
|
| 87 |
+
"tie_word_embeddings": false,
|
| 88 |
+
"transformers_version": "5.2.0",
|
| 89 |
+
"video_token_id": 248057,
|
| 90 |
+
"vision_config": {
|
| 91 |
+
"deepstack_visual_indexes": [],
|
| 92 |
+
"depth": 27,
|
| 93 |
+
"dtype": "bfloat16",
|
| 94 |
+
"hidden_act": "gelu_pytorch_tanh",
|
| 95 |
+
"hidden_size": 1152,
|
| 96 |
+
"in_channels": 3,
|
| 97 |
+
"initializer_range": 0.02,
|
| 98 |
+
"intermediate_size": 4304,
|
| 99 |
+
"model_type": "qwen3_5",
|
| 100 |
+
"num_heads": 16,
|
| 101 |
+
"num_position_embeddings": 2304,
|
| 102 |
+
"out_hidden_size": 4096,
|
| 103 |
+
"patch_size": 16,
|
| 104 |
+
"spatial_merge_size": 2,
|
| 105 |
+
"temporal_patch_size": 2
|
| 106 |
+
},
|
| 107 |
+
"vision_end_token_id": 248054,
|
| 108 |
+
"vision_start_token_id": 248053,
|
| 109 |
+
"auto_map": {
|
| 110 |
+
"AutoModel": "modeling_wemm_embedding.WeMMEmbedding",
|
| 111 |
+
"AutoModelForCausalLM": "modeling_wemm_embedding.WeMMEmbedding"
|
| 112 |
+
},
|
| 113 |
+
"matryoshka_dimensions": [
|
| 114 |
+
64,
|
| 115 |
+
128,
|
| 116 |
+
256,
|
| 117 |
+
512,
|
| 118 |
+
1024,
|
| 119 |
+
2048,
|
| 120 |
+
4096
|
| 121 |
+
]
|
| 122 |
+
}
|
config_sentence_transformers.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"__version__": {
|
| 3 |
+
"pytorch": "2.11.0+cu128",
|
| 4 |
+
"sentence_transformers": "5.7.0",
|
| 5 |
+
"transformers": "5.2.0"
|
| 6 |
+
},
|
| 7 |
+
"default_prompt_name": null,
|
| 8 |
+
"model_type": "SentenceTransformer",
|
| 9 |
+
"prompts": {},
|
| 10 |
+
"similarity_fn_name": "cosine"
|
| 11 |
+
}
|
embedding_chat_template.jinja
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{# Direct-tokenization template for vLLM 0.27 embedding requests.
|
| 2 |
+
Qwen's processor post-processor removes the role newline before images,
|
| 3 |
+
keeps it before text/video, and appends <embedding> without a preceding
|
| 4 |
+
newline. vLLM 0.27 adds each frame's vision boundaries itself, so video
|
| 5 |
+
uses a bare <video_pad> placeholder. #}
|
| 6 |
+
{%- for message in messages %}
|
| 7 |
+
{{- '<|im_start|>' + message.role }}
|
| 8 |
+
{%- if message.content is string %}
|
| 9 |
+
{{- '\n' + message.content }}
|
| 10 |
+
{%- else %}
|
| 11 |
+
{%- for item in message.content %}
|
| 12 |
+
{%- if item.type in ['image', 'image_url'] or 'image' in item or 'image_url' in item %}
|
| 13 |
+
{{- '<|vision_start|><|image_pad|><|vision_end|>' }}
|
| 14 |
+
{%- elif item.type in ['video', 'video_url'] or 'video' in item or 'video_url' in item %}
|
| 15 |
+
{{- '\n<|video_pad|>' }}
|
| 16 |
+
{%- elif item.type == 'text' or 'text' in item %}
|
| 17 |
+
{{- ('\n' if loop.first else '') + item.text }}
|
| 18 |
+
{%- else %}
|
| 19 |
+
{{- raise_exception('Unsupported embedding content type') }}
|
| 20 |
+
{%- endif %}
|
| 21 |
+
{%- endfor %}
|
| 22 |
+
{%- endif %}
|
| 23 |
+
{{- '<|im_end|>' }}
|
| 24 |
+
{%- if not loop.last %}
|
| 25 |
+
{{- '\n' }}
|
| 26 |
+
{%- endif %}
|
| 27 |
+
{%- endfor %}
|
| 28 |
+
{{- '<embedding>' }}
|
evaluation/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Reproduce the local MMEB evaluation
|
| 2 |
+
|
| 3 |
+
This is a snapshot of the local Nexus MMEB evaluator on 2026-10-15, packaged with tencent/WeMM-Embedding-9B weights. These are the upstream released models, not local training checkpoints.
|
| 4 |
+
|
| 5 |
+
## Setup and execution
|
| 6 |
+
|
| 7 |
+
Use Python 3.10 and a CUDA-capable PyTorch environment. Install dependencies:
|
| 8 |
+
|
| 9 |
+
```bash
|
| 10 |
+
python -m pip install -r evaluation/requirements.txt
|
| 11 |
+
DATA_BASEDIR=/path/to/vlm2vec_eval NPROC=8 bash evaluation/run_eval.sh image_retrieval
|
| 12 |
+
DATA_BASEDIR=/path/to/vlm2vec_eval NPROC=8 bash evaluation/run_eval.sh video_ret_5
|
| 13 |
+
DATA_BASEDIR=/path/to/vlm2vec_eval NPROC=8 bash evaluation/run_eval.sh moment_retrieval
|
| 14 |
+
DATA_BASEDIR=/path/to/vlm2vec_eval NPROC=8 bash evaluation/run_eval.sh visdoc_full
|
| 15 |
+
```
|
| 16 |
+
|
| 17 |
+
Each process loads a full model on its GPU; NPROC is data parallelism, not model sharding. Reduce BATCH_SIZE if memory is insufficient. One GPU is the default. MODEL_PATH, OUTPUT_DIR, GENERATED_MEDIA_BASEDIR, MASTER_PORT, BATCH_SIZE and FORCE_RECOMPUTE may be overridden.
|
| 18 |
+
|
| 19 |
+
The launcher sets NEXUS_WEMM_OFFICIAL=1 before importing Nexus. WeMM uses its released custom model, user-only prompt, processor settings and embedding() path. Qwen uses the Nexus Qwen-style conversation and last-token pooling. Both normalize embeddings. Task instructions, candidate selection, local/global evaluation, video sampling and metrics are determined by the included dataset parsers and YAML files; preserve them when comparing results. The video configuration uses 64 frames. The evaluator writes *_score.json files into OUTPUT_DIR.
|
| 20 |
+
|
| 21 |
+
## Data requirements
|
| 22 |
+
|
| 23 |
+
Benchmark media are not included in this model package. DATA_BASEDIR must contain image-tasks/, video-tasks/ and visdoc-tasks/ as required by the YAMLs. The dataset parsers download annotations/corpora from their configured Hugging Face sources; see nexus/Nexus/evaluation/mmeb_v2/constant.py and data/datasets/. Visual-document page images can be generated under GENERATED_MEDIA_BASEDIR, which must be writable. Internet access or an existing dataset cache is required.
|
| 24 |
+
|
| 25 |
+
## Runtime snapshot
|
| 26 |
+
|
| 27 |
+
The original runtime uses transformers 5.6.0.dev0, installed from a development source archive without a recorded Git commit. To avoid depending on a moving main branch, vendor/ contains the installed Transformers source and distribution metadata (excluding bytecode and install records, with an upstream test token redacted in testing_utils.py). The launcher prepends this runtime snapshot to PYTHONPATH. Other installed dependency versions are recorded in requirements.txt. The upstream model's recommended environment may differ; see UPSTREAM_README.md. Vendored source retains its upstream licensing.
|
| 28 |
+
|
| 29 |
+
## Scope of verification
|
| 30 |
+
|
| 31 |
+
Packaging checks include weight shard references, JSON/YAML parsing, source compilation, evaluator CLI imports, and file hashes. No new GPU benchmark run is performed during packaging. This package does not claim to reproduce any particular score without the original benchmark data and runtime hardware.
|
evaluation/configs/image_retrieval.yaml
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# RET i -> t
|
| 2 |
+
MSCOCO_i2t:
|
| 3 |
+
dataset_parser: image_i2t
|
| 4 |
+
dataset_name: MSCOCO_i2t
|
| 5 |
+
dataset_split: test
|
| 6 |
+
image_root: image-tasks/
|
| 7 |
+
eval_type: local
|
| 8 |
+
VisualNews_i2t:
|
| 9 |
+
dataset_parser: image_i2t
|
| 10 |
+
dataset_name: VisualNews_i2t
|
| 11 |
+
dataset_split: test
|
| 12 |
+
image_root: image-tasks/
|
| 13 |
+
eval_type: local
|
| 14 |
+
# RET t -> i
|
| 15 |
+
VisDial:
|
| 16 |
+
dataset_parser: image_t2i
|
| 17 |
+
dataset_name: VisDial
|
| 18 |
+
dataset_split: test
|
| 19 |
+
image_root: image-tasks/
|
| 20 |
+
eval_type: local
|
| 21 |
+
MSCOCO_t2i:
|
| 22 |
+
dataset_parser: image_t2i
|
| 23 |
+
dataset_name: MSCOCO_t2i
|
| 24 |
+
dataset_split: test
|
| 25 |
+
image_root: image-tasks/
|
| 26 |
+
eval_type: local
|
| 27 |
+
VisualNews_t2i:
|
| 28 |
+
dataset_parser: image_t2i
|
| 29 |
+
dataset_name: VisualNews_t2i
|
| 30 |
+
dataset_split: test
|
| 31 |
+
image_root: image-tasks/
|
| 32 |
+
eval_type: local
|
| 33 |
+
WebQA:
|
| 34 |
+
dataset_parser: image_t2i
|
| 35 |
+
dataset_name: WebQA
|
| 36 |
+
dataset_split: test
|
| 37 |
+
image_root: image-tasks/
|
| 38 |
+
eval_type: local
|
| 39 |
+
EDIS:
|
| 40 |
+
dataset_parser: image_t2i
|
| 41 |
+
dataset_name: EDIS
|
| 42 |
+
dataset_split: test
|
| 43 |
+
image_root: image-tasks/
|
| 44 |
+
eval_type: local
|
| 45 |
+
Wiki-SS-NQ:
|
| 46 |
+
dataset_parser: image_t2i
|
| 47 |
+
dataset_name: Wiki-SS-NQ
|
| 48 |
+
dataset_split: test
|
| 49 |
+
image_root: image-tasks/
|
| 50 |
+
eval_type: local
|
| 51 |
+
# RET i -> i
|
| 52 |
+
CIRR:
|
| 53 |
+
dataset_parser: image_i2i_vg
|
| 54 |
+
dataset_name: CIRR
|
| 55 |
+
dataset_split: test
|
| 56 |
+
image_root: image-tasks/
|
| 57 |
+
eval_type: local
|
| 58 |
+
NIGHTS:
|
| 59 |
+
dataset_parser: image_i2i_vg
|
| 60 |
+
dataset_name: NIGHTS
|
| 61 |
+
dataset_split: test
|
| 62 |
+
image_root: image-tasks/
|
| 63 |
+
eval_type: local
|
| 64 |
+
OVEN:
|
| 65 |
+
dataset_parser: image_i2i_vg
|
| 66 |
+
dataset_name: OVEN
|
| 67 |
+
dataset_split: test
|
| 68 |
+
image_root: image-tasks/
|
| 69 |
+
eval_type: local
|
| 70 |
+
FashionIQ:
|
| 71 |
+
dataset_parser: image_i2i_vg
|
| 72 |
+
dataset_name: FashionIQ
|
| 73 |
+
dataset_split: test
|
| 74 |
+
image_root: image-tasks/
|
| 75 |
+
eval_type: local
|
evaluation/configs/moment_retrieval.yaml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
QVHighlight:
|
| 2 |
+
dataset_parser: moment_retrieval
|
| 3 |
+
dataset_name: QVHighlight
|
| 4 |
+
data_path: video-tasks/data/qvhighlight.jsonl
|
| 5 |
+
video_root: ""
|
| 6 |
+
clip_root: ""
|
| 7 |
+
frame_root: video-tasks/frames/video_mret/QVHighlight
|
| 8 |
+
num_negative_clips: 9
|
| 9 |
+
max_video_frames_saved: 64
|
| 10 |
+
max_clip_frames_saved: 8
|
| 11 |
+
num_video_frames: 64
|
| 12 |
+
num_clip_frames: 8
|
| 13 |
+
eval_type: local
|
| 14 |
+
Charades-STA:
|
| 15 |
+
dataset_parser: moment_retrieval
|
| 16 |
+
dataset_name: Charades-STA
|
| 17 |
+
data_path: video-tasks/data/charades_sta.jsonl
|
| 18 |
+
video_root: ""
|
| 19 |
+
clip_root: ""
|
| 20 |
+
frame_root: video-tasks/frames/video_mret/Charades-STA
|
| 21 |
+
num_negative_clips: 9
|
| 22 |
+
max_video_frames_saved: 64
|
| 23 |
+
max_clip_frames_saved: 8
|
| 24 |
+
num_video_frames: 64
|
| 25 |
+
num_clip_frames: 8
|
| 26 |
+
eval_type: local
|
| 27 |
+
MomentSeeker:
|
| 28 |
+
dataset_parser: momentseeker
|
| 29 |
+
dataset_name: MomentSeeker
|
| 30 |
+
video_root: ""
|
| 31 |
+
clip_root: ""
|
| 32 |
+
frame_root: video-tasks/frames/video_mret/MomentSeeker
|
| 33 |
+
num_negative_clips: 9
|
| 34 |
+
num_video_frames: 64
|
| 35 |
+
eval_type: local
|
evaluation/configs/video_ret_5.yaml
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MSR-VTT:
|
| 2 |
+
dataset_parser: msrvtt
|
| 3 |
+
dataset_name: MSR-VTT
|
| 4 |
+
video_root: video-tasks/videos/video_ret/MSR-VTT/
|
| 5 |
+
frame_root: video-tasks/frames/video_ret/data/ziyan/video_retrieval/MSR-VTT/frames
|
| 6 |
+
num_sample_per_subset: None
|
| 7 |
+
max_frames_saved: 64
|
| 8 |
+
num_frames: 64
|
| 9 |
+
eval_type: global
|
| 10 |
+
MSVD:
|
| 11 |
+
dataset_parser: msvd
|
| 12 |
+
dataset_name: MSVD
|
| 13 |
+
video_root: video-tasks/videos/video_ret/MSVD/
|
| 14 |
+
frame_root: video-tasks/frames/video_ret/data/ziyan/video_retrieval/MSVD/frames
|
| 15 |
+
num_sample_per_subset: None
|
| 16 |
+
max_frames_saved: 64
|
| 17 |
+
num_frames: 64
|
| 18 |
+
eval_type: global
|
| 19 |
+
DiDeMo:
|
| 20 |
+
dataset_parser: didemo
|
| 21 |
+
dataset_name: DiDeMo
|
| 22 |
+
video_root: video-tasks/videos/video_ret/DiDeMo/
|
| 23 |
+
frame_root: video-tasks/frames/video_ret/data/ziyan/video_retrieval/DiDeMo/frames
|
| 24 |
+
num_sample_per_subset: None
|
| 25 |
+
max_frames_saved: 64
|
| 26 |
+
num_frames: 64
|
| 27 |
+
eval_type: global
|
| 28 |
+
YouCook2:
|
| 29 |
+
dataset_parser: youcook2
|
| 30 |
+
dataset_name: YouCook2
|
| 31 |
+
video_root: video-tasks/videos/video_ret/YouCook2/
|
| 32 |
+
frame_root: video-tasks/frames/video_ret/data/ziyan/video_retrieval/YouCook2/frames
|
| 33 |
+
num_sample_per_subset: None
|
| 34 |
+
max_frames_saved: 64
|
| 35 |
+
num_frames: 64
|
| 36 |
+
eval_type: global
|
| 37 |
+
VATEX:
|
| 38 |
+
dataset_parser: vatex
|
| 39 |
+
dataset_name: VATEX
|
| 40 |
+
video_root: video-tasks/videos/video_ret/VATEX/
|
| 41 |
+
frame_root: video-tasks/frames/video_ret/data/ziyan/video_retrieval/VATEX/frames
|
| 42 |
+
num_sample_per_subset: None
|
| 43 |
+
max_frames_saved: 64
|
| 44 |
+
num_frames: 64
|
| 45 |
+
eval_type: global
|
evaluation/configs/visdoc_full.yaml
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ViDoRe_arxivqa:
|
| 2 |
+
dataset_parser: vidore
|
| 3 |
+
dataset_name: ViDoRe_arxivqa
|
| 4 |
+
dataset_split: test
|
| 5 |
+
image_root: visdoc-tasks/ViDoRe_arxivqa
|
| 6 |
+
eval_type: global
|
| 7 |
+
ViDoRe_docvqa:
|
| 8 |
+
dataset_parser: vidore
|
| 9 |
+
dataset_name: ViDoRe_docvqa
|
| 10 |
+
dataset_split: test
|
| 11 |
+
image_root: visdoc-tasks/ViDoRe_docvqa
|
| 12 |
+
eval_type: global
|
| 13 |
+
ViDoRe_infovqa:
|
| 14 |
+
dataset_parser: vidore
|
| 15 |
+
dataset_name: ViDoRe_infovqa
|
| 16 |
+
dataset_split: test
|
| 17 |
+
image_root: visdoc-tasks/ViDoRe_infovqa
|
| 18 |
+
eval_type: global
|
| 19 |
+
ViDoRe_tabfquad:
|
| 20 |
+
dataset_parser: vidore
|
| 21 |
+
dataset_name: ViDoRe_tabfquad
|
| 22 |
+
dataset_split: test
|
| 23 |
+
image_root: visdoc-tasks/ViDoRe_tabfquad
|
| 24 |
+
eval_type: global
|
| 25 |
+
ViDoRe_tatdqa:
|
| 26 |
+
dataset_parser: vidore
|
| 27 |
+
dataset_name: ViDoRe_tatdqa
|
| 28 |
+
dataset_split: test
|
| 29 |
+
image_root: visdoc-tasks/ViDoRe_tatdqa
|
| 30 |
+
eval_type: global
|
| 31 |
+
ViDoRe_shiftproject:
|
| 32 |
+
dataset_parser: vidore
|
| 33 |
+
dataset_name: ViDoRe_shiftproject
|
| 34 |
+
dataset_split: test
|
| 35 |
+
image_root: visdoc-tasks/ViDoRe_shiftproject
|
| 36 |
+
eval_type: global
|
| 37 |
+
ViDoRe_syntheticDocQA_artificial_intelligence:
|
| 38 |
+
dataset_parser: vidore
|
| 39 |
+
dataset_name: ViDoRe_syntheticDocQA_artificial_intelligence
|
| 40 |
+
dataset_split: test
|
| 41 |
+
image_root: visdoc-tasks/ViDoRe_syntheticDocQA_artificial_intelligence
|
| 42 |
+
eval_type: global
|
| 43 |
+
ViDoRe_syntheticDocQA_energy:
|
| 44 |
+
dataset_parser: vidore
|
| 45 |
+
dataset_name: ViDoRe_syntheticDocQA_energy
|
| 46 |
+
dataset_split: test
|
| 47 |
+
image_root: visdoc-tasks/ViDoRe_syntheticDocQA_energy
|
| 48 |
+
eval_type: global
|
| 49 |
+
ViDoRe_syntheticDocQA_government_reports:
|
| 50 |
+
dataset_parser: vidore
|
| 51 |
+
dataset_name: ViDoRe_syntheticDocQA_government_reports
|
| 52 |
+
dataset_split: test
|
| 53 |
+
image_root: visdoc-tasks/ViDoRe_syntheticDocQA_government_reports
|
| 54 |
+
eval_type: global
|
| 55 |
+
ViDoRe_syntheticDocQA_healthcare_industry:
|
| 56 |
+
dataset_parser: vidore
|
| 57 |
+
dataset_name: ViDoRe_syntheticDocQA_healthcare_industry
|
| 58 |
+
dataset_split: test
|
| 59 |
+
image_root: visdoc-tasks/ViDoRe_syntheticDocQA_healthcare_industry
|
| 60 |
+
eval_type: global
|
| 61 |
+
ViDoRe_esg_reports_human_labeled_v2:
|
| 62 |
+
dataset_parser: vidore
|
| 63 |
+
dataset_name: ViDoRe_esg_reports_human_labeled_v2
|
| 64 |
+
dataset_split: test
|
| 65 |
+
image_root: visdoc-tasks/esg_reports_human_labeled_v2
|
| 66 |
+
eval_type: global
|
| 67 |
+
ViDoRe_biomedical_lectures_v2_multilingual:
|
| 68 |
+
dataset_parser: vidore
|
| 69 |
+
dataset_name: ViDoRe_biomedical_lectures_v2_multilingual
|
| 70 |
+
dataset_split: test
|
| 71 |
+
image_root: visdoc-tasks/biomedical_lectures_v2_multilingual
|
| 72 |
+
eval_type: global
|
| 73 |
+
ViDoRe_economics_reports_v2_multilingual:
|
| 74 |
+
dataset_parser: vidore
|
| 75 |
+
dataset_name: ViDoRe_economics_reports_v2_multilingual
|
| 76 |
+
dataset_split: test
|
| 77 |
+
image_root: visdoc-tasks/economics_reports_v2_multilingual
|
| 78 |
+
eval_type: global
|
| 79 |
+
ViDoRe_esg_reports_v2_multilingual:
|
| 80 |
+
dataset_parser: vidore
|
| 81 |
+
dataset_name: ViDoRe_esg_reports_v2_multilingual
|
| 82 |
+
dataset_split: test
|
| 83 |
+
image_root: visdoc-tasks/esg_reports_v2_multilingual
|
| 84 |
+
eval_type: global
|
| 85 |
+
VisRAG_ArxivQA:
|
| 86 |
+
dataset_parser: visrag
|
| 87 |
+
dataset_name: VisRAG_ArxivQA
|
| 88 |
+
dataset_split: test
|
| 89 |
+
image_root: visdoc-tasks/VisRAG_ArxivQA
|
| 90 |
+
eval_type: global
|
| 91 |
+
VisRAG_ChartQA:
|
| 92 |
+
dataset_parser: visrag
|
| 93 |
+
dataset_name: VisRAG_ChartQA
|
| 94 |
+
dataset_split: test
|
| 95 |
+
image_root: visdoc-tasks/VisRAG_ChartQA
|
| 96 |
+
eval_type: global
|
| 97 |
+
VisRAG_MP-DocVQA:
|
| 98 |
+
dataset_parser: visrag
|
| 99 |
+
dataset_name: VisRAG_MP-DocVQA
|
| 100 |
+
dataset_split: test
|
| 101 |
+
image_root: visdoc-tasks/VisRAG_MP-DocVQA
|
| 102 |
+
eval_type: global
|
| 103 |
+
VisRAG_SlideVQA:
|
| 104 |
+
dataset_parser: visrag
|
| 105 |
+
dataset_name: VisRAG_SlideVQA
|
| 106 |
+
dataset_split: test
|
| 107 |
+
image_root: visdoc-tasks/VisRAG_SlideVQA
|
| 108 |
+
eval_type: global
|
| 109 |
+
VisRAG_InfoVQA:
|
| 110 |
+
dataset_parser: visrag
|
| 111 |
+
dataset_name: VisRAG_InfoVQA
|
| 112 |
+
dataset_split: test
|
| 113 |
+
image_root: visdoc-tasks/VisRAG_InfoVQA
|
| 114 |
+
eval_type: global
|
| 115 |
+
VisRAG_PlotQA:
|
| 116 |
+
dataset_parser: visrag
|
| 117 |
+
dataset_name: VisRAG_PlotQA
|
| 118 |
+
dataset_split: test
|
| 119 |
+
image_root: visdoc-tasks/VisRAG_PlotQA
|
| 120 |
+
eval_type: global
|
| 121 |
+
ViDoSeek-page:
|
| 122 |
+
dataset_parser: vidore
|
| 123 |
+
dataset_name: ViDoSeek-page
|
| 124 |
+
dataset_split: test
|
| 125 |
+
image_root: visdoc-tasks/ViDoSeek-page
|
| 126 |
+
eval_type: global
|
| 127 |
+
ViDoSeek-doc:
|
| 128 |
+
dataset_parser: vidore
|
| 129 |
+
dataset_name: ViDoSeek-doc
|
| 130 |
+
dataset_split: test
|
| 131 |
+
image_root: visdoc-tasks/ViDoSeek-doc
|
| 132 |
+
eval_type: global
|
| 133 |
+
MMLongBench-page:
|
| 134 |
+
dataset_parser: vidore
|
| 135 |
+
dataset_name: MMLongBench-page
|
| 136 |
+
dataset_split: test
|
| 137 |
+
image_root: visdoc-tasks/MMLongBench-page
|
| 138 |
+
eval_type: global
|
| 139 |
+
MMLongBench-doc:
|
| 140 |
+
dataset_parser: vidore
|
| 141 |
+
dataset_name: MMLongBench-doc
|
| 142 |
+
dataset_split: test
|
| 143 |
+
image_root: visdoc-tasks/MMLongBench-doc
|
| 144 |
+
eval_type: global
|
evaluation/nexus/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright [2024] [USTC]
|
| 2 |
+
|
| 3 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
you may not use this file except in compliance with the License.
|
| 5 |
+
You may obtain a copy of the License at
|
| 6 |
+
|
| 7 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
|
| 9 |
+
Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
See the License for the specific language governing permissions and
|
| 13 |
+
limitations under the License.
|
| 14 |
+
|
| 15 |
+
Additional Terms:
|
| 16 |
+
1. Users are prohibited from utilizing Nexus to provide any commercial services to Third-Party Entities without obtaining explicit prior written authorization from USTC.
|
| 17 |
+
|
| 18 |
+
Definition of "Third-Party Entities":
|
| 19 |
+
Refers to any enterprises, social organizations, institutions, or other
|
| 20 |
+
legal entities not affiliated with the individual or organizational entity
|
| 21 |
+
that utilizes the Nexus source code, excluding the said user and its
|
| 22 |
+
directly controlled subsidiaries.
|
evaluation/nexus/Nexus/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from importlib import import_module
|
| 2 |
+
|
| 3 |
+
__all__ = [
|
| 4 |
+
"AbsInferenceArguments",
|
| 5 |
+
"TextEmbedder",
|
| 6 |
+
"BaseEmbedderInferenceEngine",
|
| 7 |
+
"TextReranker",
|
| 8 |
+
"BaseRerankerInferenceEngine",
|
| 9 |
+
"MultiModalEmbedder",
|
| 10 |
+
"MultimodalEmbedder",
|
| 11 |
+
]
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def __getattr__(name):
|
| 15 |
+
if name == "AbsInferenceArguments":
|
| 16 |
+
module = import_module("Nexus.abc")
|
| 17 |
+
return getattr(module, name)
|
| 18 |
+
if name in {"TextEmbedder", "BaseEmbedderInferenceEngine", "TextReranker", "BaseRerankerInferenceEngine"}:
|
| 19 |
+
module = import_module("Nexus.inference")
|
| 20 |
+
return getattr(module, name)
|
| 21 |
+
if name in {"MultiModalEmbedder", "MultimodalEmbedder"}:
|
| 22 |
+
module = import_module("Nexus.inference.embedder.multimodal_retrieval")
|
| 23 |
+
return getattr(module, name, getattr(module, "MultimodalEmbedder"))
|
| 24 |
+
raise AttributeError(f"module 'Nexus' has no attribute {name!r}")
|
evaluation/nexus/Nexus/abc/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from importlib import import_module
|
| 2 |
+
|
| 3 |
+
__all__ = ["AbsInferenceArguments"]
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def __getattr__(name):
|
| 7 |
+
if name == "AbsInferenceArguments":
|
| 8 |
+
module = import_module("Nexus.abc.inference.arguments")
|
| 9 |
+
return getattr(module, name)
|
| 10 |
+
raise AttributeError(f"module 'Nexus.abc' has no attribute {name!r}")
|
evaluation/nexus/Nexus/abc/arguments.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
import yaml
|
| 4 |
+
import logging
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
from typing import Any, Union, get_args, get_origin
|
| 7 |
+
from dataclasses import dataclass, asdict, fields
|
| 8 |
+
|
| 9 |
+
logger = logging.getLogger(__name__)
|
| 10 |
+
|
| 11 |
+
DEFINITE_LOCAL_PATH_KEYS = {
|
| 12 |
+
"train_data",
|
| 13 |
+
"eval_data",
|
| 14 |
+
"output_dir",
|
| 15 |
+
"dataset_dir",
|
| 16 |
+
"media_root",
|
| 17 |
+
"image_root",
|
| 18 |
+
"video_root",
|
| 19 |
+
"cache_path",
|
| 20 |
+
"cache_dir",
|
| 21 |
+
"corpus_embd_save_dir",
|
| 22 |
+
"eval_output_dir",
|
| 23 |
+
"eval_output_path",
|
| 24 |
+
}
|
| 25 |
+
MAYBE_LOCAL_OR_REMOTE_KEYS = {
|
| 26 |
+
"model_name_or_path",
|
| 27 |
+
"processor_name_or_path",
|
| 28 |
+
"embedder_name_or_path",
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def init_argument(type_, x):
|
| 33 |
+
if x is None:
|
| 34 |
+
return None
|
| 35 |
+
origin = get_origin(type_)
|
| 36 |
+
args = get_args(type_)
|
| 37 |
+
|
| 38 |
+
if type_ in [Any, object]:
|
| 39 |
+
return x
|
| 40 |
+
|
| 41 |
+
if origin is Union:
|
| 42 |
+
for tmp_type_ in args:
|
| 43 |
+
if tmp_type_ is type(None):
|
| 44 |
+
continue
|
| 45 |
+
try:
|
| 46 |
+
return init_argument(tmp_type_, x)
|
| 47 |
+
except TypeError:
|
| 48 |
+
continue
|
| 49 |
+
raise TypeError(f"Failed to init argument {x} ({type(x)}) to {type_}.")
|
| 50 |
+
|
| 51 |
+
if isinstance(x, dict):
|
| 52 |
+
try:
|
| 53 |
+
# is subclass of AbsArguments
|
| 54 |
+
return type_.from_dict(x)
|
| 55 |
+
except:
|
| 56 |
+
# dict
|
| 57 |
+
return dict(x)
|
| 58 |
+
elif origin in [list, tuple]:
|
| 59 |
+
item_type = args[0] if len(args) > 0 else Any
|
| 60 |
+
converted = [init_argument(item_type, i) for i in x]
|
| 61 |
+
return converted if origin is list else tuple(converted)
|
| 62 |
+
elif isinstance(x, list):
|
| 63 |
+
return list(x)
|
| 64 |
+
else:
|
| 65 |
+
tmp_x = None
|
| 66 |
+
try:
|
| 67 |
+
if isinstance(type_, type) and isinstance(x, type_):
|
| 68 |
+
tmp_x = x
|
| 69 |
+
else:
|
| 70 |
+
tmp_x = type_(x)
|
| 71 |
+
logger.warning(f"Init argument: Convert {x} ({type(x)}) to {tmp_x} ({type(tmp_x)}).")
|
| 72 |
+
except:
|
| 73 |
+
try:
|
| 74 |
+
for tmp_type_ in args:
|
| 75 |
+
if tmp_type_ is None:
|
| 76 |
+
continue
|
| 77 |
+
if tmp_type_ is type(None):
|
| 78 |
+
continue
|
| 79 |
+
if isinstance(x, tmp_type_):
|
| 80 |
+
tmp_x = x
|
| 81 |
+
break
|
| 82 |
+
else:
|
| 83 |
+
tmp_x = tmp_type_(x)
|
| 84 |
+
logger.warning(f"Init argument: Convert {x} ({type(x)}) to {tmp_x} ({type(tmp_x)}).")
|
| 85 |
+
except (AttributeError, TypeError):
|
| 86 |
+
raise TypeError(f"Failed to init argument {x} ({type(x)}) to {type_}.")
|
| 87 |
+
if tmp_x is None:
|
| 88 |
+
raise TypeError(f"Failed to init argument {x} ({type(x)}) to {type_}.")
|
| 89 |
+
return tmp_x
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def _looks_like_remote_reference(value: str) -> bool:
|
| 93 |
+
return "://" in value
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def _resolve_local_path_value(value: str, base_dir: Path, force: bool = False) -> str:
|
| 97 |
+
if value in [None, ""]:
|
| 98 |
+
return value
|
| 99 |
+
|
| 100 |
+
expanded = os.path.expanduser(value)
|
| 101 |
+
if os.path.isabs(expanded) or _looks_like_remote_reference(expanded):
|
| 102 |
+
return expanded
|
| 103 |
+
|
| 104 |
+
if not force:
|
| 105 |
+
candidate = base_dir / expanded
|
| 106 |
+
if not (expanded.startswith(".") or expanded.startswith("..") or candidate.exists()):
|
| 107 |
+
return value
|
| 108 |
+
|
| 109 |
+
return str((base_dir / expanded).resolve())
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def _resolve_config_paths(config_dict: dict, base_dir: Path) -> dict:
|
| 113 |
+
resolved = dict(config_dict)
|
| 114 |
+
for key, value in list(resolved.items()):
|
| 115 |
+
if value in [None, ""]:
|
| 116 |
+
continue
|
| 117 |
+
|
| 118 |
+
if key in DEFINITE_LOCAL_PATH_KEYS:
|
| 119 |
+
if isinstance(value, list):
|
| 120 |
+
resolved[key] = [_resolve_local_path_value(item, base_dir, force=True) for item in value]
|
| 121 |
+
elif isinstance(value, str):
|
| 122 |
+
resolved[key] = _resolve_local_path_value(value, base_dir, force=True)
|
| 123 |
+
continue
|
| 124 |
+
|
| 125 |
+
if key in MAYBE_LOCAL_OR_REMOTE_KEYS and isinstance(value, str):
|
| 126 |
+
resolved[key] = _resolve_local_path_value(value, base_dir, force=False)
|
| 127 |
+
return resolved
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
@dataclass
|
| 131 |
+
class AbsArguments:
|
| 132 |
+
|
| 133 |
+
def to_dict(self):
|
| 134 |
+
return asdict(self)
|
| 135 |
+
|
| 136 |
+
def to_json(self, save_path: Union[str, Path], overwrite: bool = False):
|
| 137 |
+
if isinstance(save_path, str):
|
| 138 |
+
save_path = Path(save_path)
|
| 139 |
+
|
| 140 |
+
if save_path.exists() and not overwrite:
|
| 141 |
+
raise FileExistsError(f"{save_path} already exists. Set `overwrite=True` to overwrite the file.")
|
| 142 |
+
|
| 143 |
+
save_path.parent.mkdir(parents=True, exist_ok=True)
|
| 144 |
+
with open(save_path, "w", encoding="utf-8") as f:
|
| 145 |
+
json.dump(self.to_dict(), f, indent=4)
|
| 146 |
+
|
| 147 |
+
@classmethod
|
| 148 |
+
def from_dict(cls, _dict: dict):
|
| 149 |
+
_fields_w_type = [_field.name for _field in fields(cls)]
|
| 150 |
+
|
| 151 |
+
_fields_wo_type = [
|
| 152 |
+
x for x in cls.__dict__.keys()
|
| 153 |
+
if not x.startswith("__") and not x.endswith("__") and x not in _fields_w_type
|
| 154 |
+
]
|
| 155 |
+
_fields_wo_type_dict = {
|
| 156 |
+
_field_name: _dict.pop(_field_name)
|
| 157 |
+
for _field_name in _fields_wo_type if _field_name in _dict
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
for k in _dict.keys():
|
| 161 |
+
if k not in [_field.name for _field in fields(cls)]:
|
| 162 |
+
raise ValueError(f'{k} is not in fields({cls}).')
|
| 163 |
+
|
| 164 |
+
for _field in fields(cls):
|
| 165 |
+
if _field.name not in _dict:
|
| 166 |
+
continue
|
| 167 |
+
_dict[_field.name] = init_argument(_field.type, _dict[_field.name])
|
| 168 |
+
|
| 169 |
+
_instance = cls(**_dict)
|
| 170 |
+
|
| 171 |
+
for _field_name, _field_value in _fields_wo_type_dict.items():
|
| 172 |
+
setattr(_instance, _field_name, _field_value)
|
| 173 |
+
return _instance
|
| 174 |
+
|
| 175 |
+
@classmethod
|
| 176 |
+
def from_json(cls, load_path: Union[str, Path]):
|
| 177 |
+
if isinstance(load_path, str):
|
| 178 |
+
load_path = Path(load_path)
|
| 179 |
+
|
| 180 |
+
if not load_path.exists():
|
| 181 |
+
raise FileNotFoundError(f"{load_path} does not exist.")
|
| 182 |
+
|
| 183 |
+
with open(load_path, "r", encoding="utf-8") as f:
|
| 184 |
+
_dict = _resolve_config_paths(json.load(f), load_path.parent)
|
| 185 |
+
return cls.from_dict(_dict)
|
| 186 |
+
|
| 187 |
+
@classmethod
|
| 188 |
+
def from_yaml(cls, load_path: Union[str, Path]):
|
| 189 |
+
if isinstance(load_path, str):
|
| 190 |
+
load_path = Path(load_path)
|
| 191 |
+
|
| 192 |
+
if not load_path.exists():
|
| 193 |
+
raise FileNotFoundError(f"{load_path} does not exist.")
|
| 194 |
+
|
| 195 |
+
with open(load_path, "r", encoding="utf-8") as f:
|
| 196 |
+
_dict = _resolve_config_paths(yaml.safe_load(f), load_path.parent)
|
| 197 |
+
return cls.from_dict(_dict)
|
evaluation/nexus/Nexus/abc/evaluation/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .arguments import AbsEvalArguments, AbsEvalDataLoaderArguments
|
| 2 |
+
from .data_loader import AbsEvalDataLoader
|
| 3 |
+
from .evaluator import AbsEvaluator
|
| 4 |
+
from .runner import AbsEvalRunner
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
__all__ = [
|
| 8 |
+
"AbsEvalArguments",
|
| 9 |
+
"AbsEvalDataLoaderArguments",
|
| 10 |
+
"AbsEvalDataLoader",
|
| 11 |
+
"AbsEvaluator",
|
| 12 |
+
"AbsEvalRunner",
|
| 13 |
+
]
|
evaluation/nexus/Nexus/abc/evaluation/arguments.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass, field
|
| 2 |
+
|
| 3 |
+
from Nexus.abc.arguments import AbsArguments
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
@dataclass
|
| 7 |
+
class AbsEvalArguments(AbsArguments):
|
| 8 |
+
eval_name: str = field(
|
| 9 |
+
default=None,
|
| 10 |
+
metadata={"help": "Name of the evaluation task."}
|
| 11 |
+
)
|
| 12 |
+
eval_output_dir: str = field(
|
| 13 |
+
default=None,
|
| 14 |
+
metadata={"help": "Directory to save the evaluation results."}
|
| 15 |
+
)
|
| 16 |
+
overwrite: bool = field(
|
| 17 |
+
default=False,
|
| 18 |
+
metadata={"help": "Overwrite the output directory if it exists."}
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@dataclass
|
| 23 |
+
class AbsEvalDataLoaderArguments(AbsArguments):
|
| 24 |
+
eval_dataset_name_or_path: str = field(
|
| 25 |
+
default=None,
|
| 26 |
+
metadata={"help": "Name or path to the evaluation dataset."}
|
| 27 |
+
)
|
| 28 |
+
eval_batch_size: int = field(
|
| 29 |
+
default=1,
|
| 30 |
+
metadata={"help": "Batch size for evaluation."}
|
| 31 |
+
)
|
evaluation/nexus/Nexus/abc/evaluation/data_loader.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class AbsEvalDataLoader(ABC):
|
| 5 |
+
@abstractmethod
|
| 6 |
+
def __init__(
|
| 7 |
+
self,
|
| 8 |
+
*args,
|
| 9 |
+
**kwargs
|
| 10 |
+
):
|
| 11 |
+
pass
|
evaluation/nexus/Nexus/abc/evaluation/evaluator.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
|
| 3 |
+
from .data_loader import AbsEvalDataLoader
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class AbsEvaluator(ABC):
|
| 7 |
+
def __init__(
|
| 8 |
+
self,
|
| 9 |
+
eval_name: str,
|
| 10 |
+
data_loader: AbsEvalDataLoader,
|
| 11 |
+
overwrite: bool = False
|
| 12 |
+
):
|
| 13 |
+
self.eval_name = eval_name
|
| 14 |
+
self.data_loader = data_loader
|
| 15 |
+
self.overwrite = overwrite
|
| 16 |
+
|
| 17 |
+
@abstractmethod
|
| 18 |
+
def evaluate(self, *args, **kwargs):
|
| 19 |
+
pass
|
| 20 |
+
|
| 21 |
+
@staticmethod
|
| 22 |
+
@abstractmethod
|
| 23 |
+
def compute_metrics(*args, **kwargs):
|
| 24 |
+
pass
|
evaluation/nexus/Nexus/abc/evaluation/runner.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
from typing import Any
|
| 3 |
+
|
| 4 |
+
from .arguments import AbsEvalArguments, AbsEvalDataLoaderArguments
|
| 5 |
+
from .data_loader import AbsEvalDataLoader
|
| 6 |
+
from .evaluator import AbsEvaluator
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class AbsEvalRunner(ABC):
|
| 10 |
+
def __init__(
|
| 11 |
+
self,
|
| 12 |
+
eval_args: AbsEvalArguments,
|
| 13 |
+
eval_data_loader_args: AbsEvalDataLoaderArguments
|
| 14 |
+
):
|
| 15 |
+
self.eval_args = eval_args
|
| 16 |
+
self.data_loader = self.load_data_loader(eval_data_loader_args)
|
| 17 |
+
self.evaluator = self.load_evaluator()
|
| 18 |
+
|
| 19 |
+
def load_data_loader(
|
| 20 |
+
self,
|
| 21 |
+
eval_data_loader_args: AbsEvalDataLoaderArguments
|
| 22 |
+
) -> AbsEvalDataLoader:
|
| 23 |
+
return AbsEvalDataLoader(
|
| 24 |
+
**eval_data_loader_args.to_dict()
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
def load_evaluator(self):
|
| 28 |
+
return AbsEvaluator(
|
| 29 |
+
eval_name=self.eval_args.eval_name,
|
| 30 |
+
data_loader=self.data_loader,
|
| 31 |
+
overwrite=self.eval_args.overwrite
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
@abstractmethod
|
| 35 |
+
def run(
|
| 36 |
+
self,
|
| 37 |
+
model: Any,
|
| 38 |
+
*args,
|
| 39 |
+
**kwargs
|
| 40 |
+
):
|
| 41 |
+
pass
|
evaluation/nexus/Nexus/abc/inference/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from importlib import import_module
|
| 2 |
+
|
| 3 |
+
__all__ = [
|
| 4 |
+
"AbsEmbedder",
|
| 5 |
+
"AbsReranker",
|
| 6 |
+
"AbsInferenceArguments",
|
| 7 |
+
"InferenceEngine",
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def __getattr__(name):
|
| 12 |
+
if name == "AbsEmbedder":
|
| 13 |
+
module = import_module("Nexus.abc.inference.embedder")
|
| 14 |
+
return getattr(module, name)
|
| 15 |
+
if name == "AbsReranker":
|
| 16 |
+
module = import_module("Nexus.abc.inference.reranker")
|
| 17 |
+
return getattr(module, name)
|
| 18 |
+
if name == "AbsInferenceArguments":
|
| 19 |
+
module = import_module("Nexus.abc.inference.arguments")
|
| 20 |
+
return getattr(module, name)
|
| 21 |
+
if name == "InferenceEngine":
|
| 22 |
+
module = import_module("Nexus.abc.inference.inference_engine")
|
| 23 |
+
return getattr(module, name)
|
| 24 |
+
raise AttributeError(f"module 'Nexus.abc.inference' has no attribute {name!r}")
|
evaluation/nexus/Nexus/abc/inference/arguments.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass, field
|
| 2 |
+
from typing import List, Union
|
| 3 |
+
|
| 4 |
+
from Nexus.abc.arguments import AbsArguments
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@dataclass
|
| 8 |
+
class AbsInferenceArguments(AbsArguments):
|
| 9 |
+
stage: str = field(
|
| 10 |
+
default=None,
|
| 11 |
+
metadata={"help": "Stage of the inference process."}
|
| 12 |
+
)
|
| 13 |
+
use_fp16: bool = field(
|
| 14 |
+
default=True,
|
| 15 |
+
metadata={"help": "use fp16"}
|
| 16 |
+
)
|
| 17 |
+
model_name_or_path: str = field(
|
| 18 |
+
default=None,
|
| 19 |
+
metadata={"help": "Name or path to the model."}
|
| 20 |
+
)
|
| 21 |
+
onnx_model_path: str= field(
|
| 22 |
+
default=None,
|
| 23 |
+
metadata={
|
| 24 |
+
'help':'Path to onnx model'
|
| 25 |
+
}
|
| 26 |
+
)
|
| 27 |
+
trt_model_path: str= field(
|
| 28 |
+
default=None,
|
| 29 |
+
metadata={
|
| 30 |
+
'help':'Path to trt model'
|
| 31 |
+
}
|
| 32 |
+
)
|
| 33 |
+
output_topk: int = field(
|
| 34 |
+
default=10,
|
| 35 |
+
metadata={"help": "Number of top-k results to output."}
|
| 36 |
+
)
|
| 37 |
+
infer_device: Union[str, int, List[int], List[str]] = field(
|
| 38 |
+
default="cpu",
|
| 39 |
+
metadata={"help": "Device to perform inference.", "nargs": "+"}
|
| 40 |
+
)
|
| 41 |
+
infer_mode: str = field(
|
| 42 |
+
default="normal",
|
| 43 |
+
metadata={"help": "Inference mode: normal, onnx, tensorrt.", "choices": ["normal", "onnx", "tensorrt"]}
|
| 44 |
+
)
|
| 45 |
+
infer_batch_size: int = field(
|
| 46 |
+
default=1,
|
| 47 |
+
metadata={"help": "Batch size for inference."}
|
| 48 |
+
)
|
| 49 |
+
max_workspace_size: int = field(
|
| 50 |
+
default=1 << 30,
|
| 51 |
+
metadata={
|
| 52 |
+
'help':'Max workspace size for tesorrt session, default = 1 GB'
|
| 53 |
+
}
|
| 54 |
+
)
|
| 55 |
+
normalize: bool = field(
|
| 56 |
+
default=True,
|
| 57 |
+
metadata={
|
| 58 |
+
'help':'normalize embeddings'
|
| 59 |
+
}
|
| 60 |
+
)
|
| 61 |
+
query_max_length: int=field(
|
| 62 |
+
default=512,
|
| 63 |
+
metadata={
|
| 64 |
+
'help':'Query max length'
|
| 65 |
+
}
|
| 66 |
+
)
|
| 67 |
+
passage_max_length: int=field(
|
| 68 |
+
default=512,
|
| 69 |
+
metadata={
|
| 70 |
+
'help':'Passage max length'
|
| 71 |
+
}
|
| 72 |
+
)
|
evaluation/nexus/Nexus/abc/inference/embedder/AbsEmbedder.py
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from tqdm import tqdm, trange
|
| 3 |
+
from abc import ABC, abstractmethod
|
| 4 |
+
from typing import Any, Union, List, Dict, Literal, Optional
|
| 5 |
+
|
| 6 |
+
import queue
|
| 7 |
+
import multiprocessing as mp
|
| 8 |
+
from multiprocessing import Queue
|
| 9 |
+
|
| 10 |
+
import math
|
| 11 |
+
import gc
|
| 12 |
+
import torch
|
| 13 |
+
import numpy as np
|
| 14 |
+
from transformers import is_torch_npu_available
|
| 15 |
+
|
| 16 |
+
logger = logging.getLogger(__name__)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class AbsEmbedder(ABC):
|
| 20 |
+
"""
|
| 21 |
+
Base class for embedder.
|
| 22 |
+
Extend this class and implement :meth:`encode_queries`, :meth:`encode_corpus`, :meth:`encode` for custom embedders.
|
| 23 |
+
"""
|
| 24 |
+
def __init__(self, *args, **kwargs):
|
| 25 |
+
super().__init__()
|
| 26 |
+
|
| 27 |
+
def stop_self_pool(self):
|
| 28 |
+
if self.pool is not None:
|
| 29 |
+
self.stop_multi_process_pool(self.pool)
|
| 30 |
+
self.pool = None
|
| 31 |
+
try:
|
| 32 |
+
self.model.to('cpu')
|
| 33 |
+
torch.cuda.empty_cache()
|
| 34 |
+
except:
|
| 35 |
+
pass
|
| 36 |
+
gc.collect()
|
| 37 |
+
|
| 38 |
+
@staticmethod
|
| 39 |
+
def get_target_devices(devices: Union[str, int, List[str], List[int]]) -> List[str]:
|
| 40 |
+
"""
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
devices (Union[str, int, List[str], List[int]]): specified devices, can be `str`, `int`, list of `str`, or list of `int`.
|
| 44 |
+
|
| 45 |
+
Raises:
|
| 46 |
+
ValueError: Devices should be a string or an integer or a list of strings or a list of integers.
|
| 47 |
+
|
| 48 |
+
Returns:
|
| 49 |
+
List[str]: A list of target devices in format.
|
| 50 |
+
"""
|
| 51 |
+
if devices is None:
|
| 52 |
+
if torch.cuda.is_available():
|
| 53 |
+
return [f"cuda:{i}" for i in range(torch.cuda.device_count())]
|
| 54 |
+
elif is_torch_npu_available():
|
| 55 |
+
return [f"npu:{i}" for i in range(torch.npu.device_count())]
|
| 56 |
+
elif torch.backends.mps.is_available():
|
| 57 |
+
return [f"mps:{i}" for i in range(torch.mps.device_count())]
|
| 58 |
+
else:
|
| 59 |
+
return ["cpu"]
|
| 60 |
+
elif isinstance(devices, str):
|
| 61 |
+
return [devices]
|
| 62 |
+
elif isinstance(devices, int):
|
| 63 |
+
return [f"cuda:{devices}"]
|
| 64 |
+
elif isinstance(devices, list):
|
| 65 |
+
if isinstance(devices[0], str):
|
| 66 |
+
return devices
|
| 67 |
+
elif isinstance(devices[0], int):
|
| 68 |
+
return [f"cuda:{device}" for device in devices]
|
| 69 |
+
else:
|
| 70 |
+
raise ValueError("devices should be a string or an integer or a list of strings or a list of integers.")
|
| 71 |
+
else:
|
| 72 |
+
raise ValueError("devices should be a string or an integer or a list of strings or a list of integers.")
|
| 73 |
+
|
| 74 |
+
@abstractmethod
|
| 75 |
+
def encode_query(
|
| 76 |
+
self,
|
| 77 |
+
*args,
|
| 78 |
+
**kwargs: Any
|
| 79 |
+
):
|
| 80 |
+
"""encode queries
|
| 81 |
+
"""
|
| 82 |
+
return self.encode(*args, **kwargs)
|
| 83 |
+
|
| 84 |
+
@abstractmethod
|
| 85 |
+
def encode_info(
|
| 86 |
+
self,
|
| 87 |
+
*args,
|
| 88 |
+
**kwargs: Any
|
| 89 |
+
):
|
| 90 |
+
"""
|
| 91 |
+
encode info (corpus)
|
| 92 |
+
"""
|
| 93 |
+
return self.encode(*args, **kwargs)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
@abstractmethod
|
| 97 |
+
def encode(
|
| 98 |
+
self,
|
| 99 |
+
*args, **kwargs
|
| 100 |
+
):
|
| 101 |
+
"""encode
|
| 102 |
+
"""
|
| 103 |
+
pass
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def __del__(self):
|
| 107 |
+
self.stop_self_pool()
|
| 108 |
+
|
| 109 |
+
@abstractmethod
|
| 110 |
+
def encode_single_device(
|
| 111 |
+
self,
|
| 112 |
+
*args,
|
| 113 |
+
**kwargs
|
| 114 |
+
):
|
| 115 |
+
"""
|
| 116 |
+
This method should encode on a single device.
|
| 117 |
+
"""
|
| 118 |
+
pass
|
| 119 |
+
|
| 120 |
+
# adapted from https://github.com/UKPLab/sentence-transformers/blob/1802076d4eae42ff0a5629e1b04e75785d4e193b/sentence_transformers/SentenceTransformer.py#L807
|
| 121 |
+
def start_multi_process_pool(
|
| 122 |
+
self,
|
| 123 |
+
process_target_func: Any,
|
| 124 |
+
) -> Dict[Literal["input", "output", "processes"], Any]:
|
| 125 |
+
"""
|
| 126 |
+
Starts a multi-process pool to process the encoding with several independent processes
|
| 127 |
+
via :meth:`SentenceTransformer.encode_multi_process <sentence_transformers.SentenceTransformer.encode_multi_process>`.
|
| 128 |
+
|
| 129 |
+
This method is recommended if you want to encode on multiple GPUs or CPUs. It is advised
|
| 130 |
+
to start only one process per GPU. This method works together with encode_multi_process
|
| 131 |
+
and stop_multi_process_pool.
|
| 132 |
+
|
| 133 |
+
Returns:
|
| 134 |
+
Dict[str, Any]: A dictionary with the target processes, an input queue, and an output queue.
|
| 135 |
+
"""
|
| 136 |
+
if self.model is None:
|
| 137 |
+
raise ValueError("Model is not initialized.")
|
| 138 |
+
|
| 139 |
+
logger.info("Start multi-process pool on devices: {}".format(", ".join(map(str, self.target_devices))))
|
| 140 |
+
|
| 141 |
+
self.model.to("cpu")
|
| 142 |
+
self.model.share_memory()
|
| 143 |
+
ctx = mp.get_context("spawn")
|
| 144 |
+
input_queue = ctx.Queue()
|
| 145 |
+
output_queue = ctx.Queue()
|
| 146 |
+
processes = []
|
| 147 |
+
|
| 148 |
+
for device_id in tqdm(self.target_devices, desc='initial target device'):
|
| 149 |
+
p = ctx.Process(
|
| 150 |
+
target=process_target_func,
|
| 151 |
+
args=(device_id, self, input_queue, output_queue),
|
| 152 |
+
daemon=True,
|
| 153 |
+
)
|
| 154 |
+
p.start()
|
| 155 |
+
processes.append(p)
|
| 156 |
+
|
| 157 |
+
return {"input": input_queue, "output": output_queue, "processes": processes}
|
| 158 |
+
|
| 159 |
+
# adapted from https://github.com/UKPLab/sentence-transformers/blob/1802076d4eae42ff0a5629e1b04e75785d4e193b/sentence_transformers/SentenceTransformer.py#L976
|
| 160 |
+
@staticmethod
|
| 161 |
+
def _encode_multi_process_worker(
|
| 162 |
+
target_device: str, model: 'AbsEmbedder', input_queue: Queue, results_queue: Queue
|
| 163 |
+
) -> None:
|
| 164 |
+
"""
|
| 165 |
+
Internal working process to encode sentences in multi-process setup
|
| 166 |
+
"""
|
| 167 |
+
while True:
|
| 168 |
+
try:
|
| 169 |
+
chunk_id, sentences, kwargs = (
|
| 170 |
+
input_queue.get()
|
| 171 |
+
)
|
| 172 |
+
embeddings = model.encode_single_device(
|
| 173 |
+
sentences,
|
| 174 |
+
device=target_device,
|
| 175 |
+
**kwargs
|
| 176 |
+
)
|
| 177 |
+
|
| 178 |
+
results_queue.put([chunk_id, embeddings])
|
| 179 |
+
except queue.Empty:
|
| 180 |
+
break
|
| 181 |
+
|
| 182 |
+
# copied from https://github.com/UKPLab/sentence-transformers/blob/1802076d4eae42ff0a5629e1b04e75785d4e193b/sentence_transformers/SentenceTransformer.py#L857
|
| 183 |
+
@staticmethod
|
| 184 |
+
def stop_multi_process_pool(pool: Dict[Literal["input", "output", "processes"], Any]) -> None:
|
| 185 |
+
"""
|
| 186 |
+
Stops all processes started with start_multi_process_pool.
|
| 187 |
+
|
| 188 |
+
Args:
|
| 189 |
+
pool (Dict[str, object]): A dictionary containing the input queue, output queue, and process list.
|
| 190 |
+
|
| 191 |
+
Returns:
|
| 192 |
+
None
|
| 193 |
+
"""
|
| 194 |
+
for p in pool["processes"]:
|
| 195 |
+
p.terminate()
|
| 196 |
+
|
| 197 |
+
for p in pool["processes"]:
|
| 198 |
+
p.join()
|
| 199 |
+
p.close()
|
| 200 |
+
|
| 201 |
+
pool["input"].close()
|
| 202 |
+
pool["output"].close()
|
| 203 |
+
try:
|
| 204 |
+
pool["input"].join_thread()
|
| 205 |
+
except Exception:
|
| 206 |
+
pass
|
| 207 |
+
try:
|
| 208 |
+
pool["output"].join_thread()
|
| 209 |
+
except Exception:
|
| 210 |
+
pass
|
| 211 |
+
pool = None
|
| 212 |
+
|
| 213 |
+
# adapted from https://github.com/UKPLab/sentence-transformers/blob/1802076d4eae42ff0a5629e1b04e75785d4e193b/sentence_transformers/SentenceTransformer.py#L877
|
| 214 |
+
def encode_multi_process(
|
| 215 |
+
self,
|
| 216 |
+
sentences: List[str],
|
| 217 |
+
pool: Dict[Literal["input", "output", "processes"], Any],
|
| 218 |
+
**kwargs
|
| 219 |
+
):
|
| 220 |
+
chunk_size = math.ceil(len(sentences) / len(pool["processes"]))
|
| 221 |
+
|
| 222 |
+
input_queue = pool["input"]
|
| 223 |
+
last_chunk_id = 0
|
| 224 |
+
chunk = []
|
| 225 |
+
|
| 226 |
+
for sentence in sentences:
|
| 227 |
+
chunk.append(sentence)
|
| 228 |
+
if len(chunk) >= chunk_size:
|
| 229 |
+
input_queue.put(
|
| 230 |
+
[last_chunk_id, chunk, kwargs]
|
| 231 |
+
)
|
| 232 |
+
last_chunk_id += 1
|
| 233 |
+
chunk = []
|
| 234 |
+
|
| 235 |
+
if len(chunk) > 0:
|
| 236 |
+
input_queue.put([last_chunk_id, chunk, kwargs])
|
| 237 |
+
last_chunk_id += 1
|
| 238 |
+
|
| 239 |
+
output_queue = pool["output"]
|
| 240 |
+
results_list = sorted(
|
| 241 |
+
[output_queue.get() for _ in trange(last_chunk_id, desc="Chunks")],
|
| 242 |
+
key=lambda x: x[0],
|
| 243 |
+
)
|
| 244 |
+
embeddings = self._concatenate_results_from_multi_process([result[1] for result in results_list])
|
| 245 |
+
return embeddings
|
| 246 |
+
|
| 247 |
+
def _concatenate_results_from_multi_process(self, results_list: List[Union[torch.Tensor, np.ndarray, Any]]):
|
| 248 |
+
"""concatenate and return the results from all the processes
|
| 249 |
+
|
| 250 |
+
Args:
|
| 251 |
+
results_list (List[Union[torch.Tensor, np.ndarray, Any]]): A list of results from all the processes.
|
| 252 |
+
|
| 253 |
+
Raises:
|
| 254 |
+
NotImplementedError: Unsupported type for results_list
|
| 255 |
+
|
| 256 |
+
Returns:
|
| 257 |
+
Union[torch.Tensor, np.ndarray]: return the embedding vectors in a numpy array or tensor.
|
| 258 |
+
"""
|
| 259 |
+
if isinstance(results_list[0], torch.Tensor):
|
| 260 |
+
return torch.cat(results_list, dim=0)
|
| 261 |
+
elif isinstance(results_list[0], np.ndarray):
|
| 262 |
+
return np.concatenate(results_list, axis=0)
|
| 263 |
+
else:
|
| 264 |
+
raise NotImplementedError("Unsupported type for results_list")
|
evaluation/nexus/Nexus/abc/inference/embedder/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .AbsEmbedder import AbsEmbedder
|
evaluation/nexus/Nexus/abc/inference/inference_engine.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from abc import ABC, abstractmethod
|
| 4 |
+
from typing import Union, List, Tuple, Any
|
| 5 |
+
|
| 6 |
+
import pandas as pd
|
| 7 |
+
|
| 8 |
+
try:
|
| 9 |
+
import onnx
|
| 10 |
+
except ImportError:
|
| 11 |
+
onnx = None
|
| 12 |
+
|
| 13 |
+
try:
|
| 14 |
+
import onnxruntime as ort
|
| 15 |
+
except ImportError:
|
| 16 |
+
ort = None
|
| 17 |
+
|
| 18 |
+
try:
|
| 19 |
+
import tensorrt as trt
|
| 20 |
+
except ImportError:
|
| 21 |
+
trt = None
|
| 22 |
+
|
| 23 |
+
from .arguments import AbsInferenceArguments
|
| 24 |
+
|
| 25 |
+
logger = logging.getLogger(__name__)
|
| 26 |
+
|
| 27 |
+
class InferenceEngine(ABC):
|
| 28 |
+
def __init__(self, infer_args: AbsInferenceArguments):
|
| 29 |
+
self.config = infer_args.to_dict()
|
| 30 |
+
|
| 31 |
+
# @abstractmethod
|
| 32 |
+
# def load_model(self):
|
| 33 |
+
# pass
|
| 34 |
+
|
| 35 |
+
@staticmethod
|
| 36 |
+
def load_onnx_model(onnx_model_path: Union[str, Path]):
|
| 37 |
+
if onnx is None:
|
| 38 |
+
raise ImportError("onnx is required to load ONNX models.")
|
| 39 |
+
onnx_model = onnx.load(onnx_model_path)
|
| 40 |
+
onnx.checker.check_model(onnx_model)
|
| 41 |
+
logger.info(f"====== Loaded ONNX model from {onnx_model_path} ======")
|
| 42 |
+
logger.info(onnx.helper.printable_graph(onnx_model.graph))
|
| 43 |
+
return onnx_model
|
| 44 |
+
|
| 45 |
+
# @abstractmethod
|
| 46 |
+
# def get_normal_session(self):
|
| 47 |
+
# pass
|
| 48 |
+
|
| 49 |
+
@abstractmethod
|
| 50 |
+
def get_ort_session(self) -> Any:
|
| 51 |
+
pass
|
| 52 |
+
|
| 53 |
+
@abstractmethod
|
| 54 |
+
def get_trt_session(self) -> Any:
|
| 55 |
+
pass
|
| 56 |
+
|
| 57 |
+
def get_inference_session(self):
|
| 58 |
+
if self.config["infer_mode"] == "normal":
|
| 59 |
+
return self.get_normal_session()
|
| 60 |
+
if self.config["infer_mode"] == "onnx":
|
| 61 |
+
return self.get_ort_session()
|
| 62 |
+
elif self.config["infer_mode"] == "tensorrt":
|
| 63 |
+
return self.get_trt_session()
|
| 64 |
+
else:
|
| 65 |
+
raise ValueError(f"Invalid inference mode: {self.config['infer_mode']}")
|
| 66 |
+
|
| 67 |
+
@abstractmethod
|
| 68 |
+
def convert_to_onnx(self):
|
| 69 |
+
pass
|
| 70 |
+
|
| 71 |
+
# @abstractmethod
|
| 72 |
+
# def inference(
|
| 73 |
+
# self,
|
| 74 |
+
# inputs: Union[List[str], List[Tuple[str, str]], pd.DataFrame, Any],
|
| 75 |
+
# *args,
|
| 76 |
+
# **kwargs
|
| 77 |
+
# ):
|
| 78 |
+
# pass
|
| 79 |
+
|
| 80 |
+
def save_output_topk(self, output):
|
| 81 |
+
output_df = {}
|
| 82 |
+
output_df['_'.join(self.config['request_features'])] = \
|
| 83 |
+
self.infer_df.apply(lambda row : '_'.join([str(row[feat]) for feat in self.config['request_features']]),
|
| 84 |
+
axis='columns')
|
| 85 |
+
output_df[self.feature_config['fiid']] = output.tolist()
|
| 86 |
+
output_df = pd.DataFrame(output_df)
|
| 87 |
+
output_df.to_feather(self.config['output_save_path'])
|
evaluation/nexus/Nexus/abc/inference/reranker/AbsReranker.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from abc import ABC, abstractmethod
|
| 3 |
+
from typing import Any, Union, List, Tuple, Dict, Literal, Optional
|
| 4 |
+
|
| 5 |
+
import multiprocessing as mp
|
| 6 |
+
from multiprocessing import Queue
|
| 7 |
+
|
| 8 |
+
import math
|
| 9 |
+
import gc
|
| 10 |
+
import torch
|
| 11 |
+
import numpy as np
|
| 12 |
+
from tqdm import tqdm, trange
|
| 13 |
+
from transformers import is_torch_npu_available
|
| 14 |
+
|
| 15 |
+
logger = logging.getLogger(__name__)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class AbsReranker(ABC):
|
| 19 |
+
"""
|
| 20 |
+
Base class for Reranker.
|
| 21 |
+
Extend this class and implement :meth:`compute_score_single_gpu` for custom rerankers.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
def __init__(
|
| 25 |
+
self,
|
| 26 |
+
*args,
|
| 27 |
+
**kwargs: Any,
|
| 28 |
+
):
|
| 29 |
+
pass
|
| 30 |
+
|
| 31 |
+
def stop_self_pool(self):
|
| 32 |
+
if self.pool is not None:
|
| 33 |
+
self.stop_multi_process_pool(self.pool)
|
| 34 |
+
self.pool = None
|
| 35 |
+
try:
|
| 36 |
+
self.model.to('cpu')
|
| 37 |
+
torch.cuda.empty_cache()
|
| 38 |
+
except:
|
| 39 |
+
pass
|
| 40 |
+
gc.collect()
|
| 41 |
+
|
| 42 |
+
@staticmethod
|
| 43 |
+
def get_target_devices(devices: Union[str, int, List[str], List[int]]) -> List[str]:
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
devices (Union[str, int, List[str], List[int]]): Specified devices, can be `str`, `int`, list of `str`, or list of `int`.
|
| 48 |
+
|
| 49 |
+
Raises:
|
| 50 |
+
ValueError: Devices should be a string or an integer or a list of strings or a list of integers.
|
| 51 |
+
|
| 52 |
+
Returns:
|
| 53 |
+
List[str]: A list of target devices in format
|
| 54 |
+
"""
|
| 55 |
+
if devices is None:
|
| 56 |
+
if torch.cuda.is_available():
|
| 57 |
+
return [f"cuda:{i}" for i in range(torch.cuda.device_count())]
|
| 58 |
+
elif is_torch_npu_available():
|
| 59 |
+
return [f"npu:{i}" for i in range(torch.npu.device_count())]
|
| 60 |
+
elif torch.backends.mps.is_available():
|
| 61 |
+
return ["mps"]
|
| 62 |
+
else:
|
| 63 |
+
return ["cpu"]
|
| 64 |
+
elif isinstance(devices, str):
|
| 65 |
+
return [devices]
|
| 66 |
+
elif isinstance(devices, int):
|
| 67 |
+
return [f"cuda:{devices}"]
|
| 68 |
+
elif isinstance(devices, list):
|
| 69 |
+
if isinstance(devices[0], str):
|
| 70 |
+
return devices
|
| 71 |
+
elif isinstance(devices[0], int):
|
| 72 |
+
return [f"cuda:{device}" for device in devices]
|
| 73 |
+
else:
|
| 74 |
+
raise ValueError("devices should be a string or an integer or a list of strings or a list of integers.")
|
| 75 |
+
else:
|
| 76 |
+
raise ValueError("devices should be a string or an integer or a list of strings or a list of integers.")
|
| 77 |
+
|
| 78 |
+
@abstractmethod
|
| 79 |
+
def compute_score(
|
| 80 |
+
self,
|
| 81 |
+
*args,
|
| 82 |
+
**kwargs
|
| 83 |
+
):
|
| 84 |
+
"""Compute score for each sentence pair
|
| 85 |
+
|
| 86 |
+
Returns:
|
| 87 |
+
numpy.ndarray: scores of all the sentence pairs.
|
| 88 |
+
"""
|
| 89 |
+
pass
|
| 90 |
+
|
| 91 |
+
def __del__(self):
|
| 92 |
+
self.stop_self_pool()
|
| 93 |
+
|
| 94 |
+
@abstractmethod
|
| 95 |
+
def compute_score_single_gpu(
|
| 96 |
+
self,
|
| 97 |
+
sentence_pairs: Union[List[Tuple[str, str]], Tuple[str, str]],
|
| 98 |
+
batch_size: int = 256,
|
| 99 |
+
query_max_length: Optional[int] = None,
|
| 100 |
+
max_length: int = 512,
|
| 101 |
+
normalize: bool = False,
|
| 102 |
+
device: Optional[str] = None,
|
| 103 |
+
**kwargs: Any,
|
| 104 |
+
):
|
| 105 |
+
"""
|
| 106 |
+
This method should compute the scores of sentence_pair and return scores.
|
| 107 |
+
"""
|
| 108 |
+
pass
|
| 109 |
+
|
| 110 |
+
# copied from https://github.com/UKPLab/sentence-transformers/blob/1802076d4eae42ff0a5629e1b04e75785d4e193b/sentence_transformers/SentenceTransformer.py#L857
|
| 111 |
+
def start_multi_process_pool(self) -> Dict[Literal["input", "output", "processes"], Any]:
|
| 112 |
+
"""
|
| 113 |
+
Starts a multi-process pool to process the encoding with several independent processes
|
| 114 |
+
via :meth:`SentenceTransformer.encode_multi_process <sentence_transformers.SentenceTransformer.encode_multi_process>`.
|
| 115 |
+
|
| 116 |
+
This method is recommended if you want to encode on multiple GPUs or CPUs. It is advised
|
| 117 |
+
to start only one process per GPU. This method works together with encode_multi_process
|
| 118 |
+
and stop_multi_process_pool.
|
| 119 |
+
|
| 120 |
+
Returns:
|
| 121 |
+
Dict[str, Any]: A dictionary with the target processes, an input queue, and an output queue.
|
| 122 |
+
"""
|
| 123 |
+
logger.info("Start multi-process pool on devices: {}".format(", ".join(map(str, self.target_devices))))
|
| 124 |
+
|
| 125 |
+
self.model.to("cpu")
|
| 126 |
+
self.model.share_memory()
|
| 127 |
+
ctx = mp.get_context("spawn")
|
| 128 |
+
input_queue = ctx.Queue()
|
| 129 |
+
output_queue = ctx.Queue()
|
| 130 |
+
processes = []
|
| 131 |
+
|
| 132 |
+
for device_id in tqdm(self.target_devices, desc='initial target device'):
|
| 133 |
+
p = ctx.Process(
|
| 134 |
+
target=AbsReranker._encode_multi_process_worker,
|
| 135 |
+
args=(device_id, self, input_queue, output_queue),
|
| 136 |
+
daemon=True,
|
| 137 |
+
)
|
| 138 |
+
p.start()
|
| 139 |
+
processes.append(p)
|
| 140 |
+
|
| 141 |
+
return {"input": input_queue, "output": output_queue, "processes": processes}
|
| 142 |
+
|
| 143 |
+
# copied from https://github.com/UKPLab/sentence-transformers/blob/1802076d4eae42ff0a5629e1b04e75785d4e193b/sentence_transformers/SentenceTransformer.py#L857
|
| 144 |
+
|
| 145 |
+
# copied from https://github.com/UKPLab/sentence-transformers/blob/1802076d4eae42ff0a5629e1b04e75785d4e193b/sentence_transformers/SentenceTransformer.py#L857
|
| 146 |
+
@staticmethod
|
| 147 |
+
def _encode_multi_process_worker(
|
| 148 |
+
target_device: str, model: 'AbsReranker', input_queue: Queue, results_queue: Queue
|
| 149 |
+
) -> None:
|
| 150 |
+
"""
|
| 151 |
+
Internal working process to encode sentences in multi-process setup
|
| 152 |
+
"""
|
| 153 |
+
while True:
|
| 154 |
+
try:
|
| 155 |
+
chunk_id, sentences, kwargs = (
|
| 156 |
+
input_queue.get()
|
| 157 |
+
)
|
| 158 |
+
embeddings = model.compute_score_single_gpu(
|
| 159 |
+
sentences,
|
| 160 |
+
device=target_device,
|
| 161 |
+
**kwargs
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
results_queue.put([chunk_id, embeddings])
|
| 165 |
+
except:
|
| 166 |
+
break
|
| 167 |
+
|
| 168 |
+
# copied from https://github.com/UKPLab/sentence-transformers/blob/1802076d4eae42ff0a5629e1b04e75785d4e193b/sentence_transformers/SentenceTransformer.py#L857
|
| 169 |
+
@staticmethod
|
| 170 |
+
def stop_multi_process_pool(pool: Dict[Literal["input", "output", "processes"], Any]) -> None:
|
| 171 |
+
"""
|
| 172 |
+
Stops all processes started with start_multi_process_pool.
|
| 173 |
+
|
| 174 |
+
Args:
|
| 175 |
+
pool (Dict[str, object]): A dictionary containing the input queue, output queue, and process list.
|
| 176 |
+
|
| 177 |
+
Returns:
|
| 178 |
+
None
|
| 179 |
+
"""
|
| 180 |
+
for p in pool["processes"]:
|
| 181 |
+
p.terminate()
|
| 182 |
+
|
| 183 |
+
for p in pool["processes"]:
|
| 184 |
+
p.join()
|
| 185 |
+
p.close()
|
| 186 |
+
|
| 187 |
+
pool["input"].close()
|
| 188 |
+
pool["output"].close()
|
evaluation/nexus/Nexus/abc/inference/reranker/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .AbsReranker import AbsReranker
|
evaluation/nexus/Nexus/abc/training/__init__.py
ADDED
|
File without changes
|
evaluation/nexus/Nexus/abc/training/arguments.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass, field
|
| 2 |
+
|
| 3 |
+
from Nexus.abc.arguments import AbsArguments
|
| 4 |
+
from transformers import TrainingArguments
|
| 5 |
+
|
| 6 |
+
@dataclass
|
| 7 |
+
class AbsDataArguments(AbsArguments):
|
| 8 |
+
train_data: str = field(
|
| 9 |
+
default=None,
|
| 10 |
+
metadata={"help": "Path to the data directory."}
|
| 11 |
+
)
|
| 12 |
+
eval_data: str = field(
|
| 13 |
+
default=None,
|
| 14 |
+
metadata={"help": "Path to the data directory."}
|
| 15 |
+
)
|
| 16 |
+
data_cache_dir: str = field(
|
| 17 |
+
default=None,
|
| 18 |
+
metadata={"help": "Path to the cache directory."}
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@dataclass
|
| 23 |
+
class AbsModelArguments(AbsArguments):
|
| 24 |
+
model_name_or_path: str = field(
|
| 25 |
+
default=None,
|
| 26 |
+
metadata={"help": "Name or path to the model."}
|
| 27 |
+
)
|
| 28 |
+
model_cache_dir: str = field(
|
| 29 |
+
default=None,
|
| 30 |
+
metadata={"help": "Path to the cache directory."}
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@dataclass
|
| 35 |
+
class AbsTrainingArguments(AbsArguments, TrainingArguments):
|
| 36 |
+
pass
|
evaluation/nexus/Nexus/abc/training/dataset.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from torch.utils.data import Dataset
|
| 2 |
+
|
| 3 |
+
from .arguments import AbsDataArguments
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class AbsDataset(Dataset):
|
| 7 |
+
def __init__(self, args: AbsDataArguments):
|
| 8 |
+
self.args = args
|
evaluation/nexus/Nexus/abc/training/embedder/AbsArguments.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from typing import Optional
|
| 3 |
+
from dataclasses import dataclass, field
|
| 4 |
+
from Nexus.abc.training.arguments import AbsDataArguments, AbsModelArguments, AbsTrainingArguments
|
| 5 |
+
from transformers import TrainingArguments
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@dataclass
|
| 9 |
+
class AbsEmbedderModelArguments(AbsModelArguments):
|
| 10 |
+
"""
|
| 11 |
+
Abstract class for model arguments.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
model_name_or_path: str = field(
|
| 15 |
+
metadata={"help": "The model checkpoint for initialization."}
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@dataclass
|
| 20 |
+
class AbsEmbedderDataArguments(AbsDataArguments):
|
| 21 |
+
"""
|
| 22 |
+
Abstract class for data arguments.
|
| 23 |
+
"""
|
| 24 |
+
train_data: str = field(
|
| 25 |
+
default=None, metadata={
|
| 26 |
+
"help": "One or more paths to training data. `query: str`, `pos: List[str]`, `neg: List[str]` are required in the training data.",
|
| 27 |
+
"nargs": "+"
|
| 28 |
+
}
|
| 29 |
+
)
|
| 30 |
+
cache_path: Optional[str] = field(
|
| 31 |
+
default=None, metadata={"help": "Where do you want to store the cached data"}
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def __post_init__(self):
|
| 36 |
+
if not isinstance(self.train_data, list):
|
| 37 |
+
self.train_data = self.train_data.split()
|
| 38 |
+
|
| 39 |
+
for train_dir in self.train_data:
|
| 40 |
+
if not os.path.exists(train_dir):
|
| 41 |
+
raise FileNotFoundError(f"cannot find file: {train_dir}, please set a true path")
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@dataclass
|
| 45 |
+
class AbsEmbedderTrainingArguments(AbsTrainingArguments):
|
| 46 |
+
pass
|
evaluation/nexus/Nexus/abc/training/embedder/AbsDataset.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from dataclasses import dataclass
|
| 3 |
+
from abc import abstractmethod
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
from Nexus.abc.training.dataset import AbsDataset
|
| 7 |
+
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class AbsEmbedderTrainDataset(AbsDataset):
|
| 12 |
+
"""Abstract class for training dataset.
|
| 13 |
+
|
| 14 |
+
Args:
|
| 15 |
+
args (AbsEmbedderDataArguments): Data arguments.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
pass
|
| 19 |
+
|
| 20 |
+
@dataclass
|
| 21 |
+
class AbsEmbedderCollator():
|
| 22 |
+
"""
|
| 23 |
+
The abstract embedder collator.
|
| 24 |
+
"""
|
| 25 |
+
@abstractmethod
|
| 26 |
+
def __call__(self, features):
|
| 27 |
+
return super().__call__(features)
|
| 28 |
+
|
| 29 |
+
@dataclass
|
| 30 |
+
class CallbackOutput:
|
| 31 |
+
save_checkpoint: str = None
|
| 32 |
+
stop_training: bool = False
|
| 33 |
+
|
| 34 |
+
|
evaluation/nexus/Nexus/abc/training/embedder/AbsModeling.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn, Tensor
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
import torch.distributed as dist
|
| 5 |
+
from transformers import AutoTokenizer
|
| 6 |
+
|
| 7 |
+
import logging
|
| 8 |
+
from dataclasses import dataclass
|
| 9 |
+
from abc import ABC, abstractmethod
|
| 10 |
+
from typing import Dict, Optional, List, Union
|
| 11 |
+
from Nexus.abc.training.modeling import AbsModelOutput, AbsEmbedder
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@dataclass
|
| 17 |
+
class EmbedderOutput(AbsModelOutput):
|
| 18 |
+
"""
|
| 19 |
+
Output information returned by the model.
|
| 20 |
+
"""
|
| 21 |
+
pass
|
| 22 |
+
|
| 23 |
+
class AbsEmbedderModel(AbsEmbedder, nn.Module):
|
| 24 |
+
"""Abstract class of embedding model for training.
|
| 25 |
+
|
| 26 |
+
Args:
|
| 27 |
+
base_model: The base model to train on.
|
| 28 |
+
tokenizer (AutoTokenizer, optional): The tokenizer to use. Defaults to ``None``.
|
| 29 |
+
negatives_cross_device (bool, optional): If True, will compute cross devices negative loss. Defaults to ``False``.
|
| 30 |
+
temperature (float, optional): Temperature to control the scale of scores. Defaults to ``1.0``.
|
| 31 |
+
sub_batch_size (int, optional): Sub-batch size during encoding. If negative, will not split to sub-batch.
|
| 32 |
+
Defaults to ``-1``.
|
| 33 |
+
kd_loss_type (str, optional): Type of knowledge distillation loss. Defaults to ``"kl_div"``.
|
| 34 |
+
"""
|
| 35 |
+
def __init__(self, *args, **kwargs):
|
| 36 |
+
super().__init__(*args, **kwargs)
|
| 37 |
+
self.init_modules()
|
| 38 |
+
|
| 39 |
+
def init_modules(self):
|
| 40 |
+
self.loss_function=self.get_loss_function()
|
| 41 |
+
self.score_function=self.get_score_function()
|
| 42 |
+
|
| 43 |
+
@abstractmethod
|
| 44 |
+
def get_loss_function(self):
|
| 45 |
+
pass
|
| 46 |
+
|
| 47 |
+
@abstractmethod
|
| 48 |
+
def get_score_function(self):
|
| 49 |
+
pass
|
| 50 |
+
|
| 51 |
+
@abstractmethod
|
| 52 |
+
def compute_loss(self, batch, *args, **kwargs):
|
| 53 |
+
"""Abstract method compute the loss.
|
| 54 |
+
|
| 55 |
+
Args:
|
| 56 |
+
scores (torch.Tensor): Computed score.
|
| 57 |
+
target (torch.Tensor): The target value.
|
| 58 |
+
"""
|
| 59 |
+
pass
|
| 60 |
+
|
| 61 |
+
@abstractmethod
|
| 62 |
+
def compute_score(self, *args, **kwargs):
|
| 63 |
+
"""Abstract method to compute the score.
|
| 64 |
+
"""
|
| 65 |
+
pass
|
| 66 |
+
|
| 67 |
+
@abstractmethod
|
| 68 |
+
def save(self, output_dir: str):
|
| 69 |
+
"""Abstract method to save the model.
|
| 70 |
+
|
| 71 |
+
Args:
|
| 72 |
+
output_dir (str): Directory for saving the model.
|
| 73 |
+
"""
|
| 74 |
+
pass
|
| 75 |
+
|
| 76 |
+
def forward(self, batch, *args, **kwargs):
|
| 77 |
+
"""The computation performed at every call.
|
| 78 |
+
|
| 79 |
+
Args:
|
| 80 |
+
Datas after datacollator
|
| 81 |
+
|
| 82 |
+
Returns:
|
| 83 |
+
EmbedderOutput: Output of the forward call of model.
|
| 84 |
+
"""
|
| 85 |
+
return self.compute_loss(batch,*args, **kwargs)
|
evaluation/nexus/Nexus/abc/training/embedder/AbsRunner.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import logging
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from typing import Tuple, Union
|
| 5 |
+
from abc import ABC, abstractmethod
|
| 6 |
+
from transformers import set_seed, PreTrainedTokenizer
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
from .AbsArguments import (
|
| 10 |
+
AbsEmbedderModelArguments,
|
| 11 |
+
AbsEmbedderDataArguments,
|
| 12 |
+
AbsEmbedderTrainingArguments
|
| 13 |
+
)
|
| 14 |
+
from .AbsTrainer import AbsEmbedderTrainer
|
| 15 |
+
from .AbsModeling import AbsEmbedderModel
|
| 16 |
+
from .AbsDataset import (
|
| 17 |
+
AbsEmbedderTrainDataset, AbsEmbedderCollator)
|
| 18 |
+
from Nexus.abc.training.runner import AbsRunner
|
| 19 |
+
|
| 20 |
+
logger = logging.getLogger(__name__)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class AbsEmbedderRunner(AbsRunner):
|
| 24 |
+
"""Abstract class to run embedding model fine-tuning.
|
| 25 |
+
|
| 26 |
+
Args:
|
| 27 |
+
model_args (AbsEmbedderModelArguments): Model arguments
|
| 28 |
+
data_args (AbsEmbedderDataArguments): Data arguments.
|
| 29 |
+
training_args (AbsEmbedderTrainingArguments): Training arguments.
|
| 30 |
+
"""
|
| 31 |
+
def __init__(
|
| 32 |
+
self,
|
| 33 |
+
model_args: AbsEmbedderModelArguments,
|
| 34 |
+
data_args: AbsEmbedderDataArguments,
|
| 35 |
+
training_args: AbsEmbedderTrainingArguments
|
| 36 |
+
):
|
| 37 |
+
self.model_args = model_args
|
| 38 |
+
self.data_args = data_args
|
| 39 |
+
self.training_args = training_args
|
| 40 |
+
|
| 41 |
+
@abstractmethod
|
| 42 |
+
def load_model(self, *args, **kwargs) -> AbsEmbedderModel:
|
| 43 |
+
pass
|
| 44 |
+
|
| 45 |
+
@abstractmethod
|
| 46 |
+
def load_trainer(self) -> AbsEmbedderTrainer:
|
| 47 |
+
"""Abstract method to load the trainer.
|
| 48 |
+
|
| 49 |
+
Returns:
|
| 50 |
+
AbsEmbedderTrainer: The loaded trainer instance.
|
| 51 |
+
"""
|
| 52 |
+
pass
|
| 53 |
+
|
| 54 |
+
@abstractmethod
|
| 55 |
+
def load_dataset(self, *args, **kwargs) -> AbsEmbedderTrainDataset:
|
| 56 |
+
pass
|
| 57 |
+
|
| 58 |
+
@abstractmethod
|
| 59 |
+
def load_data_collator(self) -> AbsEmbedderCollator:
|
| 60 |
+
"""Loads the appropriate data collator.
|
| 61 |
+
|
| 62 |
+
Returns:
|
| 63 |
+
AbsEmbedderCollator: Loaded data collator.
|
| 64 |
+
"""
|
| 65 |
+
pass
|
| 66 |
+
|
| 67 |
+
def run(self):
|
| 68 |
+
"""
|
| 69 |
+
Executes the training process.
|
| 70 |
+
"""
|
| 71 |
+
Path(self.training_args.output_dir).mkdir(parents=True, exist_ok=True)
|
| 72 |
+
|
| 73 |
+
# Training
|
| 74 |
+
self.trainer.train(resume_from_checkpoint=self.training_args.resume_from_checkpoint)
|
| 75 |
+
print('Training completed.')
|
| 76 |
+
self.trainer.save_model()
|
| 77 |
+
print('Training completed. save done')
|
| 78 |
+
|
| 79 |
+
|
evaluation/nexus/Nexus/abc/training/embedder/AbsTrainer.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import torch
|
| 3 |
+
from typing import Optional
|
| 4 |
+
from abc import ABC, abstractmethod
|
| 5 |
+
from transformers.trainer import Trainer
|
| 6 |
+
from Nexus.abc.training.trainer import AbsTrainer
|
| 7 |
+
from Nexus.abc.training.dataset import AbsDataset
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class AbsEmbedderTrainer(AbsTrainer):
|
| 12 |
+
"""
|
| 13 |
+
Abstract class for the trainer of embedder.
|
| 14 |
+
"""
|
| 15 |
+
@abstractmethod
|
| 16 |
+
def _save(self, output_dir: Optional[str] = None, state_dict=None):
|
| 17 |
+
pass
|
| 18 |
+
|
| 19 |
+
def compute_loss(self, model, inputs, return_outputs=False ,*args, **kwargs):
|
| 20 |
+
"""
|
| 21 |
+
How the loss is computed by Trainer. By default, all models return the loss in the first element.
|
| 22 |
+
|
| 23 |
+
Subclass and override for custom behavior.
|
| 24 |
+
|
| 25 |
+
Args:
|
| 26 |
+
model (AbsEmbedderModel): The model being trained.
|
| 27 |
+
inputs (dict): A dictionary of input tensors to be passed to the model.
|
| 28 |
+
return_outputs (bool, optional): If ``True``, returns both the loss and the model's outputs. Otherwise,
|
| 29 |
+
returns only the loss.
|
| 30 |
+
|
| 31 |
+
Returns:
|
| 32 |
+
Union[torch.Tensor, tuple(torch.Tensor, EmbedderOutput)]: The computed loss. If ``return_outputs`` is ``True``,
|
| 33 |
+
also returns the model's outputs in a tuple ``(loss, outputs)``.
|
| 34 |
+
"""
|
| 35 |
+
outputs = model(**inputs)
|
| 36 |
+
loss = outputs.loss
|
| 37 |
+
|
| 38 |
+
return (loss, outputs) if return_outputs else loss
|
| 39 |
+
|
evaluation/nexus/Nexus/abc/training/embedder/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .AbsArguments import AbsEmbedderModelArguments, AbsEmbedderDataArguments,AbsEmbedderTrainingArguments
|
| 2 |
+
from .AbsDataset import (
|
| 3 |
+
AbsEmbedderTrainDataset,
|
| 4 |
+
AbsEmbedderCollator,
|
| 5 |
+
CallbackOutput
|
| 6 |
+
)
|
| 7 |
+
from .AbsModeling import (
|
| 8 |
+
EmbedderOutput,
|
| 9 |
+
AbsEmbedderModel
|
| 10 |
+
)
|
| 11 |
+
from .AbsRunner import AbsEmbedderRunner
|
| 12 |
+
from .AbsTrainer import AbsEmbedderTrainer
|
| 13 |
+
|
| 14 |
+
__all__ = [
|
| 15 |
+
'AbsEmbedderModelArguments',
|
| 16 |
+
'AbsEmbedderDataArguments',
|
| 17 |
+
'AbsEmbedderTrainingArguments',
|
| 18 |
+
'AbsEmbedderTrainDataset',
|
| 19 |
+
'AbsEmbedderCollator',
|
| 20 |
+
'EmbedderOutput',
|
| 21 |
+
'AbsEmbedderModel',
|
| 22 |
+
'AbsEmbedderRunner',
|
| 23 |
+
'AbsEmbedderTrainer',
|
| 24 |
+
'CallbackOutput'
|
| 25 |
+
]
|
evaluation/nexus/Nexus/abc/training/modeling.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
from dataclasses import dataclass, asdict
|
| 3 |
+
|
| 4 |
+
from torch import nn
|
| 5 |
+
|
| 6 |
+
@dataclass
|
| 7 |
+
class AbsModelOutput:
|
| 8 |
+
def to_dict(self):
|
| 9 |
+
return asdict(self)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class AbsModel(ABC, nn.Module):
|
| 13 |
+
|
| 14 |
+
@abstractmethod
|
| 15 |
+
def compute_loss(self, *args, **kwargs):
|
| 16 |
+
pass
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class AbsEmbedder(ABC, nn.Module):
|
| 20 |
+
|
| 21 |
+
@abstractmethod
|
| 22 |
+
def compute_score(self, *args, **kwargs):
|
| 23 |
+
pass
|
| 24 |
+
|
| 25 |
+
@abstractmethod
|
| 26 |
+
def encode_query(self, *args, **kwargs):
|
| 27 |
+
pass
|
| 28 |
+
|
| 29 |
+
@abstractmethod
|
| 30 |
+
def encode_info(self, *args, **kwargs):
|
| 31 |
+
pass
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class AbsReranker(ABC, nn.Module):
|
| 35 |
+
|
| 36 |
+
@abstractmethod
|
| 37 |
+
def compute_score(self, *args, **kwargs):
|
| 38 |
+
pass
|
evaluation/nexus/Nexus/abc/training/reranker/AbsArguments.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from typing import Optional
|
| 3 |
+
from dataclasses import dataclass, field
|
| 4 |
+
|
| 5 |
+
from transformers import TrainingArguments
|
| 6 |
+
from Nexus.abc.training.arguments import AbsDataArguments, AbsModelArguments, AbsTrainingArguments
|
| 7 |
+
|
| 8 |
+
@dataclass
|
| 9 |
+
class AbsRerankerModelArguments(AbsModelArguments):
|
| 10 |
+
"""
|
| 11 |
+
Abstract class for reranker model arguments.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
model_name_or_path: str = field(
|
| 15 |
+
metadata={"help": "The model checkpoint for initialization."}
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@dataclass
|
| 22 |
+
class AbsRerankerDataArguments(AbsDataArguments):
|
| 23 |
+
"""
|
| 24 |
+
Abstract class for reranker data arguments.
|
| 25 |
+
"""
|
| 26 |
+
train_data: str = field(
|
| 27 |
+
default=None, metadata={
|
| 28 |
+
"help": "One or more paths to training data. `query: str`, `pos: List[str]`, `neg: List[str]` are required in the training data.",
|
| 29 |
+
"nargs": "+"
|
| 30 |
+
}
|
| 31 |
+
)
|
| 32 |
+
cache_path: Optional[str] = field(
|
| 33 |
+
default=None, metadata={"help": "Where do you want to store the cached data"}
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@dataclass
|
| 38 |
+
class AbsRerankerTrainingArguments(AbsTrainingArguments):
|
| 39 |
+
pass
|
evaluation/nexus/Nexus/abc/training/reranker/AbsDataset.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from dataclasses import dataclass
|
| 3 |
+
from Nexus.abc.training.dataset import AbsDataset
|
| 4 |
+
|
| 5 |
+
logger = logging.getLogger(__name__)
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class AbsRerankerTrainDataset(AbsDataset):
|
| 9 |
+
"""Abstract class for reranker training dataset.
|
| 10 |
+
|
| 11 |
+
Args:
|
| 12 |
+
args (AbsRerankerDataArguments): Data arguments.
|
| 13 |
+
tokenizer (PreTrainedTokenizer): Tokenizer to use.
|
| 14 |
+
"""
|
| 15 |
+
pass
|
| 16 |
+
|
| 17 |
+
@dataclass
|
| 18 |
+
class AbsRerankerCollator():
|
| 19 |
+
"""
|
| 20 |
+
The abstract reranker collator.
|
| 21 |
+
"""
|
| 22 |
+
def __call__(self, features):
|
| 23 |
+
return super().__call__(features)
|
| 24 |
+
|
| 25 |
+
# remove Abs LLM reranker class
|
evaluation/nexus/Nexus/abc/training/reranker/AbsModeling.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn, Tensor
|
| 3 |
+
from transformers import AutoTokenizer
|
| 4 |
+
from transformers.file_utils import ModelOutput
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
from dataclasses import dataclass
|
| 8 |
+
from abc import ABC, abstractmethod
|
| 9 |
+
from typing import Dict, Optional, List, Union
|
| 10 |
+
|
| 11 |
+
from Nexus.abc.training.modeling import AbsModelOutput, AbsModel, AbsReranker
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@dataclass
|
| 17 |
+
class RerankerOutput(AbsModelOutput, ModelOutput):
|
| 18 |
+
scores: Optional[Tensor] = None
|
| 19 |
+
embedding: Optional[Tensor] = None
|
| 20 |
+
|
| 21 |
+
def to_dict(self):
|
| 22 |
+
return {k: v for k, v in self.__dict__.items()}
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class AbsRerankerModel(AbsReranker, nn.Module):
|
| 26 |
+
"""Abstract class of embedding model for training.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
base_model: The base model to train on.
|
| 30 |
+
tokenizer (AutoTokenizer, optional): The tokenizer to use. Defaults to ``None``.
|
| 31 |
+
train_batch_size (int, optional): Batch size used for training. Defaults to ``4``.
|
| 32 |
+
"""
|
| 33 |
+
def __init__(self, *args, **kwargs):
|
| 34 |
+
super().__init__(*args, **kwargs)
|
| 35 |
+
self.init_modules()
|
| 36 |
+
|
| 37 |
+
def init_modules(self):
|
| 38 |
+
self.loss_function=self.get_loss_function()
|
| 39 |
+
self.score_function=self.get_score_function()
|
| 40 |
+
|
| 41 |
+
@abstractmethod
|
| 42 |
+
def get_loss_function(self):
|
| 43 |
+
pass
|
| 44 |
+
|
| 45 |
+
@abstractmethod
|
| 46 |
+
def get_score_function(self):
|
| 47 |
+
pass
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def forward(self, batch, *args, **kwargs):
|
| 51 |
+
"""The computation performed at every call.
|
| 52 |
+
"""
|
| 53 |
+
return self.compute_loss(batch, *args, **kwargs)
|
| 54 |
+
|
| 55 |
+
@abstractmethod
|
| 56 |
+
def compute_score(self, *args, **kwargs):
|
| 57 |
+
return super().compute_score(*args, **kwargs)
|
| 58 |
+
|
| 59 |
+
@abstractmethod
|
| 60 |
+
def compute_loss(self, *args, **kwargs):
|
| 61 |
+
"""Compute the loss.
|
| 62 |
+
"""
|
| 63 |
+
pass
|
| 64 |
+
|
| 65 |
+
def save(self, output_dir: str):
|
| 66 |
+
"""Save the model.
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
output_dir (str): Directory for saving the model.
|
| 70 |
+
"""
|
| 71 |
+
pass
|
| 72 |
+
|
| 73 |
+
def save_pretrained(self, *args, **kwargs):
|
| 74 |
+
"""
|
| 75 |
+
save model (and tokenizer if has)
|
| 76 |
+
"""
|
| 77 |
+
pass
|
evaluation/nexus/Nexus/abc/training/reranker/AbsRunner.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import logging
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from typing import Tuple
|
| 5 |
+
from abc import ABC, abstractmethod
|
| 6 |
+
from transformers import set_seed, PreTrainedTokenizer
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
from .AbsArguments import (
|
| 10 |
+
AbsRerankerModelArguments,
|
| 11 |
+
AbsRerankerDataArguments,
|
| 12 |
+
AbsRerankerTrainingArguments
|
| 13 |
+
)
|
| 14 |
+
from .AbsTrainer import AbsRerankerTrainer
|
| 15 |
+
from .AbsModeling import AbsRerankerModel
|
| 16 |
+
from .AbsDataset import (
|
| 17 |
+
AbsRerankerTrainDataset, AbsRerankerCollator
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
from Nexus.abc.training.runner import AbsRunner
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
logger = logging.getLogger(__name__)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class AbsRerankerRunner(AbsRunner):
|
| 27 |
+
"""Abstract class to run reranker model fine-tuning.
|
| 28 |
+
|
| 29 |
+
Args:
|
| 30 |
+
model_args (AbsRerankerModelArguments): Model arguments
|
| 31 |
+
data_args (AbsRerankerDataArguments): Data arguments.
|
| 32 |
+
training_args (AbsRerankerTrainingArguments): Training arguments.
|
| 33 |
+
"""
|
| 34 |
+
def __init__(
|
| 35 |
+
self,
|
| 36 |
+
model_args: AbsRerankerModelArguments,
|
| 37 |
+
data_args: AbsRerankerDataArguments,
|
| 38 |
+
training_args: AbsRerankerTrainingArguments
|
| 39 |
+
):
|
| 40 |
+
self.model_args = model_args
|
| 41 |
+
self.data_args = data_args
|
| 42 |
+
self.training_args = training_args
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
@abstractmethod
|
| 46 |
+
def load_model(self, *args, **kwargs) -> AbsRerankerModel:
|
| 47 |
+
pass
|
| 48 |
+
|
| 49 |
+
@abstractmethod
|
| 50 |
+
def load_trainer(self) -> AbsRerankerTrainer:
|
| 51 |
+
"""Abstract method to load the trainer.
|
| 52 |
+
|
| 53 |
+
Returns:
|
| 54 |
+
AbsRerankerTrainer: The loaded trainer instance.
|
| 55 |
+
"""
|
| 56 |
+
pass
|
| 57 |
+
|
| 58 |
+
@abstractmethod
|
| 59 |
+
def load_dataset(self, *args, **kwargs):
|
| 60 |
+
pass
|
| 61 |
+
|
| 62 |
+
def load_data_collator(self) -> AbsRerankerCollator:
|
| 63 |
+
"""Loads the appropriate data collator.
|
| 64 |
+
"""
|
| 65 |
+
pass
|
| 66 |
+
|
| 67 |
+
def run(self):
|
| 68 |
+
"""
|
| 69 |
+
Executes the training process.
|
| 70 |
+
"""
|
| 71 |
+
Path(self.training_args.output_dir).mkdir(parents=True, exist_ok=True)
|
| 72 |
+
|
| 73 |
+
# Training
|
| 74 |
+
self.trainer.train(resume_from_checkpoint=self.training_args.resume_from_checkpoint)
|
| 75 |
+
self.trainer.save_model()
|
evaluation/nexus/Nexus/abc/training/reranker/AbsTrainer.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from typing import Optional
|
| 3 |
+
from abc import ABC, abstractmethod
|
| 4 |
+
from transformers.trainer import Trainer
|
| 5 |
+
from Nexus.abc.training.trainer import AbsTrainer
|
| 6 |
+
from Nexus.abc.training.dataset import AbsDataset
|
| 7 |
+
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
class AbsRerankerTrainer(AbsTrainer):
|
| 11 |
+
"""
|
| 12 |
+
Abstract class for the trainer of reranker.
|
| 13 |
+
"""
|
| 14 |
+
@abstractmethod
|
| 15 |
+
def _save(self, output_dir: Optional[str] = None, state_dict=None):
|
| 16 |
+
pass
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def compute_loss(self, model, inputs, return_outputs=False, **kwargs):
|
| 20 |
+
"""
|
| 21 |
+
How the loss is computed by Trainer. By default, all models return the loss in the first element.
|
| 22 |
+
|
| 23 |
+
Subclass and override for custom behavior.
|
| 24 |
+
|
| 25 |
+
Args:
|
| 26 |
+
model (AbsRerankerModel): The model being trained.
|
| 27 |
+
inputs (dict): A dictionary of input tensors to be passed to the model.
|
| 28 |
+
return_outputs (bool, optional): If ``True``, returns both the loss and the model's outputs. Otherwise,
|
| 29 |
+
returns only the loss. Defaults to ``False``.
|
| 30 |
+
|
| 31 |
+
Returns:
|
| 32 |
+
Union[torch.Tensor, tuple(torch.Tensor, RerankerOutput)]: The computed loss. If ``return_outputs`` is ``True``,
|
| 33 |
+
also returns the model's outputs in a tuple ``(loss, outputs)``.
|
| 34 |
+
"""
|
| 35 |
+
inputs_dict={
|
| 36 |
+
'batch':inputs
|
| 37 |
+
}
|
| 38 |
+
outputs = model(**inputs_dict)
|
| 39 |
+
loss = outputs.loss
|
| 40 |
+
|
| 41 |
+
return (loss, outputs) if return_outputs else loss
|
evaluation/nexus/Nexus/abc/training/reranker/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .AbsArguments import AbsRerankerDataArguments, AbsRerankerModelArguments, AbsRerankerTrainingArguments
|
| 2 |
+
from .AbsDataset import (
|
| 3 |
+
AbsRerankerTrainDataset, AbsRerankerCollator
|
| 4 |
+
)
|
| 5 |
+
from .AbsModeling import AbsRerankerModel, RerankerOutput
|
| 6 |
+
from .AbsTrainer import AbsRerankerTrainer
|
| 7 |
+
from .AbsRunner import AbsRerankerRunner
|
| 8 |
+
|
| 9 |
+
__all__ = [
|
| 10 |
+
"AbsRerankerDataArguments",
|
| 11 |
+
"AbsRerankerModelArguments",
|
| 12 |
+
"AbsRerankerTrainingArguments",
|
| 13 |
+
"AbsRerankerTrainDataset",
|
| 14 |
+
"AbsRerankerCollator",
|
| 15 |
+
"AbsRerankerModel",
|
| 16 |
+
"RerankerOutput",
|
| 17 |
+
"AbsRerankerTrainer",
|
| 18 |
+
"AbsRerankerRunner",
|
| 19 |
+
]
|
evaluation/nexus/Nexus/abc/training/runner.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
|
| 3 |
+
from .dataset import AbsDataset
|
| 4 |
+
from .modeling import AbsModel
|
| 5 |
+
from .trainer import AbsTrainer
|
| 6 |
+
from .arguments import AbsTrainingArguments, AbsDataArguments, AbsModelArguments
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class AbsRunner(ABC):
|
| 10 |
+
def __init__(
|
| 11 |
+
self,
|
| 12 |
+
model_args: AbsModelArguments,
|
| 13 |
+
data_args: AbsDataArguments,
|
| 14 |
+
training_args: AbsTrainingArguments,
|
| 15 |
+
):
|
| 16 |
+
self.model_args = model_args
|
| 17 |
+
self.data_args = data_args
|
| 18 |
+
self.training_args = training_args
|
| 19 |
+
|
| 20 |
+
@abstractmethod
|
| 21 |
+
def load_model(self, *args, **kwargs) -> AbsModel:
|
| 22 |
+
pass
|
| 23 |
+
|
| 24 |
+
@abstractmethod
|
| 25 |
+
def load_dataset(self, *args, **kwargs) -> AbsDataset:
|
| 26 |
+
pass
|
| 27 |
+
|
| 28 |
+
@abstractmethod
|
| 29 |
+
def load_trainer(self, *args, **kwargs) -> AbsTrainer:
|
| 30 |
+
pass
|
| 31 |
+
|
| 32 |
+
@abstractmethod
|
| 33 |
+
def run(self, *args, **kwargs):
|
| 34 |
+
pass
|
evaluation/nexus/Nexus/abc/training/trainer.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
import inspect
|
| 3 |
+
from typing import Optional
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
from .dataset import AbsDataset
|
| 8 |
+
from .modeling import AbsModel
|
| 9 |
+
from .arguments import AbsTrainingArguments
|
| 10 |
+
from transformers import Trainer
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def _patch_accelerate_unwrap_model_signature():
|
| 14 |
+
try:
|
| 15 |
+
from accelerate import Accelerator
|
| 16 |
+
except ImportError:
|
| 17 |
+
return
|
| 18 |
+
|
| 19 |
+
signature = inspect.signature(Accelerator.unwrap_model)
|
| 20 |
+
if "keep_torch_compile" in signature.parameters:
|
| 21 |
+
return
|
| 22 |
+
if getattr(Accelerator.unwrap_model, "_nexus_keep_torch_compile_patch", False):
|
| 23 |
+
return
|
| 24 |
+
|
| 25 |
+
original_unwrap_model = Accelerator.unwrap_model
|
| 26 |
+
|
| 27 |
+
def compatible_unwrap_model(self, model, keep_fp32_wrapper: bool = True, keep_torch_compile: bool = False):
|
| 28 |
+
return original_unwrap_model(self, model, keep_fp32_wrapper=keep_fp32_wrapper)
|
| 29 |
+
|
| 30 |
+
compatible_unwrap_model._nexus_keep_torch_compile_patch = True
|
| 31 |
+
Accelerator.unwrap_model = compatible_unwrap_model
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
_patch_accelerate_unwrap_model_signature()
|
| 35 |
+
|
| 36 |
+
class AbsTrainer(Trainer):
|
| 37 |
+
@abstractmethod
|
| 38 |
+
def _save(self, output_dir: Optional[str] = None, *args, **kwargs):
|
| 39 |
+
return super()._save(output_dir, *args, **kwargs)
|
| 40 |
+
|
| 41 |
+
@torch.no_grad()
|
| 42 |
+
@abstractmethod
|
| 43 |
+
def evaluate(self, eval_dataset: AbsDataset, *args, **kwargs):
|
| 44 |
+
return super().evaluate(eval_dataset, **args, **kwargs)
|
evaluation/nexus/Nexus/data_mining/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Data mining utilities for Nexus."""
|
| 2 |
+
|
evaluation/nexus/Nexus/data_mining/multimodal_hard_negative/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Multimodal hard negative mining for Nexus training JSONL files."""
|
| 2 |
+
|
| 3 |
+
from .mining import run_mining
|
| 4 |
+
|
| 5 |
+
__all__ = ["run_mining"]
|
| 6 |
+
|