woooooz commited on
Commit
bb2b5cc
·
verified ·
1 Parent(s): b88992f

Package upstream model with reproducible local MMEB evaluation

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. LICENSE +434 -0
  3. README.md +22 -0
  4. UPSTREAM_README.md +228 -0
  5. additional_chat_templates/sentence_transformers.jinja +32 -0
  6. chat_template.jinja +154 -0
  7. config.json +122 -0
  8. config_sentence_transformers.json +11 -0
  9. embedding_chat_template.jinja +28 -0
  10. evaluation/README.md +31 -0
  11. evaluation/configs/image_retrieval.yaml +75 -0
  12. evaluation/configs/moment_retrieval.yaml +35 -0
  13. evaluation/configs/video_ret_5.yaml +45 -0
  14. evaluation/configs/visdoc_full.yaml +144 -0
  15. evaluation/nexus/LICENSE +22 -0
  16. evaluation/nexus/Nexus/__init__.py +24 -0
  17. evaluation/nexus/Nexus/abc/__init__.py +10 -0
  18. evaluation/nexus/Nexus/abc/arguments.py +197 -0
  19. evaluation/nexus/Nexus/abc/evaluation/__init__.py +13 -0
  20. evaluation/nexus/Nexus/abc/evaluation/arguments.py +31 -0
  21. evaluation/nexus/Nexus/abc/evaluation/data_loader.py +11 -0
  22. evaluation/nexus/Nexus/abc/evaluation/evaluator.py +24 -0
  23. evaluation/nexus/Nexus/abc/evaluation/runner.py +41 -0
  24. evaluation/nexus/Nexus/abc/inference/__init__.py +24 -0
  25. evaluation/nexus/Nexus/abc/inference/arguments.py +72 -0
  26. evaluation/nexus/Nexus/abc/inference/embedder/AbsEmbedder.py +264 -0
  27. evaluation/nexus/Nexus/abc/inference/embedder/__init__.py +1 -0
  28. evaluation/nexus/Nexus/abc/inference/inference_engine.py +87 -0
  29. evaluation/nexus/Nexus/abc/inference/reranker/AbsReranker.py +188 -0
  30. evaluation/nexus/Nexus/abc/inference/reranker/__init__.py +1 -0
  31. evaluation/nexus/Nexus/abc/training/__init__.py +0 -0
  32. evaluation/nexus/Nexus/abc/training/arguments.py +36 -0
  33. evaluation/nexus/Nexus/abc/training/dataset.py +8 -0
  34. evaluation/nexus/Nexus/abc/training/embedder/AbsArguments.py +46 -0
  35. evaluation/nexus/Nexus/abc/training/embedder/AbsDataset.py +34 -0
  36. evaluation/nexus/Nexus/abc/training/embedder/AbsModeling.py +85 -0
  37. evaluation/nexus/Nexus/abc/training/embedder/AbsRunner.py +79 -0
  38. evaluation/nexus/Nexus/abc/training/embedder/AbsTrainer.py +39 -0
  39. evaluation/nexus/Nexus/abc/training/embedder/__init__.py +25 -0
  40. evaluation/nexus/Nexus/abc/training/modeling.py +38 -0
  41. evaluation/nexus/Nexus/abc/training/reranker/AbsArguments.py +39 -0
  42. evaluation/nexus/Nexus/abc/training/reranker/AbsDataset.py +25 -0
  43. evaluation/nexus/Nexus/abc/training/reranker/AbsModeling.py +77 -0
  44. evaluation/nexus/Nexus/abc/training/reranker/AbsRunner.py +75 -0
  45. evaluation/nexus/Nexus/abc/training/reranker/AbsTrainer.py +41 -0
  46. evaluation/nexus/Nexus/abc/training/reranker/__init__.py +19 -0
  47. evaluation/nexus/Nexus/abc/training/runner.py +34 -0
  48. evaluation/nexus/Nexus/abc/training/trainer.py +44 -0
  49. evaluation/nexus/Nexus/data_mining/__init__.py +2 -0
  50. 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
+ [![Hugging Face](https://img.shields.io/badge/🤗-Hugging%20Face-yellow)](https://huggingface.co/collections/tencent/wemm-embedding)
27
+ [![Technical Report](https://img.shields.io/badge/📄-Technical%20Report-red)](https://arxiv.org/abs/2608.24053)
28
+ [![GitHub](https://img.shields.io/badge/GitHub-WeMM--Embedding-black?logo=github)](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
+