tomaarsen HF Staff commited on
Commit
c588694
·
verified ·
1 Parent(s): 8e92bfb

Integrate with Sentence Transformers and implement replace_image_token

Browse files
1_MultiVectorMask/config.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "skiplist_words": [],
3
+ "skiplist_tasks": [
4
+ "document"
5
+ ],
6
+ "keep_only_token_ids": null
7
+ }
README.md CHANGED
@@ -23,6 +23,8 @@ tags:
23
  - colbert
24
  - colqwen3_5
25
  - multilingual-embedding
 
 
26
  ---
27
 
28
  # webAI-Official/webAI-ColVec1.1-4b
@@ -121,6 +123,81 @@ checkpoints.
121
 
122
  ## 💻 Usage
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  The processor provides the current retrieval API:
125
 
126
  - `process_images(images)` prepares one or more document images.
 
23
  - colbert
24
  - colqwen3_5
25
  - multilingual-embedding
26
+ - sentence-transformers
27
+ - multi-vector
28
  ---
29
 
30
  # webAI-Official/webAI-ColVec1.1-4b
 
123
 
124
  ## 💻 Usage
125
 
126
+ ### Using Sentence Transformers
127
+
128
+ The checkpoint loads as a `MultiVectorEncoder`, which is available from
129
+ Sentence Transformers v6.0.0:
130
+
131
+ ```bash
132
+ pip install "sentence-transformers[image]>=6.0.0"
133
+ ```
134
+
135
+ `encode_query` and `encode_document` apply the query and document prompt
136
+ formats, the ten query-augmentation tokens, and the L2-normalized
137
+ 640-dimensional projection. `similarity` computes the MaxSim score matrix.
138
+
139
+ ```python
140
+ from io import BytesIO
141
+
142
+ import requests
143
+ from PIL import Image
144
+ from sentence_transformers import MultiVectorEncoder
145
+
146
+ model = MultiVectorEncoder("webAI-Official/webAI-ColVec1.1-4b", trust_remote_code=True)
147
+
148
+ queries = [
149
+ "When was the United States Declaration of Independence proclaimed?",
150
+ "Who printed the edition of Romeo and Juliet?",
151
+ ]
152
+ document_urls = [
153
+ "https://upload.wikimedia.org/wikipedia/commons/8/89/US-original-Declaration-1776.jpg",
154
+ "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Romeoandjuliet1597.jpg/500px-Romeoandjuliet1597.jpg",
155
+ ]
156
+ documents = [
157
+ Image.open(BytesIO(requests.get(url, headers={"User-Agent": "Mozilla/5.0"}, timeout=30).content))
158
+ for url in document_urls
159
+ ]
160
+
161
+ query_embeddings = model.encode_query(queries)
162
+ document_embeddings = model.encode_document(documents)
163
+ print(query_embeddings[0].shape, document_embeddings[0].shape)
164
+ # (27, 640) (523, 640)
165
+
166
+ scores = model.similarity(query_embeddings, document_embeddings)
167
+ print(scores)
168
+ # tensor([[23.3935, 5.7660],
169
+ # [ 4.8504, 23.1851]])
170
+ print("Best document per query:", scores.argmax(dim=1))
171
+ # Best document per query: tensor([0, 1])
172
+ ```
173
+
174
+ Documents may also be given as file paths or URLs instead of `PIL.Image`
175
+ objects. The Wikimedia URLs above are fetched with a browser `User-Agent`
176
+ because Wikimedia rejects the default one. Both encode methods accept a
177
+ `batch_size`, and the loading options are forwarded through `model_kwargs`
178
+ (`dtype`, `attn_implementation`, `device_map`) and `processor_kwargs`
179
+ (`max_num_visual_tokens`):
180
+
181
+ ```python
182
+ model = MultiVectorEncoder(
183
+ "webAI-Official/webAI-ColVec1.1-4b",
184
+ trust_remote_code=True,
185
+ model_kwargs={"attn_implementation": "sdpa", "device_map": "cuda:0"},
186
+ processor_kwargs={"max_num_visual_tokens": 1024},
187
+ )
188
+ ```
189
+
190
+ The scores above come from the plain load, which uses the checkpoint's
191
+ `bfloat16` weights and SDPA. MaxSim is accumulated in `float32` rather
192
+ than in the embedding dtype, matching the scoring path behind the reported
193
+ evaluation, so a `bfloat16` `score_retrieval` call on the same embeddings
194
+ returns coarser values. The scores still move in the second decimal place
195
+ across PyTorch and Transformers builds. Text passed to `encode_document`
196
+ is rendered as a query, because the model defines no text-document format,
197
+ and a warning says so.
198
+
199
+ ### Using transformers
200
+
201
  The processor provides the current retrieval API:
202
 
203
  - `process_images(images)` prepares one or more document images.
additional_chat_templates/sentence_transformers.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
+ {{- '<|im_start|>user\nQuery: ' + ns.text + '<|endoftext|>' * 10 + '<|im_end|><|endoftext|>' -}}
18
+ {%- endif -%}
19
+ {%- endfor -%}
config_sentence_transformers.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "MultiVectorEncoder",
3
+ "similarity_fn_name": "maxsim",
4
+ "prompts": {},
5
+ "default_prompt_name": null,
6
+ "__version__": {
7
+ "sentence_transformers": "6.0.0"
8
+ }
9
+ }
modules.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_MultiVectorMask",
12
+ "type": "sentence_transformers.multi_vector_encoder.modules.multi_vector_mask.MultiVectorMask"
13
+ }
14
+ ]
processing_colqwen35_bidirection.py CHANGED
@@ -166,6 +166,17 @@ class ColQwen35BidirectionProcessor(ProcessorMixin):
166
  if cur_min_pixels > max_pixels:
167
  self.image_processor.min_pixels = max_pixels
168
 
 
 
 
 
 
 
 
 
 
 
 
169
  @classmethod
170
  def from_pretrained(
171
  cls,
 
166
  if cur_min_pixels > max_pixels:
167
  self.image_processor.min_pixels = max_pixels
168
 
169
+ def replace_image_token(self, image_inputs: dict, image_idx: int) -> str:
170
+ """Expand one ``<|image_pad|>`` placeholder into its per-image token run.
171
+
172
+ ``ProcessorMixin.__call__`` delegates placeholder expansion here, and
173
+ ``apply_chat_template`` calls ``__call__``, so without this both raise
174
+ ``NotImplementedError`` for image inputs.
175
+ """
176
+ merge_length = self.image_processor.merge_size ** 2
177
+ num_image_tokens = image_inputs["image_grid_thw"][image_idx].prod() // merge_length
178
+ return self.image_token * num_image_tokens
179
+
180
  @classmethod
181
  def from_pretrained(
182
  cls,
sentence_bert_config.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "transformer_task": "retrieval",
3
+ "modality_config": {
4
+ "text": {
5
+ "method": "forward",
6
+ "method_output_name": null
7
+ },
8
+ "image": {
9
+ "method": "forward",
10
+ "method_output_name": null
11
+ },
12
+ "message": {
13
+ "method": "forward",
14
+ "method_output_name": null,
15
+ "format": "structured"
16
+ }
17
+ },
18
+ "module_output_name": "token_embeddings",
19
+ "processing_kwargs": {
20
+ "chat_template": {
21
+ "chat_template": "sentence_transformers"
22
+ },
23
+ "text": {
24
+ "return_mm_token_type_ids": true
25
+ }
26
+ }
27
+ }