Integrate with Sentence Transformers via MultiVectorEncoder

#1
by tomaarsen HF Staff - opened
1_Dense/config.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "in_features": 2560,
3
+ "out_features": 128,
4
+ "bias": true,
5
+ "activation_function": "torch.nn.modules.linear.Identity",
6
+ "module_input_name": "token_embeddings",
7
+ "module_output_name": "token_embeddings"
8
+ }
1_Dense/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2260e6ae0b1e8c26611963073d6f9c39e14f59897f943eb2bf6762d8baea26d4
3
+ size 1311392
2_Normalize/config.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "module_input_name": "token_embeddings",
3
+ "module_output_name": "token_embeddings"
4
+ }
3_MultiVectorMask/config.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "skiplist_words": []
3
+ }
README.md CHANGED
@@ -22,6 +22,7 @@ tags:
22
  - vidore
23
  - document-retrieval
24
  - multimodal
 
25
  base_model:
26
  - Qwen/Qwen3.5-4B
27
  datasets:
@@ -143,13 +144,64 @@ Evaluated across 8 domains with queries spanning 6 languages (EN, FR, DE, IT, PT
143
 
144
  ## Quick Start
145
 
146
- ### Installation
 
 
 
147
 
148
  ```bash
149
- pip install -r requirements.txt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  ```
151
 
152
- ### Python Inference
 
 
 
 
 
 
153
 
154
  ```python
155
  import torch
 
22
  - vidore
23
  - document-retrieval
24
  - multimodal
25
+ - sentence-transformers
26
  base_model:
27
  - Qwen/Qwen3.5-4B
28
  datasets:
 
144
 
145
  ## Quick Start
146
 
147
+ ### Using Sentence Transformers
148
+
149
+ EVIE loads as a [Sentence Transformers](https://www.sbert.net/) `MultiVectorEncoder`, which exposes
150
+ the familiar `encode_query` / `encode_document` / `similarity` API and computes MaxSim for you:
151
 
152
  ```bash
153
+ pip install "sentence-transformers[image]>=6.0.0"
154
+ ```
155
+
156
+ ```python
157
+ from sentence_transformers import MultiVectorEncoder
158
+
159
+ model = MultiVectorEncoder("tencent/EVIE-Preview-4.5B")
160
+
161
+ queries = [
162
+ "What is the variable represented on the y-axis of the graph?",
163
+ "Total outlay is maximum in which year?",
164
+ ]
165
+ documents = [
166
+ "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc1.jpg",
167
+ "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc2.jpg",
168
+ "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc3.jpg",
169
+ "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc4.jpg",
170
+ ]
171
+
172
+ query_embeddings = model.encode_query(queries)
173
+ document_embeddings = model.encode_document(documents)
174
+ print(query_embeddings[0].shape, document_embeddings[0].shape)
175
+ # torch.Size([23, 128]) torch.Size([755, 128])
176
+
177
+ scores = model.similarity(query_embeddings, document_embeddings)
178
+ print(scores)
179
+ # tensor([[17.2949, 10.7598, 7.9268, 7.3613],
180
+ # [ 6.5547, 13.3711, 6.2627, 6.1104]])
181
+ print("Best document per query:", scores.argmax(dim=1))
182
+ # Best document per query: tensor([0, 1])
183
+ ```
184
+
185
+ Documents may be given as URLs, file paths or `PIL.Image` objects. Bidirectional attention is part of
186
+ the configuration, so no extra call is needed. The scores above come from the plain load, which uses
187
+ the checkpoint's `bfloat16` weights and SDPA. Loading options are forwarded through `model_kwargs`,
188
+ and the 768-visual-token budget is a `processor_kwargs` setting:
189
+
190
+ ```python
191
+ model = MultiVectorEncoder(
192
+ "tencent/EVIE-Preview-4.5B",
193
+ model_kwargs={"attn_implementation": "flash_attention_2", "device_map": "cuda:0"},
194
+ processor_kwargs={"size": {"longest_edge": 1536 * 32 * 32, "shortest_edge": 65536}},
195
+ )
196
  ```
197
 
198
+ Text passed to `encode_document` is rendered as a query, since the model has no text-document format.
199
+
200
+ ### Using ColPali Engine
201
+
202
+ ```bash
203
+ pip install -r requirements.txt
204
+ ```
205
 
206
  ```python
207
  import torch
chat_template.jinja ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- for message in messages -%}
2
+ {%- set ns = namespace(has_image=false, text='') -%}
3
+ {%- if message['content'] is string -%}
4
+ {%- set ns.text = message['content'] -%}
5
+ {%- else -%}
6
+ {%- for item in message['content'] -%}
7
+ {%- if 'image' in item or 'image_url' in item or item.type == 'image' -%}
8
+ {%- set ns.has_image = true -%}
9
+ {%- elif 'text' in item -%}
10
+ {%- set ns.text = ns.text + item.text -%}
11
+ {%- endif -%}
12
+ {%- endfor -%}
13
+ {%- endif -%}
14
+ {%- if ns.has_image -%}
15
+ {{- '<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>Describe the image.<|im_end|><|endoftext|>' -}}
16
+ {%- else -%}
17
+ {{- ns.text + '<|endoftext|>' * 10 -}}
18
+ {%- endif -%}
19
+ {%- endfor -%}
config_sentence_transformers.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "__version__": {
3
+ "sentence_transformers": "6.0.0"
4
+ },
5
+ "model_type": "MultiVectorEncoder",
6
+ "similarity_fn_name": "maxsim",
7
+ "prompts": {},
8
+ "default_prompt_name": null
9
+ }
modules.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "idx": 0,
4
+ "name": "0",
5
+ "path": "",
6
+ "type": "sentence_transformers.base.modules.transformer.Transformer"
7
+ },
8
+ {
9
+ "idx": 1,
10
+ "name": "1",
11
+ "path": "1_Dense",
12
+ "type": "sentence_transformers.base.modules.dense.Dense"
13
+ },
14
+ {
15
+ "idx": 2,
16
+ "name": "2",
17
+ "path": "2_Normalize",
18
+ "type": "sentence_transformers.sentence_transformer.modules.normalize.Normalize"
19
+ },
20
+ {
21
+ "idx": 3,
22
+ "name": "3",
23
+ "path": "3_MultiVectorMask",
24
+ "type": "sentence_transformers.multi_vector_encoder.modules.multi_vector_mask.MultiVectorMask"
25
+ }
26
+ ]
processor_config.json CHANGED
@@ -25,7 +25,7 @@
25
  },
26
  "temporal_patch_size": 2
27
  },
28
- "processor_class": "ColQwen3_5Processor",
29
  "video_processor": {
30
  "do_convert_rgb": true,
31
  "do_normalize": true,
 
25
  },
26
  "temporal_patch_size": 2
27
  },
28
+ "processor_class": "Qwen3VLProcessor",
29
  "video_processor": {
30
  "do_convert_rgb": true,
31
  "do_normalize": true,
sentence_bert_config.json ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "transformer_task": "feature-extraction",
3
+ "modality_config": {
4
+ "text": {
5
+ "method": "forward",
6
+ "method_output_name": "last_hidden_state"
7
+ },
8
+ "image": {
9
+ "method": "forward",
10
+ "method_output_name": "last_hidden_state"
11
+ },
12
+ "message": {
13
+ "method": "forward",
14
+ "method_output_name": "last_hidden_state",
15
+ "format": "structured"
16
+ }
17
+ },
18
+ "module_output_name": "token_embeddings",
19
+ "unpad_inputs": false,
20
+ "config_kwargs": {
21
+ "text_config": {
22
+ "is_causal": false
23
+ }
24
+ }
25
+ }
tokenizer_config.json CHANGED
@@ -21,8 +21,9 @@
21
  "vision_eos_token": "<|vision_end|>"
22
  },
23
  "pad_token": "<|endoftext|>",
 
24
  "pretokenize_regex": "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
25
- "processor_class": "ColQwen3_5Processor",
26
  "split_special_tokens": false,
27
  "tokenizer_class": "Qwen2Tokenizer",
28
  "unk_token": null,
 
21
  "vision_eos_token": "<|vision_end|>"
22
  },
23
  "pad_token": "<|endoftext|>",
24
+ "padding_side": "left",
25
  "pretokenize_regex": "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
26
+ "processor_class": "Qwen3VLProcessor",
27
  "split_special_tokens": false,
28
  "tokenizer_class": "Qwen2Tokenizer",
29
  "unk_token": null,