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 Helix 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 Helix and a CLI context that points at it. See the Quickstart and Connect with CLI Contexts.
- The Python SDK:
pip install "nemo-helix[all]", ormake bootstrapfrom the repo root. - An NGC API key for the
nvcr.ioNIM images. - GPU capacity: 4× 80 GB for the validated training job, 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 SDK and the nemo CLI connect to the deployment in your current CLI context. To connect to another deployment, refer to Connect with CLI Contexts. These examples use the default workspace. If your current CLI context uses a different workspace, run nemo config set --workspace default first.
In environments without a CLI configuration file, such as Google Colab, set NHX_BASE_URL (and NHX_ACCESS_TOKEN for an authenticated deployment) before you create the client. NemoClient.from_config() reads both:
Export your NGC API key. It 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 as workspace/fileset#path.
Automodel dataset.training and retrieve-eval dataset take a fileset name with no path fragment, so copy training.jsonl and eval_beir/ to the root of a dataset fileset. Stage 1 unique-keys eval_beir/qrels/test.tsv before writing it; still drop duplicate (query-id, corpus-id) rows if you are uploading an older dump, because the BEIR loader rejects them. The next cell does the copy and unique-key.
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:
Unique-key ./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.
The NVDocs artifacts fileset has no validation JSONL. schedule.validation_split: 0.01 therefore creates a deterministic 1% holdout from training.jsonl; that holdout produces validation loss and selects the best checkpoint. It does not modify or replace the frozen eval_beir/ data used in Stage 4. Validation data is optional: when a val*.jsonl, validation*.jsonl, or dev*.jsonl file is present in the training fileset, Automodel uses it instead of creating the holdout.
These values are the configuration validated on the frozen 20,909-query NVDocs split. The scores under Interpreting results are from this job.
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
The automatic 1% holdout above supplies the validation loss for this NVDocs job. 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.
On this split, peak learning rate 2.5e-5 with floor 1.25e-5 beat 1e-5 and 2e-5 with the batch size above held fixed. If a new corpus lands on an endpoint of a small sweep, 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
retrieve-eval scores the baseline and the target in one job, and the gap between those two is the result. Absolute values shift with the eval split, corpus size, domain, and mining depth, so read a published score as a reference point rather than a number to hit.
These are reference scores for a tuned Nemotron 3 Embed 1B on a 20,909-query NVDocs eval_beir split:
These scores are from the job above on that frozen split. Validation loss was lowest at the final step, so the best and last checkpoints match.
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.