{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-03-20-build-a-domain-specific-embedding-model-in-under-a-day/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "If you are building a RAG (Retrieval-Augmented Generation) system, you have likely hit this wall: Everything works… until it doesn’t. General-purpose embedding models are trained to understand the internet; not your contracts, manufacturing logs, proprietary chemical formulations or internal taxonomy. They capture broad semantic similarity, but they do not understand the fine-grained distinctions that matter in your domain. Fine-tuning an embedding model can improve the performance of your retrieval pipeline when off-the-shelf models fail to effectively capture domain-specific nuances. Despite how critical embeddings are to RAG performance, the process remains surprisingly fragmented, the skills required are specialized, and the time investment is daunting.\n\nWith a single GPU and less than a day of training time, you can transform a general-purpose embedding model into one that truly understands your domain, no manual labeling required. To help you hit the ground running, we are also releasing a ready-to-use [synthetic training dataset](https://huggingface.co/datasets/nvidia/Retrieval-Synthetic-NVDocs-v1) generated from NVIDIA's public documentation using this exact pipeline. Using this data and the recipe, we saw over 10% improvement in both Recall@10 and NDCG@10. [Atlassian](https://www.atlassian.com/) applied this recipe to fine-tune on their [JIRA dataset](https://zenodo.org/records/15719919/files/2025-06-23%20ThePublicJiraDataset.zip?download=1), increasing Recall@60 from 0.751 to 0.951, a 26% improvement - on a single GPU.\n\n### 🔗Quick Links to Dataset and Code:\n\n1. [Embedding Model](https://huggingface.co/nvidia/llama-nemotron-embed-1b-v2)\n2. [GitHub](https://github.com/NVIDIA-NeMo/Nemotron/tree/main/src/nemotron/recipes/embed)\n3. [Synthetic dataset on NVIDIA’s public documents](https://huggingface.co/datasets/nvidia/Retrieval-Synthetic-NVDocs-v1)\n\n### 🧑💻Open Source Projects Recipe Integrates:\n\n1. [NeMo Data Designer](https://github.com/NVIDIA-NeMo/DataDesigner) for synthetic data generation\n2. [NeMo Automodel](https://github.com/NVIDIA/NeMo-Automodel) for embedding model training\n3. [BEIR](https://github.com/beir-cellar/beir) for Information retrieval evaluation\n4. [NeMo Export-Deploy](https://github.com/NVIDIA/NeMo-Export-Deploy) for ONNX/TensorRT conversion\n5. [NVIDIA NIM](https://developer.nvidia.com/nim) for production inference serving\n\n### 📋Prerequisites:\n\n- A directory of domain documents (text files - .txt, .md, or similar)\n- A valid NVIDIA API key (free at [build.nvidia.com](https://build.nvidia.com))\n- NVIDIA Ampere GPU or newer with at least 80GB memory (with [Compute Capability](https://developer.nvidia.com/cuda-gpus) >= 8.0)\n  - This tutorial has been tested on 1xA100 (80GB), and 1xH100 (80GB)\n\nBy the end of this post, you’ll know how to:\\\n📄 Generate training data from domain documents without labeled data\\\n🎯 Use hard negative mining for effective contrastive training\\\n🔗 Improve embedding quality with multi-hop queries\\\n⚙️ Fine-tune a bi-encoder embedding model\\\n📊 Evaluate whether fine-tuning improves retrieval\\\n🚀 Deploy the fine-tuned model in your pipeline\n\n## ⚙️Setup\n\nIn this tutorial, we will finetune the base model [Llama-Nemotron-Embed-1B-v2](https://huggingface.co/nvidia/llama-nemotron-embed-1b-v2) - a 1-billion-parameter embedding model that balances quality and inference cost. To get started, follow this [setup guide](https://github.com/NVIDIA-NeMo/Nemotron/blob/main/docs/nemotron/embed/README.md).\n\n## 📚 Step 1: Generate Training Data from Documents\n\nFine-tuning an embedding model requires thousands of (query, relevant document) pairs. Most use cases don’t have this data readily available. Creating it manually is expensive, slow, and often biased by the annotator’s personal interpretation of what’s “relevant.”\\\nInstead of labeling data by hand, you can use an LLM (nvidia/nemotron-3-nano-30b-a3b) to read your documents and automatically generate high-quality synthetic question–answer pairs.\n\n```shell\nnemotron embed sdg -c default corpus_dir=./data/my_domain_docs\n```\n\n### How does it work?\n\nBehind the scenes, this runs a four-stage synthetic data generation (SDG) pipeline powered by [NeMo Data Designer](https://github.com/NVIDIA-NeMo/DataDesigner):\n\n[![sdg_pipeline](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/84IMYChKX4twWnC4U6iCQ.png)](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/84IMYChKX4twWnC4U6iCQ.png)\n\n### What does the output look like?\n\n**Source document chunk:**\n\nThe thermal design power (TDP) of the H100 GPU is 700W in SXM form factor. The cooling solution must maintain junction temperature below 83°C under sustained workloads. Liquid cooling is recommended for dense deployments exceeding 4 GPUs per node, as air cooling cannot dissipate sufficient heat in standard 2U chassis configurations.\n\n**Generated QA pairs:**\n\n```json\n{\n  \"question\": \"What cooling approach is recommended when deploying more than 4 H100 GPUs per server node?\",\n  \"answer\": \"Liquid cooling is recommended for dense deployments exceeding 4 GPUs per node, as air cooling cannot dissipate sufficient heat in standard 2U chassis configurations.\",\n  \"query_type\": \"contextual\",\n  \"reasoning_type\": \"factual\",\n  \"question_complexity\": 3,\n  \"segment_ids\": [1],\n  \"quality_score\": 8.5\n}\n```\n\n```json\n{\n  \"question\": \"How does the 700W TDP of the H100 SXM constrain the choice between air and liquid cooling in multi-GPU configurations?\",\n  \"answer\": \"The 700W TDP generates substantial heat that must be dissipated to keep junction temperatures below 83°C. In dense configurations exceeding 4 GPUs per node, air cooling in standard 2U chassis cannot handle this thermal load, making liquid cooling necessary.\",\n  \"query_type\": \"multi_hop\",\n  \"reasoning_type\": \"causal\",\n  \"question_complexity\": 4,\n  \"segment_ids\": [1, 2],\n  \"hop_count\": 2,\n  \"quality_score\": 9.0\n}\n```\n\nNotice the difference: the first question is a simple factual lookup. The second requires multi-hop, causal reasoning. The pipeline generates both types, with configurable complexity levels (2–5) and hop counts (1–3). Each QA pair then undergoes quality evaluation, receiving sub-scores for relevance, accuracy, context support, and clarity, along with an overall score. Only pairs that meet the threshold are included in training.\n\n## ⛏️ Step 2: Mine Hard Negatives (and Why They Matter)\n\nIf you train an embedding model with only positive pairs (query + correct document), it learns to distinguish obviously different documents but fails on the hard cases — passages that *look* relevant but are not the right answer. In a real retrieval system, these near-misses are exactly the documents that cause bad answers. Hard negative mining finds these confusing passages so the model can learn to tell them apart.\n\n```shell\nnemotron embed prep -c default\n```\n\nThe above command runs three sub-steps automatically:\n\n### 2a. Train / Validation / Test Split\n\nThe generated QA pairs are split into training (80%) and test (20%) sets. The test set is formatted as a [BEIR](https://github.com/beir-cellar/beir)-compatible benchmark for standardized evaluation in Step 5.\n\n### 2b. Hard Negative Mining\n\nUsing the base embedding model, the pipeline:\n\n1. Embeds every query and every passage in the corpus.\n2. Computes similarity between each query and all passages.\n3. Masks out each query's labeled positive documents.\n4. Applies a margin filter: any non-positive document scoring *above* 95% of the minimum positive score is eliminated. This exclusion zone guards against false negatives — unlabeled passages that are so close to the positive they may actually be relevant.\n5. From the surviving candidates, selects the top-k highest-scoring documents as hard negatives (5 per query by default).\n\nThe result: hard negatives are the most similar non-positive passages that still fall safely below the positive-score ceiling. They are passages the current model considers highly relevant but that are not the labeled answer.\n\n**Why this works**: Training on easy negatives (completely unrelated passages) teaches the model nothing new. Training on hard negatives forces it to learn the subtle distinctions that matter in your domain. For example, in a medical corpus, a question about \"metformin dosage for Type 2 diabetes\" might have hard negatives about \"metformin side effects\" or \"insulin dosage for Type 1 diabetes\" — close but critically different. The 95% margin ceiling prevents the miner from selecting passages that are too close to the positive, which could actually be correct answers that simply weren't labeled during SDG.\n\n### 2c. Multi-Hop Unrolling\n\nMulti-hop questions reference multiple positive documents. For example, a question like *\"How does the thermal management system in Section 3.2 relate to the power constraints described in Section 5.1?\"* has two positive passages.\n\nUnrolling creates one training example per (query, positive document) pair, so the contrastive loss sees each positive independently. A question with 2 positive documents becomes 2 training examples, each with the same hard negatives but a different positive.\n\nThe final output is a training-ready JSON file:\n\n```json\n{\n  \"question_id\": \"q42_0\",\n  \"question\": \"How does the thermal management system in Section 3.2 relate to the power constraints described in Section 5.1?\",\n  \"pos_doc\": [{\"id\": \"Section 3.2\"}],\n  \"neg_doc\": [{\"id\": \"d_x7y8z9\"}, {\"id\": \"d_m4n5o6\"}, {\"id\": \"d_p1q2r3\"}, {\"id\": \"d_s4t5u6\"}, {\"id\": \"d_v7w8x9\"}]\n}, \n{\n  \"question_id\": \"q42_1\",\n  \"question\": \"How does the thermal management system in Section 3.2 relate to the power constraints described in Section 5.1?\",\n  \"pos_doc\": [{\"id\": \"Section 5.1\"}],\n  \"neg_doc\": [{\"id\": \"d_x7y8z9\"}, {\"id\": \"d_m4n5o6\"}, {\"id\": \"d_p1q2r3\"}, {\"id\": \"d_s4t5u6\"}, {\"id\": \"d_v7w8x9\"}]\n}\n```\n\n## 🔍 Step 3: Understand Multi-Hop Questions and Why They Improve Retrieval\n\nStandard embedding fine-tuning generates one question per passage and trains the model to match them. This works for simple factual lookups, but real users ask complex questions that span multiple documents or sections. If the model has only seen single-hop training data, it will struggle to retrieve *all* the relevant passages for these complex queries.\n\nThe SDG pipeline generates questions at 1 to 3 hops by default:\n\n- **1-hop:** \"What is the TDP of the H100 SXM?\" — answered by a single passage.\n- **2-hop:** \"How does the H100's TDP relate to cooling requirements in dense deployments?\" — requires connecting information from two passages.\n- **3-hop:** \"Given the TDP, cooling constraints, and rack density limits, what is the maximum number of H100 GPUs deployable in a standard data center row?\" — synthesizes three passages.\n\nEach hop is tracked with its own context summary and segment IDs, so the training data preserves the full reasoning chain. After unrolling (Step 2c), each (question, relevant passage) pair becomes an independent training signal, teaching the model that *all* of these passages are relevant to the multi-hop query.\n\nThe fine-tuned model learns to retrieve contextually related documents, not just lexically similar ones.\n\n## 🧠 Step 4: Fine-Tune the Embedding Model\n\n```shell\nnemotron embed finetune -c default\n```\n\n### How contrastive learning works\n\nThe training uses a **biencoder architecture** with **contrastive loss**.\n\n[![contrastive_learning](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/ntYmgzatUK_Sfn35GCdve.png)](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/ntYmgzatUK_Sfn35GCdve.png)\n\nThe temperature of 0.02 is deliberately aggressive, it produces a very sharp probability distribution. This works well because the hard negatives from Step 2 are high-quality: they are genuinely confusing passages that the model needs strong gradients to learn to distinguish.\n\n### Key hyperparameters\n\n| Parameter                  | Default | Notes                                                                                                                           |\n| -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| Epochs                     | 3       | **Default is tuned for the small example dataset; for real-world data, 1–2 epochs is usually sufficient** to avoid overfitting. |\n| Learning rate              | 1e-5    | Tuning: try double and half of the default value                                                                                |\n| Learning rate warmup steps | 5       | Set to 5-10% of total steps of finetune to have better early training stability                                                 |\n| Global batch size          | 128     |                                                                                                                                 |\n| Passages per query         | 5       | 1 positive + 4 hard negatives                                                                                                   |\n\n### Checkpoint frequency\n\nIf `ckpt_every_steps` is omitted from the config, checkpoint frequency is set automatically:\n\n- **Map-style datasets** (known length): defaults to once per epoch.\n- **Iterable datasets** (unknown length): defaults to twice during training.\n\nThis means you can start with a small corpus (50–100 documents) for a quick proof-of-concept and scale up later without manually tuning checkpoint settings.\n\n## 📈 Step 5: Measure the Improvement\n\nDid fine-tuning actually help? Let’s find out by running a standardized evaluation comparing the base model against the fine-tuned checkpoint on the held-out test set:\n\n```shell\nnemotron embed eval -c default\n```\n\nThe evaluation uses the [BEIR framework](https://github.com/beir-cellar/beir) and computes four standard information retrieval metrics at k = 1, 5, 10, and 100:\n\n1. nDCG@k: Ranking quality — are the best documents ranked highest?\n2. Recall@k: Coverage — what fraction of relevant documents appear in the top k?\n3. Precision@k: Accuracy — what fraction of the top k results are actually relevant?\n4. MAP@k: Average precision across all queries\n\nA successful fine-tune typically results in a 10% improvement in nDCG@10 and Recall@10 within <1 day.\n\n**Results using Retrieval Synthetic NVDocs:**\n\n```\n📊 Comparison (Base -> Fine-tuned)\n============================================================\n  NDCG:\n    NDCG@1: 0.55178 → 0.60796 (+0.05618, +10.2%)\n    NDCG@5: 0.51894 → 0.57689 (+0.05795, +11.2%)\n    NDCG@10: 0.55506 → 0.61559 (+0.06053, +10.9%)\n    NDCG@100: 0.60617 → 0.66567 (+0.05950, +9.8%)\n  Recall:\n    Recall@1: 0.28478 → 0.31547 (+0.03069, +10.8%)\n    Recall@5: 0.54486 → 0.60288 (+0.05802, +10.6%)\n    Recall@10: 0.62979 → 0.69296 (+0.06317, +10.0%)\n    Recall@100: 0.81421 → 0.87020 (+0.05599, +6.9%)\n```\n\n### What if the numbers don't improve?\n\nThe pipeline makes it easy to iterate:\n\n- **Low quality scores in SDG?** Check your document quality — clean, well-formatted text produces better synthetic data. Try a larger and more powerful LLM.\n- **Not enough training data?** Add more documents to your corpus and re-run Stage 0.\n- **Overfitting?** The default 3 epochs is calibrated for the small example dataset; for most real-world data, **1–2 epochs is enough**. Also consider increasing the quality threshold to keep only the best training examples.\n- **Wrong learning rate?** Try double or half of the default learning rate.\n\n### 🏆 Real-World Results: Atlassian\n\nThis recipe has been validated on real enterprise data by Atlassian. They applied this pipeline to fine-tune Llama-Nemotron-Embed-1B-v2 on a [public Jira dataset](https://zenodo.org/records/15719919/files/2025-06-23%20ThePublicJiraDataset.zip?download=1) using a single NVIDIA A100 80GB GPU, following the same stages described above\n\n**Recall@60 jumped from 0.751 to 0.951 — a 26.7% gain.**\\\nThe fine-tuned model retrieves the correct document within the top 60 results for 95.1% of queries, up from 75.1% with the base model. For a retrieval system underpinning Jira search, this directly translates into more relevant results for millions of users. Find more details in their blog post [Advancing semantic search for millions of Rovo users](https://www.atlassian.com/blog/atlassian-engineering/advancing-rovo-semantic-search).\n\n[![ATL result](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/hsFtTLz1WfSgaWBdHof82.png)](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/hsFtTLz1WfSgaWBdHof82.png)\n\n## 🚀 Step 6: Export and Deploy\n\nA PyTorch checkpoint is great for evaluation but too slow for production. The final two stages convert the model and serve it behind an API.\n\n### Export to ONNX / TensorRT\n\n```shell\nnemotron embed export -c default\n```\n\nThis exports the fine-tuned checkpoint to ONNX (opset 17). Optionally, it compiles a TensorRT engine for maximum inference throughput, with configurable optimization profiles for batch size (1–64) and sequence length (3–256):\n\n```shell\n# ONNX only (runs anywhere)\nnemotron embed export -c default export_to_trt=false\n\n# FP8 quantization for further speedup\nnemotron embed export -c default quant_cfg=fp8\n```\n\n### Deploy with NVIDIA NIM\n\nThe exported model is deployed inside an [NVIDIA NIM](https://developer.nvidia.com/nim) container — a production-ready inference microservice exposing an OpenAI-compatible /v1/embeddings endpoint:\n\n```shell\nnemotron embed deploy -c default\n```\n\nOnce running, any client can call it:\n\n```shell\ncurl -X POST http://localhost:8000/v1/embeddings \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"input\": [\"What cooling is needed for 8 H100 GPUs in a 2U chassis?\"],\n       \"model\": \"custom\",\n       \"input_type\": \"query\"}'\n```\n\nBecause NIM serves an OpenAI-compatible API, you can drop it into any existing RAG pipeline that uses the embeddings API format — no code changes needed.\n\n### Verify deployment accuracy\n\nThe pipeline includes a NIM accuracy verification step that runs the same BEIR evaluation against the deployed endpoint:\n\n```shell\nnemotron embed eval -c default eval_nim=true eval_base=false\n```\n\nThis catches any accuracy loss from the ONNX/TensorRT conversion. Metrics that match within tolerance (0.03 for @1, 0.01 for @5+) are marked with a check; deviations beyond conversion noise are flagged.\n\n## Putting It All Together\n\nThe full embedding fine-tuning pipeline can be run in six commands, from raw documents to a deployed model.\n\n```shell\n# 1. Generate synthetic training data from your documents\nnemotron embed sdg -c default corpus_dir=./data/my_docs\n\n# 2. Prepare the training data (split data, mine hard negatives, unroll)\nnemotron embed prep -c default\n\n# 3. Fine-tune the embedding model\nnemotron embed finetune -c default\n\n# 4. Evaluate the base vs. fine-tuned model\nnemotron embed eval -c default\n\n# 5. Export the optimized model\nnemotron embed export -c default\n\n# 6. Deploy the model\nnemotron embed deploy -c default\n```\n\n### Expected time and resources\n\n| Stage     | GPU Required?    | Estimated Time | Notes                                    |\n| --------- | ---------------- | -------------- | ---------------------------------------- |\n| SDG       | No (uses API)    | \\~1 hour       | Varies by corpus size and API rate limit |\n| Data Prep | Yes (40 GB VRAM) | \\~5 min        | Hard negative mining on GPU              |\n| Fine-Tune | Yes (80 GB VRAM) | \\~1 hours      | Varies by dataset size and epochs        |\n| Eval      | Yes (40 GB VRAM) | \\~5 min        |                                          |\n| Export    | Yes (40 GB VRAM) | \\~5 min        | TensorRT requires NGC container          |\n| Deploy    | Yes (40 GB VRAM) | \\~5 min        | NIM container startup                    |\n\n**Total: under a day, with most time being hands-off training.** For a small corpus (~500 documents), the entire pipeline completes in about 2–3 hours.\n\nThe pipeline can run end-to-end, but each stage can also be executed independently depending on your starting point. For example, if you have raw documents, you can begin with synthetic data generation (SDG), while datasets that already include hard negatives can skip earlier steps and go directly to fine-tuning. Since every stage uses standard formats such as JSON, BEIR, and ONNX, it’s easy to integrate custom components or reuse intermediate outputs in other workflows. The recipe is also flexible in how it runs, supporting execution on a local machine, inside Docker containers, or on Slurm-based clusters.\n\n## Try It Yourself\n\nIf you have domain documents and some time in your hand, you can generate your first batch of synthetic training data today! The full pipeline - from documents to a deployed, domain-adapted embedding model - runs in under a day on a single GPU. You can start with our ready-made [nvidia/Retrieval-Synthetic-NVDocs-v1](https://huggingface.co/datasets/nvidia/Retrieval-Synthetic-NVDocs-v1) dataset to try the pipeline right away. Let us know what you build.\\\n**Star the repos** for [Nemotron](https://github.com/NVIDIA/Nemotron), [NeMo Data Designer](https://github.com/NVIDIA/NeMo-Data-Designer) and [NeMo Automodel](https://github.com/NVIDIA/NeMo-Automodel) if you find them useful.\n",
  "authors": [],
  "captured_at": "2026-10-04T12:50:30.804479567+00:00",
  "category": "ai",
  "content_html": "<p>If you are building a RAG (Retrieval-Augmented Generation) system, you have likely hit this wall: Everything works… until it doesn’t. General-purpose embedding models are trained to understand the internet; not your contracts, manufacturing logs, proprietary chemical formulations or internal taxonomy. They capture broad semantic similarity, but they do not understand the fine-grained distinctions that matter in your domain. Fine-tuning an embedding model can improve the performance of your retrieval pipeline when off-the-shelf models fail to effectively capture domain-specific nuances. Despite how critical embeddings are to RAG performance, the process remains surprisingly fragmented, the skills required are specialized, and the time investment is daunting.</p>\n<p>With a single GPU and less than a day of training time, you can transform a general-purpose embedding model into one that truly understands your domain, no manual labeling required. To help you hit the ground running, we are also releasing a ready-to-use <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/nvidia/Retrieval-Synthetic-NVDocs-v1\">synthetic training dataset</a> generated from NVIDIA's public documentation using this exact pipeline. Using this data and the recipe, we saw over 10% improvement in both Recall@10 and NDCG@10. <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.atlassian.com/\">Atlassian</a> applied this recipe to fine-tune on their <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://zenodo.org/records/15719919/files/2025-06-23%20ThePublicJiraDataset.zip?download=1\">JIRA dataset</a>, increasing Recall@60 from 0.751 to 0.951, a 26% improvement - on a single GPU.</p>\n<h3>🔗Quick Links to Dataset and Code:</h3>\n<ol>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/nvidia/llama-nemotron-embed-1b-v2\">Embedding Model</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/NVIDIA-NeMo/Nemotron/tree/main/src/nemotron/recipes/embed\">GitHub</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/nvidia/Retrieval-Synthetic-NVDocs-v1\">Synthetic dataset on NVIDIA’s public documents</a></li>\n</ol>\n<h3>🧑💻Open Source Projects Recipe Integrates:</h3>\n<ol>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/NVIDIA-NeMo/DataDesigner\">NeMo Data Designer</a> for synthetic data generation</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/NVIDIA/NeMo-Automodel\">NeMo Automodel</a> for embedding model training</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/beir-cellar/beir\">BEIR</a> for Information retrieval evaluation</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/NVIDIA/NeMo-Export-Deploy\">NeMo Export-Deploy</a> for ONNX/TensorRT conversion</li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://developer.nvidia.com/nim\">NVIDIA NIM</a> for production inference serving</li>\n</ol>\n<h3>📋Prerequisites:</h3>\n<ul>\n<li>A directory of domain documents (text files - .txt, .md, or similar)</li>\n<li>A valid NVIDIA API key (free at <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://build.nvidia.com\">build.nvidia.com</a>)</li>\n<li>NVIDIA Ampere GPU or newer with at least 80GB memory (with <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://developer.nvidia.com/cuda-gpus\">Compute Capability</a> &gt;= 8.0)\n<ul>\n<li>This tutorial has been tested on 1xA100 (80GB), and 1xH100 (80GB)</li>\n</ul>\n</li>\n</ul>\n<p>By the end of this post, you’ll know how to:<br />\n📄 Generate training data from domain documents without labeled data<br />\n🎯 Use hard negative mining for effective contrastive training<br />\n🔗 Improve embedding quality with multi-hop queries<br />\n⚙️ Fine-tune a bi-encoder embedding model<br />\n📊 Evaluate whether fine-tuning improves retrieval<br />\n🚀 Deploy the fine-tuned model in your pipeline</p>\n<h2>⚙️Setup</h2>\n<p>In this tutorial, we will finetune the base model <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/nvidia/llama-nemotron-embed-1b-v2\">Llama-Nemotron-Embed-1B-v2</a> - a 1-billion-parameter embedding model that balances quality and inference cost. To get started, follow this <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/NVIDIA-NeMo/Nemotron/blob/main/docs/nemotron/embed/README.md\">setup guide</a>.</p>\n<h2>📚 Step 1: Generate Training Data from Documents</h2>\n<p>Fine-tuning an embedding model requires thousands of (query, relevant document) pairs. Most use cases don’t have this data readily available. Creating it manually is expensive, slow, and often biased by the annotator’s personal interpretation of what’s “relevant.”<br />\nInstead of labeling data by hand, you can use an LLM (nvidia/nemotron-3-nano-30b-a3b) to read your documents and automatically generate high-quality synthetic question–answer pairs.</p>\n<pre><code class=\"language-shell\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed sdg<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default corpus_dir=./data/my_domain_docs</span>\n</span></span></code></pre>\n<h3>How does it work?</h3>\n<p>Behind the scenes, this runs a four-stage synthetic data generation (SDG) pipeline powered by <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/NVIDIA-NeMo/DataDesigner\">NeMo Data Designer</a>:</p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/84IMYChKX4twWnC4U6iCQ.png\"><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/84IMYChKX4twWnC4U6iCQ.png\" alt=\"sdg_pipeline\" loading=\"eager\" decoding=\"async\" fetchpriority=\"high\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></a></p>\n<h3>What does the output look like?</h3>\n<p><strong>Source document chunk:</strong></p>\n<p>The thermal design power (TDP) of the H100 GPU is 700W in SXM form factor. The cooling solution must maintain junction temperature below 83°C under sustained workloads. Liquid cooling is recommended for dense deployments exceeding 4 GPUs per node, as air cooling cannot dissipate sufficient heat in standard 2U chassis configurations.</p>\n<p><strong>Generated QA pairs:</strong></p>\n<pre><code class=\"language-json\"><span class=\"code-snippet\" data-language=\"JSON\"><span class=\"syntax-source syntax-json\"><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>question<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>What cooling approach is recommended when deploying more than 4 H100 GPUs per server node?<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>answer<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>Liquid cooling is recommended for dense deployments exceeding 4 GPUs per node, as air cooling cannot dissipate sufficient heat in standard 2U chassis configurations.<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>query_type<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>contextual<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>reasoning_type<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>factual<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>question_complexity<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-constant syntax-numeric syntax-json\">3</span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>segment_ids<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-meta syntax-structure syntax-array syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-array syntax-begin syntax-json\">[</span><span class=\"syntax-constant syntax-numeric syntax-json\">1</span><span class=\"syntax-punctuation syntax-section syntax-array syntax-end syntax-json\">]</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>quality_score<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-constant syntax-numeric syntax-json\">8.5</span>\n</span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span>\n</span></span></code></pre>\n<pre><code class=\"language-json\"><span class=\"code-snippet\" data-language=\"JSON\"><span class=\"syntax-source syntax-json\"><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>question<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>How does the 700W TDP of the H100 SXM constrain the choice between air and liquid cooling in multi-GPU configurations?<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>answer<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>The 700W TDP generates substantial heat that must be dissipated to keep junction temperatures below 83°C. In dense configurations exceeding 4 GPUs per node, air cooling in standard 2U chassis cannot handle this thermal load, making liquid cooling necessary.<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>query_type<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>multi_hop<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>reasoning_type<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>causal<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>question_complexity<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-constant syntax-numeric syntax-json\">4</span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>segment_ids<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-meta syntax-structure syntax-array syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-array syntax-begin syntax-json\">[</span><span class=\"syntax-constant syntax-numeric syntax-json\">1</span><span class=\"syntax-punctuation syntax-separator syntax-array syntax-json\">,</span> <span class=\"syntax-constant syntax-numeric syntax-json\">2</span><span class=\"syntax-punctuation syntax-section syntax-array syntax-end syntax-json\">]</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>hop_count<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-constant syntax-numeric syntax-json\">2</span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>quality_score<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-constant syntax-numeric syntax-json\">9.0</span>\n</span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span>\n</span></span></code></pre>\n<p>Notice the difference: the first question is a simple factual lookup. The second requires multi-hop, causal reasoning. The pipeline generates both types, with configurable complexity levels (2–5) and hop counts (1–3). Each QA pair then undergoes quality evaluation, receiving sub-scores for relevance, accuracy, context support, and clarity, along with an overall score. Only pairs that meet the threshold are included in training.</p>\n<h2>⛏️ Step 2: Mine Hard Negatives (and Why They Matter)</h2>\n<p>If you train an embedding model with only positive pairs (query + correct document), it learns to distinguish obviously different documents but fails on the hard cases — passages that <em>look</em> relevant but are not the right answer. In a real retrieval system, these near-misses are exactly the documents that cause bad answers. Hard negative mining finds these confusing passages so the model can learn to tell them apart.</p>\n<pre><code class=\"language-shell\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed prep<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default</span>\n</span></span></code></pre>\n<p>The above command runs three sub-steps automatically:</p>\n<h3>2a. Train / Validation / Test Split</h3>\n<p>The generated QA pairs are split into training (80%) and test (20%) sets. The test set is formatted as a <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/beir-cellar/beir\">BEIR</a>-compatible benchmark for standardized evaluation in Step 5.</p>\n<h3>2b. Hard Negative Mining</h3>\n<p>Using the base embedding model, the pipeline:</p>\n<ol>\n<li>Embeds every query and every passage in the corpus.</li>\n<li>Computes similarity between each query and all passages.</li>\n<li>Masks out each query's labeled positive documents.</li>\n<li>Applies a margin filter: any non-positive document scoring <em>above</em> 95% of the minimum positive score is eliminated. This exclusion zone guards against false negatives — unlabeled passages that are so close to the positive they may actually be relevant.</li>\n<li>From the surviving candidates, selects the top-k highest-scoring documents as hard negatives (5 per query by default).</li>\n</ol>\n<p>The result: hard negatives are the most similar non-positive passages that still fall safely below the positive-score ceiling. They are passages the current model considers highly relevant but that are not the labeled answer.</p>\n<p><strong>Why this works</strong>: Training on easy negatives (completely unrelated passages) teaches the model nothing new. Training on hard negatives forces it to learn the subtle distinctions that matter in your domain. For example, in a medical corpus, a question about &quot;metformin dosage for Type 2 diabetes&quot; might have hard negatives about &quot;metformin side effects&quot; or &quot;insulin dosage for Type 1 diabetes&quot; — close but critically different. The 95% margin ceiling prevents the miner from selecting passages that are too close to the positive, which could actually be correct answers that simply weren't labeled during SDG.</p>\n<h3>2c. Multi-Hop Unrolling</h3>\n<p>Multi-hop questions reference multiple positive documents. For example, a question like <em>&quot;How does the thermal management system in Section 3.2 relate to the power constraints described in Section 5.1?&quot;</em> has two positive passages.</p>\n<p>Unrolling creates one training example per (query, positive document) pair, so the contrastive loss sees each positive independently. A question with 2 positive documents becomes 2 training examples, each with the same hard negatives but a different positive.</p>\n<p>The final output is a training-ready JSON file:</p>\n<pre><code class=\"language-json\"><span class=\"code-snippet\" data-language=\"JSON\"><span class=\"syntax-source syntax-json\"><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>question_id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>q42_0<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>question<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>How does the thermal management system in Section 3.2 relate to the power constraints described in Section 5.1?<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>pos_doc<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-meta syntax-structure syntax-array syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-array syntax-begin syntax-json\">[</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>Section 3.2<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-section syntax-array syntax-end syntax-json\">]</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>neg_doc<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-meta syntax-structure syntax-array syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-array syntax-begin syntax-json\">[</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>d_x7y8z9<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-array syntax-json\">,</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>d_m4n5o6<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-array syntax-json\">,</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>d_p1q2r3<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-array syntax-json\">,</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>d_s4t5u6<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-array syntax-json\">,</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>d_v7w8x9<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-section syntax-array syntax-end syntax-json\">]</span></span>\n</span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span>, \n<span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>question_id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>q42_1<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>question<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>How does the thermal management system in Section 3.2 relate to the power constraints described in Section 5.1?<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>pos_doc<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-meta syntax-structure syntax-array syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-array syntax-begin syntax-json\">[</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>Section 5.1<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-section syntax-array syntax-end syntax-json\">]</span></span><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json\">,</span></span>\n  <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>neg_doc<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-meta syntax-structure syntax-array syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-array syntax-begin syntax-json\">[</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>d_x7y8z9<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-array syntax-json\">,</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>d_m4n5o6<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-array syntax-json\">,</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>d_p1q2r3<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-array syntax-json\">,</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>d_s4t5u6<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-array syntax-json\">,</span> <span class=\"syntax-meta syntax-structure syntax-dictionary syntax-json\"><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json\">{</span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json\"><span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>id<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json\"><span class=\"syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json\">:</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-json\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json\">&quot;</span>d_v7w8x9<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-json\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span><span class=\"syntax-punctuation syntax-section syntax-array syntax-end syntax-json\">]</span></span>\n</span><span class=\"syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json\">}</span></span>\n</span></span></code></pre>\n<h2>🔍 Step 3: Understand Multi-Hop Questions and Why They Improve Retrieval</h2>\n<p>Standard embedding fine-tuning generates one question per passage and trains the model to match them. This works for simple factual lookups, but real users ask complex questions that span multiple documents or sections. If the model has only seen single-hop training data, it will struggle to retrieve <em>all</em> the relevant passages for these complex queries.</p>\n<p>The SDG pipeline generates questions at 1 to 3 hops by default:</p>\n<ul>\n<li><strong>1-hop:</strong> &quot;What is the TDP of the H100 SXM?&quot; — answered by a single passage.</li>\n<li><strong>2-hop:</strong> &quot;How does the H100's TDP relate to cooling requirements in dense deployments?&quot; — requires connecting information from two passages.</li>\n<li><strong>3-hop:</strong> &quot;Given the TDP, cooling constraints, and rack density limits, what is the maximum number of H100 GPUs deployable in a standard data center row?&quot; — synthesizes three passages.</li>\n</ul>\n<p>Each hop is tracked with its own context summary and segment IDs, so the training data preserves the full reasoning chain. After unrolling (Step 2c), each (question, relevant passage) pair becomes an independent training signal, teaching the model that <em>all</em> of these passages are relevant to the multi-hop query.</p>\n<p>The fine-tuned model learns to retrieve contextually related documents, not just lexically similar ones.</p>\n<h2>🧠 Step 4: Fine-Tune the Embedding Model</h2>\n<pre><code class=\"language-shell\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed finetune<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default</span>\n</span></span></code></pre>\n<h3>How contrastive learning works</h3>\n<p>The training uses a <strong>biencoder architecture</strong> with <strong>contrastive loss</strong>.</p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/ntYmgzatUK_Sfn35GCdve.png\"><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/ntYmgzatUK_Sfn35GCdve.png\" alt=\"contrastive_learning\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></a></p>\n<p>The temperature of 0.02 is deliberately aggressive, it produces a very sharp probability distribution. This works well because the hard negatives from Step 2 are high-quality: they are genuinely confusing passages that the model needs strong gradients to learn to distinguish.</p>\n<h3>Key hyperparameters</h3>\n<table>\n<thead>\n<tr>\n<th>Parameter</th>\n<th>Default</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Epochs</td>\n<td>3</td>\n<td><strong>Default is tuned for the small example dataset; for real-world data, 1–2 epochs is usually sufficient</strong> to avoid overfitting.</td>\n</tr>\n<tr>\n<td>Learning rate</td>\n<td>1e-5</td>\n<td>Tuning: try double and half of the default value</td>\n</tr>\n<tr>\n<td>Learning rate warmup steps</td>\n<td>5</td>\n<td>Set to 5-10% of total steps of finetune to have better early training stability</td>\n</tr>\n<tr>\n<td>Global batch size</td>\n<td>128</td>\n<td></td>\n</tr>\n<tr>\n<td>Passages per query</td>\n<td>5</td>\n<td>1 positive + 4 hard negatives</td>\n</tr>\n</tbody>\n</table>\n<h3>Checkpoint frequency</h3>\n<p>If <code>ckpt_every_steps</code> is omitted from the config, checkpoint frequency is set automatically:</p>\n<ul>\n<li><strong>Map-style datasets</strong> (known length): defaults to once per epoch.</li>\n<li><strong>Iterable datasets</strong> (unknown length): defaults to twice during training.</li>\n</ul>\n<p>This means you can start with a small corpus (50–100 documents) for a quick proof-of-concept and scale up later without manually tuning checkpoint settings.</p>\n<h2>📈 Step 5: Measure the Improvement</h2>\n<p>Did fine-tuning actually help? Let’s find out by running a standardized evaluation comparing the base model against the fine-tuned checkpoint on the held-out test set:</p>\n<pre><code class=\"language-shell\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed eval<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default</span>\n</span></span></code></pre>\n<p>The evaluation uses the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/beir-cellar/beir\">BEIR framework</a> and computes four standard information retrieval metrics at k = 1, 5, 10, and 100:</p>\n<ol>\n<li>nDCG@k: Ranking quality — are the best documents ranked highest?</li>\n<li>Recall@k: Coverage — what fraction of relevant documents appear in the top k?</li>\n<li>Precision@k: Accuracy — what fraction of the top k results are actually relevant?</li>\n<li>MAP@k: Average precision across all queries</li>\n</ol>\n<p>A successful fine-tune typically results in a 10% improvement in nDCG@10 and Recall@10 within &lt;1 day.</p>\n<p><strong>Results using Retrieval Synthetic NVDocs:</strong></p>\n<pre><code><span class=\"code-snippet\">📊 Comparison (Base -&gt; Fine-tuned)\n============================================================\n  NDCG:\n    NDCG@1: 0.55178 → 0.60796 (+0.05618, +10.2%)\n    NDCG@5: 0.51894 → 0.57689 (+0.05795, +11.2%)\n    NDCG@10: 0.55506 → 0.61559 (+0.06053, +10.9%)\n    NDCG@100: 0.60617 → 0.66567 (+0.05950, +9.8%)\n  Recall:\n    Recall@1: 0.28478 → 0.31547 (+0.03069, +10.8%)\n    Recall@5: 0.54486 → 0.60288 (+0.05802, +10.6%)\n    Recall@10: 0.62979 → 0.69296 (+0.06317, +10.0%)\n    Recall@100: 0.81421 → 0.87020 (+0.05599, +6.9%)\n</span></code></pre>\n<h3>What if the numbers don't improve?</h3>\n<p>The pipeline makes it easy to iterate:</p>\n<ul>\n<li><strong>Low quality scores in SDG?</strong> Check your document quality — clean, well-formatted text produces better synthetic data. Try a larger and more powerful LLM.</li>\n<li><strong>Not enough training data?</strong> Add more documents to your corpus and re-run Stage 0.</li>\n<li><strong>Overfitting?</strong> The default 3 epochs is calibrated for the small example dataset; for most real-world data, <strong>1–2 epochs is enough</strong>. Also consider increasing the quality threshold to keep only the best training examples.</li>\n<li><strong>Wrong learning rate?</strong> Try double or half of the default learning rate.</li>\n</ul>\n<h3>🏆 Real-World Results: Atlassian</h3>\n<p>This recipe has been validated on real enterprise data by Atlassian. They applied this pipeline to fine-tune Llama-Nemotron-Embed-1B-v2 on a <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://zenodo.org/records/15719919/files/2025-06-23%20ThePublicJiraDataset.zip?download=1\">public Jira dataset</a> using a single NVIDIA A100 80GB GPU, following the same stages described above</p>\n<p><strong>Recall@60 jumped from 0.751 to 0.951 — a 26.7% gain.</strong><br />\nThe fine-tuned model retrieves the correct document within the top 60 results for 95.1% of queries, up from 75.1% with the base model. For a retrieval system underpinning Jira search, this directly translates into more relevant results for millions of users. Find more details in their blog post <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.atlassian.com/blog/atlassian-engineering/advancing-rovo-semantic-search\">Advancing semantic search for millions of Rovo users</a>.</p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/hsFtTLz1WfSgaWBdHof82.png\"><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/hsFtTLz1WfSgaWBdHof82.png\" alt=\"ATL result\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></a></p>\n<h2>🚀 Step 6: Export and Deploy</h2>\n<p>A PyTorch checkpoint is great for evaluation but too slow for production. The final two stages convert the model and serve it behind an API.</p>\n<h3>Export to ONNX / TensorRT</h3>\n<pre><code class=\"language-shell\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed export<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default</span>\n</span></span></code></pre>\n<p>This exports the fine-tuned checkpoint to ONNX (opset 17). Optionally, it compiles a TensorRT engine for maximum inference throughput, with configurable optimization profiles for batch size (1–64) and sequence length (3–256):</p>\n<pre><code class=\"language-shell\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell\">#</span></span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"> ONNX only (runs anywhere)</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\">\n</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed export<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default export_to_trt=false</span>\n\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell\">#</span></span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"> FP8 quantization for further speedup</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\">\n</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed export<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default quant_cfg=fp8</span>\n</span></span></code></pre>\n<h3>Deploy with NVIDIA NIM</h3>\n<p>The exported model is deployed inside an <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://developer.nvidia.com/nim\">NVIDIA NIM</a> container — a production-ready inference microservice exposing an OpenAI-compatible /v1/embeddings endpoint:</p>\n<pre><code class=\"language-shell\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed deploy<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default</span>\n</span></span></code></pre>\n<p>Once running, any client can call it:</p>\n<pre><code class=\"language-shell\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">curl</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>X</span> POST http://localhost:8000/v1/embeddings <span class=\"syntax-punctuation syntax-separator syntax-continuation syntax-line syntax-shell\">\\\n</span><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\">  -</span>H</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span>Content-Type: application/json<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span> <span class=\"syntax-punctuation syntax-separator syntax-continuation syntax-line syntax-shell\">\\\n</span><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\">  -</span>d</span> <span class=\"syntax-string syntax-quoted syntax-single syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&#39;</span>{&quot;input&quot;: [&quot;What cooling is needed for 8 H100 GPUs in a 2U chassis?&quot;],\n       &quot;model&quot;: &quot;custom&quot;,\n       &quot;input_type&quot;: &quot;query&quot;}<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&#39;</span></span></span>\n</span></span></code></pre>\n<p>Because NIM serves an OpenAI-compatible API, you can drop it into any existing RAG pipeline that uses the embeddings API format — no code changes needed.</p>\n<h3>Verify deployment accuracy</h3>\n<p>The pipeline includes a NIM accuracy verification step that runs the same BEIR evaluation against the deployed endpoint:</p>\n<pre><code class=\"language-shell\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed eval<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default eval_nim=true eval_base=false</span>\n</span></span></code></pre>\n<p>This catches any accuracy loss from the ONNX/TensorRT conversion. Metrics that match within tolerance (0.03 for @1, 0.01 for @5+) are marked with a check; deviations beyond conversion noise are flagged.</p>\n<h2>Putting It All Together</h2>\n<p>The full embedding fine-tuning pipeline can be run in six commands, from raw documents to a deployed model.</p>\n<pre><code class=\"language-shell\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell\">#</span></span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"> 1. Generate synthetic training data from your documents</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\">\n</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed sdg<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default corpus_dir=./data/my_docs</span>\n\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell\">#</span></span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"> 2. Prepare the training data (split data, mine hard negatives, unroll)</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\">\n</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed prep<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default</span>\n\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell\">#</span></span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"> 3. Fine-tune the embedding model</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\">\n</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed finetune<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default</span>\n\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell\">#</span></span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"> 4. Evaluate the base vs. fine-tuned model</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\">\n</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed eval<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default</span>\n\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell\">#</span></span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"> 5. Export the optimized model</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\">\n</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed export<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default</span>\n\n<span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell\">#</span></span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"> 6. Deploy the model</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\">\n</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">nemotron</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> embed deploy<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> default</span>\n</span></span></code></pre>\n<h3>Expected time and resources</h3>\n<table>\n<thead>\n<tr>\n<th>Stage</th>\n<th>GPU Required?</th>\n<th>Estimated Time</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>SDG</td>\n<td>No (uses API)</td>\n<td>~1 hour</td>\n<td>Varies by corpus size and API rate limit</td>\n</tr>\n<tr>\n<td>Data Prep</td>\n<td>Yes (40 GB VRAM)</td>\n<td>~5 min</td>\n<td>Hard negative mining on GPU</td>\n</tr>\n<tr>\n<td>Fine-Tune</td>\n<td>Yes (80 GB VRAM)</td>\n<td>~1 hours</td>\n<td>Varies by dataset size and epochs</td>\n</tr>\n<tr>\n<td>Eval</td>\n<td>Yes (40 GB VRAM)</td>\n<td>~5 min</td>\n<td></td>\n</tr>\n<tr>\n<td>Export</td>\n<td>Yes (40 GB VRAM)</td>\n<td>~5 min</td>\n<td>TensorRT requires NGC container</td>\n</tr>\n<tr>\n<td>Deploy</td>\n<td>Yes (40 GB VRAM)</td>\n<td>~5 min</td>\n<td>NIM container startup</td>\n</tr>\n</tbody>\n</table>\n<p><strong>Total: under a day, with most time being hands-off training.</strong> For a small corpus (~500 documents), the entire pipeline completes in about 2–3 hours.</p>\n<p>The pipeline can run end-to-end, but each stage can also be executed independently depending on your starting point. For example, if you have raw documents, you can begin with synthetic data generation (SDG), while datasets that already include hard negatives can skip earlier steps and go directly to fine-tuning. Since every stage uses standard formats such as JSON, BEIR, and ONNX, it’s easy to integrate custom components or reuse intermediate outputs in other workflows. The recipe is also flexible in how it runs, supporting execution on a local machine, inside Docker containers, or on Slurm-based clusters.</p>\n<h2>Try It Yourself</h2>\n<p>If you have domain documents and some time in your hand, you can generate your first batch of synthetic training data today! The full pipeline - from documents to a deployed, domain-adapted embedding model - runs in under a day on a single GPU. You can start with our ready-made <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/nvidia/Retrieval-Synthetic-NVDocs-v1\">nvidia/Retrieval-Synthetic-NVDocs-v1</a> dataset to try the pipeline right away. Let us know what you build.<br />\n<strong>Star the repos</strong> for <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/NVIDIA/Nemotron\">Nemotron</a>, <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/NVIDIA/NeMo-Data-Designer\">NeMo Data Designer</a> and <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/NVIDIA/NeMo-Automodel\">NeMo Automodel</a> if you find them useful.</p>\n",
  "content_markdown": "If you are building a RAG (Retrieval-Augmented Generation) system, you have likely hit this wall: Everything works… until it doesn’t. General-purpose embedding models are trained to understand the internet; not your contracts, manufacturing logs, proprietary chemical formulations or internal taxonomy. They capture broad semantic similarity, but they do not understand the fine-grained distinctions that matter in your domain. Fine-tuning an embedding model can improve the performance of your retrieval pipeline when off-the-shelf models fail to effectively capture domain-specific nuances. Despite how critical embeddings are to RAG performance, the process remains surprisingly fragmented, the skills required are specialized, and the time investment is daunting.\n\nWith a single GPU and less than a day of training time, you can transform a general-purpose embedding model into one that truly understands your domain, no manual labeling required. To help you hit the ground running, we are also releasing a ready-to-use [synthetic training dataset](https://huggingface.co/datasets/nvidia/Retrieval-Synthetic-NVDocs-v1) generated from NVIDIA's public documentation using this exact pipeline. Using this data and the recipe, we saw over 10% improvement in both Recall@10 and NDCG@10. [Atlassian](https://www.atlassian.com/) applied this recipe to fine-tune on their [JIRA dataset](https://zenodo.org/records/15719919/files/2025-06-23%20ThePublicJiraDataset.zip?download=1), increasing Recall@60 from 0.751 to 0.951, a 26% improvement - on a single GPU.\n\n### 🔗Quick Links to Dataset and Code:\n\n1. [Embedding Model](https://huggingface.co/nvidia/llama-nemotron-embed-1b-v2)\n2. [GitHub](https://github.com/NVIDIA-NeMo/Nemotron/tree/main/src/nemotron/recipes/embed)\n3. [Synthetic dataset on NVIDIA’s public documents](https://huggingface.co/datasets/nvidia/Retrieval-Synthetic-NVDocs-v1)\n\n### 🧑💻Open Source Projects Recipe Integrates:\n\n1. [NeMo Data Designer](https://github.com/NVIDIA-NeMo/DataDesigner) for synthetic data generation\n2. [NeMo Automodel](https://github.com/NVIDIA/NeMo-Automodel) for embedding model training\n3. [BEIR](https://github.com/beir-cellar/beir) for Information retrieval evaluation\n4. [NeMo Export-Deploy](https://github.com/NVIDIA/NeMo-Export-Deploy) for ONNX/TensorRT conversion\n5. [NVIDIA NIM](https://developer.nvidia.com/nim) for production inference serving\n\n### 📋Prerequisites:\n\n- A directory of domain documents (text files - .txt, .md, or similar)\n- A valid NVIDIA API key (free at [build.nvidia.com](https://build.nvidia.com))\n- NVIDIA Ampere GPU or newer with at least 80GB memory (with [Compute Capability](https://developer.nvidia.com/cuda-gpus) >= 8.0)\n  - This tutorial has been tested on 1xA100 (80GB), and 1xH100 (80GB)\n\nBy the end of this post, you’ll know how to:\\\n📄 Generate training data from domain documents without labeled data\\\n🎯 Use hard negative mining for effective contrastive training\\\n🔗 Improve embedding quality with multi-hop queries\\\n⚙️ Fine-tune a bi-encoder embedding model\\\n📊 Evaluate whether fine-tuning improves retrieval\\\n🚀 Deploy the fine-tuned model in your pipeline\n\n## ⚙️Setup\n\nIn this tutorial, we will finetune the base model [Llama-Nemotron-Embed-1B-v2](https://huggingface.co/nvidia/llama-nemotron-embed-1b-v2) - a 1-billion-parameter embedding model that balances quality and inference cost. To get started, follow this [setup guide](https://github.com/NVIDIA-NeMo/Nemotron/blob/main/docs/nemotron/embed/README.md).\n\n## 📚 Step 1: Generate Training Data from Documents\n\nFine-tuning an embedding model requires thousands of (query, relevant document) pairs. Most use cases don’t have this data readily available. Creating it manually is expensive, slow, and often biased by the annotator’s personal interpretation of what’s “relevant.”\\\nInstead of labeling data by hand, you can use an LLM (nvidia/nemotron-3-nano-30b-a3b) to read your documents and automatically generate high-quality synthetic question–answer pairs.\n\n```shell\nnemotron embed sdg -c default corpus_dir=./data/my_domain_docs\n```\n\n### How does it work?\n\nBehind the scenes, this runs a four-stage synthetic data generation (SDG) pipeline powered by [NeMo Data Designer](https://github.com/NVIDIA-NeMo/DataDesigner):\n\n[![sdg_pipeline](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/84IMYChKX4twWnC4U6iCQ.png)](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/84IMYChKX4twWnC4U6iCQ.png)\n\n### What does the output look like?\n\n**Source document chunk:**\n\nThe thermal design power (TDP) of the H100 GPU is 700W in SXM form factor. The cooling solution must maintain junction temperature below 83°C under sustained workloads. Liquid cooling is recommended for dense deployments exceeding 4 GPUs per node, as air cooling cannot dissipate sufficient heat in standard 2U chassis configurations.\n\n**Generated QA pairs:**\n\n```json\n{\n  \"question\": \"What cooling approach is recommended when deploying more than 4 H100 GPUs per server node?\",\n  \"answer\": \"Liquid cooling is recommended for dense deployments exceeding 4 GPUs per node, as air cooling cannot dissipate sufficient heat in standard 2U chassis configurations.\",\n  \"query_type\": \"contextual\",\n  \"reasoning_type\": \"factual\",\n  \"question_complexity\": 3,\n  \"segment_ids\": [1],\n  \"quality_score\": 8.5\n}\n```\n\n```json\n{\n  \"question\": \"How does the 700W TDP of the H100 SXM constrain the choice between air and liquid cooling in multi-GPU configurations?\",\n  \"answer\": \"The 700W TDP generates substantial heat that must be dissipated to keep junction temperatures below 83°C. In dense configurations exceeding 4 GPUs per node, air cooling in standard 2U chassis cannot handle this thermal load, making liquid cooling necessary.\",\n  \"query_type\": \"multi_hop\",\n  \"reasoning_type\": \"causal\",\n  \"question_complexity\": 4,\n  \"segment_ids\": [1, 2],\n  \"hop_count\": 2,\n  \"quality_score\": 9.0\n}\n```\n\nNotice the difference: the first question is a simple factual lookup. The second requires multi-hop, causal reasoning. The pipeline generates both types, with configurable complexity levels (2–5) and hop counts (1–3). Each QA pair then undergoes quality evaluation, receiving sub-scores for relevance, accuracy, context support, and clarity, along with an overall score. Only pairs that meet the threshold are included in training.\n\n## ⛏️ Step 2: Mine Hard Negatives (and Why They Matter)\n\nIf you train an embedding model with only positive pairs (query + correct document), it learns to distinguish obviously different documents but fails on the hard cases — passages that *look* relevant but are not the right answer. In a real retrieval system, these near-misses are exactly the documents that cause bad answers. Hard negative mining finds these confusing passages so the model can learn to tell them apart.\n\n```shell\nnemotron embed prep -c default\n```\n\nThe above command runs three sub-steps automatically:\n\n### 2a. Train / Validation / Test Split\n\nThe generated QA pairs are split into training (80%) and test (20%) sets. The test set is formatted as a [BEIR](https://github.com/beir-cellar/beir)-compatible benchmark for standardized evaluation in Step 5.\n\n### 2b. Hard Negative Mining\n\nUsing the base embedding model, the pipeline:\n\n1. Embeds every query and every passage in the corpus.\n2. Computes similarity between each query and all passages.\n3. Masks out each query's labeled positive documents.\n4. Applies a margin filter: any non-positive document scoring *above* 95% of the minimum positive score is eliminated. This exclusion zone guards against false negatives — unlabeled passages that are so close to the positive they may actually be relevant.\n5. From the surviving candidates, selects the top-k highest-scoring documents as hard negatives (5 per query by default).\n\nThe result: hard negatives are the most similar non-positive passages that still fall safely below the positive-score ceiling. They are passages the current model considers highly relevant but that are not the labeled answer.\n\n**Why this works**: Training on easy negatives (completely unrelated passages) teaches the model nothing new. Training on hard negatives forces it to learn the subtle distinctions that matter in your domain. For example, in a medical corpus, a question about \"metformin dosage for Type 2 diabetes\" might have hard negatives about \"metformin side effects\" or \"insulin dosage for Type 1 diabetes\" — close but critically different. The 95% margin ceiling prevents the miner from selecting passages that are too close to the positive, which could actually be correct answers that simply weren't labeled during SDG.\n\n### 2c. Multi-Hop Unrolling\n\nMulti-hop questions reference multiple positive documents. For example, a question like *\"How does the thermal management system in Section 3.2 relate to the power constraints described in Section 5.1?\"* has two positive passages.\n\nUnrolling creates one training example per (query, positive document) pair, so the contrastive loss sees each positive independently. A question with 2 positive documents becomes 2 training examples, each with the same hard negatives but a different positive.\n\nThe final output is a training-ready JSON file:\n\n```json\n{\n  \"question_id\": \"q42_0\",\n  \"question\": \"How does the thermal management system in Section 3.2 relate to the power constraints described in Section 5.1?\",\n  \"pos_doc\": [{\"id\": \"Section 3.2\"}],\n  \"neg_doc\": [{\"id\": \"d_x7y8z9\"}, {\"id\": \"d_m4n5o6\"}, {\"id\": \"d_p1q2r3\"}, {\"id\": \"d_s4t5u6\"}, {\"id\": \"d_v7w8x9\"}]\n}, \n{\n  \"question_id\": \"q42_1\",\n  \"question\": \"How does the thermal management system in Section 3.2 relate to the power constraints described in Section 5.1?\",\n  \"pos_doc\": [{\"id\": \"Section 5.1\"}],\n  \"neg_doc\": [{\"id\": \"d_x7y8z9\"}, {\"id\": \"d_m4n5o6\"}, {\"id\": \"d_p1q2r3\"}, {\"id\": \"d_s4t5u6\"}, {\"id\": \"d_v7w8x9\"}]\n}\n```\n\n## 🔍 Step 3: Understand Multi-Hop Questions and Why They Improve Retrieval\n\nStandard embedding fine-tuning generates one question per passage and trains the model to match them. This works for simple factual lookups, but real users ask complex questions that span multiple documents or sections. If the model has only seen single-hop training data, it will struggle to retrieve *all* the relevant passages for these complex queries.\n\nThe SDG pipeline generates questions at 1 to 3 hops by default:\n\n- **1-hop:** \"What is the TDP of the H100 SXM?\" — answered by a single passage.\n- **2-hop:** \"How does the H100's TDP relate to cooling requirements in dense deployments?\" — requires connecting information from two passages.\n- **3-hop:** \"Given the TDP, cooling constraints, and rack density limits, what is the maximum number of H100 GPUs deployable in a standard data center row?\" — synthesizes three passages.\n\nEach hop is tracked with its own context summary and segment IDs, so the training data preserves the full reasoning chain. After unrolling (Step 2c), each (question, relevant passage) pair becomes an independent training signal, teaching the model that *all* of these passages are relevant to the multi-hop query.\n\nThe fine-tuned model learns to retrieve contextually related documents, not just lexically similar ones.\n\n## 🧠 Step 4: Fine-Tune the Embedding Model\n\n```shell\nnemotron embed finetune -c default\n```\n\n### How contrastive learning works\n\nThe training uses a **biencoder architecture** with **contrastive loss**.\n\n[![contrastive_learning](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/ntYmgzatUK_Sfn35GCdve.png)](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/ntYmgzatUK_Sfn35GCdve.png)\n\nThe temperature of 0.02 is deliberately aggressive, it produces a very sharp probability distribution. This works well because the hard negatives from Step 2 are high-quality: they are genuinely confusing passages that the model needs strong gradients to learn to distinguish.\n\n### Key hyperparameters\n\n| Parameter                  | Default | Notes                                                                                                                           |\n| -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| Epochs                     | 3       | **Default is tuned for the small example dataset; for real-world data, 1–2 epochs is usually sufficient** to avoid overfitting. |\n| Learning rate              | 1e-5    | Tuning: try double and half of the default value                                                                                |\n| Learning rate warmup steps | 5       | Set to 5-10% of total steps of finetune to have better early training stability                                                 |\n| Global batch size          | 128     |                                                                                                                                 |\n| Passages per query         | 5       | 1 positive + 4 hard negatives                                                                                                   |\n\n### Checkpoint frequency\n\nIf `ckpt_every_steps` is omitted from the config, checkpoint frequency is set automatically:\n\n- **Map-style datasets** (known length): defaults to once per epoch.\n- **Iterable datasets** (unknown length): defaults to twice during training.\n\nThis means you can start with a small corpus (50–100 documents) for a quick proof-of-concept and scale up later without manually tuning checkpoint settings.\n\n## 📈 Step 5: Measure the Improvement\n\nDid fine-tuning actually help? Let’s find out by running a standardized evaluation comparing the base model against the fine-tuned checkpoint on the held-out test set:\n\n```shell\nnemotron embed eval -c default\n```\n\nThe evaluation uses the [BEIR framework](https://github.com/beir-cellar/beir) and computes four standard information retrieval metrics at k = 1, 5, 10, and 100:\n\n1. nDCG@k: Ranking quality — are the best documents ranked highest?\n2. Recall@k: Coverage — what fraction of relevant documents appear in the top k?\n3. Precision@k: Accuracy — what fraction of the top k results are actually relevant?\n4. MAP@k: Average precision across all queries\n\nA successful fine-tune typically results in a 10% improvement in nDCG@10 and Recall@10 within <1 day.\n\n**Results using Retrieval Synthetic NVDocs:**\n\n```\n📊 Comparison (Base -> Fine-tuned)\n============================================================\n  NDCG:\n    NDCG@1: 0.55178 → 0.60796 (+0.05618, +10.2%)\n    NDCG@5: 0.51894 → 0.57689 (+0.05795, +11.2%)\n    NDCG@10: 0.55506 → 0.61559 (+0.06053, +10.9%)\n    NDCG@100: 0.60617 → 0.66567 (+0.05950, +9.8%)\n  Recall:\n    Recall@1: 0.28478 → 0.31547 (+0.03069, +10.8%)\n    Recall@5: 0.54486 → 0.60288 (+0.05802, +10.6%)\n    Recall@10: 0.62979 → 0.69296 (+0.06317, +10.0%)\n    Recall@100: 0.81421 → 0.87020 (+0.05599, +6.9%)\n```\n\n### What if the numbers don't improve?\n\nThe pipeline makes it easy to iterate:\n\n- **Low quality scores in SDG?** Check your document quality — clean, well-formatted text produces better synthetic data. Try a larger and more powerful LLM.\n- **Not enough training data?** Add more documents to your corpus and re-run Stage 0.\n- **Overfitting?** The default 3 epochs is calibrated for the small example dataset; for most real-world data, **1–2 epochs is enough**. Also consider increasing the quality threshold to keep only the best training examples.\n- **Wrong learning rate?** Try double or half of the default learning rate.\n\n### 🏆 Real-World Results: Atlassian\n\nThis recipe has been validated on real enterprise data by Atlassian. They applied this pipeline to fine-tune Llama-Nemotron-Embed-1B-v2 on a [public Jira dataset](https://zenodo.org/records/15719919/files/2025-06-23%20ThePublicJiraDataset.zip?download=1) using a single NVIDIA A100 80GB GPU, following the same stages described above\n\n**Recall@60 jumped from 0.751 to 0.951 — a 26.7% gain.**\\\nThe fine-tuned model retrieves the correct document within the top 60 results for 95.1% of queries, up from 75.1% with the base model. For a retrieval system underpinning Jira search, this directly translates into more relevant results for millions of users. Find more details in their blog post [Advancing semantic search for millions of Rovo users](https://www.atlassian.com/blog/atlassian-engineering/advancing-rovo-semantic-search).\n\n[![ATL result](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/hsFtTLz1WfSgaWBdHof82.png)](https://cdn-uploads.huggingface.co/production/uploads/662a9c2154a9f1037104a075/hsFtTLz1WfSgaWBdHof82.png)\n\n## 🚀 Step 6: Export and Deploy\n\nA PyTorch checkpoint is great for evaluation but too slow for production. The final two stages convert the model and serve it behind an API.\n\n### Export to ONNX / TensorRT\n\n```shell\nnemotron embed export -c default\n```\n\nThis exports the fine-tuned checkpoint to ONNX (opset 17). Optionally, it compiles a TensorRT engine for maximum inference throughput, with configurable optimization profiles for batch size (1–64) and sequence length (3–256):\n\n```shell\n# ONNX only (runs anywhere)\nnemotron embed export -c default export_to_trt=false\n\n# FP8 quantization for further speedup\nnemotron embed export -c default quant_cfg=fp8\n```\n\n### Deploy with NVIDIA NIM\n\nThe exported model is deployed inside an [NVIDIA NIM](https://developer.nvidia.com/nim) container — a production-ready inference microservice exposing an OpenAI-compatible /v1/embeddings endpoint:\n\n```shell\nnemotron embed deploy -c default\n```\n\nOnce running, any client can call it:\n\n```shell\ncurl -X POST http://localhost:8000/v1/embeddings \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"input\": [\"What cooling is needed for 8 H100 GPUs in a 2U chassis?\"],\n       \"model\": \"custom\",\n       \"input_type\": \"query\"}'\n```\n\nBecause NIM serves an OpenAI-compatible API, you can drop it into any existing RAG pipeline that uses the embeddings API format — no code changes needed.\n\n### Verify deployment accuracy\n\nThe pipeline includes a NIM accuracy verification step that runs the same BEIR evaluation against the deployed endpoint:\n\n```shell\nnemotron embed eval -c default eval_nim=true eval_base=false\n```\n\nThis catches any accuracy loss from the ONNX/TensorRT conversion. Metrics that match within tolerance (0.03 for @1, 0.01 for @5+) are marked with a check; deviations beyond conversion noise are flagged.\n\n## Putting It All Together\n\nThe full embedding fine-tuning pipeline can be run in six commands, from raw documents to a deployed model.\n\n```shell\n# 1. Generate synthetic training data from your documents\nnemotron embed sdg -c default corpus_dir=./data/my_docs\n\n# 2. Prepare the training data (split data, mine hard negatives, unroll)\nnemotron embed prep -c default\n\n# 3. Fine-tune the embedding model\nnemotron embed finetune -c default\n\n# 4. Evaluate the base vs. fine-tuned model\nnemotron embed eval -c default\n\n# 5. Export the optimized model\nnemotron embed export -c default\n\n# 6. Deploy the model\nnemotron embed deploy -c default\n```\n\n### Expected time and resources\n\n| Stage     | GPU Required?    | Estimated Time | Notes                                    |\n| --------- | ---------------- | -------------- | ---------------------------------------- |\n| SDG       | No (uses API)    | \\~1 hour       | Varies by corpus size and API rate limit |\n| Data Prep | Yes (40 GB VRAM) | \\~5 min        | Hard negative mining on GPU              |\n| Fine-Tune | Yes (80 GB VRAM) | \\~1 hours      | Varies by dataset size and epochs        |\n| Eval      | Yes (40 GB VRAM) | \\~5 min        |                                          |\n| Export    | Yes (40 GB VRAM) | \\~5 min        | TensorRT requires NGC container          |\n| Deploy    | Yes (40 GB VRAM) | \\~5 min        | NIM container startup                    |\n\n**Total: under a day, with most time being hands-off training.** For a small corpus (~500 documents), the entire pipeline completes in about 2–3 hours.\n\nThe pipeline can run end-to-end, but each stage can also be executed independently depending on your starting point. For example, if you have raw documents, you can begin with synthetic data generation (SDG), while datasets that already include hard negatives can skip earlier steps and go directly to fine-tuning. Since every stage uses standard formats such as JSON, BEIR, and ONNX, it’s easy to integrate custom components or reuse intermediate outputs in other workflows. The recipe is also flexible in how it runs, supporting execution on a local machine, inside Docker containers, or on Slurm-based clusters.\n\n## Try It Yourself\n\nIf you have domain documents and some time in your hand, you can generate your first batch of synthetic training data today! The full pipeline - from documents to a deployed, domain-adapted embedding model - runs in under a day on a single GPU. You can start with our ready-made [nvidia/Retrieval-Synthetic-NVDocs-v1](https://huggingface.co/datasets/nvidia/Retrieval-Synthetic-NVDocs-v1) dataset to try the pipeline right away. Let us know what you build.\\\n**Star the repos** for [Nemotron](https://github.com/NVIDIA/Nemotron), [NeMo Data Designer](https://github.com/NVIDIA/NeMo-Data-Designer) and [NeMo Automodel](https://github.com/NVIDIA/NeMo-Automodel) if you find them useful.\n",
  "dateCreated": "2026-10-04T12:50:30.804479567+00:00",
  "date_created": "2026-03-20T19:38:16+00:00",
  "date_modified": "2026-03-20T19:38:16+00:00",
  "date_published": "2026-03-20T19:38:16+00:00",
  "description": "If you are building a RAG (Retrieval-Augmented Generation) system, you have likely hit this wall: Everything works… until it doesn’t. General-purpose embedding models are trained to understand the internet; not your contracts,…",
  "external_url": "https://huggingface.co/blog/nvidia/domain-specific-embedding-finetune",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/huggingface-co/2026/03/2026-03-20-build-a-domain-specific-embedding-model-in-under-a-day.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/huggingface-co/2026/03/2026-03-20-build-a-domain-specific-embedding-model-in-under-a-day.md"
  },
  "id": "urn:aggr:item:174ea57bfa7805fd79f8d7efbcabd09873261343",
  "isBasedOn": {
    "@id": "https://huggingface.co/blog/nvidia/domain-specific-embedding-finetune"
  },
  "mainEntity": {
    "@id": "https://huggingface.co/blog/nvidia/domain-specific-embedding-finetune",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-03-20-build-a-domain-specific-embedding-model-in-under-a-day/#webpage"
    },
    "dateModified": "2026-03-20T19:38:16+00:00",
    "datePublished": "2026-03-20T19:38:16+00:00",
    "headline": "Build a Domain-Specific Embedding Model in Under a Day",
    "timeRequired": "PT13M",
    "url": "https://huggingface.co/blog/nvidia/domain-specific-embedding-finetune",
    "wordCount": 2706
  },
  "name": "Archived snapshot: Build a Domain-Specific Embedding Model in Under a Day",
  "preview": null,
  "reading_minutes": 13,
  "replicated_at": null,
  "source": "huggingface-co",
  "source_name": "Hugging Face - Blog",
  "summary": null,
  "tags": [],
  "temporalCoverage": "2026-10-04T12:50:30.804479567+00:00",
  "title": "Build a Domain-Specific Embedding Model in Under a Day",
  "url": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-03-20-build-a-domain-specific-embedding-model-in-under-a-day/",
  "word_count": 2706
}