tomaarsen HF Staff commited on
Commit
83fbe75
·
verified ·
1 Parent(s): e2a1c05

Integrate with Sentence Transformers via MultiVectorEncoder

Browse files
1_Dense/config.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "in_features": 2048,
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:75e5f5783ba184514e1bf9657296dbe12d78853233f16b4098271350738a235a
3
+ size 1049248
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,5 @@
 
 
 
 
 
 
1
+ {
2
+ "skiplist_words": [],
3
+ "skiplist_tasks": [],
4
+ "keep_only_token_ids": null
5
+ }
README.md CHANGED
@@ -20,6 +20,8 @@ tags:
20
  - multimodal_embedding
21
  - multilingual_embedding
22
  - Text-to-Visual Document (T→VD) retrieval
 
 
23
  library_name: peft
24
  pipeline_tag: visual-document-retrieval
25
  ---
@@ -85,6 +87,68 @@ KeyError: 'qwen2_5_vl'
85
 
86
  ## Usage
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  ```python
89
  import torch
90
  from PIL import Image
 
20
  - multimodal_embedding
21
  - multilingual_embedding
22
  - Text-to-Visual Document (T→VD) retrieval
23
+ - sentence-transformers
24
+ - multi-vector
25
  library_name: peft
26
  pipeline_tag: visual-document-retrieval
27
  ---
 
87
 
88
  ## Usage
89
 
90
+ ### Using Sentence Transformers
91
+
92
+ This checkpoint can be used as a multi-vector (ColBERT-style late interaction) retriever with Sentence Transformers via the `MultiVectorEncoder`:
93
+
94
+ ```bash
95
+ pip install "sentence-transformers[image]>=6.0.0"
96
+ ```
97
+
98
+ ```python
99
+ from sentence_transformers import MultiVectorEncoder
100
+
101
+ model = MultiVectorEncoder("Metric-AI/ColQwen2.5-3b-multilingual-v1.0")
102
+
103
+ queries = [
104
+ "What is the variable represented on the y-axis of the graph?",
105
+ "Total outlay is maximum in which year?",
106
+ ]
107
+ images = [
108
+ "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc1.jpg",
109
+ "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc2.jpg",
110
+ "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc3.jpg",
111
+ "https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc4.jpg",
112
+ ]
113
+
114
+ query_embeddings = model.encode_query(queries)
115
+ document_embeddings = model.encode_document(images)
116
+ print(f"Query 0 shape: {tuple(query_embeddings[0].shape)}")
117
+ print(f"Document 0 shape: {tuple(document_embeddings[0].shape)}")
118
+ # Query 0 shape: (25, 128)
119
+ # Document 0 shape: (4115, 128)
120
+
121
+ # MaxSim late-interaction scoring (rows = queries, columns = images)
122
+ scores = model.similarity(query_embeddings, document_embeddings)
123
+ print(scores)
124
+ # tensor([[15.6797, 12.8027, 12.3555, 12.2031],
125
+ # [ 8.9121, 15.5352, 9.7109, 8.1387]])
126
+ ```
127
+
128
+ `encode_query` applies the `Query: ` prefix and the ten `<|endoftext|>` augmentation tokens, `encode_document` applies the visual prompt, and both apply the 128-dimensional projection, the L2 normalization and the padding mask. Documents can be file paths, URLs or `PIL.Image` objects, and both methods accept a `batch_size`.
129
+
130
+ The scores above come from the plain load, which uses the base checkpoint's `bfloat16` weights. Loading options are forwarded through `model_kwargs`:
131
+
132
+ ```python
133
+ model = MultiVectorEncoder(
134
+ "Metric-AI/ColQwen2.5-3b-multilingual-v1.0",
135
+ model_kwargs={"dtype": "float32", "attn_implementation": "sdpa", "device_map": "cuda:0"},
136
+ )
137
+ ```
138
+
139
+ Note that `preprocessor_config.json` in this repository keeps the stock Qwen2.5-VL `max_pixels` of 12845056, so a full page turns into roughly 4000 to 5000 visual tokens rather than the 768 patches mentioned above. Pass `processor_kwargs={"max_pixels": 768 * 28 * 28}` to cap it, which is the Sentence Transformers equivalent of `ColQwen2_5_Processor.from_pretrained(..., max_num_visual_tokens=768)`.
140
+
141
+ ### Using ColPali Engine
142
+
143
+ > [!WARNING]
144
+ > Current `colpali-engine` no longer sends the `Query: ` prefix that this checkpoint was trained
145
+ > with. It was dropped from `ColQwen2_5_Processor` in 0.3.11
146
+ > ([illuin-tech/colpali#280](https://github.com/illuin-tech/colpali/pull/280)). The Sentence
147
+ > Transformers configuration in this repository reproduces the original training-time format, so
148
+ > its query embeddings differ slightly from current `colpali-engine` output. Install
149
+ > `colpali-engine<0.3.11`, or set `processor.query_prefix = "Query: "` after loading, to get the
150
+ > training-time format from the snippet below.
151
+
152
  ```python
153
  import torch
154
  from PIL import Image
additional_chat_templates/sentence_transformers.jinja ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- if task is defined and task == 'query' -%}
2
+ {%- for message in messages -%}
3
+ {%- for content in message['content'] -%}
4
+ {%- if content['type'] == 'text' -%}
5
+ {{- 'Query: ' + content['text'] -}}
6
+ {%- for _ in range(10) -%}
7
+ {{- '<|endoftext|>' -}}
8
+ {%- endfor -%}
9
+ {%- endif -%}
10
+ {%- endfor -%}
11
+ {%- endfor -%}
12
+ {%- else -%}
13
+ {%- for message in messages -%}
14
+ {%- for content in message['content'] -%}
15
+ {%- if content['type'] == 'image' -%}
16
+ {{- '<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>Describe the image.<|im_end|><|endoftext|>' -}}
17
+ {%- elif content['type'] == 'text' -%}
18
+ {{- content['text'] -}}
19
+ {%- endif -%}
20
+ {%- endfor -%}
21
+ {%- endfor -%}
22
+ {%- endif -%}
chat_template.jinja ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {% set image_count = namespace(value=0) %}{% set video_count = namespace(value=0) %}{% for message in messages %}{% if loop.first and message['role'] != 'system' %}<|im_start|>system
2
+ You are a helpful assistant.<|im_end|>
3
+ {% endif %}<|im_start|>{{ message['role'] }}
4
+ {% if message['content'] is string %}{{ message['content'] }}<|im_end|>
5
+ {% else %}{% for content in message['content'] %}{% if content['type'] == 'image' or 'image' in content or 'image_url' in content %}{% set image_count.value = image_count.value + 1 %}{% if add_vision_id %}Picture {{ image_count.value }}: {% endif %}<|vision_start|><|image_pad|><|vision_end|>{% elif content['type'] == 'video' or 'video' in content %}{% set video_count.value = video_count.value + 1 %}{% if add_vision_id %}Video {{ video_count.value }}: {% endif %}<|vision_start|><|video_pad|><|vision_end|>{% elif 'text' in content %}{{ content['text'] }}{% endif %}{% endfor %}<|im_end|>
6
+ {% endif %}{% endfor %}{% if add_generation_prompt %}<|im_start|>assistant
7
+ {% endif %}
chat_template.json DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "chat_template": "{% set image_count = namespace(value=0) %}{% set video_count = namespace(value=0) %}{% for message in messages %}{% if loop.first and message['role'] != 'system' %}<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n{% endif %}<|im_start|>{{ message['role'] }}\n{% if message['content'] is string %}{{ message['content'] }}<|im_end|>\n{% else %}{% for content in message['content'] %}{% if content['type'] == 'image' or 'image' in content or 'image_url' in content %}{% set image_count.value = image_count.value + 1 %}{% if add_vision_id %}Picture {{ image_count.value }}: {% endif %}<|vision_start|><|image_pad|><|vision_end|>{% elif content['type'] == 'video' or 'video' in content %}{% set video_count.value = video_count.value + 1 %}{% if add_vision_id %}Video {{ video_count.value }}: {% endif %}<|vision_start|><|video_pad|><|vision_end|>{% elif 'text' in content %}{{ content['text'] }}{% endif %}{% endfor %}<|im_end|>\n{% endif %}{% endfor %}{% if add_generation_prompt %}<|im_start|>assistant\n{% endif %}"
3
- }
 
 
 
 
config_sentence_transformers.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "__version__": {
3
+ "sentence_transformers": "6.0.0"
4
+ },
5
+ "default_prompt_name": null,
6
+ "model_type": "MultiVectorEncoder",
7
+ "requirements": {
8
+ "transformers": {
9
+ "specifier": ">=5.15",
10
+ "reason": "Older versions ignore the key_mapping, which silently randomizes the adapter weights."
11
+ }
12
+ },
13
+ "prompts": {
14
+ "document": "",
15
+ "query": ""
16
+ },
17
+ "similarity_fn_name": null
18
+ }
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.base.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 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "processor_class": "Qwen2_5_VLProcessor"
3
+ }
sentence_bert_config.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ "model_kwargs": {
21
+ "key_mapping": {
22
+ "^model\\.": "language_model."
23
+ }
24
+ },
25
+ "processing_kwargs": {
26
+ "chat_template": {
27
+ "chat_template": "sentence_transformers"
28
+ }
29
+ }
30
+ }
tokenizer_config.json CHANGED
@@ -202,6 +202,7 @@
202
  "extra_special_tokens": {},
203
  "model_max_length": 131072,
204
  "pad_token": "<|endoftext|>",
 
205
  "processor_class": "ColQwen2_5Processor",
206
  "split_special_tokens": false,
207
  "tokenizer_class": "Qwen2Tokenizer",
 
202
  "extra_special_tokens": {},
203
  "model_max_length": 131072,
204
  "pad_token": "<|endoftext|>",
205
+ "padding_side": "left",
206
  "processor_class": "ColQwen2_5Processor",
207
  "split_special_tokens": false,
208
  "tokenizer_class": "Qwen2Tokenizer",