From ab884c16d51d1387a0160c2b992741f6b76f184f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=81ukawski?= Date: Wed, 22 Oct 2025 13:22:46 +0200 Subject: [PATCH] docs: Add course content for Day 3 (#1943) * docs: Add course content for Day 3 * docs: update day 3 _index.md * made mockup more mockup * fixed image links * added video links for sparse vectors * added lost in PR dissection images * added videos/notebook * docs: update pitstop-project.md structure * docs: add global vocabulary for sparse vectors * pitstop setup added * pitstop fix * docs: update Discord link in pitstop-project.md --------- Co-authored-by: Kirstin Co-authored-by: Evgeniya Sukhodolskaya --- .../content/course/essentials/day-3/_index.md | 21 +- .../essentials/day-3/hybrid-search-demo.md | 392 ++++++++++++++ .../course/essentials/day-3/hybrid-search.md | 293 ++++++++++ .../essentials/day-3/pitstop-project.md | 350 ++++++++++++ .../essentials/day-3/sparse-retrieval-demo.md | 503 ++++++++++++++++++ .../course/essentials/day-3/sparse-vectors.md | 217 ++++++++ .../static/courses/day3/inverted_index.png | Bin 0 -> 44072 bytes .../static/courses/day3/sparse_neural.png | Bin 0 -> 95089 bytes 8 files changed, 1773 insertions(+), 3 deletions(-) create mode 100644 qdrant-landing/content/course/essentials/day-3/hybrid-search-demo.md create mode 100644 qdrant-landing/content/course/essentials/day-3/hybrid-search.md create mode 100644 qdrant-landing/content/course/essentials/day-3/pitstop-project.md create mode 100644 qdrant-landing/content/course/essentials/day-3/sparse-retrieval-demo.md create mode 100644 qdrant-landing/content/course/essentials/day-3/sparse-vectors.md create mode 100644 qdrant-landing/static/courses/day3/inverted_index.png create mode 100644 qdrant-landing/static/courses/day3/sparse_neural.png diff --git a/qdrant-landing/content/course/essentials/day-3/_index.md b/qdrant-landing/content/course/essentials/day-3/_index.md index 7709f3a5c..23d68e6ae 100644 --- a/qdrant-landing/content/course/essentials/day-3/_index.md +++ b/qdrant-landing/content/course/essentials/day-3/_index.md @@ -1,9 +1,24 @@ --- -title: Day 3 +title: "Day 3: Hybrid Search" isLesson: true -weight: 7 +weight: 4 --- {{< date >}} Day 3 {{< /date >}} -# Day 3 +# Hybrid Search + +Combine dense and sparse signal for precision and recall. + +--- + +## Today’s path + +1. Sparse Vectors and Inverted Indexes +2. Keyword‑based search demo +3. Hybrid Search with Score Fusion +4. Demo: Implementing a Hybrid Search System +5. Project: Building a Hybrid Search Engine + +You’ll build a hybrid engine and validate gains over dense‑only or sparse‑only search. + diff --git a/qdrant-landing/content/course/essentials/day-3/hybrid-search-demo.md b/qdrant-landing/content/course/essentials/day-3/hybrid-search-demo.md new file mode 100644 index 000000000..a5c7a1456 --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-3/hybrid-search-demo.md @@ -0,0 +1,392 @@ +--- +title: "Demo: Implementing a Hybrid Search System" +weight: 4 +--- + +{{< date >}} Day 3 {{< /date >}} + +# Demo: Implementing a Hybrid Search System + +Build a complete hybrid search system with hands-on examples. + +
+ +
+ +
+ +## What You'll Learn + +- Step-by-step hybrid search implementation +- RRF algorithm in practice +- Performance optimization techniques +- Testing and evaluation methods + +**Follow along in Colab:** + Open In Colab + + +## What You'll Discover + +In the previous lesson, you learned the theory behind hybrid search and the Universal Query API. Today you'll implement it hands-on with a real dataset, comparing dense and sparse vector search and combining them using fusion algorithms. + +**You'll learn to:** +- Create collections with both dense and sparse named vectors +- Compare dense vs. sparse search behavior on real queries +- Implement Reciprocal Rank Fusion (RRF) for hybrid search +- Explore Distribution-Based Score Fusion (DBSF) as an alternative +- Understand the limitations and strengths of each approach + +## The Hybrid Search Challenge + +Working with both semantic (dense) and lexical (sparse) search presents interesting challenges: +- **Different scoring systems**: Dense typically uses cosine similarity (\[-1, 1\]), sparse uses BM25 (unbounded) +- **Different result sets**: The same query may return completely different documents +- **Vocabulary sensitivity**: Sparse search can return fewer results or none if keywords don't match +- **User diversity**: Some users know exact terms, others use natural language + +## Step 1: Environment Setup + +### Install Required Libraries + +```python +!pip install -q qdrant-client[fastembed] +``` + +**Why the fastembed extra?** This includes FastEmbed, which provides built-in models for generating both dense and sparse embeddings without additional dependencies. You won't need separate libraries for OpenAI or other embedding providers. + +### Connect to Qdrant Cloud + +Qdrant Cloud provides the persistence and performance needed for hybrid search experimentation: + +```python +from qdrant_client import QdrantClient +from google.colab import userdata + +client = QdrantClient( + location="https://your-cluster-url.cloud.qdrant.io:6333", + api_key=userdata.get("api-key") +) +``` + +**Using Google Colab secrets:** The `userdata.get()` function accesses secrets stored in your Colab environment, similar to environment variables. This keeps your API key secure and out of your code. + +## Step 2: Create Collection with Named Vectors + +For hybrid search, we need a collection that supports both sparse and dense vectors. Qdrant allows multiple named vectors per point: + +```python +from qdrant_client import models + +# Define the collection name +collection_name = "hybrid_search_demo" + +# Create our collection with both sparse (bm25) and dense vectors +client.create_collection( + collection_name=collection_name, + vectors_config={ + "dense": models.VectorParams( + distance=models.Distance.COSINE, + size=384, + ), + }, + sparse_vectors_config={ + "sparse": models.SparseVectorParams( + modifier=models.Modifier.IDF + ) + } +) +``` + +**Key configuration details:** +- **Named vectors**: `"dense"` and `"sparse"` identify each vector type +- **Dense configuration**: 384 dimensions (matches sentence-transformers/all-MiniLM-L6-v2) +- **Cosine distance**: Typical choice for semantic similarity +- **IDF modifier**: Inverse Document Frequency weighting for BM25 sparse vectors +- **Same collection**: Both vectors exist on the same points for hybrid search + +## Step 3: Upload the Cheese Dataset + +We're using a small dataset of 10 documents describing different types of cheese and cheese-based dishes. This simple dataset makes it easy to observe the behavior of different search methods: + +```python +documents = [ + "Aged Gouda develops a crystalline texture and nutty flavor profile after 18 months of maturation.", + "Mature Gouda cheese becomes grainy and develops a rich, buttery taste with extended aging.", + "Brie cheese features a soft, creamy interior surrounded by an edible white rind.", + "This French cheese has a flowing, buttery center encased in a bloomy white crust.", + "Fresh mozzarella pairs beautifully with ripe tomatoes and basil leaves.", + "Classic Margherita pizza topped with tomato sauce, mozzarella, and fresh basil.", + "Parmesan requires at least 12 months of cave aging to develop its signature sharp taste.", + "Parmigiano-Reggiano's distinctive piquant flavor comes from extended maturation in controlled environments.", + "Grilled cheese sandwiches are the ultimate American comfort food for cold winter days.", + "Croque Monsieur combines ham and Gruyère in France's answer to the toasted cheese sandwich.", +] +``` + +Now upload with both dense and sparse embeddings: + +```python +import uuid + +client.upsert( + collection_name=collection_name, + points=[ + models.PointStruct( + id=uuid.uuid4().hex, + vector={ + "dense": models.Document( + text=doc, + model="sentence-transformers/all-MiniLM-L6-v2", + ), + "sparse": models.Document( + text=doc, + model="Qdrant/bm25", + ), + }, + payload={"text": doc}, + ) + for doc in documents + ] +) +``` + +**About this approach:** +- **Document model**: Automatically generates embeddings using specified models +- **Dual embedding**: Each point gets both dense and sparse representations +- **Small dataset**: 10 documents is perfect for observing search behavior differences +- **No batching needed**: For production with larger datasets, implement batching and retry logic + +## Step 4: Compare Dense vs. Sparse Search + +Let's create helper functions to test each search method independently. First, dense search: + +```python +def dense_search(query: str) -> list[models.ScoredPoint]: + response = client.query_points( + collection_name=collection_name, + query=models.Document( + text=query, + model="sentence-transformers/all-MiniLM-L6-v2", + ), + using="dense", + limit=3, + ) + return response.points +``` + +Now sparse search: + +```python +def sparse_search(query: str) -> list[models.ScoredPoint]: + response = client.query_points( + collection_name=collection_name, + query=models.Document( + text=query, + model="Qdrant/bm25", + ), + using="sparse", + limit=3, + ) + return response.points +``` + +### Test Queries Across Both Methods + +Now let's run both methods on different query types: + +```python +queries = [ + "nutty aged cheese", + "soft French cheese", + "pizza ingredients", + "a good lunch", +] + +for query in queries: + print("Query:", query) + + dense_results = dense_search(query) + print("Dense Results:") + for result in dense_results: + print("\t-", result.payload["text"], result.score) + + sparse_results = sparse_search(query) + print("Sparse Results:") + for result in sparse_results: + print("\t-", result.payload["text"], result.score) + print() +``` + +### Key Observations from Results + +1. **Dense and sparse produce different rankings**: For "nutty aged cheese", sparse correctly identifies the exact match as #1, while dense ranks a semantically similar document higher +2. **Sometimes rankings match**: For "soft French cheese", both methods agree on the top results, but with different confidence scores +3. **Dense always returns expected results**: Dense search will always return 3 results because any two vectors have some similarity, even if extremely low +4. **Sparse can return fewer results**: For "pizza ingredients", sparse only returns 1 result. For "a good lunch", sparse returns 0 results due to vocabulary mismatch +5. **Vocabulary mismatch problem**: When query terms don't appear in documents, sparse search fails completely, while dense understands the semantic intent + +## Step 5: Hybrid Search with Reciprocal Rank Fusion + +Now let's combine both methods using RRF. This fusion algorithm doesn't compare incompatible scores - it only uses the ranking order: + +```python +def rrf_search(query: str) -> list[models.ScoredPoint]: + response = client.query_points( + collection_name=collection_name, + prefetch=[ + models.Prefetch( + query=models.Document( + text=query, + model="Qdrant/bm25", + ), + using="sparse", + limit=3, + ), + models.Prefetch( + query=models.Document( + text=query, + model="sentence-transformers/all-MiniLM-L6-v2", + ), + using="dense", + limit=3, + ) + ], + query=models.FusionQuery(fusion=models.Fusion.RRF), + limit=3, + ) + return response.points +``` + +**How RRF works in this code:** +- **Prefetch from both**: Retrieve top 3 from sparse AND dense search +- **FusionQuery**: Applies RRF algorithm to combine rankings +- **Single API call**: The entire hybrid pipeline executes in one request +- **Result**: Documents that perform well in both methods rank higher + +### RRF Results Analysis + +```python +for query in queries: + print("Query:", query) + + rrf_results = rrf_search(query) + print("RRF Results:") + for result in rrf_results: + print("\t-", result.payload["text"], result.score) + print() +``` + +**What to notice:** +1. **Best of both worlds**: Results include documents from both dense and sparse searches +2. **Ranking preservation**: When both methods agree (like "soft French cheese"), RRF maintains the consensus +3. **Handles sparse gaps**: When sparse returns fewer results (or none), dense search fills the gaps +4. **Balanced scoring**: Documents ranked highly by both methods get boosted in RRF scores + +## Step 6: Distribution-Based Score Fusion (DBSF) + +RRF isn't the only fusion method available. DBSF normalizes scores from each query and sums them across different retrievers: + +```python +def dbsf_search(query: str) -> list[models.ScoredPoint]: + response = client.query_points( + collection_name=collection_name, + prefetch=[ + models.Prefetch( + query=models.Document( + text=query, + model="Qdrant/bm25", + ), + using="sparse", + limit=3, + ), + models.Prefetch( + query=models.Document( + text=query, + model="sentence-transformers/all-MiniLM-L6-v2", + ), + using="dense", + limit=3, + ) + ], + query=models.FusionQuery(fusion=models.Fusion.DBSF), + limit=3, + ) + return response.points +``` + +### DBSF Results + +```python +for query in queries: + print("Query:", query) + + dbsf_results = dbsf_search(query) + print("DBSF Results:") + for result in dbsf_results: + print("\t-", result.payload["text"], result.score) + print() +``` + +**Comparing DBSF to RRF:** +- In this simple example, DBSF and RRF produce identical rankings for all queries +- This is NOT a general rule - different fusion methods can produce different results +- With larger datasets and more complex queries, the differences become more apparent +- DBSF considers score distributions, while RRF only uses rank positions + +## Evaluation Considerations + +**Did fusion improve search quality?** We can't definitively say without proper evaluation. Here's why: + +### The Challenge of Search Quality + +- **Subjective relevance**: "Best results" depend on unknown user intentions +- **No ground truth**: We don't have a reference dataset defining expected outputs +- **Context matters**: Different users might prefer different results for the same query + +### Proper Evaluation Requires + +1. **Ground truth dataset**: Define expected results for each query +2. **Metrics**: Use precision, recall, NDCG, or other relevance metrics +3. **User feedback**: Collect real user satisfaction data +4. **A/B testing**: Compare different strategies in production + +**For this demo:** We're "eyeballing" results to understand behavior, but production systems need rigorous evaluation frameworks. + +## Summary & Key Takeaways + +**What you've built:** A complete hybrid search pipeline using Qdrant's Universal Query API that combines dense semantic search with sparse keyword search in a single call. + +**Key insights:** +1. **Dense vs. Sparse behavior**: Dense always returns results (semantic), sparse can return none (keyword match) +2. **Fusion solves incompatibility**: RRF and DBSF combine rankings without comparing incompatible scores +3. **Single API call**: The Universal Query API makes complex pipelines simple +4. **Complementary strengths**: Dense handles vague queries, sparse handles exact matches +5. **Evaluation matters**: Proper testing requires ground truth datasets and metrics + +**Production recommendations:** +- Start with RRF - it's simple and effective +- Test DBSF if you need score-distribution awareness +- Build evaluation datasets for your specific domain +- Monitor user satisfaction metrics +- Consider adding specialized rerankers for even better quality + +## Next Steps & Resources + +**What's next:** +1. **Experiment with parameters**: Adjust prefetch limits, try different embedding models +2. **Add reranking**: Explore more complex models for final reranking stage, such as cross-encoders +3. **Build ground truth**: Create evaluation datasets for your use case +4. **Test on your data**: Apply these techniques to domain-specific datasets + +**Additional resources:** +- [Qdrant Documentation: Hybrid Search](/documentation/concepts/hybrid-queries/) - Complete technical reference +- [Universal Query API Guide](/documentation/concepts/search/#search-api) - Advanced usage patterns + +**Ready for the next challenge?** You've mastered hybrid search fundamentals. These same techniques scale to millions of documents and power production search systems! \ No newline at end of file diff --git a/qdrant-landing/content/course/essentials/day-3/hybrid-search.md b/qdrant-landing/content/course/essentials/day-3/hybrid-search.md new file mode 100644 index 000000000..f35c22b36 --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-3/hybrid-search.md @@ -0,0 +1,293 @@ +--- +title: Hybrid Search and the Universal Query API +weight: 3 +--- + +{{< date >}} Day 3 {{< /date >}} + +# Hybrid Search and the Universal Query API + +Learn how to combine dense and sparse vector search methods to build powerful hybrid search pipelines that serve diverse user needs. + +
+ +
+ +
+ +## What You'll Learn + +- Understand when to use dense vs. sparse vectors +- Build hybrid search pipelines with Qdrant's Universal Query API +- Apply Reciprocal Rank Fusion (RRF) to combine results +- Design multi-stage retrieval and reranking strategies + +## The Challenge: Different Users, Different Search Needs + +The reality is that your users exist across a spectrum: from precise keyword searchers to vague natural language describers, and forcing a single search approach means disappointing part of your audience. Rather than compromising on search quality for different user types, hybrid search allows you to meet everyone where they are. + +### Dense Vectors: Semantic Understanding + +Dense vectors are produced by neural encoders trained in a way that associates meaning with the inputs. No matter if you ask for an **elevator** or a **lift**, your intent is to get to another floor, even though the words differ. Even an escalator, or just stairs, should not be that far away, as they serve the same purpose! + +Some models can also find what you need when you use: +- Spanish: **ascensor** +- Polish: **winda** +- German: **der Aufzug** + +Or any other language, as these models might be trained to support many of them simultaneously. + +### Sparse Vectors: Exact Matching + +On the other hand, sparse vectors are more suitable for cases where the exact match matters. They are quite often described as keyword-based or lexical search. + +Imagine you know the identifier of an item you want to find. For example, you have a particular smartphone model, so when buying some accessories, you don't want to see all the possible chargers for different devices, but only those you can use. That's where lexical search would be better suited than the dense vector search! + +### Real-World Complexity: Legal Search Example + +Unfortunately, reality is never that simple. Some of your users might be domain experts and use the same terminology as you do, so implementing just keyword-based search will be totally fine for their purposes. However, you don't want to ignore the part of your audience that can't express their intentions in proper terms. Some of your users may tend to produce vague, natural language-like descriptions of what they want to find, and in that case, dense vectors will capture that way better. + +**Imagine a legal search system:** +- **Lawyers** will typically know the paragraph, section, or even a point of the legal act they want to find +- **Others** would rather describe a very specific case they encountered +- **Mixed cases**: Maybe somebody knows the particular act, but not the paragraph? + +You don't need to choose just one method or build separate pipelines to serve both types of users! Hybrid search might be the solution you are looking for! + +## What is Hybrid Search? + +There is no single definition of hybrid search, but in general, we can call a specific pipeline hybrid when it combines at least two different search methods. + +It might be dense and sparse vectors in a chain, in which you: +1. **Prefetch** some candidates with dense vector search, then **rerank** them with sparse +2. Or vice versa: use sparse for retrieval and dense for reranking + +So, we have **retrievers** and **rerankers**. + +### Complex Multi-Stage Pipelines + +The options for building a hybrid search are endless. In some cases, people create really complex multi-stage search pipelines with: +- Faster methods for the initial retrieval +- Slower but more precise models reranking these pre-retrieved candidates + +Qdrant's Universal Query API allows you to build such complex pipelines in a single call. + +## Universal Query API + +The Universal Query API was introduced in Qdrant 1.10. It consolidated all the other APIs in a single method and, most importantly for us, made it easier to build hybrid search pipelines. + +### Pattern 1: Dense Retrieval → Sparse Reranking + +Here is how the simplest chain of dense retrieval and sparse reranking could look, assuming we have already created a collection containing two named vectors: + +```python +from qdrant_client import QdrantClient, models + +client = QdrantClient(...) +client.query_points( + collection_name="my_collection", + prefetch=[ + models.Prefetch( + query=models.Document( + text=query, + model="sentence-transformers/all-MiniLM-L6-v2", + ), + using="dense", + limit=20, + ), + ], + query=models.Document( + text=query, + model="Qdrant/bm25", + ), + using="sparse", + limit=20, +) +``` + +In this example, we: +1. **Prefetched** twenty results with dense vector search +2. Then **sparse vectors** were used to rearrange them in an order defined by sparse relevancy scores + +That means the sparse method was not used on all the points in a collection, but just a small subset of these twenty candidates. + +### Pattern 2: Sparse Retrieval → Dense Reranking + +If we swap the usage of both vectors, then we will have a different pipeline with sparse retrieval and dense reranking: + +```python +from qdrant_client import QdrantClient, models + +client = QdrantClient(...) +client.query_points( + collection_name="my_collection", + prefetch=[ + models.Prefetch( + query=models.Document( + text=query, + model="Qdrant/bm25", + ), + using="sparse", + limit=20, + ), + ], + query=models.Document( + text=query, + model="sentence-transformers/all-MiniLM-L6-v2", + ), + using="dense", + limit=20, +) +``` + +### The Limitation of Sequential Search + +Using multiple search methods in sequence isn't always the best approach. Consider our earlier examples where we either: + +1. Retrieve documents with dense vectors and then rerank with sparse method, or +2. Retrieve with the sparse method and then rerank with the dense one + +**These two approaches can produce completely different results.** The initial retrieval method is actually more critical than the reranking step, because reranking can only work with what the retriever has already selected. + +For instance, if there's a perfect document match that only sparse retrieval would identify, but you use dense retrieval first, you'll never find that document since it won't be included in the candidates passed to the reranker. + +There are ways of incorporating both signals! Let's discuss fusion! + +## Fusion: Combining Search Signals + +Each retrieval method produces a numerical score assigned to each document it selects. These scores can't be directly compared to each other between different methods, so there is no clear way to mix candidates. + +**The scoring problem:** +- Your dense retrieval may produce cosine similarity that never exceeds 1 +- BM25 scores are virtually unbounded +- How would you decide whether a cosine similarity of 0.9 is better than a BM25 score of 10.7? + +Fusion algorithms are designed to solve that. **Reciprocal Rank Fusion** is one of the most common techniques, so let's look at it. + +## Reciprocal Rank Fusion (RRF) + +The original scores returned by each method are primarily used to rank the documents by their relevance, so they define the order of how good each result is. RRF does not take the scores into account, but just the ranking defined by them, and calculates its own score based on the ranks returned by each individual method. + +### The RRF Formula + +$$\text{RRF\_score}(d) = \sum_{i=1}^{n} \frac{1}{k + \text{rank}_i(d)}$$ + +Where: +- `d` is the document +- `k` is a constant (typically 60) +- `n` is the number of retrieval methods +- `rank_i(d)` is the rank of document `d` in the i-th retrieval method + +The value of the `k` parameter can help you increase or decrease the impact of the lower ranked documents. The smaller the parameter value, the bigger the impact of the top-ranked results. + +### RRF Example: Step by Step + +Let's assume we have these results coming from both methods: + +**Dense Search Results:** +1. D1 (score: 0.95) +2. D2 (score: 0.89) +3. D3 (score: 0.85) +4. D4 (score: 0.82) + +**Sparse Search Results:** +1. D5 (score: 15.2) +2. D3 (score: 12.8) +3. D1 (score: 10.1) +4. D2 (score: 8.5) + +Now let's calculate RRF scores (using k=60): + +| Document | Dense Rank | Dense RRF | Sparse Rank | Sparse RRF | Total RRF | Final Rank | +|----------|------------|-------------------|-------------|-------------------|-----------|------------| +| D1 | 1 | 1/(60+1) = 0.0164 | 3 | 1/(60+3) = 0.0159 | 0.0323 | 2 | +| D2 | 2 | 1/(60+2) = 0.0161 | 4 | 1/(60+4) = 0.0156 | 0.0317 | 3 | +| D3 | 3 | 1/(60+3) = 0.0159 | 2 | 1/(60+2) = 0.0161 | 0.0320 | **1** | +| D4 | 4 | 1/(60+4) = 0.0156 | - | 0 | 0.0156 | 5 | +| D5 | - | 0 | 1 | 1/(60+1) = 0.0164 | 0.0164 | 4 | + +**Notice:** D3 became the winner, even though it was not the best match for any of the individual methods! However, both ranked it quite high, so it probably captures both lexical and semantic meaning. + +## Implementing RRF in Qdrant + +If you would like to implement that process in Qdrant, then you don't have to calculate the RRF scores on your own. It's just built in! + +This is an API call that you would have to make in order to retrieve twenty documents from both dense and sparse search, and then get just the top ten results based on the Reciprocal Rank Fusion: + +```python +from qdrant_client import QdrantClient, models + +client = QdrantClient(...) +client.query_points( + collection_name="my_collection", + prefetch=[ + models.Prefetch( + query=models.Document( + text=query, + model="sentence-transformers/all-MiniLM-L6-v2", + ), + using="dense", + limit=20, + ), + models.Prefetch( + query=models.Document( + text=query, + model="Qdrant/bm25", + ), + using="sparse", + limit=20, + ), + ], + query=models.FusionQuery(fusion=models.Fusion.RRF), + limit=10, +) +``` + +**Key points:** +- We prefetch 20 results from both dense and sparse methods +- The `FusionQuery` applies RRF to combine them +- We get the final top 10 results based on fused scores + +Obviously, you can nest multiple prefetch operations, and apply the fusion at any level. + +## Beyond Fusion: Advanced Reranking + +Fusion is not the only way to build a hybrid search. Sometimes, you have a bigger set of weak retrievers and one strong reranker, which might be: + +- A different neural model, such as a **cross-encoder** +- A more sophisticated dense retriever, which would practically be inapplicable for initial retrieval +- Multivector representations, such as **ColBERT** +- Custom business rules specific to your project + +**Hybrid search might be really complex.** Reranking is a general term for all the methods we apply to the results of some intermediate search operations, and fusion is one way of tackling it. + +## Key Takeaways + +**What you've learned:** +- Dense vectors excel at semantic understanding and multilingual search +- Sparse vectors are ideal for exact matching and keyword-based retrieval +- Sequential retrieval + reranking has limitations (the first stage determines what's available) +- Fusion algorithms like RRF combine results from multiple methods effectively +- Qdrant's Universal Query API makes building complex hybrid pipelines simple + +**When to use hybrid search:** +- Your users have diverse search behaviors (experts vs. casual users) +- You need both semantic understanding and exact matching +- Single-method search struggles with some query types +- You want to improve overall search quality and user satisfaction + +## Next Steps & Resources + +In the next lesson, we will build a hybrid search pipeline using Qdrant's Universal Query API with real data. Now that you know how it might be done, it's time to get your hands dirty with real implementation! + +**Additional resources:** +- [Qdrant Documentation: Hybrid Search](/documentation/concepts/hybrid-queries/) - Complete technical reference +- [Universal Query API Guide](/documentation/concepts/search/#search-api) - API documentation + +**Ready for hands-on practice?** In the next demo, you'll implement a real hybrid search, test different strategies, and measure the impact on search quality! diff --git a/qdrant-landing/content/course/essentials/day-3/pitstop-project.md b/qdrant-landing/content/course/essentials/day-3/pitstop-project.md new file mode 100644 index 000000000..63e89cdab --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-3/pitstop-project.md @@ -0,0 +1,350 @@ +--- +title: "Project: Building a Hybrid Search Engine" +weight: 5 +--- + +{{< date >}} Day 3 {{< /date >}} + +# Project: Building a Hybrid Search Engine + +Build a hybrid system that combines dense and sparse vectors with Reciprocal Rank Fusion, demonstrating how to get the best of both semantic understanding and keyword precision. + +## Your Mission + +Create a production-ready hybrid search system that leverages both dense and sparse vectors to deliver superior search results. You'll implement the complete hybrid pipeline and compare its performance against single-vector approaches. + +**Estimated Time:** 75 minutes + +## What You'll Build + +A hybrid search system that demonstrates: + +- **Dense vector search** for semantic understanding +- **Sparse vector search** for exact keyword matching +- **Reciprocal Rank Fusion** to combine results intelligently +- **Performance comparison** between hybrid and single-vector approaches +- **Domain optimization** for your specific use case + +## Setup +### Prerequisites + +* Qdrant Cloud cluster (URL + API key) +* Python 3.9+ (or Google Colab) +* Packages: `qdrant-client`, `sentence-transformers` + +### Models + +* Dense encoder: `sentence-transformers/all-MiniLM-L6-v2` (384-dim) + +### Dataset + +* A small domain dataset (e.g., 100–500 items) with at least: + + * `title` (string) + * `description` (string) — used for both dense and sparse encoders + * Optional metadata fields for later filtering + +## Build Steps + +### Step 1: Set Up Hybrid Collection + +Build on your previous work by creating a collection with both dense and sparse vectors: + +```python +from qdrant_client import QdrantClient, models +from sentence_transformers import SentenceTransformer +import time + +client = QdrantClient( + "https://your-cluster-url.cloud.qdrant.io", + api_key="your-api-key" +) + +collection_name = "day3_hybrid_search" + +# Create hybrid collection +client.create_collection( + collection_name=collection_name, + vectors_config={ + "dense": models.VectorParams(size=384, distance=models.Distance.COSINE) + }, + sparse_vectors_config={ + "sparse": models.SparseVectorParams( + index=models.SparseIndexParams(on_disk=False) + ) + } +) +``` + +### Step 2: Implement Dense and Sparse Encoding + +```python +# Dense embeddings +encoder = SentenceTransformer("all-MiniLM-L6-v2") + +# Global vocabulary - automatically extends as new texts are processed +global_vocabulary = {} + +# Simple sparse encoding (BM25-style) +def create_sparse_vector(text): + """Create sparse vector from text using term frequency""" + from collections import Counter + import re + + # Simple tokenization + words = re.findall(r"\b\w+\b", text.lower()) + word_counts = Counter(words) + + # Convert to sparse vector format, extending vocabulary as needed + indices = [] + values = [] + + for word, count in word_counts.items(): + if word not in global_vocabulary: + # Add new word to vocabulary with next available index + global_vocabulary[word] = len(global_vocabulary) + + indices.append(global_vocabulary[word]) + values.append(float(count)) + + return models.SparseVector(indices=indices, values=values) + +# Upload hybrid data +points = [] +for i, item in enumerate(your_dataset): + dense_vector = encoder.encode(item["description"]).tolist() + sparse_vector = create_sparse_vector(item["description"]) + + points.append(models.PointStruct( + id=i, + vector={"dense": dense_vector}, + sparse_vector={"sparse": sparse_vector}, + payload=item + )) + +client.upload_points(collection_name=collection_name, points=points) +``` + +### Step 3: Implement Hybrid Search with RRF + +```python +def hybrid_search_with_rrf(query_text, limit=10): + """Perform hybrid search using Reciprocal Rank Fusion""" + + # Encode query for both dense and sparse + query_dense = encoder.encode(query_text).tolist() + query_sparse = create_sparse_vector(query_text) + + # Use Qdrant's built-in RRF + response = client.query_points( + collection_name=collection_name, + prefetch=[ + models.Prefetch( + query=query_dense, + using="dense", + limit=20 + ), + models.Prefetch( + query=query_sparse, + using="sparse", + limit=20 + ) + ], + query=models.FusionQuery(fusion=models.Fusion.RRF), + limit=limit + ) + + return response.points + +# Test hybrid search +results = hybrid_search_with_rrf("your test query") +for i, point in enumerate(results, 1): + print(f"{i}. {point.payload.get('title', 'No title')} (Score: {point.score:.3f})") +``` + +### Step 4: Compare Search Approaches + +```python +def compare_search_methods(query_text): + """Compare dense, sparse, and hybrid search results""" + + print(f"Query: '{query_text}'\n") + + # Dense-only search + dense_results = client.query_points( + collection_name=collection_name, + query=encoder.encode(query_text).tolist(), + using="dense", + limit=5 + ) + + # Sparse-only search + sparse_results = client.query_points( + collection_name=collection_name, + query=create_sparse_vector(query_text), + using="sparse", + limit=5 + ) + + # Hybrid search + hybrid_results = hybrid_search_with_rrf(query_text, limit=5) + + print("DENSE SEARCH:") + for i, point in enumerate(dense_results.points, 1): + print(f" {i}. {point.payload.get('title', 'No title')} ({point.score:.3f})") + + print("\nSPARSE SEARCH:") + for i, point in enumerate(sparse_results.points, 1): + print(f" {i}. {point.payload.get('title', 'No title')} ({point.score:.3f})") + + print("\nHYBRID SEARCH (RRF):") + for i, point in enumerate(hybrid_results, 1): + print(f" {i}. {point.payload.get('title', 'No title')} ({point.score:.3f})") + + print("-" * 50) + +# Test with different query types +test_queries = [ + "exact keyword match query", + "semantic concept query", + "mixed keyword and concept query" +] + +for query in test_queries: + compare_search_methods(query) +``` + +### Step 5: Analyze Your Results + +Use the outputs from Step 4 to evaluate how hybrid compares to the single approaches. Focus on when hybrid fixes dense misses (rare keywords, exact identifiers) and when sparse misses (synonyms/semantic paraphrases). Optionally, time each method (dense/sparse/hybrid) on a few queries and note average latency. + +## Success Criteria + +You'll know you've succeeded when: + + Your hybrid collection contains both dense and sparse vectors + You can perform searches using dense, sparse, and hybrid approaches + RRF fusion combines results from both vector types effectively + You can demonstrate cases where hybrid search outperforms single-vector approaches + You understand the trade-offs between different search methods for your domain + +## Share Your Discovery + +### Step 1: Reflect on Your Findings + +1. When did hybrid search beat dense-only or sparse-only (give concrete query types)? +2. How did RRF change the ranking vs. the individual methods? +3. How did latency compare across dense, sparse, and hybrid (avg + P95)? +4. How did your sparse encoding choice (e.g., TF-IDF/BM25/SPLADE) affect results? + +### Step 2: Post Your Results + +**Post your results in** Post your results in Discord **using this:** + +```markdown +**[Day 3] Building a Hybrid Search Engine** + +**High-Level Summary** +- **Domain:** "I built hybrid search for [your domain]" +- **Winner:** "Hybrid/Dense/Sparse worked best because [one clear reason]" + +**Reproducibility** +- **Collection:** day3_hybrid_search +- **Models:** dense=[id, dim], sparse=[method] +- **Dataset:** [N items] (snapshot: YYYY-MM-DD) + +**Settings (today)** +- **Fusion:** RRF, k_dense=[..], k_sparse=[..] +- **Search:** hnsw_ef=[..] (if used) +- **Sparse encoding:** [TF-IDF/BM25/SPLADE], notes: [e.g., stopwords/stemming] + +**Head-to-Head (demo query: "[your query]")** +- **Dense top-3:** 1) …, 2) …, 3) … +- **Sparse top-3:** 1) …, 2) …, 3) … +- **Hybrid top-3:** 1) …, 2) …, 3) … + +**Latency** +- **Dense:** avg=[..] ms (P95=[..] ms) +- **Sparse:** avg=[..] ms (P95=[..] ms) +- **Hybrid (RRF):** avg=[..] ms (P95=[..] ms) + +**Why these won** +- [one line on synonyms vs exact IDs/keywords, etc.] + +**Surprise** +- "[one unexpected finding]" + +**Next step** +- "[one concrete action for tomorrow]" +``` + +## Optional: Go Further + +### Advanced Fusion Strategies + +Test Distribution-Based Score Fusion (DBSF) as an alternative to RRF: + +```python +# Compare RRF vs DBSF +dbsf_results = client.query_points( + collection_name=collection_name, + prefetch=[ + models.Prefetch(query=query_dense, using="dense", limit=20), + models.Prefetch(query=query_sparse, using="sparse", limit=20) + ], + query=models.FusionQuery(fusion=models.Fusion.DBSF), + limit=10 +) +``` + +### Performance Benchmarking + +Measure and compare search latencies: + +```python +def benchmark_search_methods(query_text, iterations=10): + """Benchmark different search approaches""" + methods = { + "dense": lambda: client.query_points( + collection_name=collection_name, + query=encoder.encode(query_text).tolist(), + using="dense", limit=10 + ), + "sparse": lambda: client.query_points( + collection_name=collection_name, + query=create_sparse_vector(query_text), + using="sparse", limit=10 + ), + "hybrid": lambda: hybrid_search_with_rrf(query_text) + } + + for method_name, method_func in methods.items(): + times = [] + for _ in range(iterations): + start = time.time() + method_func() + times.append((time.time() - start) * 1000) + + avg_time = sum(times) / len(times) + print(f"{method_name}: {avg_time:.2f}ms average") +``` + +### Custom Sparse Encoding + +Implement TFIDF sparse encoding trained on your dataset: + +```python +from sklearn.feature_extraction.text import TfidfVectorizer + +def create_tfidf_sparse_vector(text, vectorizer): + """Create sparse vector using TF-IDF""" + tfidf_matrix = vectorizer.transform([text]) + coo_matrix = tfidf_matrix.tocoo() + + return models.SparseVector( + indices=coo_matrix.col.tolist(), + values=coo_matrix.data.tolist() + ) +``` diff --git a/qdrant-landing/content/course/essentials/day-3/sparse-retrieval-demo.md b/qdrant-landing/content/course/essentials/day-3/sparse-retrieval-demo.md new file mode 100644 index 000000000..13a255ca3 --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-3/sparse-retrieval-demo.md @@ -0,0 +1,503 @@ +--- +title: "Demo: Keyword Search with Sparse Vectors" +weight: 2 +--- + +{{< date >}} Day 3 {{< /date >}} + +# Demo: Keyword Search with Sparse Vectors + +Use sparse vectors for keywords-based text retrieval. + +
+ +
+ +
+ +## What You'll Learn + +- Connection between Sparse Vectors & keywords-based retrieval +- Using BM25 in Qdrant +- Sparse Neural Retrieval +- Using SPLADE++ in Qdrant + +## Text Encoding + +In sparse vectors, each non‑zero dimension represents an object that plays a specific role for the item being represented. When we work with text, the natural choice for these objects is words. + +A corpus, a finite set of texts, makes it feasible to collect every unique word and form a vocabulary. Words in the vocabulary can be ordered and enumerated, which gives us indices for sparse vectors. + +### Bag-of-Words + +Consider a dataset of grocery shop item descriptions in English. The vocabulary for produce can be a relatively small subset of English, ordered from “apple” to “zesty” and enumerated accordingly. + +```text +Vocabulary indices (illustrative): +"cheese" -> 101 +"grated" -> 151 +"hard" -> 190 +"mac" -> 20 +"and" -> 501 +``` + +Using the vocabulary indices, we could represent each description as a sparse vector of `(index, value)` pairs. +The value could be the **term frequency (TF)**. + +**Term Frequency (TF)** +The number of times a word appears in the text. + +- Description: `Grated hard cheese` + Sparse vector: + ```text + [(101, 1.0), (151, 1.0), (190, 1.0)] + ``` + +- Description: `Mac and cheese` + Sparse vector: + ```text + [(20, 1.0), (101, 1.0), (501, 1.0)] + ``` + (Notice the shared index `101` for the word `cheese`) + +- Longer description with repeated terms: `four cheese pizza for cheese lovers` + Sparse vector: + ```text + [(101, 2.0), (130, 1.0), (131, 1.0), (490, 1.0), (705, 1.0)] + ``` + +This representations are called **bag-of-words**: words are placed in a sparse vector like in a bag, without preserving order, but counting their occurrences. + +## The Idea Behind Sparse Text Retrieval + +If texts are represented as sparse vectors, their similarity can be computed with the **dot product**. + +```text +Similarity("Grated hard cheese", "Mac and cheese") = 1.0 * 1.0 = 1.0 + +Similarity("Grated hard cheese", "four cheese pizza for cheese lovers") += 1.0 * 2.0 = 2.0 +``` + +This already hints at the idea behind a keywords-based retrieval system. We could encode all our documents as sparse vectors and retrieve & rank them based on similarity to the query. + +### TF-IDF +Yet in retrieval, we care about **relevance** between documents & queries. +Documents with the matching keywords to the query are probably relevant to it, yet it's not the same for every keyword, while some keywords are more important than others. + +#### IDF +Some keywords are common across many documents (e.g., adjectives like `tasty` or `fresh` in the dataset of grocery shop item descriptions). Others are rare and more specific (e.g., `gorgonzola`, `mozarella`). +In the grocery corpus, the keyword `mozarella` is **more important** than `tasty` because it is **rarer**. + +This importance could be expressed through **Inverse Document Frequency**. + +**Inverse Document Frequency (IDF)** +A corpus-level statistic indicating how many documents contain a term. The rarer the term, the higher its IDF. + +#### TF-IDF Weighting +Let's enhance each document’s bag-of-words representation by scaling each term’s frequency (TF) by its corpus-level IDF. + +```text +"Grated hard cheese" = +[(101, TF("cheese") * IDF("cheese")), + (151, TF("grated") * IDF("grated")), + (190, TF("hard") * IDF("hard"))] +``` + +Similarity then accounts for global term importance: +```text +Similarity("cheese for pizza", "Grated hard cheese") += 1.0 * TF("cheese") * IDF("cheese") +``` + +**TF‑IDF** is a simple, statistical model for keyword-based text retrieval. + +#### IDF in Qdrant + +Computing and maintaining per-term IDF for every term in the corpus can be annoying. +> Qdrant maintains **collection-level** IDF for sparse vectors and applies it for you during scoring. + +Enable the [IDF modifier](https://qdrant.tech/documentation/concepts/indexing/#idf-modifier) in the collection configuration: + +{{< code-snippet path="/documentation/headless/snippets/create-collection/sparse-vector-idf/" >}} + +During retrieval, Qdrant applies the IDF component per keyword when calculating similarity scores. + +## Best Matching 25 (BM25) + +The **TF-IDF** model already provides a good statistical approximation of a keyword’s role in texts. What it doesn’t take into account, is that **the length of documents affects the importance of the words** used in these documents. +TF-IDF will reward longer documents simply because they have more words. + +The very famous formula in information retrieval, **Best Matching 25 (BM25)**, makes several adjustments to the TF-IDF model to include document length in scoring. + +### BM25 Formula +For a query \(Q\) and document \(D\): + +$$ +\mathrm{BM25}(Q, D) \=\ \sum_{i=1}^{N} \mathrm{IDF}(q_i)\ +\frac{\mathrm{TF}(q_i, D)\(k_1 + 1)} +{\mathrm{TF}(q_i, D) + k_1\\left(1 - b + b \cdot \frac{|D|}{\mathrm{avg}_{\text{corpus}}(|D|)}\right)} +$$ + +Most of its components we've already introduced: +- `TF(q_i, D)`: term frequency of query word `q_i` in document `D`. +- `IDF(q_i)`: inverse document frequency of `q_i` (Qdrant can calculate and maintain this). + +Additional parameters controlling document length normalization and TF saturation: +- `k_1`: controls TF saturation (how strongly extra occurrences of a query word in the document increase the score). +- `b`: controls normalization by document length, balancing `|D|` against the corpus average `avg_corpus(|D|)`. + +To design BM25-based retrieval on sparse vectors, the aforementioned TF-IDF representation of documents should be updated with BM25 parameters. +Let’s see how to use the BM25 retriever in practice, in Qdrant. + +### BM25 in Qdrant + +**Follow along in Colab:** + Open In Colab + + +The BM25 formula can be represented as follows: + +$$ +\text{BM25}(Q, D) = \sum_{i=1}^{n} \text{IDF}(q_i) \cdot \mathrm{function}\left(\mathrm{TF}(q_i, D), k_1, b, |D|, \mathrm{avg}_{\text{corpus}}|D|\right) +$$ + +Qdrant provides tooling to compute IDF on the server side. + +When using any retrieval formula that includes IDF, such as BM25, in Qdrant, we no longer need to include the IDF component in the sparse document representations. +The IDF component will be applied by Qdrant automatically when computing similarity scores. + +#### Create a Collection for BM25 Sparse Vectors + +```python +client.create_collection( + collection_name=, + sparse_vectors_config={ + "bm25_sparse_vector": models.SparseVectorParams( + modifier=models.Modifier.IDF #Inverse Document Frequency + ), + }, +) +``` + +> Once enabled, **IDF is maintained at the collection level**. + +#### Create & Insert BM25-based Sparse Vectors + +This leaves us with the following `values` of the documents' words: + +$$ +\text{BM25}(d_i) = \mathrm{function}\left(\mathrm{TF}(d_i, D), k_1, b, |D|, \mathrm{avg}_{\text{corpus}}|D|\right) +$$ + +The FastEmbed Qdrant library provides a way to [generate these BM25 formula-based sparse representations](https://github.com/qdrant/fastembed/blob/main/fastembed/sparse/bm25.py). + +> **Update:** Since Qdrant's release [1.15.2](https://github.com/qdrant/qdrant/pull/6891), the conversion to BM25 sparse vectors happens directly in Qdrant, for all the supported Qdrant clients. +> Interface-wise, it looks the same as the local inference with FastEmbed, as we show here. +> Implementation-wise, conversion to sparse representations is also the same. + +The integration between Qdrant and FastEmbed allows you to simply pass your texts and BM25 formula parameters when indexing documents to Qdrant. The conversion to sparse vectors happens under the hood. + +> Don’t forget to enable the `IDF` modifier when using BM25-based sparse representations generated by FastEmbed (or since 1.15.2 by Qdrant), as they intentionally exclude this component. + +```python +grocery_items_descriptions = [ + "Grated hard cheese", + ... +] + +#Estimating the average length of documents in the corpus +avg_document_length = sum(len(description.split()) for description in grocery_items_descriptions) / len(grocery_items_descriptions) + +client.upsert( + collection_name=, + points=[ + models.PointStruct( + id=i, + payload={"text": description}, + vector={ + "bm25_sparse_vector": models.Document( + text=description, + model="Qdrant/bm25", + options={"avg_len": avg_document_length} #To pass BM25 parameters, here we're using default k & b for the BM25 formula + ) + }, + ) for i, description in enumerate(grocery_items_descriptions) + ], +) +``` + +#### BM25 in FastEmbed (Qdrant): Implementation Details + +**Corpus Average Length** + +Qdrant and FastEmbed do not compute $\mathrm{avg}_{\text{corpus}}|D|$ (the average document length in the corpus). You must **estimate and provide this value** as a BM25 parameter. + +**Default BM25 Parameters in FastEmbed (Qdrant)** + +- `k = 1.2` +- `b = 0.75` + +**Text Processing Pipeline** + +FastEmbed (Qdrant) uses the [Snowball stemmer](https://www.geeksforgeeks.org/snowball-stemmer-nlp/) to reduce words to their root or base form, and applies language-specific stop word lists (e.g., *and*, *or* in English) to reduce vocabulary size and improve retrieval quality. + +> If you're using BM25 with Qdrant (since 1.15.2 release), you can customize stopwords lists. + +#### Lexical Retrieval with BM25 & Qdrant + +Now let's test our BM25-based lexical search in Qdrant. + +Suppose we're searching for the word **"cheese"** — this is our query. + +```python +client.query_points( + collection_name=, + using="bm25_sparse_vector", + limit=3, + query=models.Document( + text="cheese", + model="Qdrant/bm25" + ), + with_vectors=True, +) +``` + +Let's break down what happens with this query and the documents indexed to Qdrant in the previous step. + +**Step 1** + +For every keyword in the query that is not a stop word in the target language (in our case, English, and **"cheese"** is not a stop word): +- FastEmbed (Qdrant) extracts the **stem** (root/base form) of the word. + - `"cheese"` becomes `"chees"` +- The stem is then mapped to a corresponding **index** from the vocabulary. + - `"chees"` -> `1496964506` + +**Step 2** + +Qdrant lookups up this keyword index (`1496964506`) in the **inverted index**, introduced in the previous video. + +For every document (found via the inverted index) that contains the keyword `"cheese"`, we have the BM25-based score for `"cheese"` in that particular document, precomputed by FastEmbed (Qdrant) and stored: + +$$ +\mathrm{function}\left(\mathrm{TF}(\text{"cheese"}, D), k_1, b, |D|, \mathrm{avg}_{\text{corpus}}|D|\right) +$$ + +**Step 3** + +Qdrant scales this document-specific score by the **IDF** of the keyword `"cheese"`, calculated across the entire corpus: + +$$ +\text{IDF}(\text{"cheese"}) \cdot \mathrm{function}\left(\mathrm{TF}(\text{"cheese"}, D), k_1, b, |D|, \mathrm{avg}_{\text{corpus}}|D|\right) +$$ + +**Step 4** + +The final similarity score between the query and a document is the **sum of the scores of all matching keywords**: + +$$ +\text{BM25}(\text{"cheese"}, D) = \sum_{i=1}^{1} \text{IDF}(\text{"cheese"}) \cdot \mathrm{function}\left(\mathrm{TF}(\text{"cheese"}, D), k_1, b, |D|, \mathrm{avg}_{\text{corpus}}|D|\right) +$$ + +## Sparse Neural Retrieval + +Now let’s explore an approach that makes keyword‑based retrieval semantically aware: sparse neural retrieval. + +### What Does “Semantically Aware” Mean +The **bag‑of‑words** approach builds sparse text representations without word order. It counts terms, but it does not model which words appear next to which, i.e., **context**. Yet the **meaning** of a word is strongly shaped by its context. + +**Example #1:** +Consider `"I want some hard-to-get cheese"` and `"I want to get some hard cheese"`. These two sentences use the same words but, due to different **word order**, put different meanings into the word `cheese.` +Classical retrievers that rely on word statistics computed independently of other words cannot capture this meaning-in-context, which affects relevance. + +**Example #2:** +As humans, we know that: +`"A not soft cheese"` is closer in meaning to `"hard cheese"` than to `"soft cheese"`. +**BM25** lacks this semantic awareness. + +Dense retrieval might seem like the perfect solution, but in many domains **keyword‑based search** is attractive because it is **explainable**, **exact**, and **not as recall‑heavy**. The question becomes: how can we keep exact matches while making them **meaning‑aware**? + +*A common pattern is to retrieve with **BM25** and then **rerank** the matched documents using a model that understands context. However, reranking **all** candidates can be expensive. It can be more efficient to make the **retriever** itself semantically aware from the start.* + +### The Idea Behind Sparse Neural Retrieval + +Instead of assigning word weights solely based on the corpus statistics, we could use for that **machine learning models** that have shown the ability to capture a word’s **meaning in context**. + +![The idea behind sparse neural retrieval: machine learning model assigns words weights in a sparse text representation](/courses/day3/sparse_neural.png) + +In practice, authors of sparse neural retrievers often start from dense encoders and adapt them to produce sparse text representations: similar in shape to bag‑of‑words, but with **weights produced by a machine learning model**. + +If you’re interested in details, you can check out ["Modern Sparse Neural Retrieval: From Theory to Practice"](https://qdrant.tech/articles/modern-sparse-neural-retrieval/) article. + +Probably the most famous and used model in the field of modern sparse neural retrieval is called the Sparse Lexical and Expansion Model or SPLADE. + +### SPLADE: Sparse Lexical and Expansion Model + +**SPLADE** (Sparse Lexical and Expansion Model) uses bidirectional encoder representations from transformers (BERT) as a basis and, hence, is out-of-the-box mainly suitable for **English** retrieval. + +SPLADE not only attempts to encode the meaning of the keywords already present in the text; it also **expands** documents and queries with **contextually fitting words**. + +**Query expansion example** +```text +Q: "cheese" → ["cheese", "dairy", "food", "dish", …] +``` +**Document expansion example** +```text +D: "Mac and cheese" → ["mac", "cheese", "restaurant", "brand", …] +``` + +This addresses the **Vocabulary Mismatch problem** when related semantically texts use different words (e.g., `hard grated cheese` vs. `parmesan`). + +Qdrant and FastEmbed integration allows you to easily use one of the latest models of the SPLADE family, SPLADE++. + +#### SPLADE++ in Qdrant + +**Follow along in Colab:** + Open In Colab + + + +#### Create a Collection for Sparse Neural Retrieval with SPLADE++ + +SPLADE models don’t rely on corpus-level statistics like IDF to estimate word relevance. Instead, they generate term weights in sparse representations based on their interactions within the encoded text. + +> Note that we’re **not configuring the Inverse Document Frequency (IDF) modifier** here, unlike in BM25-based retrieval. + +```python +client.create_collection( + collection_name=, + sparse_vectors_config={ + "splade_sparse_vector": models.SparseVectorParams(), + }, +) +``` + +#### Create & Insert SPLADE++ Sparse Vectors with FastEmbed + +The FastEmbed library provides **SPLADE++**; one of the latest models in the SPLADE family. + +> **Update:** Since the release of [Qdrant Cloud Inference](https://qdrant.tech/blog/qdrant-cloud-inference-launch/), you can move SPLADE++ embedding inference from local execution (as shown in this notebook) to the Qdrant Cloud, reducing latency and centralizing resource usage. + +As a result, this step looks mostly identical to using BM25 in Qdrant. + +```python +grocery_items_descriptions = [ + "Grated hard cheese", + "White crusty bread roll", + "Mac and cheese" +] + +client.upsert( + collection_name=, + points=[ + models.PointStruct( + id=i, + payload={"text": description}, + vector={ + "splade_sparse_vector": models.Document( #to run FastEmbed under the hood + text=description, + model="prithivida/Splade_PP_en_v1" + ) + }, + ) for i, description in enumerate(grocery_items_descriptions) + ], +) +``` + +However, under the hood, the process of converting a document to a sparse representation is quite different. + +#### Documents to SPLADE++ Sparse Representations + +SPLADE models generate sparse text representations made up of **tokens** produced by the SPLADE tokenizer. + +> Tokenizers break text into smaller units called **tokens**, which form the model's **vocabulary**. Depending on the tokenizer, these tokens can be words, subwords, or even characters. + +> SPLADE models operate on a fixed vocabulary of **30,522 tokens**. + +#### Text to Tokens + +Each document is first tokenized and the resulting tokens are mapped to their corresponding indices in the model’s vocabulary. +These indices are then used in the final sparse representation. + +You can explore this process in the [Tokenizer Playground](https://huggingface.co/spaces/Xenova/the-tokenizer-playground) by selecting the `custom` tokenizer and entering `Qdrant/Splade_PP_en_v1`. For example, "*cheese*" is mapped to token index `8808`, and "*mac*" to `6097`. + +#### Weighting Tokens + +The tokenized text, now represented as token indices, is passed through the SPLADE model. +SPLADE **expands** the input by adding contextually relevant tokens and simultaneously assigns each token in the final sparse representation a **weight** that reflects its role in the text. + +For example, "*mac and cheese*" will be expanded to: "*mac and cheese dairy apple dish & variety brand food made , foods difference eat restaurant or*", resulting in a SPLADE-generated sparse representation with **17 non-zero values**. + +If you’d like to experiment with SPLADE's expansion behavior, check out our documentation on [using SPLADE in FastEmbed](https://qdrant.tech/documentation/fastembed/fastembed-splade/). It includes a utility function to decode SPLADE++ sparse representations back into tokens with their corresponding weights. + +#### Sparse Neural Retrieval with SPLADE++ & Qdrant + +Let’s now see SPLADE++ in action solving the **vocabulary mismatch** problem. + +```python +client.query_points( + collection_name=, + using="splade_sparse_vector", + limit=3, + query=models.Document( + text="parmesan", + model="prithivida/Splade_PP_en_v1" + ), + with_vectors=True, +) +``` + +SPLADE expands the query "*parmesan*" with 10+ additional tokens, making it possible to match and rank the (also expanded at indexing time) "*grated hard cheese*" as the top hit, even though "*parmesan*" doesn’t appear in any document in our dataset. + +### Beyond SPLADE + +SPLADE models are a strong choice for sparse neural retrieval, but they have limitations: + +- **Token vs. word granularity.** SPLADE operates on **tokens** (subword pieces), which is less convenient for keyword‑oriented retrieval where users think in whole words. + ```text + "Parmesan" -> "par" "##mes" "##an" + "Grated" -> "gr" "##ated" + ``` + This makes exact keyword reasoning and explainability harder. + +- **Expansion trade‑offs.** Query/document **expansion** improves recall but can make vectors heavier and results less interpretable. For example, SPLADE++ might expand a document like `Mac and cheese` with an unrelated token such as `apple`, complicating explanation. + ```text + D: "Mac and cheese" → ["mac", "cheese", "restaurant", "brand", "apple", …] + ``` + +#### Qdrant's Sparse Neural Retrievers + +We’ve been exploring sparse neural retrieval as a promising approach for domains where keyword-based matching is useful, but traditional methods like BM25 fall short due to their lack of semantic understanding. + +We’ve developed and open-sourced two custom sparse neural retrievers, both built on top of the BM25 formula. +You can find all the details in the following articles: [BM42 Sparse Neural Retriever](https://qdrant.tech/articles/bm42/) and [miniCOIL Sparse Neural Retriever](https://qdrant.tech/articles/minicoil/). + +Both models can be used with FastEmbed and Qdrant in the same way we demonstrated with BM25 and SPLADE++ in this tutorial. + +- FastEmbed handle for **BM42**: `Qdrant/bm42-all-minilm-l6-v2-attentions` +- FastEmbed handle for **miniCOIL**: `Qdrant/minicoil-v1` (here's the detailed guide ["How to use miniCOIL"](https://qdrant.tech/documentation/fastembed/fastembed-minicoil/)) + +## Key Takeaways + +- Qdrant offers sparse vectors for lexical (keyword-based) retrieval. +- Qdrant calculates **Inverse Document Frequency (IDF)** (part of BM25) on the server side. Enable it when configuring a collection with sparse vectors. +- Since 1.15.2, Qdrant supports native conversion to BM25 sparse representations. +- **Sparse neural retrieval** is keyword-based retrieval that accounts for a word’s meaning in context. +- Qdrant has open-sourced its own sparse neural retriever (e.g., **miniCOIL**). +- When choosing a sparse retriever, lexical (e.g., BM25) or neural (e.g., SPLADE++, miniCOIL), **experiment on your data** to find the best fit. + +## What's Next +Sparse retrieval, even with neural weighting, has limits when semantically similar content is expressed in very different ways. **Dense retrieval** excels at discovery and naturally bridges vocabulary mismatch. + +Both methods are complementary: +- **Sparse**: precise, lightweight, explainable. +- **Dense**: flexible, strong at exploration and discovery. + +Combining them yields **Hybrid Search**. See the next section for how to configure and use hybrid retrieval. + diff --git a/qdrant-landing/content/course/essentials/day-3/sparse-vectors.md b/qdrant-landing/content/course/essentials/day-3/sparse-vectors.md new file mode 100644 index 000000000..88fd416f3 --- /dev/null +++ b/qdrant-landing/content/course/essentials/day-3/sparse-vectors.md @@ -0,0 +1,217 @@ +--- +title: Sparse Vectors and Inverted Indexes +weight: 1 +--- + +{{< date >}} Day 3 {{< /date >}} + +# Sparse Vectors and Inverted Indexes + +Create and index [sparse vector](/documentation/concepts/vectors/#sparse-vectors) representations for keywords-based search and recommendations. + + +
+ +
+ +
+ +## What You'll Learn + +- Understanding sparse vector representations +- Using sparse vectors in Qdrant + +## Sparse Vector Representations +Sparse vectors are high dimensional vectors, filled up with zeroes except for a few dimensions. Each dimension of a sparse vector refers to a certain object, and its value – a role of this object in this sparse representation. + +Let's consider a movie recommendation system: +- Each sparse vector could describe a particular user’s opinion. +- Each dimension could describe a certain movie M_X, and its value – user’s rating of this movie. + +```text + M1 M2 M3 M4 M5 M6 M7 M8 M9 M10 +User_1: [ 0, 0, 0, 0, 0, 0, 1, 5, 0, 0 ] +User_2: [ 0, 0, 0, 0, 4, 0, 0, 2, 0, 0 ] +``` + +### Comparing Sparse Representations + +Comparing two sparse representations, you'd be usually interested in how much they agree on the same features/objects (e.g., the same movie rating). + +The **dot product** distance metric, introduced in the **day 1** (*Vector Search Fundamentals/Distance Metrics*), is a perfect fit for measuring the similarity between sparse representations. +It multiplies corresponding dimensions and summes the results. + +```text +similarity(User_1, User_2) = 0*0 + ... + 0*4 + ... + 1*0 + 5*2 + ... 0*0 = 10 +``` + +You can notice that only objects/features that play a role in **both** vectors (non-zero dimensions in both) affect the final score. + +### Representations as Index-Value Pairs + +Storing thousands of zeros contributing no information is wasteful. +Instead, a sparse vector can be stored compactly as **(index, value)** pairs of its non-zero entries. + +Using the movie example: + +```text +[0, 0, 0, 0, 0, 0, 1.0, 2.0, 0, 0] + 0 1 2 3 4 5 6 7 8 9 + +→ [(6, 1.0), (7, 2.0)] +``` + +This representation is space-efficient and preserves exactly the information that impacts sparse vectors similarity. + +### Organizing Sparse Vectors: Inverted Index + +Searching for similar sparse vectors boils down to finding the vectors that share non‑zero dimensions with the query and multiplying corresponding values. + +Scanning every vector to check for matching non-zero dimensions is too slow at scale. + +**Simple idea:** keep a map from dimension index to vectors that have these dimensions non-zero (with the corresponding weights). At query time: +1. For each query non-zero dimension, check that map entry to collect matching vectors. +2. Score only those candidates using the **dot product** over the overlapping non-zero indices. + +#### Example + +Let's grasp it on a simple example: + +We have 3 documents - 3 sparse vector representations: +```text +id_1: [(6, 1.0), (12, 2.0)] +id_2: [(1, 0.2), (7, 1.0)] +id_3: [(6, 0.3), (7, 2.0), (12, -0.5)] +``` + +The proposed map could look like this: +![Inverted index: map from index to vectors with weights](/courses/day3/inverted_index.png) + +Now, if we have a query: +```text +[(6, 1.0), (7, 2.0)] +``` + +Search will consist of looking up `6` and `7` in the map & computing scores on the overlaps: +```text +id_1: (6: 1.0*1.0) = 1.0 +id_2: (7: 2.0*1.0) = 2.0 +id_3: (6: 1.0*0.3) + (7: 2.0*2.0) = 0.3 + 4.0 = 4.3 +``` + +This simple map is the essence of an **inverted index**, a data structure organizing sparse vector elements in a retrieval system. +It *inverts* the representation by mapping each non‑zero dimension to the vectors where it appears. + +Inverted index makes sure sparse search stays **exact** and fast even at large scale. + +## Sparse Vectors in Qdrant + +**Follow along in Colab:** + Open In Colab + + +Sparse vectors in Qdrant collections are configured using `sparse_vectors_config`. + +Unlike in dense vector configuration, we don’t need to define **size** or **distance metric** for sparse vectors: +- **Size** varies based on the number of non-zero elements in the sparse vector. + - The maximum number of non-zero elements (i.e., the sparse vector’s size) is limited by the `uint32` type, meaning 4,294,967,296 non-zero elements. +- **Distance metric** for comparing sparse vectors is always the `Dot product`. + +Sparse vectors are not the default in Qdrant (unlike dense vectors). That's why, to configure collection with sparse vectors, you'll **always** need to give them a name. + +> **Named vectors** additionally allow us to use multiple vectors for the same point, for example, one dense and one sparse. We’ll cover more about this in the upcoming videos on `Hybrid Search`. + +### Create a Collection with Sparse Vectors + +```python +# Create collection with a named sparse vector +client.create_collection( + collection_name=, + sparse_vectors_config={ + : models.SparseVectorParams() + }, +) +``` + +#### (Optional) Tune the Inverted Index +Defaults are chosen to work well; tune only if you understand the trade‑offs! + +**Parameters:** +- `full_scan_threshold` *(int)* – up to this number (not including), the inverted index **won’t** be used during comparison (but **is still built**). +- `on_disk` *(bool)* – store the inverted index on disk (`True`) or in RAM (`False`, default). +- `datatype` – precision of values **stored inside the index**: `uint8` | `float16` | `float32` (default). + - The **original values are still stored on disk** regardless of the `datatype` value. + +```python +client.create_collection( + collection_name=, + sparse_vectors_config={ + : models.SparseVectorParams( + index=models.SparseIndexParams( + full_scan_threshold=0, # compare directly below this size (index still built) + on_disk=False, # keep index in RAM (default False) + datatype=models.VectorStorageDatatype("float32") # precision inside the index + ) + ) + }, +) +``` + +### Store Sparse Vectors in Qdrant +Sparse vectors in Qdrant are represented by: +- `indices` – the indices of non-zero dimensions (stored as `uint32`, so they can range from 0 to 4,294,967,295). + - `indices` must be **unique** within a vector. +- `values` – the values of these non-zero dimensions (stored as a float). + - `len(indices) == len(values)`. + +```python +client.upsert( + collection_name=, + points=[ + models.PointStruct( + id=1, + vector={: models.SparseVector( + indices=[1,2,3], + values=[0.2,-0.2,0.2] + )} + ), + ... + ], +) +``` + + Don’t confuse a vector’s `indices` with the **inverted index**. + - `indices` describe which dimensions are non‑zero for the vector. + - The inverted index is a data structure that maps each dimension to all vectors where it is non‑zero. + +### Run Similarity Search on Sparse Vectors +Specify the **named vector** to search with `using="sparse_vector"`. + +```python +client.query_points( + collection_name="sparse_vectors_collection", + using=, + query=models.SparseVector(indices=[1,3], values=[1,1]), + ... +) +``` + +The similarity score for sparse vectors is calculated by comparing only the matching indices shared between the query and the points. + +## Key Takeaways +1. Choose sparse vectors when most features are absent (zero) and you need **exact, feature-aligned matching**. +They’re storage-efficient and work well alongside dense vectors in future **Hybrid Search** setups. +2. Similarity = Dot product. Sparse vector similarity in Qdrant is always measured by the **dot product**. +3. Sparse vectors are organized in **inverted index** (separate data structure from **HNSW**, which is used for dense vectors). +Search on sparse vectors in Qdrant is **exact**, as opposed to approximate dense vector search. + +## What's Next +In the next video, we’ll build keyword-based retrieval with sparse vectors in Qdrant. +We’ll use **BM25** and touch on **sparse neural retrieval**, keyword-based retrieval with semantic understanding. +This will set you up for **hybrid search** pipelines covered in the second half of this day’s material. \ No newline at end of file diff --git a/qdrant-landing/static/courses/day3/inverted_index.png b/qdrant-landing/static/courses/day3/inverted_index.png new file mode 100644 index 0000000000000000000000000000000000000000..c1096947f68225d77ea96157956b8e7e8d5184af GIT binary patch literal 44072 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 ZDY_bTji;Skx{@)MH*^fN5t literal 0 HcmV?d00001 diff --git a/qdrant-landing/static/courses/day3/sparse_neural.png b/qdrant-landing/static/courses/day3/sparse_neural.png new file mode 100644 index 0000000000000000000000000000000000000000..9a53774eacc87aac37804418bc77a87170e025ca GIT binary patch literal 95089 zcmeFZXH=70*EWh`LD(p4q=+C*1woq9q$<5gPv}h$DWO*>0Yz$P7K+qJXrXtIE-0Y1 zNFbEZ1O%i6q=ga^&KqWa?KEGgB+0(($)1CIkD&m;cFP!tsZbD}6H{>LvUDE|K#K*S2@>o;%%E7{h^ z(ooV}j(gwgHc3C0R}h!l6$E8%bxwc#*0{&pe*2ivQnb94oSHSf0aKghuv#v*rFCg-O4JYJyT8|N2)cp zS>*`XhEt#Tp|pQLQN$|FF&}x=?O1~;n)(n*^1c>LE#rr~^3-~k^yL&vW%0EaBho8B zS!dV*D_#YViO$GROSC+rElyan*sktea2W~3S{kak8oM=u(xauUw5OUI}C>r^`P`+iqVmrz0LE`x-|2GA zd1s_dwTfM9OK!staO_`+=1-}sY{6&P3zR*u)~paL+xcT^ch%dM8C_ewOl2w%`v&dGa~)<$(W; zB*=r7-;4l5f`|sh@A))AA#Kady8Bo^WV?s(MeD`+aeftj2m)*9#L0g;3VzbBhE7iP zYbcC^oJ4@vs`YFza$J<_!l^82O@lUD#nf5xfpq% z5^{W6p)`0q*{Xuk_(6>Ik4yE*Rx`H6@7ZlY^QP}@bj&=v(Okxjy@~V-mizdE?BynN z(*bQ>c%?|Z{o*L^rvqL2NBUq_>^!jx{HZ`UV*~>2V;4xco3x|Xgs*}J!|evj{CC|V zm?7pGXW^*OBc2t=?6D_mp`~@kCQL{GZX#Z~Haiv)MUajAPO8!LjUZN-w;w(u_AL8L zk>Xi4w$y04=R+BIWK$D&vd@Q3?`%5D?qk;ElYg7SMwYc92N^5B+m`KT8(_m362sB{ zbF*$bYS!A9j~@cY=Mv797Bh6b7PbzWc-MJ>-sk;XH+)y7{!OaTL-H2M~&jfD4g zDve%~xm5gp{-{E^5#qhbAZ*MC>qTi>uW@vg2#2nqi%;L5E;|mH*$fy6vrf&n#5Bs8 z5Gua!T%DhVA4npw&+Nn(5<_{4O$jW9wZ?HeOETY`kR{6H-WJeZNML2!~lS=O34CkfDc| zp!)^zLTAv9`zQ58h1ByJB~aSmYwmt=B<6;(7hLiRc$#19YbDr{3Qhs(l>^dgeS=>{IzigZFLB{jJ2XW2mm0xTog)TNQTUiBt z@TOtzFZ*6*T6v>Ws;fb>6O@w^fvOq;h zc}A|+$Z!Vk5qeDCwlk=aN>2G~4ym~AQ&4Zj!=}l8-*(Avj=5YkvF8{uuN&u38;r|1 zA1S?!e@a=Vn^_nu^fa{VL0vGrVhiu!gKHxuhu`}q0+X^Q9mPkj1*)iUNzK<_`*05b z(ldz81k3Z0Ukh|0Iy{2bFM7vLmWlO4vAA4CkP}h}LfLS_+G}Pb|I?>wxnp)08+y!^ zT)LeCgW?oAALJ7fq20c(HWiAkRn|T^?{kd#01>eJTJl$0=r$@O_*^yqH8|wl5j8G3 zK|#^0s=~SF((mnwdcDOCYx+i%sElBpwQx)KZDOxdrX@>WY1Di#P&bZ8uP7<6I&jcQeNkbV-+{MP0>S?1N*T54;%K480iUrO z6mg_0-h0-|i(WbAo5qcWzZORYN882dl4FhR=6y4q%{$hOw8hRzch+T>By{&zYS7p;I%e`uP(4{3Rgs9wGZv z8WeAj8gmLV6WX@XH9-*(8;2@~HkOTRr4}OYm4r(&e)YS778;PjtL=vDV7#)X}Jlx zYErIg2CdHYzJRI{3Z2dXR1?p@r&F zQoWOaTh^HIVtSkM(-j~S47|j)0U4?&g62u#7T=QB$9RQX=2?(Z*3C7JQwW2FG8Ho? zzA}Pz6Ax|b+*GKwAU}_z?@s(!KBR)sM_FD(W#5HsmYjymC3rJ%6;Zhw8B`V5;%xdI zq{hzwaR%|dqH{_0Y5Z-8SCl! zZ_yM>1G8=A#y_hwH#D8wb;zJ?r9p`BrW!~5J>;`jcQ*u7BB)fLxD18CkZUe$Ey&}) z8ErH_-mGdz?s{!sh(+%fwF=-Zi1{aIOYjrAZgXUQ`(DhRwP*K*^QEXd>%%@u{%>)N z3r<94A*r8%GgUP=;vMLB`HfFOZm);F`(SZQ*p6QdRRDljUm$jQk2!C>Tz880%Hc88J62XF;y zx5$nsQ*>r5bdKKp(o?s^H^PN)bWNnb1@RpF;(TXU_bkp#@Y62;l}fdG`TS5dOpBmU z!&7x~;Qy^WgQUc&;b}=ON4~guu9rs4^g#%JX{gAwJk@RV3-nBgrA#?*cl>9+`x1_Uso8h4%y5)?_o+?xHovPHbw`*@T#(2hj7f z`<=ygMSpu{by>u^=jv-X&e7Kd$t6$>c^Iupdz?TnIg3}C3?E4HoHn4xA4S1~TU~yZ zR*b%(VUF84_{|-?8y$Jb=Zw@W;iQ$Yz@!3kPiFk@oG7$iN(~v1Oe1WE9r51;?}w_M426H^z!^9Y*_clo>aGd=Ge9hN8rgR z6zwu8_h{I-{3OGwf%HA61HCgkCT`qh$x?=r{=NE<(>X}_zb>1=#$qQeX}=?AF8<}J z+}B1*D&fD&`aH#p-xQ8KCuUS?64D8bcUd}y$=6QBsjrTTar!4t<_M~r`2r2$>y%zR zEyYtdwRbX|u~EUt!ZK+A#YqyT{kpF3#{D4e)ZK0a|6N=nCdBYd(nDer>>$GE98>bayxCtm1kiY2rovu zjv|+4TYqk+`=DO5-?N!?H1`K0PxADMaRmV+u}L|*(|Y@xL=v$FtKwMnzR3jS3FAaB zD-d6;-+dW1>bE{+E_GhlAvs&KA{Q09)H#xR5Zu!!aLuqqp_`*8K9mR6!_gxwkD@I5 z15(&D!x8w{4-Y}Uw`ezfjA{6-MfonBUOY!lROVYKu|6C*)sd+gW<>v)W#GORn?k1~ zrd0P{l~q1t#>Et>im9Cs&!6YKHFWwWkd!?;#5AURW=*q1X-}MFOF!OtIGKAjC1x*9 zG^xZ9^u>X5iLRq^2pbxnVkE!iRgvQ7nOOnd=_&eq8dExWW#nMtEoB}0s^cGj_TU?m zcWSowIjFkd?chS;Re4cD3*!AjX&E=Py3fLPL_enLV z4}ghV%g_G}kJ2nJ3_iwOv%8&T_uH{pXu=UNWs{qiuJ1BRf9X6gX_Xq0WR&S?Q@N0R zKKt4a5mS-Xat>ltNFoDDt+}+>?WjiIHN&`6s&dHqfP0h|o7Yul0nRiJ-F9Tvt}3r8 z(AO_)zxU}dhA>p@jDc&G!(YN<-&^$gI$>zBG zjXC7;-6^>-6gXILkr6%*<*AbYJwFdVEcaSb-AEBCIIJV26?vZJ;n5?O*kbOLTy{I@Seps-;RAMZrH znh9MwS|(KFahu-Q-eTskllm9Z)~Wv|Lylek|BgD3zktL_l8(M)!`rt}DSxOZitdZa z|7X&lB^+R>@J)Y`w9Pu%a`(Z0>)6!M(%G~WHo3dnnO|T2HY^2;%zp|92lZjO`;sUhHG%p?AtPd5v0$A-#m-}m~T zfx4Og-zpJ*b>8DJc?mEJZ>K4Lk1A3>;>-B&Bc8G582m4ihz%VugS@&m|C|5LBD?zx z2p81Q`jTu3Cc(EUp!UbqnbRcQv1(i@ZS+iUWh=kpRD%( zOd*cl*YfNIWN(5c0qtEPk#DcHT(Dxu`|m2cFaD>Y9N~Yq@D~-!5^g*@mEzXES-1XY zi=I^hQqH-#jVL3B=SVoy#-DS_VjhkF41o82ykADx{P(B-%SQiqlp+zTKHap`X?WYo zf6XAbyoB@56DUTg{%_~-xt!|21e4+0`+A>n_?rjC}ZKJ#m8nd3cB9|4bW@ImiKqGO3kSO4D+_TO0rOZd_S+6m$(;(s^>c^+NM!a8L0 z37X&G{d1)LPi~%z@O3v1#7*FLvEDeR1#@<%o|KLDIl1-EM`BPvsI;4@o%CUUftZ!g z#!QCUwIB%`^tf>Z-)&w5{g|?Tc%g?ajdf{2=gpu+%1!I1PgsThaBG^0r7I5;%h%K^ z+*TfLZfwXa^b8bh?G%m{dNX4RV-t2D=O`#_TnvCf@u;U9y3z>FKv(B`5APN+{7trw zUKW+ow6~!aQ{1Xduf65p|ArG>Svfyh9EgI&^5!ve zz~6QwTxVP5a7SNWfLk4oFw72f$f_U0p_mjBJ|AB^!_a|*%t(e52LhI?tB^kh26)6R zxnI*vOICoCNz)~_Zf-zL-#V90cpfYR%;jiWVTt6|Z>-0-7Y<3Vfe~I6u8d^$UDev|baB07M{TViaZn5h?Z2G-FP8;# z;(2>~o$qD0{tHNOIxW~LnjDMGlokyH=(mzaZb8gL96TQA?0mt zLgEr5d!X3jiW04X0on;@(UlzoGt%?3PGZ_k{%*yy*k&`Z@gu7sa~Xp%(mf30QqoCg zYhj~k#_}nqLEYuJ?cj~j0xnmlGKFu4!8)FUFkc@(1&#*vvyrVDa;w4zM}~iz6l^}R z`#n~|?z%yk5Khx$LkyA5&ZVeM#Q}z9O1)-tEPBw=)s%|2K0_||9?kS+^OpCbS>8wM zV(AjCC+|nL$7|#gdHU;sm}$j@bTUjgcpT%0bLz`ZbT%0-c&N_6yIWo=wsLTG<@?NpqT=m4}ml zwFO_^q*GT&;z1qhKBGlWwOKVtQMqbAYrHoo3R~A8h!aXP%zW`>AbujadS`ednRV4L zT{OVf`BvsKm427^8$UOlhN(IMZMZZSd!3D$bpB7d;%C5KdsJe4eUnF8&feKFwv?Pb z^zySVJ4hr#%B32AE@~^03{qUUZ;1H$2?MD9^cr$iwxW0!ePeYpl{aqv8esVq; zMy=&ZLrd#xib0xsUqvsUxM;GvW;O2@G_)Pm)!Gy5M`mW4I}PG|H%c|mx9~1iH#^GL zQ17Hkh3cfA;1slSUB}wWcPA#zhEt|-roPAoM15qI=|PbYyOCJoQ@Km3;y49K-w1E% zAY0?3<6~N<0hwlWLROjFB#k-{n9gTt>VJ(oKs*t7j-=b1d>@c_@}3NOzP+pgJggKQ zIJG<bz9 zsiegvB=TCzaHgEjeqS=Tm$_LmqeJX3VLjF+@?4faQ}Zqv@6Tn2+I#EX=P%yu(Y_C8 z08%Uco=8Jt znI9B788!(#qP@PfbBrEFU){9M9#~k_%@*O`xv-^{-7rgV##Y+92e$b71=fW4rB6*I zNw`gBw94m-l(#V26wSl*IkKB2p$mpIy+&V!Y1J3c(et)(?^CW8S%IyuP(;~{uP+Z=F6fWj4 zQC><74tJg>G*GFk++VecvC%z)uq_uw>1O)-7@b-h3?+;?k$QU!y}j+2IEz1x^bP6~ zLI?{hczF8^!{4yO%$?_FS8M`T4{29k70wJUk%Oy?j(-J2^MsfhRQ+x^sWX%N2L?XA zfy582jMQ=sWZb<6U45Nz|3d%XsawE_v7v*7oszBeat*Bh*>yi(N5S5z$)gMLYd1c+ z?q{d?%qpSsmaQz_%skzh-rXp`P3-hsgFa1{^j9^j3U)gBd{~(OYScKkg=!bX`Xqpy z=2+_Nv`(z`bu&S!LPtK{(iB+BpkNzY|rR(3I{{BrHdqiJJel7%G+0gQ6!5cstrtB+A%|`ZIg>OHCQ*Uc0 zOCe7c)ms+rlttB;WAV=HcCCD=}+92?{88x;@Uum@e> zoD_BinUWN-@o$>pxmH2-a=3AHnnc@lnw%um$WnU!bL4F*Mqu}oVVwGBURUGPDv1`g zNwK3|^xUw+>>bjfDPt+!^KA1Cvt%M0*E^FJa{CI|cv-y5>iGK`b*)=|c(a8T(<0BU zS5Dieg-;$K$wN7t2QZdW-L$N<9f}5b@Ef!9D}-)gi2`F!yopnby!pwS5_xK)etG_W zz6E3Tekam%KX3W298B!3WUA1vOsVWSt%h&xFhG*d=i06_!`r36cGoElOj%a9DhkW% zEs1W%lJ+6R3X40P0A|pm3`e{>y=AWpieptjRwH%X9wRG|A9Cho6y8e`r>EO{L*)1J za?z%JsRcDCDXx->j8@h=Zh>}t%_{|y*K{`B*2}N$rOnlNj@-8Q4Ypq>9t0W||n7#uU zX-FcMwB5ove8{i+?xCWt<1K}#v4z-o5QVziWKjLVqiMyRLq~yss^FoM4!mu?Wp|P} zv~m`Ava!)KtldCVGSXjd5SG6SovY!{#=!I*h8xT$o{d6tZ`xehDAp&|rSwf(K9%d# z;@N7o@x&nCs-4M=JnDH%?-F~jt5LA*X>#*cibGA~r5pX1BL!HJuWH^e-Z?x;lg>VI zV~sny(VoF7|FZ%>3nKCtR31dYv>v6tN$mlUHN~^J?VYG>4aupeYE02bM{tsrG#=T@ zzpuQAR5;XC=nvBmJz5kPhTTmu+HGqy`!1+(SgBt@E&lR=v{87eePTF7C`=iMv8lxv zYe?g~D* zmoD3Flt_c3_h7xobJ!9c_WGB`pqUfTIT_5{MOA>!RL^Q(Y#OO`S7Lo#P*k*nK1n9? zB@0>~_W;^K{W!|!Sj{g;ZS(frB8N=KRIn&HI(|5y10zC6KWm(39^FNAob>e>*6GNS zKJQM&x)=DaNfxHvEqIa`tAbpX6n53Tq_Ab=Us7*Ls9xrw+N<(T?Cn%m>(6-2dUbX9 zDgEqF*Fi3JC{i$}=4-!zyPMWs>O=?By253@HI$Xcn&ie{rxJ5k4PQoRW1||UGinB3 znm=Q@nE_Wd%Y4C*n2}{=S|cZ;wncngT(Ty~!nll|T36z>DDOnbqEw_OriAa#7w+7i~m;s+r3@$>?45f{>)s!qZ&XEv+ zeJCh86WxNOTKDivyIrV|+w4I;_O3gcIPCE2?7*cxv792&tWf-y32%Cqm{lcz*`I+% zjJGPdeMX(4972zo8KK-Rce2UpO7K>&19Jy5K4Ma~8AB}y;TxrarEd`lnRp#2 zVS@o428?LgHH;9BulB-3oO~5Bc4H+kHPU&ZdqcvlhfVIlS=j;ZsW_^*xhKAfpWqf& zyk2*p7CNMeBUdZ9UE3M@GqJ_`#-ZEc>vdF@rb9MMrW^2*@6%N7ydm^5}I7@2)Sz!O-_E<1M^2s$CqJOi_)hij#>bnQx8gXCR@|1~pG4 z9QL73MW{1g=5R5mD@zfXx%0l;!=vjMl9Zxz&Lauw`RC2Sm{}t@P>V8 zN+9JW!?y2(X70GXE7m3B52k3?=Gh)aPX13)wz8rAvyh2#gsD|O&uJ^lh-q8GTf=(z zrQ1?`&yji-qWl$(;;%c92HNqyMXjy!z7jt93Hx{SO=DZN&G7|AZ!)U=LMbTjyGlvX zqR2Ud0@*v~?eKX2s)VcVi@l?C|LXfAMZ7}&{1+mqJ}GnFFsqnuRap1tdkWw+Q<@fA z$mnLj+3Fsz`#l!Me0fjOyqMAB!Y)2-RL)>Xhk1)^b@8sf$GAP zN19K6->3wO^^H%WU)8(0YhRVv)Rytz)FNPK%xjPrF;i2-{e@6Cn+9 zu;TfruopQeXchd_@1ze<;5vX59Jx;V5#b#e#>W+Qi%U^4K#Jci1B0NVo(`FXn9}iu zB#Z`(S%erE-QW*yq z-l34OkmF56*r@wd7Axy28Vr}^0xHe|T66r9hS`?dTV+rOVHjh_9$&(BuIY>NciIM+cbJrLkce6n&@+e>VHw%@dX_%+ zOD(VJDAs@c=w0&IyP;L%+2g7)=jp<*EDAJ#uELginZOrfpP5A89|;REL_5ZKb{+Ld zEy(6KHJzFy(O$>z9G93^-*cOKFUQ`tS#(((ep5_552$j{{VNRUnbCxii3totG>q_d zoX+*eRfEeh&XBeKQPKL0uO@I-9a1Top%J-w;h!4`aSvh>-o6P5JQ;SHp3E2?`7*E) zzK~eDN(>F%Us%aW7A^>g8c-)Uv`%;fi6-5sy^hCqV&=8qrmI$}m4}O2y^k9cg_G0T z!>f{;8p5q@;w5)3b0Jy~BTpH20uEvthFcR$zl0=I4kU9}zfLff${q6)Z71iNh-h2w z?8H?~!pD1CBywo&$U=S+!yZvTRe8(HJXa{Y&iJ6Ajxb5tjUR|G2fgf7vf7kcsT9LT z@~|-O1RSUN*EkynlzgO$6W3AT&XSlL5`$`5rpp$Iii=|kvzoEhntDL1jsh8X7x>-d_1?SjoR}C%GO(ky)2u%T= z_)H1a%WIr}vTN@wyx%LvPBVmE^py6 z+6gO`Wo+<__0SJ@<$k!^*r&^iY3z}*wIEZ&cqv6C{Y9sDqFsl_D@rBJA61J87Tw$=_Tpj*nR817p7$h1zNZD3+@}ID~6(ta`!0p`+5Iu)N`#4leOn1CLF}A^=AO zFHfnTFD>1S9tBF_p(6aWT8=ix`vD7u@Y{Wi2+ot{{F7i;;F zX=$n2+d!8#wzcqv-2Qy}?$2K4;qZd)Qv#|g5kO;f%Uv2}=#l@RRy%YntyZI@_%?X%fNU0q#asLxi~ z%6>)DyLEY<%ZyxUj$KTT5)D%ZahOO!fT9+;5ynd&4SPBA2Ic0A^TKmU>ScdJKlbi>31f)UCgC~U?^Dj_XcE1M`YZ8ulf%cm zn(5-XjUHLXcy@CldRCjZkhrrhr5~^w-{ALT%=Any>(#XyP9`91)MFkKS}IPH z`6JUJQ4H@dbL{;p9j5Uff&L5lkcNyq`S+lQv8e@&4!5-_8yCyLra!KSaTr^o_wn@S zV(?z{R9{r|u3yrjHCme~-^JG8R_d0q!`%(iWb)M7n8WWeG+n+~p1q+|G%NUtVLKt3 z$$7uRA|oP-8?+O%lW=YKEkfI}|1O@vyHC+Z#ykj1wUnHCgl-IScMe#esNLUMHkiEX zo{y(BuU_V5^M367R#HY=+S@cecrJ~&!K0`^rflk$xXkLis?PI_ekbQ3oIsc@pjHK% zo$1ajCw;#_te=~#s}PW@F1Rb#RL;}dlz+FixtVaxCRrjh05^jVT7uw0(&eJGh4T`T z^hwyt09_Ph>);^w$>T>zO-1L!!#HPy6Q9G3qQg3lymGlU0#w&>xE2(;)J~=d#?}r^ z`p#32nUbzVL%0U}o;nEtP8P+T!}rYd{x9wGvx=1SqG1U-GJX=h=I&=qAsxIC=hHv` zak!cxQIkXNq*m-k$#wBcs=i=y`PA2zaQhqmsm$l~!nA5&Ru+E7N5~*I=P!WF5#uoe zOVpL(WMkw?CUBUiW?R?Y3S#3bL$ZpE!*u8`#=bO(er95IqvUBD@$4gj0bdk$>ji$# zDHAYT`|5)IM}2+zTc@oaAx~BOy#EM_*aok!d8fjY%C!4emO5p5))|kaHxtWo>Y*e@ zb3lSY9T)koqz|)3u_eHEuB=TN-YQlR8(EZc0#G4 zyYDHj6l`abeC9oCh6Nz?4m)uv(4zA4=Jb&P1udBq{Bn_58LBm1dcQW0`DVY4Lcq7Z zTKga^)Yfb$`cYBUqo1Li%yumRXKJ9PW-NUY$J1M`E6co)7|?_FoB?9TFm#PKF9Tj7 zM8_Di<8rK7i#%rFUcndW1BpcIwVzzvd9g7Rj_~}7d;cd8`KEv zwh_@YS)CH_VhE_i>T!-5Z*-=TZuU=`*CU2)z6-8#zx45aG_z>7z!>{M`;2?1D8yRJ zL6+#~jI1CfPT0!J_-$zEX79hMT|stzG+^iVUl2q+c~EF_5{t5)+N|#qJZH)B-ABc^<^PxkBjuSu`oT)%$6pT=rzG&MHQCfsj1j8nNEYkUBj4l z8{qJOu?ciQN<9D+u5?Y(zq|<3uvsX z?XPEs(b<3;qm`9qS>$P}*?z+N2EEmzqpQUDJY{nluPCt}8(Yi(-fpAn%-mKuxhcMh zO)JoG(=@zkJR>cX(9l|$Sl$^doz7d3c7mHN%;&wN`|{?cb1I(&uuEXpYS_+RKAf=| zGxIW{#a8FdO^E_$?JT|25Ra};>+(x!5oI$|qXM^?aa$?yU=x4N;xNDXRJ>f%X#20V2@PIY9SetK?Y2YFh852Viz)qE_3wuJ07|_ z42ZZjdJk(yV<+ESYRDU?&{f}JR5P@*4?V_MvO8Nd!C(rTSpcpZczZ{b-Fqs}XZ)2? z1@iK9q+HNs4ZpTSt9wY-X6(D63!9}nu9p;EVX-p9ZS{kIc9NW)zG?`yoSmKXVY*ob zH8uyJi(r@WxVAQ})8I&Vtn-*F*Ht9f<~_K%tqvw}cO zY`x}^7lGWSQqnYT?Ih5;9J?gP3Mec%D!Yg7?o)3h+-BZb-HN-rWTDZg036@P{}ItT z=pMbH@+!4z;Az!=pXD>P{-D7-c7JgW`4eBmFr4i=jd=l-;0_NmNS*qE1Z>THP>`BK zJuc3Pq57rnH0kTmg+k+N2e)|nX;ePk!>p2D`k}lIj$M0eB|xS*HY1%D^OSG z|Jb`^&CZHG6Lwfz=j+ZOxQckKcMXvqJhS4&$NhA77-(v-i?-1#g^IVGJTy0JxSPLp zfj&&LNHD|DU&{HNHzS^LiE*xu3R-5`es{oWB3+uA5Pjb;JV{7;AL%n+RU>dwF?m&h zh4DG^tv^fIeUOl1xEA%=HAqV0-NC-j&he7gHo%W_O4{&(!1tW4mk4S?x<3;0d^*(t|PXxAD>02hUaQeh8Jw4xN;TW>e|uoxb+D_AMMOn zY*f328x*^&uYb$E&JQT8&NCjbj#u7jhKp6@h;$#h^a~2&oO%YSM>jCW-wj^`BXh3-;15bmTx#AoevareM#~3H_-(mpPtVp%sZXC5L)T|h+qz|h zLW#Cri6A}{m_ksG%%tFJ^FRK8b8xQQInNK&0*A?av^XWdA zRKE`~a!#@%-;}?Ipi(9tl!J@xt<{)blY896E=wK{4FWC7DG-2raM;JB6XtxK#4LYG zN*2uRe%Q3mP#vCKx5zAJ1Zw{z#<@b|5Ab?h=$%{fNmS3E00^pvFN_;oR{K0Oegvbl zx)u$io58?kAANl!OF{9@E9B!v@>%AowXUY{6)QF(GB0t=<=4A9n$!V&lM+XTKzhN_pCAxo-j$%k)qS zXy!)?`j_=82HSx_RWOO901T2GkOQ|5X~~R}X*{`vqM+!FX0oA|2p}a7tSkW-1p8e4 zQuPh&BQ=RUcJrnZMU}mxlwUW6dHYTj>SehXuYW&k4S zTH9dh^rK8v3)&bI3tO{uOS^Y5N-7GdSvWx;p;+oC0gMGys_~03ebI6hwls7#Ju**D7Z$-|775sLH6RBGDJZvAc3@vey1BS1Mj zA`7e%lS25WcJnb;iF3lq$M(=$%?ituH0W*ns6wvEu@t;K$pUW&(!{peSz{-+t)3!t za)bslybQz*3w;~zx?a;gnM_uJYo0A6_^q9M{lC=7wl(RCuw%y zm7%)LI?*_hvHGmCBKz5(2St&chiTxqhK6=ZOg0&J0QN!RQ-hJO>4&mb4=4c1@){?{ z3I*3Bn6CLVT2%;KJyT+25lY`bS<}$UOUH;X8^L!%0A^kXpib&MpgJ5W3-RG5C1lMV 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�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 literal 0 HcmV?d00001