Skip to content

Commit a860bd0

Browse files
Add threshold optimizers and other improvements (#19)
* pin redisvl below 1.0.0 * add threshold optimizer classes * update tests, docs, readme, and deps * add makefile and poetry scripts * update gitignore * formatting and update gitignore * clean up readme some
1 parent bf923ea commit a860bd0

28 files changed

+2407
-1248
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ models/
44
.python-version
55
__pycache__/
66
.env
7-
dist/
7+
dist/
8+
.pytest_cache
9+
.mypy_cache

Makefile

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.PHONY: install format lint test clean redis-start redis-stop check-types check
2+
3+
install:
4+
poetry install --all-extras
5+
6+
redis-start:
7+
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
8+
9+
redis-stop:
10+
docker stop redis-stack
11+
12+
format:
13+
poetry run format
14+
poetry run sort-imports
15+
16+
check-types:
17+
poetry run check-mypy
18+
19+
lint: format check-types
20+
21+
test:
22+
poetry run test
23+
24+
check: lint test
25+
26+
clean:
27+
find . -type d -name "__pycache__" -exec rm -rf {} +
28+
find . -type d -name ".pytest_cache" -exec rm -rf {} +
29+
find . -type d -name ".mypy_cache" -exec rm -rf {} +
30+
find . -type d -name ".coverage" -delete
31+
find . -type d -name "htmlcov" -exec rm -rf {} +
32+
find . -type d -name "dist" -exec rm -rf {} +
33+
find . -type d -name "build" -exec rm -rf {} +
34+
find . -type d -name "*.egg-info" -exec rm -rf {} +
35+
find . -type d -name "_build" -exec rm -rf {} +

README.md

Lines changed: 170 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,66 @@
11
<div align="center">
2-
<div><img src="https://raw.githubusercontent.com/redis/redis-vl-python/main/docs/_static/Redis_Logo_Red_RGB.svg" style="width: 130px"> </div>
2+
3+
<img src="https://raw.githubusercontent.com/redis/redis-vl-python/main/docs/_static/Redis_Logo_Red_RGB.svg" width="130">
4+
5+
# 🚀 Redis Retrieval Optimizer
6+
37

48
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
59
![Language](https://img.shields.io/github/languages/top/redis-applied-ai/retrieval-optimizer)
610
![GitHub last commit](https://img.shields.io/github/last-commit/redis-applied-ai/retrieval-optimizer)
711

12+
---
13+
14+
**Stop guessing. Start measuring.** 📊
15+
16+
Transform your retrieval system from *"looks good to me"* to *"proven to perform"* with systematic benchmarking and Bayesian optimization.
17+
818
</div>
919

10-
# Retrieval Optimizer
20+
## ✨ What is Redis Retrieval Optimizer?
1121

12-
The **Redis Retrieval Optimizer** is a framework for systematically measuring and improving retrieval performance for vector and hybrid search. The framework helps you select the best combination of embedding model, index type, and query settings for your specific use case.
22+
The **Retrieval Optimizer** is a powerful framework that takes the guesswork out of building high-performance search systems. Whether you're working with vector search, hybrid retrieval, or traditional text search, this tool helps you **scientifically measure and optimize** your search performance.
1323

14-
To use the Retrieval Optimizer, you start with a labeled data set consisting of a corpus of texts, a set of natural language questions, and a collection of labels. You also define a set of search methods and embedding models to test against.
24+
### 🎯 Why You Need This
1525

16-
The Retrieval Optimizer then lets you evaluate critical tradeoffs between **cost, speed, and latency**, helping you understand how different embedding models, retrieval strategies, and index configurations impact overall system performance. The tool's **Bayesian optimization** mode lets you fine-tune these index configurations. Ultimately, the tools let you implement **metrics-driven development** for your search applications — ensuring that decisions are grounded in data, not assumptions.
26+
- **🔬 Evidence-Based Decisions**: Replace assumptions with hard data about what actually works for your use case
27+
- **⚡ Performance Optimization**: Find the perfect balance between speed, accuracy, and cost
28+
- **🤖 Smart Automation**: Use Bayesian optimization to intelligently explore the configuration space
29+
- **📈 Comprehensive Metrics**: Track everything from latency to relevance with industry-standard evaluation metrics
30+
- **🔧 Easy Integration**: Works with your existing Redis setup and popular embedding models
1731

18-
# Installation
32+
### 🚀 Key Features
1933

20-
`pip install redis-retrieval-optimizer` | [pypi](https://pypi.org/project/redis-retrieval-optimizer/)
34+
| Feature | Description |
35+
|---------|-------------|
36+
| **🔄 Grid Search** | Systematically test different embedding models and retrieval strategies |
37+
| **🧠 Bayesian Optimization** | Intelligently fine-tune index configurations for optimal performance |
38+
| **📊 Rich Metrics** | Track recall, precision, NDCG, F1, query time, and indexing performance |
39+
| **🔍 Multiple Search Methods** | Compare BM25, vector search, hybrid, reranking, and weighted RRF |
40+
| **🎛️ Threshold Optimization** | Automatically tune semantic cache and router thresholds |
41+
| **🔌 Extensible Design** | Easily add custom search methods and data processors |
2142

22-
# Example notebooks
43+
44+
### 🎪 How It Works
45+
46+
1. **📁 Prepare Your Data**: Bring your corpus, queries, and relevance labels
47+
2. **⚙️ Define Your Tests**: Choose embedding models and search methods to compare
48+
3. **🔬 Run Experiments**: Let the optimizer systematically test configurations
49+
4. **📈 Analyze Results**: Get detailed performance metrics and recommendations
50+
5. **🚀 Deploy Optimized Config**: Use the best-performing setup in production
51+
52+
**The result?** A search system backed by data, not hunches.
53+
54+
55+
## 📦 Installation
56+
57+
Install the optimizer from [PyPI](https://pypi.org/project/redis-retrieval-optimizer/):
58+
59+
```bash
60+
pip install redis-retrieval-optimizer
61+
```
62+
63+
## 📚 Example Notebooks
2364

2465
For complete code examples, see the following notebooks:
2566

@@ -30,7 +71,9 @@ For complete code examples, see the following notebooks:
3071
| Bayesian Optimization | [00_bayes_study.ipynb](https://github.com/redis-applied-ai/redis-retrieval-optimizer/blob/main/docs/examples/bayesian_optimization/00_bayes_study.ipynb) |
3172
| Embedding model comparison | [00_comparison.ipynb](https://github.com/redis-applied-ai/redis-retrieval-optimizer/blob/main/docs/examples/comparison/00_comparison.ipynb) |
3273

33-
# Quick start
74+
---
75+
76+
## 🚀 Quick Start
3477

3578
The Retrieval Optimizer supports two *study* types: **Grid** and **Bayesian Optimization**. Each is suited to a different stage of building a high-quality search system.
3679

@@ -44,7 +87,7 @@ Once you've identified a solid starting point, use Bayesian optimization to **fi
4487

4588
## Running a Grid study
4689

47-
#### Define study config
90+
#### Study config
4891
```yaml
4992
# paths to necessary data files
5093
corpus: "data/nfcorpus_corpus.json"
@@ -105,7 +148,7 @@ metrics = run_grid_study(
105148
## Running a Bayesian optimization
106149
Selects the next best configuration to try based on a heuristic. This is good when it would take a very long time to test all possible configurations.
107150

108-
#### Study config:
151+
#### Study config
109152
```yaml
110153
# path to data files for easy read
111154
corpus: "data/nfcorpus_corpus.json"
@@ -194,11 +237,11 @@ metrics = run_bayes_study(
194237
| vector | hnsw | float32 | 100 | 50 | 8 | 0.002346 | 3.088 | 0.126233|
195238
| vector | hnsw | float32 | 100 | 50 | 16 | 0.001478 | 1.896 | 0.116203|
196239

240+
---
197241

242+
## 🔍 Search Methods
198243

199-
# Search methods
200-
201-
Below is a comprehensive table documenting the built-in search methods available in the Redis Retrieval Optimizer:
244+
Below is a comprehensive table documenting the built-in search methods available in the Retrieval Optimizer:
202245

203246
| Method | Description | Use Case | Key Features |
204247
|--------|-------------|----------|--------------|
@@ -232,7 +275,113 @@ CUSTOM_SEARCH_METHOD_MAP = {
232275
}
233276
```
234277

235-
## Custom processors and search methods
278+
## 🎛️ Threshold Optimization
279+
280+
The Retrieval Optimizer includes threshold optimization capabilities for RedisVL's **Semantic Cache** and **Semantic Router**. This feature helps you automatically tune distance thresholds to maximize performance metrics like F1 score, precision, or recall.
281+
282+
### Cache Threshold Optimization
283+
284+
Optimize thresholds for semantic caches to improve cache hit rates and relevance:
285+
286+
```python
287+
from redis_retrieval_optimizer.threshold_optimization import CacheThresholdOptimizer
288+
from redisvl.extensions.cache.llm import SemanticCache
289+
290+
# Create a semantic cache
291+
cache = SemanticCache(
292+
name="my_cache",
293+
redis_url="redis://localhost:6379",
294+
distance_threshold=0.5 # Initial threshold
295+
)
296+
297+
# Add some data to the cache
298+
paris_key = cache.store(
299+
prompt="what is the capital of france?",
300+
response="paris"
301+
)
302+
303+
# Define test data for optimization
304+
test_data = [
305+
{
306+
"query": "What's the capital of France??",
307+
"query_match": paris_key # Expected cache hit
308+
},
309+
{
310+
"query": "What's the capital of Britain?",
311+
"query_match": "" # Expected cache miss
312+
}
313+
]
314+
315+
# Optimize the threshold
316+
optimizer = CacheThresholdOptimizer(cache, test_data)
317+
optimizer.optimize()
318+
319+
print(f"Optimized threshold: {cache.distance_threshold}")
320+
```
321+
322+
### Router Threshold Optimization
323+
324+
Optimize thresholds for semantic routers to improve routing accuracy:
325+
326+
```python
327+
from redisvl.extensions.router import Route, SemanticRouter
328+
from redisvl.utils.vectorize import HFTextVectorizer
329+
from redis_retrieval_optimizer.threshold_optimization import RouterThresholdOptimizer
330+
331+
# Define routes
332+
routes = [
333+
Route(
334+
name="greeting",
335+
references=["hello", "hi"],
336+
metadata={"type": "greeting"},
337+
distance_threshold=0.5,
338+
),
339+
Route(
340+
name="farewell",
341+
references=["bye", "goodbye"],
342+
metadata={"type": "farewell"},
343+
distance_threshold=0.5,
344+
),
345+
]
346+
347+
# Create router
348+
router = SemanticRouter(
349+
name="my-router",
350+
vectorizer=HFTextVectorizer(),
351+
routes=routes,
352+
redis_url="redis://localhost:6379"
353+
)
354+
355+
# Define test data
356+
test_data = [
357+
{"query": "hello there", "query_match": "greeting"},
358+
{"query": "goodbye", "query_match": "farewell"},
359+
{"query": "hola", "query_match": "greeting"}, # Spanish
360+
]
361+
362+
# Optimize route thresholds
363+
optimizer = RouterThresholdOptimizer(router, test_data)
364+
optimizer.optimize(max_iterations=20, search_step=0.1)
365+
366+
print(f"Optimized thresholds: {router.route_thresholds}")
367+
```
368+
369+
### Evaluation Metrics
370+
371+
Threshold optimization supports multiple evaluation metrics:
372+
373+
```python
374+
from redis_retrieval_optimizer.threshold_optimization import EvalMetric
375+
376+
# Available metrics
377+
optimizer = CacheThresholdOptimizer(cache, test_data, eval_metric="f1") # F1 score (default)
378+
optimizer = CacheThresholdOptimizer(cache, test_data, eval_metric="precision") # Precision
379+
optimizer = CacheThresholdOptimizer(cache, test_data, eval_metric="recall") # Recall
380+
```
381+
382+
For complete documentation and examples, see [docs/examples/threshold_optimization](docs/examples/threshold_optimization/).
383+
384+
## 🔧 Custom Processors and Search Methods
236385

237386
The Retrieval Optimizer is designed to be flexible and extensible. You can define your own **corpus processors** and **search methods** to support different data formats and retrieval techniques. This is especially useful when working with domain-specific data or testing out experimental search strategies.
238387

@@ -340,7 +489,7 @@ def process_car_corpus(corpus, emb_model):
340489

341490
### Running the custom study
342491

343-
Once youve defined your search methods and processor, pass them into the study runner:
492+
Once you've defined your search methods and processor, pass them into the study runner:
344493

345494
```python
346495
from redis_retrieval_optimizer.grid_study import run_grid_study
@@ -366,15 +515,15 @@ metrics = run_grid_study(
366515
| basic_vector | sentence-transformers/all-MiniLM-L6-v2 | 0.002605 | 0.9 | 0.23 | 0.717676 |
367516

368517

369-
## Data requirements
518+
## 📊 Data Requirements
370519

371520
To run a retrieval study, you need three key datasets: **queries**, **corpus**, and **qrels**. The framework is flexible—data can be in any shape as long as you provide custom processors to interpret it. But if you're just getting started, here's the expected format and some working examples to guide you.
372521

373522
---
374523

375524
### Corpus
376525

377-
This is the full set of documents you'll be searching against. Its what gets indexed into Redis. The default assumption is that each document has a `text` field to search or embed, but you can customize this using a corpus processor.
526+
This is the full set of documents you'll be searching against. It's what gets indexed into Redis. The default assumption is that each document has a `text` field to search or embed, but you can customize this using a corpus processor.
378527

379528
**General structure**:
380529

@@ -423,7 +572,7 @@ These are the search inputs you'll evaluate against the corpus. Each query consi
423572
}
424573
```
425574

426-
> 💡 Using custom query metadata? Thats fine—just make sure your custom search method knows how to interpret it.
575+
> 💡 Using custom query metadata? That's fine—just make sure your custom search method knows how to interpret it.
427576

428577
---
429578

@@ -461,7 +610,8 @@ Qrels define the relevance of documents to each query. They are required for eva
461610

462611
> 🔍 Note: Relevance scores can be binary (`1` or `0`) for classification metrics or ranked (`2`, `1`, etc.) for ranking metrics like NDCG.
463612

464-
# Contributing
613+
## 🤝 Contributing
614+
465615
We love contributors if you have an addition follow this process:
466616
- Fork the repo
467617
- Make contribution

docs/api/threshold_optimizer.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
********************
2+
Threshold Optimizers
3+
********************
4+
5+
CacheThresholdOptimizer
6+
=======================
7+
8+
.. _cachethresholdoptimizer_api:
9+
10+
.. currentmodule:: redis_retrieval_optimizer.threshold_optimization.cache
11+
12+
.. autoclass:: CacheThresholdOptimizer
13+
:show-inheritance:
14+
:members:
15+
16+
17+
RouterThresholdOptimizer
18+
========================
19+
20+
.. _routerthresholdoptimizer_api:
21+
22+
.. currentmodule:: redis_retrieval_optimizer.threshold_optimization.router
23+
24+
.. autoclass:: RouterThresholdOptimizer
25+
:show-inheritance:
26+
:members:

0 commit comments

Comments
 (0)