SAVRN
Search Contact SAVRN

Open-weight model · Sentence similarity

vandf-rxnorm-biencoder

by Kevin Venanzi kvenanzi/vandf-rxnorm-biencoder

vandf-rxnorm-biencoder is an open-weight model for sentence similarity from Kevin Venanzi, released under Apache License 2.0. It has 109M parameters and a 512-token context. At 16-bit it needs about 0.3 GB of GPU memory, which fits on 1x MI300X from $1.85 an hour, at the lowest prices in the SAVRN Index. It draws 100 downloads a month.

Maps a VA National Drug File (VANDF) drug string to the RxNorm clinical drug confidence so you can auto-accept the sure cases and route the rest to review. Published tools such as RxMap normalize to the ingredient level (IN/MIN).

Parameters109M
Context512
Weights438.0 MB
Licenseapache-2.0
AccessOpen weights
Monthly Downloads100

Runs On

What it takes to serve vandf-rxnorm-biencoder (109M parameters): the memory its weights need at each precision, and the cheapest way to rent enough data-center GPUs to hold them.

PrecisionWeightsMemory neededCheapest setupPer hourAlso fits
16-bit 0.2 GB 0.3 GB 1x MI300X (192 GB)
Vultr
$1.85 1x H100 $1.99 · 1x MI325X $2.00
8-bit 0.1 GB 0.1 GB 1x MI300X (192 GB)
Vultr
$1.85 1x H100 $1.99 · 1x MI325X $2.00
4-bit 0.1 GB 0.1 GB 1x MI300X (192 GB)
Vultr
$1.85 1x H100 $1.99 · 1x MI325X $2.00

Memory is the weights at that precision plus 20% for the runtime and a short context; a long context needs more. Prices are the lowest on-demand hourly rates in the SAVRN Index, read Sep 23, 2026.

vandf-rxnorm-biencoder on every accelerator the SAVRN Index prices, at every precision

Model Card

By Kevin Venanzi, published under apache-2.0, revision aa1e06a1eae4.

Maps a VA National Drug File (VANDF) drug string to the RxNorm clinical drug confidence so you can auto-accept the sure cases and route the rest to review. Published tools such as RxMap normalize to the ingredient level (IN/MIN). This model targets the full clinical drug, where a miss on strength or dose form is a wrong answer. Method, hypotheses, the full sweep, and the error Mapper loads the encoder, trainconfig.json (input preprocessing), calibration.json (score → probability), and candidates.parquet (the 27,287 active RxNorm SCD/SBD names it searches) from this repo. The default acceptance threshold (0.92) was chosen on validation for 99% precision on a population that includes real…

Read Kevin Venanzi's full model card

VANDF → RxNorm clinical drug bi-encoder

Maps a VA National Drug File (VANDF) drug string to the RxNorm clinical drug it means (SCD or SBD: ingredient, strength, and dose form), with a calibrated confidence so you can auto-accept the sure cases and route the rest to review.

Input Output Confidence
METOPROLOL TARTRATE 12.5MG TAB metoprolol tartrate 12.5 MG Oral Tablet (RXCUI 866508) 0.97 → accept
ACETIC ACID 0.25% IRRG SOLN acetic acid 2.5 MG/ML Irrigation Solution 0.96 → accept
HYOSCYAMINE SO4 0.125MG/5ML ELIXIR hyoscyamine sulfate 0.125 MG/ML Oral Solution (wrong: truth is 0.025 MG/ML, listed 2nd) 0.77 → review
CATHETER,FOLEY SILICONE 22FR 5CC (nearest candidate, meaningless) 0.02 → review

Published tools such as RxMap normalize to the ingredient level (IN/MIN). This model targets the full clinical drug, where a miss on strength or dose form is a wrong answer. Method, hypotheses, the full sweep, and the error analysis are in the write-up: https://withinnoise.dev/blog/posts/vandf-rxnorm-biencoder/

Usage

pip install "rxnorm-vandf @ git+https://github.com/kvenanzi/rxnorm"
from rxnorm_vandf.infer import Mapper

mapper = Mapper.from_pretrained("kvenanzi/vandf-rxnorm-biencoder")   # ~450 MB download
for p in mapper.map(["METOPROLOL TARTRATE 12.5MG TAB", "CATHETER,FOLEY SILICONE 22FR 5CC"]):
    print(p.rxcui, p.name, p.tty, f"{p.confidence:.2f}", "accept" if p.accept else "review")
    # p.alternatives: the next four candidates as (rxcui, name, cosine)

Mapper loads the encoder, train_config.json (input preprocessing), calibration.json (score → probability), and candidates.parquet (the 27,287 active RxNorm SCD/SBD names it searches) from this repo. The default acceptance threshold (0.92) was chosen on validation for 99% precision on a population that includes real drugs with no SCD/SBD; pass threshold= to change it.

How it works

  1. Preprocessing. The VA string is lowercased, punctuation is stripped, and a deterministic rule appends the RxNorm-style concentration (0.125MG/5ML0.025 mg/ml; 0.25%2.5 mg/ml; mg/mg for gels and ointments). An embedding model can't do this arithmetic; a regex can.
  2. Retrieval. A SapBERT bi-encoder, fine-tuned with MultipleNegativesRankingLoss on 9,287 (VA string, RxNorm name) pairs plus hard negatives (same ingredients, different strength or dose form), embeds the query and all candidates; top-k by cosine.
  3. Calibration. A logistic (Platt) layer over [cosine, top-1−top-2 margin, temperature-scaled softmax] gives P(correct). Fit on validation only.

Results

Test split: 1,848 VA strings whose ingredients never appear in training. The candidate pool is all 27,287 active SCD/SBD in RxNorm 2026-09-08.

Method acc@1 recall@5 ingredient strength dose form
Exact string match 0.000 0.000
TF-IDF char 3–5-grams 0.509 0.820 0.978 0.617 0.698
MiniLM-L6 fine-tuned 0.836 0.967 0.983 0.884 0.935
+ strength normalizer 0.886 0.975 0.982 0.941 0.943
SapBERT + normalizer (this model) 0.931 0.984 0.983 0.961 0.972

Validation: acc@1 0.883, recall@5 0.961.

The 0.931 is one draw of the ingredient split. Re-drawing the split five more times and retraining the same recipe gives test acc@1 0.907 ± 0.019 (range 0.874–0.931; the published split is the most favorable of the six), and seven-fold cross-validation by ingredient gives a pooled out-of-fold acc@1 of 0.894 (Wilson 95% 0.889–0.899, n = 12,227). The training seed contributes a standard deviation of about 0.0045 (two seeds on each of the six draws). Read the headline as a band of about two points. Recall@5 (0.974–0.988) and the component accuracies vary little across draws.

Abstention. Thresholds chosen on validation, measured on test:

Population Signal Auto-accepted Precision of accepted
VA strings that have an SCD/SBD softmax 79.0% 0.988
+ real drugs with no SCD/SBD (packs, ingredient-only) platt (default) 46.0% 0.982

The validation target was 99%; test lands at 98.2–98.8%. Treat the achieved number as the estimate, not the target. In cross-validation of the same recipe, a 99% threshold fit on the pooled out-of-fold predictions (about 11,000 strings) achieved 98.6–99.8% on each held-out fold, which indicates that the shortfall here comes from choosing the threshold on one validation split of about 2,300 strings.

An 18-run sweep (3 encoders × 3 negative strategies × normalizer on/off) found the three effects roughly additive: domain pre-training (SapBERT vs general encoders) +8.6 points val acc@1, the strength normalizer +4.8, ingredient-matched hard negatives +3. The hard-negatives effect was then re-run on all six ingredient splits (18 runs): ingredient-matched negatives beat in-batch-only on every split, by 1.6 points of test acc@1 on average (95% CI 0.8–2.4). Live charts: https://wandb.ai/within-noise/rxnorm-vandf.

A follow-up tested a second source vocabulary (the FDA label names), an auxiliary strength head, and cross-validation by ingredient; neither training change altered accuracy on VA strings. Write-up: https://withinnoise.dev/blog/posts/vandf-rxnorm-interventions/

For mapping new strings, the same recipe trained on every ingredient family is published as https://huggingface.co/kvenanzi/vandf-rxnorm-biencoder-all (no test set of its own; cross-validated acc@1 0.894 on unseen ingredients).

Limitations

  • Trained on VA strings only. Other systems' drug names are a different distribution. On FDA Structured Product Label names (MTHSPL) the same recipe scores acc@1 0.709 ± 0.057 across twelve draw-and-seed runs; other vocabularies are unmeasured.
  • One split for calibration. This model's abstention thresholds were chosen on one validation draw. Re-choose thresholds on your own held-out data.
  • Candidates are RxNorm 2026-09-08. RxNorm changes monthly; rebuild candidates.parquet for a newer release (scripts/03_build_dataset.py in the repo).
  • Not for unsupervised clinical use. A 7% top-1 error rate on medication codes is a safety problem; use the confidence to route uncertain strings to a pharmacist, or use the top-5 as suggestions.
  • Of the 128 test errors, 46 involve strength (often an underdetermined string: MANNITOL 250MG/ML INJ vs RxNorm's 50 ML mannitol 250 MG/ML Injection), 43 dose form, 31 ingredient, and 25 are SCD-vs-SBD twins with identical components.

Training details

  • Base: cambridgeltl/SapBERT-from-PubMedBERT-fulltext (110M params, Apache-2.0)
  • Loss: MultipleNegativesRankingLoss, triplets (anchor, positive, hard negative), NO_DUPLICATES batch sampler; negatives drawn from train-split candidates only
  • 4 epochs, batch 64, lr 2e-5, 10% warmup, fp16, max_seq_length 96, seed 42
  • Best epoch by validation acc@1; test scored once
  • ~20 min on a GTX 1070, ~2 min on a Colab A100

Data and license

Training pairs come from RxNorm itself: a VANDF atom and an RxNorm SCD/SBD atom that share an RXCUI are the same drug by NLM's curation. Only the VANDF and RXNORM source vocabularies were used (UMLS source restriction category 0: "general terms of the License apply with no additional restrictions"). No PHI. Dataset: kvenanzi/vandf-rxnorm-pairs.

Model weights: Apache-2.0, as the base model.

References

  • Liu F, Shareghi E, Meng Z, Basaldella M, Collier N (2021). "Self-Alignment Pretraining for Biomedical Entity Representations". NAACL-HLT 2021:4228–4238. https://aclanthology.org/2021.naacl-main.334/ (SapBERT)
  • Reimers N, Gurevych I (2019). "Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks". EMNLP-IJCNLP 2019:3982–3992. https://aclanthology.org/D19-1410/
  • Henderson M et al (2017). "Efficient Natural Language Response Suggestion for Smart Reply". arXiv:1705.00652. (the in-batch negatives loss)
  • Guo C, Pleiss G, Sun Y, Weinberger KQ (2017). "On Calibration of Modern Neural Networks". ICML 2017. arXiv:1706.04599. (temperature scaling)
  • Platt JC (1999). "Probabilistic Outputs for Support Vector Machines and Comparisons to Regularized Likelihood Methods". In Advances in Large Margin Classifiers, MIT Press, pp. 61–74.
  • Nelson SJ, Zeng K, Kilbourne J, Powell T, Moore R (2011). "Normalized names for clinical drugs: RxNorm at 6 years". JAMIA 18(4):441–448. https://doi.org/10.1136/amiajnl-2011-000116
  • Korpela E, Rubin LH, Dastgheyb RM, Xu Y (2026). "RxMap: an LLM-assisted tool for medication normalization". JAMIA Open 9(3):ooag085. https://doi.org/10.1093/jamiaopen/ooag085
  • RxNorm is produced by the U.S. National Library of Medicine; VANDF by the U.S. Department of Veterans Affairs.

Configuration

Architecture
BertModel
Context length (tokens)
512
Layers
12
Hidden size
768
Feed-forward size
3,072
Attention heads
12
Vocabulary size
30,522
Model type
bert

Identity and Version

Repository
kvenanzi/vandf-rxnorm-biencoder
Publisher
Kevin Venanzi
Task
Sentence similarity
Modality
Text
Library
sentence-transformers
Parameters
109M parameters
Languages
en
Revision
aa1e06a1eae4b8ef0cf65008a44278afbf80e854
First published
2026-09-14
Last updated
2026-09-23

Files and Weights

13 files, 439.7 MB in total. The weights are 1 file totalling 438.0 MB in safetensors.

Weights1 file · 438.0 MB
Configuration7 files · 2.6 KB
Tokenizer2 files · 706.7 KB
Documentation1 file · 9.8 KB
Other1 file · 1.1 MB
Repository1 file · 1.5 KB
Every file
FileTypeSizeSHA-256
model.safetensorsWeights438.0 MB 0707edfe488c
1_Pooling/config.jsonConfiguration90 B
calibration.jsonConfiguration549 B
config.jsonConfiguration706 B
config_sentence_transformers.jsonConfiguration284 B
modules.jsonConfiguration277 B
sentence_bert_config.jsonConfiguration241 B
train_config.jsonConfiguration414 B
README.mdDocumentation9.8 KB
candidates.parquetOther1.1 MB afe3a761c940
.gitattributesRepository1.5 KB
tokenizer.jsonTokenizer706.3 KB
tokenizer_config.jsonTokenizer381 B

License and Download

License
apache-2.0
Access
Open weights, no gate
Download size
438.0 MB
Download from Kevin Venanzi

Released by Kevin Venanzi through its official repository on Hugging Face. Read the license.

Built From

Evaluations

Each result is shown as reported, with the conditions its reporter stated. None is a SAVRN measurement. A comparison lines two results up only when their configuration, unit and setup are all stated and identical.

BenchmarkConditionsResultReported byRevisionDate
VANDF-RxNorm pairs, test split (held-out ingredients) Task VANDF string to RxNorm clinical drug (SCD/SBD) mappingMetric acc@1Comparison conditions not established 0.931 kvenanzi
Publisher reported
Evaluated revision not stated
VANDF-RxNorm pairs, test split (held-out ingredients) Task VANDF string to RxNorm clinical drug (SCD/SBD) mappingMetric recall@5Comparison conditions not established 0.984 kvenanzi
Publisher reported
Evaluated revision not stated

Memory Requirements

PrecisionWeights in memory
As published438.0 MB
16-bit0.2 GB
8-bit0.1 GB
4-bit0.1 GB

Weights only, from the published parameter count; the key-value cache and runtime add to this.

Questions About vandf-rxnorm-biencoder

How much GPU memory does vandf-rxnorm-biencoder need?

About 0.3 GB at 16-bit and 0.1 GB at 4-bit: the weights (109M parameters) plus a working margin. A long context needs more.

What is the cheapest GPU to run vandf-rxnorm-biencoder on?

At 16-bit, 1x MI300X from $1.85 an hour; at 4-bit, 1x MI300X from $1.85 an hour, at the lowest on-demand prices the SAVRN Index lists.

Can I use vandf-rxnorm-biencoder commercially?

Yes. vandf-rxnorm-biencoder is released under Apache License 2.0. The Apache License 2.0 is a permissive open-source license. It permits commercial use, modification and redistribution. It requires keeping the license and copyright notices and any NOTICE file, stating significant changes, and it includes an express patent grant from contributors.

What is vandf-rxnorm-biencoder's context length?

512 tokens, from the maximum position embeddings in its published configuration.

Similar Models

This is a sentence-transformers model finetuned from BAAI/bge-base-en-v1.5. It maps sentences & paragraphs to a 768-dimensional dense vector space and can be used for semantic textual similarity, semantic search, paraphrase mining, text classification, clustering, and more. First install the Sentence Transformers library: Then you can load this model and run inference. Approximate statistics based on the first 45 samples: Approximate statistics based on the first 5 samples: - evalstrategy: steps - perdevicetrainbatchsize: 16 - perdeviceevalbatchsize: 16 - learningrate: 3e-06 - maxsteps: 24 - warmupratio: 0.1 - batchsampler: noduplicates - overwriteoutputdir: False - dopredict: False…

Open weights 109M parameters 512 tokens sentence-transformers

Model · Sentence similarity

pubmedbert-base-embeddings

NeuML

This is a PubMedBERT-base model fined-tuned using sentence-transformers. It maps sentences & paragraphs to a 768 dimensional dense vector space and can be used for tasks like clustering or semantic search. The training dataset was generated using a random sample of PubMed title-abstract pairs along with similar title pairs. PubMedBERT Embeddings produces higher quality embeddings than generalized models for medical literature. Further fine-tuning for a medical subdomain will result in even better performance. This model can be used to build embeddings databases with txtai for semantic search and/or as a knowledge source for retrieval augmented generation (RAG). Alternatively, the model can…

Open weights apache-2.0 109M parameters 512 tokens sentence-transformers

Model · Sentence similarity

vandf-rxnorm-biencoder-all

Kevin Venanzi

The recipe of kvenanzi/vandf-rxnorm-biencoder, trained on all 14,369 VA National Drug File (VANDF) strings that have a clinical drug in RxNorm, so that every ingredient family in the file is in training. It maps a VANDF drug string to the RxNorm clinical drug it names (SCD or SBD: ingredient, strength, and dose form), with a calibrated confidence for routing uncertain strings to review. This model has no test set of its own: every VA string was used for training. Its expected accuracy comes from seven-fold cross-validation of the recipe, and a comparison with the first model on strings neither model trained on (below). Write-up: https://withinnoise.dev/blog/posts/vandf-rxnorm-interventions/…

Open weights apache-2.0 109M parameters 512 tokens sentence-transformers

Model · Sentence similarity

finetuned-medical-bert

Fico Filbert

This is a sentence-transformers model finetuned from google-bert/bert-base-uncased. It maps inputs to a 768-dimensional dense vector space and can be used for semantic textual similarity, semantic search, paraphrase mining, classification, clustering, and more. First install the Sentence Transformers library: Then you can load this model and run inference. Approximate statistics based on the first 100 samples: - perdevicetrainbatchsize: 16 - perdeviceevalbatchsize: 16 - multidatasetbatchsampler: roundrobin - dopredict: False - predictionlossonly: True - perdevicetrainbatchsize: 16 - perdeviceevalbatchsize: 16 - gradientaccumulationsteps: 1 - evalaccumulationsteps: None…

Open weights 109M parameters 512 tokens sentence-transformers

Model · Sentence similarity

e5-base-v2

Liang Wang

Liang Wang, Nan Yang, Xiaolong Huang, Binxing Jiao, Linjun Yang, Daxin Jiang, Rangan Majumder, Furu Wei, arXiv 2022 This model has 12 layers and the embedding size is 768. Below is an example to encode queries and passages from the MS-MARCO passage ranking dataset. Please refer to our paper at https://arxiv.org/pdf/2212.03533.pdf. Check out unilm/e5 to reproduce evaluation results on the BEIR and MTEB benchmark. Below is an example for usage with sentencetransformers. Package requirements pip install sentencetransformers~=2.2.2 1. Do I need to add the prefix "query: " and "passage: " to input texts? Yes, this is how the model is trained, otherwise you will see a performance degradation.…

Open weights mit 109M parameters 512 tokens sentence-transformers

Model · Sentence similarity

e5-base

Liang Wang

News (May 2023): please switch to e5-base-v2, which has better performance and same method of usage. Liang Wang, Nan Yang, Xiaolong Huang, Binxing Jiao, Linjun Yang, Daxin Jiang, Rangan Majumder, Furu Wei, arXiv 2022 This model has 12 layers and the embedding size is 768. Below is an example to encode queries and passages from the MS-MARCO passage ranking dataset. Please refer to our paper at https://arxiv.org/pdf/2212.03533.pdf. Check out unilm/e5 to reproduce evaluation results on the BEIR and MTEB benchmark. Below is an example for usage with sentencetransformers. Package requirements pip install sentencetransformers~=2.2.2 1. Do I need to add the prefix "query: " and "passage: " to…

Open weights mit 109M parameters 512 tokens sentence-transformers