Spaces:
Running
Running
File size: 14,287 Bytes
ed65693 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 | ---
title: Calibrated Hybrid Retrieval Masterclass
emoji: π¬
colorFrom: blue
colorTo: indigo
sdk: static
pinned: false
---
# π¬ Calibrated Entropy-Weighted Hybrid Retrieval
[](https://github.com/ayushmath07/Semantic-Document-Retrieval-API/actions/workflows/ci.yml)
[](https://www.python.org/downloads/release/python-3110/)
[](https://fastapi.tiangolo.com/)
[](LICENSE)
[](https://github.com/astral-sh/ruff)
A research-oriented hybrid retrieval system that tests whether **score calibration makes retrieval uncertainty measurable and useful**. Combines BM25 sparse retrieval, FAISS dense retrieval, corpus-level CDF score calibration, entropy-weighted adaptive fusion, and cross-encoder reranking β all behind a FastAPI service.
> **Benchmark:** [BEIR SciFact](https://github.com/beir-cellar/beir) β 5,183 PubMed abstracts, 300 test claims, document-level qrels.
---
## β¨ Highlights
- **7 retrieval modes** for systematic ablation β from single-retriever baselines to full calibrated reranking
- **Corpus-level CDF calibration** maps BM25 and dense scores to a common probability space, making entropy comparable across retrievers
- **Entropy-weighted adaptive fusion** dynamically adjusts BM25βdense balance per query based on each retriever's confidence
- **Statistical rigor** β paired bootstrap significance tests (1,000 resamples) and Pearson correlations with p-values
- **Production-ready API** with FastAPI, Docker, CI/CD, and Swagger docs
---
## ποΈ Architecture
```mermaid
flowchart LR
Q["π Query"] --> BM25["BM25\n(Sparse)"]
Q --> FAISS["FAISS\n(Dense)"]
BM25 --> CAL_S["CDF\nCalibration"]
FAISS --> CAL_D["CDF\nCalibration"]
CAL_S --> ENT_S["Shannon\nEntropy H_s"]
CAL_D --> ENT_D["Shannon\nEntropy H_d"]
ENT_S --> ALPHA["Ξ± = H_d / (H_d + H_s + Ξ΅)"]
ENT_D --> ALPHA
CAL_S --> FUSE["Weighted Fusion\nΞ±Β·sparse + (1-Ξ±)Β·dense"]
CAL_D --> FUSE
ALPHA --> FUSE
FUSE --> RERANK["Cross-Encoder\nReranking"]
RERANK --> RES["π Results + Telemetry"]
style Q fill:#4A90D9,stroke:#2C5F8A,color:#fff
style BM25 fill:#E8913A,stroke:#B86E2C,color:#fff
style FAISS fill:#7B68EE,stroke:#5A4DB8,color:#fff
style CAL_S fill:#20B2AA,stroke:#178A82,color:#fff
style CAL_D fill:#20B2AA,stroke:#178A82,color:#fff
style ENT_S fill:#FF6B6B,stroke:#CC5555,color:#fff
style ENT_D fill:#FF6B6B,stroke:#CC5555,color:#fff
style ALPHA fill:#FFD93D,stroke:#CCB030,color:#333
style FUSE fill:#6BCB77,stroke:#4FA35C,color:#fff
style RERANK fill:#9B59B6,stroke:#7D3C98,color:#fff
style RES fill:#4A90D9,stroke:#2C5F8A,color:#fff
```
**Pipeline:** Query β parallel BM25 + FAISS retrieval β CDF calibration to corpus percentiles β Shannon entropy per retriever β adaptive Ξ± weighting β score fusion β cross-encoder reranking β ranked results with full telemetry.
---
## π§ͺ Research Question
> Does calibrating BM25 and dense retrieval scores to a common probability space via corpus-level CDF normalization make entropy a more reliable predictor of per-query retrieval quality, and does that translate to better fusion?
### Hypotheses
| ID | Hypothesis | Result |
|---|---|---|
| **H1** | CDF entropy has the strongest negative correlation with retrieval quality | β **Not supported** β z-score entropy is the strongest predictor |
| **H2** | Entropy fusion outperforms RRF and fixed-alpha baselines | β οΈ **Partially supported** β beats fixed-alpha significantly, ties with RRF |
<details>
<summary><b>π Method Details</b></summary>
### Corpus-Level CDFs
Built offline during indexing β **not** per-query top-k (which would normalize away the distributional signal):
```
50 SciFact test claims + 50 pseudo-queries from corpus chunks
β
βΌ
score every query against all 17,243 chunks
β
βΌ
sort 1,724,300 BM25 scores and 1,724,300 dense scores
β
βΌ
corpus_cdf_bm25.npy and corpus_cdf_dense.npy
```
### Score Calibration
```
calibrated_score = CDF_corpus(raw_score) # maps to corpus percentile
```
### Entropy Computation
```
H = βΞ£α΅’ pα΅’ logβ(pα΅’)
```
### Adaptive Fusion
```
Ξ± = H_dense / (H_dense + H_sparse + Ξ΅)
fused = Ξ± Β· sparse_score + (1 β Ξ±) Β· dense_score
```
When dense retrieval has high entropy (low confidence), Ξ± increases β system trusts BM25 more. When sparse retrieval has high entropy, Ξ± decreases β system trusts dense retrieval more.
### Bug Fixes from Initial Run
The first toy-corpus run exposed two evaluation bugs that were fixed before SciFact benchmarking:
- **Entropy invariance bug:** Entropy was subtracting the minimum score before normalization, causing raw/min-max/z-score entropy to collapse under affine transforms. Fixed: nonnegative calibrated scores are treated as probability mass directly; softmax is used only for negative-valued z-scores.
- **Chunk-level evaluation:** Metrics were computed at chunk-level, but BEIR qrels are document-level. Fixed: retrieved chunks are deduplicated by source document ID before metric computation.
</details>
---
## π Benchmark Results (BEIR SciFact)
```bash
python scripts/build_index.py --dataset scifact
python scripts/evaluate.py
```
### Retrieval Ablation
| Mode | NDCG@10 | MRR | P@3 | P@5 | R@5 | p95 Latency |
|:---|---:|---:|---:|---:|---:|---:|
| `dense` | 0.6715 | 0.6329 | 0.2466 | 0.1647 | 0.7495 | 33 ms |
| `sparse` | 0.6151 | 0.5804 | 0.2233 | 0.1520 | 0.7089 | 130 ms |
| `rrf` | 0.7028 | 0.6713 | 0.2555 | 0.1680 | 0.7744 | 249 ms |
| `hybrid_fixed` | 0.6829 | 0.6527 | 0.2522 | 0.1647 | 0.7594 | 232 ms |
| `hybrid_calibrated` | 0.6981 | 0.6672 | 0.2489 | 0.1660 | 0.7661 | 241 ms |
| `hybrid_fixed_rerank` | 0.7006 | 0.6653 | 0.2578 | 0.1700 | 0.7714 | 2432 ms |
| **`hybrid_calibrated_rerank`** | **0.7071** | **0.6719** | **0.2622** | **0.1720** | **0.7838** | 2116 ms |
> **Best overall:** `hybrid_calibrated_rerank` achieves the highest NDCG@10 (0.7071) with the lowest per-query variance (Ο = 0.3703).
<details>
<summary><b>π H1: Entropy Correlation Analysis</b></summary>
Pearson correlations β entropy as predictor vs. per-query retrieval quality. Stronger negative r = higher entropy better predicts lower quality.
| Retriever | Calibration | r vs NDCG@10 | p-value | r vs MRR | p-value |
|:---|:---|---:|---:|---:|---:|
| Sparse | raw | 0.0033 | 0.9550 | 0.0057 | 0.9220 |
| Sparse | min-max | 0.0033 | 0.9550 | 0.0057 | 0.9220 |
| Sparse | z-score | **β0.3759** | <0.000001 | **β0.3855** | <0.000001 |
| Sparse | CDF | β0.0085 | 0.8834 | β0.0031 | 0.9575 |
| Dense | raw | 0.0865 | 0.1348 | 0.0636 | 0.2718 |
| Dense | min-max | 0.0053 | 0.9273 | β0.0164 | 0.7775 |
| Dense | z-score | **β0.4065** | <0.000001 | **β0.3970** | <0.000001 |
| Dense | CDF | β0.1151 | 0.0463 | β0.1100 | 0.0571 |
**Verdict:** H1 is **not supported** as originally stated. Z-score entropy is the strongest negative predictor for both retrievers on SciFact. CDF entropy shows a weak but significant correlation for dense retrieval only.
</details>
<details>
<summary><b>π H2: Paired Bootstrap Significance Tests</b></summary>
1,000 paired bootstrap resamples over 300 SciFact test queries.
| Comparison | Metric | Mean Ξ | 95% CI | p-value | Significant? |
|:---|:---|---:|:---|---:|:---|
| `calibrated` vs `rrf` | NDCG@10 | β0.0047 | [β0.017, +0.007] | 0.426 | No |
| `calibrated` vs `rrf` | P@3 | β0.0067 | [β0.012, β0.002] | 0.002 | Yes, worse |
| `calibrated` vs `fixed` | NDCG@10 | **+0.0152** | [+0.003, +0.027] | **0.012** | β
Yes |
| `calibrated` vs `fixed` | MRR | **+0.0145** | [+0.002, +0.027] | **0.028** | β
Yes |
| `cal_rerank` vs `fix_rerank` | NDCG@10 | +0.0065 | [β0.006, +0.020] | 0.314 | No |
**Verdict:** H2 is **partially supported**. CDF entropy fusion significantly outperforms fixed-alpha fusion (p=0.012 on NDCG@10). It does not beat RRF, and with reranking the improvement is not statistically significant.
</details>
---
## ποΈ Retrieval Modes
| Mode | Strategy | Description |
|:---|:---|:---|
| `dense` | Single | FAISS dense retrieval only |
| `sparse` | Single | BM25 lexical retrieval only |
| `rrf` | Fusion | Reciprocal Rank Fusion (k=60) |
| `hybrid_fixed` | Fusion | Min-max calibration + fixed Ξ±=0.5 |
| `hybrid_calibrated` | Fusion | CDF calibration + entropy-weighted Ξ± |
| `hybrid_fixed_rerank` | Fusion + Rerank | Fixed hybrid β cross-encoder |
| `hybrid_calibrated_rerank` | Full Pipeline | CDF entropy hybrid β cross-encoder |
---
## π Quick Start
```bash
# 1. Clone and set up
git clone https://github.com/ayushmath07/Semantic-Document-Retrieval-API.git
cd Semantic-Document-Retrieval-API
python -m venv venv
venv\Scripts\activate # Windows
# source venv/bin/activate # macOS/Linux
pip install -r requirements.txt
# 2. Build the index (downloads SciFact on first run)
python scripts/build_index.py --dataset scifact
# 3. Start the API
uvicorn app.main:app --reload
```
Open **[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)** for interactive Swagger docs.
> **Note:** First run downloads SentenceTransformers models (~90 MB). Dense retrieval uses `all-MiniLM-L6-v2`; reranking uses `cross-encoder/ms-marco-MiniLM-L6-v2`.
---
## π‘ API Reference
### `GET /modes` β List retrieval modes
```bash
curl http://127.0.0.1:8000/modes
```
### `POST /query` β Search documents
```bash
curl -X POST http://127.0.0.1:8000/query \
-H "Content-Type: application/json" \
-d '{"question": "0-dimensional biomaterials show inductive properties.", "top_k": 3, "mode": "hybrid_calibrated_rerank"}'
```
<details>
<summary>Example response</summary>
```json
{
"question": "0-dimensional biomaterials show inductive properties.",
"answer": "[4983046] Biomaterial dimensionality ...",
"sources": [
{
"source": "4983046",
"chunk": 1,
"text": "...",
"score": 0.823,
"cross_encoder_score": 2.41
}
],
"retrieval_latency_ms": 241.5,
"telemetry": {
"mode": "hybrid_calibrated_rerank",
"alpha": 0.487,
"h_sparse": 8.21,
"h_dense": 7.79,
"calibration": "cdf",
"reranked": true
}
}
```
</details>
### `POST /upload` β Upload documents
```bash
curl -X POST http://127.0.0.1:8000/upload \
-F "file=@paper.pdf"
```
Supports `.pdf`, `.txt`, and `.md` files.
### `GET /health` β Health check
### `GET /documents` β List indexed documents
---
## π Project Structure
```
app/
βββ main.py FastAPI application and route handlers
βββ retriever.py Seven-mode retrieval orchestrator
βββ calibration.py CDF transforms, entropy, QPP predictors, statistics
βββ fusion.py RRF, fixed linear fusion, entropy-weighted fusion
βββ reranker.py Cross-encoder reranking (ms-marco-MiniLM-L6-v2)
βββ sparse_retriever.py BM25 index, search, and full-corpus scoring
βββ datasets.py SciFact corpus/query/qrels loaders
scripts/
βββ build_index.py Builds FAISS, BM25, CDFs, corpus LM, doc lookup
βββ evaluate.py Runs H1/H2 evaluation and bootstrap significance tests
tests/ pytest test suite (9 tests)
eval/ Golden set labels and generated results
data/faiss_index/ Runtime artifacts (FAISS, BM25, CDFs, metadata)
```
<details>
<summary><b>Runtime artifacts</b></summary>
| Artifact | Purpose |
|:---|:---|
| `index.faiss`, `index.pkl` | Dense FAISS vector store |
| `bm25_index.pkl` | Tokenized BM25 corpus and metadata |
| `corpus_cdf_bm25.npy` | Corpus-level BM25 score CDF |
| `corpus_cdf_dense.npy` | Corpus-level dense score CDF |
| `corpus_lm.pkl` | Corpus unigram language model (Clarity Score) |
| `doc_lookup.pkl` | (source, chunk) β text lookup |
| `index_metadata.json` | Dataset and artifact build metadata |
</details>
---
## βοΈ Configuration
| Variable | Default | Description |
|:---|:---|:---|
| `EMBEDDING_MODEL` | `sentence-transformers/all-MiniLM-L6-v2` | HuggingFace embedding model |
| `DATA_DIR` | `data` | Base directory for uploads and index |
| `MIN_RELEVANCE_SCORE` | `0.0` | Minimum similarity threshold (dense-only mode) |
| `CANDIDATE_K` | `20` | Candidates per retriever before fusion |
Copy `.env.example` to `.env` to customize:
```bash
cp .env.example .env
```
---
## π³ Docker
```bash
docker compose up --build
```
API docs at **[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)**.
---
## π§ͺ Testing
```bash
pytest tests/ -v
```
9 tests covering: health checks, upload validation, empty-index behavior, mode listing, mode validation, query telemetry, calibration entropy behavior, and document listing.
---
## π¬ Run Full Evaluation
```bash
python scripts/build_index.py --dataset scifact
python scripts/evaluate.py
```
The evaluator outputs:
- Aggregate metrics for all 7 modes
- NDCG@10 variance and per-query metrics
- H1 Pearson correlations with p-values
- H2 paired bootstrap confidence intervals and p-values
- Per-query rankings, relevance flags, and telemetry
Full results are saved to `eval/results.json`.
---
## π‘ Interpretation
The original toy CS corpus was useful for plumbing, but it was too easy and too small to validate the research claim. SciFact changes the story: **calibration is not a universal win**, RRF remains a strong baseline, and the clearest uncertainty signal comes from z-score entropy rather than CDF entropy. That is a stronger project outcome than a polished toy result because the system now produces **falsifiable, benchmarked evidence**.
---
## π€ Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, code style, and PR guidelines.
## π License
[MIT](LICENSE)
|