mirror of
https://github.com/qdrant/landing_page.git
synced 2026-10-06 19:38:30 +02:00
Document custom RRF (#1985)
* make dedicated fusion strategy sections * docs: Java, Go, C# snippets Signed-off-by: Anush008 <anushshetty90@gmail.com> * add formula for rrf * add rust and python snippets * Use consistent formatting --------- Signed-off-by: Anush008 <anushshetty90@gmail.com> Co-authored-by: Anush008 <anushshetty90@gmail.com> Co-authored-by: Tim Visée <tim@visee.me>
This commit is contained in:
co-authored by
Anush008
Tim Visée
parent
5ed4822559
commit
902e937a25
@@ -10,7 +10,7 @@ hideInSidebar: false # Optional. If true, the page will not be shown in the side
|
||||
|
||||
_Available as of v1.10.0_
|
||||
|
||||
With the introduction of [many named vectors per point](/documentation/concepts/vectors/#named-vectors), there are use-cases when the best search is obtained by combining multiple queries,
|
||||
With the introduction of [multiple named vectors per point](/documentation/concepts/vectors/#named-vectors), there are use-cases when the best search is obtained by combining multiple queries,
|
||||
or by performing the search in more than one stage.
|
||||
|
||||
Qdrant has a flexible and universal interface to make this possible, called `Query API` ([API reference](https://api.qdrant.tech/api-reference/search/query-points)).
|
||||
@@ -35,33 +35,46 @@ One of the most common problems when you have different representations of the s
|
||||
For example, in text search, it is often useful to combine dense and sparse vectors get the best of semantics,
|
||||
plus the best of matching specific words.
|
||||
|
||||
Qdrant currently has two ways of combining the results from different queries:
|
||||
Qdrant has a few ways of fusing the results from different queries: `rrf` and `dbsf`
|
||||
|
||||
- `rrf` -
|
||||
<a href=https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf target="_blank">
|
||||
Reciprocal Rank Fusion
|
||||
</a>
|
||||
### Reciprocal Rank Fusion (RRF)
|
||||
<a href=https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf target="_blank">
|
||||
RRF</a> considers the positions of results within each query, and boosts the ones that appear closer to the top in multiple sets of results.
|
||||
|
||||
The formula is simple, but needs access to the rank of each result in each query.
|
||||
|
||||
Considers the positions of results within each query, and boosts the ones that appear closer to the top in multiple of them.
|
||||
$$ score(d\in D) = \sum_{r_d\in R(d)} \frac{1}{k + r_d} $$
|
||||
|
||||
- `dbsf` -
|
||||
<a href=https://medium.com/plain-simple-software/distribution-based-score-fusion-dbsf-a-new-approach-to-vector-search-ranking-f87c37488b18 target="_blank">
|
||||
Distribution-Based Score Fusion
|
||||
</a> _(available as of v1.11.0)_
|
||||
Where $D$ the set of points across all results, $R(d)$ is the set of rankings for a particular document, and $k$ is a constant (set to 2 by default).
|
||||
|
||||
Normalizes the scores of the points in each query, using the mean +/- the 3rd standard deviation as limits, and then sums the scores of the same point across different queries.
|
||||
Here is an example of RRF for a query containing two prefetches against different named vectors configured to hold sparse and dense vectors, respectively.
|
||||
|
||||
<aside role="status"><code>dbsf</code> is stateless and calculates the normalization limits only based on the results of each query, not on all the scores that it has seen.</aside>
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-points/hybrid-rrf/" >}}
|
||||
|
||||
Here is an example of Reciprocal Rank Fusion for a query containing two prefetches against different named vectors configured to respectively hold sparse and dense vectors.
|
||||
#### Parametrized RRF
|
||||
_Available as of v1.16.0_
|
||||
|
||||
To change the value of constant $k$ in the formula, use the dedicated `rrf` query variant.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-points/hybrid-rrf-k/" >}}
|
||||
|
||||
|
||||
### Distribution-Based Score Fusion (DBSF)
|
||||
|
||||
_Available as of v1.11.0_
|
||||
|
||||
<a href=https://medium.com/plain-simple-software/distribution-based-score-fusion-dbsf-a-new-approach-to-vector-search-ranking-f87c37488b18 target="_blank">
|
||||
DBSF</a>
|
||||
normalizes the scores of the points in each query, using the mean +/- the 3rd standard deviation as limits, and then sums the scores of the same point across different queries.
|
||||
|
||||
<aside role="status"><code>dbsf</code> is stateless and calculates the normalization limits only based on the results of each query, not on all the scores that it has seen.</aside>
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-points/hybrid-basic/" >}}
|
||||
|
||||
## Multi-stage queries
|
||||
|
||||
In many cases, the usage of a larger vector representation gives more accurate search results, but it is also more expensive to compute.
|
||||
In general, larger vector representations give more accurate search results, but makes them more expensive to compute.
|
||||
|
||||
Splitting the search into two stages is a known technique:
|
||||
Splitting the search into two stages is a known technique to mitigate this effect:
|
||||
|
||||
- First, use a smaller and cheaper representation to get a large list of candidates.
|
||||
- Then, re-score the candidates using the larger and more accurate representation.
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
This code snippet demonstrates a hybrid search functionality where you can query points from a collection using both sparse and dense vectors. The code showcases how to combine the results of multiple queries with different vector representations using Reciprocal Rank Fusion (RRF), parametrized by constant k. RRF boosts results that are closer to the top in multiple queries. This hybrid search approach can be useful for obtaining semantic and specific word matching results simultaneously.
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.QueryAsync(
|
||||
collectionName: "{collection_name}",
|
||||
prefetch: new List<PrefetchQuery>
|
||||
{
|
||||
// 2+ prefetches here
|
||||
},
|
||||
query: new Rrf
|
||||
{
|
||||
K = 60,
|
||||
}
|
||||
);
|
||||
```
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.Query(context.Background(), &qdrant.QueryPoints{
|
||||
CollectionName: "{collection_name}",
|
||||
Prefetch: []*qdrant.PrefetchQuery{
|
||||
// 2+ prefetches here
|
||||
},
|
||||
Query: qdrant.NewQueryRRF(
|
||||
&qdrant.Rrf{
|
||||
K: qdrant.PtrOf(uint32(60)),
|
||||
}),
|
||||
})
|
||||
```
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
```http
|
||||
POST /collections/{collection_name}/points/query
|
||||
{
|
||||
"prefetch": [
|
||||
// 2+ prefetches here
|
||||
],
|
||||
"query": { "rrf": {"k": 60 } }, // <--- parameterized reciprocal rank fusion
|
||||
"limit": 10
|
||||
}
|
||||
```
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
```java
|
||||
import java.util.List;
|
||||
|
||||
import static io.qdrant.client.QueryFactory.rrf;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Points.PrefetchQuery;
|
||||
import io.qdrant.client.grpc.Points.QueryPoints;
|
||||
import io.qdrant.client.grpc.Points.Rrf;
|
||||
|
||||
QdrantClient client = new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client
|
||||
.queryAsync(
|
||||
QueryPoints.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
// .addPrefetch(...) <┐
|
||||
// .addPrefetch(...) <┴─ 2+ prefetches here
|
||||
.setQuery(rrf(Rrf.newBuilder().setK(60).build()))
|
||||
.build())
|
||||
.get();
|
||||
```
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.query_points(
|
||||
collection_name="{collection_name}",
|
||||
prefetch=[
|
||||
# 2+ prefetches here
|
||||
],
|
||||
query=models.RrfQuery(rrf=models.Rrf(k=60)),
|
||||
)
|
||||
```
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
```rust
|
||||
use qdrant_client::Qdrant;
|
||||
use qdrant_client::qdrant::{Rrf, PrefetchQueryBuilder, Query, QueryPointsBuilder};
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
client.query(
|
||||
QueryPointsBuilder::new("{collection_name}")
|
||||
// .add_prefetch(...) <┐
|
||||
// .add_prefetch(...) <┴─ 2+ prefetches here
|
||||
.query(Query::new_rrf(RrfBuilder::with_k(60))
|
||||
).await?;
|
||||
```
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.query("{collection_name}", {
|
||||
prefetch: [
|
||||
// 2+ prefetches here
|
||||
],
|
||||
query: { rrf: { k: 60 } },
|
||||
});
|
||||
```
|
||||
+1
-2
@@ -1,9 +1,8 @@
|
||||
```java
|
||||
import static io.qdrant.client.QueryFactory.nearest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.qdrant.client.QueryFactory.fusion;
|
||||
import static io.qdrant.client.QueryFactory.nearest;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
Reference in New Issue
Block a user