This model uses
Memory Tokens (MemTok) to compress multi-vector visual document representations for efficient ColBERT-style late interaction retrieval. Model weights are initialized from
Qwen2.5-VL-3B-Instruct and finetuned on the
ColPali train set for text-to-visual-document retrieval with bidirectional attention.
1import torch
2from transformers import AutoProcessor
3from qwen_vl_utils import process_vision_info
4
5from src.arguments import ModelArguments
6from src.encoder.multivec_encoder import MultiVecEncoder
7from src.models.qwen2_5_vl_embed.qwen2_5_vl_embed import Qwen2_5ForEmbedding
8from src.utils import get_appending_token_strings
9
10MODEL_ID = "hltcoe/MemTok_qwen2.5-vl_colpali"
11IMAGE_PATH = "PLACEHOLDER"
12NUM_MEMORY_TOKENS = 64
13APPENDING_SUFFIX = "".join(get_appending_token_strings(NUM_MEMORY_TOKENS))
14
15# --- Setup ---
16model_args = ModelArguments(
17 model_name_or_path=MODEL_ID,
18 pooling="memory",
19 normalize=True,
20 num_appending_token=NUM_MEMORY_TOKENS,
21 use_parametric_appending_tokens=True,
22 attn_implementation="flash_attention_2",
23)
24
25processor = AutoProcessor.from_pretrained(MODEL_ID)
26model = MultiVecEncoder.load(
27 Qwen2_5ForEmbedding,
28 model_args,
29 attn_implementation=model_args.attn_implementation,
30 dtype=torch.bfloat16,
31)
32model = model.to("cuda").eval()
33
34# --- Encode an image document ---
35passage_messages = [
36 {
37 "role": "user",
38 "content": [
39 {"type": "text", "text": "Passage: "},
40 {"type": "image", "image": IMAGE_PATH, "max_pixels": 1003520, "min_pixels": 614656},
41 ],
42 }
43]
44text = processor.apply_chat_template(passage_messages, tokenize=False, add_generation_prompt=False)
45text += APPENDING_SUFFIX
46image_inputs, video_inputs = process_vision_info(passage_messages)
47passage_inputs = processor(
48 text=[text], images=image_inputs, videos=video_inputs, padding=True, return_tensors="pt",
49).to("cuda")
50
51with torch.amp.autocast(device_type="cuda", dtype=torch.bfloat16):
52 with torch.inference_mode():
53 doc_embeddings, doc_mask = model.encode(passage_inputs, is_query=False)
54 print(doc_embeddings.shape)
55 # doc_embeddings: (1, 64, 2048) — 64 MemTok vectors
56
57# --- Encode a text query ---
58query_messages = [{"role": "user", "content": [{"type": "text", "text": "Query: What types of tissues are unable to regenerate spontaneously?"}]}]
59query_text = processor.apply_chat_template(query_messages, tokenize=False, add_generation_prompt=False)
60query_inputs = processor(text=[query_text], padding=True, return_tensors="pt").to("cuda")
61
62with torch.amp.autocast(device_type="cuda", dtype=torch.bfloat16):
63 with torch.inference_mode():
64 query_embeddings, query_mask = model.encode(query_inputs, is_query=True)
65 print(query_embeddings.shape)
66
67# --- ColBERT MaxSim scoring ---
68score = model.compute_similarity(query_embeddings, doc_embeddings, query_mask, doc_mask)
69print(f"Similarity score: {score.item():.4f}")
For running inference and evaluation from the command line, see the
Quick Start section.
1@misc{qin2026multivectorindexcompressionmodality,
2 title={Multi-Vector Index Compression in Any Modality},
3 author={Hanxiang Qin and Alexander Martin and Rohan Jha and Chunsheng Zuo and Reno Kriz and Benjamin Van Durme},
4 year={2026},
5 eprint={2602.21202},
6 archivePrefix={arXiv},
7 primaryClass={cs.IR},
8 url={https://arxiv.org/abs/2602.21202},
9}