score-fusion-methods.md

reference

← Back to skill

Content hash: 9bd533d9a8b561b575fdd540cfb39c91aaf2f71d9af34ffed7c6452e9658f245
# Score Fusion Methods Compared

## Why naive score combination fails

BM25 scores are unbounded positive floats; cosine similarity is bounded [-1, 1].
Averaging them means the retriever with larger-magnitude scores dominates.

## Methods

### 1. Reciprocal Rank Fusion (RRF) — recommended

```
score(d) = Σ  1 / (k + rank_r(d))
```
- Works on **ranks**, not scores — robust to scale differences
- `k=60` is the standard constant; tune on your data
- Built into Weaviate, Qdrant, OpenSearch 2.19+

### 2. Min-max normalization

```
norm_score = (score - min) / (max - min)
```
- Requires knowing min/max per query, which can be noisy
- Sensitive to outliers (a single high-scoring doc distorts the scale)

### 3. Z-score normalization

```
norm_score = (score - μ) / σ
```
- Assumes normal distribution — rarely holds for retrieval scores
- Negative scores are confusing downstream

### 4. Weighted linear combination

```
final_score = α × bm25_norm + (1-α) × dense_norm
```
- Only works AFTER normalization — still fragile
- α is another hyperparameter to tune per dataset

## When hybrid search helps

| Query type | BM25 | Dense | Hybrid best? |
|-----------|------|-------|-------------|
| Exact product code | ✅ | ❌ | Yes — BM25 carries it |
| Paraphrase question | ❌ | ✅ | Yes — dense carries it |
| Named entity + concept | ⚠️ | ⚠️ | Yes — both contribute |
| Simple factual lookup | ✅ | ✅ | No — either works alone |
| Out-of-domain jargon | ✅ | ❌ | Yes — BM25 backstop |

## Production checklist

- [ ] Pull `retrieval_k` ≥ 2× `final_k` candidates from each retriever
- [ ] Apply metadata filters in BOTH retrievers before fusion
- [ ] Tune RRF `k` on your eval set (60 is a good default)
- [ ] Verify hybrid ≥ best single retriever on your complaint queries