hxssgaa tomaarsen HF Staff commited on
Commit
be3da89
·
1 Parent(s): bf790bd

Fix transformers 5.x loading and add Sentence Transformers support (#4)

Browse files

- Fix transformers 5.x loading and add Sentence Transformers support (1b87f5df5fe780bcf0da3f35460441f9ac708750)


Co-authored-by: Tom Aarsen <tomaarsen@users.noreply.huggingface.co>

1_MultiVectorMask/config.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {}
README.md CHANGED
@@ -3,6 +3,8 @@ license: apache-2.0
3
  license_name: apache-2.0
4
  license_link: https://www.apache.org/licenses/LICENSE-2.0
5
  tags:
 
 
6
  - text
7
  - image
8
  - video
@@ -156,7 +158,47 @@ pip install transformers pillow requests
156
  pip install flash-attn --no-build-isolation
157
  ```
158
 
159
- ### Inference Code
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
  ```python
162
  import torch
@@ -243,7 +285,7 @@ scores = processor.score_multi_vector(query_embeddings, doc_embeddings)
243
  print(scores)
244
  ```
245
 
246
- ### 🎞️ Lightweight Video Retrieval
247
 
248
  ColQwen3 generalizes to short videos while learning from image-text retrieval task. This minimal example samples a clip with `torchvision`, encodes queries and frames, then pools frame embeddings with a per-dimension max before MaxSim scoring.
249
 
 
3
  license_name: apache-2.0
4
  license_link: https://www.apache.org/licenses/LICENSE-2.0
5
  tags:
6
+ - sentence-transformers
7
+ - multi-vector
8
  - text
9
  - image
10
  - video
 
158
  pip install flash-attn --no-build-isolation
159
  ```
160
 
161
+ ### Using Sentence Transformers
162
+
163
+ `tomoro-colqwen3-embed-4b` can be used as a multi-vector (ColBERT-style late interaction) retriever directly with Sentence Transformers via the `MultiVectorEncoder`.
164
+
165
+ ```bash
166
+ pip install "sentence-transformers[image]>=6.0.0"
167
+ ```
168
+
169
+ ```python
170
+ from sentence_transformers import MultiVectorEncoder
171
+
172
+ model = MultiVectorEncoder("TomoroAI/tomoro-colqwen3-embed-4b", trust_remote_code=True)
173
+
174
+ queries = [
175
+ "What is the variable represented on the y-axis of the graph?",
176
+ "Total outlay is maximum in which year?",
177
+ ]
178
+ documents = [
179
+ f"https://huggingface.co/datasets/sentence-transformers/example-documents/resolve/main/doc{i}.jpg"
180
+ for i in range(1, 5)
181
+ ]
182
+
183
+ query_embeddings = model.encode_query(queries, convert_to_tensor=True)
184
+ document_embeddings = model.encode_document(documents, convert_to_tensor=True)
185
+ print(f"Query 0 shape: {tuple(query_embeddings[0].shape)}")
186
+ print(f"Document 0 shape: {tuple(document_embeddings[0].shape)}")
187
+ # Query 0 shape: (23, 320)
188
+ # Document 0 shape: (1251, 320)
189
+
190
+ # MaxSim late-interaction scoring (rows = queries, columns = images)
191
+ scores = model.similarity(query_embeddings, document_embeddings)
192
+ print(scores)
193
+ # tensor([[12.8291, 9.0850, 6.4121, 5.8818],
194
+ # [ 4.5928, 10.7617, 4.7812, 5.3145]])
195
+ ```
196
+
197
+ > [!NOTE]
198
+ > Pages are tiled adaptively, so document embeddings vary in length (1251 tokens for the first three
199
+ > example pages, 1271 for the fourth). MaxSim handles that, and `model.similarity` masks the padding.
200
+
201
+ ### Using Transformers
202
 
203
  ```python
204
  import torch
 
285
  print(scores)
286
  ```
287
 
288
+ #### 🎞️ Lightweight Video Retrieval
289
 
290
  ColQwen3 generalizes to short videos while learning from image-text retrieval task. This minimal example samples a clip with `torchvision`, encodes queries and frames, then pools frame embeddings with a per-dimension max before MaxSim scoring.
291
 
config_sentence_transformers.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "__version__": {
3
+ "sentence_transformers": "6.0.0"
4
+ },
5
+ "model_type": "MultiVectorEncoder",
6
+ "similarity_fn_name": "maxsim",
7
+ "prompts": {
8
+ "query": "",
9
+ "document": ""
10
+ }
11
+ }
configuration_colqwen3.py CHANGED
@@ -32,7 +32,7 @@ class ColQwen3Config(PretrainedConfig):
32
  """Configuration for ColQwen3 retrieval model."""
33
 
34
  model_type = "colqwen3"
35
- sub_configs: dict[str, Any] = {"vision_config": Qwen3VLVisionConfig, "text_config": Qwen3VLTextConfig}
36
 
37
  def __init__(
38
  self,
@@ -70,9 +70,11 @@ class ColQwen3Config(PretrainedConfig):
70
  if embed_dim <= 0:
71
  raise ValueError(f"`embed_dim` must be positive, got {embed_dim}.")
72
 
73
- super().__init__(**kwargs)
 
74
  self.vision_config = vision_config
75
  self.text_config = text_config
 
76
  self.embed_dim = embed_dim
77
  self.padding_side = padding_side
78
  self.initializer_range = initializer_range
 
32
  """Configuration for ColQwen3 retrieval model."""
33
 
34
  model_type = "colqwen3"
35
+ sub_configs = {"vision_config": Qwen3VLVisionConfig, "text_config": Qwen3VLTextConfig}
36
 
37
  def __init__(
38
  self,
 
70
  if embed_dim <= 0:
71
  raise ValueError(f"`embed_dim` must be positive, got {embed_dim}.")
72
 
73
+ # transformers 5.x runs config validators inside PretrainedConfig.__init__, and
74
+ # validate_token_ids reaches get_text_config(), so the sub-configs must exist first.
75
  self.vision_config = vision_config
76
  self.text_config = text_config
77
+ super().__init__(**kwargs)
78
  self.embed_dim = embed_dim
79
  self.padding_side = padding_side
80
  self.initializer_range = initializer_range
modeling_colqwen3.py CHANGED
@@ -173,6 +173,7 @@ class ColQwen3(ColQwen3PreTrainedModel):
173
  cache_position: Optional[torch.LongTensor] = None,
174
  pixel_values_videos: Optional[torch.Tensor] = None,
175
  video_grid_thw: Optional[torch.LongTensor] = None,
 
176
  ) -> ColQwen3ForRetrievalOutput:
177
  r"""
178
  image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
@@ -201,6 +202,8 @@ class ColQwen3(ColQwen3PreTrainedModel):
201
  image_grid_thw=image_grid_thw,
202
  video_grid_thw=video_grid_thw,
203
  cache_position=cache_position,
 
 
204
  )
205
 
206
  vlm_hidden_states = vlm_output.hidden_states if output_hidden_states else None
@@ -237,7 +240,10 @@ class ColQwen3(ColQwen3PreTrainedModel):
237
  def set_output_embeddings(self, new_embeddings):
238
  self.vlm.set_output_embeddings(new_embeddings)
239
 
240
- def tie_weights(self):
 
 
 
241
  return self.vlm.tie_weights()
242
 
243
  def resize_token_embeddings(
 
173
  cache_position: Optional[torch.LongTensor] = None,
174
  pixel_values_videos: Optional[torch.Tensor] = None,
175
  video_grid_thw: Optional[torch.LongTensor] = None,
176
+ mm_token_type_ids: Optional[torch.LongTensor] = None,
177
  ) -> ColQwen3ForRetrievalOutput:
178
  r"""
179
  image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
 
202
  image_grid_thw=image_grid_thw,
203
  video_grid_thw=video_grid_thw,
204
  cache_position=cache_position,
205
+ # transformers 5.x Qwen3-VL needs this to compute multimodal RoPE.
206
+ **({"mm_token_type_ids": mm_token_type_ids} if mm_token_type_ids is not None else {}),
207
  )
208
 
209
  vlm_hidden_states = vlm_output.hidden_states if output_hidden_states else None
 
240
  def set_output_embeddings(self, new_embeddings):
241
  self.vlm.set_output_embeddings(new_embeddings)
242
 
243
+ def tie_weights(self, *args, **kwargs):
244
+ # transformers 5.x calls this with missing_keys/recompute_mapping during loading. Those refer
245
+ # to the wrapper's key namespace, so they must not be forwarded to the inner VLM, whose params
246
+ # are still meta at that point (torch.equal then fails on meta tensors).
247
  return self.vlm.tie_weights()
248
 
249
  def resize_token_embeddings(
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
+ ]
sentence_bert_config.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "transformer_task": "retrieval",
3
+ "modality_config": {
4
+ "text": {
5
+ "method": "forward",
6
+ "method_output_name": "embeddings"
7
+ },
8
+ "image": {
9
+ "method": "forward",
10
+ "method_output_name": "embeddings"
11
+ }
12
+ },
13
+ "module_output_name": "token_embeddings",
14
+ "processor_kwargs": {
15
+ "max_num_visual_tokens": 1280
16
+ },
17
+ "processing_kwargs": {
18
+ "text": {
19
+ "return_mm_token_type_ids": true
20
+ }
21
+ }
22
+ }