Bi-encoder retrieval models learn by separating a matching text pair from the alternatives visible during training. MultipleNegativesRankingLoss turns the other positive texts in each batch into in-batch negatives, so a curated set of anchor-positive pairs can fine-tune a retriever without a separate negative column.
The active Python environment needs the current sentence-transformers[train] extra, which supplies datasets and accelerate for SentenceTransformerTrainer. The training workflow uses eight unique support query-answer pairs so every batch contains distinct candidate answers.
Batch size controls how many in-batch negatives each anchor sees. Use BatchSamplers.NO_DUPLICATES to keep repeated text from becoming a false negative, and consider CachedMultipleNegativesRankingLoss when a larger effective batch must fit within limited device memory.
from datasets import Dataset from sentence_transformers import ( SentenceTransformer, SentenceTransformerTrainer, SentenceTransformerTrainingArguments, ) from sentence_transformers.sentence_transformer import losses from sentence_transformers.sentence_transformer.training_args import BatchSamplers model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") output_dir = "support-mnrl-model"
train_dataset = Dataset.from_dict( { "anchor": [ "reset a locked account", "reset a user password", "enable two-factor authentication", "rotate an API token", "restore a deleted project", "invite a new team member", "export audit logs", "change the billing contact", ], "positive": [ "Unlock the account from the admin users page.", "Open the user profile and send a password reset email.", "Open account security and enroll an authenticator app.", "Revoke the old API token and create a replacement token.", "Open deleted projects and restore the selected project.", "Open team settings and send an invitation email.", "Open compliance reports and export the audit log CSV.", "Open billing settings and update the primary contact.", ], } )
Each positive becomes a negative candidate for the other anchors in its batch. Duplicate or equivalent texts therefore create false negatives by presenting the same meaning as an incorrect answer.
loss = losses.MultipleNegativesRankingLoss(model) args = SentenceTransformerTrainingArguments( output_dir="mnrl-checkpoints", num_train_epochs=1, per_device_train_batch_size=4, learning_rate=2e-5, warmup_steps=0.1, batch_sampler=BatchSamplers.NO_DUPLICATES, save_strategy="no", logging_steps=1, disable_tqdm=True, report_to=[], )
A batch size of four gives each anchor three in-batch alternatives in this small run. Larger batches expose more negatives but require more memory unless the cached loss variant is used.
trainer = SentenceTransformerTrainer( model=model, args=args, train_dataset=train_dataset, loss=loss, ) trainer.train() model.save_pretrained(output_dir) print(f"training rows: {len(train_dataset)}") print(f"loss: {loss.__class__.__name__}") print(f"saved model: {output_dir}")
$ python3 train_mnrl.py
{'loss': '0.0005047', 'grad_norm': '0.04047', 'learning_rate': '0', 'epoch': '0.5'}
{'loss': '0.006253', 'grad_norm': '0.7316', 'learning_rate': '2e-05', 'epoch': '1'}
##### snipped #####
training rows: 8
loss: MultipleNegativesRankingLoss
saved model: support-mnrl-model
from sentence_transformers import SentenceTransformer model = SentenceTransformer("support-mnrl-model") query = "How can I replace an API token?" documents = [ "Revoke the old API token and create a replacement token.", "Open billing settings and update the primary contact.", "Open deleted projects and restore the selected project.", ] query_embedding = model.encode([query]) document_embeddings = model.encode(documents) scores = model.similarity(query_embedding, document_embeddings)[0] best_index = scores.argmax().item() print(f"embedding width: {model.get_embedding_dimension()}") print(f"top document: {documents[best_index]}") print(f"top score: {float(scores[best_index]):.4f}") if best_index != 0: raise SystemExit("The matching API-token answer did not rank first.")
$ python3 verify_mnrl.py embedding width: 384 top document: Revoke the old API token and create a replacement token. top score: 0.8125