Fine-tuning an embedding model
Training an embedding model further on your own query-passage pairs so domain jargon lands near its meaning. Typically +5–15 points recall in-domain.
Off-the-shelf Embedding models learned from general internet text. If your field has its own vocabulary, such as part numbers, contract clause names or medical abbreviations, the model may not place related texts close together.
Fine-tuning shows the model thousands of 'this question goes with this passage' examples from your field so it learns those connections. It is worth doing only after you have checked that simpler fixes, like keyword search alongside vector search, are not enough.
A capable new hire who needs a month shadowing the team before 'CR-7 on the Q-line' means anything to them.
Mining hard negatives blindly from the top results: in dense domains many 'negatives' are unlabelled positives, and the model learns to push correct answers away.
Standard recipe: contrastive training with in-batch negatives (MultipleNegativesRankingLoss in Sentence Transformers, an InfoNCE objective) on (query, positive passage) pairs, plus 1–3 hard negatives per pair mined from the current model's top-50 that are not labelled relevant. Larger batches give more negatives per step; GradCache or CachedMNRL makes batch 1,024+ fit on one GPU. Typical run: 5k–50k pairs, 1–3 epochs, learning rate 1e-5 to 2e-5, warmup 10%. A base model like bge-base (110M) trains in under an hour on one A100 at 20k pairs.
Pairs come from search logs (query → clicked or cited doc), support tickets resolved by an article, or synthetic questions generated per chunk by an LLM and filtered by a Cross-encoder. The main risks are false negatives in hard-negative mining (a 'negative' that is actually relevant teaches the wrong thing), overfitting to the synthetic question style, and forgetting general ability. Hold out 10–20% of queries by document, not by pair, so leakage does not inflate gains. A fine-tuned model is a new model: every vector must be re-embedded via a full Backfill. API models mostly cannot be fine-tuned (as of Oct 2026), so this usually means self-hosting an open model.
Sentence Transformers v3+ trainer, base model bge-base-en-v1.5 or bge-m3 (or Qwen3-Embedding-0.6B for multilingual), CachedMultipleNegativesRankingLoss, batch 512–1,024, 2 hard negatives per pair from ranks 10–50 filtered by a cross-encoder, lr 2e-5, 1–2 epochs, golden set split by document.
Aerospace maintenance manuals, 1.4M chunks. Baseline bge-base-en-v1.5 recall@10 = 0.62 on 600 technician queries like 'torque spec MLG axle nut post-retread'. Hybrid with BM25 lifts it to 0.71.
Fine-tune on 18k pairs: 6k from search logs (query → doc opened > 30 s) and 12k synthetic questions from Claude Haiku 4.5, filtered by bge-reranker-v2-m3 score >0.5, with 2 hard negatives each from the base model's top-50. Recall@10 alone: 0.78; hybrid: 0.84. Re-embedding 1.4M chunks takes 40 minutes on one L4.
State the order: "Before fine-tuning I try hybrid search and a reranker, because they are reversible. I fine-tune when the golden set shows vocabulary misses that BM25 cannot fix, I have ≥5k real or filtered synthetic pairs, and I can own a self-hosted model and its backfills." Expect: "how do you know the gain is real?" A held-out split by document, the same golden set across baseline, hybrid and fine-tuned, and a check that general queries didn't regress.
Numbers worth knowing
- Typical in-domain gain
- +5–15 points recall@10jargon-heavy domain, 10k–50k pairs, vs same base model
- Training time
- <1 h on one A100110M model, 20k pairs, 2 epochs
- Minimum useful data
- ~5k pairsreal or cross-encoder-filtered synthetic
How to decide
| When | Do this |
|---|---|
| Misses are exact identifiers (part numbers, statute ids) | Add BM25 / Hybrid search first; fine-tuning won't memorize ids. |
| Misses are domain paraphrase ('MLG' vs 'main landing gear') and you have ≥5k pairs | Fine-tune an open model and backfill. |
| You must stay on an API embedding model | Add a Reranker or a query-side instruction instead. |
Cost maths
Training: 1 A100-hour ≈ $2–4 (illustrative on-demand, Oct 2026). Synthetic pairs: 12k questions × ~1.2k input + 60 output tokens on Claude Haiku 4.5 at $1/M in, $5/M out ≈ $14 + $4 = $18. Backfill: 1.4M chunks on one L4 at ~600 passages/s ≈ 40 min ≈ $0.60. The recurring cost is ownership: a GPU embedding service, retraining as the corpus shifts, and a backfill every retrain.
Operating it
- Golden-set recall plateaus after hybrid + reranker and misses are paraphrase, not identifiers
- Search logs exceed ~5k distinct queries/week with click or citation signal: enough for a retrain cycle
- Fine-tuned model's recall drifts >3 points as the corpus changes: schedule quarterly retraining
- Golden-set recall@10 on real queries held out by document: gate any new checkpoint on ≥2-point gain
- Recall on a general-domain control set: alert on a drop >3 points (catastrophic forgetting)
- Online answer acceptance or citation click-through during canary: roll back on a 2-point drop
When it goes wrong
A fine-tuned model scored +14 points offline, but live answer acceptance on a legal research tool fell.
A/B test over two weeks: acceptance 61% vs 66% control; offline golden set still showed the gain.
Train/test split was by pair, not by document, so the same contracts appeared in both; synthetic questions also mirrored chunk wording that real users never used.
Re-split by document, rebuilt the golden set from real queries only, mixed 40% real log pairs into training; the honest gain was +6 points and held online.
Sources
- Sentence Transformers training overview — Sentence Transformers, 2025docs
- Representation Learning with Contrastive Predictive Coding (InfoNCE) — van den Oord et al., 2018paper
- Dense Passage Retrieval for Open-Domain Question Answering — Karpukhin et al., 2020paper
- M3-Embedding (BGE-M3) — Chen et al., 2024paper
Check yourself
Three questions. Every option is explained once you answer.