Loading…
Loading…
A fine-tuned version of Snowflake/snowflake-arctic-embed-xs optimised for entity resolution -- matching records that refer to the same real-world entity across messy, inconsistent datasets.
The canonical use case is matching counterparty names, addresses, and identifiers between a clean reference master (side A) and noisy operational data (side B). For example, resolving "GS Intl Ltd" to "Goldman Sachs International".
This model was trained as part of Melder, an open-source record matching engine in Rust.
Evaluated on a held-out dataset of 10,000 entity pairs (never seen during training):
| Metric | Base model (untrained) | This model (R22) |
|---|---|---|
| Score overlap (lower is better) | 0.162 | 0.031 (5.2x reduction) |
| Combined recall | 98.1% | 99.7% |
| Precision | 84.2% | 88.6% |
| False positives in auto-match | 131 | 0 |
| Non-matches in review queue | 2,826 | 184 (93.5% reduction) |
| Missed matches (clean) | 4 | 19 |
| Missed matches (heavy noise) | 0 | 11 |
"Score overlap" measures how much the score distributions of true matches and non-matches overlap -- lower means better separation. This model reduces overlap by 5.2x compared to the base model, meaning the scoring threshold between "match" and "not a match" becomes much cleaner.
Combined recall (auto-matched + review) stays at 99.7%, meaning almost no true matches are lost. The main benefit of fine-tuning is cleaning the review queue -- non-matches that would have required human review are pushed clearly below threshold.
The model produces 384-dimensional L2-normalised embeddings. Cosine similarity (= dot product for normalised vectors) between embeddings indicates how likely two records refer to the same entity.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("themelder/arctic-embed-xs-entity-resolution")
# Encode entity records (concatenate name + address for best results)
queries = ["Goldman Sachs International 133 Fleet Street, London EC4A 2BB"]
candidates = [
"GS Intl Ltd 133 Fleet St London EC4A 2BB",
"Morgan Stanley & Co 20 Bank Street, London E14 4AD",
"Goldman Sachs Asset Management Christchurch Court, London EC1A 7HT",
]
query_emb = model.encode(queries, prompt_name="query")
candidate_emb = model.encode(candidates)
scores = query_emb @ candidate_emb.T
for candidate, score in sorted(zip(candidates, scores[0]), key=lambda x: -x[1]):
print(f"{score:.3f} {candidate}")
# 0.872 GS Intl Ltd 133 Fleet St London EC4A 2BB
# 0.614 Goldman Sachs Asset Management Christchurch Court, London EC1A 7HT
# 0.298 Morgan Stanley & Co 20 Bank Street, London E14 4AD
In your Melder config YAML, point the model at the HuggingFace model ID or a local path to the ONNX export:
embeddings:
model: themelder/arctic-embed-xs-entity-resolution
Melder uses the ONNX export (model.onnx) for inference via fastembed. The model produces 384-dimensional embeddings at roughly 2x the speed of BGE-small models (6 layers vs 12).
The repository includes model.onnx for direct use with ONNX Runtime in any language (Rust, C++, Java, etc.) without Python dependencies.
| Property | Value |
|---|---|
| Base model | Snowflake/snowflake-arctic-embed-xs |
| Architecture | BERT (6 layers, 12 heads, 384 hidden) |
| Parameters | 22M |
| Embedding dimension | 384 |
| Max sequence length | 512 tokens |
| Similarity function | Cosine similarity |
| Pooling | CLS token |
| Output | L2-normalised |
Fine-tuned using LoRA (Low-Rank Adaptation) over 22 iterative rounds. Each round:
This iterative approach means the model learns from its own mistakes -- hard negatives from round N become training signal for round N+1. Combined with accumulation of pairs across all rounds, the model sees progressively harder examples.
| Parameter | Value |
|---|---|
| Loss function | MultipleNegativesRankingLoss |
| Batch size | 128 |
| Learning rate | 2e-5 |
| Epochs per round | 1 |
| Warmup ratio | 0.1 |
| LoRA rank | 8 |
| LoRA alpha | 16 |
| LoRA dropout | 0.1 |
| Rounds | 22 |
| Total training pairs (final round) | ~127,000 |
| Optimizer | AdamW (fused) |
Synthetic entity resolution data generated by Melder's data generator:
Training pairs consist of:
We tested four base models across 12 experiments:
| Model | Parameters | Best overlap | Combined recall | Encoding speed |
|---|---|---|---|---|
| all-MiniLM-L6-v2 | 22M | (baseline only) | -- | 2x |
| BAAI/bge-small-en-v1.5 | 33M | 0.070 | 97.3% | 1x |
| BAAI/bge-base-en-v1.5 | 110M | 0.046 | ~98.5% | 0.5x |
| Snowflake/arctic-embed-xs | 22M | 0.031 | 99.7% | 2x |
Arctic-embed-xs won on every metric despite being the smallest model. Its superior pre-training (400M samples with hard negative mining) gives it better out-of-the-box entity discrimination than larger models trained on simpler data.
Score overlap coefficient across training rounds (holdout, lower is better):
| R0 | R4 | R8 | R10 | R14 | R17 | R22 |
|---|---|---|---|---|---|---|
| 0.162 | 0.156 | 0.085 | 0.047 | 0.034 | 0.033 | 0.031 |
The model converges cleanly with no regression or oscillation. Extended training to R26 confirmed convergence (overlap 0.030, within noise).
If you use this model, please cite:
@misc{melder-arctic-embed-xs-er,
title={Arctic-embed-xs fine-tuned for Entity Resolution},
author={Melder Contributors},
year={2026},
url={https://huggingface.co/themelder/arctic-embed-xs-entity-resolution},
}
Snowflake/snowflake-arctic-embed-xs