Score Normalization

Mapping the raw scores produced by different retrieval systems onto a common scale so they can be meaningfully combined. It is the enabling step for every score-based fusion strategy — Relative Score Fusion, Linear Score Combination, and weighted blends generally — and the step that Reciprocal Rank Fusion avoids needing by discarding scores altogether.


Why it’s necessary

Scores from different retrieval paths are not on comparable scales, and often not on bounded scales at all:

SourceRangeBehavior
BM25 / lexicalUnboundedGrows with term frequency, term rarity, document shortness; no ceiling
Cosine / Dense Vector RetrievalTypically boundedMongoDB’s $vectorSearch emits 0.0–1.0 directly
ELSER / learned sparseUnboundedSums of learned term weights
Domain values (price, distance, rating)ArbitraryWhatever the field happens to be

Combining un-normalized scores does not degrade gracefully — it silently hands the ranking to whichever branch has the larger numeric scale, while still returning a single plausible-looking _score. See Hybrid Fusion Failure - BM25 Displacing Reference Documents for a worked failure, and the warning under Hybrid Search about bool/should clauses summing raw scores.

Strategies

None — pass raw scores through. Only defensible when the pipelines already share a scale.

Min-max scaling (minMaxScaler) — linearly map the observed [min, max] of a result set to [0, 1]. Intuitive and preserves relative gaps, but only as stable as the extremes: a single outlier stretches the range and flattens everything else. Because min and max are computed per result set, the same document can normalize differently across queries.

Sigmoid — squash any real value into (0, 1) with a logistic curve. Bounded regardless of input range, so it tolerates unbounded lexical scores, but it saturates: inputs far from the curve’s center collapse toward 0 or 1 and lose their differences. In Reciprocal Rank Fusion and Relative Score Fusion a raw distance score of 85.0 normalizes to exactly 1.0 while a rating of 4.2 becomes ~0.985 — the distance pipeline’s internal gradations are erased because its scale (0–100) sits far outside the range where sigmoid discriminates.

Z-score / L2 — standardize by distribution rather than extremes. Steadier than min-max on noisy corpora where outliers are common.

Where it sits in a fusion pipeline

pipeline A ──→ raw scores ──→ normalize ──┐
                                          ├──→ weight ──→ combine (sum / avg / custom) ──→ final
pipeline B ──→ raw scores ──→ normalize ──┘

Normalization precedes weighting: weights applied to un-normalized scores compound the scale mismatch rather than correcting it.

Normalizing Across a Distributed Cluster

Min-max is trivial arithmetic on one machine. It stops being trivial the moment the index is sharded, because min and max are properties of a result set, and each node only sees its own shard. Normalize locally and the same document scores differently depending on which node held it — the fusion weights then encode shard membership rather than relevance.

Improving Zero-Shot Ranking with Vespa Hybrid Search - part two resolves this with a custom searcher in Vespa’s query dispatcher, i.e. after the merge rather than during matching:

  1. Content nodes return match-features alongside their hits — the raw per-branch scores travel up with each document
  2. The dispatcher computes the global min and max across everything the nodes returned
  3. All scores are scaled uniformly against those globals
  4. Linear weighting combines the normalized values

The generalizable rule: score-based fusion belongs at the first point in the topology that sees the whole candidate set. Rank-based RRF avoids the problem for a second reason beyond ignoring magnitude — ranks are comparable across shards in a way that raw scores are not.

A residual caveat this does not fix: even globally computed min and max are per-query extremes, so the same document still normalizes differently across queries.

Engine support

  • MongoDB Atlas$scoreFusion takes input.normalization of none, sigmoid, or minMaxScaler, then combines by avg (default) or a custom expression. The $score stage can also normalize at the pipeline level, though deferring to $scoreFusion is the simpler default.
  • OpenSearch — a dedicated normalization processor in the hybrid search pipeline, ahead of combination.
  • Elasticsearch — score normalization and fusion for BM25 + kNN combination; note that a plain bool/should query does not normalize.
  • Vespa — no built-in normalization stage; min-max is implemented as a custom searcher in the query dispatcher, fed by match-features carried up from the content nodes. More work, and it puts the normalization at the only place in a distributed topology where it is correct.

The RRF alternative

Reciprocal Rank Fusion sidesteps normalization entirely by using only rank position, which is why it is robust out of the box and the usual recommendation for getting started. The cost is that it discards magnitude: two documents at rank 1 contribute identically even when one scored far better, and consecutively ranked documents are treated as evenly spaced when they may not be. Choosing between the two is a choice about whether your score gaps carry real information worth normalizing to preserve.

Articles

Case Studies