Embedding Model Customization
Fine-tune Nemotron 3 Embed 1B on domain retrieval data and score it against the base checkpoint with BEIR nDCG and Recall. This is the NeMo Platform mapping of the Nemotron embed recipe.
Pre-trained embeddings handle general retrieval well but underperform on specialized vocabulary, document structure, and query phrasing. Fine-tuning adapts the model to your corpus. Expect larger gains where the base model has the least prior exposure, such as legal, biomedical, and internal engineering documents, and smaller ones on corpora close to its pre-training mix.
This notebook starts from NVIDIA’s published NVDocs Stage 0 dump instead of generating Q&A pairs, so the first job it runs is hard-negative mining.
Deployment precedes evaluation because retrieve-eval scores Inference Gateway providers rather than filesets, so both models have to be served first. Export is a setting on the training job rather than a stage of its own.
Dataset format
Stage 1 writes contrastive rows as JSONL:
neg_doc is a non-empty list of hard negatives: passages that score close to the positive but do not answer the query. Mining fills it during Stage 1 against a fileset-backed encoder, and Automodel samples four of them per query (train_n_passages: 5).
Prerequisites
- A running NeMo Platform at
NMP_BASE_URL, defaulthttp://localhost:8080. See the Quickstart. - The Python SDK:
pip install "nemo-platform[all]", ormake bootstrapfrom the repo root. - An NGC API key for the
nvcr.ioNIM images. - GPU capacity: 80 GB for training, about 40 GB for mining and evaluation. Serving the base and tuned NIMs side by side takes two GPUs.
- A fileset-backed encoder entity, created in the next section. Models that Inference Gateway discovers from a running endpoint have
fileset: nulland can be neither trained nor used as the mining encoder.
1. Initialize the SDK
The nemo CLI reads NMP_BASE_URL too, and --base-url overrides it per command. The NGC key becomes a Platform secret so the cluster can pull NIM images:
2. Register the trainable base (Nemotron 3 Embed 1B)
Mining and training both read weights from a fileset, so register the Hugging Face checkpoint as a fileset and back a model entity with it.
Equivalent CLI:
The entity is ready once nemo models get reports a non-null fileset and a populated spec. For a gated repo, create the token first with nemo secrets create hf-token --value "$HF_TOKEN" and add "token_secret":"hf-token" to --storage.
3. Stage 1: mine hard negatives from the NVDocs dump
Retrieval-Synthetic-NVDocs-v1 is NVIDIA’s published Stage 0 output, so Stage 1 reads it directly and no LLM is called. Point sdg_input at the dump, turn on mining, and name the encoder entity from the previous section.
A hard negative is a corpus passage that the current encoder ranks near the positive even though it does not answer the query. Mining embeds every training query and the whole corpus on GPU, then keeps the highest-scoring passages below min_positive_score * hard_neg_margin. Start with hard_neg_margin: 0.95 and hard_negatives_to_mine: 5; Stage 2 uses four of those candidates with train_n_passages: 5. Raise the margin toward 1.0 for harder negatives, or lower it to 0.85–0.90 to reduce false negatives. Change it only if Stage 4 metrics plateau. See the Nemotron recipe’s hard-negative mining guidance.
To generate Q&A pairs from your own corpus first, run Generate retrieval training data and pass that job’s artifacts fileset as sdg_input.
Equivalent CLI:
Stage 1 writes training.jsonl and eval_beir/ under its job result, addressed as workspace/fileset#path. In 0.6, Automodel dataset.training and retrieve-eval dataset take a fileset name without a path fragment, so the next cell copies both artifacts to the root of a fileset of its own. Release 0.7 resolves workspace/fileset#path directly and the copy is no longer needed.
The copy also drops duplicate (query-id, corpus-id) rows from eval_beir/qrels/test.tsv, which the BEIR loader rejects.
Freeze the resulting fileset and reuse it for every run. Scoring two runs on different splits measures data variance rather than model quality.
Equivalent CLI, where RESULT_PATH is the path fragment of the artifacts result’s artifact_url:
Deduplicate ./artifacts/eval_beir/qrels/test.tsv before the upload; the cell below does it inline.
4. Stage 2: Automodel bi_encoder
model and dataset.training are independent inputs. The entity supplies the encoder weights; the fileset supplies the contrastive rows, with training.jsonl at its root. To train on an existing triplet dataset instead, build that fileset in Train on SPECTER or triplet JSONL and point dataset.training at it — SPECTER carries no eval_beir, so keep the NVDocs artifacts fileset for Stage 4.
These values match the Nemotron embed recipe, except for epochs: the recipe’s 3 is calibrated for its example dataset, and NVDocs-scale corpora train for 1–2.
export.primary: hf writes the Hugging Face checkpoint to the fileset root, where Retriever NIM 2.2.0 looks for it, and keeps ONNX under alternates/onnx. Unmerged lora is rejected for bi_encoder, since an embedding NIM serves a full checkpoint and cannot load a standalone adapter.
Equivalent CLI:
training.recipe, training.finetuning_type, and training.retrieval.export.primary have no per-flag form; supply them through --spec or --spec-file.
Interpreting contrastive loss
A healthy run drops sharply over the first 20–30% of training, then flattens toward a stable floor with validation loss tracking close behind. Validation loss rising while training loss falls is overfitting: cut epochs. Spikes or NaN mean the learning rate is too high. A curve that never moves points at the learning rate being too low or at the data itself.
There is no target loss value, since the absolute number depends on batch size, negative count, and temperature. Loss is also not retrieval accuracy: it can keep improving while nDCG stagnates when the mined negatives are too easy. Stage 4 metrics are the ground truth.
When iterating, sweep the learning rate at 5e-6, 1e-5, and 2e-5 with everything else fixed. If the best result lands on an endpoint, extend one step further in that direction.
5. Stage 3: deploy Retriever NIM 2.2.0
The base deployment serves the weights baked into the NIM image (model_spec: {}); the tuned deployment mounts the Automodel output entity. Both need override_config.nimLegacy: false, because NIM 2.x replaced NIM_MODEL_NAME and NIM_MODEL_PATH with their NIM_ENGINE_* equivalents and rejects the retired names. Scoring both models at once occupies two GPUs; the first image pull can take several minutes.
Equivalent CLI:
retrieve-eval only scores entities with a provider attached, so both nemo models get calls must show a non-empty model_providers list before Stage 4. Training exported Hugging Face weights to the fileset root; had it exported ONNX as primary, the tuned executor would also need "additional_envs":{"NIM_ENGINE_MODEL_PATH":"alternates/hf"}.
6. Serving sanity (/v1/embeddings)
One request confirms the endpoint answers and returns 2048 dimensions. Pass input_type as query or document to match how the text will be used at retrieval time.
Equivalent CLI:
7. Stage 4: retrieve-eval on the frozen eval_beir split
retrieve-eval embeds the corpus with each model, ranks the queries, and reports nDCG@k, Recall@k, Precision@k, and MAP@k for the target and the baseline. Pass the same artifacts fileset every run.
Equivalent CLI:
target, baseline, and k have no per-flag form; supply them through --spec or --spec-file.
Interpreting results
An example result after finetuning for 3 epochs on NVDocs SDG dataset and evaluating on 20,909-query eval_beir split:
Compare the fine-tuned model against the base checkpoint in the same evaluation. The eval split, corpus size, domain, and mining depth all move the scale.
Hyperparameters
Learning rate is the most sensitive parameter, followed by epochs, warmup, and batch size.
Batch size sets how many gradient steps an epoch takes rather than how many negatives each query sees, so smaller datasets benefit from a smaller global batch. When you scale data, adding documents helps more than generating more queries per document: new documents bring new vocabulary and retrieval patterns, while extra queries cover the same passage from another angle.
Troubleshooting
neg_doc must contain at least 1 document: Stage 1 ran without mining. Re-runretrieval-preparewithenable_mining: trueand a fileset-backedmodel.- CUDA out of memory during mining: lower
mining.query_embedding_batch_sizeandmining.document_embedding_batch_size. - Empty train split: the corpus is too small. Generating your own data needs 50 documents at minimum and 500 or more for good coverage.
retrieve-evalreports nomodel_providers: the NIM is not serving the entity yet. Wait forREADYand for the gateway to attach the provider.
Clean up
Each deployment holds a GPU. Release them once evaluation finishes; the configs can stay for the next run.