{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-08-26-training-and-finetuning-multi-vector-embedding-models-with/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "[Sentence Transformers](https://sbert.net/) is a Python library for using and training embedding and reranker models for a wide range of applications, such as retrieval augmented generation, semantic search, semantic textual similarity, and more. Its v6.0 update introduces a fourth model type: `MultiVectorEncoder`, for ColBERT-style late interaction retrieval, alongside a complete training approach for it. In this blogpost, I'll show you how to use it to finetune a multi-vector model that outperforms general-purpose retrievers on your data. This method can also train strong new multi-vector models from scratch. Everything below runs on `pip install -U \"sentence-transformers[train]\"`.\n\nFinetuning multi-vector models involves several components: the model itself, datasets, loss functions, training arguments, evaluators, and the trainer class. I'll have a look at each of these components, accompanied by practical examples of how they can be used for finetuning strong multi-vector models.\n\nLastly, in the [Evaluation](https://huggingface.co/blog/train-multi-vector-encoder#evaluation) section, I'll show you that my finetuned [multi-vector-encoder/mLateOn-medical](https://huggingface.co/multi-vector-encoder/mLateOn-medical) model, trained in 14.5 hours on a single RTX 3090 alongside this blogpost, easily outperforms every general-purpose retrieval model I could find on my medical retrieval evaluation: dense, sparse, lexical, and multi-vector alike.\n\n[![NDCG@10 on MIRIAD versus active parameters: the finetuned mLateOn-medical reaches the top at a fraction of the size of the strongest general-purpose models](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png)](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png)\n\nIf you're interested in finetuning dense embedding models, sparse embedding models, or rerankers instead, then consider reading through my prior [Training and Finetuning Embedding Models](https://huggingface.co/blog/train-sentence-transformers), [Training and Finetuning Sparse Embedding Models](https://huggingface.co/blog/train-sparse-encoder), and [Training and Finetuning Reranker Models](https://huggingface.co/blog/train-reranker) blogposts.\n\n> This blogpost is about *training* multi-vector models. If you want to learn how to *use* them, from loading and encoding to indexing in vector databases, see the companion [Multi-Vector (Late Interaction) Embedding Models with Sentence Transformers](https://huggingface.co/blog/multi-vector-encoder) blogpost.\n\n## Table of Contents\n\n- [What are Multi-Vector models?](https://huggingface.co/blog/train-multi-vector-encoder#what-are-multi-vector-models)\n- [Why Finetune?](https://huggingface.co/blog/train-multi-vector-encoder#why-finetune)\n- [Training Components](https://huggingface.co/blog/train-multi-vector-encoder#training-components)\n- [Model](https://huggingface.co/blog/train-multi-vector-encoder#model)\n  - [Finetuning an existing multi-vector model](https://huggingface.co/blog/train-multi-vector-encoder#finetuning-an-existing-multi-vector-model)\n  - [Building one from a base transformer](https://huggingface.co/blog/train-multi-vector-encoder#building-one-from-a-base-transformer)\n  - [Which starting point should you pick?](https://huggingface.co/blog/train-multi-vector-encoder#which-starting-point-should-you-pick)\n- [Dataset](https://huggingface.co/blog/train-multi-vector-encoder#dataset)\n  - [Data on the Hugging Face Hub](https://huggingface.co/blog/train-multi-vector-encoder#data-on-the-hugging-face-hub)\n  - [Local Data](https://huggingface.co/blog/train-multi-vector-encoder#local-data)\n  - [Dataset Format](https://huggingface.co/blog/train-multi-vector-encoder#dataset-format)\n- [Loss Function](https://huggingface.co/blog/train-multi-vector-encoder#loss-function)\n- [Training Arguments](https://huggingface.co/blog/train-multi-vector-encoder#training-arguments)\n- [Evaluator](https://huggingface.co/blog/train-multi-vector-encoder#evaluator)\n- [Trainer](https://huggingface.co/blog/train-multi-vector-encoder#trainer)\n  - [Callbacks](https://huggingface.co/blog/train-multi-vector-encoder#callbacks)\n  - [Multi-Dataset Training](https://huggingface.co/blog/train-multi-vector-encoder#multi-dataset-training)\n- [Evaluation](https://huggingface.co/blog/train-multi-vector-encoder#evaluation)\n  - [Optimizing the index](https://huggingface.co/blog/train-multi-vector-encoder#optimizing-the-index)\n- [Acknowledgements](https://huggingface.co/blog/train-multi-vector-encoder#acknowledgements)\n- [Additional Resources](https://huggingface.co/blog/train-multi-vector-encoder#additional-resources)\n  - [Training Examples](https://huggingface.co/blog/train-multi-vector-encoder#training-examples)\n  - [Documentation](https://huggingface.co/blog/train-multi-vector-encoder#documentation)\n\n## What are Multi-Vector models?\n\nA dense embedding model compresses a whole text into a single vector, and similarity is one dot product between two such summaries. A multi-vector model (also called a late-interaction or ColBERT-style model) skips that compression. It keeps **one small vector per token** and scores a query against a document with the MaxSim operator, where every query token finds its best-matching document token and the scores are summed. Token-level matching preserves exactly the fine-grained signals that a single vector has to average away, which usually means stronger retrieval, at the cost of a bigger index.\n\nThe companion [Multi-Vector Embedding Models](https://huggingface.co/blog/multi-vector-encoder) blogpost covers the architecture, encoding, scoring, and indexing in detail, so I'll keep this section short and get to the training.\n\n[![Dense embedding versus multi-vector late interaction](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/multi-vector-encoder/maxsim_explainer.gif)](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/multi-vector-encoder/maxsim_explainer.gif)\n\n## Why Finetune?\n\nFinetuning multi-vector models significantly improves their retrieval performance on your specific domain: the vocabulary, the query style, and the notion of relevance all differ between web search, legal discovery, code search, and scientific literature review. Because queries and documents are matched token by token, multi-vector models pick up fine-grained domain signals that single-vector models tend to average away, and they respond very well to even modest amounts of in-domain finetuning data.\n\nBeyond that, most released retrieval models were configured for short passages. The classic ColBERT checkpoints truncate documents at 180 or 300 tokens, and many popular dense models at 256 or 512, because their MS MARCO-style training data rarely goes beyond that. If your documents are long, these models silently discard most of every document before scoring it. On my medical evaluation with passages averaging 941 tokens, I measured that this truncation costs up to 0.24 NDCG@10, considerably more than any difference between model architectures. When you train your own model, you configure the document length that *your* data needs.\n\nLightOn ran into this same dynamic with code retrieval, where general [LateOn](https://huggingface.co/lightonai/LateOn) wasn't enough and they trained [LateOn-Code](https://huggingface.co/lightonai/LateOn-Code). Your domain, whether that's medical, legal, financial, or your company's internal documents, is not getting an official model. This blogpost shows you how to build it yourself, in a matter of hours, on a single consumer GPU.\n\n## Training Components\n\nTraining MultiVectorEncoder models involves the following components:\n\n1. [**Model**](https://huggingface.co/blog/train-multi-vector-encoder#model): The model to finetune or the architecture to build fresh.\n2. [**Dataset**](https://huggingface.co/blog/train-multi-vector-encoder#dataset): The data used for training and evaluation.\n3. [**Loss Function**](https://huggingface.co/blog/train-multi-vector-encoder#loss-function): A function that measures the model's performance and guides the optimization process.\n4. [**Training Arguments**](https://huggingface.co/blog/train-multi-vector-encoder#training-arguments) (optional): Parameters that impact training performance, tracking, and debugging.\n5. [**Evaluator**](https://huggingface.co/blog/train-multi-vector-encoder#evaluator) (optional): A class for evaluating the model before, during, or after training.\n6. [**Trainer**](https://huggingface.co/blog/train-multi-vector-encoder#trainer): Brings together all training components.\n\nLet's take a closer look at each component.\n\n## Model\n\nMulti-vector training gives you a real choice of starting point, and it matters more than you might expect.\n\n### Finetuning an existing multi-vector model\n\nIf you want to further finetune an existing multi-vector model, you don't have to worry about the architecture at all:\n\n```python\nfrom sentence_transformers import MultiVectorEncoder\n\n# Loading in fp32 is preferred for training if your memory can handle it\nmodel = MultiVectorEncoder(\n    \"lightonai/mLateOn-unsupervised\",\n    model_kwargs={\"torch_dtype\": \"float32\"},\n    processor_kwargs={\"model_max_length\": 8192},  # the tokenizer-level token limit\n)\n```\n\nThe checkpoint brings its own recipe along: its query and document marker tokens, its projection head, its scoring skiplist. For finetuning, you generally want to keep all of that and change only what your data demands. The first thing to check is the length configuration, since many released checkpoints cap documents at 180 to 512 tokens (see [Why Finetune?](https://huggingface.co/blog/train-multi-vector-encoder#why-finetune)), and my medical passages run to 1,400 tokens. The mLateOn family already serves the backbone's full 8192 token context, but if your starting checkpoint carries caps, lift them:\n\n```python\n# Let the model read full documents instead of the caps it was trained with,\n# e.g. GTE-ModernColBERT-v1 ships with query_length=48 and document_length=300\nmodel[0].query_length = None\nmodel[0].document_length = None\n```\n\nWith the per-task caps unset, truncation falls back to the tokenizer's `model_max_length`, which is why I configure that limit at load time above.\n\nI made one more change, adding a punctuation skiplist that excludes punctuation tokens from document-side scoring and storage. In a 4-way ablation (none, punctuation, stopwords, both) it modestly won on quality, and it shrinks the document index by 9.6% on this data for free:\n\n```python\nimport string\n\n# model[2] is the MultiVectorMask module\nmodel[2].skiplist_words = list(string.punctuation)\nmodel[2].resolve_with_tokenizer(model.tokenizer)  # token ids are cached, so re-resolve after changing\n```\n\n### Building one from a base transformer\n\nYou can also point `MultiVectorEncoder` at any base transformer, and a fresh, randomly initialized token-level projection is appended for you:\n\n```python\nfrom sentence_transformers import MultiVectorEncoder\n\nmodel = MultiVectorEncoder(\"answerdotai/ModernBERT-base\", model_kwargs={\"torch_dtype\": \"float32\"})\n# MultiVectorEncoder(\n#   (0): Transformer({..., 'architecture': 'ModernBertModel'})\n#   (1): Dense({'in_features': 768, 'out_features': 128, 'bias': False, ...})\n#   (2): MultiVectorMask({'skiplist_words': [], 'skiplist_tasks': ['document'], ...})\n#   (3): Normalize({...})\n# )\n```\n\nThat's the classic ColBERT pipeline: a `Transformer` producing contextualized token embeddings, a token-level `Dense` projecting each of them down to 128 dimensions, a `MultiVectorMask` deciding which tokens count during scoring, and a token-level `Normalize`. The projection starts random, so training is required before this model is useful. Interestingly, this works with strong dense embedding backbones too. A fresh projection on [Alibaba-NLP/gte-modernbert-base](https://huggingface.co/Alibaba-NLP/gte-modernbert-base) reached within 0.03 of the existing-checkpoint starting points in my experiments, from nothing but the projection and 25k training pairs.\n\nThe classic ColBERT tokenization tricks (`[MASK]` query expansion, `[Q]` / `[D]` prefix tokens, a document length cap, a punctuation skiplist) are all off by default and configurable. See [Creating Custom Models](https://sbert.net/docs/multi_vector_encoder/usage/custom_models.html) for the full set. For what it's worth, I tested `[MASK]` query expansion in four configurations for my domain finetune and none of them made a measurable difference, so don't feel obliged to reach for the classic recipe.\n\n### Which starting point should you pick?\n\nI measured this directly while preparing this blogpost, taking six starting points and training each with the identical recipe on 25k medical question-passage pairs from [MIRIAD](https://huggingface.co/datasets/tomaarsen/miriad-4.4M-split), then evaluating on 1,000 held-out questions against a 50,000 passage corpus:\n\n| Starting point                                                                               | Zero-shot NDCG@10 | After 25k pairs | Delta       |\n| -------------------------------------------------------------------------------------------- | ----------------- | --------------- | ----------- |\n| [lightonai/mLateOn-unsupervised](https://huggingface.co/lightonai/mLateOn-unsupervised)     | 0.9087            | **0.9398**      | **+0.0311** |\n| [lightonai/mLateOn](https://huggingface.co/lightonai/mLateOn)                               | 0.9277            | 0.9319          | +0.0042     |\n| [lightonai/LateOn-unsupervised](https://huggingface.co/lightonai/LateOn-unsupervised)       | 0.9026            | **0.9206**      | **+0.0180** |\n| [lightonai/LateOn](https://huggingface.co/lightonai/LateOn)                                 | 0.9185            | 0.9105          | -0.0080     |\n| [lightonai/GTE-ModernColBERT-v1](https://huggingface.co/lightonai/GTE-ModernColBERT-v1)     | 0.9198            | 0.9007          | -0.0191     |\n| Fresh head on [gte-modernbert-base](https://huggingface.co/Alibaba-NLP/gte-modernbert-base) | -                 | 0.9177          | -           |\n\nThe result surprised me, and it replicated across two model families. \\*The `-unsupervised` checkpoints adapt to a new domain far better than their finished siblings, overtaking them despite starting lower. These checkpoints sit after large-scale contrastive pretraining but before supervised finetuning on general retrieval, so they carry all the late-interaction structure with none of the general-purpose tuning that domain training then has to undo. The finished checkpoints, by contrast, barely moved or even regressed, at every learning rate I tried.\n\nSo, if the model family you like publishes a pre-supervised checkpoint, start there. If not, a fresh projection on a strong retrieval-pretrained backbone is a close runner-up. Continuing from a fully finished checkpoint is the weakest option for domain adaptation, despite being the most natural-feeling one.\n\n## Dataset\n\nThe [`MultiVectorEncoderTrainer`](https://sbert.net/docs/package_reference/multi_vector_encoder/trainer.html) uses [`datasets.Dataset`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset) or [`datasets.DatasetDict`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.DatasetDict) instances for training and evaluation. You can load data from the [Hugging Face Datasets Hub](https://huggingface.co/datasets) or use local data in whatever format you prefer (e.g. CSV, JSON, Parquet, Arrow, or SQL).\n\n**Note:** Lots of public datasets that work out of the box with Sentence Transformers have been tagged with `sentence-transformers` on the Hugging Face Hub, so you can easily find them on [https://huggingface.co/datasets?other=sentence-transformers](https://huggingface.co/datasets?other=sentence-transformers). Consider browsing through these to find ready-to-go datasets that might be useful for your tasks, domains, or languages.\n\n### Data on the Hugging Face Hub\n\nYou can use the [`load_dataset`](https://huggingface.co/docs/datasets/main/en/package_reference/loading_methods#datasets.load_dataset) function to load data from datasets on the Hub:\n\n```python\nfrom datasets import load_dataset\n\ntrain_dataset = load_dataset(\"tomaarsen/miriad-4.4M-split\", split=\"train\")\n\nprint(train_dataset)\n\"\"\"\nDataset({\n    features: ['question', 'passage_text'],\n    num_rows: 4467542\n})\n\"\"\"\n```\n\nThis is the dataset I'll train on in this blogpost: 4.4 million medical questions from [MIRIAD](https://huggingface.co/datasets/miriad/miriad-4.4M), each paired with the source passage that contains its answer (averaging 941 tokens). Simple (query, relevant passage) pairs like these are the easiest retrieval training data to collect for your own domain, and as you'll see, they're all you need.\n\n### Local Data\n\nYou can also use [`load_dataset`](https://huggingface.co/docs/datasets/main/en/package_reference/loading_methods#datasets.load_dataset) for loading local data in common file formats:\n\n```python\nfrom datasets import load_dataset\n\ndataset = load_dataset(\"csv\", data_files=\"my_file.csv\")\n# or\ndataset = load_dataset(\"json\", data_files=\"my_file.json\")\n```\n\nAnd if your local data requires pre-processing, you can use [`datasets.Dataset.from_dict`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset.from_dict) to initialize your dataset with a dictionary of lists:\n\n```python\nfrom datasets import Dataset\n\nqueries = []\ndocuments = []\n# Open a file, perform preprocessing, filtering, cleaning, etc.\n# and append to the lists\n\ndataset = Dataset.from_dict({\n    \"query\": queries,\n    \"document\": documents,\n})\n```\n\n### Dataset Format\n\nIt is important that your dataset format matches your loss function (or that you choose a loss function that matches your dataset format). Verifying whether a dataset format works with a loss function involves two steps:\n\n1. If your loss function requires a *Label* according to the [Loss Overview](https://sbert.net/docs/multi_vector_encoder/loss_overview.html) table, then your dataset must have a **column named \"label\" or \"score\"**. This column is automatically taken as the label.\n2. All columns not named \"label\" or \"score\" are considered *Inputs* according to the [Loss Overview](https://sbert.net/docs/multi_vector_encoder/loss_overview.html) table. The number of remaining columns must match the number of valid inputs for your chosen loss. The names of these columns are **irrelevant**, only the **order matters**.\n\nThere are two multi-vector specific conventions on top of this:\n\n- Positional query and document assignment: the first column is embedded as the *query* and all following columns as *documents*, regardless of the column names. This default can be overridden per column via the standard `router_mapping` training argument.\n- Knowledge distillation format: one column per candidate document, i.e. `(query, document_1, ..., document_N, scores)` where `scores` is a list of N teacher scores per row. For KD datasets that store query and document *IDs* alongside separate text datasets (e.g. [lightonai/ms-marco-en-bge](https://huggingface.co/datasets/lightonai/ms-marco-en-bge)), you can use [`resolve_ids`](https://sbert.net/docs/package_reference/util.html#sentence_transformers.util.dataset.resolve_ids) to resolve the IDs to texts on the fly.\n\n## Loss Function\n\nLoss functions quantify how well a model performs for a given batch of data, allowing an optimizer to update the model weights to produce more favourable (i.e., lower) loss values. The right loss function for your task depends on the data you have and what you're trying to achieve. You can find a full list of options in the [Loss Overview](https://sbert.net/docs/multi_vector_encoder/loss_overview.html).\n\nFor the common case of question-answer or question-passage pairs, the workhorse is in-batch negatives training with [`MultiVectorMultipleNegativesRankingLoss`](https://sbert.net/docs/package_reference/multi_vector_encoder/losses.html#multivectormultiplenegativesrankingloss), where every other document in the batch acts as a negative for each query. Bigger batches mean more negatives and stronger training, so in practice you'll want its GradCache variant, [`CachedMultiVectorMultipleNegativesRankingLoss`](https://sbert.net/docs/package_reference/multi_vector_encoder/losses.html#cachedmultivectormultiplenegativesrankingloss), which decouples the effective batch size from what fits on your GPU:\n\n```python\nfrom sentence_transformers import MultiVectorEncoder\nfrom sentence_transformers.multi_vector_encoder.losses import CachedMultiVectorMultipleNegativesRankingLoss\n\nmodel = MultiVectorEncoder(\"lightonai/mLateOn-unsupervised\", model_kwargs={\"torch_dtype\": \"float32\"})\n\nloss = CachedMultiVectorMultipleNegativesRankingLoss(\n    model=model,\n    mini_batch_size=16,  # how many documents to encode per chunk: bounds memory, not quality\n)\n```\n\nThe `mini_batch_size` parameter bounds the memory by encoding documents in chunks of this size, while the effective contrastive batch size (128 in my run below, and in my ablations bigger batches bought nothing further) stays a free choice. GradCache guarantees identical results regardless of the chunk size, so lower it for smaller GPUs at only a wall-clock cost. When your document lengths vary a lot, consider its sibling `mini_batch_num_tokens`, which packs each chunk to a total token budget instead of a document count, so a chunk of unusually long documents can never spike your memory (my `mini_batch_size=16` at roughly 940 tokens per document corresponds to `mini_batch_num_tokens=15_000`).\n\nOne multi-vector specific trap is that the contrastive losses default to `scale=1.0`, unlike the dense embedding equivalent which defaults to `scale=20.0`. That 20.0 exists because a cosine similarity is a single value in \\[-1, 1\\], too narrow a range for a sharp softmax. A MaxSim score instead sums one best-match similarity per query token, so it already spans roughly \\[0, query\\_length\\]: a 32-token query can score up to 32. So don't copy `scale=20.0` over from a dense training script, since it would saturate the softmax and kill your gradients.\n\nFor distillation from a stronger teacher, which is how the strongest general-purpose late-interaction models are trained, see [`MultiVectorDistillKLDivLoss`](https://sbert.net/docs/package_reference/multi_vector_encoder/losses.html#multivectordistillkldivloss) and the Knowledge Distillation tab in the [Training Overview](https://sbert.net/docs/multi_vector_encoder/training_overview.html#trainer) documentation.\n\n## Training Arguments\n\nYou can customize the training process using the [`MultiVectorEncoderTrainingArguments`](https://sbert.net/docs/package_reference/multi_vector_encoder/training_args.html) class. This class lets you adjust parameters that can impact training speed and help you understand what's happening during training.\n\nFor more information on the most useful training arguments, check out the [Multi-Vector Encoder > Training Overview > Training Arguments](https://sbert.net/docs/multi_vector_encoder/training_overview.html#training-arguments). It's worth reading to get the most out of your training.\n\nHere's an example, using the values from my actual training run:\n\n```python\nfrom sentence_transformers import MultiVectorEncoderTrainingArguments\nfrom sentence_transformers.base.sampler import BatchSamplers\n\nargs = MultiVectorEncoderTrainingArguments(\n    # Required parameter:\n    output_dir=\"models/mLateOn-medical\",\n    # Optional training parameters:\n    num_train_epochs=1,\n    per_device_train_batch_size=128,  # the effective contrastive batch, thanks to GradCache\n    per_device_eval_batch_size=16,\n    learning_rate=1e-4,\n    warmup_steps=0.05,\n    prompts={\"question\": \"[Q] \", \"passage_text\": \"[D] \"},  # the checkpoint's markers, keyed by training column\n    fp16=False,  # Set to True if you have a GPU that supports FP16\n    bf16=True,  # Set to True if you have a GPU that supports BF16\n    batch_sampler=BatchSamplers.NO_DUPLICATES,  # in-batch negatives benefit from no duplicates\n    # Optional tracking/debugging parameters:\n    eval_strategy=\"steps\",\n    eval_steps=0.1,\n    save_strategy=\"steps\",\n    save_steps=0.05,\n    logging_steps=0.01,\n    run_name=\"mLateOn-medical\",  # Will be used in e.g. Trackio, W&B, etc.\n)\n```\n\nA few of these deserve a comment:\n\n- `prompts`: training does not automatically apply the prompts stored in the model, so map them onto your training columns explicitly. Here that is the checkpoint's `[Q]` marker for the question column and `[D]` for the passage column, keeping training consistent with inference.\n- `max_length` (deliberately not set): this argument caps tokenization during *training only*, for when you want cheaper training than the model's full serving length. I measured what that shortcut costs on this data. Training at 512 tokens lost about 0.015 NDCG@10 for about 2x the speed, and the deficit did not shrink with more data, because the model simply never sees what got cut off. Leave it unset so training matches inference, unless you need the speedup more than the quality.\n- `learning_rate=1e-4`: after a sweep from 5e-6 to 2e-4, I had the best luck with this higher-than-usual learning rate.\n\n## Evaluator\n\nTo track your model's performance during training, you can pass an `eval_dataset` to the trainer for evaluation loss, but concrete retrieval metrics are much more informative. Sentence Transformers includes the following built-in evaluators for multi-vector models:\n\n| Evaluator                                                                                                                                                             | Required Data                                                                 |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |\n| [`MultiVectorInformationRetrievalEvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectorinformationretrievalevaluator) | Queries, corpus, and relevant document mappings                               |\n| [`MultiVectorNanoBEIREvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectornanobeirevaluator)                         | No data required                                                              |\n| [`MultiVectorTripletEvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectortripletevaluator)                           | (anchor, positive, negative) triplets                                         |\n| [`MultiVectorRerankingEvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectorrerankingevaluator)                       | List of `{'query': '...', 'positive': [...], 'negative': [...]}` dictionaries |\n| [`MultiVectorDistillationEvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectordistillationevaluator)                 | Queries with candidate documents and teacher scores                           |\n\nFor domain finetuning, the [`MultiVectorInformationRetrievalEvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectorinformationretrievalevaluator) built from your own held-out data is the one that matters. One tip on constructing it is that the corpus should be hard enough that models can be told apart. In my case the MIRIAD questions are generated from their own source passages, which makes retrieval unusually easy. Against just the 10k gold passages, nearly every model scored above 0.97 NDCG@10. If your evaluation saturates like that, add *distractor* passages (I use deduplicated passages from the training split) until the scores spread out:\n\n```python\nfrom datasets import load_dataset\nfrom sentence_transformers.multi_vector_encoder.evaluation import MultiVectorInformationRetrievalEvaluator\n\ndataset = load_dataset(\"tomaarsen/miriad-4.4M-split\")\n\n# Gold: 1,000 evaluation questions, each mapping to its own passage, with the\n# eval split's full ~10k unique passages as the initial corpus\ncorpus = {}\nqueries = {}\nrelevant_docs = {}\npassage_to_id = {}\nfor idx, row in enumerate(dataset[\"eval\"]):\n    if row[\"passage_text\"] not in passage_to_id:\n        passage_to_id[row[\"passage_text\"]] = f\"p{len(passage_to_id)}\"\n        corpus[passage_to_id[row[\"passage_text\"]]] = row[\"passage_text\"]\n    if idx < 1_000:\n        queries[f\"q{idx}\"] = row[\"question\"]\n        relevant_docs[f\"q{idx}\"] = {passage_to_id[row[\"passage_text\"]]}\n\n# Distractors: unique train passages that make the haystack realistic\nseen = set(passage_to_id)\nfor row in dataset[\"train\"]:\n    if len(corpus) >= 200_000:\n        break\n    if row[\"passage_text\"] not in seen:\n        seen.add(row[\"passage_text\"])\n        corpus[f\"d{len(corpus)}\"] = row[\"passage_text\"]\n\nevaluator = MultiVectorInformationRetrievalEvaluator(\n    queries=queries,\n    corpus=corpus,\n    relevant_docs=relevant_docs,\n    name=\"miriad-dev\",\n    batch_size=16,\n)\n# results = evaluator(model)\n```\n\n## Trainer\n\nThe [`MultiVectorEncoderTrainer`](https://sbert.net/docs/package_reference/multi_vector_encoder/trainer.html) is where all previous components come together. Here is the complete script that trained [multi-vector-encoder/mLateOn-medical](https://huggingface.co/multi-vector-encoder/mLateOn-medical), the model from the introduction:\n\n```python\nimport logging\nimport string\nimport traceback\n\nfrom datasets import load_dataset\n\nfrom sentence_transformers import (\n    MultiVectorEncoder,\n    MultiVectorEncoderModelCardData,\n    MultiVectorEncoderTrainer,\n    MultiVectorEncoderTrainingArguments,\n)\nfrom sentence_transformers.base.sampler import BatchSamplers\nfrom sentence_transformers.multi_vector_encoder.evaluation import MultiVectorInformationRetrievalEvaluator\nfrom sentence_transformers.multi_vector_encoder.losses import CachedMultiVectorMultipleNegativesRankingLoss\n\nlogging.basicConfig(format=\"%(asctime)s - %(message)s\", datefmt=\"%Y-%m-%d %H:%M:%S\", level=logging.INFO)\n\n\ndef main():\n    # 1. Load the starting checkpoint: contrastively pretrained, not yet supervised\n    # Loading in fp32 is preferred for training if your memory can handle it\n    model = MultiVectorEncoder(\n        \"lightonai/mLateOn-unsupervised\",\n        model_kwargs={\"torch_dtype\": \"float32\"},\n        processor_kwargs={\"model_max_length\": 8192},\n        model_card_data=MultiVectorEncoderModelCardData(\n            language=\"en\",\n            license=\"apache-2.0\",\n            model_name=\"mLateOn finetuned on MIRIAD medical retrieval\",\n        ),\n    )\n\n    # 2. Lift the per-task length caps so training and inference see full medical passages\n    model[0].query_length = None\n    model[0].document_length = None\n\n    # 3. Skip punctuation tokens during scoring: a small quality win and a 9.6% smaller index\n    model[2].skiplist_words = list(string.punctuation)\n    model[2].resolve_with_tokenizer(model.tokenizer)\n\n    # 4. Load 1 million medical question-passage pairs\n    train_dataset = load_dataset(\"tomaarsen/miriad-4.4M-split\", split=\"train\").select(range(1_000_000))\n\n    # 5. In-batch negatives with GradCache: large effective batch, memory-bounded chunks\n    loss = CachedMultiVectorMultipleNegativesRankingLoss(model=model, mini_batch_size=16)\n\n    # 6. A light dev evaluator to watch progress during training: 500 held-out questions\n    # against the eval split's ~10k unique passages. The full 200k protocol runs afterwards.\n    eval_split = load_dataset(\"tomaarsen/miriad-4.4M-split\", split=\"eval\")\n    corpus, queries, relevant_docs, passage_to_id = {}, {}, {}, {}\n    for idx, row in enumerate(eval_split):\n        if row[\"passage_text\"] not in passage_to_id:\n            passage_to_id[row[\"passage_text\"]] = f\"p{len(passage_to_id)}\"\n            corpus[passage_to_id[row[\"passage_text\"]]] = row[\"passage_text\"]\n        if idx < 500:\n            queries[f\"q{idx}\"] = row[\"question\"]\n            relevant_docs[f\"q{idx}\"] = {passage_to_id[row[\"passage_text\"]]}\n    dev_evaluator = MultiVectorInformationRetrievalEvaluator(\n        queries=queries, corpus=corpus, relevant_docs=relevant_docs, name=\"miriad-dev\", batch_size=16\n    )\n\n    # 7. Training arguments, as discussed above\n    run_name = \"mLateOn-medical\"\n    args = MultiVectorEncoderTrainingArguments(\n        output_dir=f\"models/{run_name}\",\n        num_train_epochs=1,\n        per_device_train_batch_size=128,\n        per_device_eval_batch_size=16,\n        learning_rate=1e-4,\n        warmup_steps=0.05,\n        prompts={\"question\": \"[Q] \", \"passage_text\": \"[D] \"},\n        fp16=False,  # Set to True if you have a GPU that supports FP16\n        bf16=True,  # Set to True if you have a GPU that supports BF16\n        batch_sampler=BatchSamplers.NO_DUPLICATES,\n        eval_strategy=\"steps\",\n        eval_steps=0.1,\n        save_strategy=\"steps\",\n        save_steps=0.05,\n        logging_steps=0.01,\n        run_name=run_name,\n    )\n\n    # 8. Create a trainer & train\n    trainer = MultiVectorEncoderTrainer(\n        model=model,\n        args=args,\n        train_dataset=train_dataset,\n        loss=loss,\n        evaluator=dev_evaluator,\n    )\n    trainer.train()\n\n    # 9. Save the trained model\n    model.save_pretrained(f\"models/{run_name}/final\")\n\n    # 10. (Optional) Push it to the Hugging Face Hub\n    try:\n        model.push_to_hub(run_name)\n    except Exception:\n        logging.error(f\"Error uploading model to the Hugging Face Hub:\\n{traceback.format_exc()}\")\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\nThat's the whole recipe: a pre-supervised checkpoint, a million domain pairs, in-batch negatives, full document length, and a higher-than-usual learning rate. The run took 14.5 hours on my single RTX 3090 at a peak of 17.5 GB VRAM, and every one of those choices was the winner of a measured comparison rather than a guess.\n\nFor readers on smaller budgets, my scaling experiments put 100k pairs (75 minutes of training) within 0.012 NDCG@10 of the full million-pair run. Most of the gain comes in the first hour.\n\n### Callbacks\n\nThe MultiVectorEncoder trainer supports various [`transformers.TrainerCallback`](https://huggingface.co/docs/transformers/main_classes/callback#transformers.TrainerCallback) subclasses, including:\n\n- [`WandbCallback`](https://huggingface.co/docs/transformers/en/main_classes/callback#transformers.integrations.WandbCallback) for logging training metrics to W&B if `wandb` is installed\n- [`TensorBoardCallback`](https://huggingface.co/docs/transformers/en/main_classes/callback#transformers.integrations.TensorBoardCallback) for logging training metrics to TensorBoard if `tensorboard` is accessible\n- [`CodeCarbonCallback`](https://huggingface.co/docs/transformers/en/main_classes/callback#transformers.integrations.CodeCarbonCallback) for tracking carbon emissions during training if `codecarbon` is installed\n\nEnable these via the `report_to` training argument, e.g. `report_to=[\"wandb\", \"codecarbon\"]`, with the required dependencies installed. It defaults to `\"none\"`, and `report_to=\"all\"` activates every integration whose dependency is installed.\n\nRefer to the [Transformers Callbacks documentation](https://huggingface.co/docs/transformers/en/main_classes/callback) for more information on these callbacks and how to create your own.\n\n### Multi-Dataset Training\n\nTypically, top-performing general-purpose models are trained on multiple datasets simultaneously. However, this approach can be challenging due to the varying formats of each dataset. Fortunately, the [`MultiVectorEncoderTrainer`](https://sbert.net/docs/package_reference/multi_vector_encoder/trainer.html) allows you to train on multiple datasets without requiring a uniform format. Additionally, it provides the flexibility to apply different loss functions to each dataset. Here are the steps to train with multiple datasets at once:\n\n- Use a dictionary of [`datasets.Dataset`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset) instances (or a [`datasets.DatasetDict`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.DatasetDict)) as the `train_dataset` (and optionally also `eval_dataset`).\n- (Optional) Use a dictionary of loss functions mapping dataset names to losses. Only required if you wish to use different loss functions for different datasets.\n\nEach training/evaluation batch will only contain samples from one of the datasets. The order in which batches are sampled from the multiple datasets is defined by the [`MultiDatasetBatchSamplers`](https://sbert.net/docs/package_reference/sentence_transformer/sampler.html#sentence_transformers.training_args.MultiDatasetBatchSamplers) enum, which can be passed to the [`MultiVectorEncoderTrainingArguments`](https://sbert.net/docs/package_reference/multi_vector_encoder/training_args.html) via `multi_dataset_batch_sampler`. Valid options are:\n\n- `MultiDatasetBatchSamplers.ROUND_ROBIN`: Round-robin sampling from each dataset until one is exhausted. With this strategy, it's likely that not all samples from each dataset are used, but each dataset is sampled from equally.\n- `MultiDatasetBatchSamplers.PROPORTIONAL` (default): Sample from each dataset in proportion to its size. With this strategy, all samples from each dataset are used and larger datasets are sampled from more frequently.\n\n## Evaluation\n\nTo find out where the finetuned model stands, I evaluated it against over 50 retrieval model configurations across four architecture families on the MIRIAD evaluation set, built exactly as in the [Evaluator](https://huggingface.co/blog/train-multi-vector-encoder#evaluator) section above, with 1,000 held-out medical questions searching 200,000 unique passages (the 10k gold passages hidden among 190k deduplicated distractors from the training split). This corpus is four times the size of the 50,000-passage one from [Which starting point should you pick?](https://huggingface.co/blog/train-multi-vector-encoder#which-starting-point-should-you-pick), so scores are not comparable between the two tables.\n\n[![NDCG@10 versus active parameters on the MIRIAD 200k benchmark, with an arrow marking the finetuning jump from mLateOn-unsupervised to mLateOn-medical](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png)](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png)\n\nThe headline results, with the full table in the collapsible below:\n\n| Model                                                                                                           | Family                      | NDCG@10    |\n| --------------------------------------------------------------------------------------------------------------- | --------------------------- | ---------- |\n| [**multi-vector-encoder/mLateOn-medical (mine)**](https://huggingface.co/multi-vector-encoder/mLateOn-medical) | **Multi-vector, finetuned** | **0.9139** |\n| [lightonai/mLateOn](https://huggingface.co/lightonai/mLateOn)                                                  | Multi-vector, zero-shot     | 0.8520     |\n| [lightonai/GTE-ModernColBERT-v1](https://huggingface.co/lightonai/GTE-ModernColBERT-v1) (cap lifted)           | Multi-vector, zero-shot     | 0.8502     |\n| [Qwen/Qwen3-Embedding-4B](https://huggingface.co/Qwen/Qwen3-Embedding-4B)                                      | Dense, zero-shot            | 0.7817     |\n| [voyageai/voyage-4-nano](https://huggingface.co/voyageai/voyage-4-nano)                                        | Dense, zero-shot            | 0.7563     |\n| BM25                                                                                                            | Lexical                     | 0.7501     |\n| [naver/splade-v3](https://huggingface.co/naver/splade-v3)                                                      | Sparse, zero-shot           | 0.6853     |\n\nThe finetuned model tops the table, beating the strongest zero-shot model of any architecture by +0.062 NDCG@10. In other words, the strongest zero-shot model returns the right passage as the very first hit for 75.8% of the queries, while the finetuned model does so for 84.9%, cutting the rank-1 error by more than a third.\n\nThe architecture pattern is just as clear, with the top of the table exclusively late interaction. On long documents, one vector per token beats one vector per document, even at matched training and matched backbones. DenseOn and LateOn share training data and architecture except for the head, and the late-interaction sibling wins by +0.12, with the multilingual pair (mDenseOn and mLateOn) replicating this at +0.13. Scale doesn't rescue single vectors either. [Qwen3-Embedding-4B](https://huggingface.co/Qwen/Qwen3-Embedding-4B), the strongest dense model with roughly 33x the active (non-embedding) parameters of mine, still stops 0.13 short, and the 8B version scores lower than the 4B.\n\nBM25 also performs surprisingly well, beating every sparse model, every truncation-capped multi-vector model, and all but three dense models: the multi-billion [Qwen3-Embedding-4B](https://huggingface.co/Qwen/Qwen3-Embedding-4B) and [8B](https://huggingface.co/Qwen/Qwen3-Embedding-8B), and [voyage-4-nano](https://huggingface.co/voyageai/voyage-4-nano), which reads its full 32k token context to edge past by just 0.006. Don't expect that to transfer to your own data though. MIRIAD's questions are generated from the passages, so the lexical overlap between a query and its gold passage is far larger than in typical retrieval, and BM25's unlimited context length lets it use every one of those overlapping words while most neural checkpoints truncate. A BM25 baseline is cheap and always worth running, just don't count on this margin.\n\nThe full field at a glance, sorted by score and colored by architecture family.\n\n[![Sorted NDCG@10 on the MIRIAD 200k benchmark for every evaluated model, colored by architecture family](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_ndcg_by_model.png)](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_ndcg_by_model.png)\n\nClick to see the full evaluation table\n\n| Model                                                                                                                                                      | Family                  | NDCG@10    | acc@1 |\n| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ---------- | ----- |\n| [**multi-vector-encoder/mLateOn-medical (mine)**](https://huggingface.co/multi-vector-encoder/mLateOn-medical)                                            | Multi-vector, finetuned | **0.9139** | 0.849 |\n| [lightonai/mLateOn](https://huggingface.co/lightonai/mLateOn)                                                                                             | Multi-vector            | 0.8520     | 0.758 |\n| [lightonai/GTE-ModernColBERT-v1](https://huggingface.co/lightonai/GTE-ModernColBERT-v1) @1024                                                             | Multi-vector            | 0.8502     | 0.763 |\n| [lightonai/LateOn](https://huggingface.co/lightonai/LateOn) @1024                                                                                         | Multi-vector            | 0.8485     | 0.760 |\n| [lightonai/mLateOn-unsupervised](https://huggingface.co/lightonai/mLateOn-unsupervised)                                                                   | Multi-vector            | 0.8304     | 0.733 |\n| [mixedbread-ai/mxbai-edge-colbert-v0-32m](https://huggingface.co/mixedbread-ai/mxbai-edge-colbert-v0-32m) @1024                                           | Multi-vector            | 0.8186     | 0.727 |\n| [Qwen/Qwen3-Embedding-4B](https://huggingface.co/Qwen/Qwen3-Embedding-4B)                                                                                 | Dense                   | 0.7817     | 0.669 |\n| [Qwen/Qwen3-Embedding-8B](https://huggingface.co/Qwen/Qwen3-Embedding-8B)                                                                                 | Dense                   | 0.7747     | 0.654 |\n| [perplexity-ai/pplx-embed-v1-late-0.6b](https://huggingface.co/perplexity-ai/pplx-embed-v1-late-0.6b) @1024                                               | Multi-vector            | 0.7702     | 0.632 |\n| [lightonai/ColBERT-Zero](https://huggingface.co/lightonai/ColBERT-Zero)                                                                                   | Multi-vector            | 0.7613     | 0.675 |\n| [LiquidAI/LFM2.5-ColBERT-350M](https://huggingface.co/LiquidAI/LFM2.5-ColBERT-350M)                                                                       | Multi-vector            | 0.7582     | 0.664 |\n| [voyageai/voyage-4-nano](https://huggingface.co/voyageai/voyage-4-nano)                                                                                   | Dense                   | 0.7563     | 0.638 |\n| BM25                                                                                                                                                       | Lexical                 | 0.7501     | 0.641 |\n| [jinaai/jina-embeddings-v5-text-small-retrieval](https://huggingface.co/jinaai/jina-embeddings-v5-text-small-retrieval)                                   | Dense                   | 0.7470     | 0.620 |\n| [Qwen/Qwen3-Embedding-0.6B](https://huggingface.co/Qwen/Qwen3-Embedding-0.6B)                                                                             | Dense                   | 0.7408     | 0.620 |\n| [perplexity-ai/pplx-embed-v1-0.6b](https://huggingface.co/perplexity-ai/pplx-embed-v1-0.6b)                                                               | Dense                   | 0.7384     | 0.615 |\n| [mixedbread-ai/mxbai-edge-colbert-v0-32m](https://huggingface.co/mixedbread-ai/mxbai-edge-colbert-v0-32m)                                                 | Multi-vector            | 0.7350     | 0.639 |\n| [mixedbread-ai/mxbai-edge-colbert-v0-17m](https://huggingface.co/mixedbread-ai/mxbai-edge-colbert-v0-17m)                                                 | Multi-vector            | 0.7271     | 0.631 |\n| [answerdotai/answerai-colbert-small-v1](https://huggingface.co/answerdotai/answerai-colbert-small-v1) @512                                                | Multi-vector            | 0.7264     | 0.615 |\n| [lightonai/DenseOn](https://huggingface.co/lightonai/DenseOn) @1024                                                                                       | Dense                   | 0.7239     | 0.597 |\n| [lightonai/mDenseOn](https://huggingface.co/lightonai/mDenseOn) @1024                                                                                     | Dense                   | 0.7227     | 0.585 |\n| [jinaai/jina-embeddings-v5-text-nano-retrieval](https://huggingface.co/jinaai/jina-embeddings-v5-text-nano-retrieval)                                     | Dense                   | 0.7206     | 0.587 |\n| [microsoft/harrier-oss-v1-0.6b](https://huggingface.co/microsoft/harrier-oss-v1-0.6b)                                                                     | Dense                   | 0.7126     | 0.572 |\n| [Alibaba-NLP/gte-modernbert-base](https://huggingface.co/Alibaba-NLP/gte-modernbert-base)                                                                 | Dense                   | 0.7102     | 0.582 |\n| [Snowflake/snowflake-arctic-embed-l-v2.0](https://huggingface.co/Snowflake/snowflake-arctic-embed-l-v2.0)                                                 | Dense                   | 0.7068     | 0.568 |\n| [perplexity-ai/pplx-embed-v1-late-0.6b](https://huggingface.co/perplexity-ai/pplx-embed-v1-late-0.6b)                                                     | Multi-vector            | 0.7008     | 0.570 |\n| [google/embeddinggemma-300m](https://huggingface.co/google/embeddinggemma-300m)                                                                           | Dense                   | 0.7000     | 0.563 |\n| [lightonai/DenseOn](https://huggingface.co/lightonai/DenseOn)                                                                                             | Dense                   | 0.6943     | 0.570 |\n| [naver/splade-v3](https://huggingface.co/naver/splade-v3)                                                                                                 | Sparse                  | 0.6853     | 0.574 |\n| [ibm-granite/granite-embedding-small-english-r2](https://huggingface.co/ibm-granite/granite-embedding-small-english-r2)                                   | Dense                   | 0.6813     | 0.546 |\n| [naver/splade-v3-distilbert](https://huggingface.co/naver/splade-v3-distilbert)                                                                           | Sparse                  | 0.6806     | 0.567 |\n| [codefuse-ai/F2LLM-v2-0.6B](https://huggingface.co/codefuse-ai/F2LLM-v2-0.6B)                                                                             | Dense                   | 0.6799     | 0.536 |\n| [colbert-ir/colbertv2.0](https://huggingface.co/colbert-ir/colbertv2.0) @512                                                                              | Multi-vector            | 0.6785     | 0.571 |\n| [prithivida/Splade\\_PP\\_en\\_v1](https://huggingface.co/prithivida/Splade_PP_en_v1)                                                                        | Sparse                  | 0.6755     | 0.577 |\n| [lightonai/LateOn](https://huggingface.co/lightonai/LateOn)                                                                                               | Multi-vector            | 0.6713     | 0.561 |\n| [tomaarsen/embeddinggemma-300m-miriad-unsloth](https://huggingface.co/tomaarsen/embeddinggemma-300m-miriad-unsloth)                                       | Dense, finetuned        | 0.6705     | 0.530 |\n| [lightonai/LateOn-regularized](https://huggingface.co/lightonai/LateOn-regularized)                                                                       | Multi-vector            | 0.6673     | 0.554 |\n| [lightonai/LateOn-unsupervised](https://huggingface.co/lightonai/LateOn-unsupervised)                                                                     | Multi-vector            | 0.6672     | 0.553 |\n| [lightonai/GTE-ModernColBERT-v1](https://huggingface.co/lightonai/GTE-ModernColBERT-v1)                                                                   | Multi-vector            | 0.6612     | 0.555 |\n| [opensearch-project/opensearch-neural-sparse-encoding-v2-distill](https://huggingface.co/opensearch-project/opensearch-neural-sparse-encoding-v2-distill) | Sparse                  | 0.6518     | 0.531 |\n| [nomic-ai/nomic-embed-text-v1.5](https://huggingface.co/nomic-ai/nomic-embed-text-v1.5) (prompted)                                                        | Dense                   | 0.6387     | 0.498 |\n| [mixedbread-ai/mxbai-embed-large-v1](https://huggingface.co/mixedbread-ai/mxbai-embed-large-v1)                                                           | Dense                   | 0.6355     | 0.502 |\n| [BAAI/bge-large-en-v1.5](https://huggingface.co/BAAI/bge-large-en-v1.5)                                                                                   | Dense                   | 0.6308     | 0.498 |\n| [jinaai/jina-colbert-v2](https://huggingface.co/jinaai/jina-colbert-v2) @1024                                                                             | Multi-vector            | 0.6218     | 0.504 |\n| [nomic-ai/nomic-embed-text-v1.5](https://huggingface.co/nomic-ai/nomic-embed-text-v1.5)                                                                   | Dense                   | 0.6203     | 0.487 |\n| [answerdotai/answerai-colbert-small-v1](https://huggingface.co/answerdotai/answerai-colbert-small-v1)                                                     | Multi-vector            | 0.6184     | 0.514 |\n| [tomaarsen/splade-modernbert-base-miriad](https://huggingface.co/tomaarsen/splade-modernbert-base-miriad)                                                 | Sparse, finetuned       | 0.6142     | 0.473 |\n| [NeuML/biomedbert-base-colbert](https://huggingface.co/NeuML/biomedbert-base-colbert)                                                                     | Multi-vector            | 0.5963     | 0.463 |\n| [BAAI/bge-base-en-v1.5](https://huggingface.co/BAAI/bge-base-en-v1.5)                                                                                     | Dense                   | 0.5930     | 0.454 |\n| [BAAI/bge-small-en-v1.5](https://huggingface.co/BAAI/bge-small-en-v1.5)                                                                                   | Dense                   | 0.5881     | 0.457 |\n| [sentence-transformers/all-mpnet-base-v2](https://huggingface.co/sentence-transformers/all-mpnet-base-v2)                                                 | Dense                   | 0.5159     | 0.396 |\n| [jinaai/jina-colbert-v2](https://huggingface.co/jinaai/jina-colbert-v2)                                                                                   | Multi-vector            | 0.4992     | 0.401 |\n| [mixedbread-ai/mxbai-colbert-large-v1](https://huggingface.co/mixedbread-ai/mxbai-colbert-large-v1)                                                       | Multi-vector            | 0.4690     | 0.358 |\n| [sentence-transformers/static-retrieval-mrl-en-v1](https://huggingface.co/sentence-transformers/static-retrieval-mrl-en-v1)                               | Dense                   | 0.4614     | 0.323 |\n| [sentence-transformers/all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)                                                   | Dense                   | 0.4458     | 0.321 |\n| [colbert-ir/colbertv2.0](https://huggingface.co/colbert-ir/colbertv2.0)                                                                                   | Multi-vector            | 0.4347     | 0.346 |\n\nModels marked `@N` are evaluated with their document length cap lifted to N tokens, since their native caps (180 to 512 tokens) would otherwise truncate the 941-token average passages. For every multi-vector model this lift was worth +0.08 to +0.24 NDCG@10 over the as-served row, and even the dense DenseOn gained +0.03 from the same treatment.\n\nNote that this does not mean that [multi-vector-encoder/mLateOn-medical](https://huggingface.co/multi-vector-encoder/mLateOn-medical) is the strongest model on *all* domains. It's simply the strongest in *my* domain. This is totally fine, as I just need this model to work well on my data.\n\nDon't underestimate the power of finetuning multi-vector models on your domain. Fourteen and a half hours on a single consumer GPU produced a model that no general-purpose retriever comes close to on this data, and the recipe is a single script with no teacher model and no mined negatives!\n\n### Optimizing the index\n\nThe fair objection to multi-vector retrieval is index size, and this domain is close to the worst case for it. Storing one vector per token, my model needs about 878 vectors per passage, so the 200,000-passage corpus takes roughly 45 GB at fp16, where a dense model needs well under 1 GB. Document length is what makes that gap so wide. The Natural Questions passages in the [companion post](https://huggingface.co/blog/multi-vector-encoder) average about 125 token vectors each, seven times fewer, so a corpus of short passages starts from a far smaller index than this one does. The [`HierarchicalTokenPooling`](https://sbert.net/docs/package_reference/multi_vector_encoder/modules.html#hierarchicaltokenpooling) module compresses exactly this by clustering each document's token embeddings and storing the cluster means, keeping roughly `1 / pool_factor` of the vectors:\n\n```python\nfrom sentence_transformers.multi_vector_encoder.modules import HierarchicalTokenPooling\n\npooling = HierarchicalTokenPooling(pool_factor=4)\ndocument_embeddings = model.encode_document(passages, token_pooling=pooling)\n```\n\nI measured it post-hoc on the finished model, with no pooling-aware training, and on long documents it is remarkably cheap.\n\n[![Embedding size for the 200,000-passage corpus versus NDCG@10, with the token pooling trajectory sweeping the multi-vector index into dense-model territory](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_index_size_ndcg.png)](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_index_size_ndcg.png)\n\nThe solid points are uncompressed embeddings, so that every family is counted the same way and scored with exact search. You would not deploy any of them like that, though. Dense indexes routinely use int8 or binary quantization with rescoring, sparse indexes compress their postings, and multi-vector indexes use PLAID-style residual compression. Don't read those points as the disk you need to buy, but as relative storage cost.\n\nToken pooling is the solid line. Halving the vector count costs 0.0033 NDCG@10 and leaves rank-1 accuracy untouched, and keeping only a quarter of them, at 11.2 GB, still scores 0.8991. The curve keeps going (I measured out to a tenth of the vectors, still at 0.8765) but there is little reason to push pooling that far once quantization is on the table, which is what the dashed line below is about.\n\nThe dashed line is what a real deployment might look like. I gave Omar Khattab early access to the model and the benchmark, and he measured these configurations with [fast-plaid](https://github.com/lightonai/fast-plaid) at 1-bit residual quantization, using compact 17-bit centroid ids and 18-bit document ids instead of its ordinary unpacked 64-bit integers, plus document-side pruning:\n\n| configuration            | vectors kept | index   | NDCG@10 |\n| ------------------------ | ------------ | ------- | ------- |\n| 1-bit PLAID, all vectors | 100%         | 3.37 GB | 0.8984  |\n| 1-bit PLAID + pruning    | 65%          | 2.23 GB | 0.8830  |\n| 1-bit PLAID + pruning    | 42%          | 1.45 GB | 0.8642  |\n\nThat first row is 13x smaller than the raw embeddings, for 0.0155 NDCG@10. That is a far better trade than anywhere on the pooling curve. Quantization shrinks each vector while pooling and pruning cut how many you keep, so they compose, and quantization is the one to reach for first. Push further and the last row lands at 1.45 GB, *smaller* than the fp16 embeddings of [Qwen3-Embedding-8B](https://huggingface.co/Qwen/Qwen3-Embedding-8B) (1.64 GB), while scoring 0.0895 higher. The objection that multi-vector indexes are too big does not survive a properly configured index.\n\nThe pruning here is naive, meant only to establish that token reduction works on top of quantization, so read the bottom two rows as a floor rather than the frontier. If you would rather not hand-tune quantization at all, the [Indexing](https://huggingface.co/blog/multi-vector-encoder#indexing) section of the companion post covers fast-plaid, Qdrant, Weaviate, and Vespa.\n\nMulti-vector retrieval is only as expensive as its index. The raw embeddings for this corpus are 45 GB, and a properly configured index is at least 7x smaller at nearly the same accuracy. The index deserves as much of your attention as the checkpoint.\n\n## Acknowledgements\n\nThanks to [Omar Khattab](https://github.com/okhat) for measuring the quantized and pruned index configurations in [Optimizing the index](https://huggingface.co/blog/train-multi-vector-encoder#optimizing-the-index), and for the discussions around late-interaction index costs.\n\n## Additional Resources\n\n### Training Examples\n\nThese pages have training examples with explanations as well as links to training scripts. You can use them to get familiar with the multi-vector training loop:\n\n- [MIRIAD](https://sbert.net/examples/multi_vector_encoder/training/miriad/README.html): domain-specific training on medical retrieval, an earlier and simpler cousin of this blogpost's recipe\n- [MS MARCO](https://sbert.net/examples/multi_vector_encoder/training/msmarco/README.html): contrastive and knowledge distillation recipes\n- [Multimodal](https://sbert.net/examples/multi_vector_encoder/training/multimodal/README.html): ColPali-style visual document retrieval training\n- [PEFT Adapters](https://sbert.net/examples/multi_vector_encoder/training/peft/README.html): parameter-efficient finetuning with LoRA\n\n### Documentation\n\nFor further learning, you may also want to explore the following resources on Sentence Transformers:\n\n- [Installation](https://sbert.net/docs/installation.html)\n- [Quickstart](https://sbert.net/docs/quickstart.html)\n- [Usage](https://sbert.net/docs/multi_vector_encoder/usage/usage.html)\n- [Creating Custom Models](https://sbert.net/docs/multi_vector_encoder/usage/custom_models.html)\n- [Pretrained Models](https://sbert.net/docs/multi_vector_encoder/pretrained_models.html)\n- [Training Overview](https://sbert.net/docs/multi_vector_encoder/training_overview.html) (This blogpost is a distillation of the Training Overview documentation)\n- [Loss Overview](https://sbert.net/docs/multi_vector_encoder/loss_overview.html)\n- [API Reference](https://sbert.net/docs/package_reference/multi_vector_encoder/index.html)\n\nAnd here is an advanced page that might interest you:\n\n- [Distributed Training](https://sbert.net/docs/sentence_transformer/training/distributed.html)\n\nAnd the companion blogpost, covering everything about *using* these models:\n\n- [Multi-Vector (Late Interaction) Embedding Models with Sentence Transformers](https://huggingface.co/blog/multi-vector-encoder)\n",
  "authors": [],
  "captured_at": "2026-10-19T01:26:53.998049939+00:00",
  "category": "ai",
  "content_html": "<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/\">Sentence Transformers</a> is a Python library for using and training embedding and reranker models for a wide range of applications, such as retrieval augmented generation, semantic search, semantic textual similarity, and more. Its v6.0 update introduces a fourth model type: <code>MultiVectorEncoder</code>, for ColBERT-style late interaction retrieval, alongside a complete training approach for it. In this blogpost, I'll show you how to use it to finetune a multi-vector model that outperforms general-purpose retrievers on your data. This method can also train strong new multi-vector models from scratch. Everything below runs on <code>pip install -U &quot;sentence-transformers[train]&quot;</code>.</p>\n<p>Finetuning multi-vector models involves several components: the model itself, datasets, loss functions, training arguments, evaluators, and the trainer class. I'll have a look at each of these components, accompanied by practical examples of how they can be used for finetuning strong multi-vector models.</p>\n<p>Lastly, in the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#evaluation\">Evaluation</a> section, I'll show you that my finetuned <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/multi-vector-encoder/mLateOn-medical\">multi-vector-encoder/mLateOn-medical</a> model, trained in 14.5 hours on a single RTX 3090 alongside this blogpost, easily outperforms every general-purpose retrieval model I could find on my medical retrieval evaluation: dense, sparse, lexical, and multi-vector alike.</p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png\"><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png\" alt=\"NDCG@10 on MIRIAD versus active parameters: the finetuned mLateOn-medical reaches the top at a fraction of the size of the strongest general-purpose models\" loading=\"eager\" decoding=\"async\" fetchpriority=\"high\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></a></p>\n<p>If you're interested in finetuning dense embedding models, sparse embedding models, or rerankers instead, then consider reading through my prior <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-sentence-transformers\">Training and Finetuning Embedding Models</a>, <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-sparse-encoder\">Training and Finetuning Sparse Embedding Models</a>, and <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-reranker\">Training and Finetuning Reranker Models</a> blogposts.</p>\n<blockquote>\n<p>This blogpost is about <em>training</em> multi-vector models. If you want to learn how to <em>use</em> them, from loading and encoding to indexing in vector databases, see the companion <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/multi-vector-encoder\">Multi-Vector (Late Interaction) Embedding Models with Sentence Transformers</a> blogpost.</p>\n</blockquote>\n<h2>Table of Contents</h2>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#what-are-multi-vector-models\">What are Multi-Vector models?</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#why-finetune\">Why Finetune?</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#training-components\">Training Components</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#model\">Model</a>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#finetuning-an-existing-multi-vector-model\">Finetuning an existing multi-vector model</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#building-one-from-a-base-transformer\">Building one from a base transformer</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#which-starting-point-should-you-pick\">Which starting point should you pick?</a></li>\n</ul>\n</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#dataset\">Dataset</a>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#data-on-the-hugging-face-hub\">Data on the Hugging Face Hub</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#local-data\">Local Data</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#dataset-format\">Dataset Format</a></li>\n</ul>\n</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#loss-function\">Loss Function</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#training-arguments\">Training Arguments</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#evaluator\">Evaluator</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#trainer\">Trainer</a>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#callbacks\">Callbacks</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#multi-dataset-training\">Multi-Dataset Training</a></li>\n</ul>\n</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#evaluation\">Evaluation</a>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#optimizing-the-index\">Optimizing the index</a></li>\n</ul>\n</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#acknowledgements\">Acknowledgements</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#additional-resources\">Additional Resources</a>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#training-examples\">Training Examples</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#documentation\">Documentation</a></li>\n</ul>\n</li>\n</ul>\n<h2>What are Multi-Vector models?</h2>\n<p>A dense embedding model compresses a whole text into a single vector, and similarity is one dot product between two such summaries. A multi-vector model (also called a late-interaction or ColBERT-style model) skips that compression. It keeps <strong>one small vector per token</strong> and scores a query against a document with the MaxSim operator, where every query token finds its best-matching document token and the scores are summed. Token-level matching preserves exactly the fine-grained signals that a single vector has to average away, which usually means stronger retrieval, at the cost of a bigger index.</p>\n<p>The companion <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/multi-vector-encoder\">Multi-Vector Embedding Models</a> blogpost covers the architecture, encoding, scoring, and indexing in detail, so I'll keep this section short and get to the training.</p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/multi-vector-encoder/maxsim_explainer.gif\"><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/multi-vector-encoder/maxsim_explainer.gif\" alt=\"Dense embedding versus multi-vector late interaction\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></a></p>\n<h2>Why Finetune?</h2>\n<p>Finetuning multi-vector models significantly improves their retrieval performance on your specific domain: the vocabulary, the query style, and the notion of relevance all differ between web search, legal discovery, code search, and scientific literature review. Because queries and documents are matched token by token, multi-vector models pick up fine-grained domain signals that single-vector models tend to average away, and they respond very well to even modest amounts of in-domain finetuning data.</p>\n<p>Beyond that, most released retrieval models were configured for short passages. The classic ColBERT checkpoints truncate documents at 180 or 300 tokens, and many popular dense models at 256 or 512, because their MS MARCO-style training data rarely goes beyond that. If your documents are long, these models silently discard most of every document before scoring it. On my medical evaluation with passages averaging 941 tokens, I measured that this truncation costs up to 0.24 NDCG@10, considerably more than any difference between model architectures. When you train your own model, you configure the document length that <em>your</em> data needs.</p>\n<p>LightOn ran into this same dynamic with code retrieval, where general <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/LateOn\">LateOn</a> wasn't enough and they trained <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/LateOn-Code\">LateOn-Code</a>. Your domain, whether that's medical, legal, financial, or your company's internal documents, is not getting an official model. This blogpost shows you how to build it yourself, in a matter of hours, on a single consumer GPU.</p>\n<h2>Training Components</h2>\n<p>Training MultiVectorEncoder models involves the following components:</p>\n<ol>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#model\"><strong>Model</strong></a>: The model to finetune or the architecture to build fresh.</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#dataset\"><strong>Dataset</strong></a>: The data used for training and evaluation.</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#loss-function\"><strong>Loss Function</strong></a>: A function that measures the model's performance and guides the optimization process.</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#training-arguments\"><strong>Training Arguments</strong></a> (optional): Parameters that impact training performance, tracking, and debugging.</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#evaluator\"><strong>Evaluator</strong></a> (optional): A class for evaluating the model before, during, or after training.</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#trainer\"><strong>Trainer</strong></a>: Brings together all training components.</li>\n</ol>\n<p>Let's take a closer look at each component.</p>\n<h2>Model</h2>\n<p>Multi-vector training gives you a real choice of starting point, and it matters more than you might expect.</p>\n<h3>Finetuning an existing multi-vector model</h3>\n<p>If you want to further finetune an existing multi-vector model, you don't have to worry about the architecture at all:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">MultiVectorEncoder</span></span>\n\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Loading in fp32 is preferred for training if your memory can handle it\n</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">MultiVectorEncoder</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n    <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">lightonai/mLateOn-unsupervised<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">model_kwargs</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">torch_dtype<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">float32<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">processor_kwargs</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">model_max_length<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">8192</span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> the tokenizer-level token limit\n</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n</span></span></code></pre>\n<p>The checkpoint brings its own recipe along: its query and document marker tokens, its projection head, its scoring skiplist. For finetuning, you generally want to keep all of that and change only what your data demands. The first thing to check is the length configuration, since many released checkpoints cap documents at 180 to 512 tokens (see <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#why-finetune\">Why Finetune?</a>), and my medical passages run to 1,400 tokens. The mLateOn family already serves the backbone's full 8192 token context, but if your starting checkpoint carries caps, lift them:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Let the model read full documents instead of the caps it was trained with,\n</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> e.g. GTE-ModernColBERT-v1 ships with query_length=48 and document_length=300\n</span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">0</span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-generic-name syntax-python\">query_length</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-constant syntax-language syntax-python\">None</span>\n<span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">0</span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-generic-name syntax-python\">document_length</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-constant syntax-language syntax-python\">None</span>\n</span></span></code></pre>\n<p>With the per-task caps unset, truncation falls back to the tokenizer's <code>model_max_length</code>, which is why I configure that limit at load time above.</p>\n<p>I made one more change, adding a punctuation skiplist that excludes punctuation tokens from document-side scoring and storage. In a 4-way ablation (none, punctuation, stopwords, both) it modestly won on quality, and it shrinks the document index by 9.6% on this data for free:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">string</span></span></span>\n\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> model[2] is the MultiVectorMask module\n</span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">2</span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-generic-name syntax-python\">skiplist_words</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-type syntax-python\">list</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">string</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-generic-name syntax-python\">punctuation</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n<span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">2</span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">resolve_with_tokenizer</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-generic-name syntax-python\">tokenizer</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> token ids are cached, so re-resolve after changing\n</span></span></span></code></pre>\n<h3>Building one from a base transformer</h3>\n<p>You can also point <code>MultiVectorEncoder</code> at any base transformer, and a fresh, randomly initialized token-level projection is appended for you:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">MultiVectorEncoder</span></span>\n\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">MultiVectorEncoder</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">answerdotai/ModernBERT-base<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">model_kwargs</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">torch_dtype<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">float32<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> MultiVectorEncoder(\n</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span>   (0): Transformer({..., &#39;architecture&#39;: &#39;ModernBertModel&#39;})\n</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span>   (1): Dense({&#39;in_features&#39;: 768, &#39;out_features&#39;: 128, &#39;bias&#39;: False, ...})\n</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span>   (2): MultiVectorMask({&#39;skiplist_words&#39;: [], &#39;skiplist_tasks&#39;: [&#39;document&#39;], ...})\n</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span>   (3): Normalize({...})\n</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> )\n</span></span></span></code></pre>\n<p>That's the classic ColBERT pipeline: a <code>Transformer</code> producing contextualized token embeddings, a token-level <code>Dense</code> projecting each of them down to 128 dimensions, a <code>MultiVectorMask</code> deciding which tokens count during scoring, and a token-level <code>Normalize</code>. The projection starts random, so training is required before this model is useful. Interestingly, this works with strong dense embedding backbones too. A fresh projection on <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Alibaba-NLP/gte-modernbert-base\">Alibaba-NLP/gte-modernbert-base</a> reached within 0.03 of the existing-checkpoint starting points in my experiments, from nothing but the projection and 25k training pairs.</p>\n<p>The classic ColBERT tokenization tricks (<code>[MASK]</code> query expansion, <code>[Q]</code> / <code>[D]</code> prefix tokens, a document length cap, a punctuation skiplist) are all off by default and configurable. See <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/usage/custom_models.html\">Creating Custom Models</a> for the full set. For what it's worth, I tested <code>[MASK]</code> query expansion in four configurations for my domain finetune and none of them made a measurable difference, so don't feel obliged to reach for the classic recipe.</p>\n<h3>Which starting point should you pick?</h3>\n<p>I measured this directly while preparing this blogpost, taking six starting points and training each with the identical recipe on 25k medical question-passage pairs from <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/tomaarsen/miriad-4.4M-split\">MIRIAD</a>, then evaluating on 1,000 held-out questions against a 50,000 passage corpus:</p>\n<table>\n<thead>\n<tr>\n<th>Starting point</th>\n<th>Zero-shot NDCG@10</th>\n<th>After 25k pairs</th>\n<th>Delta</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/mLateOn-unsupervised\">lightonai/mLateOn-unsupervised</a></td>\n<td>0.9087</td>\n<td><strong>0.9398</strong></td>\n<td><strong>+0.0311</strong></td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/mLateOn\">lightonai/mLateOn</a></td>\n<td>0.9277</td>\n<td>0.9319</td>\n<td>+0.0042</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/LateOn-unsupervised\">lightonai/LateOn-unsupervised</a></td>\n<td>0.9026</td>\n<td><strong>0.9206</strong></td>\n<td><strong>+0.0180</strong></td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/LateOn\">lightonai/LateOn</a></td>\n<td>0.9185</td>\n<td>0.9105</td>\n<td>-0.0080</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/GTE-ModernColBERT-v1\">lightonai/GTE-ModernColBERT-v1</a></td>\n<td>0.9198</td>\n<td>0.9007</td>\n<td>-0.0191</td>\n</tr>\n<tr>\n<td>Fresh head on <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Alibaba-NLP/gte-modernbert-base\">gte-modernbert-base</a></td>\n<td>-</td>\n<td>0.9177</td>\n<td>-</td>\n</tr>\n</tbody>\n</table>\n<p>The result surprised me, and it replicated across two model families. *The <code>-unsupervised</code> checkpoints adapt to a new domain far better than their finished siblings, overtaking them despite starting lower. These checkpoints sit after large-scale contrastive pretraining but before supervised finetuning on general retrieval, so they carry all the late-interaction structure with none of the general-purpose tuning that domain training then has to undo. The finished checkpoints, by contrast, barely moved or even regressed, at every learning rate I tried.</p>\n<p>So, if the model family you like publishes a pre-supervised checkpoint, start there. If not, a fresh projection on a strong retrieval-pretrained backbone is a close runner-up. Continuing from a fully finished checkpoint is the weakest option for domain adaptation, despite being the most natural-feeling one.</p>\n<h2>Dataset</h2>\n<p>The <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/trainer.html\"><code>MultiVectorEncoderTrainer</code></a> uses <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset\"><code>datasets.Dataset</code></a> or <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.DatasetDict\"><code>datasets.DatasetDict</code></a> instances for training and evaluation. You can load data from the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets\">Hugging Face Datasets Hub</a> or use local data in whatever format you prefer (e.g. CSV, JSON, Parquet, Arrow, or SQL).</p>\n<p><strong>Note:</strong> Lots of public datasets that work out of the box with Sentence Transformers have been tagged with <code>sentence-transformers</code> on the Hugging Face Hub, so you can easily find them on <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets?other=sentence-transformers\">https://huggingface.co/datasets?other=sentence-transformers</a>. Consider browsing through these to find ready-to-go datasets that might be useful for your tasks, domains, or languages.</p>\n<h3>Data on the Hugging Face Hub</h3>\n<p>You can use the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/datasets/main/en/package_reference/loading_methods#datasets.load_dataset\"><code>load_dataset</code></a> function to load data from datasets on the Hub:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">datasets</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">load_dataset</span></span>\n\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">train_dataset</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">load_dataset</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">tomaarsen/miriad-4.4M-split<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">split</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">train<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n<span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-function syntax-builtin syntax-python\">print</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">train_dataset</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n<span class=\"syntax-comment syntax-block syntax-documentation syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-python\">&quot;&quot;&quot;</span>\nDataset({\n    features: [&#39;question&#39;, &#39;passage_text&#39;],\n    num_rows: 4467542\n})\n<span class=\"syntax-punctuation syntax-definition syntax-comment syntax-end syntax-python\">&quot;&quot;&quot;</span></span>\n</span></span></code></pre>\n<p>This is the dataset I'll train on in this blogpost: 4.4 million medical questions from <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/miriad/miriad-4.4M\">MIRIAD</a>, each paired with the source passage that contains its answer (averaging 941 tokens). Simple (query, relevant passage) pairs like these are the easiest retrieval training data to collect for your own domain, and as you'll see, they're all you need.</p>\n<h3>Local Data</h3>\n<p>You can also use <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/datasets/main/en/package_reference/loading_methods#datasets.load_dataset\"><code>load_dataset</code></a> for loading local data in common file formats:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">datasets</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">load_dataset</span></span>\n\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">dataset</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">load_dataset</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">csv<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">data_files</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">my_file.csv<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> or\n</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">dataset</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">load_dataset</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">json<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">data_files</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">my_file.json<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n</span></span></code></pre>\n<p>And if your local data requires pre-processing, you can use <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset.from_dict\"><code>datasets.Dataset.from_dict</code></a> to initialize your dataset with a dictionary of lists:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">datasets</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">Dataset</span></span>\n\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">queries</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-structure syntax-list syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-list syntax-begin syntax-python\">[</span><span class=\"syntax-punctuation syntax-section syntax-list syntax-end syntax-python\">]</span></span>\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">documents</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-structure syntax-list syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-list syntax-begin syntax-python\">[</span><span class=\"syntax-punctuation syntax-section syntax-list syntax-end syntax-python\">]</span></span>\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Open a file, perform preprocessing, filtering, cleaning, etc.\n</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> and append to the lists\n</span>\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">dataset</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">Dataset</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">from_dict</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span>\n    <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">query<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">queries</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary-or-set syntax-python\">,</span>\n    <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">document<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">documents</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary-or-set syntax-python\">,</span>\n<span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n</span></span></code></pre>\n<h3>Dataset Format</h3>\n<p>It is important that your dataset format matches your loss function (or that you choose a loss function that matches your dataset format). Verifying whether a dataset format works with a loss function involves two steps:</p>\n<ol>\n<li>If your loss function requires a <em>Label</em> according to the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/loss_overview.html\">Loss Overview</a> table, then your dataset must have a <strong>column named &quot;label&quot; or &quot;score&quot;</strong>. This column is automatically taken as the label.</li>\n<li>All columns not named &quot;label&quot; or &quot;score&quot; are considered <em>Inputs</em> according to the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/loss_overview.html\">Loss Overview</a> table. The number of remaining columns must match the number of valid inputs for your chosen loss. The names of these columns are <strong>irrelevant</strong>, only the <strong>order matters</strong>.</li>\n</ol>\n<p>There are two multi-vector specific conventions on top of this:</p>\n<ul>\n<li>Positional query and document assignment: the first column is embedded as the <em>query</em> and all following columns as <em>documents</em>, regardless of the column names. This default can be overridden per column via the standard <code>router_mapping</code> training argument.</li>\n<li>Knowledge distillation format: one column per candidate document, i.e. <code>(query, document_1, ..., document_N, scores)</code> where <code>scores</code> is a list of N teacher scores per row. For KD datasets that store query and document <em>IDs</em> alongside separate text datasets (e.g. <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/lightonai/ms-marco-en-bge\">lightonai/ms-marco-en-bge</a>), you can use <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/util.html#sentence_transformers.util.dataset.resolve_ids\"><code>resolve_ids</code></a> to resolve the IDs to texts on the fly.</li>\n</ul>\n<h2>Loss Function</h2>\n<p>Loss functions quantify how well a model performs for a given batch of data, allowing an optimizer to update the model weights to produce more favourable (i.e., lower) loss values. The right loss function for your task depends on the data you have and what you're trying to achieve. You can find a full list of options in the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/loss_overview.html\">Loss Overview</a>.</p>\n<p>For the common case of question-answer or question-passage pairs, the workhorse is in-batch negatives training with <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/losses.html#multivectormultiplenegativesrankingloss\"><code>MultiVectorMultipleNegativesRankingLoss</code></a>, where every other document in the batch acts as a negative for each query. Bigger batches mean more negatives and stronger training, so in practice you'll want its GradCache variant, <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/losses.html#cachedmultivectormultiplenegativesrankingloss\"><code>CachedMultiVectorMultipleNegativesRankingLoss</code></a>, which decouples the effective batch size from what fits on your GPU:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">MultiVectorEncoder</span></span>\n<span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">multi_vector_encoder</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">losses</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">CachedMultiVectorMultipleNegativesRankingLoss</span></span>\n\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">MultiVectorEncoder</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">lightonai/mLateOn-unsupervised<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">model_kwargs</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">torch_dtype<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">float32<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">loss</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">CachedMultiVectorMultipleNegativesRankingLoss</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n    <span class=\"syntax-variable syntax-parameter syntax-python\">model</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">mini_batch_size</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">16</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> how many documents to encode per chunk: bounds memory, not quality\n</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n</span></span></code></pre>\n<p>The <code>mini_batch_size</code> parameter bounds the memory by encoding documents in chunks of this size, while the effective contrastive batch size (128 in my run below, and in my ablations bigger batches bought nothing further) stays a free choice. GradCache guarantees identical results regardless of the chunk size, so lower it for smaller GPUs at only a wall-clock cost. When your document lengths vary a lot, consider its sibling <code>mini_batch_num_tokens</code>, which packs each chunk to a total token budget instead of a document count, so a chunk of unusually long documents can never spike your memory (my <code>mini_batch_size=16</code> at roughly 940 tokens per document corresponds to <code>mini_batch_num_tokens=15_000</code>).</p>\n<p>One multi-vector specific trap is that the contrastive losses default to <code>scale=1.0</code>, unlike the dense embedding equivalent which defaults to <code>scale=20.0</code>. That 20.0 exists because a cosine similarity is a single value in [-1, 1], too narrow a range for a sharp softmax. A MaxSim score instead sums one best-match similarity per query token, so it already spans roughly [0, query_length]: a 32-token query can score up to 32. So don't copy <code>scale=20.0</code> over from a dense training script, since it would saturate the softmax and kill your gradients.</p>\n<p>For distillation from a stronger teacher, which is how the strongest general-purpose late-interaction models are trained, see <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/losses.html#multivectordistillkldivloss\"><code>MultiVectorDistillKLDivLoss</code></a> and the Knowledge Distillation tab in the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/training_overview.html#trainer\">Training Overview</a> documentation.</p>\n<h2>Training Arguments</h2>\n<p>You can customize the training process using the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/training_args.html\"><code>MultiVectorEncoderTrainingArguments</code></a> class. This class lets you adjust parameters that can impact training speed and help you understand what's happening during training.</p>\n<p>For more information on the most useful training arguments, check out the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/training_overview.html#training-arguments\">Multi-Vector Encoder &gt; Training Overview &gt; Training Arguments</a>. It's worth reading to get the most out of your training.</p>\n<p>Here's an example, using the values from my actual training run:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">MultiVectorEncoderTrainingArguments</span></span>\n<span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">base</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">sampler</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">BatchSamplers</span></span>\n\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">args</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">MultiVectorEncoderTrainingArguments</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Required parameter:\n</span>    <span class=\"syntax-variable syntax-parameter syntax-python\">output_dir</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">models/mLateOn-medical<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Optional training parameters:\n</span>    <span class=\"syntax-variable syntax-parameter syntax-python\">num_train_epochs</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">1</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">per_device_train_batch_size</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">128</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> the effective contrastive batch, thanks to GradCache\n</span>    <span class=\"syntax-variable syntax-parameter syntax-python\">per_device_eval_batch_size</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">16</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">learning_rate</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-float syntax-python\">1e-4</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">warmup_steps</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-float syntax-python\">0<span class=\"syntax-punctuation syntax-separator syntax-decimal syntax-python\">.</span>05</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">prompts</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">question<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">[Q] <span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary-or-set syntax-python\">,</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">[D] <span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> the checkpoint&#39;s markers, keyed by training column\n</span>    <span class=\"syntax-variable syntax-parameter syntax-python\">fp16</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-language syntax-python\">False</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Set to True if you have a GPU that supports FP16\n</span>    <span class=\"syntax-variable syntax-parameter syntax-python\">bf16</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-language syntax-python\">True</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Set to True if you have a GPU that supports BF16\n</span>    <span class=\"syntax-variable syntax-parameter syntax-python\">batch_sampler</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">BatchSamplers</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-variable syntax-other syntax-constant syntax-python\">NO_DUPLICATES</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> in-batch negatives benefit from no duplicates\n</span>    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Optional tracking/debugging parameters:\n</span>    <span class=\"syntax-variable syntax-parameter syntax-python\">eval_strategy</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">steps<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">eval_steps</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-float syntax-python\">0<span class=\"syntax-punctuation syntax-separator syntax-decimal syntax-python\">.</span>1</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">save_strategy</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">steps<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">save_steps</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-float syntax-python\">0<span class=\"syntax-punctuation syntax-separator syntax-decimal syntax-python\">.</span>05</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">logging_steps</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-float syntax-python\">0<span class=\"syntax-punctuation syntax-separator syntax-decimal syntax-python\">.</span>01</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">run_name</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">mLateOn-medical<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Will be used in e.g. Trackio, W&amp;B, etc.\n</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n</span></span></code></pre>\n<p>A few of these deserve a comment:</p>\n<ul>\n<li><code>prompts</code>: training does not automatically apply the prompts stored in the model, so map them onto your training columns explicitly. Here that is the checkpoint's <code>[Q]</code> marker for the question column and <code>[D]</code> for the passage column, keeping training consistent with inference.</li>\n<li><code>max_length</code> (deliberately not set): this argument caps tokenization during <em>training only</em>, for when you want cheaper training than the model's full serving length. I measured what that shortcut costs on this data. Training at 512 tokens lost about 0.015 NDCG@10 for about 2x the speed, and the deficit did not shrink with more data, because the model simply never sees what got cut off. Leave it unset so training matches inference, unless you need the speedup more than the quality.</li>\n<li><code>learning_rate=1e-4</code>: after a sweep from 5e-6 to 2e-4, I had the best luck with this higher-than-usual learning rate.</li>\n</ul>\n<h2>Evaluator</h2>\n<p>To track your model's performance during training, you can pass an <code>eval_dataset</code> to the trainer for evaluation loss, but concrete retrieval metrics are much more informative. Sentence Transformers includes the following built-in evaluators for multi-vector models:</p>\n<table>\n<thead>\n<tr>\n<th>Evaluator</th>\n<th>Required Data</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectorinformationretrievalevaluator\"><code>MultiVectorInformationRetrievalEvaluator</code></a></td>\n<td>Queries, corpus, and relevant document mappings</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectornanobeirevaluator\"><code>MultiVectorNanoBEIREvaluator</code></a></td>\n<td>No data required</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectortripletevaluator\"><code>MultiVectorTripletEvaluator</code></a></td>\n<td>(anchor, positive, negative) triplets</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectorrerankingevaluator\"><code>MultiVectorRerankingEvaluator</code></a></td>\n<td>List of <code>{'query': '...', 'positive': [...], 'negative': [...]}</code> dictionaries</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectordistillationevaluator\"><code>MultiVectorDistillationEvaluator</code></a></td>\n<td>Queries with candidate documents and teacher scores</td>\n</tr>\n</tbody>\n</table>\n<p>For domain finetuning, the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectorinformationretrievalevaluator\"><code>MultiVectorInformationRetrievalEvaluator</code></a> built from your own held-out data is the one that matters. One tip on constructing it is that the corpus should be hard enough that models can be told apart. In my case the MIRIAD questions are generated from their own source passages, which makes retrieval unusually easy. Against just the 10k gold passages, nearly every model scored above 0.97 NDCG@10. If your evaluation saturates like that, add <em>distractor</em> passages (I use deduplicated passages from the training split) until the scores spread out:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">datasets</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">load_dataset</span></span>\n<span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">multi_vector_encoder</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">evaluation</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">MultiVectorInformationRetrievalEvaluator</span></span>\n\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">dataset</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">load_dataset</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">tomaarsen/miriad-4.4M-split<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Gold: 1,000 evaluation questions, each mapping to its own passage, with the\n</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> eval split&#39;s full ~10k unique passages as the initial corpus\n</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">corpus</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-empty-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-python\">}</span></span></span>\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">queries</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-empty-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-python\">}</span></span></span>\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">relevant_docs</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-empty-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-python\">}</span></span></span>\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-empty-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-python\">}</span></span></span>\n<span class=\"syntax-meta syntax-statement syntax-for syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-for syntax-python\">for</span> <span class=\"syntax-meta syntax-generic-name syntax-python\">idx</span><span class=\"syntax-punctuation syntax-separator syntax-target-list syntax-python\">,</span> <span class=\"syntax-meta syntax-generic-name syntax-python\">row</span> <span class=\"syntax-meta syntax-statement syntax-for syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-for syntax-in syntax-python\">in</span></span></span><span class=\"syntax-meta syntax-statement syntax-for syntax-python\"> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-function syntax-builtin syntax-python\">enumerate</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">dataset</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">eval<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span><span class=\"syntax-punctuation syntax-section syntax-block syntax-for syntax-python\">:</span></span>\n    <span class=\"syntax-meta syntax-statement syntax-if syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-conditional syntax-python\">if</span> <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-logical syntax-python\">not</span> <span class=\"syntax-keyword syntax-operator syntax-logical syntax-python\">in</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span><span class=\"syntax-punctuation syntax-section syntax-block syntax-conditional syntax-python\">:</span></span>\n        <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-storage syntax-type syntax-string syntax-python\">f</span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">p</span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-begin syntax-python\">{</span><span class=\"syntax-source syntax-python syntax-embedded\"><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-function syntax-builtin syntax-python\">len</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span></span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-end syntax-python\">}</span></span><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span>\n        <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">corpus</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span>\n    <span class=\"syntax-meta syntax-statement syntax-if syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-conditional syntax-python\">if</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">idx</span></span> <span class=\"syntax-keyword syntax-operator syntax-comparison syntax-python\">&lt;</span> <span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">1_000</span><span class=\"syntax-punctuation syntax-section syntax-block syntax-conditional syntax-python\">:</span></span>\n        <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">queries</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-storage syntax-type syntax-string syntax-python\">f</span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">q</span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-begin syntax-python\">{</span><span class=\"syntax-source syntax-python syntax-embedded\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">idx</span></span></span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-end syntax-python\">}</span></span><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">question<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span>\n        <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">relevant_docs</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-storage syntax-type syntax-string syntax-python\">f</span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">q</span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-begin syntax-python\">{</span><span class=\"syntax-source syntax-python syntax-embedded\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">idx</span></span></span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-end syntax-python\">}</span></span><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span>\n\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Distractors: unique train passages that make the haystack realistic\n</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">seen</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-type syntax-python\">set</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n<span class=\"syntax-meta syntax-statement syntax-for syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-for syntax-python\">for</span> <span class=\"syntax-meta syntax-generic-name syntax-python\">row</span> <span class=\"syntax-meta syntax-statement syntax-for syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-for syntax-in syntax-python\">in</span></span></span><span class=\"syntax-meta syntax-statement syntax-for syntax-python\"> <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">dataset</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">train<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-punctuation syntax-section syntax-block syntax-for syntax-python\">:</span></span>\n    <span class=\"syntax-meta syntax-statement syntax-if syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-conditional syntax-python\">if</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-function syntax-builtin syntax-python\">len</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">corpus</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span> <span class=\"syntax-keyword syntax-operator syntax-comparison syntax-python\">&gt;=</span> <span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">200_000</span><span class=\"syntax-punctuation syntax-section syntax-block syntax-conditional syntax-python\">:</span></span>\n        <span class=\"syntax-keyword syntax-control syntax-flow syntax-python\">break</span>\n    <span class=\"syntax-meta syntax-statement syntax-if syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-conditional syntax-python\">if</span> <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-logical syntax-python\">not</span> <span class=\"syntax-keyword syntax-operator syntax-logical syntax-python\">in</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">seen</span></span><span class=\"syntax-punctuation syntax-section syntax-block syntax-conditional syntax-python\">:</span></span>\n        <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">seen</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">add</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n        <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">corpus</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-storage syntax-type syntax-string syntax-python\">f</span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">d</span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-begin syntax-python\">{</span><span class=\"syntax-source syntax-python syntax-embedded\"><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-function syntax-builtin syntax-python\">len</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">corpus</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span></span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-end syntax-python\">}</span></span><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span>\n\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">evaluator</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">MultiVectorInformationRetrievalEvaluator</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n    <span class=\"syntax-variable syntax-parameter syntax-python\">queries</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">queries</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">corpus</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">corpus</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">relevant_docs</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">relevant_docs</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">name</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">miriad-dev<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">batch_size</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">16</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n</span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> results = evaluator(model)\n</span></span></span></code></pre>\n<h2>Trainer</h2>\n<p>The <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/trainer.html\"><code>MultiVectorEncoderTrainer</code></a> is where all previous components come together. Here is the complete script that trained <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/multi-vector-encoder/mLateOn-medical\">multi-vector-encoder/mLateOn-medical</a>, the model from the introduction:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">logging</span></span></span>\n<span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">string</span></span></span>\n<span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">traceback</span></span></span>\n\n<span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">datasets</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">load_dataset</span></span>\n\n<span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-punctuation syntax-section syntax-import-list syntax-begin syntax-python\">(</span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\">\n    <span class=\"syntax-meta syntax-generic-name syntax-python\">MultiVectorEncoder</span><span class=\"syntax-punctuation syntax-separator syntax-import-list syntax-python\">,</span>\n    <span class=\"syntax-meta syntax-generic-name syntax-python\">MultiVectorEncoderModelCardData</span><span class=\"syntax-punctuation syntax-separator syntax-import-list syntax-python\">,</span>\n    <span class=\"syntax-meta syntax-generic-name syntax-python\">MultiVectorEncoderTrainer</span><span class=\"syntax-punctuation syntax-separator syntax-import-list syntax-python\">,</span>\n    <span class=\"syntax-meta syntax-generic-name syntax-python\">MultiVectorEncoderTrainingArguments</span><span class=\"syntax-punctuation syntax-separator syntax-import-list syntax-python\">,</span>\n<span class=\"syntax-punctuation syntax-section syntax-import-list syntax-end syntax-python\">)</span></span>\n<span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">base</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">sampler</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">BatchSamplers</span></span>\n<span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">multi_vector_encoder</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">evaluation</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">MultiVectorInformationRetrievalEvaluator</span></span>\n<span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">multi_vector_encoder</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">losses</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">CachedMultiVectorMultipleNegativesRankingLoss</span></span>\n\n<span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">logging</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">basicConfig</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-variable syntax-parameter syntax-python\">format</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-constant syntax-other syntax-placeholder syntax-python\">%(<span class=\"syntax-variable syntax-other syntax-placeholder syntax-python\">asctime</span>)s</span> - <span class=\"syntax-constant syntax-other syntax-placeholder syntax-python\">%(<span class=\"syntax-variable syntax-other syntax-placeholder syntax-python\">message</span>)s</span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">datefmt</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-constant syntax-other syntax-placeholder syntax-python\">%Y</span>-<span class=\"syntax-constant syntax-other syntax-placeholder syntax-python\">%m</span>-<span class=\"syntax-constant syntax-other syntax-placeholder syntax-python\">%d</span> <span class=\"syntax-constant syntax-other syntax-placeholder syntax-python\">%H</span>:<span class=\"syntax-constant syntax-other syntax-placeholder syntax-python\">%M</span>:<span class=\"syntax-constant syntax-other syntax-placeholder syntax-python\">%S</span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">level</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">logging</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-variable syntax-other syntax-constant syntax-python\">INFO</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n\n<span class=\"syntax-meta syntax-function syntax-python\"><span class=\"syntax-storage syntax-type syntax-function syntax-python\">def</span> <span class=\"syntax-entity syntax-name syntax-function syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">main</span></span></span><span class=\"syntax-meta syntax-function syntax-parameters syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-python\">(</span></span><span class=\"syntax-meta syntax-function syntax-parameters syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-end syntax-python\">)</span></span><span class=\"syntax-meta syntax-function syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-function syntax-begin syntax-python\">:</span></span>\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> 1. Load the starting checkpoint: contrastively pretrained, not yet supervised\n</span>    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Loading in fp32 is preferred for training if your memory can handle it\n</span>    <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">MultiVectorEncoder</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n        <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">lightonai/mLateOn-unsupervised<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">model_kwargs</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">torch_dtype<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">float32<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">processor_kwargs</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">model_max_length<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">8192</span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">model_card_data</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">MultiVectorEncoderModelCardData</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n            <span class=\"syntax-variable syntax-parameter syntax-python\">language</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">en<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n            <span class=\"syntax-variable syntax-parameter syntax-python\">license</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">apache-2.0<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n            <span class=\"syntax-variable syntax-parameter syntax-python\">model_name</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">mLateOn finetuned on MIRIAD medical retrieval<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        </span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    </span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> 2. Lift the per-task length caps so training and inference see full medical passages\n</span>    <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">0</span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-generic-name syntax-python\">query_length</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-constant syntax-language syntax-python\">None</span>\n    <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">0</span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-generic-name syntax-python\">document_length</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-constant syntax-language syntax-python\">None</span>\n\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> 3. Skip punctuation tokens during scoring: a small quality win and a 9.6% smaller index\n</span>    <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">2</span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-generic-name syntax-python\">skiplist_words</span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-type syntax-python\">list</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">string</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-generic-name syntax-python\">punctuation</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n    <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">2</span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">resolve_with_tokenizer</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-generic-name syntax-python\">tokenizer</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> 4. Load 1 million medical question-passage pairs\n</span>    <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">train_dataset</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">load_dataset</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">tomaarsen/miriad-4.4M-split<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">split</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">train<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">select</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-function syntax-builtin syntax-python\">range</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">1_000_000</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> 5. In-batch negatives with GradCache: large effective batch, memory-bounded chunks\n</span>    <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">loss</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">CachedMultiVectorMultipleNegativesRankingLoss</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-variable syntax-parameter syntax-python\">model</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">mini_batch_size</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">16</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> 6. A light dev evaluator to watch progress during training: 500 held-out questions\n</span>    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> against the eval split&#39;s ~10k unique passages. The full 200k protocol runs afterwards.\n</span>    <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">eval_split</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">load_dataset</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">tomaarsen/miriad-4.4M-split<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">split</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">eval<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n    <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">corpus</span></span>, <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">queries</span></span>, <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">relevant_docs</span></span>, <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-empty-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-python\">}</span></span></span>, <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-empty-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-python\">}</span></span></span>, <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-empty-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-python\">}</span></span></span>, <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-empty-dictionary syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-python\">}</span></span></span>\n    <span class=\"syntax-meta syntax-statement syntax-for syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-for syntax-python\">for</span> <span class=\"syntax-meta syntax-generic-name syntax-python\">idx</span><span class=\"syntax-punctuation syntax-separator syntax-target-list syntax-python\">,</span> <span class=\"syntax-meta syntax-generic-name syntax-python\">row</span> <span class=\"syntax-meta syntax-statement syntax-for syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-for syntax-in syntax-python\">in</span></span></span><span class=\"syntax-meta syntax-statement syntax-for syntax-python\"> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-function syntax-builtin syntax-python\">enumerate</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">eval_split</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span><span class=\"syntax-punctuation syntax-section syntax-block syntax-for syntax-python\">:</span></span>\n        <span class=\"syntax-meta syntax-statement syntax-if syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-conditional syntax-python\">if</span> <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-logical syntax-python\">not</span> <span class=\"syntax-keyword syntax-operator syntax-logical syntax-python\">in</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span><span class=\"syntax-punctuation syntax-section syntax-block syntax-conditional syntax-python\">:</span></span>\n            <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-storage syntax-type syntax-string syntax-python\">f</span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">p</span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-begin syntax-python\">{</span><span class=\"syntax-source syntax-python syntax-embedded\"><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-function syntax-builtin syntax-python\">len</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span></span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-end syntax-python\">}</span></span><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span>\n            <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">corpus</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span>\n        <span class=\"syntax-meta syntax-statement syntax-if syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-conditional syntax-python\">if</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">idx</span></span> <span class=\"syntax-keyword syntax-operator syntax-comparison syntax-python\">&lt;</span> <span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">500</span><span class=\"syntax-punctuation syntax-section syntax-block syntax-conditional syntax-python\">:</span></span>\n            <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">queries</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-storage syntax-type syntax-string syntax-python\">f</span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">q</span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-begin syntax-python\">{</span><span class=\"syntax-source syntax-python syntax-embedded\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">idx</span></span></span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-end syntax-python\">}</span></span><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">question<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span>\n            <span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">relevant_docs</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-storage syntax-type syntax-string syntax-python\">f</span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">q</span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-begin syntax-python\">{</span><span class=\"syntax-source syntax-python syntax-embedded\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">idx</span></span></span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-end syntax-python\">}</span></span><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passage_to_id</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">row</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-python\">[</span></span><span class=\"syntax-meta syntax-item-access syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span></span><span class=\"syntax-meta syntax-item-access syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-brackets syntax-end syntax-python\">]</span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span>\n    <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">dev_evaluator</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">MultiVectorInformationRetrievalEvaluator</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n        <span class=\"syntax-variable syntax-parameter syntax-python\">queries</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">queries</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">corpus</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">corpus</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">relevant_docs</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">relevant_docs</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">name</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">miriad-dev<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">batch_size</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">16</span>\n    </span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> 7. Training arguments, as discussed above\n</span>    <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">run_name</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">mLateOn-medical<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span>\n    <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">args</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">MultiVectorEncoderTrainingArguments</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n        <span class=\"syntax-variable syntax-parameter syntax-python\">output_dir</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-storage syntax-type syntax-string syntax-python\">f</span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">models/</span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-begin syntax-python\">{</span><span class=\"syntax-source syntax-python syntax-embedded\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">run_name</span></span></span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-end syntax-python\">}</span></span><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">num_train_epochs</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">1</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">per_device_train_batch_size</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">128</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">per_device_eval_batch_size</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">16</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">learning_rate</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-float syntax-python\">1e-4</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">warmup_steps</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-float syntax-python\">0<span class=\"syntax-punctuation syntax-separator syntax-decimal syntax-python\">.</span>05</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">prompts</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">question<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">[Q] <span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary-or-set syntax-python\">,</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">passage_text<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">[D] <span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">fp16</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-language syntax-python\">False</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Set to True if you have a GPU that supports FP16\n</span>        <span class=\"syntax-variable syntax-parameter syntax-python\">bf16</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-language syntax-python\">True</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> Set to True if you have a GPU that supports BF16\n</span>        <span class=\"syntax-variable syntax-parameter syntax-python\">batch_sampler</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">BatchSamplers</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-variable syntax-other syntax-constant syntax-python\">NO_DUPLICATES</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">eval_strategy</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">steps<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">eval_steps</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-float syntax-python\">0<span class=\"syntax-punctuation syntax-separator syntax-decimal syntax-python\">.</span>1</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">save_strategy</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">steps<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">save_steps</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-float syntax-python\">0<span class=\"syntax-punctuation syntax-separator syntax-decimal syntax-python\">.</span>05</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">logging_steps</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-float syntax-python\">0<span class=\"syntax-punctuation syntax-separator syntax-decimal syntax-python\">.</span>01</span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">run_name</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">run_name</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    </span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> 8. Create a trainer &amp; train\n</span>    <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">trainer</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">MultiVectorEncoderTrainer</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n        <span class=\"syntax-variable syntax-parameter syntax-python\">model</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">args</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">args</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">train_dataset</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">train_dataset</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">loss</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">loss</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n        <span class=\"syntax-variable syntax-parameter syntax-python\">evaluator</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">dev_evaluator</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    </span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n    <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">trainer</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">train</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> 9. Save the trained model\n</span>    <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">save_pretrained</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-storage syntax-type syntax-string syntax-python\">f</span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">models/</span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-begin syntax-python\">{</span><span class=\"syntax-source syntax-python syntax-embedded\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">run_name</span></span></span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-end syntax-python\">}</span></span><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">/final<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n    <span class=\"syntax-comment syntax-line syntax-number-sign syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-python\">#</span> 10. (Optional) Push it to the Hugging Face Hub\n</span>    <span class=\"syntax-meta syntax-statement syntax-try syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-try syntax-python\">try</span><span class=\"syntax-punctuation syntax-section syntax-block syntax-try syntax-python\">:</span></span>\n        <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">push_to_hub</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">run_name</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n    <span class=\"syntax-meta syntax-statement syntax-except syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-except syntax-python\">except</span> <span class=\"syntax-support syntax-type syntax-exception syntax-python\">Exception</span><span class=\"syntax-punctuation syntax-section syntax-block syntax-except syntax-python\">:</span></span>\n        <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">logging</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">error</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-storage syntax-type syntax-string syntax-python\">f</span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-interpolated syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">Error uploading model to the Hugging Face Hub:<span class=\"syntax-constant syntax-character syntax-escape syntax-python\">\\n</span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-begin syntax-python\">{</span><span class=\"syntax-source syntax-python syntax-embedded\"><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">traceback</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">format_exc</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span></span></span><span class=\"syntax-meta syntax-interpolation syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-interpolation syntax-end syntax-python\">}</span></span><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n\n\n<span class=\"syntax-meta syntax-statement syntax-if syntax-python\"><span class=\"syntax-keyword syntax-control syntax-flow syntax-conditional syntax-python\">if</span> <span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-variable syntax-magic syntax-python\">__name__</span></span> <span class=\"syntax-keyword syntax-operator syntax-comparison syntax-python\">==</span> <span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">__main__<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-block syntax-conditional syntax-python\">:</span></span>\n    <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">main</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n</span></span></code></pre>\n<p>That's the whole recipe: a pre-supervised checkpoint, a million domain pairs, in-batch negatives, full document length, and a higher-than-usual learning rate. The run took 14.5 hours on my single RTX 3090 at a peak of 17.5 GB VRAM, and every one of those choices was the winner of a measured comparison rather than a guess.</p>\n<p>For readers on smaller budgets, my scaling experiments put 100k pairs (75 minutes of training) within 0.012 NDCG@10 of the full million-pair run. Most of the gain comes in the first hour.</p>\n<h3>Callbacks</h3>\n<p>The MultiVectorEncoder trainer supports various <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/transformers/main_classes/callback#transformers.TrainerCallback\"><code>transformers.TrainerCallback</code></a> subclasses, including:</p>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/transformers/en/main_classes/callback#transformers.integrations.WandbCallback\"><code>WandbCallback</code></a> for logging training metrics to W&amp;B if <code>wandb</code> is installed</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/transformers/en/main_classes/callback#transformers.integrations.TensorBoardCallback\"><code>TensorBoardCallback</code></a> for logging training metrics to TensorBoard if <code>tensorboard</code> is accessible</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/transformers/en/main_classes/callback#transformers.integrations.CodeCarbonCallback\"><code>CodeCarbonCallback</code></a> for tracking carbon emissions during training if <code>codecarbon</code> is installed</li>\n</ul>\n<p>Enable these via the <code>report_to</code> training argument, e.g. <code>report_to=[&quot;wandb&quot;, &quot;codecarbon&quot;]</code>, with the required dependencies installed. It defaults to <code>&quot;none&quot;</code>, and <code>report_to=&quot;all&quot;</code> activates every integration whose dependency is installed.</p>\n<p>Refer to the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/transformers/en/main_classes/callback\">Transformers Callbacks documentation</a> for more information on these callbacks and how to create your own.</p>\n<h3>Multi-Dataset Training</h3>\n<p>Typically, top-performing general-purpose models are trained on multiple datasets simultaneously. However, this approach can be challenging due to the varying formats of each dataset. Fortunately, the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/trainer.html\"><code>MultiVectorEncoderTrainer</code></a> allows you to train on multiple datasets without requiring a uniform format. Additionally, it provides the flexibility to apply different loss functions to each dataset. Here are the steps to train with multiple datasets at once:</p>\n<ul>\n<li>Use a dictionary of <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset\"><code>datasets.Dataset</code></a> instances (or a <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.DatasetDict\"><code>datasets.DatasetDict</code></a>) as the <code>train_dataset</code> (and optionally also <code>eval_dataset</code>).</li>\n<li>(Optional) Use a dictionary of loss functions mapping dataset names to losses. Only required if you wish to use different loss functions for different datasets.</li>\n</ul>\n<p>Each training/evaluation batch will only contain samples from one of the datasets. The order in which batches are sampled from the multiple datasets is defined by the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/sentence_transformer/sampler.html#sentence_transformers.training_args.MultiDatasetBatchSamplers\"><code>MultiDatasetBatchSamplers</code></a> enum, which can be passed to the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/training_args.html\"><code>MultiVectorEncoderTrainingArguments</code></a> via <code>multi_dataset_batch_sampler</code>. Valid options are:</p>\n<ul>\n<li><code>MultiDatasetBatchSamplers.ROUND_ROBIN</code>: Round-robin sampling from each dataset until one is exhausted. With this strategy, it's likely that not all samples from each dataset are used, but each dataset is sampled from equally.</li>\n<li><code>MultiDatasetBatchSamplers.PROPORTIONAL</code> (default): Sample from each dataset in proportion to its size. With this strategy, all samples from each dataset are used and larger datasets are sampled from more frequently.</li>\n</ul>\n<h2>Evaluation</h2>\n<p>To find out where the finetuned model stands, I evaluated it against over 50 retrieval model configurations across four architecture families on the MIRIAD evaluation set, built exactly as in the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#evaluator\">Evaluator</a> section above, with 1,000 held-out medical questions searching 200,000 unique passages (the 10k gold passages hidden among 190k deduplicated distractors from the training split). This corpus is four times the size of the 50,000-passage one from <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#which-starting-point-should-you-pick\">Which starting point should you pick?</a>, so scores are not comparable between the two tables.</p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png\"><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png\" alt=\"NDCG@10 versus active parameters on the MIRIAD 200k benchmark, with an arrow marking the finetuning jump from mLateOn-unsupervised to mLateOn-medical\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></a></p>\n<p>The headline results, with the full table in the collapsible below:</p>\n<table>\n<thead>\n<tr>\n<th>Model</th>\n<th>Family</th>\n<th>NDCG@10</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/multi-vector-encoder/mLateOn-medical\"><strong>multi-vector-encoder/mLateOn-medical (mine)</strong></a></td>\n<td><strong>Multi-vector, finetuned</strong></td>\n<td><strong>0.9139</strong></td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/mLateOn\">lightonai/mLateOn</a></td>\n<td>Multi-vector, zero-shot</td>\n<td>0.8520</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/GTE-ModernColBERT-v1\">lightonai/GTE-ModernColBERT-v1</a> (cap lifted)</td>\n<td>Multi-vector, zero-shot</td>\n<td>0.8502</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Qwen/Qwen3-Embedding-4B\">Qwen/Qwen3-Embedding-4B</a></td>\n<td>Dense, zero-shot</td>\n<td>0.7817</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/voyageai/voyage-4-nano\">voyageai/voyage-4-nano</a></td>\n<td>Dense, zero-shot</td>\n<td>0.7563</td>\n</tr>\n<tr>\n<td>BM25</td>\n<td>Lexical</td>\n<td>0.7501</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/naver/splade-v3\">naver/splade-v3</a></td>\n<td>Sparse, zero-shot</td>\n<td>0.6853</td>\n</tr>\n</tbody>\n</table>\n<p>The finetuned model tops the table, beating the strongest zero-shot model of any architecture by +0.062 NDCG@10. In other words, the strongest zero-shot model returns the right passage as the very first hit for 75.8% of the queries, while the finetuned model does so for 84.9%, cutting the rank-1 error by more than a third.</p>\n<p>The architecture pattern is just as clear, with the top of the table exclusively late interaction. On long documents, one vector per token beats one vector per document, even at matched training and matched backbones. DenseOn and LateOn share training data and architecture except for the head, and the late-interaction sibling wins by +0.12, with the multilingual pair (mDenseOn and mLateOn) replicating this at +0.13. Scale doesn't rescue single vectors either. <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Qwen/Qwen3-Embedding-4B\">Qwen3-Embedding-4B</a>, the strongest dense model with roughly 33x the active (non-embedding) parameters of mine, still stops 0.13 short, and the 8B version scores lower than the 4B.</p>\n<p>BM25 also performs surprisingly well, beating every sparse model, every truncation-capped multi-vector model, and all but three dense models: the multi-billion <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Qwen/Qwen3-Embedding-4B\">Qwen3-Embedding-4B</a> and <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Qwen/Qwen3-Embedding-8B\">8B</a>, and <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/voyageai/voyage-4-nano\">voyage-4-nano</a>, which reads its full 32k token context to edge past by just 0.006. Don't expect that to transfer to your own data though. MIRIAD's questions are generated from the passages, so the lexical overlap between a query and its gold passage is far larger than in typical retrieval, and BM25's unlimited context length lets it use every one of those overlapping words while most neural checkpoints truncate. A BM25 baseline is cheap and always worth running, just don't count on this margin.</p>\n<p>The full field at a glance, sorted by score and colored by architecture family.</p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_ndcg_by_model.png\"><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_ndcg_by_model.png\" alt=\"Sorted NDCG@10 on the MIRIAD 200k benchmark for every evaluated model, colored by architecture family\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></a></p>\n<p>Click to see the full evaluation table</p>\n<table>\n<thead>\n<tr>\n<th>Model</th>\n<th>Family</th>\n<th>NDCG@10</th>\n<th>acc@1</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/multi-vector-encoder/mLateOn-medical\"><strong>multi-vector-encoder/mLateOn-medical (mine)</strong></a></td>\n<td>Multi-vector, finetuned</td>\n<td><strong>0.9139</strong></td>\n<td>0.849</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/mLateOn\">lightonai/mLateOn</a></td>\n<td>Multi-vector</td>\n<td>0.8520</td>\n<td>0.758</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/GTE-ModernColBERT-v1\">lightonai/GTE-ModernColBERT-v1</a> @1024</td>\n<td>Multi-vector</td>\n<td>0.8502</td>\n<td>0.763</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/LateOn\">lightonai/LateOn</a> @1024</td>\n<td>Multi-vector</td>\n<td>0.8485</td>\n<td>0.760</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/mLateOn-unsupervised\">lightonai/mLateOn-unsupervised</a></td>\n<td>Multi-vector</td>\n<td>0.8304</td>\n<td>0.733</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/mixedbread-ai/mxbai-edge-colbert-v0-32m\">mixedbread-ai/mxbai-edge-colbert-v0-32m</a> @1024</td>\n<td>Multi-vector</td>\n<td>0.8186</td>\n<td>0.727</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Qwen/Qwen3-Embedding-4B\">Qwen/Qwen3-Embedding-4B</a></td>\n<td>Dense</td>\n<td>0.7817</td>\n<td>0.669</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Qwen/Qwen3-Embedding-8B\">Qwen/Qwen3-Embedding-8B</a></td>\n<td>Dense</td>\n<td>0.7747</td>\n<td>0.654</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/perplexity-ai/pplx-embed-v1-late-0.6b\">perplexity-ai/pplx-embed-v1-late-0.6b</a> @1024</td>\n<td>Multi-vector</td>\n<td>0.7702</td>\n<td>0.632</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/ColBERT-Zero\">lightonai/ColBERT-Zero</a></td>\n<td>Multi-vector</td>\n<td>0.7613</td>\n<td>0.675</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/LiquidAI/LFM2.5-ColBERT-350M\">LiquidAI/LFM2.5-ColBERT-350M</a></td>\n<td>Multi-vector</td>\n<td>0.7582</td>\n<td>0.664</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/voyageai/voyage-4-nano\">voyageai/voyage-4-nano</a></td>\n<td>Dense</td>\n<td>0.7563</td>\n<td>0.638</td>\n</tr>\n<tr>\n<td>BM25</td>\n<td>Lexical</td>\n<td>0.7501</td>\n<td>0.641</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/jinaai/jina-embeddings-v5-text-small-retrieval\">jinaai/jina-embeddings-v5-text-small-retrieval</a></td>\n<td>Dense</td>\n<td>0.7470</td>\n<td>0.620</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Qwen/Qwen3-Embedding-0.6B\">Qwen/Qwen3-Embedding-0.6B</a></td>\n<td>Dense</td>\n<td>0.7408</td>\n<td>0.620</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/perplexity-ai/pplx-embed-v1-0.6b\">perplexity-ai/pplx-embed-v1-0.6b</a></td>\n<td>Dense</td>\n<td>0.7384</td>\n<td>0.615</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/mixedbread-ai/mxbai-edge-colbert-v0-32m\">mixedbread-ai/mxbai-edge-colbert-v0-32m</a></td>\n<td>Multi-vector</td>\n<td>0.7350</td>\n<td>0.639</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/mixedbread-ai/mxbai-edge-colbert-v0-17m\">mixedbread-ai/mxbai-edge-colbert-v0-17m</a></td>\n<td>Multi-vector</td>\n<td>0.7271</td>\n<td>0.631</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/answerdotai/answerai-colbert-small-v1\">answerdotai/answerai-colbert-small-v1</a> @512</td>\n<td>Multi-vector</td>\n<td>0.7264</td>\n<td>0.615</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/DenseOn\">lightonai/DenseOn</a> @1024</td>\n<td>Dense</td>\n<td>0.7239</td>\n<td>0.597</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/mDenseOn\">lightonai/mDenseOn</a> @1024</td>\n<td>Dense</td>\n<td>0.7227</td>\n<td>0.585</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/jinaai/jina-embeddings-v5-text-nano-retrieval\">jinaai/jina-embeddings-v5-text-nano-retrieval</a></td>\n<td>Dense</td>\n<td>0.7206</td>\n<td>0.587</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/microsoft/harrier-oss-v1-0.6b\">microsoft/harrier-oss-v1-0.6b</a></td>\n<td>Dense</td>\n<td>0.7126</td>\n<td>0.572</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Alibaba-NLP/gte-modernbert-base\">Alibaba-NLP/gte-modernbert-base</a></td>\n<td>Dense</td>\n<td>0.7102</td>\n<td>0.582</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Snowflake/snowflake-arctic-embed-l-v2.0\">Snowflake/snowflake-arctic-embed-l-v2.0</a></td>\n<td>Dense</td>\n<td>0.7068</td>\n<td>0.568</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/perplexity-ai/pplx-embed-v1-late-0.6b\">perplexity-ai/pplx-embed-v1-late-0.6b</a></td>\n<td>Multi-vector</td>\n<td>0.7008</td>\n<td>0.570</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/google/embeddinggemma-300m\">google/embeddinggemma-300m</a></td>\n<td>Dense</td>\n<td>0.7000</td>\n<td>0.563</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/DenseOn\">lightonai/DenseOn</a></td>\n<td>Dense</td>\n<td>0.6943</td>\n<td>0.570</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/naver/splade-v3\">naver/splade-v3</a></td>\n<td>Sparse</td>\n<td>0.6853</td>\n<td>0.574</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/ibm-granite/granite-embedding-small-english-r2\">ibm-granite/granite-embedding-small-english-r2</a></td>\n<td>Dense</td>\n<td>0.6813</td>\n<td>0.546</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/naver/splade-v3-distilbert\">naver/splade-v3-distilbert</a></td>\n<td>Sparse</td>\n<td>0.6806</td>\n<td>0.567</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/codefuse-ai/F2LLM-v2-0.6B\">codefuse-ai/F2LLM-v2-0.6B</a></td>\n<td>Dense</td>\n<td>0.6799</td>\n<td>0.536</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/colbert-ir/colbertv2.0\">colbert-ir/colbertv2.0</a> @512</td>\n<td>Multi-vector</td>\n<td>0.6785</td>\n<td>0.571</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/prithivida/Splade_PP_en_v1\">prithivida/Splade_PP_en_v1</a></td>\n<td>Sparse</td>\n<td>0.6755</td>\n<td>0.577</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/LateOn\">lightonai/LateOn</a></td>\n<td>Multi-vector</td>\n<td>0.6713</td>\n<td>0.561</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/tomaarsen/embeddinggemma-300m-miriad-unsloth\">tomaarsen/embeddinggemma-300m-miriad-unsloth</a></td>\n<td>Dense, finetuned</td>\n<td>0.6705</td>\n<td>0.530</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/LateOn-regularized\">lightonai/LateOn-regularized</a></td>\n<td>Multi-vector</td>\n<td>0.6673</td>\n<td>0.554</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/LateOn-unsupervised\">lightonai/LateOn-unsupervised</a></td>\n<td>Multi-vector</td>\n<td>0.6672</td>\n<td>0.553</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/lightonai/GTE-ModernColBERT-v1\">lightonai/GTE-ModernColBERT-v1</a></td>\n<td>Multi-vector</td>\n<td>0.6612</td>\n<td>0.555</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/opensearch-project/opensearch-neural-sparse-encoding-v2-distill\">opensearch-project/opensearch-neural-sparse-encoding-v2-distill</a></td>\n<td>Sparse</td>\n<td>0.6518</td>\n<td>0.531</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/nomic-ai/nomic-embed-text-v1.5\">nomic-ai/nomic-embed-text-v1.5</a> (prompted)</td>\n<td>Dense</td>\n<td>0.6387</td>\n<td>0.498</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/mixedbread-ai/mxbai-embed-large-v1\">mixedbread-ai/mxbai-embed-large-v1</a></td>\n<td>Dense</td>\n<td>0.6355</td>\n<td>0.502</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/BAAI/bge-large-en-v1.5\">BAAI/bge-large-en-v1.5</a></td>\n<td>Dense</td>\n<td>0.6308</td>\n<td>0.498</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/jinaai/jina-colbert-v2\">jinaai/jina-colbert-v2</a> @1024</td>\n<td>Multi-vector</td>\n<td>0.6218</td>\n<td>0.504</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/nomic-ai/nomic-embed-text-v1.5\">nomic-ai/nomic-embed-text-v1.5</a></td>\n<td>Dense</td>\n<td>0.6203</td>\n<td>0.487</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/answerdotai/answerai-colbert-small-v1\">answerdotai/answerai-colbert-small-v1</a></td>\n<td>Multi-vector</td>\n<td>0.6184</td>\n<td>0.514</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/tomaarsen/splade-modernbert-base-miriad\">tomaarsen/splade-modernbert-base-miriad</a></td>\n<td>Sparse, finetuned</td>\n<td>0.6142</td>\n<td>0.473</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/NeuML/biomedbert-base-colbert\">NeuML/biomedbert-base-colbert</a></td>\n<td>Multi-vector</td>\n<td>0.5963</td>\n<td>0.463</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/BAAI/bge-base-en-v1.5\">BAAI/bge-base-en-v1.5</a></td>\n<td>Dense</td>\n<td>0.5930</td>\n<td>0.454</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/BAAI/bge-small-en-v1.5\">BAAI/bge-small-en-v1.5</a></td>\n<td>Dense</td>\n<td>0.5881</td>\n<td>0.457</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/sentence-transformers/all-mpnet-base-v2\">sentence-transformers/all-mpnet-base-v2</a></td>\n<td>Dense</td>\n<td>0.5159</td>\n<td>0.396</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/jinaai/jina-colbert-v2\">jinaai/jina-colbert-v2</a></td>\n<td>Multi-vector</td>\n<td>0.4992</td>\n<td>0.401</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/mixedbread-ai/mxbai-colbert-large-v1\">mixedbread-ai/mxbai-colbert-large-v1</a></td>\n<td>Multi-vector</td>\n<td>0.4690</td>\n<td>0.358</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/sentence-transformers/static-retrieval-mrl-en-v1\">sentence-transformers/static-retrieval-mrl-en-v1</a></td>\n<td>Dense</td>\n<td>0.4614</td>\n<td>0.323</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2\">sentence-transformers/all-MiniLM-L6-v2</a></td>\n<td>Dense</td>\n<td>0.4458</td>\n<td>0.321</td>\n</tr>\n<tr>\n<td><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/colbert-ir/colbertv2.0\">colbert-ir/colbertv2.0</a></td>\n<td>Multi-vector</td>\n<td>0.4347</td>\n<td>0.346</td>\n</tr>\n</tbody>\n</table>\n<p>Models marked <code>@N</code> are evaluated with their document length cap lifted to N tokens, since their native caps (180 to 512 tokens) would otherwise truncate the 941-token average passages. For every multi-vector model this lift was worth +0.08 to +0.24 NDCG@10 over the as-served row, and even the dense DenseOn gained +0.03 from the same treatment.</p>\n<p>Note that this does not mean that <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/multi-vector-encoder/mLateOn-medical\">multi-vector-encoder/mLateOn-medical</a> is the strongest model on <em>all</em> domains. It's simply the strongest in <em>my</em> domain. This is totally fine, as I just need this model to work well on my data.</p>\n<p>Don't underestimate the power of finetuning multi-vector models on your domain. Fourteen and a half hours on a single consumer GPU produced a model that no general-purpose retriever comes close to on this data, and the recipe is a single script with no teacher model and no mined negatives!</p>\n<h3>Optimizing the index</h3>\n<p>The fair objection to multi-vector retrieval is index size, and this domain is close to the worst case for it. Storing one vector per token, my model needs about 878 vectors per passage, so the 200,000-passage corpus takes roughly 45 GB at fp16, where a dense model needs well under 1 GB. Document length is what makes that gap so wide. The Natural Questions passages in the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/multi-vector-encoder\">companion post</a> average about 125 token vectors each, seven times fewer, so a corpus of short passages starts from a far smaller index than this one does. The <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/modules.html#hierarchicaltokenpooling\"><code>HierarchicalTokenPooling</code></a> module compresses exactly this by clustering each document's token embeddings and storing the cluster means, keeping roughly <code>1 / pool_factor</code> of the vectors:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-from syntax-python\">from</span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-meta syntax-import-source syntax-python\"> <span class=\"syntax-meta syntax-import-path syntax-python\"><span class=\"syntax-meta syntax-import-name syntax-python\">sentence_transformers</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">multi_vector_encoder</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span><span class=\"syntax-meta syntax-import-name syntax-python\">modules</span></span> <span class=\"syntax-meta syntax-statement syntax-import syntax-python\"><span class=\"syntax-keyword syntax-control syntax-import syntax-python\">import</span></span></span></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"></span><span class=\"syntax-meta syntax-statement syntax-import syntax-python\"> <span class=\"syntax-meta syntax-generic-name syntax-python\">HierarchicalTokenPooling</span></span>\n\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">pooling</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">HierarchicalTokenPooling</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-variable syntax-parameter syntax-python\">pool_factor</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">4</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n<span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">document_embeddings</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span> <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">model</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">encode_document</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">passages</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">token_pooling</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">pooling</span></span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n</span></span></code></pre>\n<p>I measured it post-hoc on the finished model, with no pooling-aware training, and on long documents it is remarkably cheap.</p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_index_size_ndcg.png\"><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_index_size_ndcg.png\" alt=\"Embedding size for the 200,000-passage corpus versus NDCG@10, with the token pooling trajectory sweeping the multi-vector index into dense-model territory\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></a></p>\n<p>The solid points are uncompressed embeddings, so that every family is counted the same way and scored with exact search. You would not deploy any of them like that, though. Dense indexes routinely use int8 or binary quantization with rescoring, sparse indexes compress their postings, and multi-vector indexes use PLAID-style residual compression. Don't read those points as the disk you need to buy, but as relative storage cost.</p>\n<p>Token pooling is the solid line. Halving the vector count costs 0.0033 NDCG@10 and leaves rank-1 accuracy untouched, and keeping only a quarter of them, at 11.2 GB, still scores 0.8991. The curve keeps going (I measured out to a tenth of the vectors, still at 0.8765) but there is little reason to push pooling that far once quantization is on the table, which is what the dashed line below is about.</p>\n<p>The dashed line is what a real deployment might look like. I gave Omar Khattab early access to the model and the benchmark, and he measured these configurations with <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/lightonai/fast-plaid\">fast-plaid</a> at 1-bit residual quantization, using compact 17-bit centroid ids and 18-bit document ids instead of its ordinary unpacked 64-bit integers, plus document-side pruning:</p>\n<table>\n<thead>\n<tr>\n<th>configuration</th>\n<th>vectors kept</th>\n<th>index</th>\n<th>NDCG@10</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1-bit PLAID, all vectors</td>\n<td>100%</td>\n<td>3.37 GB</td>\n<td>0.8984</td>\n</tr>\n<tr>\n<td>1-bit PLAID + pruning</td>\n<td>65%</td>\n<td>2.23 GB</td>\n<td>0.8830</td>\n</tr>\n<tr>\n<td>1-bit PLAID + pruning</td>\n<td>42%</td>\n<td>1.45 GB</td>\n<td>0.8642</td>\n</tr>\n</tbody>\n</table>\n<p>That first row is 13x smaller than the raw embeddings, for 0.0155 NDCG@10. That is a far better trade than anywhere on the pooling curve. Quantization shrinks each vector while pooling and pruning cut how many you keep, so they compose, and quantization is the one to reach for first. Push further and the last row lands at 1.45 GB, <em>smaller</em> than the fp16 embeddings of <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Qwen/Qwen3-Embedding-8B\">Qwen3-Embedding-8B</a> (1.64 GB), while scoring 0.0895 higher. The objection that multi-vector indexes are too big does not survive a properly configured index.</p>\n<p>The pruning here is naive, meant only to establish that token reduction works on top of quantization, so read the bottom two rows as a floor rather than the frontier. If you would rather not hand-tune quantization at all, the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/multi-vector-encoder#indexing\">Indexing</a> section of the companion post covers fast-plaid, Qdrant, Weaviate, and Vespa.</p>\n<p>Multi-vector retrieval is only as expensive as its index. The raw embeddings for this corpus are 45 GB, and a properly configured index is at least 7x smaller at nearly the same accuracy. The index deserves as much of your attention as the checkpoint.</p>\n<h2>Acknowledgements</h2>\n<p>Thanks to <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/okhat\">Omar Khattab</a> for measuring the quantized and pruned index configurations in <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/train-multi-vector-encoder#optimizing-the-index\">Optimizing the index</a>, and for the discussions around late-interaction index costs.</p>\n<h2>Additional Resources</h2>\n<h3>Training Examples</h3>\n<p>These pages have training examples with explanations as well as links to training scripts. You can use them to get familiar with the multi-vector training loop:</p>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/examples/multi_vector_encoder/training/miriad/README.html\">MIRIAD</a>: domain-specific training on medical retrieval, an earlier and simpler cousin of this blogpost's recipe</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/examples/multi_vector_encoder/training/msmarco/README.html\">MS MARCO</a>: contrastive and knowledge distillation recipes</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/examples/multi_vector_encoder/training/multimodal/README.html\">Multimodal</a>: ColPali-style visual document retrieval training</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/examples/multi_vector_encoder/training/peft/README.html\">PEFT Adapters</a>: parameter-efficient finetuning with LoRA</li>\n</ul>\n<h3>Documentation</h3>\n<p>For further learning, you may also want to explore the following resources on Sentence Transformers:</p>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/installation.html\">Installation</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/quickstart.html\">Quickstart</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/usage/usage.html\">Usage</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/usage/custom_models.html\">Creating Custom Models</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/pretrained_models.html\">Pretrained Models</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/training_overview.html\">Training Overview</a> (This blogpost is a distillation of the Training Overview documentation)</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/multi_vector_encoder/loss_overview.html\">Loss Overview</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/package_reference/multi_vector_encoder/index.html\">API Reference</a></li>\n</ul>\n<p>And here is an advanced page that might interest you:</p>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://sbert.net/docs/sentence_transformer/training/distributed.html\">Distributed Training</a></li>\n</ul>\n<p>And the companion blogpost, covering everything about <em>using</em> these models:</p>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/multi-vector-encoder\">Multi-Vector (Late Interaction) Embedding Models with Sentence Transformers</a></li>\n</ul>\n",
  "content_markdown": "[Sentence Transformers](https://sbert.net/) is a Python library for using and training embedding and reranker models for a wide range of applications, such as retrieval augmented generation, semantic search, semantic textual similarity, and more. Its v6.0 update introduces a fourth model type: `MultiVectorEncoder`, for ColBERT-style late interaction retrieval, alongside a complete training approach for it. In this blogpost, I'll show you how to use it to finetune a multi-vector model that outperforms general-purpose retrievers on your data. This method can also train strong new multi-vector models from scratch. Everything below runs on `pip install -U \"sentence-transformers[train]\"`.\n\nFinetuning multi-vector models involves several components: the model itself, datasets, loss functions, training arguments, evaluators, and the trainer class. I'll have a look at each of these components, accompanied by practical examples of how they can be used for finetuning strong multi-vector models.\n\nLastly, in the [Evaluation](https://huggingface.co/blog/train-multi-vector-encoder#evaluation) section, I'll show you that my finetuned [multi-vector-encoder/mLateOn-medical](https://huggingface.co/multi-vector-encoder/mLateOn-medical) model, trained in 14.5 hours on a single RTX 3090 alongside this blogpost, easily outperforms every general-purpose retrieval model I could find on my medical retrieval evaluation: dense, sparse, lexical, and multi-vector alike.\n\n[![NDCG@10 on MIRIAD versus active parameters: the finetuned mLateOn-medical reaches the top at a fraction of the size of the strongest general-purpose models](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png)](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png)\n\nIf you're interested in finetuning dense embedding models, sparse embedding models, or rerankers instead, then consider reading through my prior [Training and Finetuning Embedding Models](https://huggingface.co/blog/train-sentence-transformers), [Training and Finetuning Sparse Embedding Models](https://huggingface.co/blog/train-sparse-encoder), and [Training and Finetuning Reranker Models](https://huggingface.co/blog/train-reranker) blogposts.\n\n> This blogpost is about *training* multi-vector models. If you want to learn how to *use* them, from loading and encoding to indexing in vector databases, see the companion [Multi-Vector (Late Interaction) Embedding Models with Sentence Transformers](https://huggingface.co/blog/multi-vector-encoder) blogpost.\n\n## Table of Contents\n\n- [What are Multi-Vector models?](https://huggingface.co/blog/train-multi-vector-encoder#what-are-multi-vector-models)\n- [Why Finetune?](https://huggingface.co/blog/train-multi-vector-encoder#why-finetune)\n- [Training Components](https://huggingface.co/blog/train-multi-vector-encoder#training-components)\n- [Model](https://huggingface.co/blog/train-multi-vector-encoder#model)\n  - [Finetuning an existing multi-vector model](https://huggingface.co/blog/train-multi-vector-encoder#finetuning-an-existing-multi-vector-model)\n  - [Building one from a base transformer](https://huggingface.co/blog/train-multi-vector-encoder#building-one-from-a-base-transformer)\n  - [Which starting point should you pick?](https://huggingface.co/blog/train-multi-vector-encoder#which-starting-point-should-you-pick)\n- [Dataset](https://huggingface.co/blog/train-multi-vector-encoder#dataset)\n  - [Data on the Hugging Face Hub](https://huggingface.co/blog/train-multi-vector-encoder#data-on-the-hugging-face-hub)\n  - [Local Data](https://huggingface.co/blog/train-multi-vector-encoder#local-data)\n  - [Dataset Format](https://huggingface.co/blog/train-multi-vector-encoder#dataset-format)\n- [Loss Function](https://huggingface.co/blog/train-multi-vector-encoder#loss-function)\n- [Training Arguments](https://huggingface.co/blog/train-multi-vector-encoder#training-arguments)\n- [Evaluator](https://huggingface.co/blog/train-multi-vector-encoder#evaluator)\n- [Trainer](https://huggingface.co/blog/train-multi-vector-encoder#trainer)\n  - [Callbacks](https://huggingface.co/blog/train-multi-vector-encoder#callbacks)\n  - [Multi-Dataset Training](https://huggingface.co/blog/train-multi-vector-encoder#multi-dataset-training)\n- [Evaluation](https://huggingface.co/blog/train-multi-vector-encoder#evaluation)\n  - [Optimizing the index](https://huggingface.co/blog/train-multi-vector-encoder#optimizing-the-index)\n- [Acknowledgements](https://huggingface.co/blog/train-multi-vector-encoder#acknowledgements)\n- [Additional Resources](https://huggingface.co/blog/train-multi-vector-encoder#additional-resources)\n  - [Training Examples](https://huggingface.co/blog/train-multi-vector-encoder#training-examples)\n  - [Documentation](https://huggingface.co/blog/train-multi-vector-encoder#documentation)\n\n## What are Multi-Vector models?\n\nA dense embedding model compresses a whole text into a single vector, and similarity is one dot product between two such summaries. A multi-vector model (also called a late-interaction or ColBERT-style model) skips that compression. It keeps **one small vector per token** and scores a query against a document with the MaxSim operator, where every query token finds its best-matching document token and the scores are summed. Token-level matching preserves exactly the fine-grained signals that a single vector has to average away, which usually means stronger retrieval, at the cost of a bigger index.\n\nThe companion [Multi-Vector Embedding Models](https://huggingface.co/blog/multi-vector-encoder) blogpost covers the architecture, encoding, scoring, and indexing in detail, so I'll keep this section short and get to the training.\n\n[![Dense embedding versus multi-vector late interaction](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/multi-vector-encoder/maxsim_explainer.gif)](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/multi-vector-encoder/maxsim_explainer.gif)\n\n## Why Finetune?\n\nFinetuning multi-vector models significantly improves their retrieval performance on your specific domain: the vocabulary, the query style, and the notion of relevance all differ between web search, legal discovery, code search, and scientific literature review. Because queries and documents are matched token by token, multi-vector models pick up fine-grained domain signals that single-vector models tend to average away, and they respond very well to even modest amounts of in-domain finetuning data.\n\nBeyond that, most released retrieval models were configured for short passages. The classic ColBERT checkpoints truncate documents at 180 or 300 tokens, and many popular dense models at 256 or 512, because their MS MARCO-style training data rarely goes beyond that. If your documents are long, these models silently discard most of every document before scoring it. On my medical evaluation with passages averaging 941 tokens, I measured that this truncation costs up to 0.24 NDCG@10, considerably more than any difference between model architectures. When you train your own model, you configure the document length that *your* data needs.\n\nLightOn ran into this same dynamic with code retrieval, where general [LateOn](https://huggingface.co/lightonai/LateOn) wasn't enough and they trained [LateOn-Code](https://huggingface.co/lightonai/LateOn-Code). Your domain, whether that's medical, legal, financial, or your company's internal documents, is not getting an official model. This blogpost shows you how to build it yourself, in a matter of hours, on a single consumer GPU.\n\n## Training Components\n\nTraining MultiVectorEncoder models involves the following components:\n\n1. [**Model**](https://huggingface.co/blog/train-multi-vector-encoder#model): The model to finetune or the architecture to build fresh.\n2. [**Dataset**](https://huggingface.co/blog/train-multi-vector-encoder#dataset): The data used for training and evaluation.\n3. [**Loss Function**](https://huggingface.co/blog/train-multi-vector-encoder#loss-function): A function that measures the model's performance and guides the optimization process.\n4. [**Training Arguments**](https://huggingface.co/blog/train-multi-vector-encoder#training-arguments) (optional): Parameters that impact training performance, tracking, and debugging.\n5. [**Evaluator**](https://huggingface.co/blog/train-multi-vector-encoder#evaluator) (optional): A class for evaluating the model before, during, or after training.\n6. [**Trainer**](https://huggingface.co/blog/train-multi-vector-encoder#trainer): Brings together all training components.\n\nLet's take a closer look at each component.\n\n## Model\n\nMulti-vector training gives you a real choice of starting point, and it matters more than you might expect.\n\n### Finetuning an existing multi-vector model\n\nIf you want to further finetune an existing multi-vector model, you don't have to worry about the architecture at all:\n\n```python\nfrom sentence_transformers import MultiVectorEncoder\n\n# Loading in fp32 is preferred for training if your memory can handle it\nmodel = MultiVectorEncoder(\n    \"lightonai/mLateOn-unsupervised\",\n    model_kwargs={\"torch_dtype\": \"float32\"},\n    processor_kwargs={\"model_max_length\": 8192},  # the tokenizer-level token limit\n)\n```\n\nThe checkpoint brings its own recipe along: its query and document marker tokens, its projection head, its scoring skiplist. For finetuning, you generally want to keep all of that and change only what your data demands. The first thing to check is the length configuration, since many released checkpoints cap documents at 180 to 512 tokens (see [Why Finetune?](https://huggingface.co/blog/train-multi-vector-encoder#why-finetune)), and my medical passages run to 1,400 tokens. The mLateOn family already serves the backbone's full 8192 token context, but if your starting checkpoint carries caps, lift them:\n\n```python\n# Let the model read full documents instead of the caps it was trained with,\n# e.g. GTE-ModernColBERT-v1 ships with query_length=48 and document_length=300\nmodel[0].query_length = None\nmodel[0].document_length = None\n```\n\nWith the per-task caps unset, truncation falls back to the tokenizer's `model_max_length`, which is why I configure that limit at load time above.\n\nI made one more change, adding a punctuation skiplist that excludes punctuation tokens from document-side scoring and storage. In a 4-way ablation (none, punctuation, stopwords, both) it modestly won on quality, and it shrinks the document index by 9.6% on this data for free:\n\n```python\nimport string\n\n# model[2] is the MultiVectorMask module\nmodel[2].skiplist_words = list(string.punctuation)\nmodel[2].resolve_with_tokenizer(model.tokenizer)  # token ids are cached, so re-resolve after changing\n```\n\n### Building one from a base transformer\n\nYou can also point `MultiVectorEncoder` at any base transformer, and a fresh, randomly initialized token-level projection is appended for you:\n\n```python\nfrom sentence_transformers import MultiVectorEncoder\n\nmodel = MultiVectorEncoder(\"answerdotai/ModernBERT-base\", model_kwargs={\"torch_dtype\": \"float32\"})\n# MultiVectorEncoder(\n#   (0): Transformer({..., 'architecture': 'ModernBertModel'})\n#   (1): Dense({'in_features': 768, 'out_features': 128, 'bias': False, ...})\n#   (2): MultiVectorMask({'skiplist_words': [], 'skiplist_tasks': ['document'], ...})\n#   (3): Normalize({...})\n# )\n```\n\nThat's the classic ColBERT pipeline: a `Transformer` producing contextualized token embeddings, a token-level `Dense` projecting each of them down to 128 dimensions, a `MultiVectorMask` deciding which tokens count during scoring, and a token-level `Normalize`. The projection starts random, so training is required before this model is useful. Interestingly, this works with strong dense embedding backbones too. A fresh projection on [Alibaba-NLP/gte-modernbert-base](https://huggingface.co/Alibaba-NLP/gte-modernbert-base) reached within 0.03 of the existing-checkpoint starting points in my experiments, from nothing but the projection and 25k training pairs.\n\nThe classic ColBERT tokenization tricks (`[MASK]` query expansion, `[Q]` / `[D]` prefix tokens, a document length cap, a punctuation skiplist) are all off by default and configurable. See [Creating Custom Models](https://sbert.net/docs/multi_vector_encoder/usage/custom_models.html) for the full set. For what it's worth, I tested `[MASK]` query expansion in four configurations for my domain finetune and none of them made a measurable difference, so don't feel obliged to reach for the classic recipe.\n\n### Which starting point should you pick?\n\nI measured this directly while preparing this blogpost, taking six starting points and training each with the identical recipe on 25k medical question-passage pairs from [MIRIAD](https://huggingface.co/datasets/tomaarsen/miriad-4.4M-split), then evaluating on 1,000 held-out questions against a 50,000 passage corpus:\n\n| Starting point                                                                               | Zero-shot NDCG@10 | After 25k pairs | Delta       |\n| -------------------------------------------------------------------------------------------- | ----------------- | --------------- | ----------- |\n| [lightonai/mLateOn-unsupervised](https://huggingface.co/lightonai/mLateOn-unsupervised)     | 0.9087            | **0.9398**      | **+0.0311** |\n| [lightonai/mLateOn](https://huggingface.co/lightonai/mLateOn)                               | 0.9277            | 0.9319          | +0.0042     |\n| [lightonai/LateOn-unsupervised](https://huggingface.co/lightonai/LateOn-unsupervised)       | 0.9026            | **0.9206**      | **+0.0180** |\n| [lightonai/LateOn](https://huggingface.co/lightonai/LateOn)                                 | 0.9185            | 0.9105          | -0.0080     |\n| [lightonai/GTE-ModernColBERT-v1](https://huggingface.co/lightonai/GTE-ModernColBERT-v1)     | 0.9198            | 0.9007          | -0.0191     |\n| Fresh head on [gte-modernbert-base](https://huggingface.co/Alibaba-NLP/gte-modernbert-base) | -                 | 0.9177          | -           |\n\nThe result surprised me, and it replicated across two model families. \\*The `-unsupervised` checkpoints adapt to a new domain far better than their finished siblings, overtaking them despite starting lower. These checkpoints sit after large-scale contrastive pretraining but before supervised finetuning on general retrieval, so they carry all the late-interaction structure with none of the general-purpose tuning that domain training then has to undo. The finished checkpoints, by contrast, barely moved or even regressed, at every learning rate I tried.\n\nSo, if the model family you like publishes a pre-supervised checkpoint, start there. If not, a fresh projection on a strong retrieval-pretrained backbone is a close runner-up. Continuing from a fully finished checkpoint is the weakest option for domain adaptation, despite being the most natural-feeling one.\n\n## Dataset\n\nThe [`MultiVectorEncoderTrainer`](https://sbert.net/docs/package_reference/multi_vector_encoder/trainer.html) uses [`datasets.Dataset`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset) or [`datasets.DatasetDict`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.DatasetDict) instances for training and evaluation. You can load data from the [Hugging Face Datasets Hub](https://huggingface.co/datasets) or use local data in whatever format you prefer (e.g. CSV, JSON, Parquet, Arrow, or SQL).\n\n**Note:** Lots of public datasets that work out of the box with Sentence Transformers have been tagged with `sentence-transformers` on the Hugging Face Hub, so you can easily find them on [https://huggingface.co/datasets?other=sentence-transformers](https://huggingface.co/datasets?other=sentence-transformers). Consider browsing through these to find ready-to-go datasets that might be useful for your tasks, domains, or languages.\n\n### Data on the Hugging Face Hub\n\nYou can use the [`load_dataset`](https://huggingface.co/docs/datasets/main/en/package_reference/loading_methods#datasets.load_dataset) function to load data from datasets on the Hub:\n\n```python\nfrom datasets import load_dataset\n\ntrain_dataset = load_dataset(\"tomaarsen/miriad-4.4M-split\", split=\"train\")\n\nprint(train_dataset)\n\"\"\"\nDataset({\n    features: ['question', 'passage_text'],\n    num_rows: 4467542\n})\n\"\"\"\n```\n\nThis is the dataset I'll train on in this blogpost: 4.4 million medical questions from [MIRIAD](https://huggingface.co/datasets/miriad/miriad-4.4M), each paired with the source passage that contains its answer (averaging 941 tokens). Simple (query, relevant passage) pairs like these are the easiest retrieval training data to collect for your own domain, and as you'll see, they're all you need.\n\n### Local Data\n\nYou can also use [`load_dataset`](https://huggingface.co/docs/datasets/main/en/package_reference/loading_methods#datasets.load_dataset) for loading local data in common file formats:\n\n```python\nfrom datasets import load_dataset\n\ndataset = load_dataset(\"csv\", data_files=\"my_file.csv\")\n# or\ndataset = load_dataset(\"json\", data_files=\"my_file.json\")\n```\n\nAnd if your local data requires pre-processing, you can use [`datasets.Dataset.from_dict`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset.from_dict) to initialize your dataset with a dictionary of lists:\n\n```python\nfrom datasets import Dataset\n\nqueries = []\ndocuments = []\n# Open a file, perform preprocessing, filtering, cleaning, etc.\n# and append to the lists\n\ndataset = Dataset.from_dict({\n    \"query\": queries,\n    \"document\": documents,\n})\n```\n\n### Dataset Format\n\nIt is important that your dataset format matches your loss function (or that you choose a loss function that matches your dataset format). Verifying whether a dataset format works with a loss function involves two steps:\n\n1. If your loss function requires a *Label* according to the [Loss Overview](https://sbert.net/docs/multi_vector_encoder/loss_overview.html) table, then your dataset must have a **column named \"label\" or \"score\"**. This column is automatically taken as the label.\n2. All columns not named \"label\" or \"score\" are considered *Inputs* according to the [Loss Overview](https://sbert.net/docs/multi_vector_encoder/loss_overview.html) table. The number of remaining columns must match the number of valid inputs for your chosen loss. The names of these columns are **irrelevant**, only the **order matters**.\n\nThere are two multi-vector specific conventions on top of this:\n\n- Positional query and document assignment: the first column is embedded as the *query* and all following columns as *documents*, regardless of the column names. This default can be overridden per column via the standard `router_mapping` training argument.\n- Knowledge distillation format: one column per candidate document, i.e. `(query, document_1, ..., document_N, scores)` where `scores` is a list of N teacher scores per row. For KD datasets that store query and document *IDs* alongside separate text datasets (e.g. [lightonai/ms-marco-en-bge](https://huggingface.co/datasets/lightonai/ms-marco-en-bge)), you can use [`resolve_ids`](https://sbert.net/docs/package_reference/util.html#sentence_transformers.util.dataset.resolve_ids) to resolve the IDs to texts on the fly.\n\n## Loss Function\n\nLoss functions quantify how well a model performs for a given batch of data, allowing an optimizer to update the model weights to produce more favourable (i.e., lower) loss values. The right loss function for your task depends on the data you have and what you're trying to achieve. You can find a full list of options in the [Loss Overview](https://sbert.net/docs/multi_vector_encoder/loss_overview.html).\n\nFor the common case of question-answer or question-passage pairs, the workhorse is in-batch negatives training with [`MultiVectorMultipleNegativesRankingLoss`](https://sbert.net/docs/package_reference/multi_vector_encoder/losses.html#multivectormultiplenegativesrankingloss), where every other document in the batch acts as a negative for each query. Bigger batches mean more negatives and stronger training, so in practice you'll want its GradCache variant, [`CachedMultiVectorMultipleNegativesRankingLoss`](https://sbert.net/docs/package_reference/multi_vector_encoder/losses.html#cachedmultivectormultiplenegativesrankingloss), which decouples the effective batch size from what fits on your GPU:\n\n```python\nfrom sentence_transformers import MultiVectorEncoder\nfrom sentence_transformers.multi_vector_encoder.losses import CachedMultiVectorMultipleNegativesRankingLoss\n\nmodel = MultiVectorEncoder(\"lightonai/mLateOn-unsupervised\", model_kwargs={\"torch_dtype\": \"float32\"})\n\nloss = CachedMultiVectorMultipleNegativesRankingLoss(\n    model=model,\n    mini_batch_size=16,  # how many documents to encode per chunk: bounds memory, not quality\n)\n```\n\nThe `mini_batch_size` parameter bounds the memory by encoding documents in chunks of this size, while the effective contrastive batch size (128 in my run below, and in my ablations bigger batches bought nothing further) stays a free choice. GradCache guarantees identical results regardless of the chunk size, so lower it for smaller GPUs at only a wall-clock cost. When your document lengths vary a lot, consider its sibling `mini_batch_num_tokens`, which packs each chunk to a total token budget instead of a document count, so a chunk of unusually long documents can never spike your memory (my `mini_batch_size=16` at roughly 940 tokens per document corresponds to `mini_batch_num_tokens=15_000`).\n\nOne multi-vector specific trap is that the contrastive losses default to `scale=1.0`, unlike the dense embedding equivalent which defaults to `scale=20.0`. That 20.0 exists because a cosine similarity is a single value in \\[-1, 1\\], too narrow a range for a sharp softmax. A MaxSim score instead sums one best-match similarity per query token, so it already spans roughly \\[0, query\\_length\\]: a 32-token query can score up to 32. So don't copy `scale=20.0` over from a dense training script, since it would saturate the softmax and kill your gradients.\n\nFor distillation from a stronger teacher, which is how the strongest general-purpose late-interaction models are trained, see [`MultiVectorDistillKLDivLoss`](https://sbert.net/docs/package_reference/multi_vector_encoder/losses.html#multivectordistillkldivloss) and the Knowledge Distillation tab in the [Training Overview](https://sbert.net/docs/multi_vector_encoder/training_overview.html#trainer) documentation.\n\n## Training Arguments\n\nYou can customize the training process using the [`MultiVectorEncoderTrainingArguments`](https://sbert.net/docs/package_reference/multi_vector_encoder/training_args.html) class. This class lets you adjust parameters that can impact training speed and help you understand what's happening during training.\n\nFor more information on the most useful training arguments, check out the [Multi-Vector Encoder > Training Overview > Training Arguments](https://sbert.net/docs/multi_vector_encoder/training_overview.html#training-arguments). It's worth reading to get the most out of your training.\n\nHere's an example, using the values from my actual training run:\n\n```python\nfrom sentence_transformers import MultiVectorEncoderTrainingArguments\nfrom sentence_transformers.base.sampler import BatchSamplers\n\nargs = MultiVectorEncoderTrainingArguments(\n    # Required parameter:\n    output_dir=\"models/mLateOn-medical\",\n    # Optional training parameters:\n    num_train_epochs=1,\n    per_device_train_batch_size=128,  # the effective contrastive batch, thanks to GradCache\n    per_device_eval_batch_size=16,\n    learning_rate=1e-4,\n    warmup_steps=0.05,\n    prompts={\"question\": \"[Q] \", \"passage_text\": \"[D] \"},  # the checkpoint's markers, keyed by training column\n    fp16=False,  # Set to True if you have a GPU that supports FP16\n    bf16=True,  # Set to True if you have a GPU that supports BF16\n    batch_sampler=BatchSamplers.NO_DUPLICATES,  # in-batch negatives benefit from no duplicates\n    # Optional tracking/debugging parameters:\n    eval_strategy=\"steps\",\n    eval_steps=0.1,\n    save_strategy=\"steps\",\n    save_steps=0.05,\n    logging_steps=0.01,\n    run_name=\"mLateOn-medical\",  # Will be used in e.g. Trackio, W&B, etc.\n)\n```\n\nA few of these deserve a comment:\n\n- `prompts`: training does not automatically apply the prompts stored in the model, so map them onto your training columns explicitly. Here that is the checkpoint's `[Q]` marker for the question column and `[D]` for the passage column, keeping training consistent with inference.\n- `max_length` (deliberately not set): this argument caps tokenization during *training only*, for when you want cheaper training than the model's full serving length. I measured what that shortcut costs on this data. Training at 512 tokens lost about 0.015 NDCG@10 for about 2x the speed, and the deficit did not shrink with more data, because the model simply never sees what got cut off. Leave it unset so training matches inference, unless you need the speedup more than the quality.\n- `learning_rate=1e-4`: after a sweep from 5e-6 to 2e-4, I had the best luck with this higher-than-usual learning rate.\n\n## Evaluator\n\nTo track your model's performance during training, you can pass an `eval_dataset` to the trainer for evaluation loss, but concrete retrieval metrics are much more informative. Sentence Transformers includes the following built-in evaluators for multi-vector models:\n\n| Evaluator                                                                                                                                                             | Required Data                                                                 |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |\n| [`MultiVectorInformationRetrievalEvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectorinformationretrievalevaluator) | Queries, corpus, and relevant document mappings                               |\n| [`MultiVectorNanoBEIREvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectornanobeirevaluator)                         | No data required                                                              |\n| [`MultiVectorTripletEvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectortripletevaluator)                           | (anchor, positive, negative) triplets                                         |\n| [`MultiVectorRerankingEvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectorrerankingevaluator)                       | List of `{'query': '...', 'positive': [...], 'negative': [...]}` dictionaries |\n| [`MultiVectorDistillationEvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectordistillationevaluator)                 | Queries with candidate documents and teacher scores                           |\n\nFor domain finetuning, the [`MultiVectorInformationRetrievalEvaluator`](https://sbert.net/docs/package_reference/multi_vector_encoder/evaluation.html#multivectorinformationretrievalevaluator) built from your own held-out data is the one that matters. One tip on constructing it is that the corpus should be hard enough that models can be told apart. In my case the MIRIAD questions are generated from their own source passages, which makes retrieval unusually easy. Against just the 10k gold passages, nearly every model scored above 0.97 NDCG@10. If your evaluation saturates like that, add *distractor* passages (I use deduplicated passages from the training split) until the scores spread out:\n\n```python\nfrom datasets import load_dataset\nfrom sentence_transformers.multi_vector_encoder.evaluation import MultiVectorInformationRetrievalEvaluator\n\ndataset = load_dataset(\"tomaarsen/miriad-4.4M-split\")\n\n# Gold: 1,000 evaluation questions, each mapping to its own passage, with the\n# eval split's full ~10k unique passages as the initial corpus\ncorpus = {}\nqueries = {}\nrelevant_docs = {}\npassage_to_id = {}\nfor idx, row in enumerate(dataset[\"eval\"]):\n    if row[\"passage_text\"] not in passage_to_id:\n        passage_to_id[row[\"passage_text\"]] = f\"p{len(passage_to_id)}\"\n        corpus[passage_to_id[row[\"passage_text\"]]] = row[\"passage_text\"]\n    if idx < 1_000:\n        queries[f\"q{idx}\"] = row[\"question\"]\n        relevant_docs[f\"q{idx}\"] = {passage_to_id[row[\"passage_text\"]]}\n\n# Distractors: unique train passages that make the haystack realistic\nseen = set(passage_to_id)\nfor row in dataset[\"train\"]:\n    if len(corpus) >= 200_000:\n        break\n    if row[\"passage_text\"] not in seen:\n        seen.add(row[\"passage_text\"])\n        corpus[f\"d{len(corpus)}\"] = row[\"passage_text\"]\n\nevaluator = MultiVectorInformationRetrievalEvaluator(\n    queries=queries,\n    corpus=corpus,\n    relevant_docs=relevant_docs,\n    name=\"miriad-dev\",\n    batch_size=16,\n)\n# results = evaluator(model)\n```\n\n## Trainer\n\nThe [`MultiVectorEncoderTrainer`](https://sbert.net/docs/package_reference/multi_vector_encoder/trainer.html) is where all previous components come together. Here is the complete script that trained [multi-vector-encoder/mLateOn-medical](https://huggingface.co/multi-vector-encoder/mLateOn-medical), the model from the introduction:\n\n```python\nimport logging\nimport string\nimport traceback\n\nfrom datasets import load_dataset\n\nfrom sentence_transformers import (\n    MultiVectorEncoder,\n    MultiVectorEncoderModelCardData,\n    MultiVectorEncoderTrainer,\n    MultiVectorEncoderTrainingArguments,\n)\nfrom sentence_transformers.base.sampler import BatchSamplers\nfrom sentence_transformers.multi_vector_encoder.evaluation import MultiVectorInformationRetrievalEvaluator\nfrom sentence_transformers.multi_vector_encoder.losses import CachedMultiVectorMultipleNegativesRankingLoss\n\nlogging.basicConfig(format=\"%(asctime)s - %(message)s\", datefmt=\"%Y-%m-%d %H:%M:%S\", level=logging.INFO)\n\n\ndef main():\n    # 1. Load the starting checkpoint: contrastively pretrained, not yet supervised\n    # Loading in fp32 is preferred for training if your memory can handle it\n    model = MultiVectorEncoder(\n        \"lightonai/mLateOn-unsupervised\",\n        model_kwargs={\"torch_dtype\": \"float32\"},\n        processor_kwargs={\"model_max_length\": 8192},\n        model_card_data=MultiVectorEncoderModelCardData(\n            language=\"en\",\n            license=\"apache-2.0\",\n            model_name=\"mLateOn finetuned on MIRIAD medical retrieval\",\n        ),\n    )\n\n    # 2. Lift the per-task length caps so training and inference see full medical passages\n    model[0].query_length = None\n    model[0].document_length = None\n\n    # 3. Skip punctuation tokens during scoring: a small quality win and a 9.6% smaller index\n    model[2].skiplist_words = list(string.punctuation)\n    model[2].resolve_with_tokenizer(model.tokenizer)\n\n    # 4. Load 1 million medical question-passage pairs\n    train_dataset = load_dataset(\"tomaarsen/miriad-4.4M-split\", split=\"train\").select(range(1_000_000))\n\n    # 5. In-batch negatives with GradCache: large effective batch, memory-bounded chunks\n    loss = CachedMultiVectorMultipleNegativesRankingLoss(model=model, mini_batch_size=16)\n\n    # 6. A light dev evaluator to watch progress during training: 500 held-out questions\n    # against the eval split's ~10k unique passages. The full 200k protocol runs afterwards.\n    eval_split = load_dataset(\"tomaarsen/miriad-4.4M-split\", split=\"eval\")\n    corpus, queries, relevant_docs, passage_to_id = {}, {}, {}, {}\n    for idx, row in enumerate(eval_split):\n        if row[\"passage_text\"] not in passage_to_id:\n            passage_to_id[row[\"passage_text\"]] = f\"p{len(passage_to_id)}\"\n            corpus[passage_to_id[row[\"passage_text\"]]] = row[\"passage_text\"]\n        if idx < 500:\n            queries[f\"q{idx}\"] = row[\"question\"]\n            relevant_docs[f\"q{idx}\"] = {passage_to_id[row[\"passage_text\"]]}\n    dev_evaluator = MultiVectorInformationRetrievalEvaluator(\n        queries=queries, corpus=corpus, relevant_docs=relevant_docs, name=\"miriad-dev\", batch_size=16\n    )\n\n    # 7. Training arguments, as discussed above\n    run_name = \"mLateOn-medical\"\n    args = MultiVectorEncoderTrainingArguments(\n        output_dir=f\"models/{run_name}\",\n        num_train_epochs=1,\n        per_device_train_batch_size=128,\n        per_device_eval_batch_size=16,\n        learning_rate=1e-4,\n        warmup_steps=0.05,\n        prompts={\"question\": \"[Q] \", \"passage_text\": \"[D] \"},\n        fp16=False,  # Set to True if you have a GPU that supports FP16\n        bf16=True,  # Set to True if you have a GPU that supports BF16\n        batch_sampler=BatchSamplers.NO_DUPLICATES,\n        eval_strategy=\"steps\",\n        eval_steps=0.1,\n        save_strategy=\"steps\",\n        save_steps=0.05,\n        logging_steps=0.01,\n        run_name=run_name,\n    )\n\n    # 8. Create a trainer & train\n    trainer = MultiVectorEncoderTrainer(\n        model=model,\n        args=args,\n        train_dataset=train_dataset,\n        loss=loss,\n        evaluator=dev_evaluator,\n    )\n    trainer.train()\n\n    # 9. Save the trained model\n    model.save_pretrained(f\"models/{run_name}/final\")\n\n    # 10. (Optional) Push it to the Hugging Face Hub\n    try:\n        model.push_to_hub(run_name)\n    except Exception:\n        logging.error(f\"Error uploading model to the Hugging Face Hub:\\n{traceback.format_exc()}\")\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\nThat's the whole recipe: a pre-supervised checkpoint, a million domain pairs, in-batch negatives, full document length, and a higher-than-usual learning rate. The run took 14.5 hours on my single RTX 3090 at a peak of 17.5 GB VRAM, and every one of those choices was the winner of a measured comparison rather than a guess.\n\nFor readers on smaller budgets, my scaling experiments put 100k pairs (75 minutes of training) within 0.012 NDCG@10 of the full million-pair run. Most of the gain comes in the first hour.\n\n### Callbacks\n\nThe MultiVectorEncoder trainer supports various [`transformers.TrainerCallback`](https://huggingface.co/docs/transformers/main_classes/callback#transformers.TrainerCallback) subclasses, including:\n\n- [`WandbCallback`](https://huggingface.co/docs/transformers/en/main_classes/callback#transformers.integrations.WandbCallback) for logging training metrics to W&B if `wandb` is installed\n- [`TensorBoardCallback`](https://huggingface.co/docs/transformers/en/main_classes/callback#transformers.integrations.TensorBoardCallback) for logging training metrics to TensorBoard if `tensorboard` is accessible\n- [`CodeCarbonCallback`](https://huggingface.co/docs/transformers/en/main_classes/callback#transformers.integrations.CodeCarbonCallback) for tracking carbon emissions during training if `codecarbon` is installed\n\nEnable these via the `report_to` training argument, e.g. `report_to=[\"wandb\", \"codecarbon\"]`, with the required dependencies installed. It defaults to `\"none\"`, and `report_to=\"all\"` activates every integration whose dependency is installed.\n\nRefer to the [Transformers Callbacks documentation](https://huggingface.co/docs/transformers/en/main_classes/callback) for more information on these callbacks and how to create your own.\n\n### Multi-Dataset Training\n\nTypically, top-performing general-purpose models are trained on multiple datasets simultaneously. However, this approach can be challenging due to the varying formats of each dataset. Fortunately, the [`MultiVectorEncoderTrainer`](https://sbert.net/docs/package_reference/multi_vector_encoder/trainer.html) allows you to train on multiple datasets without requiring a uniform format. Additionally, it provides the flexibility to apply different loss functions to each dataset. Here are the steps to train with multiple datasets at once:\n\n- Use a dictionary of [`datasets.Dataset`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset) instances (or a [`datasets.DatasetDict`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.DatasetDict)) as the `train_dataset` (and optionally also `eval_dataset`).\n- (Optional) Use a dictionary of loss functions mapping dataset names to losses. Only required if you wish to use different loss functions for different datasets.\n\nEach training/evaluation batch will only contain samples from one of the datasets. The order in which batches are sampled from the multiple datasets is defined by the [`MultiDatasetBatchSamplers`](https://sbert.net/docs/package_reference/sentence_transformer/sampler.html#sentence_transformers.training_args.MultiDatasetBatchSamplers) enum, which can be passed to the [`MultiVectorEncoderTrainingArguments`](https://sbert.net/docs/package_reference/multi_vector_encoder/training_args.html) via `multi_dataset_batch_sampler`. Valid options are:\n\n- `MultiDatasetBatchSamplers.ROUND_ROBIN`: Round-robin sampling from each dataset until one is exhausted. With this strategy, it's likely that not all samples from each dataset are used, but each dataset is sampled from equally.\n- `MultiDatasetBatchSamplers.PROPORTIONAL` (default): Sample from each dataset in proportion to its size. With this strategy, all samples from each dataset are used and larger datasets are sampled from more frequently.\n\n## Evaluation\n\nTo find out where the finetuned model stands, I evaluated it against over 50 retrieval model configurations across four architecture families on the MIRIAD evaluation set, built exactly as in the [Evaluator](https://huggingface.co/blog/train-multi-vector-encoder#evaluator) section above, with 1,000 held-out medical questions searching 200,000 unique passages (the 10k gold passages hidden among 190k deduplicated distractors from the training split). This corpus is four times the size of the 50,000-passage one from [Which starting point should you pick?](https://huggingface.co/blog/train-multi-vector-encoder#which-starting-point-should-you-pick), so scores are not comparable between the two tables.\n\n[![NDCG@10 versus active parameters on the MIRIAD 200k benchmark, with an arrow marking the finetuning jump from mLateOn-unsupervised to mLateOn-medical](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png)](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_model_size_ndcg.png)\n\nThe headline results, with the full table in the collapsible below:\n\n| Model                                                                                                           | Family                      | NDCG@10    |\n| --------------------------------------------------------------------------------------------------------------- | --------------------------- | ---------- |\n| [**multi-vector-encoder/mLateOn-medical (mine)**](https://huggingface.co/multi-vector-encoder/mLateOn-medical) | **Multi-vector, finetuned** | **0.9139** |\n| [lightonai/mLateOn](https://huggingface.co/lightonai/mLateOn)                                                  | Multi-vector, zero-shot     | 0.8520     |\n| [lightonai/GTE-ModernColBERT-v1](https://huggingface.co/lightonai/GTE-ModernColBERT-v1) (cap lifted)           | Multi-vector, zero-shot     | 0.8502     |\n| [Qwen/Qwen3-Embedding-4B](https://huggingface.co/Qwen/Qwen3-Embedding-4B)                                      | Dense, zero-shot            | 0.7817     |\n| [voyageai/voyage-4-nano](https://huggingface.co/voyageai/voyage-4-nano)                                        | Dense, zero-shot            | 0.7563     |\n| BM25                                                                                                            | Lexical                     | 0.7501     |\n| [naver/splade-v3](https://huggingface.co/naver/splade-v3)                                                      | Sparse, zero-shot           | 0.6853     |\n\nThe finetuned model tops the table, beating the strongest zero-shot model of any architecture by +0.062 NDCG@10. In other words, the strongest zero-shot model returns the right passage as the very first hit for 75.8% of the queries, while the finetuned model does so for 84.9%, cutting the rank-1 error by more than a third.\n\nThe architecture pattern is just as clear, with the top of the table exclusively late interaction. On long documents, one vector per token beats one vector per document, even at matched training and matched backbones. DenseOn and LateOn share training data and architecture except for the head, and the late-interaction sibling wins by +0.12, with the multilingual pair (mDenseOn and mLateOn) replicating this at +0.13. Scale doesn't rescue single vectors either. [Qwen3-Embedding-4B](https://huggingface.co/Qwen/Qwen3-Embedding-4B), the strongest dense model with roughly 33x the active (non-embedding) parameters of mine, still stops 0.13 short, and the 8B version scores lower than the 4B.\n\nBM25 also performs surprisingly well, beating every sparse model, every truncation-capped multi-vector model, and all but three dense models: the multi-billion [Qwen3-Embedding-4B](https://huggingface.co/Qwen/Qwen3-Embedding-4B) and [8B](https://huggingface.co/Qwen/Qwen3-Embedding-8B), and [voyage-4-nano](https://huggingface.co/voyageai/voyage-4-nano), which reads its full 32k token context to edge past by just 0.006. Don't expect that to transfer to your own data though. MIRIAD's questions are generated from the passages, so the lexical overlap between a query and its gold passage is far larger than in typical retrieval, and BM25's unlimited context length lets it use every one of those overlapping words while most neural checkpoints truncate. A BM25 baseline is cheap and always worth running, just don't count on this margin.\n\nThe full field at a glance, sorted by score and colored by architecture family.\n\n[![Sorted NDCG@10 on the MIRIAD 200k benchmark for every evaluated model, colored by architecture family](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_ndcg_by_model.png)](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_ndcg_by_model.png)\n\nClick to see the full evaluation table\n\n| Model                                                                                                                                                      | Family                  | NDCG@10    | acc@1 |\n| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ---------- | ----- |\n| [**multi-vector-encoder/mLateOn-medical (mine)**](https://huggingface.co/multi-vector-encoder/mLateOn-medical)                                            | Multi-vector, finetuned | **0.9139** | 0.849 |\n| [lightonai/mLateOn](https://huggingface.co/lightonai/mLateOn)                                                                                             | Multi-vector            | 0.8520     | 0.758 |\n| [lightonai/GTE-ModernColBERT-v1](https://huggingface.co/lightonai/GTE-ModernColBERT-v1) @1024                                                             | Multi-vector            | 0.8502     | 0.763 |\n| [lightonai/LateOn](https://huggingface.co/lightonai/LateOn) @1024                                                                                         | Multi-vector            | 0.8485     | 0.760 |\n| [lightonai/mLateOn-unsupervised](https://huggingface.co/lightonai/mLateOn-unsupervised)                                                                   | Multi-vector            | 0.8304     | 0.733 |\n| [mixedbread-ai/mxbai-edge-colbert-v0-32m](https://huggingface.co/mixedbread-ai/mxbai-edge-colbert-v0-32m) @1024                                           | Multi-vector            | 0.8186     | 0.727 |\n| [Qwen/Qwen3-Embedding-4B](https://huggingface.co/Qwen/Qwen3-Embedding-4B)                                                                                 | Dense                   | 0.7817     | 0.669 |\n| [Qwen/Qwen3-Embedding-8B](https://huggingface.co/Qwen/Qwen3-Embedding-8B)                                                                                 | Dense                   | 0.7747     | 0.654 |\n| [perplexity-ai/pplx-embed-v1-late-0.6b](https://huggingface.co/perplexity-ai/pplx-embed-v1-late-0.6b) @1024                                               | Multi-vector            | 0.7702     | 0.632 |\n| [lightonai/ColBERT-Zero](https://huggingface.co/lightonai/ColBERT-Zero)                                                                                   | Multi-vector            | 0.7613     | 0.675 |\n| [LiquidAI/LFM2.5-ColBERT-350M](https://huggingface.co/LiquidAI/LFM2.5-ColBERT-350M)                                                                       | Multi-vector            | 0.7582     | 0.664 |\n| [voyageai/voyage-4-nano](https://huggingface.co/voyageai/voyage-4-nano)                                                                                   | Dense                   | 0.7563     | 0.638 |\n| BM25                                                                                                                                                       | Lexical                 | 0.7501     | 0.641 |\n| [jinaai/jina-embeddings-v5-text-small-retrieval](https://huggingface.co/jinaai/jina-embeddings-v5-text-small-retrieval)                                   | Dense                   | 0.7470     | 0.620 |\n| [Qwen/Qwen3-Embedding-0.6B](https://huggingface.co/Qwen/Qwen3-Embedding-0.6B)                                                                             | Dense                   | 0.7408     | 0.620 |\n| [perplexity-ai/pplx-embed-v1-0.6b](https://huggingface.co/perplexity-ai/pplx-embed-v1-0.6b)                                                               | Dense                   | 0.7384     | 0.615 |\n| [mixedbread-ai/mxbai-edge-colbert-v0-32m](https://huggingface.co/mixedbread-ai/mxbai-edge-colbert-v0-32m)                                                 | Multi-vector            | 0.7350     | 0.639 |\n| [mixedbread-ai/mxbai-edge-colbert-v0-17m](https://huggingface.co/mixedbread-ai/mxbai-edge-colbert-v0-17m)                                                 | Multi-vector            | 0.7271     | 0.631 |\n| [answerdotai/answerai-colbert-small-v1](https://huggingface.co/answerdotai/answerai-colbert-small-v1) @512                                                | Multi-vector            | 0.7264     | 0.615 |\n| [lightonai/DenseOn](https://huggingface.co/lightonai/DenseOn) @1024                                                                                       | Dense                   | 0.7239     | 0.597 |\n| [lightonai/mDenseOn](https://huggingface.co/lightonai/mDenseOn) @1024                                                                                     | Dense                   | 0.7227     | 0.585 |\n| [jinaai/jina-embeddings-v5-text-nano-retrieval](https://huggingface.co/jinaai/jina-embeddings-v5-text-nano-retrieval)                                     | Dense                   | 0.7206     | 0.587 |\n| [microsoft/harrier-oss-v1-0.6b](https://huggingface.co/microsoft/harrier-oss-v1-0.6b)                                                                     | Dense                   | 0.7126     | 0.572 |\n| [Alibaba-NLP/gte-modernbert-base](https://huggingface.co/Alibaba-NLP/gte-modernbert-base)                                                                 | Dense                   | 0.7102     | 0.582 |\n| [Snowflake/snowflake-arctic-embed-l-v2.0](https://huggingface.co/Snowflake/snowflake-arctic-embed-l-v2.0)                                                 | Dense                   | 0.7068     | 0.568 |\n| [perplexity-ai/pplx-embed-v1-late-0.6b](https://huggingface.co/perplexity-ai/pplx-embed-v1-late-0.6b)                                                     | Multi-vector            | 0.7008     | 0.570 |\n| [google/embeddinggemma-300m](https://huggingface.co/google/embeddinggemma-300m)                                                                           | Dense                   | 0.7000     | 0.563 |\n| [lightonai/DenseOn](https://huggingface.co/lightonai/DenseOn)                                                                                             | Dense                   | 0.6943     | 0.570 |\n| [naver/splade-v3](https://huggingface.co/naver/splade-v3)                                                                                                 | Sparse                  | 0.6853     | 0.574 |\n| [ibm-granite/granite-embedding-small-english-r2](https://huggingface.co/ibm-granite/granite-embedding-small-english-r2)                                   | Dense                   | 0.6813     | 0.546 |\n| [naver/splade-v3-distilbert](https://huggingface.co/naver/splade-v3-distilbert)                                                                           | Sparse                  | 0.6806     | 0.567 |\n| [codefuse-ai/F2LLM-v2-0.6B](https://huggingface.co/codefuse-ai/F2LLM-v2-0.6B)                                                                             | Dense                   | 0.6799     | 0.536 |\n| [colbert-ir/colbertv2.0](https://huggingface.co/colbert-ir/colbertv2.0) @512                                                                              | Multi-vector            | 0.6785     | 0.571 |\n| [prithivida/Splade\\_PP\\_en\\_v1](https://huggingface.co/prithivida/Splade_PP_en_v1)                                                                        | Sparse                  | 0.6755     | 0.577 |\n| [lightonai/LateOn](https://huggingface.co/lightonai/LateOn)                                                                                               | Multi-vector            | 0.6713     | 0.561 |\n| [tomaarsen/embeddinggemma-300m-miriad-unsloth](https://huggingface.co/tomaarsen/embeddinggemma-300m-miriad-unsloth)                                       | Dense, finetuned        | 0.6705     | 0.530 |\n| [lightonai/LateOn-regularized](https://huggingface.co/lightonai/LateOn-regularized)                                                                       | Multi-vector            | 0.6673     | 0.554 |\n| [lightonai/LateOn-unsupervised](https://huggingface.co/lightonai/LateOn-unsupervised)                                                                     | Multi-vector            | 0.6672     | 0.553 |\n| [lightonai/GTE-ModernColBERT-v1](https://huggingface.co/lightonai/GTE-ModernColBERT-v1)                                                                   | Multi-vector            | 0.6612     | 0.555 |\n| [opensearch-project/opensearch-neural-sparse-encoding-v2-distill](https://huggingface.co/opensearch-project/opensearch-neural-sparse-encoding-v2-distill) | Sparse                  | 0.6518     | 0.531 |\n| [nomic-ai/nomic-embed-text-v1.5](https://huggingface.co/nomic-ai/nomic-embed-text-v1.5) (prompted)                                                        | Dense                   | 0.6387     | 0.498 |\n| [mixedbread-ai/mxbai-embed-large-v1](https://huggingface.co/mixedbread-ai/mxbai-embed-large-v1)                                                           | Dense                   | 0.6355     | 0.502 |\n| [BAAI/bge-large-en-v1.5](https://huggingface.co/BAAI/bge-large-en-v1.5)                                                                                   | Dense                   | 0.6308     | 0.498 |\n| [jinaai/jina-colbert-v2](https://huggingface.co/jinaai/jina-colbert-v2) @1024                                                                             | Multi-vector            | 0.6218     | 0.504 |\n| [nomic-ai/nomic-embed-text-v1.5](https://huggingface.co/nomic-ai/nomic-embed-text-v1.5)                                                                   | Dense                   | 0.6203     | 0.487 |\n| [answerdotai/answerai-colbert-small-v1](https://huggingface.co/answerdotai/answerai-colbert-small-v1)                                                     | Multi-vector            | 0.6184     | 0.514 |\n| [tomaarsen/splade-modernbert-base-miriad](https://huggingface.co/tomaarsen/splade-modernbert-base-miriad)                                                 | Sparse, finetuned       | 0.6142     | 0.473 |\n| [NeuML/biomedbert-base-colbert](https://huggingface.co/NeuML/biomedbert-base-colbert)                                                                     | Multi-vector            | 0.5963     | 0.463 |\n| [BAAI/bge-base-en-v1.5](https://huggingface.co/BAAI/bge-base-en-v1.5)                                                                                     | Dense                   | 0.5930     | 0.454 |\n| [BAAI/bge-small-en-v1.5](https://huggingface.co/BAAI/bge-small-en-v1.5)                                                                                   | Dense                   | 0.5881     | 0.457 |\n| [sentence-transformers/all-mpnet-base-v2](https://huggingface.co/sentence-transformers/all-mpnet-base-v2)                                                 | Dense                   | 0.5159     | 0.396 |\n| [jinaai/jina-colbert-v2](https://huggingface.co/jinaai/jina-colbert-v2)                                                                                   | Multi-vector            | 0.4992     | 0.401 |\n| [mixedbread-ai/mxbai-colbert-large-v1](https://huggingface.co/mixedbread-ai/mxbai-colbert-large-v1)                                                       | Multi-vector            | 0.4690     | 0.358 |\n| [sentence-transformers/static-retrieval-mrl-en-v1](https://huggingface.co/sentence-transformers/static-retrieval-mrl-en-v1)                               | Dense                   | 0.4614     | 0.323 |\n| [sentence-transformers/all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)                                                   | Dense                   | 0.4458     | 0.321 |\n| [colbert-ir/colbertv2.0](https://huggingface.co/colbert-ir/colbertv2.0)                                                                                   | Multi-vector            | 0.4347     | 0.346 |\n\nModels marked `@N` are evaluated with their document length cap lifted to N tokens, since their native caps (180 to 512 tokens) would otherwise truncate the 941-token average passages. For every multi-vector model this lift was worth +0.08 to +0.24 NDCG@10 over the as-served row, and even the dense DenseOn gained +0.03 from the same treatment.\n\nNote that this does not mean that [multi-vector-encoder/mLateOn-medical](https://huggingface.co/multi-vector-encoder/mLateOn-medical) is the strongest model on *all* domains. It's simply the strongest in *my* domain. This is totally fine, as I just need this model to work well on my data.\n\nDon't underestimate the power of finetuning multi-vector models on your domain. Fourteen and a half hours on a single consumer GPU produced a model that no general-purpose retriever comes close to on this data, and the recipe is a single script with no teacher model and no mined negatives!\n\n### Optimizing the index\n\nThe fair objection to multi-vector retrieval is index size, and this domain is close to the worst case for it. Storing one vector per token, my model needs about 878 vectors per passage, so the 200,000-passage corpus takes roughly 45 GB at fp16, where a dense model needs well under 1 GB. Document length is what makes that gap so wide. The Natural Questions passages in the [companion post](https://huggingface.co/blog/multi-vector-encoder) average about 125 token vectors each, seven times fewer, so a corpus of short passages starts from a far smaller index than this one does. The [`HierarchicalTokenPooling`](https://sbert.net/docs/package_reference/multi_vector_encoder/modules.html#hierarchicaltokenpooling) module compresses exactly this by clustering each document's token embeddings and storing the cluster means, keeping roughly `1 / pool_factor` of the vectors:\n\n```python\nfrom sentence_transformers.multi_vector_encoder.modules import HierarchicalTokenPooling\n\npooling = HierarchicalTokenPooling(pool_factor=4)\ndocument_embeddings = model.encode_document(passages, token_pooling=pooling)\n```\n\nI measured it post-hoc on the finished model, with no pooling-aware training, and on long documents it is remarkably cheap.\n\n[![Embedding size for the 200,000-passage corpus versus NDCG@10, with the token pooling trajectory sweeping the multi-vector index into dense-model territory](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_index_size_ndcg.png)](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/train-multi-vector-encoder/mve_medical_index_size_ndcg.png)\n\nThe solid points are uncompressed embeddings, so that every family is counted the same way and scored with exact search. You would not deploy any of them like that, though. Dense indexes routinely use int8 or binary quantization with rescoring, sparse indexes compress their postings, and multi-vector indexes use PLAID-style residual compression. Don't read those points as the disk you need to buy, but as relative storage cost.\n\nToken pooling is the solid line. Halving the vector count costs 0.0033 NDCG@10 and leaves rank-1 accuracy untouched, and keeping only a quarter of them, at 11.2 GB, still scores 0.8991. The curve keeps going (I measured out to a tenth of the vectors, still at 0.8765) but there is little reason to push pooling that far once quantization is on the table, which is what the dashed line below is about.\n\nThe dashed line is what a real deployment might look like. I gave Omar Khattab early access to the model and the benchmark, and he measured these configurations with [fast-plaid](https://github.com/lightonai/fast-plaid) at 1-bit residual quantization, using compact 17-bit centroid ids and 18-bit document ids instead of its ordinary unpacked 64-bit integers, plus document-side pruning:\n\n| configuration            | vectors kept | index   | NDCG@10 |\n| ------------------------ | ------------ | ------- | ------- |\n| 1-bit PLAID, all vectors | 100%         | 3.37 GB | 0.8984  |\n| 1-bit PLAID + pruning    | 65%          | 2.23 GB | 0.8830  |\n| 1-bit PLAID + pruning    | 42%          | 1.45 GB | 0.8642  |\n\nThat first row is 13x smaller than the raw embeddings, for 0.0155 NDCG@10. That is a far better trade than anywhere on the pooling curve. Quantization shrinks each vector while pooling and pruning cut how many you keep, so they compose, and quantization is the one to reach for first. Push further and the last row lands at 1.45 GB, *smaller* than the fp16 embeddings of [Qwen3-Embedding-8B](https://huggingface.co/Qwen/Qwen3-Embedding-8B) (1.64 GB), while scoring 0.0895 higher. The objection that multi-vector indexes are too big does not survive a properly configured index.\n\nThe pruning here is naive, meant only to establish that token reduction works on top of quantization, so read the bottom two rows as a floor rather than the frontier. If you would rather not hand-tune quantization at all, the [Indexing](https://huggingface.co/blog/multi-vector-encoder#indexing) section of the companion post covers fast-plaid, Qdrant, Weaviate, and Vespa.\n\nMulti-vector retrieval is only as expensive as its index. The raw embeddings for this corpus are 45 GB, and a properly configured index is at least 7x smaller at nearly the same accuracy. The index deserves as much of your attention as the checkpoint.\n\n## Acknowledgements\n\nThanks to [Omar Khattab](https://github.com/okhat) for measuring the quantized and pruned index configurations in [Optimizing the index](https://huggingface.co/blog/train-multi-vector-encoder#optimizing-the-index), and for the discussions around late-interaction index costs.\n\n## Additional Resources\n\n### Training Examples\n\nThese pages have training examples with explanations as well as links to training scripts. You can use them to get familiar with the multi-vector training loop:\n\n- [MIRIAD](https://sbert.net/examples/multi_vector_encoder/training/miriad/README.html): domain-specific training on medical retrieval, an earlier and simpler cousin of this blogpost's recipe\n- [MS MARCO](https://sbert.net/examples/multi_vector_encoder/training/msmarco/README.html): contrastive and knowledge distillation recipes\n- [Multimodal](https://sbert.net/examples/multi_vector_encoder/training/multimodal/README.html): ColPali-style visual document retrieval training\n- [PEFT Adapters](https://sbert.net/examples/multi_vector_encoder/training/peft/README.html): parameter-efficient finetuning with LoRA\n\n### Documentation\n\nFor further learning, you may also want to explore the following resources on Sentence Transformers:\n\n- [Installation](https://sbert.net/docs/installation.html)\n- [Quickstart](https://sbert.net/docs/quickstart.html)\n- [Usage](https://sbert.net/docs/multi_vector_encoder/usage/usage.html)\n- [Creating Custom Models](https://sbert.net/docs/multi_vector_encoder/usage/custom_models.html)\n- [Pretrained Models](https://sbert.net/docs/multi_vector_encoder/pretrained_models.html)\n- [Training Overview](https://sbert.net/docs/multi_vector_encoder/training_overview.html) (This blogpost is a distillation of the Training Overview documentation)\n- [Loss Overview](https://sbert.net/docs/multi_vector_encoder/loss_overview.html)\n- [API Reference](https://sbert.net/docs/package_reference/multi_vector_encoder/index.html)\n\nAnd here is an advanced page that might interest you:\n\n- [Distributed Training](https://sbert.net/docs/sentence_transformer/training/distributed.html)\n\nAnd the companion blogpost, covering everything about *using* these models:\n\n- [Multi-Vector (Late Interaction) Embedding Models with Sentence Transformers](https://huggingface.co/blog/multi-vector-encoder)\n",
  "dateCreated": "2026-10-19T01:26:53.998049939+00:00",
  "date_created": "2026-08-26T00:00:00+00:00",
  "date_modified": "2026-08-26T00:00:00+00:00",
  "date_published": "2026-08-26T00:00:00+00:00",
  "description": "Sentence Transformers is a Python library for using and training embedding and reranker models for a wide range of applications, such as retrieval augmented generation, semantic search, semantic textual similarity, and more. Its v6.0…",
  "external_url": "https://huggingface.co/blog/train-multi-vector-encoder",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/huggingface-co/2026/08/2026-08-26-training-and-finetuning-multi-vector-embedding-models-with.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/huggingface-co/2026/08/2026-08-26-training-and-finetuning-multi-vector-embedding-models-with.md"
  },
  "id": "urn:aggr:item:0d00a6a331ca6b9ff36092b327c397da0f6a1da5",
  "isBasedOn": {
    "@id": "https://huggingface.co/blog/train-multi-vector-encoder"
  },
  "mainEntity": {
    "@id": "https://huggingface.co/blog/train-multi-vector-encoder",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-08-26-training-and-finetuning-multi-vector-embedding-models-with/#webpage"
    },
    "dateModified": "2026-08-26T00:00:00+00:00",
    "datePublished": "2026-08-26T00:00:00+00:00",
    "headline": "Training and Finetuning Multi-Vector Embedding Models with Sentence Transformers",
    "timeRequired": "PT26M",
    "url": "https://huggingface.co/blog/train-multi-vector-encoder",
    "wordCount": 5676
  },
  "name": "Archived snapshot: Training and Finetuning Multi-Vector Embedding Models with Sentence Transformers",
  "preview": null,
  "reading_minutes": 26,
  "replicated_at": null,
  "source": "huggingface-co",
  "source_name": "Hugging Face - Blog",
  "summary": null,
  "tags": [],
  "temporalCoverage": "2026-10-19T01:26:53.998049939+00:00",
  "title": "Training and Finetuning Multi-Vector Embedding Models with Sentence Transformers",
  "url": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-08-26-training-and-finetuning-multi-vector-embedding-models-with/",
  "word_count": 5676
}